diff options
Diffstat (limited to 'modules/mono')
42 files changed, 1673 insertions, 576 deletions
diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 320bbe7090..a1dfcf6377 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -31,11 +31,18 @@ def make_cs_files_header(src, dst): if i > 0: header.write(', ') header.write(byte_to_str(buf[buf_idx])) - inserted_files += '\tr_files.insert(\"' + file + '\", ' \ + inserted_files += '\tr_files.insert("' + file + '", ' \ 'CompressedFile(_cs_' + name + '_compressed_size, ' \ '_cs_' + name + '_uncompressed_size, ' \ '_cs_' + name + '_compressed));\n' header.write(' };\n') + version_file = os.path.join(src, 'VERSION.txt') + with open(version_file, 'r') as content_file: + try: + glue_version = int(content_file.read()) # make sure the format is valid + header.write('\n#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n') + except ValueError: + raise ValueError('Invalid C# glue version in: ' + version_file) 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' @@ -57,10 +64,10 @@ if env['tools']: 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) +vars.Update(env_mono) # Glue sources -if env['mono_glue']: +if env_mono['mono_glue']: env_mono.add_source_files(env.modules_sources, 'glue/*.cpp') else: env_mono.Append(CPPDEFINES=['MONO_GLUE_DISABLED']) @@ -80,23 +87,23 @@ def find_msbuild_unix(filename): import sys hint_dirs = ['/opt/novell/mono/bin'] - if sys.platform == "darwin": + 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 - elif os.path.isfile(hint_path + ".exe"): - return hint_path + ".exe" + elif os.path.isfile(hint_path + '.exe'): + return hint_path + '.exe' - for hint_dir in os.environ["PATH"].split(os.pathsep): + 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 - if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK): - return hint_path + ".exe" + if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): + return hint_path + '.exe' return None @@ -152,7 +159,7 @@ def mono_build_solution(source, target, env): xbuild_fallback = env['xbuild_fallback'] if xbuild_fallback and os.name == 'nt': - print("Option 'xbuild_fallback' not supported on Windows") + print('Option \'xbuild_fallback\' not supported on Windows') xbuild_fallback = False if xbuild_fallback: @@ -202,10 +209,16 @@ def mono_build_solution(source, target, env): copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) +output_dir = Dir('#bin').abspath +assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath -mono_sln_builder = Builder(action = mono_build_solution) +mono_sln_builder = Builder(action=mono_build_solution) env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder}) env_mono.MonoBuildSolution( - os.path.join(Dir('#bin').abspath, 'GodotSharpTools.dll'), + os.path.join(assemblies_output_dir, 'GodotSharpTools.dll'), 'editor/GodotSharpTools/GodotSharpTools.sln' ) + +if os.path.normpath(output_dir) != os.path.normpath(assemblies_output_dir): + rel_assemblies_output_dir = os.path.relpath(assemblies_output_dir, output_dir) + env_mono.Append(CPPDEFINES={'GD_MONO_EDITOR_ASSEMBLIES_DIR': rel_assemblies_output_dir}) diff --git a/modules/mono/config.py b/modules/mono/config.py index b4e6433256..831d849ea6 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -2,8 +2,9 @@ import imp import os import sys +import subprocess -from SCons.Script import BoolVariable, Environment, Variables +from SCons.Script import BoolVariable, Dir, Environment, PathVariable, Variables monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') @@ -29,20 +30,16 @@ def is_enabled(): return False -def copy_file_no_replace(src_dir, dst_dir, name): +def copy_file(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) + copyfile(src_path, dst_path) def configure(env): @@ -51,11 +48,13 @@ def configure(env): envvars = Variables() envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) + envvars.Add(PathVariable('mono_assemblies_output_dir', 'Path to the assemblies output directory', '#bin', PathVariable.PathIsDirCreate)) envvars.Update(env) bits = env['bits'] mono_static = env['mono_static'] + assemblies_output_dir = Dir(env['mono_assemblies_output_dir']).abspath mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] @@ -99,11 +98,14 @@ def configure(env): 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') + copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll') + + copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll') else: sharedlib_ext = '.dylib' if sys.platform == 'darwin' else '.so' mono_root = '' + mono_lib_path = '' if bits == '32': if os.getenv('MONO32_PREFIX'): @@ -148,7 +150,9 @@ def configure(env): 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) + copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + + copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll') else: if mono_static: raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually') @@ -157,8 +161,10 @@ def configure(env): mono_lib_path = '' mono_so_name = '' + mono_prefix = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip() tmpenv = Environment() + tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') for hint_dir in tmpenv['LIBPATH']: @@ -171,7 +177,8 @@ def configure(env): 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) + copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + copy_file(os.path.join(mono_prefix, 'lib', 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll') env.Append(LINKFLAGS='-rdynamic') diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 0dc0018224..1d0afa7f2d 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -118,6 +118,8 @@ void CSharpLanguage::init() { #ifdef TOOLS_ENABLED EditorNode::add_init_callback(&gdsharp_editor_init_callback); + + GLOBAL_DEF("mono/export/include_scripts_content", true); #endif } @@ -280,6 +282,15 @@ void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("@\" \""); // verbatim string literal } +static String get_base_class_name(const String &p_base_class_name, const String p_class_name) { + + String base_class = p_base_class_name; + if (p_class_name == base_class) { + base_class = "Godot." + base_class; + } + return base_class; +} + Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { String script_template = "using " BINDINGS_NAMESPACE ";\n" @@ -306,7 +317,8 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin "// }\n" "}\n"; - script_template = script_template.replace("%BASE_CLASS_NAME%", p_base_class_name) + String base_class_name = get_base_class_name(p_base_class_name, p_class_name); + script_template = script_template.replace("%BASE_CLASS_NAME%", base_class_name) .replace("%CLASS_NAME%", p_class_name); Ref<CSharpScript> script; @@ -325,12 +337,24 @@ bool CSharpLanguage::is_using_templates() { void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { String src = p_script->get_source_code(); - src = src.replace("%BASE%", p_base_class_name) + String base_class_name = get_base_class_name(p_base_class_name, p_class_name); + src = src.replace("%BASE%", base_class_name) .replace("%CLASS%", p_class_name) .replace("%TS%", _get_indentation()); p_script->set_source_code(src); } +String CSharpLanguage::validate_path(const String &p_path) const { + + String class_name = p_path.get_file().get_basename(); + List<String> keywords; + get_reserved_words(&keywords); + if (keywords.find(class_name)) { + return TTR("Class name can't be a reserved keyword"); + } + return ""; +} + Script *CSharpLanguage::create_script() const { return memnew(CSharpScript); @@ -454,7 +478,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() #ifdef DEBUG_ENABLED // Printing an error here will result in endless recursion, so we must be careful - if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated) + if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated) return Vector<StackInfo>(); MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr()); @@ -721,8 +745,10 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { 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->signals_invalidated = true; scr->exports_invalidated = true; scr->reload(p_soft_reload); + scr->update_signals(); scr->update_exports(); //restore state if saved @@ -755,8 +781,10 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { //if instance states were saved, set them! } - if (Engine::get_singleton()->is_editor_hint()) + if (Engine::get_singleton()->is_editor_hint()) { EditorNode::get_singleton()->get_property_editor()->update_tree(); + NodeDock::singleton->update_lists(); + } } #endif @@ -1545,6 +1573,74 @@ bool CSharpScript::_update_exports() { return false; } +bool CSharpScript::_update_signals() { + if (!valid) + return false; + + bool changed = false; + + if (signals_invalidated) { + signals_invalidated = false; + + GDMonoClass *top = script_class; + + _signals.clear(); + changed = true; // TODO Do a real check for change + + while (top && top != native) { + const Vector<GDMonoClass *> &delegates = top->get_all_delegates(); + for (int i = delegates.size() - 1; i >= 0; --i) { + Vector<Argument> parameters; + + GDMonoClass *delegate = delegates[i]; + + if (_get_signal(top, delegate, parameters)) { + _signals[delegate->get_name()] = parameters; + } + } + + top = top->get_parent_class(); + } + } + + return changed; +} + +bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms) { + if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) { + MonoType *raw_type = GDMonoClass::get_raw_type(p_delegate); + + if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) { + // Arguments are accessibles as arguments of .Invoke method + GDMonoMethod *invoke = p_delegate->get_method("Invoke", -1); + + Vector<StringName> names; + Vector<ManagedType> types; + invoke->get_parameter_names(names); + invoke->get_parameter_types(types); + + if (names.size() == types.size()) { + for (int i = 0; i < names.size(); ++i) { + Argument arg; + arg.name = names[i]; + arg.type = GDMonoMarshal::managed_to_variant_type(types[i]); + + if (arg.type == Variant::NIL) { + ERR_PRINTS("Unknown type of signal parameter: " + arg.name + " in " + p_class->get_full_name()); + return false; + } + + params.push_back(arg); + } + + return true; + } + } + } + + return false; +} + #ifdef TOOLS_ENABLED bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { @@ -1866,12 +1962,15 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this)); placeholders.insert(si); _update_exports(); + _update_signals(); return si; #else return NULL; #endif } + update_signals(); + if (native) { String native_name = native->get_name(); if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) { @@ -2035,6 +2134,31 @@ void CSharpScript::update_exports() { #endif } +bool CSharpScript::has_script_signal(const StringName &p_signal) const { + if (_signals.has(p_signal)) + return true; + + return false; +} + +void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const { + for (const Map<StringName, Vector<Argument> >::Element *E = _signals.front(); E; E = E->next()) { + MethodInfo mi; + + mi.name = E->key(); + for (int i = 0; i < E->get().size(); i++) { + PropertyInfo arg; + arg.name = E->get()[i].name; + mi.arguments.push_back(arg); + } + r_signals->push_back(mi); + } +} + +void CSharpScript::update_signals() { + _update_signals(); +} + Ref<Script> CSharpScript::get_base_script() const { // TODO search in metadata file once we have it, not important any way? @@ -2099,6 +2223,7 @@ CSharpScript::CSharpScript() : #ifdef TOOLS_ENABLED source_changed_cache = false; exports_invalidated = true; + signals_invalidated = true; #endif _resource_path_changed(); diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index f18e339e18..8666149111 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -85,13 +85,20 @@ class CSharpScript : public Script { SelfList<CSharpScript> script_list; + struct Argument { + String name; + Variant::Type type; + }; + + Map<StringName, Vector<Argument> > _signals; + bool signals_invalidated; + #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 @@ -104,6 +111,9 @@ class CSharpScript : public Script { void _clear(); + bool _update_signals(); + bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms); + bool _update_exports(); #ifdef TOOLS_ENABLED bool _get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported); @@ -137,8 +147,9 @@ public: 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 {} + virtual bool has_script_signal(const StringName &p_signal) const; + virtual void get_script_signal_list(List<MethodInfo> *r_signals) const; + virtual void update_signals(); /* 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; @@ -283,6 +294,7 @@ public: virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); /* 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 String validate_path(const String &p_path) const; virtual Script *create_script() const; virtual bool has_named_classes() const; virtual bool supports_builtin_mode() const; diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml index 0f33c76eb2..082bc30fd8 100644 --- a/modules/mono/doc_classes/@C#.xml +++ b/modules/mono/doc_classes/@C#.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="@C#" category="Core" version="3.0-stable"> +<class name="@C#" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index 3efe71f1b3..a1f7399653 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSharpScript" inherits="Script" category="Core" version="3.0-stable"> +<class name="CSharpScript" inherits="Script" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml index 1e5edf2a2a..985c66464b 100644 --- a/modules/mono/doc_classes/GodotSharp.xml +++ b/modules/mono/doc_classes/GodotSharp.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GodotSharp" inherits="Object" category="Core" version="3.0-stable"> +<class name="GodotSharp" inherits="Object" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 62c7a94755..a210b8e480 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -70,8 +70,6 @@ #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" @@ -105,6 +103,8 @@ #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" +#define BINDINGS_GENERATOR_VERSION UINT32_C(2) + const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n"; bool BindingsGenerator::verbose_output = false; @@ -529,7 +529,15 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo "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); + cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK); + + cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = "); + cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n"); + cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = "); + cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); + cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = "); + cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n"); + cs_icalls_content.push_back("\n"); #define ADD_INTERNAL_CALL(m_icall) \ if (!m_icall.editor_only) { \ @@ -551,7 +559,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); - String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS ".cs"); + String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS ".cs"); Error err = _save_file(internal_methods_file, cs_icalls_content); if (err != OK) @@ -626,7 +634,15 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, "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); + cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK); + + cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = "); + cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n"); + cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = "); + cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); + cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = "); + cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n"); + cs_icalls_content.push_back("\n"); #define ADD_INTERNAL_CALL(m_icall) \ if (m_icall.editor_only) { \ @@ -648,7 +664,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); - String internal_methods_file = path_join(core_dir, CS_CLASS_NATIVECALLS_EDITOR ".cs"); + String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs"); Error err = _save_file(internal_methods_file, cs_icalls_content); if (err != OK) @@ -714,7 +730,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } output.push_back(INDENT1 "public "); - output.push_back(itype.is_singleton ? "static class " : "class "); + bool is_abstract = !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled' + output.push_back(itype.is_singleton ? "static class " : (is_abstract ? "abstract class " : "class ")); output.push_back(itype.proxy_name); if (itype.is_singleton) { @@ -882,7 +899,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str 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(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); output.push_back("." ICALL_PREFIX); output.push_back(itype.name); output.push_back(SINGLETON_ICALL_SUFFIX "();\n"); @@ -912,7 +929,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str // 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(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); output.push_back("." + ctor_method); output.push_back("(this);\n" CLOSE_BLOCK_L2); } else { @@ -956,7 +973,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str "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 + " = false;\n" INDENT5 BINDINGS_CLASS_NATIVECALLS "." ICALL_OBJECT_DTOR "(this, " BINDINGS_PTR_FIELD ");\n" CLOSE_BLOCK_L4 CLOSE_BLOCK_L3 INDENT3 "this." BINDINGS_PTR_FIELD " = IntPtr.Zero;\n" INDENT3 "GC.SuppressFinalize(this);\n" INDENT3 "disposed = true;\n" CLOSE_BLOCK_L2); @@ -1229,7 +1246,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf { if (p_itype.is_object_type && !p_imethod.is_virtual && !p_imethod.requires_object_call) { p_output.push_back(MEMBER_BEGIN "private static IntPtr "); - p_output.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); + p_output.push_back(method_bind_field + " = " BINDINGS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); p_output.push_back(p_imethod.name); p_output.push_back("\");\n"); } @@ -1310,7 +1327,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf const InternalCall *im_icall = match->value(); - String im_call = im_icall->editor_only ? CS_CLASS_NATIVECALLS_EDITOR : CS_CLASS_NATIVECALLS; + String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS; im_call += "." + im_icall->name + "(" + icall_params + ");\n"; if (p_imethod.arguments.size()) @@ -1400,25 +1417,33 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { } 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(String::num_uint64(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()) + + output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "; }\n#endif // TOOLS_ENABLED\n"); + + output.push_back("uint32_t get_bindings_version() { return "); + output.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n"); + output.push_back("uint32_t get_cs_glue_version() { return "); + output.push_back(String::num_uint64(CS_GLUE_VERSION) + "; }\n"); + output.push_back("void register_generated_icalls() " OPEN_BLOCK); output.push_back("\tgodot_register_header_icalls();"); -#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"); \ +#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 ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_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; @@ -1486,6 +1511,14 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { return OK; } +uint32_t BindingsGenerator::get_version() { + return BINDINGS_GENERATOR_VERSION; +} + +uint32_t BindingsGenerator::get_cs_glue_version() { + return CS_GLUE_VERSION; +} + Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) { FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE); @@ -1528,9 +1561,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte 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"; + c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set("; + c_in_statements += itos(i); + c_in_statements += sformat(", &%s_in);\n", c_param_name); } } else { if (i > 0) @@ -2231,7 +2264,8 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { "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); + "return NativeCalls." ICALL_PREFIX "NodePath_operator_String(NodePath." CS_SMETHOD_GETINSTANCE "(from));\n" CLOSE_BLOCK_L2 + MEMBER_BEGIN "public override string ToString()\n" OPEN_BLOCK_L2 "return (string)this;\n" CLOSE_BLOCK_L2); builtin_types.insert(itype.cname, itype); // RID @@ -2365,7 +2399,7 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant:: imethod.name = mi.name; imethod.cname = imethod.name; - imethod.proxy_name = mi.name; + imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(mi.name)); for (int i = 0; i < mi.arguments.size(); i++) { ArgumentInterface iarg; diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 9b5a9cea88..f6194139af 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -536,6 +536,9 @@ public: 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 uint32_t get_version(); + static uint32_t get_cs_glue_version(); + void initialize(); _FORCE_INLINE_ static BindingsGenerator *get_singleton() { diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 6b41b10981..2f2b5768db 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -33,7 +33,6 @@ #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" @@ -178,13 +177,15 @@ bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_s return true; } -bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name) { +bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) { 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)) { + if (!FileAccess::exists(assembly_dst) || + FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) || + GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) { DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String xml_file = p_assembly_name + ".xml"; @@ -200,36 +201,49 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String & memdelete(da); if (err != OK) { - show_build_error_dialog("Failed to copy " API_ASSEMBLY_NAME ".dll"); + show_build_error_dialog("Failed to copy " + assembly_file); return false; } + + GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false); } return true; } -bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) { +String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) { + + uint64_t api_hash = p_api_type == APIAssembly::API_CORE ? + GDMono::get_singleton()->get_api_core_hash() : + GDMono::get_singleton()->get_api_editor_hash(); + return String::num_uint64(api_hash) + + "_" + String::num_uint64(BindingsGenerator::get_version()) + + "_" + String::num_uint64(BindingsGenerator::get_cs_glue_version()); +} + +bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) { - String api_name = p_api_type == API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; + String api_name = p_api_type == APIAssembly::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 core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir() + .plus_file(_api_folder_name(APIAssembly::API_CORE)) + .plus_file(API_ASSEMBLY_NAME); + String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir() + .plus_file(_api_folder_name(APIAssembly::API_EDITOR)) + .plus_file(EDITOR_API_ASSEMBLY_NAME); - String api_sln_dir = p_api_type == API_CORE ? core_api_sln_dir : editor_api_sln_dir; + String api_sln_dir = p_api_type == APIAssembly::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) { + if (p_api_type == APIAssembly::API_EDITOR) { core_api_assembly = core_api_sln_dir.plus_file("bin") .plus_file(api_build_config) .plus_file(API_ASSEMBLY_NAME ".dll"); @@ -242,7 +256,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) { BindingsGenerator *gen = BindingsGenerator::get_singleton(); bool gen_verbose = OS::get_singleton()->is_stdout_verbose(); - Error err = p_api_type == API_CORE ? + Error err = p_api_type == APIAssembly::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); @@ -275,7 +289,7 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) { // 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)) + if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type)) return false; pr.step("Done"); @@ -283,22 +297,22 @@ bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) { return true; } -bool GodotSharpBuilds::build_project_blocking() { +bool GodotSharpBuilds::build_project_blocking(const String &p_config) { if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path())) return true; // No solution to build - if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE)) + if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE)) return false; - if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR)) + if (!GodotSharpBuilds::make_api_sln(APIAssembly::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"); + MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config); if (!GodotSharpBuilds::get_singleton()->build(build_info)) { GodotSharpBuilds::show_build_error_dialog("Failed to build project solution"); return false; @@ -309,6 +323,11 @@ bool GodotSharpBuilds::build_project_blocking() { return true; } +bool GodotSharpBuilds::editor_build_callback() { + + return build_project_blocking("Tools"); +} + GodotSharpBuilds *GodotSharpBuilds::singleton = NULL; void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) { @@ -362,7 +381,7 @@ GodotSharpBuilds::GodotSharpBuilds() { singleton = this; - EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::build_project_blocking); + EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::editor_build_callback); // Build tool settings EditorSettings *ed_settings = EditorSettings::get_singleton(); @@ -462,6 +481,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { if (ex) { exited = true; + GDMonoUtils::print_unhandled_exception(ex); 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); @@ -482,6 +502,7 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { if (ex) { exited = true; + GDMonoUtils::print_unhandled_exception(ex); 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); @@ -504,11 +525,10 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { } } -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; +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 index 5d2390ecd9..27b771e324 100644 --- a/modules/mono/editor/godotsharp_builds.h +++ b/modules/mono/editor/godotsharp_builds.h @@ -31,6 +31,7 @@ #ifndef GODOTSHARP_BUILDS_H #define GODOTSHARP_BUILDS_H +#include "../mono_gd/gd_mono.h" #include "mono_bottom_panel.h" #include "mono_build_info.h" @@ -56,17 +57,14 @@ private: HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds; + static String _api_folder_name(APIAssembly::Type p_api_type); + 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 @@ -89,11 +87,13 @@ public: 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 copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type); + + static bool make_api_sln(APIAssembly::Type p_api_type); - static bool make_api_sln(APIType p_api_type); + static bool build_project_blocking(const String &p_config); - static bool build_project_blocking(); + static bool editor_build_callback(); GodotSharpBuilds(); ~GodotSharpBuilds(); diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index 0ef3adfdd0..998da8bda3 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -41,6 +41,7 @@ #include "../utils/path_utils.h" #include "bindings_generator.h" #include "csharp_project.h" +#include "godotsharp_export.h" #include "net_solution.h" #ifdef WINDOWS_ENABLED @@ -84,10 +85,10 @@ bool GodotSharpEditor::_create_project_solution() { return false; } - if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE)) + if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE)) return false; - if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR)) + if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR)) return false; pr.step(TTR("Done")); @@ -278,10 +279,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { String about_text = String("C# support in Godot Engine is a brand new feature and a work in progress.\n") + "It is at the alpha stage and thus not suitable for use in production.\n\n" + - "As of Godot 3.0, C# support is not feature-complete and can crash in some situations. " + - "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases.\n" + + "As of Godot 3.0, C# support is not feature-complete and may crash in some situations. " + + "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases, " + + "including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" + "The main missing feature is the ability to export games using C# assemblies - you will therefore be able to develop and run games in the editor, " + - "but not to share them as standalone binaries. This feature is of course high on the priority list and should be available in 3.0.1.\n\n" + + "but not to share them as standalone binaries yet. This feature is of course high on the priority list and should be available as soon as possible.\n\n" + "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc.:\n\n" + " https://github.com/godotengine/godot/issues\n\n" + "Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!"; @@ -315,6 +317,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { EditorSettings *ed_settings = EditorSettings::get_singleton(); EDITOR_DEF("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")); + + // Export plugin + Ref<GodotSharpExport> godotsharp_export; + godotsharp_export.instance(); + EditorExport::get_singleton()->add_export_plugin(godotsharp_export); } GodotSharpEditor::~GodotSharpEditor() { diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h index 81c49aec30..66da814c8b 100644 --- a/modules/mono/editor/godotsharp_editor.h +++ b/modules/mono/editor/godotsharp_editor.h @@ -32,7 +32,6 @@ #define GODOTSHARP_EDITOR_H #include "godotsharp_builds.h" - #include "monodevelop_instance.h" class GodotSharpEditor : public Node { diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp new file mode 100644 index 0000000000..cd09e6516a --- /dev/null +++ b/modules/mono/editor/godotsharp_export.cpp @@ -0,0 +1,166 @@ +/*************************************************************************/ +/* godotsharp_export.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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_export.h" + +#include "../csharp_script.h" +#include "../godotsharp_defs.h" +#include "../godotsharp_dirs.h" +#include "godotsharp_builds.h" + +void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) { + + if (p_type != CSharpLanguage::get_singleton()->get_type()) + return; + + ERR_FAIL_COND(p_path.get_extension() != CSharpLanguage::get_singleton()->get_extension()); + + // TODO what if the source file is not part of the game's C# project + + if (!GLOBAL_GET("mono/export/include_scripts_content")) { + // We don't want to include the source code on exported games + add_file(p_path, Vector<uint8_t>(), false); + skip(); + } +} + +void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags) { + + // TODO right now there is no way to stop the export process with an error + + ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized()); + ERR_FAIL_NULL(GDMono::get_singleton()->get_tools_domain()); + + String build_config = p_debug ? "Debug" : "Release"; + + ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config)); + + // Add API assemblies + + String core_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll"); + ERR_FAIL_COND(!_add_assembly(core_api_dll_path, core_api_dll_path)); + + String editor_api_dll_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); + ERR_FAIL_COND(!_add_assembly(editor_api_dll_path, editor_api_dll_path)); + + // Add project assembly + + String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name"); + if (project_dll_name.empty()) { + project_dll_name = "UnnamedProject"; + } + + String project_dll_src_path = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config).plus_file(project_dll_name + ".dll"); + String project_dll_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(project_dll_name + ".dll"); + ERR_FAIL_COND(!_add_assembly(project_dll_src_path, project_dll_dst_path)); + + // Add dependencies + + MonoDomain *prev_domain = mono_domain_get(); + MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain"); + + ERR_FAIL_COND(!export_domain); + ERR_FAIL_COND(!mono_domain_set(export_domain, false)); + + Map<String, String> dependencies; + dependencies.insert("mscorlib", GDMono::get_singleton()->get_corlib_assembly()->get_path()); + + GDMonoAssembly *scripts_assembly = GDMonoAssembly::load_from(project_dll_name, project_dll_src_path, /* refonly: */ true); + + ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name); + ERR_FAIL_COND(!scripts_assembly); + + Error depend_error = _get_assembly_dependencies(scripts_assembly, dependencies); + + GDMono::get_singleton()->finalize_and_unload_domain(export_domain); + mono_domain_set(prev_domain, false); + + ERR_FAIL_COND(depend_error != OK); + + for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) { + String depend_src_path = E->value(); + String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file()); + ERR_FAIL_COND(!_add_assembly(depend_src_path, depend_dst_path)); + } +} + +bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_dst_path) { + + FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ); + ERR_FAIL_COND_V(!f, false); + + Vector<uint8_t> data; + data.resize(f->get_len()); + f->get_buffer(data.ptrw(), data.size()); + + add_file(p_dst_path, data, false); + + return true; +} + +Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies) { + + MonoImage *image = p_assembly->get_image(); + + for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) { + MonoAssemblyName *ref_aname = aname_prealloc; + mono_assembly_get_assemblyref(image, i, ref_aname); + String ref_name = mono_assembly_name_get_name(ref_aname); + + if (ref_name == "mscorlib" || r_dependencies.find(ref_name)) + continue; + + GDMonoAssembly *ref_assembly = NULL; + if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true)) { + ERR_EXPLAIN("Cannot load refonly assembly: " + ref_name); + ERR_FAIL_V(ERR_CANT_RESOLVE); + } + + r_dependencies.insert(ref_name, ref_assembly->get_path()); + + Error err = _get_assembly_dependencies(ref_assembly, r_dependencies); + if (err != OK) + return err; + } + + return OK; +} + +GodotSharpExport::GodotSharpExport() { + // MonoAssemblyName is an incomplete type (internal to mono), so we can't allocate it ourselves. + // There isn't any api to allocate an empty one either, so we need to do it this way. + aname_prealloc = mono_assembly_name_new("whatever"); + mono_assembly_name_free(aname_prealloc); // "it does not frees the object itself, only the name members" (typo included) +} + +GodotSharpExport::~GodotSharpExport() { + if (aname_prealloc) + mono_free(aname_prealloc); +} diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h new file mode 100644 index 0000000000..b38db9660c --- /dev/null +++ b/modules/mono/editor/godotsharp_export.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* godotsharp_export.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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_EXPORT_H +#define GODOTSHARP_EXPORT_H + +#include <mono/metadata/image.h> + +#include "editor/editor_export.h" + +#include "../mono_gd/gd_mono_header.h" + +class GodotSharpExport : public EditorExportPlugin { + + MonoAssemblyName *aname_prealloc; + + bool _add_assembly(const String &p_src_path, const String &p_dst_path); + + Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, Map<String, String> &r_dependencies); + +protected: + virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features); + virtual void _export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags); + +public: + GodotSharpExport(); + ~GodotSharpExport(); +}; + +#endif // GODOTSHARP_EXPORT_H diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp index 43689548b5..f1cf0bcdf5 100644 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -142,7 +142,7 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) { void MonoBottomPanel::_build_project_pressed() { - GodotSharpBuilds::get_singleton()->build_project_blocking(); + GodotSharpBuilds::get_singleton()->build_project_blocking("Tools"); MonoReloadNode::get_singleton()->restart_reload_timer(); CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true); @@ -197,7 +197,7 @@ MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) { toolbar_hbc->set_h_size_flags(SIZE_EXPAND_FILL); panel_builds_tab->add_child(toolbar_hbc); - ToolButton *build_project_btn = memnew(ToolButton); + Button *build_project_btn = memnew(Button); build_project_btn->set_text(TTR("Build Project")); build_project_btn->set_focus_mode(FOCUS_NONE); build_project_btn->connect("pressed", this, "_build_project_pressed"); @@ -335,16 +335,14 @@ void MonoBuildTab::_update_issues_list() { 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"); + return get_icon("StatusError", "EditorIcons"); } else { - return get_icon("DependencyOkHl", "EditorIcons"); + return get_icon("StatusSuccess", "EditorIcons"); } } else { - return get_icon("GraphTime", "EditorIcons"); + return get_icon("Stop", "EditorIcons"); } } @@ -439,21 +437,16 @@ 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); +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)), + error_count(0), + warning_count(0), + errors_visible(true), + warnings_visible(true) { 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/glue/cs_files/AABB.cs b/modules/mono/glue/cs_files/AABB.cs index e6e12f7ba3..25458c93e1 100644 --- a/modules/mono/glue/cs_files/AABB.cs +++ b/modules/mono/glue/cs_files/AABB.cs @@ -7,6 +7,12 @@ using System; // file: core/variant_call.cpp // commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + namespace Godot { public struct AABB : IEquatable<AABB> @@ -75,7 +81,7 @@ namespace Godot return new AABB(begin, end - begin); } - public float GetArea() + public real_t GetArea() { return size.x * size.y * size.z; } @@ -108,7 +114,7 @@ namespace Godot public Vector3 GetLongestAxis() { Vector3 axis = new Vector3(1f, 0f, 0f); - float max_size = size.x; + real_t max_size = size.x; if (size.y > max_size) { @@ -128,7 +134,7 @@ namespace Godot public Vector3.Axis GetLongestAxisIndex() { Vector3.Axis axis = Vector3.Axis.X; - float max_size = size.x; + real_t max_size = size.x; if (size.y > max_size) { @@ -145,9 +151,9 @@ namespace Godot return axis; } - public float GetLongestAxisSize() + public real_t GetLongestAxisSize() { - float max_size = size.x; + real_t max_size = size.x; if (size.y > max_size) max_size = size.y; @@ -161,7 +167,7 @@ namespace Godot public Vector3 GetShortestAxis() { Vector3 axis = new Vector3(1f, 0f, 0f); - float max_size = size.x; + real_t max_size = size.x; if (size.y < max_size) { @@ -181,7 +187,7 @@ namespace Godot public Vector3.Axis GetShortestAxisIndex() { Vector3.Axis axis = Vector3.Axis.X; - float max_size = size.x; + real_t max_size = size.x; if (size.y < max_size) { @@ -198,9 +204,9 @@ namespace Godot return axis; } - public float GetShortestAxisSize() + public real_t GetShortestAxisSize() { - float max_size = size.x; + real_t max_size = size.x; if (size.y < max_size) max_size = size.y; @@ -222,7 +228,7 @@ namespace Godot (dir.z > 0f) ? -half_extents.z : half_extents.z); } - public AABB Grow(float by) + public AABB Grow(real_t by) { AABB res = this; @@ -354,23 +360,23 @@ namespace Godot public bool IntersectsSegment(Vector3 from, Vector3 to) { - float min = 0f; - float max = 1f; + real_t min = 0f; + real_t 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; + real_t seg_from = from[i]; + real_t seg_to = to[i]; + real_t box_begin = position[i]; + real_t box_end = box_begin + size[i]; + real_t cmin, cmax; if (seg_from < seg_to) { if (seg_from > box_end || seg_to < box_begin) return false; - float length = seg_to - seg_from; + real_t 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; } @@ -379,7 +385,7 @@ namespace Godot if (seg_to > box_end || seg_from < box_begin) return false; - float length = seg_to - seg_from; + real_t 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; } @@ -419,7 +425,8 @@ namespace Godot return new AABB(min, max - min); } - + + // Constructors public AABB(Vector3 position, Vector3 size) { this.position = position; diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs index ea92b1641b..2e7e5404c4 100644 --- a/modules/mono/glue/cs_files/Basis.cs +++ b/modules/mono/glue/cs_files/Basis.cs @@ -1,6 +1,12 @@ using System; using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + namespace Godot { [StructLayout(LayoutKind.Sequential)] @@ -41,9 +47,27 @@ namespace Godot new Basis(0f, -1f, 0f, 0f, 0f, -1f, 1f, 0f, 0f) }; - public Vector3 x; - public Vector3 y; - public Vector3 z; + public Vector3 x + { + get { return GetAxis(0); } + set { SetAxis(0, value); } + } + + public Vector3 y + { + get { return GetAxis(1); } + set { SetAxis(1, value); } + } + + public Vector3 z + { + get { return GetAxis(2); } + set { SetAxis(2, value); } + } + + private Vector3 _x; + private Vector3 _y; + private Vector3 _z; public static Basis Identity { @@ -70,11 +94,11 @@ namespace Godot switch (index) { case 0: - return x; + return _x; case 1: - return y; + return _y; case 2: - return z; + return _z; default: throw new IndexOutOfRangeException(); } @@ -84,13 +108,13 @@ namespace Godot switch (index) { case 0: - x = value; + _x = value; return; case 1: - y = value; + _y = value; return; case 2: - z = value; + _z = value; return; default: throw new IndexOutOfRangeException(); @@ -98,18 +122,18 @@ namespace Godot } } - public float this[int index, int axis] + public real_t this[int index, int axis] { get { switch (index) { case 0: - return x[axis]; + return _x[axis]; case 1: - return y[axis]; + return _y[axis]; case 2: - return z[axis]; + return _z[axis]; default: throw new IndexOutOfRangeException(); } @@ -119,13 +143,13 @@ namespace Godot switch (index) { case 0: - x[axis] = value; + _x[axis] = value; return; case 1: - y[axis] = value; + _y[axis] = value; return; case 2: - z[axis] = value; + _z[axis] = value; return; default: throw new IndexOutOfRangeException(); @@ -143,7 +167,7 @@ namespace Godot ); } - public float Determinant() + public real_t 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]) + @@ -155,6 +179,13 @@ namespace Godot return new Vector3(this[0, axis], this[1, axis], this[2, axis]); } + public void SetAxis(int axis, Vector3 value) + { + this[0, axis] = value.x; + this[1, axis] = value.y; + this[2, axis] = value.z; + } + public Vector3 GetEuler() { Basis m = this.Orthonormalized(); @@ -162,7 +193,7 @@ namespace Godot Vector3 euler; euler.z = 0.0f; - float mxy = m.y[2]; + real_t mxy = m[1, 2]; if (mxy < 1.0f) @@ -170,19 +201,19 @@ namespace Godot 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]); + euler.y = Mathf.Atan2(m[0, 2], m[2, 2]); + euler.z = Mathf.Atan2(m[1, 0], m[1, 1]); } else { euler.x = Mathf.PI * 0.5f; - euler.y = -Mathf.Atan2(-m.x[1], m.x[0]); + euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]); } } else { euler.x = -Mathf.PI * 0.5f; - euler.y = -Mathf.Atan2(m.x[1], m.x[0]); + euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]); } return euler; @@ -196,7 +227,7 @@ namespace Godot { for (int j = 0; j < 3; j++) { - float v = orth[i, j]; + real_t v = orth[i, j]; if (v > 0.5f) v = 1.0f; @@ -222,26 +253,26 @@ namespace Godot { Basis inv = this; - float[] co = new float[3] + real_t[] co = new real_t[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]; + real_t 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 + real_t.NaN, real_t.NaN, real_t.NaN, + real_t.NaN, real_t.NaN, real_t.NaN, + real_t.NaN, real_t.NaN, real_t.NaN ); } - float s = 1.0f / det; + real_t s = 1.0f / det; inv = new Basis ( @@ -274,7 +305,7 @@ namespace Godot return Basis.CreateFromAxes(xAxis, yAxis, zAxis); } - public Basis Rotated(Vector3 axis, float phi) + public Basis Rotated(Vector3 axis, real_t phi) { return new Basis(axis, phi) * this; } @@ -296,17 +327,17 @@ namespace Godot return m; } - public float Tdotx(Vector3 with) + public real_t Tdotx(Vector3 with) { return this[0, 0] * with[0] + this[1, 0] * with[1] + this[2, 0] * with[2]; } - public float Tdoty(Vector3 with) + public real_t Tdoty(Vector3 with) { return this[0, 1] * with[0] + this[1, 1] * with[1] + this[2, 1] * with[2]; } - public float Tdotz(Vector3 with) + public real_t Tdotz(Vector3 with) { return this[0, 2] * with[0] + this[1, 2] * with[1] + this[2, 2] * with[2]; } @@ -315,7 +346,7 @@ namespace Godot { Basis tr = this; - float temp = this[0, 1]; + real_t temp = this[0, 1]; this[0, 1] = this[1, 0]; this[1, 0] = temp; @@ -351,91 +382,91 @@ namespace Godot } public Quat Quat() { - float trace = x[0] + y[1] + z[2]; + real_t trace = _x[0] + _y[1] + _z[2]; if (trace > 0.0f) { - float s = Mathf.Sqrt(trace + 1.0f) * 2f; - float inv_s = 1f / s; + real_t s = Mathf.Sqrt(trace + 1.0f) * 2f; + real_t 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, + (_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; + } else if (_x[0] > _y[1] && _x[0] > _z[2]) { + real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f; + real_t 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 + (_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; + } else if (_y[1] > _z[2]) { + real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; return new Quat( - (x[1] + y[0]) * inv_s, + (_x[1] + _y[0]) * inv_s, s * 0.25f, - (y[2] + z[1]) * inv_s, - (x[2] - z[0]) * inv_s + (_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; + real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; return new Quat( - (x[2] + z[0]) * inv_s, - (y[2] + z[1]) * inv_s, + (_x[2] + _z[0]) * inv_s, + (_y[2] + _z[1]) * inv_s, s * 0.25f, - (y[0] - x[1]) * inv_s + (_y[0] - _x[1]) * inv_s ); } } public Basis(Quat quat) { - float s = 2.0f / quat.LengthSquared(); - - 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)); + real_t s = 2.0f / quat.LengthSquared(); + + real_t xs = quat.x * s; + real_t ys = quat.y * s; + real_t zs = quat.z * s; + real_t wx = quat.w * xs; + real_t wy = quat.w * ys; + real_t wz = quat.w * zs; + real_t xx = quat.x * xs; + real_t xy = quat.x * ys; + real_t xz = quat.x * zs; + real_t yy = quat.y * ys; + real_t yz = quat.y * zs; + real_t 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) + public Basis(Vector3 axis, real_t 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); + real_t cosine = Mathf.Cos( (real_t)phi); + real_t sine = Mathf.Sin( (real_t)phi); - this.x = new Vector3 + 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 + 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 + this._z = new Vector3 ( axis.z * axis.x * (1.0f - cosine) - axis.y * sine, axis.y * axis.z * (1.0f - cosine) + axis.x * sine, @@ -445,16 +476,16 @@ namespace Godot public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) { - this.x = xAxis; - this.y = yAxis; - this.z = 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) + public Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { - this.x = new Vector3(xx, xy, xz); - this.y = new Vector3(yx, yy, yz); - this.z = new Vector3(zx, zy, 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) @@ -489,21 +520,21 @@ namespace Godot public bool Equals(Basis other) { - return x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z); + return _x.Equals(other[0]) && _y.Equals(other[1]) && _z.Equals(other[2]); } public override int GetHashCode() { - return x.GetHashCode() ^ y.GetHashCode() ^ z.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() + this._x.ToString(), + this._y.ToString(), + this._z.ToString() }); } @@ -511,9 +542,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(format), - this.y.ToString(format), - this.z.ToString(format) + 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 index f9e31e9703..aa42c487c8 100644 --- a/modules/mono/glue/cs_files/Color.cs +++ b/modules/mono/glue/cs_files/Color.cs @@ -45,8 +45,8 @@ namespace Godot { get { - float max = Mathf.Max(r, Mathf.Max(g, b)); - float min = Mathf.Min(r, Mathf.Min(g, b)); + float max = (float) Mathf.Max(r, (float) Mathf.Max(g, b)); + float min = (float) Mathf.Min(r, (float) Mathf.Min(g, b)); float delta = max - min; @@ -79,8 +79,8 @@ namespace Godot { get { - float max = Mathf.Max(r, Mathf.Max(g, b)); - float min = Mathf.Min(r, Mathf.Min(g, b)); + float max = (float) Mathf.Max(r, (float) Mathf.Max(g, b)); + float min = (float) Mathf.Min(r, (float) Mathf.Min(g, b)); float delta = max - min; @@ -96,7 +96,7 @@ namespace Godot { get { - return Mathf.Max(r, Mathf.Max(g, b)); + return (float) Mathf.Max(r, (float) Mathf.Max(g, b)); } set { @@ -316,7 +316,8 @@ namespace Godot return txt; } - + + // Constructors public Color(float r, float g, float b, float a = 1.0f) { this.r = r; @@ -375,7 +376,7 @@ namespace Godot private String _to_hex(float val) { - int v = (int)Mathf.Clamp(val * 255.0f, 0, 255); + int v = (int) Mathf.Clamp(val * 255.0f, 0, 255); string ret = string.Empty; diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs index b335ef55e4..1ee7e7d21c 100644 --- a/modules/mono/glue/cs_files/GD.cs +++ b/modules/mono/glue/cs_files/GD.cs @@ -1,5 +1,7 @@ using System; +// TODO: Add comments describing what this class does. It is not obvious. + namespace Godot { public static partial class GD diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs index 476396e9a3..adbcc855ef 100644 --- a/modules/mono/glue/cs_files/Mathf.cs +++ b/modules/mono/glue/cs_files/Mathf.cs @@ -1,51 +1,63 @@ using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + namespace Godot { public static class Mathf { - public const float PI = 3.14159274f; - public const float Epsilon = 1e-06f; + // Define constants with Decimal precision and cast down to double or float. + public const real_t PI = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979 + + #if REAL_T_IS_DOUBLE + public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used. + #else + public const real_t Epsilon = 1e-06f; + #endif - private const float Deg2RadConst = 0.0174532924f; - private const float Rad2DegConst = 57.29578f; + private const real_t Deg2RadConst = (real_t) 0.0174532925199432957692369077M; // 0.0174532924f and 0.0174532925199433 + private const real_t Rad2DegConst = (real_t) 57.295779513082320876798154814M; // 57.29578f and 57.2957795130823 - public static float Abs(float s) + public static real_t Abs(real_t s) { return Math.Abs(s); } - public static float Acos(float s) + public static real_t Acos(real_t s) { - return (float)Math.Acos(s); + return (real_t)Math.Acos(s); } - public static float Asin(float s) + public static real_t Asin(real_t s) { - return (float)Math.Asin(s); + return (real_t)Math.Asin(s); } - public static float Atan(float s) + public static real_t Atan(real_t s) { - return (float)Math.Atan(s); + return (real_t)Math.Atan(s); } - public static float Atan2(float x, float y) + public static real_t Atan2(real_t x, real_t y) { - return (float)Math.Atan2(x, y); + return (real_t)Math.Atan2(x, y); } - public static Vector2 Cartesian2Polar(float x, float y) - { - return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); - } + public static Vector2 Cartesian2Polar(real_t x, real_t y) + { + return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); + } - public static float Ceil(float s) + public static real_t Ceil(real_t s) { - return (float)Math.Ceiling(s); + return (real_t)Math.Ceiling(s); } - public static float Clamp(float val, float min, float max) + public static real_t Clamp(real_t val, real_t min, real_t max) { if (val < min) { @@ -59,17 +71,17 @@ namespace Godot return val; } - public static float Cos(float s) + public static real_t Cos(real_t s) { - return (float)Math.Cos(s); + return (real_t)Math.Cos(s); } - public static float Cosh(float s) + public static real_t Cosh(real_t s) { - return (float)Math.Cosh(s); + return (real_t)Math.Cosh(s); } - public static int Decimals(float step) + public static int Decimals(real_t step) { return Decimals((decimal)step); } @@ -79,12 +91,12 @@ namespace Godot return BitConverter.GetBytes(decimal.GetBits(step)[3])[2]; } - public static float Deg2Rad(float deg) + public static real_t Deg2Rad(real_t deg) { return deg * Deg2RadConst; } - public static float Ease(float s, float curve) + public static real_t Ease(real_t s, real_t curve) { if (s < 0f) { @@ -117,17 +129,17 @@ namespace Godot return 0f; } - public static float Exp(float s) + public static real_t Exp(real_t s) { - return (float)Math.Exp(s); + return (real_t)Math.Exp(s); } - public static float Floor(float s) + public static real_t Floor(real_t s) { - return (float)Math.Floor(s); + return (real_t)Math.Floor(s); } - public static float Fposmod(float x, float y) + public static real_t Fposmod(real_t x, real_t y) { if (x >= 0f) { @@ -139,14 +151,14 @@ namespace Godot } } - public static float Lerp(float from, float to, float weight) + public static real_t Lerp(real_t from, real_t to, real_t weight) { return from + (to - from) * Clamp(weight, 0f, 1f); } - public static float Log(float s) + public static real_t Log(real_t s) { - return (float)Math.Log(s); + return (real_t)Math.Log(s); } public static int Max(int a, int b) @@ -154,7 +166,7 @@ namespace Godot return (a > b) ? a : b; } - public static float Max(float a, float b) + public static real_t Max(real_t a, real_t b) { return (a > b) ? a : b; } @@ -164,7 +176,7 @@ namespace Godot return (a < b) ? a : b; } - public static float Min(float a, float b) + public static real_t Min(real_t a, real_t b) { return (a < b) ? a : b; } @@ -181,47 +193,52 @@ namespace Godot return val; } - public static Vector2 Polar2Cartesian(float r, float th) - { - return new Vector2(r * Cos(th), r * Sin(th)); - } + public static Vector2 Polar2Cartesian(real_t r, real_t th) + { + return new Vector2(r * Cos(th), r * Sin(th)); + } - public static float Pow(float x, float y) + public static real_t Pow(real_t x, real_t y) { - return (float)Math.Pow(x, y); + return (real_t)Math.Pow(x, y); } - public static float Rad2Deg(float rad) + public static real_t Rad2Deg(real_t rad) { return rad * Rad2DegConst; } - public static float Round(float s) + public static real_t Round(real_t s) { - return (float)Math.Round(s); + return (real_t)Math.Round(s); } - public static float Sign(float s) + public static int RoundToInt(real_t s) + { + return (int)Math.Round(s); + } + + public static real_t Sign(real_t s) { return (s < 0f) ? -1f : 1f; } - public static float Sin(float s) + public static real_t Sin(real_t s) { - return (float)Math.Sin(s); + return (real_t)Math.Sin(s); } - public static float Sinh(float s) + public static real_t Sinh(real_t s) { - return (float)Math.Sinh(s); + return (real_t)Math.Sinh(s); } - public static float Sqrt(float s) + public static real_t Sqrt(real_t s) { - return (float)Math.Sqrt(s); + return (real_t)Math.Sqrt(s); } - public static float Stepify(float s, float step) + public static real_t Stepify(real_t s, real_t step) { if (step != 0f) { @@ -231,14 +248,26 @@ namespace Godot return s; } - public static float Tan(float s) + public static real_t Tan(real_t s) + { + return (real_t)Math.Tan(s); + } + + public static real_t Tanh(real_t s) + { + return (real_t)Math.Tanh(s); + } + + public static int Wrap(int val, int min, int max) { - return (float)Math.Tan(s); + int rng = max - min; + return min + ((((val - min) % rng) + rng) % rng); } - public static float Tanh(float s) + public static real_t Wrap(real_t val, real_t min, real_t max) { - return (float)Math.Tanh(s); + real_t rng = max - min; + return min + (val - min) - (rng * Floor((val - min) / rng)); } } } diff --git a/modules/mono/glue/cs_files/Plane.cs b/modules/mono/glue/cs_files/Plane.cs index b347c0835a..0f74f3b66a 100644 --- a/modules/mono/glue/cs_files/Plane.cs +++ b/modules/mono/glue/cs_files/Plane.cs @@ -1,12 +1,18 @@ using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + namespace Godot { public struct Plane : IEquatable<Plane> { Vector3 normal; - public float x + public real_t x { get { @@ -18,7 +24,7 @@ namespace Godot } } - public float y + public real_t y { get { @@ -30,7 +36,7 @@ namespace Godot } } - public float z + public real_t z { get { @@ -42,7 +48,7 @@ namespace Godot } } - float d; + real_t d; public Vector3 Center { @@ -52,7 +58,7 @@ namespace Godot } } - public float DistanceTo(Vector3 point) + public real_t DistanceTo(Vector3 point) { return normal.Dot(point) - d; } @@ -62,15 +68,15 @@ namespace Godot return normal * d; } - public bool HasPoint(Vector3 point, float epsilon = Mathf.Epsilon) + public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon) { - float dist = normal.Dot(point) - d; + real_t dist = normal.Dot(point) - d; return Mathf.Abs(dist) <= epsilon; } public Vector3 Intersect3(Plane b, Plane c) { - float denom = normal.Cross(b.normal).Dot(c.normal); + real_t denom = normal.Cross(b.normal).Dot(c.normal); if (Mathf.Abs(denom) <= Mathf.Epsilon) return new Vector3(); @@ -84,12 +90,12 @@ namespace Godot public Vector3 IntersectRay(Vector3 from, Vector3 dir) { - float den = normal.Dot(dir); + real_t den = normal.Dot(dir); if (Mathf.Abs(den) <= Mathf.Epsilon) return new Vector3(); - float dist = (normal.Dot(from) - d) / den; + real_t dist = (normal.Dot(from) - d) / den; // This is a ray, before the emitting pos (from) does not exist if (dist > Mathf.Epsilon) @@ -101,12 +107,12 @@ namespace Godot public Vector3 IntersectSegment(Vector3 begin, Vector3 end) { Vector3 segment = begin - end; - float den = normal.Dot(segment); + real_t den = normal.Dot(segment); if (Mathf.Abs(den) <= Mathf.Epsilon) return new Vector3(); - float dist = (normal.Dot(begin) - d) / den; + real_t dist = (normal.Dot(begin) - d) / den; if (dist < -Mathf.Epsilon || dist > (1.0f + Mathf.Epsilon)) return new Vector3(); @@ -121,7 +127,7 @@ namespace Godot public Plane Normalized() { - float len = normal.Length(); + real_t len = normal.Length(); if (len == 0) return new Plane(0, 0, 0, 0); @@ -133,14 +139,14 @@ namespace Godot { return point - normal * DistanceTo(point); } - - public Plane(float a, float b, float c, float d) + + // Constructors + public Plane(real_t a, real_t b, real_t c, real_t d) { normal = new Vector3(a, b, c); this.d = d; } - - public Plane(Vector3 normal, float d) + public Plane(Vector3 normal, real_t d) { this.normal = normal; this.d = d; diff --git a/modules/mono/glue/cs_files/Quat.cs b/modules/mono/glue/cs_files/Quat.cs index c0ac41c5d7..0cf3e00ddb 100644 --- a/modules/mono/glue/cs_files/Quat.cs +++ b/modules/mono/glue/cs_files/Quat.cs @@ -1,6 +1,12 @@ using System; using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + namespace Godot { [StructLayout(LayoutKind.Sequential)] @@ -8,17 +14,17 @@ namespace Godot { private static readonly Quat identity = new Quat(0f, 0f, 0f, 1f); - public float x; - public float y; - public float z; - public float w; + public real_t x; + public real_t y; + public real_t z; + public real_t w; public static Quat Identity { get { return identity; } } - public float this[int index] + public real_t this[int index] { get { @@ -58,15 +64,15 @@ namespace Godot } } - public Quat CubicSlerp(Quat b, Quat preA, Quat postB, float t) + public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t) { - float t2 = (1.0f - t) * t * 2f; + real_t 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) + public real_t Dot(Quat b) { return x * b.x + y * b.y + z * b.z + w * b.w; } @@ -76,12 +82,12 @@ namespace Godot return new Quat(-x, -y, -z, w); } - public float Length() + public real_t Length() { return Mathf.Sqrt(LengthSquared()); } - public float LengthSquared() + public real_t LengthSquared() { return Dot(this); } @@ -91,20 +97,27 @@ namespace Godot return this / Length(); } - public void Set(float x, float y, float z, float w) + public void Set(real_t x, real_t y, real_t z, real_t w) { this.x = x; this.y = y; this.z = z; this.w = w; } + public void Set(Quat q) + { + this.x = q.x; + this.y = q.y; + this.z = q.z; + this.w = q.w; + } - public Quat Slerp(Quat b, float t) + public Quat Slerp(Quat b, real_t t) { // Calculate cosine - float cosom = x * b.x + y * b.y + z * b.z + w * b.w; + real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w; - float[] to1 = new float[4]; + real_t[] to1 = new real_t[4]; // Adjust signs if necessary if (cosom < 0.0) @@ -122,13 +135,13 @@ namespace Godot to1[3] = b.w; } - float sinom, scale0, scale1; + real_t sinom, scale0, scale1; // Calculate coefficients if ((1.0 - cosom) > Mathf.Epsilon) { // Standard case (Slerp) - float omega = Mathf.Acos(cosom); + real_t omega = Mathf.Acos(cosom); sinom = Mathf.Sin(omega); scale0 = Mathf.Sin((1.0f - t) * omega) / sinom; scale1 = Mathf.Sin(t * omega) / sinom; @@ -150,19 +163,19 @@ namespace Godot ); } - public Quat Slerpni(Quat b, float t) + public Quat Slerpni(Quat b, real_t t) { - float dot = this.Dot(b); + real_t 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; + real_t theta = Mathf.Acos(dot); + real_t sinT = 1.0f / Mathf.Sin(theta); + real_t newFactor = Mathf.Sin(t * theta) * sinT; + real_t invFactor = Mathf.Sin((1.0f - t) * theta) * sinT; return new Quat ( @@ -180,17 +193,26 @@ namespace Godot return new Vector3(q.x, q.y, q.z); } - public Quat(float x, float y, float z, float w) + // Constructors + public Quat(real_t x, real_t y, real_t z, real_t w) { this.x = x; this.y = y; this.z = z; this.w = w; + } + public Quat(Quat q) + { + this.x = q.x; + this.y = q.y; + this.z = q.z; + this.w = q.w; } - - public Quat(Vector3 axis, float angle) + + public Quat(Vector3 axis, real_t angle) { - float d = axis.Length(); + real_t d = axis.Length(); + real_t angle_t = angle; if (d == 0f) { @@ -201,12 +223,12 @@ namespace Godot } else { - float s = Mathf.Sin(angle * 0.5f) / d; + real_t s = Mathf.Sin(angle_t * 0.5f) / d; x = axis.x * s; y = axis.y * s; z = axis.z * s; - w = Mathf.Cos(angle * 0.5f); + w = Mathf.Cos(angle_t * 0.5f); } } @@ -258,17 +280,17 @@ namespace Godot ); } - public static Quat operator *(Quat left, float right) + public static Quat operator *(Quat left, real_t right) { return new Quat(left.x * right, left.y * right, left.z * right, left.w * right); } - public static Quat operator *(float left, Quat right) + public static Quat operator *(real_t 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) + public static Quat operator /(Quat left, real_t right) { return left * (1.0f / right); } diff --git a/modules/mono/glue/cs_files/Rect2.cs b/modules/mono/glue/cs_files/Rect2.cs index e1fbb65da5..decee35f8c 100644 --- a/modules/mono/glue/cs_files/Rect2.cs +++ b/modules/mono/glue/cs_files/Rect2.cs @@ -1,6 +1,12 @@ using System; using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + namespace Godot { [StructLayout(LayoutKind.Sequential)] @@ -26,7 +32,7 @@ namespace Godot get { return position + size; } } - public float Area + public real_t Area { get { return GetArea(); } } @@ -80,12 +86,12 @@ namespace Godot return expanded; } - public float GetArea() + public real_t GetArea() { return size.x * size.y; } - public Rect2 Grow(float by) + public Rect2 Grow(real_t by) { Rect2 g = this; @@ -97,7 +103,7 @@ namespace Godot return g; } - public Rect2 GrowIndividual(float left, float top, float right, float bottom) + public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom) { Rect2 g = this; @@ -109,7 +115,7 @@ namespace Godot return g; } - public Rect2 GrowMargin(Margin margin, float by) + public Rect2 GrowMargin(Margin margin, real_t by) { Rect2 g = this; @@ -169,14 +175,24 @@ namespace Godot return newRect; } - + + // Constructors public Rect2(Vector2 position, Vector2 size) { this.position = position; this.size = size; } - - public Rect2(float x, float y, float width, float height) + public Rect2(Vector2 position, real_t width, real_t height) + { + this.position = position; + this.size = new Vector2(width, height); + } + public Rect2(real_t x, real_t y, Vector2 size) + { + this.position = new Vector2(x, y); + this.size = size; + } + public Rect2(real_t x, real_t y, real_t width, real_t height) { this.position = new Vector2(x, y); this.size = new Vector2(width, height); diff --git a/modules/mono/glue/cs_files/SignalAttribute.cs b/modules/mono/glue/cs_files/SignalAttribute.cs new file mode 100644 index 0000000000..d8a6cabb83 --- /dev/null +++ b/modules/mono/glue/cs_files/SignalAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Delegate)] + public class SignalAttribute : Attribute + { + public SignalAttribute() + { + } + } +} diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/cs_files/StringExtensions.cs index 5c3ceff97d..cbc337ab19 100644 --- a/modules/mono/glue/cs_files/StringExtensions.cs +++ b/modules/mono/glue/cs_files/StringExtensions.cs @@ -287,7 +287,7 @@ namespace Godot if (sep == -1) return @base; - return @base + rs.substr(0, sep); + return @base + rs.Substr(0, sep); } // <summary> @@ -478,7 +478,7 @@ namespace Godot // </summary> public static bool IsValidIpAddress(this string instance) { - string[] ip = instance.split("."); + string[] ip = instance.Split("."); if (ip.Length != 4) return false; @@ -489,7 +489,7 @@ namespace Godot if (!n.IsValidInteger()) return false; - int val = n.to_int(); + int val = n.ToInt(); if (val < 0 || val > 255) return false; } @@ -571,7 +571,7 @@ namespace Godot // <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) + public static bool Matchn(this string instance, string expr) { return instance.ExprMatch(expr, false); } @@ -830,7 +830,7 @@ namespace Godot // <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) + public static string[] Split(this string instance, string divisor, bool allow_empty = true) { return instance.Split(new string[] { divisor }, StringSplitOptions.RemoveEmptyEntries); } @@ -838,7 +838,7 @@ namespace Godot // <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) + public static float[] SplitFloats(this string instance, string divisor, bool allow_empty = true) { List<float> ret = new List<float>(); int from = 0; @@ -872,7 +872,7 @@ namespace Godot // <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) + public static string StripEdges(this string instance, bool left = true, bool right = true) { if (left) { @@ -890,7 +890,7 @@ namespace Godot // <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) + public static string Substr(this string instance, int from, int len) { return instance.Substring(from, len); } @@ -898,7 +898,7 @@ namespace Godot // <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) + public static byte[] ToAscii(this string instance) { return Encoding.ASCII.GetBytes(instance); } @@ -906,7 +906,7 @@ namespace Godot // <summary> // Convert a string, containing a decimal number, into a [code]float[/code]. // </summary> - public static float to_float(this string instance) + public static float ToFloat(this string instance) { return float.Parse(instance); } @@ -914,7 +914,7 @@ namespace Godot // <summary> // Convert a string, containing an integer number, into an [code]int[/code]. // </summary> - public static int to_int(this string instance) + public static int ToInt(this string instance) { return int.Parse(instance); } @@ -922,7 +922,7 @@ namespace Godot // <summary> // Return the string converted to lowercase. // </summary> - public static string to_lower(this string instance) + public static string ToLower(this string instance) { return instance.ToLower(); } @@ -930,7 +930,7 @@ namespace Godot // <summary> // Return the string converted to uppercase. // </summary> - public static string to_upper(this string instance) + public static string ToUpper(this string instance) { return instance.ToUpper(); } @@ -938,7 +938,7 @@ namespace Godot // <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) + public static byte[] ToUtf8(this string instance) { return Encoding.UTF8.GetBytes(instance); } @@ -946,7 +946,7 @@ namespace Godot // <summary> // Return a copy of the string with special characters escaped using the XML standard. // </summary> - public static string xml_escape(this string instance) + public static string XmlEscape(this string instance) { return SecurityElement.Escape(instance); } @@ -954,7 +954,7 @@ namespace Godot // <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) + public static string XmlUnescape(this string instance) { return SecurityElement.FromString(instance).Text; } diff --git a/modules/mono/glue/cs_files/Transform.cs b/modules/mono/glue/cs_files/Transform.cs index 5214100d36..ce26c60706 100644 --- a/modules/mono/glue/cs_files/Transform.cs +++ b/modules/mono/glue/cs_files/Transform.cs @@ -1,6 +1,12 @@ using System; using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + namespace Godot { [StructLayout(LayoutKind.Sequential)] @@ -24,7 +30,7 @@ namespace Godot public Transform LookingAt(Vector3 target, Vector3 up) { Transform t = this; - t.set_look_at(origin, target, up); + t.SetLookAt(origin, target, up); return t; } @@ -33,7 +39,7 @@ namespace Godot return new Transform(basis.Orthonormalized(), origin); } - public Transform Rotated(Vector3 axis, float phi) + public Transform Rotated(Vector3 axis, real_t phi) { return new Transform(new Basis(axis, phi), new Vector3()) * this; } @@ -43,7 +49,7 @@ namespace Godot return new Transform(basis.Scaled(scale), origin * scale); } - public void set_look_at(Vector3 eye, Vector3 target, Vector3 up) + public void SetLookAt(Vector3 eye, Vector3 target, Vector3 up) { // Make rotation matrix // Z vector @@ -97,7 +103,8 @@ namespace Godot (basis[0, 2] * vInv.x) + (basis[1, 2] * vInv.y) + (basis[2, 2] * vInv.z) ); } - + + // Constructors public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin) { this.basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); diff --git a/modules/mono/glue/cs_files/Transform2D.cs b/modules/mono/glue/cs_files/Transform2D.cs index fe7c5b5706..836cca129e 100644 --- a/modules/mono/glue/cs_files/Transform2D.cs +++ b/modules/mono/glue/cs_files/Transform2D.cs @@ -1,6 +1,12 @@ using System; using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + namespace Godot { [StructLayout(LayoutKind.Sequential)] @@ -27,7 +33,7 @@ namespace Godot get { return o; } } - public float Rotation + public real_t Rotation { get { return Mathf.Atan2(y.x, o.y); } } @@ -73,7 +79,7 @@ namespace Godot } - public float this[int index, int axis] + public real_t this[int index, int axis] { get { @@ -107,7 +113,7 @@ namespace Godot { Transform2D inv = this; - float det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1]; + real_t det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1]; if (det == 0) { @@ -119,9 +125,9 @@ namespace Godot ); } - float idet = 1.0f / det; + real_t idet = 1.0f / det; - float temp = this[0, 0]; + real_t temp = this[0, 0]; this[0, 0] = this[1, 1]; this[1, 1] = temp; @@ -143,10 +149,10 @@ namespace Godot return new Vector2(x.Dot(v), y.Dot(v)); } - public Transform2D InterpolateWith(Transform2D m, float c) + public Transform2D InterpolateWith(Transform2D m, real_t c) { - float r1 = Rotation; - float r2 = m.Rotation; + real_t r1 = Rotation; + real_t r2 = m.Rotation; Vector2 s1 = Scale; Vector2 s2 = m.Scale; @@ -155,7 +161,7 @@ namespace Godot 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); + real_t dot = v1.Dot(v2); // Clamp dot to [-1, 1] dot = (dot < -1.0f) ? -1.0f : ((dot > 1.0f) ? 1.0f : dot); @@ -169,7 +175,7 @@ namespace Godot } else { - float angle = c * Mathf.Acos(dot); + real_t angle = c * Mathf.Acos(dot); Vector2 v3 = (v2 - v1 * dot).Normalized(); v = v1 * Mathf.Cos(angle) + v3 * Mathf.Sin(angle); } @@ -192,7 +198,7 @@ namespace Godot Transform2D inv = this; // Swap - float temp = inv.x.y; + real_t temp = inv.x.y; inv.x.y = inv.y.x; inv.y.x = temp; @@ -218,7 +224,7 @@ namespace Godot return on; } - public Transform2D Rotated(float phi) + public Transform2D Rotated(real_t phi) { return this * new Transform2D(phi, new Vector2()); } @@ -232,12 +238,12 @@ namespace Godot return copy; } - private float Tdotx(Vector2 with) + private real_t Tdotx(Vector2 with) { return this[0, 0] * with[0] + this[1, 0] * with[1]; } - private float Tdoty(Vector2 with) + private real_t Tdoty(Vector2 with) { return this[0, 1] * with[0] + this[1, 1] * with[1]; } @@ -259,24 +265,26 @@ namespace Godot Vector2 vInv = v - o; return new Vector2(x.Dot(vInv), y.Dot(vInv)); } - + + // Constructors 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) + + public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t 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) + public Transform2D(real_t rot, Vector2 pos) { - float cr = Mathf.Cos(rot); - float sr = Mathf.Sin(rot); + real_t cr = Mathf.Cos( (real_t)rot); + real_t sr = Mathf.Sin( (real_t)rot); x.x = cr; y.y = cr; x.y = -sr; @@ -288,7 +296,7 @@ namespace Godot { left.o = left.Xform(right.o); - float x0, x1, y0, y1; + real_t x0, x1, y0, y1; x0 = left.Tdotx(right.x); x1 = left.Tdoty(right.x); diff --git a/modules/mono/glue/cs_files/VERSION.txt b/modules/mono/glue/cs_files/VERSION.txt new file mode 100755 index 0000000000..0cfbf08886 --- /dev/null +++ b/modules/mono/glue/cs_files/VERSION.txt @@ -0,0 +1 @@ +2 diff --git a/modules/mono/glue/cs_files/Vector2.cs b/modules/mono/glue/cs_files/Vector2.cs index 238775bda2..6fbe374611 100644 --- a/modules/mono/glue/cs_files/Vector2.cs +++ b/modules/mono/glue/cs_files/Vector2.cs @@ -8,15 +8,21 @@ using System.Runtime.InteropServices; // file: core/variant_call.cpp // commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + namespace Godot { [StructLayout(LayoutKind.Sequential)] public struct Vector2 : IEquatable<Vector2> { - public float x; - public float y; + public real_t x; + public real_t y; - public float this[int index] + public real_t this[int index] { get { @@ -48,7 +54,7 @@ namespace Godot internal void Normalize() { - float length = x * x + y * y; + real_t length = x * x + y * y; if (length != 0f) { @@ -58,7 +64,7 @@ namespace Godot } } - private float Cross(Vector2 b) + private real_t Cross(Vector2 b) { return x * b.y - y * b.x; } @@ -68,22 +74,22 @@ namespace Godot return new Vector2(Mathf.Abs(x), Mathf.Abs(y)); } - public float Angle() + public real_t Angle() { return Mathf.Atan2(y, x); } - public float AngleTo(Vector2 to) + public real_t AngleTo(Vector2 to) { return Mathf.Atan2(Cross(to), Dot(to)); } - public float AngleToPoint(Vector2 to) + public real_t AngleToPoint(Vector2 to) { return Mathf.Atan2(x - to.x, y - to.y); } - public float Aspect() + public real_t Aspect() { return x / y; } @@ -93,10 +99,10 @@ namespace Godot return -Reflect(n); } - public Vector2 Clamped(float length) + public Vector2 Clamped(real_t length) { Vector2 v = this; - float l = this.Length(); + real_t l = this.Length(); if (l > 0 && length < l) { @@ -107,15 +113,15 @@ namespace Godot return v; } - public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, float t) + public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t) { Vector2 p0 = preA; Vector2 p1 = this; Vector2 p2 = b; Vector2 p3 = postB; - float t2 = t * t; - float t3 = t2 * t; + real_t t2 = t * t; + real_t t3 = t2 * t; return 0.5f * ((p1 * 2.0f) + (-p0 + p2) * t + @@ -123,17 +129,17 @@ namespace Godot (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); } - public float DistanceSquaredTo(Vector2 to) + public real_t DistanceSquaredTo(Vector2 to) { return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y); } - public float DistanceTo(Vector2 to) + public real_t DistanceTo(Vector2 to) { return Mathf.Sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y)); } - public float Dot(Vector2 with) + public real_t Dot(Vector2 with) { return x * with.x + y * with.y; } @@ -148,17 +154,17 @@ namespace Godot return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; } - public float Length() + public real_t Length() { return Mathf.Sqrt(x * x + y * y); } - public float LengthSquared() + public real_t LengthSquared() { return x * x + y * y; } - public Vector2 LinearInterpolate(Vector2 b, float t) + public Vector2 LinearInterpolate(Vector2 b, real_t t) { Vector2 res = this; @@ -180,12 +186,23 @@ namespace Godot return 2.0f * n * Dot(n) - this; } - public Vector2 Rotated(float phi) + public Vector2 Rotated(real_t phi) { - float rads = Angle() + phi; + real_t rads = Angle() + phi; return new Vector2(Mathf.Cos(rads), Mathf.Sin(rads)) * Length(); } + public void Set(real_t x, real_t y) + { + this.x = x; + this.y = y; + } + public void Set(Vector2 v) + { + this.x = v.x; + this.y = v.y; + } + public Vector2 Slide(Vector2 n) { return this - n * Dot(n); @@ -200,12 +217,36 @@ namespace Godot { return new Vector2(y, -x); } - - public Vector2(float x, float y) + + private static readonly Vector2 zero = new Vector2 (0, 0); + private static readonly Vector2 one = new Vector2 (1, 1); + private static readonly Vector2 negOne = new Vector2 (-1, -1); + + private static readonly Vector2 up = new Vector2 (0, 1); + private static readonly Vector2 down = new Vector2 (0, -1); + private static readonly Vector2 right = new Vector2 (1, 0); + private static readonly Vector2 left = new Vector2 (-1, 0); + + public static Vector2 Zero { get { return zero; } } + public static Vector2 One { get { return one; } } + public static Vector2 NegOne { get { return negOne; } } + + public static Vector2 Up { get { return up; } } + public static Vector2 Down { get { return down; } } + public static Vector2 Right { get { return right; } } + public static Vector2 Left { get { return left; } } + + // Constructors + public Vector2(real_t x, real_t y) { this.x = x; this.y = y; } + public Vector2(Vector2 v) + { + this.x = v.x; + this.y = v.y; + } public static Vector2 operator +(Vector2 left, Vector2 right) { @@ -228,14 +269,14 @@ namespace Godot return vec; } - public static Vector2 operator *(Vector2 vec, float scale) + public static Vector2 operator *(Vector2 vec, real_t scale) { vec.x *= scale; vec.y *= scale; return vec; } - public static Vector2 operator *(float scale, Vector2 vec) + public static Vector2 operator *(real_t scale, Vector2 vec) { vec.x *= scale; vec.y *= scale; @@ -249,7 +290,7 @@ namespace Godot return left; } - public static Vector2 operator /(Vector2 vec, float scale) + public static Vector2 operator /(Vector2 vec, real_t scale) { vec.x /= scale; vec.y /= scale; diff --git a/modules/mono/glue/cs_files/Vector3.cs b/modules/mono/glue/cs_files/Vector3.cs index 190caa4b53..285736d7b8 100644 --- a/modules/mono/glue/cs_files/Vector3.cs +++ b/modules/mono/glue/cs_files/Vector3.cs @@ -8,6 +8,12 @@ using System.Runtime.InteropServices; // file: core/variant_call.cpp // commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + namespace Godot { [StructLayout(LayoutKind.Sequential)] @@ -20,11 +26,11 @@ namespace Godot Z } - public float x; - public float y; - public float z; + public real_t x; + public real_t y; + public real_t z; - public float this[int index] + public real_t this[int index] { get { @@ -61,7 +67,7 @@ namespace Godot internal void Normalize() { - float length = this.Length(); + real_t length = this.Length(); if (length == 0f) { @@ -80,7 +86,7 @@ namespace Godot return new Vector3(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z)); } - public float AngleTo(Vector3 to) + public real_t AngleTo(Vector3 to) { return Mathf.Atan2(Cross(to).Length(), Dot(to)); } @@ -105,15 +111,15 @@ namespace Godot ); } - public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, float t) + public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t t) { Vector3 p0 = preA; Vector3 p1 = this; Vector3 p2 = b; Vector3 p3 = postB; - float t2 = t * t; - float t3 = t2 * t; + real_t t2 = t * t; + real_t t3 = t2 * t; return 0.5f * ( (p1 * 2.0f) + (-p0 + p2) * t + @@ -122,17 +128,17 @@ namespace Godot ); } - public float DistanceSquaredTo(Vector3 b) + public real_t DistanceSquaredTo(Vector3 b) { return (b - this).LengthSquared(); } - public float DistanceTo(Vector3 b) + public real_t DistanceTo(Vector3 b) { return (b - this).Length(); } - public float Dot(Vector3 b) + public real_t Dot(Vector3 b) { return x * b.x + y * b.y + z * b.z; } @@ -152,25 +158,25 @@ namespace Godot return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; } - public float Length() + public real_t Length() { - float x2 = x * x; - float y2 = y * y; - float z2 = z * z; + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; return Mathf.Sqrt(x2 + y2 + z2); } - public float LengthSquared() + public real_t LengthSquared() { - float x2 = x * x; - float y2 = y * y; - float z2 = z * z; + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; return x2 + y2 + z2; } - public Vector3 LinearInterpolate(Vector3 b, float t) + public Vector3 LinearInterpolate(Vector3 b, real_t t) { return new Vector3 ( @@ -215,11 +221,24 @@ namespace Godot return 2.0f * n * Dot(n) - this; } - public Vector3 Rotated(Vector3 axis, float phi) + public Vector3 Rotated(Vector3 axis, real_t phi) { return new Basis(axis, phi).Xform(this); } + public void Set(real_t x, real_t y, real_t z) + { + this.x = x; + this.y = y; + this.z = z; + } + public void Set(Vector3 v) + { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + public Vector3 Slide(Vector3 n) { return this - n * Dot(n); @@ -243,13 +262,42 @@ namespace Godot 0f, 0f, z ); } - - public Vector3(float x, float y, float z) + + private static readonly Vector3 zero = new Vector3 (0, 0, 0); + private static readonly Vector3 one = new Vector3 (1, 1, 1); + private static readonly Vector3 negOne = new Vector3 (-1, -1, -1); + + private static readonly Vector3 up = new Vector3 (0, 1, 0); + private static readonly Vector3 down = new Vector3 (0, -1, 0); + private static readonly Vector3 right = new Vector3 (1, 0, 0); + private static readonly Vector3 left = new Vector3 (-1, 0, 0); + private static readonly Vector3 forward = new Vector3 (0, 0, -1); + private static readonly Vector3 back = new Vector3 (0, 0, 1); + + public static Vector3 Zero { get { return zero; } } + public static Vector3 One { get { return one; } } + public static Vector3 NegOne { get { return negOne; } } + + public static Vector3 Up { get { return up; } } + public static Vector3 Down { get { return down; } } + public static Vector3 Right { get { return right; } } + public static Vector3 Left { get { return left; } } + public static Vector3 Forward { get { return forward; } } + public static Vector3 Back { get { return back; } } + + // Constructors + public Vector3(real_t x, real_t y, real_t z) { this.x = x; this.y = y; this.z = z; } + public Vector3(Vector3 v) + { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } public static Vector3 operator +(Vector3 left, Vector3 right) { @@ -275,7 +323,7 @@ namespace Godot return vec; } - public static Vector3 operator *(Vector3 vec, float scale) + public static Vector3 operator *(Vector3 vec, real_t scale) { vec.x *= scale; vec.y *= scale; @@ -283,7 +331,7 @@ namespace Godot return vec; } - public static Vector3 operator *(float scale, Vector3 vec) + public static Vector3 operator *(real_t scale, Vector3 vec) { vec.x *= scale; vec.y *= scale; @@ -299,7 +347,7 @@ namespace Godot return left; } - public static Vector3 operator /(Vector3 vec, float scale) + public static Vector3 operator /(Vector3 vec, real_t scale) { vec.x /= scale; vec.y /= scale; diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h index 4c26c3e6bd..f604464e8f 100644 --- a/modules/mono/godotsharp_defs.h +++ b/modules/mono/godotsharp_defs.h @@ -39,4 +39,7 @@ #define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor" #define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools" +#define BINDINGS_CLASS_NATIVECALLS "NativeCalls" +#define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls" + #endif // GODOTSHARP_DEFS_H diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index f5febd415b..0646580eaa 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -42,11 +42,15 @@ #include "project_settings.h" #include "../csharp_script.h" +#include "../godotsharp_dirs.h" #include "../utils/path_utils.h" +#include "gd_mono_class.h" +#include "gd_mono_marshal.h" #include "gd_mono_utils.h" #ifdef TOOLS_ENABLED #include "../editor/godotsharp_editor.h" +#include "main/main.h" #endif void gdmono_unhandled_exception_hook(MonoObject *exc, void *user_data) { @@ -70,7 +74,31 @@ void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) { GDMono *GDMono::singleton = NULL; +namespace { + +void setup_runtime_main_args() { + CharString execpath = OS::get_singleton()->get_executable_path().utf8(); + + List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); + + List<CharString> cmdline_args_utf8; + Vector<char *> main_args; + main_args.resize(cmdline_args.size() + 1); + + main_args[0] = execpath.ptrw(); + + int i = 1; + for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) { + CharString &stored = cmdline_args_utf8.push_back(E->get().utf8())->get(); + main_args[i] = stored.ptrw(); + i++; + } + + mono_runtime_set_main_args(main_args.size(), main_args.ptrw()); +} + #ifdef DEBUG_ENABLED + static bool _wait_for_debugger_msecs(uint32_t p_msecs) { do { @@ -92,24 +120,7 @@ static bool _wait_for_debugger_msecs(uint32_t p_msecs) { 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); @@ -121,7 +132,7 @@ void gdmono_debug_init() { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() || ProjectSettings::get_singleton()->get_resource_path().empty() || - _is_project_manager_requested()) { + Main::is_project_manager()) { return; } #endif @@ -136,8 +147,11 @@ void gdmono_debug_init() { }; mono_jit_parse_options(2, (char **)options); } + #endif +} // namespace + void GDMono::initialize() { ERR_FAIL_NULL(Engine::get_singleton()); @@ -190,6 +204,8 @@ void GDMono::initialize() { GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread()); + setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs + runtime_initialized = true; OS::get_singleton()->print("Mono: Runtime initialized\n"); @@ -222,7 +238,46 @@ void GDMono::initialize() { _register_internal_calls(); // The following assemblies are not required at initialization - _load_all_script_assemblies(); +#ifndef MONO_GLUE_DISABLED + if (_load_api_assemblies()) { + if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) { + // Everything is fine with the api assemblies, load the project assembly + _load_project_assembly(); + } else { +#ifdef TOOLS_ENABLED + // The assembly was successfuly loaded, but the full api could not be cached. + // This is most likely an outdated assembly loaded because of an invalid version in the metadata, + // so we invalidate the version in the metadata and unload the script domain. + + if (core_api_assembly_out_of_sync) { + ERR_PRINT("The loaded Core API assembly is out of sync"); + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { + ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed"); + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } + + if (editor_api_assembly_out_of_sync) { + ERR_PRINT("The loaded Editor API assembly is out of sync"); + metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true); + } + + OS::get_singleton()->print("Mono: Proceeding to unload scripts domain because of invalid API assemblies\n"); + + Error err = _unload_scripts_domain(); + if (err != OK) { + WARN_PRINT("Mono: Failed to unload scripts domain"); + } +#else + ERR_PRINT("The loaded API assembly is invalid"); + CRASH_NOW(); +#endif + } + } +#else + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n"); +#endif mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL); @@ -233,7 +288,11 @@ void GDMono::initialize() { namespace GodotSharpBindings { uint64_t get_core_api_hash(); +#ifdef TOOLS_ENABLED uint64_t get_editor_api_hash(); +#endif // TOOLS_ENABLED +uint32_t get_bindings_version(); +uint32_t get_cs_glue_version(); void register_generated_icalls(); } // namespace GodotSharpBindings @@ -285,43 +344,84 @@ GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) { return assemblies[domain_id].getptr(p_name); } -bool GDMono::_load_assembly(const String &p_name, GDMonoAssembly **r_assembly) { +bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) { + + CRASH_COND(!r_assembly); + + MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8()); + bool result = load_assembly(p_name, aname, r_assembly, p_refonly); + mono_assembly_name_free(aname); + mono_free(aname); + + return result; +} + +bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) { CRASH_COND(!r_assembly); if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + "...\n").utf8()); + OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...\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); + MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly); if (!assembly) return false; + ERR_FAIL_COND_V(status != MONO_IMAGE_OK, 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()); + OS::get_singleton()->print(String("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8()); return true; } +APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) { + APIAssembly::Version api_assembly_version; + + const char *nativecalls_name = p_api_type == APIAssembly::API_CORE ? + BINDINGS_CLASS_NATIVECALLS : + BINDINGS_CLASS_NATIVECALLS_EDITOR; + + GDMonoClass *nativecalls_klass = p_api_assembly->get_class(BINDINGS_NAMESPACE, nativecalls_name); + + if (nativecalls_klass) { + GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash"); + if (api_hash_field) + api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(NULL)); + + GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version"); + if (binds_ver_field) + api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(NULL)); + + GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version"); + if (cs_glue_ver_field) + api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(NULL)); + } + + return api_assembly_version; +} + +String APIAssembly::to_string(APIAssembly::Type p_type) { + return p_type == APIAssembly::API_CORE ? "API_CORE" : "API_EDITOR"; +} + bool GDMono::_load_corlib_assembly() { if (corlib_assembly) return true; - bool success = _load_assembly("mscorlib", &corlib_assembly); + bool success = load_assembly("mscorlib", &corlib_assembly); if (success) GDMonoUtils::update_corlib_cache(); @@ -331,13 +431,25 @@ bool GDMono::_load_corlib_assembly() { bool GDMono::_load_core_api_assembly() { - if (api_assembly) + if (core_api_assembly) return true; - bool success = _load_assembly(API_ASSEMBLY_NAME, &api_assembly); +#ifdef TOOLS_ENABLED + if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) + return false; +#endif - if (success) + bool success = load_assembly(API_ASSEMBLY_NAME, &core_api_assembly); + + if (success) { +#ifndef MONO_GLUE_DISABLED + APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE); + core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || + GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || + GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; +#endif GDMonoUtils::update_godot_api_cache(); + } return success; } @@ -348,7 +460,23 @@ bool GDMono::_load_editor_api_assembly() { if (editor_api_assembly) return true; - return _load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); +#ifdef TOOLS_ENABLED + if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) + return false; +#endif + + bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); + + if (success) { +#ifndef MONO_GLUE_DISABLED + APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR); + editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || + GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || + GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; +#endif + } + + return success; } #endif @@ -360,7 +488,7 @@ bool GDMono::_load_editor_tools_assembly() { _GDMONO_SCOPE_DOMAIN_(tools_domain) - return _load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly); + return load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly); } #endif @@ -374,17 +502,20 @@ bool GDMono::_load_project_assembly() { name = "UnnamedProject"; } - bool success = _load_assembly(name, &project_assembly); + bool success = load_assembly(name, &project_assembly); - if (success) + if (success) { mono_assembly_set_main(project_assembly->get_assembly()); + } else { + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->printerr("Mono: Failed to load project assembly\n"); + } return success; } -bool GDMono::_load_all_script_assemblies() { +bool GDMono::_load_api_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"); @@ -399,20 +530,72 @@ bool GDMono::_load_all_script_assemblies() { #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; +} + +#ifdef TOOLS_ENABLED +String GDMono::_get_api_assembly_metadata_path() { + + return GodotSharpDirs::get_res_metadata_dir().plus_file("api_assemblies.cfg"); +} + +void GDMono::metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated) { + + String section = APIAssembly::to_string(p_api_type); + String path = _get_api_assembly_metadata_path(); + + Ref<ConfigFile> metadata; + metadata.instance(); + metadata->load(path); + + metadata->set_value(section, "invalidated", p_invalidated); + + String assembly_path = GodotSharpDirs::get_res_assemblies_dir() + .plus_file(p_api_type == APIAssembly::API_CORE ? + API_ASSEMBLY_NAME ".dll" : + EDITOR_API_ASSEMBLY_NAME ".dll"); + + ERR_FAIL_COND(!FileAccess::exists(assembly_path)); + + uint64_t modified_time = FileAccess::get_modified_time(assembly_path); + + metadata->set_value(section, "invalidated_asm_modified_time", String::num_uint64(modified_time)); + + String dir = path.get_base_dir(); + if (!DirAccess::exists(dir)) { + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND(!da); + Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(dir)); + ERR_FAIL_COND(err != OK); } - return true; -#else - if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->print("Mono: Glue disbled, ignoring script assemblies\n"); + Error save_err = metadata->save(path); + ERR_FAIL_COND(save_err != OK); +} - return true; -#endif +bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type) { + + String section = APIAssembly::to_string(p_api_type); + + Ref<ConfigFile> metadata; + metadata.instance(); + metadata->load(_get_api_assembly_metadata_path()); + + String assembly_path = GodotSharpDirs::get_res_assemblies_dir() + .plus_file(p_api_type == APIAssembly::API_CORE ? + API_ASSEMBLY_NAME ".dll" : + EDITOR_API_ASSEMBLY_NAME ".dll"); + + if (!FileAccess::exists(assembly_path)) + return false; + + uint64_t modified_time = FileAccess::get_modified_time(assembly_path); + + uint64_t stored_modified_time = metadata->get_value(section, "invalidated_asm_modified_time", 0); + + return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time; } +#endif Error GDMono::_load_scripts_domain() { @@ -455,12 +638,15 @@ Error GDMono::_unload_scripts_domain() { _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain)); - api_assembly = NULL; + core_api_assembly = NULL; project_assembly = NULL; #ifdef TOOLS_ENABLED editor_api_assembly = NULL; #endif + core_api_assembly_out_of_sync = false; + editor_api_assembly_out_of_sync = false; + MonoDomain *domain = scripts_domain; scripts_domain = NULL; @@ -515,16 +701,80 @@ Error GDMono::reload_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"); +#ifndef MONO_GLUE_DISABLED + if (!_load_api_assemblies()) { return ERR_CANT_OPEN; } + if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) { + // Everything is fine with the api assemblies, load the project assembly + _load_project_assembly(); + } else { + // The assembly was successfuly loaded, but the full api could not be cached. + // This is most likely an outdated assembly loaded because of an invalid version in the metadata, + // so we invalidate the version in the metadata and unload the script domain. + + if (core_api_assembly_out_of_sync) { + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { + ERR_PRINT("Core API assembly is in sync, but the cache update failed"); + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } + + if (editor_api_assembly_out_of_sync) { + metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true); + } + + Error err = _unload_scripts_domain(); + if (err != OK) { + WARN_PRINT("Mono: Failed to unload scripts domain"); + } + + return ERR_CANT_RESOLVE; + } + + if (!_load_project_assembly()) + return ERR_CANT_OPEN; +#else + if (OS::get_singleton()->is_stdout_verbose()) + OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n"); +#endif + return OK; } #endif +Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { + + CRASH_COND(p_domain == NULL); + + String domain_name = mono_domain_get_friendly_name(p_domain); + + if (OS::get_singleton()->is_stdout_verbose()) { + OS::get_singleton()->print(String("Mono: Unloading domain `" + domain_name + "`...\n").utf8()); + } + + if (mono_domain_get() != root_domain) + mono_domain_set(root_domain, true); + + mono_gc_collect(mono_gc_max_generation()); + mono_domain_finalize(p_domain, 2000); + mono_gc_collect(mono_gc_max_generation()); + + _domain_assemblies_cleanup(mono_domain_get_id(p_domain)); + + MonoObject *ex = NULL; + mono_domain_try_unload(p_domain, &ex); + + if (ex) { + ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`:"); + mono_print_unhandled_exception(ex); + return FAILED; + } + + return OK; +} + GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) { MonoImage *image = mono_class_get_image(p_raw_class); @@ -576,8 +826,11 @@ GDMono::GDMono() { tools_domain = NULL; #endif + core_api_assembly_out_of_sync = false; + editor_api_assembly_out_of_sync = false; + corlib_assembly = NULL; - api_assembly = NULL; + core_api_assembly = NULL; project_assembly = NULL; #ifdef TOOLS_ENABLED editor_api_assembly = NULL; diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 67251778c6..5e01152870 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -31,6 +31,8 @@ #ifndef GD_MONO_H #define GD_MONO_H +#include "core/io/config_file.h" + #include "../godotsharp_defs.h" #include "gd_mono_assembly.h" #include "gd_mono_log.h" @@ -39,6 +41,43 @@ #include "../utils/mono_reg_utils.h" #endif +namespace APIAssembly { +enum Type { + API_CORE, + API_EDITOR +}; + +struct Version { + uint64_t godot_api_hash; + uint32_t bindings_version; + uint32_t cs_glue_version; + + bool operator==(const Version &p_other) const { + return godot_api_hash == p_other.godot_api_hash && + bindings_version == p_other.bindings_version && + cs_glue_version == p_other.cs_glue_version; + } + + Version() : + godot_api_hash(0), + bindings_version(0), + cs_glue_version(0) { + } + + Version(uint64_t p_godot_api_hash, + uint32_t p_bindings_version, + uint32_t p_cs_glue_version) : + godot_api_hash(p_godot_api_hash), + bindings_version(p_bindings_version), + cs_glue_version(p_cs_glue_version) { + } + + static Version get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, Type p_api_type); +}; + +String to_string(Type p_type); +} // namespace APIAssembly + #define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain() #ifdef TOOLS_ENABLED #define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain() @@ -55,8 +94,11 @@ class GDMono { MonoDomain *tools_domain; #endif + bool core_api_assembly_out_of_sync; + bool editor_api_assembly_out_of_sync; + GDMonoAssembly *corlib_assembly; - GDMonoAssembly *api_assembly; + GDMonoAssembly *core_api_assembly; GDMonoAssembly *project_assembly; #ifdef TOOLS_ENABLED GDMonoAssembly *editor_api_assembly; @@ -75,7 +117,11 @@ class GDMono { #endif bool _load_project_assembly(); - bool _load_all_script_assemblies(); + bool _load_api_assemblies(); + +#ifdef TOOLS_ENABLED + String _get_api_assembly_metadata_path(); +#endif void _register_internal_calls(); @@ -94,8 +140,6 @@ class GDMono { void _initialize_and_check_api_hashes(); #endif - bool _load_assembly(const String &p_name, GDMonoAssembly **r_assembly); - GDMonoLog *gdmono_log; #ifdef WINDOWS_ENABLED @@ -113,6 +157,11 @@ public: #endif #endif +#ifdef TOOLS_ENABLED + void metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated); + bool metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type); +#endif + static GDMono *get_singleton() { return singleton; } // Do not use these, unless you know what you're doing @@ -128,7 +177,7 @@ public: #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_core_api_assembly() const { return core_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; } @@ -145,6 +194,10 @@ public: Error reload_scripts_domain(); #endif + bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false); + bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false); + Error finalize_and_unload_domain(MonoDomain *p_domain); + void initialize(); GDMono(); diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index ef39b8549d..d062d56dcf 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -43,7 +43,23 @@ bool GDMonoAssembly::no_search = false; Vector<String> GDMonoAssembly::search_dirs; -MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data) { +MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) { + return GDMonoAssembly::_search_hook(aname, user_data, false); +} + +MonoAssembly *GDMonoAssembly::assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data) { + return GDMonoAssembly::_search_hook(aname, user_data, true); +} + +MonoAssembly *GDMonoAssembly::assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) { + return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, false); +} + +MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) { + return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true); +} + +MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly) { (void)user_data; // UNUSED @@ -60,7 +76,7 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d no_search = true; // Avoid the recursion madness String path; - MonoAssembly *res = NULL; + GDMonoAssembly *res = NULL; for (int i = 0; i < search_dirs.size(); i++) { const String &search_dir = search_dirs[i]; @@ -68,44 +84,49 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d if (has_extension) { path = search_dir.plus_file(name); if (FileAccess::exists(path)) { - res = _load_assembly_from(name.get_basename(), path); - break; + res = _load_assembly_from(name.get_basename(), path, refonly); + if (res != NULL) + break; } } else { path = search_dir.plus_file(name + ".dll"); if (FileAccess::exists(path)) { - res = _load_assembly_from(name, path); - break; + res = _load_assembly_from(name, path, refonly); + if (res != NULL) + break; } path = search_dir.plus_file(name + ".exe"); if (FileAccess::exists(path)) { - res = _load_assembly_from(name, path); - break; + res = _load_assembly_from(name, path, refonly); + if (res != NULL) + break; } } } no_search = false; - return res; + return res ? res->get_assembly() : NULL; } -MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) { +MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly) { (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()); +#ifdef GD_MONO_EDITOR_ASSEMBLIES_DIR + search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir().plus_file(_MKSTR(GD_MONO_EDITOR_ASSEMBLIES_DIR)).simplify_path()); +#endif const char *rootdir = mono_assembly_getrootdir(); if (rootdir) { search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5")); + search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5").plus_file("Facades")); } if (assemblies_path) { @@ -121,10 +142,11 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse if (has_extension ? name == "mscorlib.dll" : name == "mscorlib") { GDMonoAssembly **stored_assembly = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name); - if (stored_assembly) return (*stored_assembly)->get_assembly(); + if (stored_assembly) + return (*stored_assembly)->get_assembly(); String path; - MonoAssembly *res = NULL; + GDMonoAssembly *res = NULL; for (int i = 0; i < search_dirs.size(); i++) { const String &search_dir = search_dirs[i]; @@ -132,53 +154,57 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse if (has_extension) { path = search_dir.plus_file(name); if (FileAccess::exists(path)) { - res = _load_assembly_from(name.get_basename(), path); - break; + res = _load_assembly_from(name.get_basename(), path, refonly); + if (res != NULL) + break; } } else { path = search_dir.plus_file(name + ".dll"); if (FileAccess::exists(path)) { - res = _load_assembly_from(name, path); - break; + res = _load_assembly_from(name, path, refonly); + if (res != NULL) + break; } } } - if (res) return res; + return res ? res->get_assembly() : NULL; } return NULL; } -MonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path) { +GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) { GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path)); - MonoDomain *domain = mono_domain_get(); - - Error err = assembly->load(domain); + Error err = assembly->load(p_refonly); if (err != OK) { memdelete(assembly); ERR_FAIL_V(NULL); } + MonoDomain *domain = mono_domain_get(); GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, assembly); - return assembly->get_assembly(); + return 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); + mono_install_assembly_search_hook(&assembly_search_hook, NULL); + mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, NULL); + mono_install_assembly_preload_hook(&assembly_preload_hook, NULL); + mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, NULL); } -Error GDMonoAssembly::load(MonoDomain *p_domain) { +Error GDMonoAssembly::load(bool p_refonly) { ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE); + refonly = p_refonly; + uint64_t last_modified_time = FileAccess::get_modified_time(path); Vector<uint8_t> data = FileAccess::get_file_as_array(path); @@ -186,14 +212,15 @@ Error GDMonoAssembly::load(MonoDomain *p_domain) { String image_filename(path); - MonoImageOpenStatus status; + MonoImageOpenStatus status = MONO_IMAGE_OK; image = mono_image_open_from_data_with_name( (char *)&data[0], data.size(), - true, &status, false, + true, &status, refonly, image_filename.utf8().get_data()); - ERR_FAIL_COND_V(status != MONO_IMAGE_OK || image == NULL, ERR_FILE_CANT_OPEN); + ERR_FAIL_COND_V(status != MONO_IMAGE_OK, ERR_FILE_CANT_OPEN); + ERR_FAIL_NULL_V(image, ERR_FILE_CANT_OPEN); #ifdef DEBUG_ENABLED String pdb_path(path + ".pdb"); @@ -213,15 +240,10 @@ no_pdb: #endif - assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, false); + assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, refonly); 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; @@ -373,12 +395,26 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) return match; } +GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) { + + GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); + if (loaded_asm) + return *loaded_asm; + + no_search = true; + GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly); + no_search = false; + + return res; +} + GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) { loaded = false; gdobject_class_cache_updated = false; name = p_name; path = p_path; + refonly = false; modified_time = 0; assembly = NULL; image = NULL; diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 8a5fa19626..5cf744a5a2 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -71,6 +71,7 @@ class GDMonoAssembly { MonoAssembly *assembly; MonoImage *image; + bool refonly; bool loaded; String name; @@ -90,19 +91,25 @@ class GDMonoAssembly { 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 *assembly_search_hook(MonoAssemblyName *aname, void *user_data); + static MonoAssembly *assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data); + static MonoAssembly *assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data); + static MonoAssembly *assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data); - static MonoAssembly *_load_assembly_from(const String &p_name, const String &p_path); + static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly); + static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly); + + static GDMonoAssembly *_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly); friend class GDMono; static void initialize(); public: - Error load(MonoDomain *p_domain); + Error load(bool p_refonly); Error wrapper_for_image(MonoImage *p_image); void unload(); + _FORCE_INLINE_ bool is_refonly() const { return refonly; } _FORCE_INLINE_ bool is_loaded() const { return loaded; } _FORCE_INLINE_ MonoImage *get_image() const { return image; } _FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; } @@ -115,6 +122,8 @@ public: GDMonoClass *get_object_derived_class(const StringName &p_class); + static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly); + GDMonoAssembly(const String &p_name, const String &p_path = String()); ~GDMonoAssembly(); }; diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index b826352f02..66339d7ae6 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -404,6 +404,33 @@ const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() { return properties_list; } +const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() { + if (delegates_fetched) + return delegates_list; + + void *iter = NULL; + MonoClass *raw_class = NULL; + while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != NULL) { + if (mono_class_is_delegate(raw_class)) { + StringName name = mono_class_get_name(raw_class); + + Map<StringName, GDMonoClass *>::Element *match = delegates.find(name); + + if (match) { + delegates_list.push_back(match->get()); + } else { + GDMonoClass *delegate = memnew(GDMonoClass(mono_class_get_namespace(raw_class), mono_class_get_name(raw_class), raw_class, assembly)); + delegates.insert(name, delegate); + delegates_list.push_back(delegate); + } + } + } + + delegates_fetched = true; + + return delegates_list; +} + GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) { namespace_name = p_namespace; @@ -417,6 +444,7 @@ GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name methods_fetched = false; fields_fetched = false; properties_fetched = false; + delegates_fetched = false; } GDMonoClass::~GDMonoClass() { diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index afccf2fc63..417c138594 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -90,6 +90,10 @@ class GDMonoClass { Map<StringName, GDMonoProperty *> properties; Vector<GDMonoProperty *> properties_list; + bool delegates_fetched; + Map<StringName, GDMonoClass *> delegates; + Vector<GDMonoClass *> delegates_list; + friend class GDMonoAssembly; GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly); @@ -133,6 +137,8 @@ public: GDMonoProperty *get_property(const StringName &p_name); const Vector<GDMonoProperty *> &get_all_properties(); + const Vector<GDMonoClass *> &get_all_delegates(); + ~GDMonoClass(); }; diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 1f8e9a1926..ad52904945 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -229,6 +229,23 @@ String GDMonoMethod::get_signature_desc(bool p_namespaces) const { return res; } +void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const { + if (params_count > 0) { + const char **_names = memnew_arr(const char *, params_count); + mono_method_get_param_names(mono_method, _names); + for (int i = 0; i < params_count; ++i) { + names.push_back(StringName(_names[i])); + } + memdelete_arr(_names); + } +} + +void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const { + for (int i = 0; i < param_types.size(); ++i) { + types.push_back(param_types[i]); + } +} + GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) { name = p_name; diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index 14df8dcfb4..a173af83f4 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -80,6 +80,9 @@ public: String get_ret_type_full_name() const; String get_signature_desc(bool p_namespaces = false) const; + void get_parameter_names(Vector<StringName> &names) const; + void get_parameter_types(Vector<ManagedType> &types) const; + GDMonoMethod(StringName p_name, MonoMethod *p_method); ~GDMonoMethod(); }; diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index a2f0819a72..db136a1313 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -114,6 +114,7 @@ void MonoCache::clear_members() { class_ExportAttribute = NULL; field_ExportAttribute_hint = NULL; field_ExportAttribute_hintString = NULL; + class_SignalAttribute = NULL; class_ToolAttribute = NULL; class_RemoteAttribute = NULL; class_SyncAttribute = NULL; @@ -142,7 +143,7 @@ void MonoCache::cleanup() { godot_api_cache_updated = false; } -#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) +#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) void update_corlib_cache() { @@ -201,6 +202,7 @@ void update_godot_api_cache() { 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, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString")); + CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute)); 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)); @@ -243,7 +245,7 @@ void update_godot_api_cache() { mono_runtime_object_init(task_scheduler); mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler); - mono_cache.corlib_cache_updated = true; + mono_cache.godot_api_cache_updated = true; } void clear_cache() { @@ -303,7 +305,7 @@ GDMonoClass *type_get_proxy_class(const StringName &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); + GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name); #ifdef TOOLS_ENABLED if (!klass) { @@ -319,7 +321,7 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) { do { const GDMonoAssembly *assembly = klass->get_assembly(); - if (assembly == GDMono::get_singleton()->get_api_assembly()) + if (assembly == GDMono::get_singleton()->get_core_api_assembly()) return klass; #ifdef TOOLS_ENABLED if (assembly == GDMono::get_singleton()->get_editor_api_assembly()) @@ -418,37 +420,56 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) { if (!ScriptDebugger::get_singleton()) return; - GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace); - MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr()); + ScriptLanguage::StackInfo separator; + separator.file = ""; + separator.func = "--- " + RTR("End of inner exception stack trace") + " ---"; + separator.line = 0; + + Vector<ScriptLanguage::StackInfo> si; + String exc_msg = ""; + + while (p_exc != NULL) { + GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace); + MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr()); - MonoBoolean need_file_info = true; - void *ctor_args[2] = { p_exc, &need_file_info }; + MonoBoolean need_file_info = true; + void *ctor_args[2] = { p_exc, &need_file_info }; - MonoObject *unexpected_exc = NULL; - CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc); + MonoObject *unexpected_exc = NULL; + CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc); - if (unexpected_exc != NULL) { - mono_print_unhandled_exception(unexpected_exc); + if (unexpected_exc != NULL) { + mono_print_unhandled_exception(unexpected_exc); - if (p_recursion_caution) { - // Called from CSharpLanguage::get_current_stack_info, - // so printing an error here could result in endless recursion - OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed"); - return; - } else { - ERR_FAIL(); + if (p_recursion_caution) { + // Called from CSharpLanguage::get_current_stack_info, + // so printing an error here could result in endless recursion + OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed"); + return; + } else { + ERR_FAIL(); + } } - } - Vector<ScriptLanguage::StackInfo> si; - if (stack_trace != NULL && !p_recursion_caution) - si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace); + Vector<ScriptLanguage::StackInfo> _si; + if (stack_trace != NULL && !p_recursion_caution) { + _si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace); + for (int i = _si.size() - 1; i >= 0; i--) + si.insert(0, _si[i]); + } + + exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc); + + GDMonoProperty *p_prop = GDMono::get_singleton()->get_class(mono_object_get_class(p_exc))->get_property("InnerException"); + p_exc = p_prop != NULL ? p_prop->get_value(p_exc) : NULL; + if (p_exc != NULL) + si.insert(0, separator); + } String file = si.size() ? si[0].file : __FILE__; String func = si.size() ? si[0].func : FUNCTION_STR; int line = si.size() ? si[0].line : __LINE__; String error_msg = "Unhandled exception"; - String exc_msg = GDMonoUtils::get_exception_name_and_message(p_exc); ScriptDebugger::get_singleton()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si); #endif diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 259da46c31..1a34180d15 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -108,6 +108,7 @@ struct MonoCache { GDMonoClass *class_ExportAttribute; GDMonoField *field_ExportAttribute_hint; GDMonoField *field_ExportAttribute_hintString; + GDMonoClass *class_SignalAttribute; GDMonoClass *class_ToolAttribute; GDMonoClass *class_RemoteAttribute; GDMonoClass *class_SyncAttribute; |