diff options
Diffstat (limited to 'modules/mono')
90 files changed, 5356 insertions, 1885 deletions
diff --git a/modules/mono/SCsub b/modules/mono/SCsub index aa8626e6da..f3cf4c9c5d 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -5,9 +5,11 @@ Import('env_modules') env_mono = env_modules.Clone() -from compat import byte_to_str +# TODO move functions to their own modules def make_cs_files_header(src, dst): + from compat import byte_to_str + with open(dst, 'w') as header: header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n') header.write('#ifndef _CS_FILES_DATA_H\n') @@ -16,14 +18,20 @@ def make_cs_files_header(src, dst): header.write('#include "ustring.h"\n') inserted_files = '' import os - for file in os.listdir(src): - if file.endswith('.cs'): - with open(os.path.join(src, file), 'rb') as f: + latest_mtime = 0 + for root, _, files in os.walk(src): + files = [f for f in files if f.endswith('.cs')] + for file in files: + filepath = os.path.join(root, file) + filepath_src_rel = os.path.relpath(filepath, src) + mtime = os.path.getmtime(filepath) + latest_mtime = mtime if mtime > latest_mtime else latest_mtime + with open(filepath, 'rb') as f: buf = f.read() decomp_size = len(buf) import zlib buf = zlib.compress(buf) - name = os.path.splitext(file)[0] + name = os.path.splitext(os.path.normpath(filepath_src_rel))[0].strip(os.sep).replace(os.sep, '_').replace('.', '_dotto_') header.write('\nstatic const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n') header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n') header.write('static const unsigned char _cs_' + name + '_compressed[] = { ') @@ -31,11 +39,13 @@ 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("' + filepath_src_rel + '", ' \ 'CompressedFile(_cs_' + name + '_compressed_size, ' \ '_cs_' + name + '_uncompressed_size, ' \ '_cs_' + name + '_compressed));\n' header.write(' };\n') + glue_version = int(latest_mtime) # The latest modified time will do for now + header.write('\n#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n') header.write('\nstruct CompressedFile\n' '{\n' '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' @@ -52,6 +62,7 @@ env_mono.add_source_files(env.modules_sources, 'utils/*.cpp') if env['tools']: env_mono.add_source_files(env.modules_sources, 'editor/*.cpp') + # NOTE: It is safe to generate this file here, since this is still execute serially make_cs_files_header('glue/cs_files', 'glue/cs_compressed.gen.h') vars = Variables() @@ -68,6 +79,13 @@ else: if ARGUMENTS.get('yolo_copy', False): env_mono.Append(CPPDEFINES=['YOLO_COPY']) +# Configure TLS checks + +import tls_configure +conf = Configure(env_mono) +tls_configure.configure(conf) +env_mono = conf.Finish() + # Build GodotSharpTools solution @@ -80,23 +98,23 @@ def find_msbuild_unix(filename): import sys hint_dirs = ['/opt/novell/mono/bin'] - if sys.platform == "darwin": - hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin'] + hint_dirs + if sys.platform == 'darwin': + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/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 @@ -120,15 +138,25 @@ def find_msbuild_windows(): if not mono_root: raise RuntimeError('Cannot find mono root directory') + framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5') + mono_bin_dir = os.path.join(mono_root, 'bin') + msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat') + + if os.path.isfile(msbuild_mono): + # The (Csc/Vbc/Fsc)ToolExe environment variables are required when + # building with Mono's MSBuild. They must point to the batch files + # in Mono's bin directory to make sure they are executed with Mono. + mono_msbuild_env = { + 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'), + 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'), + 'FscToolExe': os.path.join(mono_bin_dir, 'fsharpc.bat') + } + return (msbuild_mono, framework_path, mono_msbuild_env) + msbuild_tools_path = monoreg.find_msbuild_tools_path_reg() if msbuild_tools_path: - return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), os.path.join(mono_root, 'lib', 'mono', '4.5')) - else: - msbuild_mono = os.path.join(mono_root, 'bin', 'msbuild.bat') - - if os.path.isfile(msbuild_mono): - return (msbuild_mono, '') + return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {}) return None @@ -138,21 +166,28 @@ def mono_build_solution(source, target, env): import mono_reg_utils as monoreg from shutil import copyfile - framework_path_override = '' + framework_path = '' + + msbuild_env = os.environ.copy() + + # Needed when running from Developer Command Prompt for VS + if 'PLATFORM' in msbuild_env: + del msbuild_env['PLATFORM'] if os.name == 'nt': msbuild_info = find_msbuild_windows() if msbuild_info is None: raise RuntimeError('Cannot find MSBuild executable') msbuild_path = msbuild_info[0] - framework_path_override = msbuild_info[1] + framework_path = msbuild_info[1] + msbuild_env.update(msbuild_info[2]) else: msbuild_path = find_msbuild_unix('msbuild') if msbuild_path is None: xbuild_fallback = env['xbuild_fallback'] if xbuild_fallback and os.name == 'nt': - print("Option 'xbuild_fallback' not supported on Windows") + print('Option \'xbuild_fallback\' not supported on Windows') xbuild_fallback = False if xbuild_fallback: @@ -176,14 +211,8 @@ def mono_build_solution(source, target, env): '/p:Configuration=' + build_config, ] - if framework_path_override: - msbuild_args += ['/p:FrameworkPathOverride=' + framework_path_override] - - msbuild_env = os.environ.copy() - - # Needed when running from Developer Command Prompt for VS - if 'PLATFORM' in msbuild_env: - del msbuild_env['PLATFORM'] + if framework_path: + msbuild_args += ['/p:FrameworkPathOverride=' + framework_path] try: subprocess.check_call(msbuild_args, env=msbuild_env) @@ -202,10 +231,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 7c1846dcc2..70fd1a35f1 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -2,24 +2,17 @@ import imp import os import sys +import subprocess -from SCons.Script import BoolVariable, Environment, Variables +from distutils.version import LooseVersion +from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') -def find_file_in_dir(directory, files, prefix='', extension=''): - if not extension.startswith('.'): - extension = '.' + extension - for curfile in files: - if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): - return curfile - return '' - - -def can_build(platform): - if platform in ["javascript"]: +def can_build(env, platform): + if platform in ['javascript']: return False # Not yet supported return True @@ -29,39 +22,68 @@ def is_enabled(): return False -def copy_file_no_replace(src_dir, dst_dir, name): +def get_doc_classes(): + return [ + '@C#', + 'CSharpScript', + 'GodotSharp', + ] + + +def get_doc_path(): + return 'doc_classes' + + +def find_file_in_dir(directory, files, prefix='', extension=''): + if not extension.startswith('.'): + extension = '.' + extension + for curfile in files: + if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): + return curfile + return '' + + +def 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 custom_path_is_dir_create(key, val, env): + """Validator to check if Path is a directory, creating it if it does not exist. + Similar to PathIsDirCreate, except it uses SCons.Script.Dir() and + SCons.Script.File() in order to support the '#' top level directory token. + """ + # Dir constructor will throw an error if the path points to a file + fsDir = Dir(val) + if not fsDir.exists: + os.makedirs(fsDir.abspath) def configure(env): env.use_ptrcall = True - env.add_module_version_string("mono") + env.add_module_version_string('mono') 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', custom_path_is_dir_create)) 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'] if env['platform'] == 'windows': - if mono_static: - raise RuntimeError('mono-static: Not supported on Windows') + mono_root = '' if bits == '32': if os.getenv('MONO32_PREFIX'): @@ -77,33 +99,63 @@ def configure(env): if not mono_root: raise RuntimeError('Mono installation directory not found') + mono_version = mono_root_try_find_mono_version(mono_root) + configure_for_mono_version(env, mono_version) + mono_lib_path = os.path.join(mono_root, 'lib') env.Append(LIBPATH=mono_lib_path) env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) - mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') + if mono_static: + lib_suffix = Environment()['LIBSUFFIX'] + + if env.msvc: + mono_static_lib_name = 'libmono-static-sgen' + else: + mono_static_lib_name = 'libmonosgen-2.0' + + if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)): + raise RuntimeError('Could not find static mono library in: ' + mono_lib_path) - if not mono_lib_name: - raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + if env.msvc: + env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix) + + env.Append(LINKFLAGS='Mincore' + lib_suffix) + env.Append(LINKFLAGS='msvcrt' + lib_suffix) + env.Append(LINKFLAGS='LIBCMT' + lib_suffix) + env.Append(LINKFLAGS='Psapi' + lib_suffix) + else: + env.Append(LINKFLAGS=os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)) - if os.getenv('VCINSTALLDIR'): - env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + env.Append(LIBS='psapi') + env.Append(LIBS='version') else: - env.Append(LIBS=mono_lib_name) + mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') - mono_bin_path = os.path.join(mono_root, 'bin') + if not mono_lib_name: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + if env.msvc: + env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + else: + env.Append(LIBS=mono_lib_name) + + mono_bin_path = os.path.join(mono_root, 'bin') - mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') + mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') - if not mono_dll_name: - raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) + 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'): @@ -112,7 +164,17 @@ def configure(env): if os.getenv('MONO64_PREFIX'): mono_root = os.getenv('MONO64_PREFIX') + # We can't use pkg-config to link mono statically, + # but we can still use it to find the mono root directory + if not mono_root and mono_static: + mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext) + if not mono_root: + raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') + if mono_root: + mono_version = mono_root_try_find_mono_version(mono_root) + configure_for_mono_version(env, mono_version) + mono_lib_path = os.path.join(mono_root, 'lib') env.Append(LIBPATH=mono_lib_path) @@ -128,18 +190,18 @@ def configure(env): if mono_static: mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a') - if sys.platform == "darwin": + if sys.platform == 'darwin': env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) - elif sys.platform == "linux" or sys.platform == "linux2": + elif sys.platform == 'linux' or sys.platform == 'linux2': env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) else: raise RuntimeError('mono-static: Not supported on this platform') else: env.Append(LIBS=[mono_lib]) - if sys.platform == "darwin": + if sys.platform == 'darwin': env.Append(LIBS=['iconv', 'pthread']) - elif sys.platform == "linux" or sys.platform == "linux2": + elif sys.platform == 'linux' or sys.platform == 'linux2': env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) if not mono_static: @@ -148,15 +210,20 @@ 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') + assert not mono_static + + mono_version = pkgconfig_try_find_mono_version() + configure_for_mono_version(env, mono_version) env.ParseConfig('pkg-config monosgen-2 --cflags --libs') 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')) @@ -172,18 +239,52 @@ 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') -def get_doc_classes(): - return [ - "@C#", - "CSharpScript", - "GodotSharp", - ] +def configure_for_mono_version(env, mono_version): + if mono_version is None: + raise RuntimeError('Mono JIT compiler version not found') + print('Mono JIT compiler version: ' + str(mono_version)) + if mono_version >= LooseVersion("5.12.0"): + env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS']) -def get_doc_path(): - return "doc_classes" +def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext): + 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']: + name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext) + if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')): + return os.path.join(hint_dir, '..') + return '' + + +def pkgconfig_try_find_mono_version(): + from compat import decode_utf8 + + lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines() + greater_version = None + for line in lines: + try: + version = LooseVersion(decode_utf8(line)) + if greater_version is None or version > greater_version: + greater_version = version + except ValueError: + pass + return greater_version + + +def mono_root_try_find_mono_version(mono_root): + from compat import decode_utf8 + + output = subprocess.check_output([os.path.join(mono_root, 'bin', 'mono'), '--version']) + first_line = decode_utf8(output.splitlines()[0]) + try: + return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())]) + except (ValueError, IndexError): + return None diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 1901b2cae8..d2a861dbe8 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -49,6 +49,8 @@ #include "mono_gd/gd_mono_class.h" #include "mono_gd/gd_mono_marshal.h" #include "signal_awaiter_utils.h" +#include "utils/macros.h" +#include "utils/thread_local.h" #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var) @@ -118,6 +120,8 @@ void CSharpLanguage::init() { #ifdef TOOLS_ENABLED EditorNode::add_init_callback(&gdsharp_editor_init_callback); + + GLOBAL_DEF("mono/export/include_scripts_content", false); #endif } @@ -280,34 +284,42 @@ 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" "using System;\n" "\n" - "public class %CLASS_NAME% : %BASE_CLASS_NAME%\n" + "public class %CLASS% : %BASE%\n" "{\n" - " // Member variables here, example:\n" + " // Declare member variables here. Examples:\n" " // private int a = 2;\n" - " // private string b = \"textvar\";\n" + " // private string b = \"text\";\n" "\n" + " // Called when the node enters the scene tree for the first time.\n" " public override void _Ready()\n" " {\n" - " // Called every time the node is added to the scene.\n" - " // Initialization here\n" " \n" " }\n" "\n" - "// public override void _Process(float delta)\n" - "// {\n" - "// // Called every frame. Delta is time since last frame.\n" - "// // Update game logic here.\n" - "// \n" - "// }\n" + "// // Called every frame. 'delta' is the elapsed time since the previous frame.\n" + "// public override void _Process(float delta)\n" + "// {\n" + "// \n" + "// }\n" "}\n"; - script_template = script_template.replace("%BASE_CLASS_NAME%", p_base_class_name) - .replace("%CLASS_NAME%", p_class_name); + String base_class_name = get_base_class_name(p_base_class_name, p_class_name); + script_template = script_template.replace("%BASE%", base_class_name) + .replace("%CLASS%", p_class_name); Ref<CSharpScript> script; script.instance(); @@ -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); @@ -422,7 +446,7 @@ String CSharpLanguage::make_function(const String &p_class, const String &p_name s += variant_type_to_managed_name(arg.get_slice(":", 1)) + " " + escape_csharp_keyword(arg.get_slice(":", 0)); } - s += ")\n{\n // Replace with function body\n}\n"; + s += ")\n{\n // Replace with function body.\n}\n"; return s; #else @@ -452,9 +476,9 @@ String CSharpLanguage::_get_indentation() const { 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 + _TLS_RECURSION_GUARD_V_(Vector<StackInfo>()); - 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()); @@ -476,15 +500,15 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() #ifdef DEBUG_ENABLED Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) { - // Printing an error here could result in endless recursion, so we must be careful + _TLS_RECURSION_GUARD_V_(Vector<StackInfo>()); - MonoObject *exc = NULL; + MonoException *exc = NULL; GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames); - MonoArray *frames = st_get_frames(p_stack_trace, &exc); + MonoArray *frames = st_get_frames(p_stack_trace, (MonoObject **)&exc); if (exc) { - GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */); + GDMonoUtils::debug_print_unhandled_exception(exc); return Vector<StackInfo>(); } @@ -499,16 +523,16 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec si.resize(frame_count); for (int i = 0; i < frame_count; i++) { - StackInfo &sif = si[i]; + StackInfo &sif = si.write[i]; MonoObject *frame = mono_array_get(frames, MonoObject *, i); MonoString *file_name; int file_line_num; MonoString *method_decl; - get_sf_info(frame, &file_name, &file_line_num, &method_decl, &exc); + get_sf_info(frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc); if (exc) { - GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */); + GDMonoUtils::debug_print_unhandled_exception(exc); return Vector<StackInfo>(); } @@ -537,12 +561,12 @@ void CSharpLanguage::frame() { ERR_FAIL_NULL(thunk); - MonoObject *ex; - thunk(task_scheduler, &ex); + MonoException *exc = NULL; + thunk(task_scheduler, (MonoObject **)&exc); - if (ex) { - mono_print_unhandled_exception(ex); - ERR_FAIL(); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); + _UNREACHABLE_(); } } } @@ -614,33 +638,41 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft #ifdef TOOLS_ENABLED void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { - if (gdmono->is_runtime_initialized()) { + if (!gdmono->is_runtime_initialized()) + return; - GDMonoAssembly *proj_assembly = gdmono->get_project_assembly(); + GDMonoAssembly *proj_assembly = gdmono->get_project_assembly(); - String name = ProjectSettings::get_singleton()->get("application/config/name"); - if (name.empty()) { - name = "UnnamedProject"; - } + String name = ProjectSettings::get_singleton()->get("application/config/name"); + if (name.empty()) { + name = "UnnamedProject"; + } - if (proj_assembly) { - String proj_asm_path = proj_assembly->get_path(); + name += ".dll"; - if (!FileAccess::exists(proj_assembly->get_path())) { - // Maybe it wasn't loaded from the default path, so check this as well - proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name); - if (!FileAccess::exists(proj_asm_path)) - return; // No assembly to load - } + if (proj_assembly) { + String proj_asm_path = proj_assembly->get_path(); - if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time()) - return; // Already up to date - } else { - if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name))) + if (!FileAccess::exists(proj_assembly->get_path())) { + // Maybe it wasn't loaded from the default path, so check this as well + proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name); + if (!FileAccess::exists(proj_asm_path)) return; // No assembly to load } + + if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time()) + return; // Already up to date + } else { + if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name))) + return; // No assembly to load } + if (!gdmono->get_core_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) + return; // The core API assembly to load is invalidated + + if (!gdmono->get_editor_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) + return; // The editor API assembly to load is invalidated + #ifndef NO_THREADS lock->lock(); #endif @@ -704,6 +736,9 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { obj->get_script_instance()->get_property_state(state); map[obj->get_instance_id()] = state; obj->set_script(RefPtr()); + } else { + // no instance found. Let's remove it so we don't loop forever + E->get()->placeholders.erase(E->get()->placeholders.front()->get()); } } @@ -715,16 +750,31 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { } } - if (gdmono->reload_scripts_domain() != OK) + if (gdmono->reload_scripts_domain() != OK) { + // Failed to reload the scripts domain + // Make sure to add the scripts back to their owners before returning + for (Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > >::Element *E = to_reload.front(); E; E = E->next()) { + Ref<CSharpScript> scr = E->key(); + for (Map<ObjectID, List<Pair<StringName, Variant> > >::Element *F = E->get().front(); F; F = F->next()) { + Object *obj = ObjectDB::get_instance(F->key()); + if (!obj) + continue; + obj->set_script(scr.get_ref_ptr()); + // Save reload state for next time if not saved + if (!scr->pending_reload_state.has(obj->get_instance_id())) { + scr->pending_reload_state[obj->get_instance_id()] = F->get(); + } + } + } return; + } for (Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > >::Element *E = to_reload.front(); E; E = E->next()) { Ref<CSharpScript> scr = E->key(); - scr->signals_invalidated = true; scr->exports_invalidated = true; + scr->signals_invalidated = true; scr->reload(p_soft_reload); - scr->update_signals(); scr->update_exports(); //restore state if saved @@ -747,6 +797,14 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { continue; } + if (scr->valid && scr->is_tool() && obj->get_script_instance()->is_placeholder()) { + // Script instance was a placeholder, but now the script was built successfully and is a tool script. + // We have to replace the placeholder with an actual C# script instance. + scr->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(obj->get_script_instance())); + ScriptInstance *script_instance = scr->instance_create(obj); + obj->set_script_instance(script_instance); // Not necessary as it's already done in instance_create, but just in case... + } + for (List<Pair<StringName, Variant> >::Element *G = F->get().front(); G; G = G->next()) { obj->get_script_instance()->set(G->get().first, G->get().second); } @@ -758,7 +816,7 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { } if (Engine::get_singleton()->is_editor_hint()) { - EditorNode::get_singleton()->get_property_editor()->update_tree(); + EditorNode::get_singleton()->get_inspector()->update_tree(); NodeDock::singleton->update_lists(); } } @@ -1117,11 +1175,11 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { GDMonoProperty *property = top->get_property(p_name); if (property) { - MonoObject *exc = NULL; + MonoException *exc = NULL; MonoObject *value = property->get_value(mono_object, &exc); if (exc) { r_ret = Variant(); - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } else { r_ret = GDMonoMarshal::mono_object_to_variant(value); } @@ -1353,21 +1411,27 @@ bool CSharpInstance::refcount_decremented() { return ref_dying; } -ScriptInstance::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember *p_member) const { +MultiplayerAPI::RPCMode CSharpInstance::_member_get_rpc_mode(GDMonoClassMember *p_member) const { if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) - return RPC_MODE_REMOTE; + return MultiplayerAPI::RPC_MODE_REMOTE; if (p_member->has_attribute(CACHED_CLASS(SyncAttribute))) - return RPC_MODE_SYNC; + return MultiplayerAPI::RPC_MODE_SYNC; if (p_member->has_attribute(CACHED_CLASS(MasterAttribute))) - return RPC_MODE_MASTER; + return MultiplayerAPI::RPC_MODE_MASTER; if (p_member->has_attribute(CACHED_CLASS(SlaveAttribute))) - return RPC_MODE_SLAVE; + return MultiplayerAPI::RPC_MODE_SLAVE; + if (p_member->has_attribute(CACHED_CLASS(RemoteSyncAttribute))) + return MultiplayerAPI::RPC_MODE_REMOTESYNC; + if (p_member->has_attribute(CACHED_CLASS(MasterSyncAttribute))) + return MultiplayerAPI::RPC_MODE_MASTERSYNC; + if (p_member->has_attribute(CACHED_CLASS(SlaveSyncAttribute))) + return MultiplayerAPI::RPC_MODE_SLAVESYNC; - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } -ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const { +MultiplayerAPI::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const { GDMonoClass *top = script->script_class; @@ -1380,10 +1444,10 @@ ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) top = top->get_parent_class(); } - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } -ScriptInstance::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variable) const { +MultiplayerAPI::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variable) const { GDMonoClass *top = script->script_class; @@ -1401,7 +1465,7 @@ ScriptInstance::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variab top = top->get_parent_class(); } - return RPC_MODE_DISABLED; + return MultiplayerAPI::RPC_MODE_DISABLED; } void CSharpInstance::notification(int p_notification) { @@ -1504,8 +1568,12 @@ void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List bool CSharpScript::_update_exports() { #ifdef TOOLS_ENABLED - if (!valid) + if (!valid) { + for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { + E->get()->set_build_failed(true); + } return false; + } bool changed = false; @@ -1527,12 +1595,12 @@ bool CSharpScript::_update_exports() { CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0); - MonoObject *ex = NULL; - ctor->invoke(tmp_object, NULL, &ex); + MonoException *exc = NULL; + ctor->invoke(tmp_object, NULL, &exc); - if (ex) { + if (exc) { ERR_PRINT("Exception thrown from constructor of temporary MonoObject:"); - mono_print_unhandled_exception(ex); + GDMonoUtils::debug_print_unhandled_exception(exc); tmp_object = NULL; ERR_FAIL_V(false); } @@ -1581,11 +1649,11 @@ bool CSharpScript::_update_exports() { exported_members_cache.push_front(prop_info); if (tmp_object) { - MonoObject *exc = NULL; + MonoException *exc = NULL; MonoObject *ret = property->get_value(tmp_object, &exc); if (exc) { exported_members_defval_cache[name] = Variant(); - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); } else { exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret); } @@ -1607,6 +1675,7 @@ bool CSharpScript::_update_exports() { _update_exports_values(values, propnames); for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { + E->get()->set_build_failed(false); E->get()->update(propnames, values); } } @@ -1616,45 +1685,38 @@ bool CSharpScript::_update_exports() { return false; } -bool CSharpScript::_update_signals() { -#ifdef TOOLS_ENABLED - if (!valid) - return false; - - bool changed = false; - - if (signals_invalidated) { - signals_invalidated = false; +void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class) { - GDMonoClass *top = script_class; + // no need to load the script's signals more than once + if (!signals_invalidated) { + return; + } - _signals.clear(); - changed = true; // TODO Do a real check for change + // make sure this classes signals are empty when loading for the first time + _signals.clear(); - 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 *top = p_class; + while (top && top != p_native_class) { + const Vector<GDMonoClass *> &delegates = top->get_all_delegates(); + for (int i = delegates.size() - 1; i >= 0; --i) { + Vector<Argument> parameters; - GDMonoClass *delegate = delegates[i]; + GDMonoClass *delegate = delegates[i]; - if (_get_signal(top, delegate, parameters)) { - _signals[delegate->get_name()] = parameters; - } + if (_get_signal(top, delegate, parameters)) { + _signals[delegate->get_name()] = parameters; } - - top = top->get_parent_class(); } + + top = top->get_parent_class(); } - return changed; -#endif - return false; + signals_invalidated = false; } 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); + MonoType *raw_type = p_delegate->get_mono_type(); if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) { // Arguments are accessibles as arguments of .Invoke method @@ -1724,7 +1786,7 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute)); - PropertyHint hint; + PropertyHint hint = PROPERTY_HINT_NONE; String hint_string; if (variant_type == Variant::NIL) { @@ -1772,6 +1834,12 @@ void CSharpScript::_clear() { Variant CSharpScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { + if (unlikely(GDMono::get_singleton() == NULL)) { + // Probably not the best error but eh. + r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; + return Variant(); + } + GDMonoClass *top = script_class; while (top && top != native) { @@ -1882,6 +1950,8 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class) { top = top->get_parent_class(); } + script->load_script_signals(script->script_class, script->native); + return script; } @@ -1902,7 +1972,11 @@ bool CSharpScript::can_instance() const { } #endif - return valid || (!tool && !ScriptServer::is_scripting_enabled()); +#ifdef TOOLS_ENABLED + return valid && (tool || ScriptServer::is_scripting_enabled()); +#else + return valid; +#endif } StringName CSharpScript::get_instance_base_type() const { @@ -1952,7 +2026,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg // Construct GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount); - ctor->invoke(mono_object, p_args, NULL); + ctor->invoke(mono_object, p_args); // Tie managed to unmanaged instance->gchandle = MonoGCHandle::create_strong(mono_object); @@ -2000,22 +2074,23 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Variant::Call ScriptInstance *CSharpScript::instance_create(Object *p_this) { - if (!valid) - return NULL; - - if (!tool && !ScriptServer::is_scripting_enabled()) { -#ifdef TOOLS_ENABLED - PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this)); - placeholders.insert(si); - _update_exports(); - _update_signals(); - return si; -#else - return NULL; +#ifdef DEBUG_ENABLED + CRASH_COND(!valid); #endif + + if (!script_class) { + if (GDMono::get_singleton()->get_project_assembly() == NULL) { + // The project assembly is not loaded + ERR_EXPLAIN("Cannot instance script because the project assembly is not loaded. Script: " + get_path()); + ERR_FAIL_V(NULL); + } else { + // The project assembly is loaded, but the class could not found + ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path()); + ERR_FAIL_V(NULL); + } } - update_signals(); + ERR_FAIL_COND_V(!valid, NULL); if (native) { String native_name = native->get_name(); @@ -2032,6 +2107,18 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { return _create_instance(NULL, 0, p_this, Object::cast_to<Reference>(p_this), unchecked_error); } +PlaceHolderScriptInstance *CSharpScript::placeholder_instance_create(Object *p_this) { + +#ifdef TOOLS_ENABLED + PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this)); + placeholders.insert(si); + _update_exports(); + return si; +#else + return NULL; +#endif +} + bool CSharpScript::instance_has(const Object *p_this) const { #ifndef NO_THREADS @@ -2069,6 +2156,9 @@ void CSharpScript::set_source_code(const String &p_code) { bool CSharpScript::has_method(const StringName &p_method) const { + if (!script_class) + return false; + return script_class->has_fetched_method_unknown_params(p_method); } @@ -2091,20 +2181,13 @@ Error CSharpScript::reload(bool p_keep_state) { if (project_assembly) { script_class = project_assembly->get_object_derived_class(name); - if (!script_class) { - ERR_PRINTS("Cannot find class " + name + " for script " + get_path()); - } -#ifdef DEBUG_ENABLED - else if (OS::get_singleton()->is_stdout_verbose()) { - OS::get_singleton()->print(String("Found class " + script_class->get_namespace() + "." + - script_class->get_name() + " for script " + get_path() + "\n") - .utf8()); - } -#endif - valid = script_class != NULL; if (script_class) { +#ifdef DEBUG_ENABLED + print_verbose("Found class " + script_class->get_namespace() + "." + script_class->get_name() + " for script " + get_path()); +#endif + tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute)); native = GDMonoUtils::get_class_native_base(script_class); @@ -2142,6 +2225,8 @@ Error CSharpScript::reload(bool p_keep_state) { top->fetch_methods_with_godot_api_checks(native); top = top->get_parent_class(); } + + load_script_signals(script_class, native); } return OK; @@ -2201,12 +2286,6 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const { } } -void CSharpScript::update_signals() { -#ifdef TOOLS_ENABLED - _update_signals(); -#endif -} - Ref<Script> CSharpScript::get_base_script() const { // TODO search in metadata file once we have it, not important any way? @@ -2271,9 +2350,10 @@ CSharpScript::CSharpScript() : #ifdef TOOLS_ENABLED source_changed_cache = false; exports_invalidated = true; - signals_invalidated = true; #endif + signals_invalidated = true; + _resource_path_changed(); #ifdef DEBUG_ENABLED @@ -2336,7 +2416,9 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p CRASH_COND(mono_domain_get() == NULL); #endif -#else +#endif + +#ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() && mono_domain_get() == NULL) { CRASH_COND(Thread::get_caller_id() == Thread::get_main_id()); @@ -2345,14 +2427,20 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p // because this may be called by one of the editor's worker threads. // Attach this thread temporarily to reload the script. - MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); - CRASH_COND(mono_thread == NULL); + if (SCRIPTS_DOMAIN) { + MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); + CRASH_COND(mono_thread == NULL); + script->reload(); + mono_thread_detach(mono_thread); + } + + } else { // just reload it normally +#endif script->reload(); - mono_thread_detach(mono_thread); - } else // just reload it normally +#ifdef TOOLS_ENABLED + } #endif - script->reload(); if (r_error) *r_error = OK; diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index b4fecfa912..1a5d0c8a69 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -90,15 +90,15 @@ class CSharpScript : public Script { 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; - Map<StringName, Vector<Argument> > _signals; - bool signals_invalidated; - void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames); virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder); #endif @@ -111,7 +111,7 @@ class CSharpScript : public Script { void _clear(); - bool _update_signals(); + void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class); bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms); bool _update_exports(); @@ -139,6 +139,7 @@ public: virtual bool can_instance() const; virtual StringName get_instance_base_type() const; virtual ScriptInstance *instance_create(Object *p_this); + virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this); virtual bool instance_has(const Object *p_this) const; virtual bool has_source_code() const; @@ -149,7 +150,6 @@ public: 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; @@ -192,7 +192,7 @@ class CSharpInstance : public ScriptInstance { void _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount); - RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const; + MultiplayerAPI::RPCMode _member_get_rpc_mode(GDMonoClassMember *p_member) const; public: MonoObject *get_mono_object() const; @@ -213,8 +213,8 @@ public: virtual void refcount_incremented(); virtual bool refcount_decremented(); - virtual RPCMode get_rpc_mode(const StringName &p_method) const; - virtual RPCMode get_rset_mode(const StringName &p_variable) const; + virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const; + virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const; virtual void notification(int p_notification); void call_notification_no_check(MonoObject *p_mono_object, int p_notification); @@ -293,7 +293,8 @@ public: virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; 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; } + /* 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, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) 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 0c2bb948ea..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.1-dev"> +<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 9bd57f1d4d..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.1-dev"> +<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 51f07523e7..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.1-dev"> +<class name="GodotSharp" inherits="Object" category="Core" version="3.1"> <brief_description> </brief_description> <description> diff --git a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs index 04da0600cc..16beacb45c 100644 --- a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs +++ b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs @@ -16,24 +16,48 @@ namespace GodotSharpTools.Build private extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode); [MethodImpl(MethodImplOptions.InternalCall)] - private extern static void godot_icall_BuildInstance_get_MSBuildInfo(ref string msbuildPath, ref string frameworkPath); + private extern static string godot_icall_BuildInstance_get_MSBuildPath(); + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static string godot_icall_BuildInstance_get_FrameworkPath(); + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static string godot_icall_BuildInstance_get_MonoWindowsBinDir(); + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static bool godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows(); - private struct MSBuildInfo + private static string GetMSBuildPath() { - public string path; - public string frameworkPathOverride; + string msbuildPath = godot_icall_BuildInstance_get_MSBuildPath(); + + if (msbuildPath == null) + throw new FileNotFoundException("Cannot find the MSBuild executable."); + + return msbuildPath; } - private static MSBuildInfo GetMSBuildInfo() + private static string GetFrameworkPath() { - MSBuildInfo msbuildInfo = new MSBuildInfo(); + return godot_icall_BuildInstance_get_FrameworkPath(); + } - godot_icall_BuildInstance_get_MSBuildInfo(ref msbuildInfo.path, ref msbuildInfo.frameworkPathOverride); + private static string MonoWindowsBinDir + { + get + { + string monoWinBinDir = godot_icall_BuildInstance_get_MonoWindowsBinDir(); - if (msbuildInfo.path == null) - throw new FileNotFoundException("Cannot find the MSBuild executable."); + if (monoWinBinDir == null) + throw new FileNotFoundException("Cannot find the Windows Mono binaries directory."); - return msbuildInfo; + return monoWinBinDir; + } + } + + private static bool UsingMonoMSBuildOnWindows + { + get + { + return godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows(); + } } private string solution; @@ -54,25 +78,38 @@ namespace GodotSharpTools.Build public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null) { - MSBuildInfo msbuildInfo = GetMSBuildInfo(); + bool debugMSBuild = IsDebugMSBuildRequested(); List<string> customPropertiesList = new List<string>(); if (customProperties != null) customPropertiesList.AddRange(customProperties); - if (msbuildInfo.frameworkPathOverride != null) - customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride); + string frameworkPath = GetFrameworkPath(); + + if (!string.IsNullOrEmpty(frameworkPath)) + customPropertiesList.Add("FrameworkPathOverride=" + frameworkPath); string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList); - ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs); + ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs); + + bool redirectOutput = !debugMSBuild; - // No console output, thanks - startInfo.RedirectStandardOutput = true; - startInfo.RedirectStandardError = true; + startInfo.RedirectStandardOutput = redirectOutput; + startInfo.RedirectStandardError = redirectOutput; startInfo.UseShellExecute = false; + if (UsingMonoMSBuildOnWindows) + { + // These environment variables are required for Mono's MSBuild to find the compilers. + // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono. + string monoWinBinDir = MonoWindowsBinDir; + startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat")); + startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat")); + startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat")); + } + // Needed when running from Developer Command Prompt for VS RemovePlatformVariable(startInfo.EnvironmentVariables); @@ -82,8 +119,11 @@ namespace GodotSharpTools.Build process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); + if (redirectOutput) + { + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + } process.WaitForExit(); @@ -95,28 +135,41 @@ namespace GodotSharpTools.Build public bool BuildAsync(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null) { + bool debugMSBuild = IsDebugMSBuildRequested(); + if (process != null) throw new InvalidOperationException("Already in use"); - MSBuildInfo msbuildInfo = GetMSBuildInfo(); - List<string> customPropertiesList = new List<string>(); if (customProperties != null) customPropertiesList.AddRange(customProperties); - if (msbuildInfo.frameworkPathOverride.Length > 0) - customPropertiesList.Add("FrameworkPathOverride=" + msbuildInfo.frameworkPathOverride); + string frameworkPath = GetFrameworkPath(); + + if (!string.IsNullOrEmpty(frameworkPath)) + customPropertiesList.Add("FrameworkPathOverride=" + frameworkPath); string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList); - ProcessStartInfo startInfo = new ProcessStartInfo(msbuildInfo.path, compilerArgs); + ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs); - // No console output, thanks - startInfo.RedirectStandardOutput = true; - startInfo.RedirectStandardError = true; + bool redirectOutput = !debugMSBuild; + + startInfo.RedirectStandardOutput = redirectOutput; + startInfo.RedirectStandardError = redirectOutput; startInfo.UseShellExecute = false; + if (UsingMonoMSBuildOnWindows) + { + // These environment variables are required for Mono's MSBuild to find the compilers. + // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono. + string monoWinBinDir = MonoWindowsBinDir; + startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat")); + startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat")); + startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat")); + } + // Needed when running from Developer Command Prompt for VS RemovePlatformVariable(startInfo.EnvironmentVariables); @@ -127,8 +180,11 @@ namespace GodotSharpTools.Build process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); + if (redirectOutput) + { + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + } return true; } @@ -176,6 +232,11 @@ namespace GodotSharpTools.Build Dispose(); } + private static bool IsDebugMSBuildRequested() + { + return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1"; + } + public void Dispose() { if (process != null) diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj index 981083a3c2..1c8714e31d 100644 --- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj +++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj @@ -11,7 +11,7 @@ </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> - <DebugType>full</DebugType> + <DebugType>portable</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug</OutputPath> <DefineConstants>DEBUG;</DefineConstants> @@ -20,7 +20,7 @@ <ConsolePause>false</ConsolePause> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>full</DebugType> + <DebugType>portable</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release</OutputPath> <ErrorReport>prompt</ErrorReport> diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs index 6bf54a0156..1d863e6f61 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs @@ -70,7 +70,7 @@ namespace GodotSharpTools.Project var toolsGroup = root.AddPropertyGroup(); toolsGroup.Condition = " '$(Configuration)|$(Platform)' == 'Tools|AnyCPU' "; toolsGroup.AddProperty("DebugSymbols", "true"); - toolsGroup.AddProperty("DebugType", "full"); + toolsGroup.AddProperty("DebugType", "portable"); toolsGroup.AddProperty("Optimize", "false"); toolsGroup.AddProperty("DefineConstants", "DEBUG;TOOLS;"); toolsGroup.AddProperty("ErrorReport", "prompt"); @@ -148,7 +148,7 @@ namespace GodotSharpTools.Project var debugGroup = root.AddPropertyGroup(); debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "; debugGroup.AddProperty("DebugSymbols", "true"); - debugGroup.AddProperty("DebugType", "full"); + debugGroup.AddProperty("DebugType", "portable"); debugGroup.AddProperty("Optimize", "false"); debugGroup.AddProperty("DefineConstants", "DEBUG;"); debugGroup.AddProperty("ErrorReport", "prompt"); @@ -157,7 +157,7 @@ namespace GodotSharpTools.Project var releaseGroup = root.AddPropertyGroup(); releaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "; - releaseGroup.AddProperty("DebugType", "full"); + releaseGroup.AddProperty("DebugType", "portable"); releaseGroup.AddProperty("Optimize", "true"); releaseGroup.AddProperty("ErrorReport", "prompt"); releaseGroup.AddProperty("WarningLevel", "4"); diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 952e033565..b97bb5e95f 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" @@ -102,8 +100,8 @@ #define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot" #define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type #define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array" -#define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary" -#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object" + +#define BINDINGS_GENERATOR_VERSION UINT32_C(2) const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n"; @@ -248,14 +246,14 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { if (imethod.is_virtual) continue; - const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type); + const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type); String im_sig; String im_unique_sig; if (p_itype.is_object_type) { im_sig += "IntPtr " CS_PARAM_METHODBIND ", "; - im_unique_sig += imethod.return_type.operator String() + ",IntPtr,IntPtr"; + im_unique_sig += imethod.return_type.cname.operator String() + ",IntPtr,IntPtr"; } im_sig += "IntPtr " CS_PARAM_INSTANCE; @@ -263,7 +261,7 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { // Get arguments information int i = 0; for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) { - const TypeInterface *arg_type = _get_type_by_name_or_placeholder(F->get().type); + const TypeInterface *arg_type = _get_type_or_placeholder(F->get().type); im_sig += ", "; im_sig += arg_type->im_type_in; @@ -514,6 +512,15 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo data.resize(file_data.uncompressed_size); Compression::decompress(data.ptrw(), file_data.uncompressed_size, file_data.data, file_data.compressed_size, Compression::MODE_DEFLATE); + String output_dir = output_file.get_base_dir(); + + if (!DirAccess::exists(output_dir)) { + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); + Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(output_dir)); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + } + FileAccessRef file = FileAccess::open(output_file, FileAccess::WRITE); ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE); file->store_buffer(data.ptr(), data.size()); @@ -529,7 +536,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 +566,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 +641,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 +671,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 +737,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 = itype.is_object_type && !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 partial class " : (is_abstract ? "abstract partial class " : "partial class ")); output.push_back(itype.proxy_name); if (itype.is_singleton) { @@ -882,7 +906,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 +936,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 +980,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); @@ -1052,12 +1076,12 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte } if (getter && setter) { - ERR_FAIL_COND_V(getter->return_type != setter->arguments.back()->get().type, ERR_BUG); + ERR_FAIL_COND_V(getter->return_type.cname != setter->arguments.back()->get().type.cname, ERR_BUG); } - StringName proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type; + const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type; - const TypeInterface *prop_itype = _get_type_by_name_or_null(proptype_name); + const TypeInterface *prop_itype = _get_type_or_null(proptype_name); ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(p_iprop.cname)); @@ -1105,9 +1129,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.push_back(getter->proxy_name + "("); if (p_iprop.index != -1) { const ArgumentInterface &idx_arg = getter->arguments.front()->get(); - if (idx_arg.type != name_cache.type_int) { + if (idx_arg.type.cname != name_cache.type_int) { // Assume the index parameter is an enum - const TypeInterface *idx_arg_type = _get_type_by_name_or_null(idx_arg.type); + const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type); CRASH_COND(idx_arg_type == NULL); p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index)); } else { @@ -1122,9 +1146,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.push_back(setter->proxy_name + "("); if (p_iprop.index != -1) { const ArgumentInterface &idx_arg = setter->arguments.front()->get(); - if (idx_arg.type != name_cache.type_int) { + if (idx_arg.type.cname != name_cache.type_int) { // Assume the index parameter is an enum - const TypeInterface *idx_arg_type = _get_type_by_name_or_null(idx_arg.type); + const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type); CRASH_COND(idx_arg_type == NULL); p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", "); } else { @@ -1141,7 +1165,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output) { - const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type); + const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type); String method_bind_field = "method_bind_" + itos(p_method_bind_count); @@ -1158,7 +1182,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Retrieve information from the arguments for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { const ArgumentInterface &iarg = F->get(); - const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type); + const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); // Add the current arguments to the signature // If the argument has a default value which is not a constant, we will make it Nullable @@ -1229,7 +1253,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,22 +1334,19 @@ 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; - im_call += "." + im_icall->name + "(" + icall_params + ");\n"; + String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS; + im_call += "." + im_icall->name + "(" + icall_params + ")"; if (p_imethod.arguments.size()) p_output.push_back(cs_in_statements); if (return_type->cname == name_cache.type_void) { - p_output.push_back(im_call); + p_output.push_back(im_call + ";\n"); } else if (return_type->cs_out.empty()) { - p_output.push_back("return " + im_call); + p_output.push_back("return " + im_call + ";\n"); } else { - p_output.push_back(return_type->im_type_out); - p_output.push_back(" " LOCAL_RET " = "); - p_output.push_back(im_call); - p_output.push_back(INDENT3); - p_output.push_back(sformat(return_type->cs_out, LOCAL_RET) + "\n"); + p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out)); + p_output.push_back("\n"); } p_output.push_back(CLOSE_BLOCK_L2); @@ -1400,25 +1421,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 +1515,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); @@ -1507,9 +1544,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte if (p_imethod.is_virtual) return OK; // Ignore - bool ret_void = p_imethod.return_type == name_cache.type_void; + bool ret_void = p_imethod.return_type.cname == name_cache.type_void; - const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type); + const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type); String argc_str = itos(p_imethod.arguments.size()); @@ -1521,16 +1558,16 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte int i = 0; for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { const ArgumentInterface &iarg = F->get(); - const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type); + const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); String c_param_name = "arg" + itos(i + 1); if (p_imethod.is_vararg) { if (i < p_imethod.arguments.size() - 1) { c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name); - c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(0, "; - c_in_statements += sformat("&%s_in", c_param_name); - c_in_statements += ");\n"; + 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) @@ -1622,7 +1659,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n" "\t\t" C_LOCAL_PTRCALL_ARGS ".set("); p_output.push_back(real_argc_str); - p_output.push_back(" + i, &varargs[i]);\n\t" CLOSE_BLOCK); + p_output.push_back(" + i, &varargs.write[i]);\n\t" CLOSE_BLOCK); } else { p_output.push_back(c_in_statements); p_output.push_back("\tconst void* " C_LOCAL_PTRCALL_ARGS "["); @@ -1662,42 +1699,49 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte return OK; } -const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_null(const StringName &p_cname) { +const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(const TypeReference &p_typeref) { - const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_cname); + const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_typeref.cname); if (builtin_type_match) return &builtin_type_match->get(); - const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_cname); + const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_typeref.cname); if (obj_type_match) return &obj_type_match.get(); - const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_cname); + if (p_typeref.is_enum) { + const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_typeref.cname); + + if (enum_match) + return &enum_match->get(); - if (enum_match) - return &enum_match->get(); + // Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead. + const Map<StringName, TypeInterface>::Element *int_match = builtin_types.find(name_cache.type_int); + ERR_FAIL_NULL_V(int_match, NULL); + return &int_match->get(); + } return NULL; } -const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_placeholder(const StringName &p_cname) { +const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) { - const TypeInterface *found = _get_type_by_name_or_null(p_cname); + const TypeInterface *found = _get_type_or_null(p_typeref); if (found) return found; - ERR_PRINTS(String() + "Type not found. Creating placeholder: " + p_cname.operator String()); + ERR_PRINTS(String() + "Type not found. Creating placeholder: " + p_typeref.cname.operator String()); - const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_cname); + const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_typeref.cname); if (match) return &match->get(); TypeInterface placeholder; - TypeInterface::create_placeholder_type(placeholder, p_cname); + TypeInterface::create_placeholder_type(placeholder, p_typeref.cname); return &placeholder_types.insert(placeholder.cname, placeholder)->get(); } @@ -1730,6 +1774,13 @@ void BindingsGenerator::_populate_object_type_interfaces() { continue; } + if (!ClassDB::is_class_enabled(type_cname)) { + if (verbose_output) + WARN_PRINTS("Ignoring type " + type_cname.operator String() + " because it's not enabled"); + class_list.pop_front(); + continue; + } + ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(type_cname); TypeInterface itype = TypeInterface::create_object_type(type_cname, api_type); @@ -1841,7 +1892,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { // The method Object.free is registered as a virtual method, but without the virtual flag. // This is because this method is not supposed to be overridden, but called. // We assume the return type is void. - imethod.return_type = name_cache.type_void; + imethod.return_type.cname = name_cache.type_void; // Actually, more methods like this may be added in the future, // which could actually will return something different. @@ -1856,21 +1907,22 @@ void BindingsGenerator::_populate_object_type_interfaces() { } else { ERR_PRINTS("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name); } - } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { // TODO redundant? - imethod.return_type = return_info.class_name; + } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + imethod.return_type.cname = return_info.class_name; + imethod.return_type.is_enum = true; } else if (return_info.class_name != StringName()) { - imethod.return_type = return_info.class_name; + imethod.return_type.cname = return_info.class_name; } else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { - imethod.return_type = return_info.hint_string; + imethod.return_type.cname = return_info.hint_string; } else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { - imethod.return_type = name_cache.type_Variant; + imethod.return_type.cname = name_cache.type_Variant; } else if (return_info.type == Variant::NIL) { - imethod.return_type = name_cache.type_void; + imethod.return_type.cname = name_cache.type_void; } else { - imethod.return_type = Variant::get_type_name(return_info.type); + imethod.return_type.cname = Variant::get_type_name(return_info.type); } - if (!itype.requires_collections && imethod.return_type == name_cache.type_Dictionary) + if (!itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary) itype.requires_collections = true; for (int i = 0; i < argc; i++) { @@ -1879,21 +1931,22 @@ void BindingsGenerator::_populate_object_type_interfaces() { ArgumentInterface iarg; iarg.name = arginfo.name; - if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { // TODO redundant? - iarg.type = arginfo.class_name; + if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + iarg.type.cname = arginfo.class_name; + iarg.type.is_enum = true; } else if (arginfo.class_name != StringName()) { - iarg.type = arginfo.class_name; + iarg.type.cname = arginfo.class_name; } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { - iarg.type = arginfo.hint_string; + iarg.type.cname = arginfo.hint_string; } else if (arginfo.type == Variant::NIL) { - iarg.type = name_cache.type_Variant; + iarg.type.cname = name_cache.type_Variant; } else { - iarg.type = Variant::get_type_name(arginfo.type); + iarg.type.cname = Variant::get_type_name(arginfo.type); } iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); - if (!itype.requires_collections && iarg.type == name_cache.type_Dictionary) + if (!itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary) itype.requires_collections = true; if (m && m->has_default_argument(i)) { @@ -1905,7 +1958,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { if (imethod.is_vararg) { ArgumentInterface ivararg; - ivararg.type = name_cache.type_VarArg; + ivararg.type.cname = name_cache.type_VarArg; ivararg.name = "@args"; imethod.add_argument(ivararg); } @@ -1990,17 +2043,11 @@ void BindingsGenerator::_populate_object_type_interfaces() { itype.enums.push_back(ienum); TypeInterface enum_itype; + enum_itype.is_enum = true; enum_itype.name = itype.name + "." + String(*k); enum_itype.cname = StringName(enum_itype.name); enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name; - enum_itype.c_arg_in = "&%s"; - enum_itype.c_type = "int"; - enum_itype.c_type_in = "int"; - enum_itype.c_type_out = "int"; - enum_itype.cs_type = enum_itype.proxy_name; - enum_itype.im_type_in = enum_itype.proxy_name; - enum_itype.im_type_out = enum_itype.proxy_name; - enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[enum_itype.proxy_name]; + TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); } @@ -2035,7 +2082,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg switch (p_val.get_type()) { case Variant::NIL: - if (ClassDB::class_exists(r_iarg.type)) { + if (ClassDB::class_exists(r_iarg.type.cname)) { // Object type r_iarg.default_argument = "null"; } else { @@ -2048,7 +2095,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg r_iarg.default_argument = bool(p_val) ? "true" : "false"; break; case Variant::INT: - if (r_iarg.type != name_cache.type_int) { + if (r_iarg.type.cname != name_cache.type_int) { r_iarg.default_argument = "(%s)" + r_iarg.default_argument; } break; @@ -2109,7 +2156,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg default: {} } - if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type == name_cache.type_Variant && r_iarg.default_argument != "null") + if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null") r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF; } @@ -2128,7 +2175,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_arg_in = "&%s_in"; \ itype.c_type_in = m_type_in; \ itype.cs_in = "ref %s"; \ - itype.cs_out = "return (" #m_type ")%0;"; \ + itype.cs_out = "return (%1)%0;"; \ itype.im_type_out = "object"; \ builtin_types.insert(itype.cname, itype); \ } @@ -2223,7 +2270,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new NodePath(%0);"; + itype.cs_out = "return new %1(%0);"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; _populate_builtin_type(itype, Variant::NODE_PATH); @@ -2246,7 +2293,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new RID(%0);"; + itype.cs_out = "return new %1(%0);"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; _populate_builtin_type(itype, Variant::_RID); @@ -2303,7 +2350,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { #define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t) - INSERT_ARRAY(Array, object); INSERT_ARRAY(PoolIntArray, int); INSERT_ARRAY_FULL(PoolByteArray, PoolByteArray, byte); @@ -2321,20 +2367,36 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { #undef INSERT_ARRAY + // Array + itype = TypeInterface(); + itype.name = "Array"; + itype.cname = itype.name; + itype.proxy_name = "Array"; + itype.c_out = "\treturn memnew(Array(%1));\n"; + itype.c_type = itype.name; + itype.c_type_in = itype.c_type + "*"; + itype.c_type_out = itype.c_type + "*"; + itype.cs_type = itype.proxy_name; + itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; + itype.cs_out = "return new Array(%0);"; + itype.im_type_in = "IntPtr"; + itype.im_type_out = "IntPtr"; + builtin_types.insert(itype.cname, itype); + // Dictionary itype = TypeInterface(); itype.name = "Dictionary"; itype.cname = itype.name; - itype.proxy_name = "Dictionary<object, object>"; - itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_DICT "(%1);\n"; - itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_DICT "(%1);\n"; - itype.c_arg_in = "&%s_in"; + itype.proxy_name = "Dictionary"; + itype.c_out = "\treturn memnew(Dictionary(%1));\n"; itype.c_type = itype.name; - itype.c_type_in = "MonoObject*"; - itype.c_type_out = "MonoObject*"; + itype.c_type_in = itype.c_type + "*"; + itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; + itype.cs_out = "return new Dictionary(%0);"; + itype.im_type_in = "IntPtr"; + itype.im_type_out = "IntPtr"; builtin_types.insert(itype.cname, itype); // void (fictitious type to represent the return type of methods that do not return anything) @@ -2375,11 +2437,11 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant:: iarg.name = pi.name; if (pi.type == Variant::NIL) - iarg.type = name_cache.type_Variant; + iarg.type.cname = name_cache.type_Variant; else - iarg.type = Variant::get_type_name(pi.type); + iarg.type.cname = Variant::get_type_name(pi.type); - if (!r_itype.requires_collections && iarg.type == name_cache.type_Dictionary) + if (!r_itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary) r_itype.requires_collections = true; if ((mi.default_arguments.size() - mi.arguments.size() + i) >= 0) @@ -2390,12 +2452,12 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant:: if (mi.return_val.type == Variant::NIL) { if (mi.return_val.name != "") - imethod.return_type = name_cache.type_Variant; + imethod.return_type.cname = name_cache.type_Variant; } else { - imethod.return_type = Variant::get_type_name(mi.return_val.type); + imethod.return_type.cname = Variant::get_type_name(mi.return_val.type); } - if (!r_itype.requires_collections && imethod.return_type == name_cache.type_Dictionary) + if (!r_itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary) r_itype.requires_collections = true; if (r_itype.class_doc) { @@ -2461,13 +2523,11 @@ void BindingsGenerator::_populate_global_constants() { EnumInterface &ienum = E->get(); TypeInterface enum_itype; - enum_itype = TypeInterface::create_value_type(ienum.cname); - enum_itype.c_arg_in = "&%s"; - enum_itype.c_type = "int"; - enum_itype.c_type_in = "int"; - enum_itype.c_type_out = "int"; - enum_itype.im_type_in = enum_itype.name; - enum_itype.im_type_out = enum_itype.name; + enum_itype.is_enum = true; + enum_itype.name = ienum.cname.operator String(); + enum_itype.cname = ienum.cname; + enum_itype.proxy_name = enum_itype.name; + TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); ienum.prefix = _determine_enum_prefix(ienum); @@ -2488,15 +2548,13 @@ void BindingsGenerator::_populate_global_constants() { hardcoded_enums.push_back("Vector3.Axis"); for (List<StringName>::Element *E = hardcoded_enums.front(); E; E = E->next()) { // These enums are not generated and must be written manually (e.g.: Vector3.Axis) - // Here, we are assuming core types do not begin with underscore + // Here, we assume core types do not begin with underscore TypeInterface enum_itype; - enum_itype = TypeInterface::create_value_type(E->get()); - enum_itype.c_arg_in = "&%s"; - enum_itype.c_type = "int"; - enum_itype.c_type_in = "int"; - enum_itype.c_type_out = "int"; - enum_itype.im_type_in = enum_itype.name; - enum_itype.im_type_out = enum_itype.name; + enum_itype.is_enum = true; + enum_itype.name = E->get().operator String(); + enum_itype.cname = E->get(); + enum_itype.proxy_name = enum_itype.name; + TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); } } diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 9b5a9cea88..5b33a0e53f 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -81,6 +81,15 @@ class BindingsGenerator { const DocData::PropertyDoc *prop_doc; }; + struct TypeReference { + StringName cname; + bool is_enum; + + TypeReference() { + is_enum = false; + } + }; + struct ArgumentInterface { enum DefaultParamMode { CONSTANT, @@ -88,7 +97,8 @@ class BindingsGenerator { NULLABLE_REF }; - StringName type; + TypeReference type; + String name; String default_argument; DefaultParamMode def_param_mode; @@ -110,7 +120,7 @@ class BindingsGenerator { /** * [TypeInterface::name] of the return type */ - StringName return_type; + TypeReference return_type; /** * Determines if the method has a variable number of arguments (VarArg) @@ -146,7 +156,7 @@ class BindingsGenerator { } MethodInterface() { - return_type = BindingsGenerator::get_singleton()->name_cache.type_void; + return_type.cname = BindingsGenerator::get_singleton()->name_cache.type_void; is_vararg = false; is_virtual = false; requires_object_call = false; @@ -175,6 +185,7 @@ class BindingsGenerator { ClassDB::APIType api_type; + bool is_enum; bool is_object_type; bool is_singleton; bool is_reference; @@ -276,7 +287,9 @@ class BindingsGenerator { * One or more statements that determine how a variable of this type is returned from a method. * It must contain the return statement(s). * Formatting elements: - * %0 or %s: name of the variable to be returned + * %0: internal method call statement + * %1: [cs_type] of the return type + * %2: [im_type_out] of the return type */ String cs_out; @@ -293,8 +306,6 @@ class BindingsGenerator { /** * Type used for the return type of internal call methods. - * If [cs_out] is not empty and the method return type is not void, - * it is also used for the type of the return variable. */ String im_type_out; @@ -379,10 +390,24 @@ class BindingsGenerator { r_itype.im_type_out = r_itype.proxy_name; } + static void postsetup_enum_type(TypeInterface &r_enum_itype) { + r_enum_itype.c_arg_in = "&%s"; + r_enum_itype.c_type = "int"; + r_enum_itype.c_type_in = "int"; + r_enum_itype.c_type_out = "int"; + r_enum_itype.cs_type = r_enum_itype.proxy_name; + r_enum_itype.cs_in = "(int)%s"; + r_enum_itype.cs_out = "return (%1)%0;"; + r_enum_itype.im_type_in = "int"; + r_enum_itype.im_type_out = "int"; + r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name]; + } + TypeInterface() { api_type = ClassDB::API_NONE; + is_enum = false; is_object_type = false; is_singleton = false; is_reference = false; @@ -492,6 +517,8 @@ class BindingsGenerator { return "Ref"; else if (p_type.is_object_type) return "Obj"; + else if (p_type.is_enum) + return "int"; return p_type.name; } @@ -501,8 +528,8 @@ class BindingsGenerator { void _generate_header_icalls(); void _generate_method_icalls(const TypeInterface &p_itype); - const TypeInterface *_get_type_by_name_or_null(const StringName &p_cname); - const TypeInterface *_get_type_by_name_or_placeholder(const StringName &p_cname); + const TypeInterface *_get_type_or_null(const TypeReference &p_typeref); + const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref); void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg); void _populate_builtin_type(TypeInterface &r_itype, Variant::Type vtype); @@ -536,6 +563,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/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index e4269b0aec..bc95607743 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -47,11 +47,11 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi Variant dir = p_dir; Variant compile_items = p_files; const Variant *args[2] = { &dir, &compile_items }; - MonoObject *ex = NULL; - MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL_V(String()); } @@ -68,11 +68,11 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll Variant core_dll_path = p_core_dll_path; Variant compile_items = p_files; const Variant *args[3] = { &dir, &core_dll_path, &compile_items }; - MonoObject *ex = NULL; - MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL_V(String()); } @@ -89,11 +89,11 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve Variant name = p_name; Variant compile_items = p_files; const Variant *args[3] = { &dir, &name, &compile_items }; - MonoObject *ex = NULL; - MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL_V(String()); } @@ -110,11 +110,11 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str Variant item_type = p_item_type; Variant include = p_include; const Variant *args[3] = { &project_path, &item_type, &include }; - MonoObject *ex = NULL; - klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL(); } } diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 6b41b10981..2faab1718d 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -33,13 +33,16 @@ #include "main/main.h" #include "../godotsharp_dirs.h" -#include "../mono_gd/gd_mono.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_marshal.h" #include "../utils/path_utils.h" #include "bindings_generator.h" #include "godotsharp_editor.h" +#define PROP_NAME_MSBUILD_MONO "MSBuild (Mono)" +#define PROP_NAME_MSBUILD_VS "MSBuild (VS Build Tools)" +#define PROP_NAME_XBUILD "xbuild (Deprecated)" + void godot_icall_BuildInstance_ExitCallback(MonoString *p_solution, MonoString *p_config, int p_exit_code) { String solution = GDMonoMarshal::mono_string_to_godot(p_solution); @@ -61,6 +64,7 @@ String _find_build_engine_on_unix(const String &p_name) { const char *locations[] = { #ifdef OSX_ENABLED "/Library/Frameworks/Mono.framework/Versions/Current/bin/", + "/usr/local/var/homebrew/linked/mono/bin/", #endif "/opt/novell/mono/bin/" }; @@ -77,46 +81,41 @@ String _find_build_engine_on_unix(const String &p_name) { } #endif -void godot_icall_BuildInstance_get_MSBuildInfo(MonoString **r_msbuild_path, MonoString **r_framework_path) { +MonoString *godot_icall_BuildInstance_get_MSBuildPath() { GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool"))); #if defined(WINDOWS_ENABLED) switch (build_tool) { - case GodotSharpBuilds::MSBUILD: { + case GodotSharpBuilds::MSBUILD_VS: { static String msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path(); if (msbuild_tools_path.length()) { if (!msbuild_tools_path.ends_with("\\")) msbuild_tools_path += "\\"; - // FrameworkPathOverride - const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info(); - if (mono_reg_info.assembly_dir.length()) { - *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe"); - - String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5"); - *r_framework_path = GDMonoMarshal::mono_string_from_godot(framework_path); - } else { - ERR_PRINT("Cannot find Mono's assemblies directory in the registry"); - } - - return; + return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe"); } - if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n"); - } // fall through + print_verbose("Cannot find executable for '" PROP_NAME_MSBUILD_VS "'. Trying with '" PROP_NAME_MSBUILD_MONO "'..."); + } // FALL THROUGH case GodotSharpBuilds::MSBUILD_MONO: { String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat"); if (!FileAccess::exists(msbuild_path)) { - WARN_PRINTS("Cannot find msbuild ('mono/builds/build_tool'). Tried with path: " + msbuild_path); + WARN_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_MONO "'. Tried with path: " + msbuild_path); } - *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(msbuild_path); + return GDMonoMarshal::mono_string_from_godot(msbuild_path); + } break; + case GodotSharpBuilds::XBUILD: { + String xbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("xbuild.bat"); + + if (!FileAccess::exists(xbuild_path)) { + WARN_PRINTS("Cannot find executable for '" PROP_NAME_XBUILD "'. Tried with path: " + xbuild_path); + } - return; + return GDMonoMarshal::mono_string_from_godot(xbuild_path); } break; default: ERR_EXPLAIN("You don't deserve to live"); @@ -126,31 +125,74 @@ void godot_icall_BuildInstance_get_MSBuildInfo(MonoString **r_msbuild_path, Mono static String msbuild_path = _find_build_engine_on_unix("msbuild"); static String xbuild_path = _find_build_engine_on_unix("xbuild"); - if (build_tool != GodotSharpBuilds::XBUILD) { - if (msbuild_path.empty()) { - WARN_PRINT("Cannot find msbuild ('mono/builds/build_tool')."); - return; + if (build_tool == GodotSharpBuilds::XBUILD) { + if (xbuild_path.empty()) { + WARN_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'"); + return NULL; } } else { - if (xbuild_path.empty()) { - WARN_PRINT("Cannot find xbuild ('mono/builds/build_tool')."); - return; + if (msbuild_path.empty()) { + WARN_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'"); + return NULL; } } - *r_msbuild_path = GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path); + return GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path); +#else + (void)build_tool; // UNUSED + + ERR_EXPLAIN("Not implemented on this platform"); + ERR_FAIL_V(NULL); +#endif +} + +MonoString *godot_icall_BuildInstance_get_FrameworkPath() { + +#if defined(WINDOWS_ENABLED) + const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info(); + if (mono_reg_info.assembly_dir.length()) { + String framework_path = path_join(mono_reg_info.assembly_dir, "mono", "4.5"); + return GDMonoMarshal::mono_string_from_godot(framework_path); + } + + ERR_EXPLAIN("Cannot find Mono's assemblies directory in the registry"); + ERR_FAIL_V(NULL); +#else + return NULL; +#endif +} + +MonoString *godot_icall_BuildInstance_get_MonoWindowsBinDir() { + +#if defined(WINDOWS_ENABLED) + const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info(); + if (mono_reg_info.bin_dir.length()) { + return GDMonoMarshal::mono_string_from_godot(mono_reg_info.bin_dir); + } + + ERR_EXPLAIN("Cannot find Mono's binaries directory in the registry"); + ERR_FAIL_V(NULL); +#else + return NULL; +#endif +} + +MonoBoolean godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows() { - return; +#if defined(WINDOWS_ENABLED) + return GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool"))) == GodotSharpBuilds::MSBUILD_MONO; #else - ERR_PRINT("Not implemented on this platform"); - return; + return false; #endif } void GodotSharpBuilds::_register_internal_calls() { mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback); - mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildInfo", (void *)godot_icall_BuildInstance_get_MSBuildInfo); + mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath); + mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_FrameworkPath", (void *)godot_icall_BuildInstance_get_FrameworkPath); + mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MonoWindowsBinDir", (void *)godot_icall_BuildInstance_get_MonoWindowsBinDir); + mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows", (void *)godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows); } void GodotSharpBuilds::show_build_error_dialog(const String &p_message) { @@ -167,6 +209,8 @@ bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_s if (!FileAccess::exists(api_assembly_file)) { MonoBuildInfo api_build_info(api_sln_file, p_config); + // TODO Replace this global NoWarn with '#pragma warning' directives on generated files, + // once we start to actively document manually maintained C# classes api_build_info.custom_props.push_back("NoWarn=1591"); // Ignore missing documentation warnings if (!GodotSharpBuilds::get_singleton()->build(api_build_info)) { @@ -178,13 +222,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 +246,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_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 core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(API_ASSEMBLY_NAME "_" + itos(core_hash)); - String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(EDITOR_API_ASSEMBLY_NAME "_" + itos(editor_hash)); - - String api_sln_dir = p_api_type == API_CORE ? core_api_sln_dir : editor_api_sln_dir; + String api_sln_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 +301,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 +334,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 +342,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 +368,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,25 +426,19 @@ 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(); -#ifdef WINDOWS_ENABLED - // TODO: Default to MSBUILD_MONO if its csc.exe issue is fixed in the installed mono version - EDITOR_DEF("mono/builds/build_tool", MSBUILD); -#else EDITOR_DEF("mono/builds/build_tool", MSBUILD_MONO); -#endif ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM, + PROP_NAME_MSBUILD_MONO #ifdef WINDOWS_ENABLED - "MSBuild (Mono),MSBuild (System)" -#else - "MSBuild (Mono),xbuild (Deprecated)" + "," PROP_NAME_MSBUILD_VS #endif - )); + "," PROP_NAME_XBUILD)); } GodotSharpBuilds::~GodotSharpBuilds() { @@ -405,12 +463,12 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { exit_code = -1; - String logs_dir = GodotSharpDirs::get_build_logs_dir().plus_file(build_info.solution.md5_text() + "_" + build_info.configuration); + String log_dirpath = build_info.get_log_dirpath(); if (build_tab) { build_tab->on_build_start(); } else { - build_tab = memnew(MonoBuildTab(build_info, logs_dir)); + build_tab = memnew(MonoBuildTab(build_info, log_dirpath)); MonoBottomPanel::get_singleton()->add_build_tab(build_tab); } @@ -432,12 +490,12 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { // Remove old issues file String issues_file = "msbuild_issues.csv"; - DirAccessRef d = DirAccess::create_for_path(logs_dir); + DirAccessRef d = DirAccess::create_for_path(log_dirpath); if (d->file_exists(issues_file)) { Error err = d->remove(issues_file); if (err != OK) { exited = true; - String file_path = ProjectSettings::get_singleton()->localize_path(logs_dir).plus_file(issues_file); + String file_path = ProjectSettings::get_singleton()->localize_path(log_dirpath).plus_file(issues_file); String message = "Cannot remove issues file: " + file_path; build_tab->on_build_exec_failed(message); ERR_EXPLAIN(message); @@ -456,13 +514,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { const Variant *ctor_args[2] = { &solution, &config }; - MonoObject *ex = NULL; + MonoException *exc = NULL; GDMonoMethod *ctor = klass->get_method(".ctor", 2); - ctor->invoke(mono_object, ctor_args, &ex); + ctor->invoke(mono_object, ctor_args, &exc); - if (ex) { + if (exc) { exited = true; - String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); + GDMonoUtils::debug_unhandled_exception(exc); + String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc); build_tab->on_build_exec_failed(message); ERR_EXPLAIN(message); ERR_FAIL(); @@ -470,19 +529,21 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { // Call Build - Variant logger_assembly = OS::get_singleton()->get_executable_path().get_base_dir().plus_file(EDITOR_TOOLS_ASSEMBLY_NAME) + ".dll"; - Variant logger_output_dir = logs_dir; + String logger_assembly_path = GDMono::get_singleton()->get_editor_tools_assembly()->get_path(); + Variant logger_assembly = ProjectSettings::get_singleton()->globalize_path(logger_assembly_path); + Variant logger_output_dir = log_dirpath; Variant custom_props = build_info.custom_props; const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props }; - ex = NULL; + exc = NULL; GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3); - build_method->invoke(mono_object, args, &ex); + build_method->invoke(mono_object, args, &exc); - if (ex) { + if (exc) { exited = true; - String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); + GDMonoUtils::debug_unhandled_exception(exc); + String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc); build_tab->on_build_exec_failed(message); ERR_EXPLAIN(message); ERR_FAIL(); @@ -494,8 +555,9 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { exited = true; exit_code = klass->get_field("exitCode")->get_int_value(mono_object); - if (exit_code != 0 && OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->print(String("MSBuild finished with exit code " + itos(exit_code) + "\n").utf8()); + if (exit_code != 0) { + print_verbose("MSBuild finished with exit code " + itos(exit_code)); + } build_tab->on_build_exit(exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR); } else { @@ -504,11 +566,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..4afc284d45 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,24 +57,20 @@ 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 - MSBUILD -#else - XBUILD // Deprecated + MSBUILD_VS, #endif + XBUILD // Deprecated }; _FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; } @@ -89,11 +86,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 975cf0f030..faeb58e5a7 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")); @@ -277,13 +278,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { about_label->set_autowrap(true); 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 may crash in some situations. " + - "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases, " + + "It is currently in an alpha stage and is not suitable for use in production.\n\n" + + "As of Godot 3.1, C# support is not feature-complete and may crash in some situations. " + + "Bugs and usability issues will be addressed gradually over future 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 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" + + "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!"; about_label->set_text(about_text); @@ -316,6 +315,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 20378a0162..9317550d28 100644 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -73,7 +73,7 @@ void MonoBottomPanel::_update_build_tabs_list() { if (no_current_tab || current_tab == i) { build_tabs_list->select(i); - _build_tab_item_selected(i); + _build_tabs_item_selected(i); } } } @@ -105,21 +105,27 @@ void MonoBottomPanel::show_build_tab() { ERR_PRINT("Builds tab not found"); } -void MonoBottomPanel::_build_tab_item_selected(int p_idx) { +void MonoBottomPanel::_build_tabs_item_selected(int p_idx) { ERR_FAIL_INDEX(p_idx, build_tabs->get_tab_count()); + build_tabs->set_current_tab(p_idx); + if (!build_tabs->is_visible()) + build_tabs->set_visible(true); + + warnings_btn->set_visible(true); + errors_btn->set_visible(true); + view_log_btn->set_visible(true); } -void MonoBottomPanel::_build_tab_changed(int p_idx) { +void MonoBottomPanel::_build_tabs_nothing_selected() { - if (p_idx < 0 || p_idx >= build_tabs->get_tab_count()) { - warnings_btn->set_visible(false); - errors_btn->set_visible(false); - } else { - warnings_btn->set_visible(true); - errors_btn->set_visible(true); - } + if (build_tabs->get_tab_count() != 0) // just in case + build_tabs->set_visible(false); + + warnings_btn->set_visible(false); + errors_btn->set_visible(false); + view_log_btn->set_visible(false); } void MonoBottomPanel::_warnings_toggled(bool p_pressed) { @@ -142,12 +148,28 @@ 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); } +void MonoBottomPanel::_view_log_pressed() { + + if (build_tabs_list->is_anything_selected()) { + Vector<int> selected_items = build_tabs_list->get_selected_items(); + CRASH_COND(selected_items.size() != 1); + int selected_item = selected_items[0]; + + MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_tab_control(selected_item)); + ERR_FAIL_NULL(build_tab); + + String log_dirpath = build_tab->get_build_info().get_log_dirpath(); + + OS::get_singleton()->shell_open(log_dirpath.plus_file("msbuild_log.txt")); + } +} + void MonoBottomPanel::_notification(int p_what) { switch (p_what) { @@ -163,10 +185,11 @@ void MonoBottomPanel::_notification(int p_what) { void MonoBottomPanel::_bind_methods() { ClassDB::bind_method(D_METHOD("_build_project_pressed"), &MonoBottomPanel::_build_project_pressed); + ClassDB::bind_method(D_METHOD("_view_log_pressed"), &MonoBottomPanel::_view_log_pressed); ClassDB::bind_method(D_METHOD("_warnings_toggled", "pressed"), &MonoBottomPanel::_warnings_toggled); ClassDB::bind_method(D_METHOD("_errors_toggled", "pressed"), &MonoBottomPanel::_errors_toggled); - ClassDB::bind_method(D_METHOD("_build_tab_item_selected", "idx"), &MonoBottomPanel::_build_tab_item_selected); - ClassDB::bind_method(D_METHOD("_build_tab_changed", "idx"), &MonoBottomPanel::_build_tab_changed); + ClassDB::bind_method(D_METHOD("_build_tabs_item_selected", "idx"), &MonoBottomPanel::_build_tabs_item_selected); + ClassDB::bind_method(D_METHOD("_build_tabs_nothing_selected"), &MonoBottomPanel::_build_tabs_nothing_selected); } MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) { @@ -223,6 +246,15 @@ MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) { errors_btn->connect("toggled", this, "_errors_toggled"); toolbar_hbc->add_child(errors_btn); + toolbar_hbc->add_spacer(); + + view_log_btn = memnew(Button); + view_log_btn->set_text(TTR("View log")); + view_log_btn->set_focus_mode(FOCUS_NONE); + view_log_btn->set_visible(false); + view_log_btn->connect("pressed", this, "_view_log_pressed"); + toolbar_hbc->add_child(view_log_btn); + HSplitContainer *hsc = memnew(HSplitContainer); hsc->set_h_size_flags(SIZE_EXPAND_FILL); hsc->set_v_size_flags(SIZE_EXPAND_FILL); @@ -230,14 +262,14 @@ MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) { build_tabs_list = memnew(ItemList); build_tabs_list->set_h_size_flags(SIZE_EXPAND_FILL); - build_tabs_list->connect("item_selected", this, "_build_tab_item_selected"); + build_tabs_list->connect("item_selected", this, "_build_tabs_item_selected"); + build_tabs_list->connect("nothing_selected", this, "_build_tabs_nothing_selected"); hsc->add_child(build_tabs_list); build_tabs = memnew(TabContainer); build_tabs->set_tab_align(TabContainer::ALIGN_LEFT); build_tabs->set_h_size_flags(SIZE_EXPAND_FILL); build_tabs->set_tabs_visible(false); - build_tabs->connect("tab_changed", this, "_build_tab_changed"); hsc->add_child(build_tabs); } } @@ -407,9 +439,14 @@ void MonoBuildTab::stop_build() { void MonoBuildTab::_issue_activated(int p_idx) { - ERR_FAIL_INDEX(p_idx, issues.size()); + ERR_FAIL_INDEX(p_idx, issues_list->get_item_count()); + + // Get correct issue idx from issue list + int issue_idx = this->issues_list->get_item_metadata(p_idx); - const BuildIssue &issue = issues[p_idx]; + ERR_FAIL_INDEX(issue_idx, issues.size()); + + const BuildIssue &issue = issues[issue_idx]; if (issue.project_file.empty() && issue.file.empty()) return; @@ -437,21 +474,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/editor/mono_bottom_panel.h b/modules/mono/editor/mono_bottom_panel.h index a44d3a9af8..03240e9a13 100644 --- a/modules/mono/editor/mono_bottom_panel.h +++ b/modules/mono/editor/mono_bottom_panel.h @@ -53,16 +53,18 @@ class MonoBottomPanel : public VBoxContainer { Button *warnings_btn; Button *errors_btn; + Button *view_log_btn; void _update_build_tabs_list(); - void _build_tab_item_selected(int p_idx); - void _build_tab_changed(int p_idx); + void _build_tabs_item_selected(int p_idx); + void _build_tabs_nothing_selected(); void _warnings_toggled(bool p_pressed); void _errors_toggled(bool p_pressed); void _build_project_pressed(); + void _view_log_pressed(); static MonoBottomPanel *singleton; diff --git a/modules/mono/editor/mono_build_info.cpp b/modules/mono/editor/mono_build_info.cpp new file mode 100644 index 0000000000..e4af2aac4f --- /dev/null +++ b/modules/mono/editor/mono_build_info.cpp @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* mono_build_info.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 "mono_build_info.h" + +#include "../godotsharp_dirs.h" +#include "../mono_gd/gd_mono_utils.h" + +uint32_t MonoBuildInfo::Hasher::hash(const MonoBuildInfo &p_key) { + + uint32_t hash = 0; + + GDMonoUtils::hash_combine(hash, p_key.solution.hash()); + GDMonoUtils::hash_combine(hash, p_key.configuration.hash()); + + return hash; +} + +bool MonoBuildInfo::operator==(const MonoBuildInfo &p_b) const { + + return p_b.solution == solution && p_b.configuration == configuration; +} + +String MonoBuildInfo::get_log_dirpath() { + + return GodotSharpDirs::get_build_logs_dir().plus_file(solution.md5_text() + "_" + configuration); +} + +MonoBuildInfo::MonoBuildInfo() {} + +MonoBuildInfo::MonoBuildInfo(const String &p_solution, const String &p_config) { + + solution = p_solution; + configuration = p_config; +} diff --git a/modules/mono/editor/mono_build_info.h b/modules/mono/editor/mono_build_info.h index 4806764a61..64ba0f4037 100644 --- a/modules/mono/editor/mono_build_info.h +++ b/modules/mono/editor/mono_build_info.h @@ -31,35 +31,25 @@ #ifndef MONO_BUILD_INFO_H #define MONO_BUILD_INFO_H -#include "../mono_gd/gd_mono_utils.h" +#include "core/ustring.h" +#include "core/vector.h" struct MonoBuildInfo { struct Hasher { - static _FORCE_INLINE_ uint32_t hash(const MonoBuildInfo &p_key) { - uint32_t hash = 0; - - GDMonoUtils::hash_combine(hash, p_key.solution.hash()); - GDMonoUtils::hash_combine(hash, p_key.configuration.hash()); - - return hash; - } + static uint32_t hash(const MonoBuildInfo &p_key); }; String solution; String configuration; Vector<String> custom_props; - MonoBuildInfo() {} + bool operator==(const MonoBuildInfo &p_b) const; - MonoBuildInfo(const String &p_solution, const String &p_config) { - solution = p_solution; - configuration = p_config; - } + String get_log_dirpath(); - bool operator==(const MonoBuildInfo &p_b) const { - return p_b.solution == solution && p_b.configuration == configuration; - } + MonoBuildInfo(); + MonoBuildInfo(const String &p_solution, const String &p_config); }; #endif // MONO_BUILD_INFO_H diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/mono/editor/monodevelop_instance.cpp index 48a285561d..9f05711fd6 100644 --- a/modules/mono/editor/monodevelop_instance.cpp +++ b/modules/mono/editor/monodevelop_instance.cpp @@ -40,14 +40,14 @@ void MonoDevelopInstance::execute(const Vector<String> &p_files) { ERR_FAIL_NULL(execute_method); ERR_FAIL_COND(gc_handle.is_null()); - MonoObject *ex = NULL; + MonoException *exc = NULL; Variant files = p_files; const Variant *args[1] = { &files }; - execute_method->invoke(gc_handle->get_target(), args, &ex); + execute_method->invoke(gc_handle->get_target(), args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL(); } } @@ -68,14 +68,14 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) { MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr()); GDMonoMethod *ctor = klass->get_method(".ctor", 1); - MonoObject *ex = NULL; + MonoException *exc = NULL; Variant solution = p_solution; const Variant *args[1] = { &solution }; - ctor->invoke(obj, args, &ex); + ctor->invoke(obj, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL(); } diff --git a/modules/mono/glue/builtin_types_glue.h b/modules/mono/glue/builtin_types_glue.h index 460de84b65..ef9f152682 100644 --- a/modules/mono/glue/builtin_types_glue.h +++ b/modules/mono/glue/builtin_types_glue.h @@ -1,3 +1,33 @@ +/*************************************************************************/ +/* builtin_types_glue.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 BUILTIN_TYPES_GLUE_H #define BUILTIN_TYPES_GLUE_H diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp new file mode 100644 index 0000000000..148bb32398 --- /dev/null +++ b/modules/mono/glue/collections_glue.cpp @@ -0,0 +1,240 @@ +/*************************************************************************/ +/* collections_glue.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 "collections_glue.h" + +#include <mono/metadata/exception.h> + +#include "../mono_gd/gd_mono_class.h" + +Array *godot_icall_Array_Ctor() { + return memnew(Array); +} + +void godot_icall_Array_Dtor(Array *ptr) { + memdelete(ptr); +} + +MonoObject *godot_icall_Array_At(Array *ptr, int index) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return NULL; + } + return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index)); +} + +void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return; + } + ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value); +} + +int godot_icall_Array_Count(Array *ptr) { + return ptr->size(); +} + +void godot_icall_Array_Add(Array *ptr, MonoObject *item) { + ptr->append(GDMonoMarshal::mono_object_to_variant(item)); +} + +void godot_icall_Array_Clear(Array *ptr) { + ptr->clear(); +} + +bool godot_icall_Array_Contains(Array *ptr, MonoObject *item) { + return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1; +} + +void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) { + int count = ptr->size(); + + if (mono_array_length(array) < (array_index + count)) { + MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + GDMonoUtils::set_pending_exception(exc); + return; + } + + for (int i = 0; i < count; i++) { + MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i)); + mono_array_setref(array, array_index, boxed); + array_index++; + } +} + +int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) { + return ptr->find(GDMonoMarshal::mono_object_to_variant(item)); +} + +void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return; + } + ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item)); +} + +bool godot_icall_Array_Remove(Array *ptr, MonoObject *item) { + int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item)); + if (idx >= 0) { + ptr->remove(idx); + return true; + } + return false; +} + +void godot_icall_Array_RemoveAt(Array *ptr, int index) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return; + } + ptr->remove(index); +} + +Dictionary *godot_icall_Dictionary_Ctor() { + return memnew(Dictionary); +} + +void godot_icall_Dictionary_Dtor(Dictionary *ptr) { + memdelete(ptr); +} + +MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) { + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + if (ret == NULL) { + MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr()); +#ifdef DEBUG_ENABLED + CRASH_COND(!exc); +#endif + GDMonoUtils::runtime_object_init(exc); + GDMonoUtils::set_pending_exception((MonoException *)exc); + return NULL; + } + return GDMonoMarshal::variant_to_mono_object(ret); +} + +void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) { + ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value); +} + +Array *godot_icall_Dictionary_Keys(Dictionary *ptr) { + return memnew(Array(ptr->keys())); +} + +Array *godot_icall_Dictionary_Values(Dictionary *ptr) { + return memnew(Array(ptr->values())); +} + +int godot_icall_Dictionary_Count(Dictionary *ptr) { + return ptr->size(); +} + +void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) { + Variant varKey = GDMonoMarshal::mono_object_to_variant(key); + Variant *ret = ptr->getptr(varKey); + if (ret != NULL) { + GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists")); + return; + } + ptr->operator[](varKey) = GDMonoMarshal::mono_object_to_variant(value); +} + +void godot_icall_Dictionary_Clear(Dictionary *ptr) { + ptr->clear(); +} + +bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) { + // no dupes + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + return ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value); +} + +bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) { + return ptr->has(GDMonoMarshal::mono_object_to_variant(key)); +} + +bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) { + return ptr->erase(GDMonoMarshal::mono_object_to_variant(key)); +} + +bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) { + Variant varKey = GDMonoMarshal::mono_object_to_variant(key); + + // no dupes + Variant *ret = ptr->getptr(varKey); + if (ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value)) { + ptr->erase(varKey); + return true; + } + + return false; +} + +bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) { + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + if (ret == NULL) { + *value = NULL; + return false; + } + *value = GDMonoMarshal::variant_to_mono_object(ret); + return true; +} + +void godot_register_collections_icalls() { + mono_add_internal_call("Godot.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor); + mono_add_internal_call("Godot.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor); + mono_add_internal_call("Godot.Array::godot_icall_Array_At", (void *)godot_icall_Array_At); + mono_add_internal_call("Godot.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt); + mono_add_internal_call("Godot.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count); + mono_add_internal_call("Godot.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add); + mono_add_internal_call("Godot.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear); + mono_add_internal_call("Godot.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains); + mono_add_internal_call("Godot.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo); + mono_add_internal_call("Godot.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf); + mono_add_internal_call("Godot.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert); + mono_add_internal_call("Godot.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove); + mono_add_internal_call("Godot.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt); + + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue); +} diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h new file mode 100644 index 0000000000..eb5ecfb725 --- /dev/null +++ b/modules/mono/glue/collections_glue.h @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* collections_glue.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 COLLECTIONS_GLUE_H +#define COLLECTIONS_GLUE_H + +#include "core/array.h" + +#include "../mono_gd/gd_mono_marshal.h" + +// Array + +Array *godot_icall_Array_Ctor(); + +void godot_icall_Array_Dtor(Array *ptr); + +MonoObject *godot_icall_Array_At(Array *ptr, int index); + +void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value); + +int godot_icall_Array_Count(Array *ptr); + +void godot_icall_Array_Add(Array *ptr, MonoObject *item); + +void godot_icall_Array_Clear(Array *ptr); + +bool godot_icall_Array_Contains(Array *ptr, MonoObject *item); + +void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index); + +int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item); + +void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item); + +bool godot_icall_Array_Remove(Array *ptr, MonoObject *item); + +void godot_icall_Array_RemoveAt(Array *ptr, int index); + +// Dictionary + +Dictionary *godot_icall_Dictionary_Ctor(); + +void godot_icall_Dictionary_Dtor(Dictionary *ptr); + +MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key); + +void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value); + +Array *godot_icall_Dictionary_Keys(Dictionary *ptr); + +Array *godot_icall_Dictionary_Values(Dictionary *ptr); + +int godot_icall_Dictionary_Count(Dictionary *ptr); + +void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value); + +void godot_icall_Dictionary_Clear(Dictionary *ptr); + +bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value); + +bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key); + +bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key); + +bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value); + +bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value); + +// Register internal calls + +void godot_register_collections_icalls(); + +#endif // COLLECTIONS_GLUE_H diff --git a/modules/mono/glue/cs_files/AABB.cs b/modules/mono/glue/cs_files/AABB.cs index e6e12f7ba3..0df2e615f1 100644 --- a/modules/mono/glue/cs_files/AABB.cs +++ b/modules/mono/glue/cs_files/AABB.cs @@ -1,62 +1,60 @@ -using System; - // file: core/math/aabb.h // commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 // file: core/math/aabb.cpp // commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0 // file: core/variant_call.cpp // commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif namespace Godot { public struct AABB : IEquatable<AABB> { - private Vector3 position; - private Vector3 size; + private Vector3 _position; + private Vector3 _size; public Vector3 Position { - get - { - return position; - } + get { return _position; } + set { _position = value; } } public Vector3 Size { - get - { - return size; - } + get { return _size; } + set { _size = value; } } public Vector3 End { - get - { - return position + size; - } + get { return _position + _size; } + set { _size = value - _position; } } public bool Encloses(AABB with) { - Vector3 src_min = position; - Vector3 src_max = position + size; - Vector3 dst_min = with.position; - Vector3 dst_max = with.position + with.size; - - return ((src_min.x <= dst_min.x) && - (src_max.x > dst_max.x) && - (src_min.y <= dst_min.y) && - (src_max.y > dst_max.y) && - (src_min.z <= dst_min.z) && - (src_max.z > dst_max.z)); + Vector3 src_min = _position; + Vector3 src_max = _position + _size; + Vector3 dst_min = with._position; + Vector3 dst_max = with._position + with._size; + + return src_min.x <= dst_min.x && + src_max.x > dst_max.x && + src_min.y <= dst_min.y && + src_max.y > dst_max.y && + src_min.z <= dst_min.z && + src_max.z > dst_max.z; } public AABB Expand(Vector3 to_point) { - Vector3 begin = position; - Vector3 end = position + size; + Vector3 begin = _position; + Vector3 end = _position + _size; if (to_point.x < begin.x) begin.x = to_point.x; @@ -75,9 +73,9 @@ namespace Godot return new AABB(begin, end - begin); } - public float GetArea() + public real_t GetArea() { - return size.x * size.y * size.z; + return _size.x * _size.y * _size.z; } public Vector3 GetEndpoint(int idx) @@ -85,21 +83,21 @@ namespace Godot switch (idx) { case 0: - return new Vector3(position.x, position.y, position.z); + return new Vector3(_position.x, _position.y, _position.z); case 1: - return new Vector3(position.x, position.y, position.z + size.z); + return new Vector3(_position.x, _position.y, _position.z + _size.z); case 2: - return new Vector3(position.x, position.y + size.y, position.z); + return new Vector3(_position.x, _position.y + _size.y, _position.z); case 3: - return new Vector3(position.x, position.y + size.y, position.z + size.z); + return new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z); case 4: - return new Vector3(position.x + size.x, position.y, position.z); + return new Vector3(_position.x + _size.x, _position.y, _position.z); case 5: - return new Vector3(position.x + size.x, position.y, position.z + size.z); + return new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z); case 6: - return new Vector3(position.x + size.x, position.y + size.y, position.z); + return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z); case 7: - return new Vector3(position.x + size.x, position.y + size.y, position.z + size.z); + return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z); default: throw new ArgumentOutOfRangeException(nameof(idx), String.Format("Index is {0}, but a value from 0 to 7 is expected.", idx)); } @@ -107,19 +105,18 @@ namespace Godot public Vector3 GetLongestAxis() { - Vector3 axis = new Vector3(1f, 0f, 0f); - float max_size = size.x; + var axis = new Vector3(1f, 0f, 0f); + real_t max_size = _size.x; - if (size.y > max_size) + if (_size.y > max_size) { axis = new Vector3(0f, 1f, 0f); - max_size = size.y; + max_size = _size.y; } - if (size.z > max_size) + if (_size.z > max_size) { axis = new Vector3(0f, 0f, 1f); - max_size = size.z; } return axis; @@ -127,52 +124,50 @@ namespace Godot public Vector3.Axis GetLongestAxisIndex() { - Vector3.Axis axis = Vector3.Axis.X; - float max_size = size.x; + var axis = Vector3.Axis.X; + real_t max_size = _size.x; - if (size.y > max_size) + if (_size.y > max_size) { axis = Vector3.Axis.Y; - max_size = size.y; + max_size = _size.y; } - if (size.z > max_size) + if (_size.z > max_size) { axis = Vector3.Axis.Z; - max_size = size.z; } 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; + if (_size.y > max_size) + max_size = _size.y; - if (size.z > max_size) - max_size = size.z; + if (_size.z > max_size) + max_size = _size.z; return max_size; } public Vector3 GetShortestAxis() { - Vector3 axis = new Vector3(1f, 0f, 0f); - float max_size = size.x; + var axis = new Vector3(1f, 0f, 0f); + real_t max_size = _size.x; - if (size.y < max_size) + if (_size.y < max_size) { axis = new Vector3(0f, 1f, 0f); - max_size = size.y; + max_size = _size.y; } - if (size.z < max_size) + if (_size.z < max_size) { axis = new Vector3(0f, 0f, 1f); - max_size = size.z; } return axis; @@ -180,85 +175,84 @@ namespace Godot public Vector3.Axis GetShortestAxisIndex() { - Vector3.Axis axis = Vector3.Axis.X; - float max_size = size.x; + var axis = Vector3.Axis.X; + real_t max_size = _size.x; - if (size.y < max_size) + if (_size.y < max_size) { axis = Vector3.Axis.Y; - max_size = size.y; + max_size = _size.y; } - if (size.z < max_size) + if (_size.z < max_size) { axis = Vector3.Axis.Z; - max_size = size.z; } 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; + if (_size.y < max_size) + max_size = _size.y; - if (size.z < max_size) - max_size = size.z; + if (_size.z < max_size) + max_size = _size.z; return max_size; } public Vector3 GetSupport(Vector3 dir) { - Vector3 half_extents = size * 0.5f; - Vector3 ofs = position + half_extents; + Vector3 half_extents = _size * 0.5f; + Vector3 ofs = _position + half_extents; return ofs + new Vector3( - (dir.x > 0f) ? -half_extents.x : half_extents.x, - (dir.y > 0f) ? -half_extents.y : half_extents.y, - (dir.z > 0f) ? -half_extents.z : half_extents.z); + dir.x > 0f ? -half_extents.x : half_extents.x, + dir.y > 0f ? -half_extents.y : half_extents.y, + dir.z > 0f ? -half_extents.z : half_extents.z); } - public AABB Grow(float by) + public AABB Grow(real_t by) { - AABB res = this; + var res = this; - res.position.x -= by; - res.position.y -= by; - res.position.z -= by; - res.size.x += 2.0f * by; - res.size.y += 2.0f * by; - res.size.z += 2.0f * by; + res._position.x -= by; + res._position.y -= by; + res._position.z -= by; + res._size.x += 2.0f * by; + res._size.y += 2.0f * by; + res._size.z += 2.0f * by; return res; } public bool HasNoArea() { - return size.x <= 0f || size.y <= 0f || size.z <= 0f; + return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f; } public bool HasNoSurface() { - return size.x <= 0f && size.y <= 0f && size.z <= 0f; + return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f; } public bool HasPoint(Vector3 point) { - if (point.x < position.x) + if (point.x < _position.x) return false; - if (point.y < position.y) + if (point.y < _position.y) return false; - if (point.z < position.z) + if (point.z < _position.z) return false; - if (point.x > position.x + size.x) + if (point.x > _position.x + _size.x) return false; - if (point.y > position.y + size.y) + if (point.y > _position.y + _size.y) return false; - if (point.z > position.z + size.z) + if (point.z > _position.z + _size.z) return false; return true; @@ -266,10 +260,10 @@ namespace Godot public AABB Intersection(AABB with) { - Vector3 src_min = position; - Vector3 src_max = position + size; - Vector3 dst_min = with.position; - Vector3 dst_max = with.position + with.size; + Vector3 src_min = _position; + Vector3 src_max = _position + _size; + Vector3 dst_min = with._position; + Vector3 dst_max = with._position + with._size; Vector3 min, max; @@ -277,48 +271,42 @@ namespace Godot { return new AABB(); } - else - { - min.x = (src_min.x > dst_min.x) ? src_min.x : dst_min.x; - max.x = (src_max.x < dst_max.x) ? src_max.x : dst_max.x; - } + + min.x = src_min.x > dst_min.x ? src_min.x : dst_min.x; + max.x = src_max.x < dst_max.x ? src_max.x : dst_max.x; if (src_min.y > dst_max.y || src_max.y < dst_min.y) { return new AABB(); } - else - { - min.y = (src_min.y > dst_min.y) ? src_min.y : dst_min.y; - max.y = (src_max.y < dst_max.y) ? src_max.y : dst_max.y; - } + + min.y = src_min.y > dst_min.y ? src_min.y : dst_min.y; + max.y = src_max.y < dst_max.y ? src_max.y : dst_max.y; if (src_min.z > dst_max.z || src_max.z < dst_min.z) { return new AABB(); } - else - { - min.z = (src_min.z > dst_min.z) ? src_min.z : dst_min.z; - max.z = (src_max.z < dst_max.z) ? src_max.z : dst_max.z; - } + + min.z = src_min.z > dst_min.z ? src_min.z : dst_min.z; + max.z = src_max.z < dst_max.z ? src_max.z : dst_max.z; return new AABB(min, max - min); } public bool Intersects(AABB with) { - if (position.x >= (with.position.x + with.size.x)) + if (_position.x >= with._position.x + with._size.x) return false; - if ((position.x + size.x) <= with.position.x) + if (_position.x + _size.x <= with._position.x) return false; - if (position.y >= (with.position.y + with.size.y)) + if (_position.y >= with._position.y + with._size.y) return false; - if ((position.y + size.y) <= with.position.y) + if (_position.y + _size.y <= with._position.y) return false; - if (position.z >= (with.position.z + with.size.z)) + if (_position.z >= with._position.z + with._size.z) return false; - if ((position.z + size.z) <= with.position.z) + if (_position.z + _size.z <= with._position.z) return false; return true; @@ -328,14 +316,14 @@ namespace Godot { Vector3[] points = { - new Vector3(position.x, position.y, position.z), - new Vector3(position.x, position.y, position.z + size.z), - new Vector3(position.x, position.y + size.y, position.z), - new Vector3(position.x, position.y + size.y, position.z + size.z), - new Vector3(position.x + size.x, position.y, position.z), - new Vector3(position.x + size.x, position.y, position.z + size.z), - new Vector3(position.x + size.x, position.y + size.y, position.z), - new Vector3(position.x + size.x, position.y + size.y, position.z + size.z), + new Vector3(_position.x, _position.y, _position.z), + new Vector3(_position.x, _position.y, _position.z + _size.z), + new Vector3(_position.x, _position.y + _size.y, _position.z), + new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z), + new Vector3(_position.x + _size.x, _position.y, _position.z), + new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z), + new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z), + new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z) }; bool over = false; @@ -354,23 +342,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 +367,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; } @@ -400,30 +388,31 @@ namespace Godot public AABB Merge(AABB with) { - Vector3 beg_1 = position; - Vector3 beg_2 = with.position; - Vector3 end_1 = new Vector3(size.x, size.y, size.z) + beg_1; - Vector3 end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2; - - Vector3 min = new Vector3( - (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x, - (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y, - (beg_1.z < beg_2.z) ? beg_1.z : beg_2.z + Vector3 beg_1 = _position; + Vector3 beg_2 = with._position; + var end_1 = new Vector3(_size.x, _size.y, _size.z) + beg_1; + var end_2 = new Vector3(with._size.x, with._size.y, with._size.z) + beg_2; + + var min = new Vector3( + beg_1.x < beg_2.x ? beg_1.x : beg_2.x, + beg_1.y < beg_2.y ? beg_1.y : beg_2.y, + beg_1.z < beg_2.z ? beg_1.z : beg_2.z ); - Vector3 max = new Vector3( - (end_1.x > end_2.x) ? end_1.x : end_2.x, - (end_1.y > end_2.y) ? end_1.y : end_2.y, - (end_1.z > end_2.z) ? end_1.z : end_2.z + var max = new Vector3( + end_1.x > end_2.x ? end_1.x : end_2.x, + end_1.y > end_2.y ? end_1.y : end_2.y, + end_1.z > end_2.z ? end_1.z : end_2.z ); return new AABB(min, max - min); } - + + // Constructors public AABB(Vector3 position, Vector3 size) { - this.position = position; - this.size = size; + _position = position; + _size = size; } public static bool operator ==(AABB left, AABB right) @@ -448,20 +437,20 @@ namespace Godot public bool Equals(AABB other) { - return position == other.position && size == other.size; + return _position == other._position && _size == other._size; } public override int GetHashCode() { - return position.GetHashCode() ^ size.GetHashCode(); + return _position.GetHashCode() ^ _size.GetHashCode(); } public override string ToString() { return String.Format("{0} - {1}", new object[] { - this.position.ToString(), - this.size.ToString() + _position.ToString(), + _size.ToString() }); } @@ -469,8 +458,8 @@ namespace Godot { return String.Format("{0} - {1}", new object[] { - this.position.ToString(format), - this.size.ToString(format) + _position.ToString(format), + _size.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/Array.cs b/modules/mono/glue/cs_files/Array.cs new file mode 100644 index 0000000000..1ec4d7d20a --- /dev/null +++ b/modules/mono/glue/cs_files/Array.cs @@ -0,0 +1,340 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Godot +{ + class ArraySafeHandle : SafeHandle + { + public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) + { + this.handle = handle; + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + protected override bool ReleaseHandle() + { + Array.godot_icall_Array_Dtor(handle); + return true; + } + } + + public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable + { + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Array_Ctor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Array_At(IntPtr ptr, int index); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Array_Count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Add(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Clear(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index); + + ArraySafeHandle safeHandle; + bool disposed = false; + + public Array() + { + safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); + } + + internal Array(ArraySafeHandle handle) + { + safeHandle = handle; + } + + internal Array(IntPtr handle) + { + safeHandle = new ArraySafeHandle(handle); + } + + internal IntPtr GetPtr() + { + return safeHandle.DangerousGetHandle(); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (safeHandle != null) + { + safeHandle.Dispose(); + safeHandle = null; + } + + disposed = true; + } + + public object this[int index] + { + get + { + return godot_icall_Array_At(GetPtr(), index); + } + set + { + godot_icall_Array_SetAt(GetPtr(), index, value); + } + } + + public int Count + { + get + { + return godot_icall_Array_Count(GetPtr()); + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public void Add(object item) + { + godot_icall_Array_Add(GetPtr(), item); + } + + public void Clear() + { + godot_icall_Array_Clear(GetPtr()); + } + + public bool Contains(object item) + { + return godot_icall_Array_Contains(GetPtr(), item); + } + + public void CopyTo(object[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + + // Internal call may throw ArgumentException + godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex); + } + + public IEnumerator<object> GetEnumerator() + { + int count = Count; + + for (int i = 0; i < count; i++) + { + yield return godot_icall_Array_At(GetPtr(), i); + } + } + + public int IndexOf(object item) + { + return godot_icall_Array_IndexOf(GetPtr(), item); + } + + public void Insert(int index, object item) + { + godot_icall_Array_Insert(GetPtr(), index, item); + } + + public bool Remove(object item) + { + return godot_icall_Array_Remove(GetPtr(), item); + } + + public void RemoveAt(int index) + { + godot_icall_Array_RemoveAt(GetPtr(), index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> + { + Array objectArray; + + public Array() + { + objectArray = new Array(); + } + + public Array(Array array) + { + objectArray = array; + } + + internal Array(IntPtr handle) + { + objectArray = new Array(handle); + } + + internal Array(ArraySafeHandle handle) + { + objectArray = new Array(handle); + } + + public static explicit operator Array(Array<T> from) + { + return from.objectArray; + } + + public T this[int index] + { + get + { + return (T)objectArray[index]; + } + set + { + objectArray[index] = value; + } + } + + public int Count + { + get + { + return objectArray.Count; + } + } + + public bool IsReadOnly + { + get + { + return objectArray.IsReadOnly; + } + } + + public void Add(T item) + { + objectArray.Add(item); + } + + public void Clear() + { + objectArray.Clear(); + } + + public bool Contains(T item) + { + return objectArray.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + + // TODO This may be quite slow because every element access is an internal call. + // It could be moved entirely to an internal call if we find out how to do the cast there. + + int count = objectArray.Count; + + if (array.Length < (arrayIndex + count)) + throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + + for (int i = 0; i < count; i++) + { + array[arrayIndex] = (T)objectArray[i]; + arrayIndex++; + } + } + + public IEnumerator<T> GetEnumerator() + { + int count = objectArray.Count; + + for (int i = 0; i < count; i++) + { + yield return (T)objectArray[i]; + } + } + + public int IndexOf(T item) + { + return objectArray.IndexOf(item); + } + + public void Insert(int index, T item) + { + objectArray.Insert(index, item); + } + + public bool Remove(T item) + { + return objectArray.Remove(item); + } + + public void RemoveAt(int index) + { + objectArray.RemoveAt(index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + internal IntPtr GetPtr() + { + return objectArray.GetPtr(); + } + } +} diff --git a/modules/mono/glue/cs_files/Attributes/ExportAttribute.cs b/modules/mono/glue/cs_files/Attributes/ExportAttribute.cs new file mode 100644 index 0000000000..6adf044886 --- /dev/null +++ b/modules/mono/glue/cs_files/Attributes/ExportAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class ExportAttribute : Attribute + { + private PropertyHint hint; + private string hintString; + + public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString = "") + { + this.hint = hint; + this.hintString = hintString; + } + } +} diff --git a/modules/mono/glue/cs_files/GodotMethodAttribute.cs b/modules/mono/glue/cs_files/Attributes/GodotMethodAttribute.cs index 21333c8dab..55848769d5 100644 --- a/modules/mono/glue/cs_files/GodotMethodAttribute.cs +++ b/modules/mono/glue/cs_files/Attributes/GodotMethodAttribute.cs @@ -2,7 +2,7 @@ using System; namespace Godot { - [AttributeUsage(AttributeTargets.Method, Inherited = true)] + [AttributeUsage(AttributeTargets.Method)] internal class GodotMethodAttribute : Attribute { private string methodName; diff --git a/modules/mono/glue/cs_files/RPCAttributes.cs b/modules/mono/glue/cs_files/Attributes/RPCAttributes.cs index 08841ffd76..6bf9560bfa 100644 --- a/modules/mono/glue/cs_files/RPCAttributes.cs +++ b/modules/mono/glue/cs_files/Attributes/RPCAttributes.cs @@ -13,4 +13,13 @@ namespace Godot [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] public class SlaveAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class RemoteSyncAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class MasterSyncAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class SlaveSyncAttribute : Attribute {} } diff --git a/modules/mono/glue/cs_files/SignalAttribute.cs b/modules/mono/glue/cs_files/Attributes/SignalAttribute.cs index d8a6cabb83..3957387be9 100644 --- a/modules/mono/glue/cs_files/SignalAttribute.cs +++ b/modules/mono/glue/cs_files/Attributes/SignalAttribute.cs @@ -5,8 +5,5 @@ namespace Godot [AttributeUsage(AttributeTargets.Delegate)] public class SignalAttribute : Attribute { - public SignalAttribute() - { - } } } diff --git a/modules/mono/glue/cs_files/Attributes/ToolAttribute.cs b/modules/mono/glue/cs_files/Attributes/ToolAttribute.cs new file mode 100644 index 0000000000..d0437409af --- /dev/null +++ b/modules/mono/glue/cs_files/Attributes/ToolAttribute.cs @@ -0,0 +1,7 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Class)] + public class ToolAttribute : Attribute {} +} diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs index c6cdc069ef..c280d32c61 100644 --- a/modules/mono/glue/cs_files/Basis.cs +++ b/modules/mono/glue/cs_files/Basis.cs @@ -1,5 +1,10 @@ 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 { @@ -13,8 +18,7 @@ namespace Godot new Vector3(0f, 0f, 1f) ); - private static readonly Basis[] orthoBases = new Basis[24] - { + private static readonly Basis[] orthoBases = { new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f), new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f), new Basis(-1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f), @@ -41,9 +45,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 +92,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 +106,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 +120,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 +141,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 +165,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,14 +177,21 @@ 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(); + Basis m = Orthonormalized(); Vector3 euler; euler.z = 0.0f; - float mxy = m.y[2]; + real_t mxy = m[1, 2]; if (mxy < 1.0f) @@ -170,19 +199,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.x = Mathf.Pi * 0.5f; + 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.x = -Mathf.Pi * 0.5f; + euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]); } return euler; @@ -190,13 +219,13 @@ namespace Godot public int GetOrthogonalIndex() { - Basis orth = this; + var orth = this; for (int i = 0; i < 3; i++) { 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; @@ -220,28 +249,27 @@ namespace Godot public Basis Inverse() { - Basis inv = this; + var inv = this; - float[] co = new float[3] - { + real_t[] co = { 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 ( @@ -266,22 +294,22 @@ namespace Godot Vector3 zAxis = GetAxis(2); xAxis.Normalize(); - yAxis = (yAxis - xAxis * (xAxis.Dot(yAxis))); + yAxis = yAxis - xAxis * xAxis.Dot(yAxis); yAxis.Normalize(); - zAxis = (zAxis - xAxis * (xAxis.Dot(zAxis)) - yAxis * (yAxis.Dot(zAxis))); + zAxis = zAxis - xAxis * xAxis.Dot(zAxis) - yAxis * yAxis.Dot(zAxis); zAxis.Normalize(); - return Basis.CreateFromAxes(xAxis, yAxis, zAxis); + return 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; } public Basis Scaled(Vector3 scale) { - Basis m = this; + var m = this; m[0, 0] *= scale.x; m[0, 1] *= scale.x; @@ -296,36 +324,36 @@ 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]; } public Basis Transposed() { - Basis tr = this; + var tr = this; - float temp = this[0, 1]; - this[0, 1] = this[1, 0]; - this[1, 0] = temp; + real_t temp = tr[0, 1]; + tr[0, 1] = tr[1, 0]; + tr[1, 0] = temp; - temp = this[0, 2]; - this[0, 2] = this[2, 0]; - this[2, 0] = temp; + temp = tr[0, 2]; + tr[0, 2] = tr[2, 0]; + tr[2, 0] = temp; - temp = this[1, 2]; - this[1, 2] = this[2, 1]; - this[2, 1] = temp; + temp = tr[1, 2]; + tr[1, 2] = tr[2, 1]; + tr[2, 1] = temp; return tr; } @@ -344,98 +372,122 @@ namespace Godot { return new Vector3 ( - (this[0, 0] * v.x) + (this[1, 0] * v.y) + (this[2, 0] * v.z), - (this[0, 1] * v.x) + (this[1, 1] * v.y) + (this[2, 1] * v.z), - (this[0, 2] * v.x) + (this[1, 2] * v.y) + (this[2, 2] * v.z) + this[0, 0] * v.x + this[1, 0] * v.y + this[2, 0] * v.z, + this[0, 1] * v.x + this[1, 1] * v.y + this[2, 1] * v.z, + this[0, 2] * v.x + this[1, 2] * v.y + this[2, 2] * v.z ); } public Quat Quat() { - float trace = x[0] + y[1] + z[2]; + 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; - return new Quat( - s * 0.25f, - (x[1] + y[0]) * inv_s, - (x[2] + z[0]) * inv_s, - (z[1] - y[2]) * inv_s - ); - } else if (y[1] > z[2]) { - float s = Mathf.Sqrt(-x[0] + y[1] - z[2] + 1.0f) * 2f; - float inv_s = 1f / s; - return new Quat( - (x[1] + y[0]) * inv_s, - s * 0.25f, - (y[2] + z[1]) * inv_s, - (x[2] - z[0]) * inv_s - ); - } else { - float s = Mathf.Sqrt(-x[0] - y[1] + z[2] + 1.0f) * 2f; - float inv_s = 1f / s; - return new Quat( - (x[2] + z[0]) * inv_s, - (y[2] + z[1]) * inv_s, - s * 0.25f, - (y[0] - x[1]) * inv_s - ); } + + 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 + ); + } + + 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, + s * 0.25f, + (_y[2] + _z[1]) * inv_s, + (_x[2] - _z[0]) * inv_s + ); + } else { + 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, + s * 0.25f, + (_y[0] - _x[1]) * inv_s + ); + } } public Basis(Quat quat) { - float s = 2.0f / quat.LengthSquared(); + 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; + + _x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy); + _y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx); + _z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy)); + } + + public Basis(Vector3 euler) + { + real_t c; + real_t s; + + c = Mathf.Cos(euler.x); + s = Mathf.Sin(euler.x); + var xmat = new Basis(1, 0, 0, 0, c, -s, 0, s, c); + + c = Mathf.Cos(euler.y); + s = Mathf.Sin(euler.y); + var ymat = new Basis(c, 0, s, 0, 1, 0, -s, 0, c); - 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; + c = Mathf.Cos(euler.z); + s = Mathf.Sin(euler.z); + var zmat = new Basis(c, -s, 0, s, c, 0, 0, 0, 1); - 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)); + this = ymat * xmat * zmat; } - 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); + var 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( phi); + real_t sine = Mathf.Sin( phi); - this.x = new Vector3 + _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 + _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 + _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 +497,16 @@ namespace Godot public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) { - this.x = xAxis; - this.y = yAxis; - this.z = zAxis; + _x = xAxis; + _y = yAxis; + _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, yx, zx); - this.y = new Vector3(xy, yy, zy); - this.z = new Vector3(xz, yz, zz); + _x = new Vector3(xx, xy, xz); + _y = new Vector3(yx, yy, yz); + _z = new Vector3(zx, zy, zz); } public static Basis operator *(Basis left, Basis right) @@ -489,21 +541,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() + _x.ToString(), + _y.ToString(), + _z.ToString() }); } @@ -511,9 +563,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(format), - this.y.ToString(format), - this.z.ToString(format) + _x.ToString(format), + _y.ToString(format), + _z.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs index f9e31e9703..49e04b333a 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 = Math.Max(r, Math.Max(g, b)); + float min = Math.Min(r, Math.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 = Math.Max(r, Math.Max(g, b)); + float min = Math.Min(r, Math.Min(g, b)); float delta = max - min; @@ -96,7 +96,7 @@ namespace Godot { get { - return Mathf.Max(r, Mathf.Max(g, b)); + return Math.Max(r, Math.Max(g, b)); } set { @@ -104,7 +104,7 @@ namespace Godot } } - private static readonly Color black = new Color(0f, 0f, 0f, 1.0f); + private static readonly Color black = new Color(0f, 0f, 0f); public Color Black { @@ -180,7 +180,7 @@ namespace Godot hue += 1.0f; } - saturation = (max == 0) ? 0 : 1f - (1f * min / max); + saturation = max == 0 ? 0 : 1f - 1f * min / max; value = max / 255f; } @@ -232,12 +232,10 @@ namespace Godot { return new Color(0, 0, 0, 0); } - else - { - res.r = (r * a * sa + over.r * over.a) / res.a; - res.g = (g * a * sa + over.g * over.a) / res.a; - res.b = (b * a * sa + over.b * over.a) / res.a; - } + + res.r = (r * a * sa + over.r * over.a) / res.a; + res.g = (g * a * sa + over.g * over.a) / res.a; + res.b = (b * a * sa + over.b * over.a) / res.a; return res; } @@ -251,9 +249,13 @@ namespace Godot ); } - public float Gray() + public Color Darkened(float amount) { - return (r + g + b) / 3.0f; + Color res = this; + res.r = res.r * (1.0f - amount); + res.g = res.g * (1.0f - amount); + res.b = res.b * (1.0f - amount); + return res; } public Color Inverted() @@ -265,47 +267,108 @@ namespace Godot ); } - public Color LinearInterpolate(Color b, float t) + public Color Lightened(float amount) { Color res = this; + res.r = res.r + (1.0f - res.r) * amount; + res.g = res.g + (1.0f - res.g) * amount; + res.b = res.b + (1.0f - res.b) * amount; + return res; + } + + public Color LinearInterpolate(Color c, float t) + { + var res = this; - res.r += (t * (b.r - this.r)); - res.g += (t * (b.g - this.g)); - res.b += (t * (b.b - this.b)); - res.a += (t * (b.a - this.a)); + res.r += t * (c.r - r); + res.g += t * (c.g - g); + res.b += t * (c.b - b); + res.a += t * (c.a - a); return res; } - public int To32() + public int ToAbgr32() { - int c = (byte)(a * 255); + int c = (byte)Math.Round(a * 255); c <<= 8; - c |= (byte)(r * 255); + c |= (byte)Math.Round(b * 255); c <<= 8; - c |= (byte)(g * 255); + c |= (byte)Math.Round(g * 255); c <<= 8; - c |= (byte)(b * 255); + c |= (byte)Math.Round(r * 255); + + return c; + } + + public long ToAbgr64() + { + long c = (ushort)Math.Round(a * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(r * 65535); return c; } public int ToArgb32() { - int c = (byte)(a * 255); + int c = (byte)Math.Round(a * 255); c <<= 8; - c |= (byte)(r * 255); + c |= (byte)Math.Round(r * 255); c <<= 8; - c |= (byte)(g * 255); + c |= (byte)Math.Round(g * 255); c <<= 8; - c |= (byte)(b * 255); + c |= (byte)Math.Round(b * 255); + + return c; + } + + public long ToArgb64() + { + long c = (ushort)Math.Round(a * 65535); + c <<= 16; + c |= (ushort)Math.Round(r * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + + return c; + } + + public int ToRgba32() + { + int c = (byte)Math.Round(r * 255); + c <<= 8; + c |= (byte)Math.Round(g * 255); + c <<= 8; + c |= (byte)Math.Round(b * 255); + c <<= 8; + c |= (byte)Math.Round(a * 255); + + return c; + } + + public long ToRgba64() + { + long c = (ushort)Math.Round(r * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + c <<= 16; + c |= (ushort)Math.Round(a * 65535); return c; } public string ToHtml(bool include_alpha = true) { - String txt = string.Empty; + var txt = string.Empty; txt += _to_hex(r); txt += _to_hex(g); @@ -316,7 +379,8 @@ namespace Godot return txt; } - + + // Constructors public Color(float r, float g, float b, float a = 1.0f) { this.r = r; @@ -327,13 +391,24 @@ namespace Godot public Color(int rgba) { - this.a = (rgba & 0xFF) / 255.0f; + a = (rgba & 0xFF) / 255.0f; rgba >>= 8; - this.b = (rgba & 0xFF) / 255.0f; + b = (rgba & 0xFF) / 255.0f; rgba >>= 8; - this.g = (rgba & 0xFF) / 255.0f; + g = (rgba & 0xFF) / 255.0f; rgba >>= 8; - this.r = (rgba & 0xFF) / 255.0f; + r = (rgba & 0xFF) / 255.0f; + } + + public Color(long rgba) + { + a = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + b = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + g = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + r = (rgba & 0xFFFF) / 65535.0f; } private static int _parse_col(string str, int ofs) @@ -343,7 +418,7 @@ namespace Godot for (int i = 0; i < 2; i++) { int c = str[i + ofs]; - int v = 0; + int v; if (c >= '0' && c <= '9') { @@ -375,9 +450,9 @@ namespace Godot private String _to_hex(float val) { - int v = (int)Mathf.Clamp(val * 255.0f, 0, 255); + int v = Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255)); - string ret = string.Empty; + var ret = string.Empty; for (int i = 0; i < 2; i++) { @@ -404,7 +479,7 @@ namespace Godot if (color[0] == '#') color = color.Substring(1, color.Length - 1); - bool alpha = false; + bool alpha; if (color.Length == 8) alpha = true; @@ -433,7 +508,7 @@ namespace Godot public static Color Color8(byte r8, byte g8, byte b8, byte a8) { - return new Color((float)r8 / 255f, (float)g8 / 255f, (float)b8 / 255f, (float)a8 / 255f); + return new Color(r8 / 255f, g8 / 255f, b8 / 255f, a8 / 255f); } public Color(string rgba) @@ -450,7 +525,7 @@ namespace Godot if (rgba[0] == '#') rgba = rgba.Substring(1); - bool alpha = false; + bool alpha; if (rgba.Length == 8) { @@ -512,14 +587,11 @@ namespace Godot if (left.g == right.g) { if (left.b == right.b) - return (left.a < right.a); - else - return (left.b < right.b); - } - else - { - return left.g < right.g; + return left.a < right.a; + return left.b < right.b; } + + return left.g < right.g; } return left.r < right.r; @@ -532,14 +604,11 @@ namespace Godot if (left.g == right.g) { if (left.b == right.b) - return (left.a > right.a); - else - return (left.b > right.b); - } - else - { - return left.g > right.g; + return left.a > right.a; + return left.b > right.b; } + + return left.g > right.g; } return left.r > right.r; @@ -567,24 +636,12 @@ namespace Godot public override string ToString() { - return String.Format("{0},{1},{2},{3}", new object[] - { - this.r.ToString(), - this.g.ToString(), - this.b.ToString(), - this.a.ToString() - }); + return String.Format("{0},{1},{2},{3}", r.ToString(), g.ToString(), b.ToString(), a.ToString()); } public string ToString(string format) { - return String.Format("{0},{1},{2},{3}", new object[] - { - this.r.ToString(format), - this.g.ToString(format), - this.b.ToString(format), - this.a.ToString(format) - }); + return String.Format("{0},{1},{2},{3}", r.ToString(format), g.ToString(format), b.ToString(format), a.ToString(format)); } } } diff --git a/modules/mono/glue/cs_files/DebuggingUtils.cs b/modules/mono/glue/cs_files/DebuggingUtils.cs index ffaaf00837..b27816084e 100644 --- a/modules/mono/glue/cs_files/DebuggingUtils.cs +++ b/modules/mono/glue/cs_files/DebuggingUtils.cs @@ -14,7 +14,7 @@ namespace Godot else if (type == typeof(void)) sb.Append("void"); else - sb.Append(type.ToString()); + sb.Append(type); sb.Append(" "); } @@ -32,7 +32,7 @@ namespace Godot return; } - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); if (methodBase is MethodInfo) sb.AppendTypeName(((MethodInfo)methodBase).ReturnType); diff --git a/modules/mono/glue/cs_files/Dictionary.cs b/modules/mono/glue/cs_files/Dictionary.cs new file mode 100644 index 0000000000..30d17c2a59 --- /dev/null +++ b/modules/mono/glue/cs_files/Dictionary.cs @@ -0,0 +1,406 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Godot +{ + class DictionarySafeHandle : SafeHandle + { + public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) + { + this.handle = handle; + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + protected override bool ReleaseHandle() + { + Dictionary.godot_icall_Dictionary_Dtor(handle); + return true; + } + } + + public class Dictionary : + IDictionary<object, object>, + ICollection<KeyValuePair<object, object>>, + IEnumerable<KeyValuePair<object, object>>, + IDisposable + { + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Ctor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Dictionary_Count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value); + + DictionarySafeHandle safeHandle; + bool disposed = false; + + public Dictionary() + { + safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); + } + + internal Dictionary(DictionarySafeHandle handle) + { + safeHandle = handle; + } + + internal Dictionary(IntPtr handle) + { + safeHandle = new DictionarySafeHandle(handle); + } + + internal IntPtr GetPtr() + { + return safeHandle.DangerousGetHandle(); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (safeHandle != null) + { + safeHandle.Dispose(); + safeHandle = null; + } + + disposed = true; + } + + public object this[object key] + { + get + { + return godot_icall_Dictionary_GetValue(GetPtr(), key); + } + set + { + godot_icall_Dictionary_SetValue(GetPtr(), key, value); + } + } + + public ICollection<object> Keys + { + get + { + IntPtr handle = godot_icall_Dictionary_Keys(GetPtr()); + return new Array(new ArraySafeHandle(handle)); + } + } + + public ICollection<object> Values + { + get + { + IntPtr handle = godot_icall_Dictionary_Values(GetPtr()); + return new Array(new ArraySafeHandle(handle)); + } + } + + public int Count + { + get + { + return godot_icall_Dictionary_Count(GetPtr()); + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public void Add(object key, object value) + { + godot_icall_Dictionary_Add(GetPtr(), key, value); + } + + public void Add(KeyValuePair<object, object> item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + godot_icall_Dictionary_Clear(GetPtr()); + } + + public bool Contains(KeyValuePair<object, object> item) + { + return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value); + } + + public bool ContainsKey(object key) + { + return godot_icall_Dictionary_ContainsKey(GetPtr(), key); + } + + public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex) + { + // TODO 3 internal calls, can reduce to 1 + Array keys = (Array)Keys; + Array values = (Array)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]); + arrayIndex++; + } + } + + public IEnumerator<KeyValuePair<object, object>> GetEnumerator() + { + // TODO 3 internal calls, can reduce to 1 + Array keys = (Array)Keys; + Array values = (Array)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + yield return new KeyValuePair<object, object>(keys[i], values[i]); + } + } + + public bool Remove(object key) + { + return godot_icall_Dictionary_RemoveKey(GetPtr(), key); + } + + public bool Remove(KeyValuePair<object, object> item) + { + return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); + } + + public bool TryGetValue(object key, out object value) + { + object retValue; + bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue); + value = found ? retValue : default(object); + return found; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + + public class Dictionary<TKey, TValue> : + IDictionary<TKey, TValue>, + ICollection<KeyValuePair<TKey, TValue>>, + IEnumerable<KeyValuePair<TKey, TValue>> + { + Dictionary objectDict; + + public Dictionary() + { + objectDict = new Dictionary(); + } + + public Dictionary(Dictionary dictionary) + { + objectDict = dictionary; + } + + internal Dictionary(IntPtr handle) + { + objectDict = new Dictionary(handle); + } + + internal Dictionary(DictionarySafeHandle handle) + { + objectDict = new Dictionary(handle); + } + + public static explicit operator Dictionary(Dictionary<TKey, TValue> from) + { + return from.objectDict; + } + + public TValue this[TKey key] + { + get + { + return (TValue)objectDict[key]; + } + set + { + objectDict[key] = value; + } + } + + public ICollection<TKey> Keys + { + get + { + IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr()); + return new Array<TKey>(new ArraySafeHandle(handle)); + } + } + + public ICollection<TValue> Values + { + get + { + IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr()); + return new Array<TValue>(new ArraySafeHandle(handle)); + } + } + + public int Count + { + get + { + return objectDict.Count; + } + } + + public bool IsReadOnly + { + get + { + return objectDict.IsReadOnly; + } + } + + public void Add(TKey key, TValue value) + { + objectDict.Add(key, value); + } + + public void Add(KeyValuePair<TKey, TValue> item) + { + objectDict.Add(item.Key, item.Value); + } + + public void Clear() + { + objectDict.Clear(); + } + + public bool Contains(KeyValuePair<TKey, TValue> item) + { + return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); + } + + public bool ContainsKey(TKey key) + { + return objectDict.ContainsKey(key); + } + + public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) + { + // TODO 3 internal calls, can reduce to 1 + Array<TKey> keys = (Array<TKey>)Keys; + Array<TValue> values = (Array<TValue>)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]); + arrayIndex++; + } + } + + public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() + { + // TODO 3 internal calls, can reduce to 1 + Array<TKey> keys = (Array<TKey>)Keys; + Array<TValue> values = (Array<TValue>)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]); + } + } + + public bool Remove(TKey key) + { + return objectDict.Remove(key); + } + + public bool Remove(KeyValuePair<TKey, TValue> item) + { + return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value)); + } + + public bool TryGetValue(TKey key, out TValue value) + { + object retValue; + bool found = objectDict.TryGetValue(key, out retValue); + value = found ? (TValue)retValue : default(TValue); + return found; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + internal IntPtr GetPtr() + { + return objectDict.GetPtr(); + } + } +} diff --git a/modules/mono/glue/cs_files/ExportAttribute.cs b/modules/mono/glue/cs_files/ExportAttribute.cs deleted file mode 100644 index e6f569e1bb..0000000000 --- a/modules/mono/glue/cs_files/ExportAttribute.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Godot -{ - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public class ExportAttribute : Attribute - { - private PropertyHint hint; - private string hintString; - - public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString = "") - { - this.hint = hint; - this.hintString = hintString; - } - } -} diff --git a/modules/mono/glue/cs_files/Extensions/NodeExtensions.cs b/modules/mono/glue/cs_files/Extensions/NodeExtensions.cs new file mode 100644 index 0000000000..71534d7782 --- /dev/null +++ b/modules/mono/glue/cs_files/Extensions/NodeExtensions.cs @@ -0,0 +1,45 @@ +namespace Godot +{ + public partial class Node + { + public T GetNode<T>(NodePath path) where T : Godot.Node + { + return (T)GetNode(path); + } + + public T GetNodeOrNull<T>(NodePath path) where T : Godot.Node + { + return GetNode(path) as T; + } + + public T GetChild<T>(int idx) where T : Godot.Node + { + return (T)GetChild(idx); + } + + public T GetChildOrNull<T>(int idx) where T : Godot.Node + { + return GetChild(idx) as T; + } + + public T GetOwner<T>() where T : Godot.Node + { + return (T)GetOwner(); + } + + public T GetOwnerOrNull<T>() where T : Godot.Node + { + return GetOwner() as T; + } + + public T GetParent<T>() where T : Godot.Node + { + return (T)GetParent(); + } + + public T GetParentOrNull<T>() where T : Godot.Node + { + return GetParent() as T; + } + } +} diff --git a/modules/mono/glue/cs_files/Extensions/ObjectExtensions.cs b/modules/mono/glue/cs_files/Extensions/ObjectExtensions.cs new file mode 100644 index 0000000000..5c9e6609f4 --- /dev/null +++ b/modules/mono/glue/cs_files/Extensions/ObjectExtensions.cs @@ -0,0 +1,17 @@ +using System; + +namespace Godot +{ + public partial class Object + { + public static bool IsInstanceValid(Object instance) + { + return instance != null && instance.NativeInstance != IntPtr.Zero; + } + + public static WeakRef WeakRef(Object obj) + { + return NativeCalls.godot_icall_Godot_weakref(Object.GetPtr(obj)); + } + } +} diff --git a/modules/mono/glue/cs_files/Extensions/ResourceLoaderExtensions.cs b/modules/mono/glue/cs_files/Extensions/ResourceLoaderExtensions.cs new file mode 100644 index 0000000000..ceecc589e6 --- /dev/null +++ b/modules/mono/glue/cs_files/Extensions/ResourceLoaderExtensions.cs @@ -0,0 +1,10 @@ +namespace Godot +{ + public static partial class ResourceLoader + { + public static T Load<T>(string path) where T : Godot.Resource + { + return (T) Load(path); + } + } +} diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs index b335ef55e4..0a5d703f27 100644 --- a/modules/mono/glue/cs_files/GD.cs +++ b/modules/mono/glue/cs_files/GD.cs @@ -1,4 +1,11 @@ using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +// TODO: Add comments describing what this class does. It is not obvious. namespace Godot { @@ -14,22 +21,22 @@ namespace Godot return NativeCalls.godot_icall_Godot_convert(what, type); } - public static float Db2Linear(float db) + public static real_t Db2Linear(real_t db) { - return (float)Math.Exp(db * 0.11512925464970228420089957273422); + return (real_t)Math.Exp(db * 0.11512925464970228420089957273422); } - public static float Dectime(float value, float amount, float step) + public static real_t DecTime(real_t value, real_t amount, real_t step) { - float sgn = value < 0 ? -1.0f : 1.0f; - float val = Mathf.Abs(value); + real_t sgn = Mathf.Sign(value); + real_t val = Mathf.Abs(value); val -= amount * step; - if (val < 0.0f) - val = 0.0f; + if (val < 0) + val = 0; return val * sgn; } - public static FuncRef Funcref(Object instance, string funcname) + public static FuncRef FuncRef(Object instance, string funcname) { var ret = new FuncRef(); ret.SetInstance(instance); @@ -47,9 +54,9 @@ namespace Godot return NativeCalls.godot_icall_Godot_instance_from_id(instanceId); } - public static double Linear2Db(double linear) + public static real_t Linear2Db(real_t linear) { - return Math.Log(linear) * 8.6858896380650365530225783783321; + return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321); } public static Resource Load(string path) @@ -57,6 +64,11 @@ namespace Godot return ResourceLoader.Load(path); } + public static T Load<T>(string path) where T : Godot.Resource + { + return (T) ResourceLoader.Load(path); + } + public static void Print(params object[] what) { NativeCalls.godot_icall_Godot_print(what); @@ -67,29 +79,29 @@ namespace Godot Print(System.Environment.StackTrace); } - public static void Printerr(params object[] what) + public static void PrintErr(params object[] what) { NativeCalls.godot_icall_Godot_printerr(what); } - public static void Printraw(params object[] what) + public static void PrintRaw(params object[] what) { NativeCalls.godot_icall_Godot_printraw(what); } - public static void Prints(params object[] what) + public static void PrintS(params object[] what) { NativeCalls.godot_icall_Godot_prints(what); } - public static void Printt(params object[] what) + public static void PrintT(params object[] what) { NativeCalls.godot_icall_Godot_printt(what); } public static int[] Range(int length) { - int[] ret = new int[length]; + var ret = new int[length]; for (int i = 0; i < length; i++) { @@ -104,7 +116,7 @@ namespace Godot if (to < from) return new int[0]; - int[] ret = new int[to - from]; + var ret = new int[to - from]; for (int i = from; i < to; i++) { @@ -122,14 +134,14 @@ namespace Godot return new int[0]; // Calculate count - int count = 0; + int count; if (increment > 0) - count = ((to - from - 1) / increment) + 1; + count = (to - from - 1) / increment + 1; else - count = ((from - to - 1) / -increment) + 1; + count = (from - to - 1) / -increment + 1; - int[] ret = new int[count]; + var ret = new int[count]; if (increment > 0) { @@ -180,10 +192,5 @@ namespace Godot { return NativeCalls.godot_icall_Godot_var2str(var); } - - public static WeakRef Weakref(Object obj) - { - return NativeCalls.godot_icall_Godot_weakref(Object.GetPtr(obj)); - } } } diff --git a/modules/mono/glue/cs_files/GodotSynchronizationContext.cs b/modules/mono/glue/cs_files/GodotSynchronizationContext.cs index eb4d0bed1c..da3c7bac83 100644 --- a/modules/mono/glue/cs_files/GodotSynchronizationContext.cs +++ b/modules/mono/glue/cs_files/GodotSynchronizationContext.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; diff --git a/modules/mono/glue/cs_files/GodotTaskScheduler.cs b/modules/mono/glue/cs_files/GodotTaskScheduler.cs index f587645a49..3d23ec10f1 100644 --- a/modules/mono/glue/cs_files/GodotTaskScheduler.cs +++ b/modules/mono/glue/cs_files/GodotTaskScheduler.cs @@ -14,6 +14,7 @@ namespace Godot public GodotTaskScheduler() { Context = new GodotSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(Context); } protected sealed override void QueueTask(Task task) @@ -36,7 +37,7 @@ namespace Godot TryDequeue(task); } - return base.TryExecuteTask(task); + return TryExecuteTask(task); } protected sealed override bool TryDequeue(Task task) @@ -57,7 +58,6 @@ namespace Godot public void Activate() { - SynchronizationContext.SetSynchronizationContext(Context); ExecuteQueuedTasks(); Context.ExecutePendingContinuations(); } diff --git a/modules/mono/glue/cs_files/IAwaiter.cs b/modules/mono/glue/cs_files/IAwaiter.cs deleted file mode 100644 index 73c71b5634..0000000000 --- a/modules/mono/glue/cs_files/IAwaiter.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Runtime.CompilerServices; - -namespace Godot -{ - public interface IAwaiter : INotifyCompletion - { - bool IsCompleted { get; } - - void GetResult(); - } - - public interface IAwaiter<out TResult> : INotifyCompletion - { - bool IsCompleted { get; } - - TResult GetResult(); - } -} diff --git a/modules/mono/glue/cs_files/IAwaitable.cs b/modules/mono/glue/cs_files/Interfaces/IAwaitable.cs index 0397957d00..0397957d00 100644 --- a/modules/mono/glue/cs_files/IAwaitable.cs +++ b/modules/mono/glue/cs_files/Interfaces/IAwaitable.cs diff --git a/modules/mono/glue/cs_files/Interfaces/IAwaiter.cs b/modules/mono/glue/cs_files/Interfaces/IAwaiter.cs new file mode 100644 index 0000000000..d3be9d781c --- /dev/null +++ b/modules/mono/glue/cs_files/Interfaces/IAwaiter.cs @@ -0,0 +1,18 @@ +using System.Runtime.CompilerServices; + +namespace Godot +{ + public interface IAwaiter : INotifyCompletion + { + bool IsCompleted { get; } + + void GetResult(); + } + + public interface IAwaiter<out TResult> : INotifyCompletion + { + bool IsCompleted { get; } + + TResult GetResult(); + } +} diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs index 2bdfb95c51..6ad4b3dcb2 100644 --- a/modules/mono/glue/cs_files/MarshalUtils.cs +++ b/modules/mono/glue/cs_files/MarshalUtils.cs @@ -1,36 +1,17 @@ using System; -using System.Collections.Generic; namespace Godot { - internal static class MarshalUtils + static class MarshalUtils { - private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values) + static bool IsArrayGenericType(Type type) { - Dictionary<object, object> ret = new Dictionary<object, object>(); - - for (int i = 0; i < keys.Length; i++) - { - ret.Add(keys[i], values[i]); - } - - return ret; - } - - private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo) - { - Dictionary<object, object>.KeyCollection keys = from.Keys; - keysTo = new object[keys.Count]; - keys.CopyTo(keysTo, 0); - - Dictionary<object, object>.ValueCollection values = from.Values; - valuesTo = new object[values.Count]; - values.CopyTo(valuesTo, 0); + return type.GetGenericTypeDefinition() == typeof(Array<>); } - private static Type GetDictionaryType() + static bool IsDictionaryGenericType(Type type) { - return typeof(Dictionary<object, object>); + return type.GetGenericTypeDefinition() == typeof(Dictionary<, >); } } } diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs index 476396e9a3..a89dfe5f27 100644 --- a/modules/mono/glue/cs_files/Mathf.cs +++ b/modules/mono/glue/cs_files/Mathf.cs @@ -1,75 +1,85 @@ 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 static partial 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. - private const float Deg2RadConst = 0.0174532924f; - private const float Rad2DegConst = 57.29578f; + public const real_t Tau = (real_t) 6.2831853071795864769252867666M; // 6.2831855f and 6.28318530717959 + public const real_t Pi = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979 + public const real_t Inf = real_t.PositiveInfinity; + public const real_t NaN = real_t.NaN; - public static float Abs(float s) + 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 real_t Abs(real_t s) { return Math.Abs(s); } - public static float Acos(float s) + public static int Abs(int s) { - return (float)Math.Acos(s); + return Math.Abs(s); } - public static float Asin(float s) + public static real_t Acos(real_t s) { - return (float)Math.Asin(s); + return (real_t)Math.Acos(s); } - public static float Atan(float s) + public static real_t Asin(real_t s) { - return (float)Math.Atan(s); + return (real_t)Math.Asin(s); } - public static float Atan2(float x, float y) + public static real_t Atan(real_t s) { - return (float)Math.Atan2(x, y); + return (real_t)Math.Atan(s); } - public static Vector2 Cartesian2Polar(float x, float y) - { - return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); - } + public static real_t Atan2(real_t x, real_t y) + { + return (real_t)Math.Atan2(x, y); + } - public static float Ceil(float s) + public static Vector2 Cartesian2Polar(real_t x, real_t y) { - return (float)Math.Ceiling(s); + return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); } - public static float Clamp(float val, float min, float max) + public static real_t Ceil(real_t s) { - if (val < min) - { - return min; - } - else if (val > max) - { - return max; - } + return (real_t)Math.Ceiling(s); + } - return val; + public static int Clamp(int value, int min, int max) + { + return value < min ? min : value > max ? max : value; } - public static float Cos(float s) + public static real_t Clamp(real_t value, real_t min, real_t max) { - return (float)Math.Cos(s); + return value < min ? min : value > max ? max : value; } - public static float Cosh(float s) + public static real_t Cos(real_t s) { - return (float)Math.Cosh(s); + return (real_t)Math.Cos(s); } - public static int Decimals(float step) + public static real_t Cosh(real_t s) + { + return (real_t)Math.Cosh(s); + } + + public static int Decimals(real_t step) { return Decimals((decimal)step); } @@ -79,12 +89,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) { @@ -104,7 +114,8 @@ namespace Godot return Pow(s, curve); } - else if (curve < 0f) + + if (curve < 0f) { if (s < 0.5f) { @@ -117,111 +128,145 @@ 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 InverseLerp(real_t from, real_t to, real_t weight) { - if (x >= 0f) - { - return x % y; - } - else - { - return y - (-x % y); - } + return (weight - from) / (to - from); } - public static float Lerp(float from, float to, float weight) + public static bool IsInf(real_t s) { - return from + (to - from) * Clamp(weight, 0f, 1f); + return real_t.IsInfinity(s); } - public static float Log(float s) + public static bool IsNaN(real_t s) { - return (float)Math.Log(s); + return real_t.IsNaN(s); + } + + public static real_t Lerp(real_t from, real_t to, real_t weight) + { + return from + (to - from) * weight; + } + + public static real_t Log(real_t s) + { + return (real_t)Math.Log(s); } public static int Max(int a, int b) { - return (a > b) ? a : b; + 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; + return a > b ? a : b; } public static int Min(int a, int b) { - return (a < b) ? a : b; + return a < b ? a : b; + } + + public static real_t Min(real_t a, real_t b) + { + return a < b ? a : b; } - public static float Min(float a, float b) + public static int NearestPo2(int value) { - return (a < b) ? a : b; + value--; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value++; + return value; } - public static int NearestPo2(int val) + public static Vector2 Polar2Cartesian(real_t r, real_t th) { - val--; - val |= val >> 1; - val |= val >> 2; - val |= val >> 4; - val |= val >> 8; - val |= val >> 16; - val++; - return val; + return new Vector2(r * Cos(th), r * Sin(th)); } - public static Vector2 Polar2Cartesian(float r, float th) - { - return new Vector2(r * Cos(th), r * Sin(th)); - } + /// <summary> + /// Performs a canonical Modulus operation, where the output is on the range [0, b). + /// </summary> + public static real_t PosMod(real_t a, real_t b) + { + real_t c = a % b; + if ((c < 0 && b > 0) || (c > 0 && b < 0)) + { + c += b; + } + return c; + } + + /// <summary> + /// Performs a canonical Modulus operation, where the output is on the range [0, b). + /// </summary> + public static int PosMod(int a, int b) + { + int c = a % b; + if ((c < 0 && b > 0) || (c > 0 && b < 0)) + { + c += b; + } + return c; + } - 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 (real_t)Math.Round(s); + } + + public static int Sign(int s) { - return (float)Math.Round(s); + return s < 0 ? -1 : 1; } - public static float Sign(float s) + public static real_t Sign(real_t s) { - return (s < 0f) ? -1f : 1f; + 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 +276,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 value, int min, int max) { - return (float)Math.Tan(s); + int rng = max - min; + return min + ((value - min) % rng + rng) % rng; } - public static float Tanh(float s) + public static real_t Wrap(real_t value, real_t min, real_t max) { - return (float)Math.Tanh(s); + real_t rng = max - min; + return min + ((value - min) % rng + rng) % rng; } } } diff --git a/modules/mono/glue/cs_files/MathfEx.cs b/modules/mono/glue/cs_files/MathfEx.cs new file mode 100644 index 0000000000..739b7fb568 --- /dev/null +++ b/modules/mono/glue/cs_files/MathfEx.cs @@ -0,0 +1,39 @@ +using System; + +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + public static partial class Mathf + { + // Define constants with Decimal precision and cast down to double or float. + + public const real_t E = (real_t) 2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045 + public const real_t Sqrt2 = (real_t) 1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095 + +#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 + + public static int CeilToInt(real_t s) + { + return (int)Math.Ceiling(s); + } + + public static int FloorToInt(real_t s) + { + return (int)Math.Floor(s); + } + + public static int RoundToInt(real_t s) + { + return (int)Math.Round(s); + } + } +}
\ No newline at end of file diff --git a/modules/mono/glue/cs_files/Plane.cs b/modules/mono/glue/cs_files/Plane.cs index b347c0835a..9611dce11e 100644 --- a/modules/mono/glue/cs_files/Plane.cs +++ b/modules/mono/glue/cs_files/Plane.cs @@ -1,95 +1,106 @@ 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; + private Vector3 _normal; - public float x + public Vector3 Normal + { + get { return _normal; } + set { _normal = value; } + } + + public real_t x { get { - return normal.x; + return _normal.x; } set { - normal.x = value; + _normal.x = value; } } - public float y + public real_t y { get { - return normal.y; + return _normal.y; } set { - normal.y = value; + _normal.y = value; } } - public float z + public real_t z { get { - return normal.z; + return _normal.z; } set { - normal.z = value; + _normal.z = value; } } - float d; + public real_t D { get; set; } public Vector3 Center { get { - return normal * d; + return _normal * D; } } - public float DistanceTo(Vector3 point) + public real_t DistanceTo(Vector3 point) { - return normal.Dot(point) - d; + return _normal.Dot(point) - D; } public Vector3 GetAnyPoint() { - return normal * d; + 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(); - Vector3 result = (b.normal.Cross(c.normal) * this.d) + - (c.normal.Cross(normal) * b.d) + - (normal.Cross(b.normal) * c.d); + Vector3 result = b._normal.Cross(c._normal) * D + + c._normal.Cross(_normal) * b.D + + _normal.Cross(b._normal) * c.D; return result / denom; } 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,14 +112,14 @@ 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)) + if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon) return new Vector3(); return begin + segment * -dist; @@ -116,46 +127,55 @@ namespace Godot public bool IsPointOver(Vector3 point) { - return normal.Dot(point) > d; + return _normal.Dot(point) > D; } public Plane Normalized() { - float len = normal.Length(); + real_t len = _normal.Length(); if (len == 0) return new Plane(0, 0, 0, 0); - return new Plane(normal / len, d / len); + return new Plane(_normal / len, D / len); } public Vector3 Project(Vector3 point) { - return point - normal * DistanceTo(point); + return point - _normal * DistanceTo(point); } + + // Constants + private static readonly Plane _planeYZ = new Plane(1, 0, 0, 0); + private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0); + private static readonly Plane _planeXY = new Plane(0, 0, 1, 0); - public Plane(float a, float b, float c, float d) + public static Plane PlaneYZ { get { return _planeYZ; } } + public static Plane PlaneXZ { get { return _planeXZ; } } + public static Plane PlaneXY { get { return _planeXY; } } + + // Constructors + public Plane(real_t a, real_t b, real_t c, real_t d) { - normal = new Vector3(a, b, c); - this.d = 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; + this._normal = normal; + this.D = d; } public Plane(Vector3 v1, Vector3 v2, Vector3 v3) { - normal = (v1 - v3).Cross(v1 - v2); - normal.Normalize(); - d = normal.Dot(v1); + _normal = (v1 - v3).Cross(v1 - v2); + _normal.Normalize(); + D = _normal.Dot(v1); } public static Plane operator -(Plane plane) { - return new Plane(-plane.normal, -plane.d); + return new Plane(-plane._normal, -plane.D); } public static bool operator ==(Plane left, Plane right) @@ -180,20 +200,20 @@ namespace Godot public bool Equals(Plane other) { - return normal == other.normal && d == other.d; + return _normal == other._normal && D == other.D; } public override int GetHashCode() { - return normal.GetHashCode() ^ d.GetHashCode(); + return _normal.GetHashCode() ^ D.GetHashCode(); } public override string ToString() { return String.Format("({0}, {1})", new object[] { - this.normal.ToString(), - this.d.ToString() + _normal.ToString(), + D.ToString() }); } @@ -201,8 +221,8 @@ namespace Godot { return String.Format("({0}, {1})", new object[] { - this.normal.ToString(format), - this.d.ToString(format) + _normal.ToString(format), + D.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/Quat.cs b/modules/mono/glue/cs_files/Quat.cs index c0ac41c5d7..c69c55d997 100644 --- a/modules/mono/glue/cs_files/Quat.cs +++ b/modules/mono/glue/cs_files/Quat.cs @@ -1,5 +1,10 @@ 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 { @@ -8,17 +13,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 +63,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 +81,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 +96,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) + { + x = q.x; + y = q.y; + z = q.z; + 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]; + var to1 = new real_t[4]; // Adjust signs if necessary if (cosom < 0.0) @@ -122,13 +134,13 @@ namespace Godot to1[3] = b.w; } - float sinom, scale0, scale1; + real_t sinom, scale0, scale1; // Calculate coefficients - if ((1.0 - cosom) > Mathf.Epsilon) + 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,47 +162,56 @@ 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 = 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 ( - invFactor * this.x + newFactor * b.x, - invFactor * this.y + newFactor * b.y, - invFactor * this.z + newFactor * b.z, - invFactor * this.w + newFactor * b.w + invFactor * x + newFactor * b.x, + invFactor * y + newFactor * b.y, + invFactor * z + newFactor * b.z, + invFactor * w + newFactor * b.w ); } public Vector3 Xform(Vector3 v) { Quat q = this * v; - q *= this.Inverse(); + q *= Inverse(); 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) + { + x = q.x; + y = q.y; + z = q.z; + 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 +222,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 +279,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); } @@ -305,24 +326,12 @@ namespace Godot public override string ToString() { - return String.Format("({0}, {1}, {2}, {3})", new object[] - { - this.x.ToString(), - this.y.ToString(), - this.z.ToString(), - this.w.ToString() - }); + return String.Format("({0}, {1}, {2}, {3})", x.ToString(), y.ToString(), z.ToString(), w.ToString()); } public string ToString(string format) { - return String.Format("({0}, {1}, {2}, {3})", new object[] - { - this.x.ToString(format), - this.y.ToString(format), - this.z.ToString(format), - this.w.ToString(format) - }); + return String.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format)); } } } diff --git a/modules/mono/glue/cs_files/Rect2.cs b/modules/mono/glue/cs_files/Rect2.cs index e1fbb65da5..cb25c267bc 100644 --- a/modules/mono/glue/cs_files/Rect2.cs +++ b/modules/mono/glue/cs_files/Rect2.cs @@ -1,68 +1,81 @@ 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)] public struct Rect2 : IEquatable<Rect2> { - private Vector2 position; - private Vector2 size; + private Vector2 _position; + private Vector2 _size; public Vector2 Position { - get { return position; } - set { position = value; } + get { return _position; } + set { _position = value; } } public Vector2 Size { - get { return size; } - set { size = value; } + get { return _size; } + set { _size = value; } } public Vector2 End { - get { return position + size; } + get { return _position + _size; } + set { _size = value - _position; } } - public float Area + public real_t Area { get { return GetArea(); } } + public Rect2 Abs() + { + Vector2 end = End; + Vector2 topLeft = new Vector2(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y)); + return new Rect2(topLeft, _size.Abs()); + } + public Rect2 Clip(Rect2 b) { - Rect2 newRect = b; + var newRect = b; if (!Intersects(newRect)) return new Rect2(); - newRect.position.x = Mathf.Max(b.position.x, position.x); - newRect.position.y = Mathf.Max(b.position.y, position.y); + newRect._position.x = Mathf.Max(b._position.x, _position.x); + newRect._position.y = Mathf.Max(b._position.y, _position.y); - Vector2 bEnd = b.position + b.size; - Vector2 end = position + size; + Vector2 bEnd = b._position + b._size; + Vector2 end = _position + _size; - newRect.size.x = Mathf.Min(bEnd.x, end.x) - newRect.position.x; - newRect.size.y = Mathf.Min(bEnd.y, end.y) - newRect.position.y; + newRect._size.x = Mathf.Min(bEnd.x, end.x) - newRect._position.x; + newRect._size.y = Mathf.Min(bEnd.y, end.y) - newRect._position.y; return newRect; } public bool Encloses(Rect2 b) { - return (b.position.x >= position.x) && (b.position.y >= position.y) && - ((b.position.x + b.size.x) < (position.x + size.x)) && - ((b.position.y + b.size.y) < (position.y + size.y)); + return b._position.x >= _position.x && b._position.y >= _position.y && + b._position.x + b._size.x < _position.x + _size.x && + b._position.y + b._size.y < _position.y + _size.y; } public Rect2 Expand(Vector2 to) { - Rect2 expanded = this; + var expanded = this; - Vector2 begin = expanded.position; - Vector2 end = expanded.position + expanded.size; + Vector2 begin = expanded._position; + Vector2 end = expanded._position + expanded._size; if (to.x < begin.x) begin.x = to.x; @@ -74,68 +87,68 @@ namespace Godot if (to.y > end.y) end.y = to.y; - expanded.position = begin; - expanded.size = end - begin; + expanded._position = begin; + expanded._size = end - begin; return expanded; } - public float GetArea() + public real_t GetArea() { - return size.x * size.y; + return _size.x * _size.y; } - public Rect2 Grow(float by) + public Rect2 Grow(real_t by) { - Rect2 g = this; + var g = this; - g.position.x -= by; - g.position.y -= by; - g.size.x += by * 2; - g.size.y += by * 2; + g._position.x -= by; + g._position.y -= by; + g._size.x += by * 2; + g._size.y += by * 2; 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; + var g = this; - g.position.x -= left; - g.position.y -= top; - g.size.x += left + right; - g.size.y += top + bottom; + g._position.x -= left; + g._position.y -= top; + g._size.x += left + right; + g._size.y += top + bottom; return g; } - public Rect2 GrowMargin(Margin margin, float by) + public Rect2 GrowMargin(Margin margin, real_t by) { - Rect2 g = this; + var g = this; - g.GrowIndividual((Margin.Left == margin) ? by : 0, - (Margin.Top == margin) ? by : 0, - (Margin.Right == margin) ? by : 0, - (Margin.Bottom == margin) ? by : 0); + g.GrowIndividual(Margin.Left == margin ? by : 0, + Margin.Top == margin ? by : 0, + Margin.Right == margin ? by : 0, + Margin.Bottom == margin ? by : 0); return g; } public bool HasNoArea() { - return size.x <= 0 || size.y <= 0; + return _size.x <= 0 || _size.y <= 0; } public bool HasPoint(Vector2 point) { - if (point.x < position.x) + if (point.x < _position.x) return false; - if (point.y < position.y) + if (point.y < _position.y) return false; - if (point.x >= (position.x + size.x)) + if (point.x >= _position.x + _size.x) return false; - if (point.y >= (position.y + size.y)) + if (point.y >= _position.y + _size.y) return false; return true; @@ -143,13 +156,13 @@ namespace Godot public bool Intersects(Rect2 b) { - if (position.x > (b.position.x + b.size.x)) + if (_position.x > b._position.x + b._size.x) return false; - if ((position.x + size.x) < b.position.x) + if (_position.x + _size.x < b._position.x) return false; - if (position.y > (b.position.y + b.size.y)) + if (_position.y > b._position.y + b._size.y) return false; - if ((position.y + size.y) < b.position.y) + if (_position.y + _size.y < b._position.y) return false; return true; @@ -159,27 +172,37 @@ namespace Godot { Rect2 newRect; - newRect.position.x = Mathf.Min(b.position.x, position.x); - newRect.position.y = Mathf.Min(b.position.y, position.y); + newRect._position.x = Mathf.Min(b._position.x, _position.x); + newRect._position.y = Mathf.Min(b._position.y, _position.y); - newRect.size.x = Mathf.Max(b.position.x + b.size.x, position.x + size.x); - newRect.size.y = Mathf.Max(b.position.y + b.size.y, position.y + size.y); + newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x); + newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y); - newRect.size = newRect.size - newRect.position; // Make relative again + newRect._size = newRect._size - newRect._position; // Make relative again return newRect; } - + + // Constructors public Rect2(Vector2 position, Vector2 size) { - this.position = position; - this.size = size; + _position = position; + _size = size; } - - public Rect2(float x, float y, float width, float height) + public Rect2(Vector2 position, real_t width, real_t height) + { + _position = position; + _size = new Vector2(width, height); + } + public Rect2(real_t x, real_t y, Vector2 size) + { + _position = new Vector2(x, y); + _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); + _position = new Vector2(x, y); + _size = new Vector2(width, height); } public static bool operator ==(Rect2 left, Rect2 right) @@ -204,20 +227,20 @@ namespace Godot public bool Equals(Rect2 other) { - return position.Equals(other.position) && size.Equals(other.size); + return _position.Equals(other._position) && _size.Equals(other._size); } public override int GetHashCode() { - return position.GetHashCode() ^ size.GetHashCode(); + return _position.GetHashCode() ^ _size.GetHashCode(); } public override string ToString() { return String.Format("({0}, {1})", new object[] { - this.position.ToString(), - this.size.ToString() + _position.ToString(), + _size.ToString() }); } @@ -225,8 +248,8 @@ namespace Godot { return String.Format("({0}, {1})", new object[] { - this.position.ToString(format), - this.size.ToString(format) + _position.ToString(format), + _size.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/SignalAwaiter.cs b/modules/mono/glue/cs_files/SignalAwaiter.cs index 19ccc26e79..c06f6b05c9 100644 --- a/modules/mono/glue/cs_files/SignalAwaiter.cs +++ b/modules/mono/glue/cs_files/SignalAwaiter.cs @@ -4,15 +4,15 @@ namespace Godot { public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]> { - private bool completed = false; - private object[] result = null; - private Action action = null; + private bool completed; + private object[] result; + private Action action; - public SignalAwaiter(Godot.Object source, string signal, Godot.Object target) + public SignalAwaiter(Object source, string signal, Object target) { NativeCalls.godot_icall_Object_connect_signal_awaiter( - Godot.Object.GetPtr(source), - signal, Godot.Object.GetPtr(target), this + Object.GetPtr(source), + signal, Object.GetPtr(target), this ); } diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/cs_files/StringExtensions.cs index 5c3ceff97d..b58f8bc6a8 100644 --- a/modules/mono/glue/cs_files/StringExtensions.cs +++ b/modules/mono/glue/cs_files/StringExtensions.cs @@ -1,4 +1,3 @@ -//using System; using System; using System.Collections.Generic; using System.Globalization; @@ -43,11 +42,9 @@ namespace Godot { return instance.Substring(prev, i - prev); } - else - { - count++; - prev = i + 1; - } + + count++; + prev = i + 1; } i++; @@ -83,7 +80,7 @@ namespace Godot // </summary> public static string[] Bigrams(this string instance) { - string[] b = new string[instance.Length - 1]; + var b = new string[instance.Length - 1]; for (int i = 0; i < b.Length; i++) { @@ -98,7 +95,7 @@ namespace Godot // </summary> public static string CEscape(this string instance) { - StringBuilder sb = new StringBuilder(string.Copy(instance)); + var sb = new StringBuilder(string.Copy(instance)); sb.Replace("\\", "\\\\"); sb.Replace("\a", "\\a"); @@ -120,7 +117,7 @@ namespace Godot // </summary> public static string CUnescape(this string instance) { - StringBuilder sb = new StringBuilder(string.Copy(instance)); + var sb = new StringBuilder(string.Copy(instance)); sb.Replace("\\a", "\a"); sb.Replace("\\b", "\b"); @@ -143,7 +140,7 @@ namespace Godot public static string Capitalize(this string instance) { string aux = instance.Replace("_", " ").ToLower(); - string cap = string.Empty; + var cap = string.Empty; for (int i = 0; i < aux.GetSliceCount(" "); i++) { @@ -178,13 +175,13 @@ namespace Godot { if (to[to_idx] == 0 && instance[instance_idx] == 0) return 0; // We're equal - else if (instance[instance_idx] == 0) + if (instance[instance_idx] == 0) return -1; // If this is empty, and the other one is not, then we're less... I think? - else if (to[to_idx] == 0) + if (to[to_idx] == 0) return 1; // Otherwise the other one is smaller... - else if (instance[instance_idx] < to[to_idx]) // More than + if (instance[instance_idx] < to[to_idx]) // More than return -1; - else if (instance[instance_idx] > to[to_idx]) // Less than + if (instance[instance_idx] > to[to_idx]) // Less than return 1; instance_idx++; @@ -226,7 +223,7 @@ namespace Godot if (pos < 0) return instance; - return instance.Substring(pos + 1, instance.Length); + return instance.Substring(pos + 1); } // <summary> @@ -260,12 +257,12 @@ namespace Godot { int basepos = instance.Find("://"); - string rs = string.Empty; - string @base = string.Empty; + string rs; + var @base = string.Empty; if (basepos != -1) { - int end = basepos + 3; + var end = basepos + 3; rs = instance.Substring(end, instance.Length); @base = instance.Substring(0, end); } @@ -287,7 +284,7 @@ namespace Godot if (sep == -1) return @base; - return @base + rs.substr(0, sep); + return @base + rs.Substr(0, sep); } // <summary> @@ -312,8 +309,8 @@ namespace Godot int hashv = 5381; int c; - while ((c = (int)instance[index++]) != 0) - hashv = ((hashv << 5) + hashv) + c; // hash * 33 + c + while ((c = instance[index++]) != 0) + hashv = (hashv << 5) + hashv + c; // hash * 33 + c return hashv; } @@ -379,7 +376,7 @@ namespace Godot while (instance[src] != 0 && text[tgt] != 0) { - bool match = false; + bool match; if (case_insensitive) { @@ -455,7 +452,10 @@ namespace Godot return false; // Don't start with number plz } - bool valid_char = (instance[i] >= '0' && instance[i] <= '9') || (instance[i] >= 'a' && instance[i] <= 'z') || (instance[i] >= 'A' && instance[i] <= 'Z') || instance[i] == '_'; + bool valid_char = instance[i] >= '0' && + instance[i] <= '9' || instance[i] >= 'a' && + instance[i] <= 'z' || instance[i] >= 'A' && + instance[i] <= 'Z' || instance[i] == '_'; if (!valid_char) return false; @@ -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; } @@ -502,7 +502,7 @@ namespace Godot // </summary> public static string JsonEscape(this string instance) { - StringBuilder sb = new StringBuilder(string.Copy(instance)); + var sb = new StringBuilder(string.Copy(instance)); sb.Replace("\\", "\\\\"); sb.Replace("\b", "\\b"); @@ -551,7 +551,7 @@ namespace Godot case '\0': return instance[0] == 0; case '*': - return ExprMatch(expr + 1, instance, caseSensitive) || (instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive)); + return ExprMatch(expr + 1, instance, caseSensitive) || instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive); case '?': return instance[0] != 0 && instance[0] != '.' && ExprMatch(expr + 1, instance + 1, caseSensitive); default: @@ -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); } @@ -581,7 +581,7 @@ namespace Godot // </summary> public static byte[] Md5Buffer(this string instance) { - return NativeCalls.godot_icall_String_md5_buffer(instance); + return NativeCalls.godot_icall_String_md5_buffer(instance); } // <summary> @@ -589,7 +589,7 @@ namespace Godot // </summary> public static string Md5Text(this string instance) { - return NativeCalls.godot_icall_String_md5_text(instance); + return NativeCalls.godot_icall_String_md5_text(instance); } // <summary> @@ -610,13 +610,13 @@ namespace Godot { if (to[to_idx] == 0 && instance[instance_idx] == 0) return 0; // We're equal - else if (instance[instance_idx] == 0) + if (instance[instance_idx] == 0) return -1; // If this is empty, and the other one is not, then we're less... I think? - else if (to[to_idx] == 0) + if (to[to_idx] == 0) return 1; // Otherwise the other one is smaller.. - else if (char.ToUpper(instance[instance_idx]) < char.ToUpper(to[to_idx])) // More than + if (char.ToUpper(instance[instance_idx]) < char.ToUpper(to[to_idx])) // More than return -1; - else if (char.ToUpper(instance[instance_idx]) > char.ToUpper(to[to_idx])) // Less than + if (char.ToUpper(instance[instance_idx]) > char.ToUpper(to[to_idx])) // Less than return 1; instance_idx++; @@ -724,8 +724,7 @@ namespace Godot { if (instance.Length > 0 && instance[instance.Length - 1] == '/') return instance + file; - else - return instance + "/" + file; + return instance + "/" + file; } // <summary> @@ -749,7 +748,7 @@ namespace Godot // </summary> public static int Rfind(this string instance, string what, int from = -1) { - return NativeCalls.godot_icall_String_rfind(instance, what, from); + return NativeCalls.godot_icall_String_rfind(instance, what, from); } // <summary> @@ -757,7 +756,7 @@ namespace Godot // </summary> public static int Rfindn(this string instance, string what, int from = -1) { - return NativeCalls.godot_icall_String_rfindn(instance, what, from); + return NativeCalls.godot_icall_String_rfindn(instance, what, from); } // <summary> @@ -771,12 +770,12 @@ namespace Godot if (pos < 0) return string.Empty; - return instance.Substring(pos, (instance.Length - pos)); + return instance.Substring(pos, instance.Length - pos); } public static byte[] Sha256Buffer(this string instance) { - return NativeCalls.godot_icall_String_sha256_buffer(instance); + return NativeCalls.godot_icall_String_sha256_buffer(instance); } // <summary> @@ -784,7 +783,7 @@ namespace Godot // </summary> public static string Sha256Text(this string instance) { - return NativeCalls.godot_icall_String_sha256_text(instance); + return NativeCalls.godot_icall_String_sha256_text(instance); } // <summary> @@ -824,23 +823,23 @@ namespace Godot } } - return (2.0f * inter) / sum; + return 2.0f * inter / sum; } // <summary> // Split the string by a divisor string, return an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". // </summary> - public static string[] split(this string instance, string divisor, bool allow_empty = true) + public static string[] Split(this string instance, string divisor, bool allow_empty = true) { - return instance.Split(new string[] { divisor }, StringSplitOptions.RemoveEmptyEntries); + return instance.Split(new[] { divisor }, StringSplitOptions.RemoveEmptyEntries); } // <summary> // Split the string in floats by using a divisor string, return an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",". // </summary> - public static float[] split_floats(this string instance, string divisor, bool allow_empty = true) + public static float[] SplitFloats(this string instance, string divisor, bool allow_empty = true) { - List<float> ret = new List<float>(); + var ret = new List<float>(); int from = 0; int len = instance.Length; @@ -849,7 +848,7 @@ namespace Godot int end = instance.Find(divisor, from); if (end < 0) end = len; - if (allow_empty || (end > from)) + if (allow_empty || end > from) ret.Add(float.Parse(instance.Substring(from))); if (end == len) break; @@ -872,25 +871,22 @@ 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) { if (right) return instance.Trim(non_printable); - else - return instance.TrimStart(non_printable); - } - else - { - return instance.TrimEnd(non_printable); + return instance.TrimStart(non_printable); } + + return instance.TrimEnd(non_printable); } // <summary> // Return part of the string from the position [code]from[/code], with length [code]len[/code]. // </summary> - public static string substr(this string instance, int from, int len) + public static string Substr(this string instance, int from, int len) { return instance.Substring(from, len); } @@ -898,7 +894,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 +902,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 +910,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 +918,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 +926,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 +934,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 +942,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 +950,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/ToolAttribute.cs b/modules/mono/glue/cs_files/ToolAttribute.cs deleted file mode 100644 index d8601b5b32..0000000000 --- a/modules/mono/glue/cs_files/ToolAttribute.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System; - -namespace Godot -{ - [AttributeUsage(AttributeTargets.Class)] - public class ToolAttribute : Attribute {} -} diff --git a/modules/mono/glue/cs_files/Transform.cs b/modules/mono/glue/cs_files/Transform.cs index 5214100d36..e432d5b52c 100644 --- a/modules/mono/glue/cs_files/Transform.cs +++ b/modules/mono/glue/cs_files/Transform.cs @@ -1,5 +1,10 @@ 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 { @@ -23,8 +28,8 @@ namespace Godot public Transform LookingAt(Vector3 target, Vector3 up) { - Transform t = this; - t.set_look_at(origin, target, up); + var t = this; + t.SetLookAt(origin, target, up); return t; } @@ -33,7 +38,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 +48,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 @@ -92,21 +97,33 @@ namespace Godot return new Vector3 ( - (basis[0, 0] * vInv.x) + (basis[1, 0] * vInv.y) + (basis[2, 0] * vInv.z), - (basis[0, 1] * vInv.x) + (basis[1, 1] * vInv.y) + (basis[2, 1] * vInv.z), - (basis[0, 2] * vInv.x) + (basis[1, 2] * vInv.y) + (basis[2, 2] * vInv.z) + basis[0, 0] * vInv.x + basis[1, 0] * vInv.y + basis[2, 0] * vInv.z, + basis[0, 1] * vInv.x + basis[1, 1] * vInv.y + basis[2, 1] * vInv.z, + basis[0, 2] * vInv.x + basis[1, 2] * vInv.y + basis[2, 2] * vInv.z ); } + // Constants + private static readonly Transform _identity = new Transform(Basis.Identity, Vector3.Zero); + private static readonly Transform _flipX = new Transform(new Basis(new Vector3(-1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1)), Vector3.Zero); + private static readonly Transform _flipY = new Transform(new Basis(new Vector3(1, 0, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 1)), Vector3.Zero); + private static readonly Transform _flipZ = new Transform(new Basis(new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, -1)), Vector3.Zero); + + public static Transform Identity { get { return _identity; } } + public static Transform FlipX { get { return _flipX; } } + public static Transform FlipY { get { return _flipY; } } + public static Transform FlipZ { get { return _flipZ; } } + + // Constructors public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin) { - this.basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); + basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); this.origin = origin; } public Transform(Quat quat, Vector3 origin) { - this.basis = new Basis(quat); + basis = new Basis(quat); this.origin = origin; } @@ -157,8 +174,8 @@ namespace Godot { return String.Format("{0} - {1}", new object[] { - this.basis.ToString(), - this.origin.ToString() + basis.ToString(), + origin.ToString() }); } @@ -166,8 +183,8 @@ namespace Godot { return String.Format("{0} - {1}", new object[] { - this.basis.ToString(format), - this.origin.ToString(format) + basis.ToString(format), + origin.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/Transform2D.cs b/modules/mono/glue/cs_files/Transform2D.cs index fe7c5b5706..8d30833066 100644 --- a/modules/mono/glue/cs_files/Transform2D.cs +++ b/modules/mono/glue/cs_files/Transform2D.cs @@ -1,33 +1,26 @@ 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)] public struct Transform2D : IEquatable<Transform2D> { - private static readonly Transform2D identity = new Transform2D - ( - new Vector2(1f, 0f), - new Vector2(0f, 1f), - new Vector2(0f, 0f) - ); - public Vector2 x; public Vector2 y; public Vector2 o; - public static Transform2D Identity - { - get { return identity; } - } - public Vector2 Origin { get { return o; } } - public float Rotation + public real_t Rotation { get { return Mathf.Atan2(y.x, o.y); } } @@ -73,7 +66,7 @@ namespace Godot } - public float this[int index, int axis] + public real_t this[int index, int axis] { get { @@ -105,9 +98,9 @@ namespace Godot public Transform2D AffineInverse() { - Transform2D inv = this; + var 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 +112,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,24 +136,24 @@ 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; // Slerp rotation - Vector2 v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1)); - Vector2 v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2)); + var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1)); + var 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); + dot = dot < -1.0f ? -1.0f : (dot > 1.0f ? 1.0f : dot); - Vector2 v = new Vector2(); + Vector2 v; if (dot > 0.9995f) { @@ -169,7 +162,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); } @@ -179,7 +172,7 @@ namespace Godot Vector2 p2 = m.Origin; // Construct matrix - Transform2D res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c)); + var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c)); Vector2 scale = s1.LinearInterpolate(s2, c); res.x *= scale; res.y *= scale; @@ -189,10 +182,10 @@ namespace Godot public Transform2D Inverse() { - Transform2D inv = this; + var inv = this; // Swap - float temp = inv.x.y; + real_t temp = inv.x.y; inv.x.y = inv.y.x; inv.y.x = temp; @@ -203,13 +196,13 @@ namespace Godot public Transform2D Orthonormalized() { - Transform2D on = this; + var on = this; Vector2 onX = on.x; Vector2 onY = on.y; onX.Normalize(); - onY = onY - onX * (onX.Dot(onY)); + onY = onY - onX * onX.Dot(onY); onY.Normalize(); on.x = onX; @@ -218,33 +211,33 @@ namespace Godot return on; } - public Transform2D Rotated(float phi) + public Transform2D Rotated(real_t phi) { return this * new Transform2D(phi, new Vector2()); } public Transform2D Scaled(Vector2 scale) { - Transform2D copy = this; + var copy = this; copy.x *= scale; copy.y *= scale; copy.o *= scale; 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]; } public Transform2D Translated(Vector2 offset) { - Transform2D copy = this; + var copy = this; copy.o += copy.BasisXform(offset); return copy; } @@ -260,23 +253,34 @@ namespace Godot return new Vector2(x.Dot(vInv), y.Dot(vInv)); } + // Constants + private static readonly Transform2D _identity = new Transform2D(new Vector2(1f, 0f), new Vector2(0f, 1f), Vector2.Zero); + private static readonly Transform2D _flipX = new Transform2D(new Vector2(-1f, 0f), new Vector2(0f, 1f), Vector2.Zero); + private static readonly Transform2D _flipY = new Transform2D(new Vector2(1f, 0f), new Vector2(0f, -1f), Vector2.Zero); + + public static Transform2D Identity { get { return _identity; } } + public static Transform2D FlipX { get { return _flipX; } } + public static Transform2D FlipY { get { return _flipY; } } + + // Constructors public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin) { - this.x = xAxis; - this.y = yAxis; - this.o = origin; + x = xAxis; + y = yAxis; + 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); + x = new Vector2(xx, xy); + y = new Vector2(yx, yy); + 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(rot); + real_t sr = Mathf.Sin(rot); x.x = cr; y.y = cr; x.y = -sr; @@ -288,7 +292,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); @@ -337,9 +341,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(), - this.y.ToString(), - this.o.ToString() + x.ToString(), + y.ToString(), + o.ToString() }); } @@ -347,9 +351,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(format), - this.y.ToString(format), - this.o.ToString(format) + x.ToString(format), + y.ToString(format), + o.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/Vector2.cs b/modules/mono/glue/cs_files/Vector2.cs index 238775bda2..080b8802ba 100644 --- a/modules/mono/glue/cs_files/Vector2.cs +++ b/modules/mono/glue/cs_files/Vector2.cs @@ -1,22 +1,26 @@ -using System; -using System.Runtime.InteropServices; - // file: core/math/math_2d.h // commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 // file: core/math/math_2d.cpp // commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 // file: core/variant_call.cpp // commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +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)] 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 +52,7 @@ namespace Godot internal void Normalize() { - float length = x * x + y * y; + real_t length = x * x + y * y; if (length != 0f) { @@ -58,7 +62,7 @@ namespace Godot } } - private float Cross(Vector2 b) + public real_t Cross(Vector2 b) { return x * b.y - y * b.x; } @@ -68,22 +72,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 +97,15 @@ namespace Godot return -Reflect(n); } - public Vector2 Clamped(float length) + public Vector2 Ceil() { - Vector2 v = this; - float l = this.Length(); + return new Vector2(Mathf.Ceil(x), Mathf.Ceil(y)); + } + + public Vector2 Clamped(real_t length) + { + var v = this; + real_t l = Length(); if (l > 0 && length < l) { @@ -107,33 +116,33 @@ 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; + var p0 = preA; + var p1 = this; + var p2 = b; + var 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) + + return 0.5f * (p1 * 2.0f + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); } - public float 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,44 +157,71 @@ 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; + var res = this; - res.x += (t * (b.x - x)); - res.y += (t * (b.y - y)); + res.x += t * (b.x - x); + res.y += t * (b.y - y); return res; } public Vector2 Normalized() { - Vector2 result = this; + var result = this; result.Normalize(); return result; } + public Vector2 Project(Vector2 onNormal) + { + return onNormal * (Dot(onNormal) / onNormal.LengthSquared()); + } + public Vector2 Reflect(Vector2 n) { 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 Vector2 Round() + { + return new Vector2(Mathf.Round(x), Mathf.Round(y)); + } + + public void Set(real_t x, real_t y) + { + this.x = x; + this.y = y; + } + public void Set(Vector2 v) + { + x = v.x; + y = v.y; + } + + public Vector2 Slerp(Vector2 b, real_t t) + { + real_t theta = AngleTo(b); + return Rotated(theta * t); + } + public Vector2 Slide(Vector2 n) { return this - n * Dot(n); @@ -201,11 +237,38 @@ namespace Godot return new Vector2(y, -x); } - public Vector2(float x, float y) + // Constants + 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 _inf = new Vector2(Mathf.Inf, Mathf.Inf); + + 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 NegOne { get { return _negOne; } } + public static Vector2 One { get { return _one; } } + public static Vector2 Inf { get { return _inf; } } + + 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) + { + x = v.x; + y = v.y; + } public static Vector2 operator +(Vector2 left, Vector2 right) { @@ -228,14 +291,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 +312,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; @@ -279,10 +342,8 @@ namespace Godot { return left.y < right.y; } - else - { - return left.x < right.x; - } + + return left.x < right.x; } public static bool operator >(Vector2 left, Vector2 right) @@ -291,10 +352,8 @@ namespace Godot { return left.y > right.y; } - else - { - return left.x > right.x; - } + + return left.x > right.x; } public static bool operator <=(Vector2 left, Vector2 right) @@ -303,10 +362,8 @@ namespace Godot { return left.y <= right.y; } - else - { - return left.x <= right.x; - } + + return left.x <= right.x; } public static bool operator >=(Vector2 left, Vector2 right) @@ -315,10 +372,8 @@ namespace Godot { return left.y >= right.y; } - else - { - return left.x >= right.x; - } + + return left.x >= right.x; } public override bool Equals(object obj) @@ -345,8 +400,8 @@ namespace Godot { return String.Format("({0}, {1})", new object[] { - this.x.ToString(), - this.y.ToString() + x.ToString(), + y.ToString() }); } @@ -354,8 +409,8 @@ namespace Godot { return String.Format("({0}, {1})", new object[] { - this.x.ToString(format), - this.y.ToString(format) + x.ToString(format), + y.ToString(format) }); } } diff --git a/modules/mono/glue/cs_files/Vector3.cs b/modules/mono/glue/cs_files/Vector3.cs index 190caa4b53..6fffe5e4d6 100644 --- a/modules/mono/glue/cs_files/Vector3.cs +++ b/modules/mono/glue/cs_files/Vector3.cs @@ -1,12 +1,16 @@ -using System; -using System.Runtime.InteropServices; - // file: core/math/vector3.h // commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0 // file: core/math/vector3.cpp // commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 // file: core/variant_call.cpp // commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +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 { @@ -20,11 +24,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 +65,7 @@ namespace Godot internal void Normalize() { - float length = this.Length(); + real_t length = Length(); if (length == 0f) { @@ -80,7 +84,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)); } @@ -99,40 +103,40 @@ namespace Godot { return new Vector3 ( - (y * b.z) - (z * b.y), - (z * b.x) - (x * b.z), - (x * b.y) - (y * b.x) + y * b.z - z * b.y, + z * b.x - x * b.z, + x * b.y - y * b.x ); } - 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; + var p0 = preA; + var p1 = this; + var p2 = b; + var 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 + + p1 * 2.0f + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3 ); } - public float 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,31 +156,31 @@ 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 ( - x + (t * (b.x - x)), - y + (t * (b.y - y)), - z + (t * (b.z - z)) + x + t * (b.x - x), + y + t * (b.y - y), + z + t * (b.z - z) ); } @@ -192,7 +196,7 @@ namespace Godot public Vector3 Normalized() { - Vector3 v = this; + var v = this; v.Normalize(); return v; } @@ -206,6 +210,11 @@ namespace Godot ); } + public Vector3 Project(Vector3 onNormal) + { + return onNormal * (Dot(onNormal) / onNormal.LengthSquared()); + } + public Vector3 Reflect(Vector3 n) { #if DEBUG @@ -215,11 +224,35 @@ namespace Godot return 2.0f * n * Dot(n) - this; } - public Vector3 Rotated(Vector3 axis, float phi) + public Vector3 Round() + { + return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z)); + } + + 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) + { + x = v.x; + y = v.y; + z = v.z; + } + + public Vector3 Slerp(Vector3 b, real_t t) + { + real_t theta = AngleTo(b); + return Rotated(Cross(b), theta * t); + } + public Vector3 Slide(Vector3 n) { return this - n * Dot(n); @@ -243,13 +276,45 @@ namespace Godot 0f, 0f, z ); } - - public Vector3(float x, float y, float z) + + // Constants + 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 _inf = new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf); + + 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 Inf { get { return _inf; } } + + 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) + { + x = v.x; + y = v.y; + z = v.z; + } public static Vector3 operator +(Vector3 left, Vector3 right) { @@ -275,7 +340,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 +348,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 +364,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; @@ -331,8 +396,7 @@ namespace Godot { if (left.y == right.y) return left.z < right.z; - else - return left.y < right.y; + return left.y < right.y; } return left.x < right.x; @@ -344,8 +408,7 @@ namespace Godot { if (left.y == right.y) return left.z > right.z; - else - return left.y > right.y; + return left.y > right.y; } return left.x > right.x; @@ -357,8 +420,7 @@ namespace Godot { if (left.y == right.y) return left.z <= right.z; - else - return left.y < right.y; + return left.y < right.y; } return left.x < right.x; @@ -370,8 +432,7 @@ namespace Godot { if (left.y == right.y) return left.z >= right.z; - else - return left.y > right.y; + return left.y > right.y; } return left.x > right.x; @@ -401,9 +462,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(), - this.y.ToString(), - this.z.ToString() + x.ToString(), + y.ToString(), + z.ToString() }); } @@ -411,9 +472,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - this.x.ToString(format), - this.y.ToString(format), - this.z.ToString(format) + x.ToString(format), + y.ToString(format), + z.ToString(format) }); } } diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h index cedc8e9992..6a6f3062b4 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -29,6 +29,7 @@ /*************************************************************************/ #include "builtin_types_glue.h" +#include "collections_glue.h" #include "../csharp_script.h" #include "../mono_gd/gd_mono_class.h" @@ -308,4 +309,5 @@ MonoObject *godot_icall_Godot_weakref(Object *p_obj) { void godot_register_header_icalls() { godot_register_builtin_type_icalls(); + godot_register_collections_icalls(); } 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_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp index 91a60fb716..9f0e933a8c 100644 --- a/modules/mono/mono_gc_handle.cpp +++ b/modules/mono/mono_gc_handle.cpp @@ -34,15 +34,12 @@ uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) { - return mono_gchandle_new( - p_object, - false /* do not pin the object */ - ); + return mono_gchandle_new(p_object, /* pinned: */ false); } uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) { - return mono_gchandle_new_weakref(p_object, false); + return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false); } Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) { diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 39474f8cbc..fadac941e9 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -42,7 +42,10 @@ #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 @@ -50,14 +53,6 @@ #include "main/main.h" #endif -void gdmono_unhandled_exception_hook(MonoObject *exc, void *user_data) { - - (void)user_data; // UNUSED - - GDMonoUtils::print_unhandled_exception(exc); - abort(); -} - #ifdef MONO_PRINT_HANDLER_ENABLED void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) { @@ -71,7 +66,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.write[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.write[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 { @@ -93,9 +112,7 @@ static bool _wait_for_debugger_msecs(uint32_t p_msecs) { return mono_is_debugger_attached(); } -#endif -#ifdef DEBUG_ENABLED void gdmono_debug_init() { mono_debug_init(MONO_DEBUG_FORMAT_MONO); @@ -122,13 +139,16 @@ void gdmono_debug_init() { }; mono_jit_parse_options(2, (char **)options); } + #endif +} // namespace + void GDMono::initialize() { ERR_FAIL_NULL(Engine::get_singleton()); - OS::get_singleton()->print("Mono: Initializing module...\n"); + print_verbose("Mono: Initializing module..."); #ifdef DEBUG_METHODS_ENABLED _initialize_and_check_api_hashes(); @@ -169,6 +189,8 @@ void GDMono::initialize() { mono_config_parse(NULL); + mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL); + root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319"); ERR_EXPLAIN("Mono: Failed to initialize runtime"); @@ -176,9 +198,11 @@ 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"); + print_verbose("Mono: Runtime initialized"); // mscorlib assembly MUST be present at initialization ERR_EXPLAIN("Mono: Failed to load mscorlib assembly"); @@ -202,24 +226,64 @@ void GDMono::initialize() { #ifdef DEBUG_ENABLED bool debugger_attached = _wait_for_debugger_msecs(500); if (!debugger_attached && OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->printerr("Mono: Debugger wait timeout\n"); + print_error("Mono: Debugger wait timeout"); #endif _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); + } - mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL); + print_line("Mono: Proceeding to unload scripts domain because of invalid API assemblies."); - OS::get_singleton()->print("Mono: INITIALIZED\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 + print_verbose("Mono: Glue disabled, ignoring script assemblies."); +#endif + + print_verbose("Mono: INITIALIZED"); } #ifndef MONO_GLUE_DISABLED 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 @@ -271,43 +335,82 @@ 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); - if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + "...\n").utf8()); - - MonoImageOpenStatus status = MONO_IMAGE_OK; MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8()); - MonoAssembly *assembly = mono_assembly_load_full(aname, NULL, &status, false); + 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); + + print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "..."); + + MonoImageOpenStatus status = MONO_IMAGE_OK; + 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()); + print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path()); 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(); @@ -317,13 +420,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; } @@ -334,7 +449,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 @@ -346,7 +477,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 @@ -360,53 +491,106 @@ 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()) + print_error("Mono: Failed to load project assembly"); + } 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"); + print_error("Mono: Failed to load Core API assembly"); return false; } else { #ifdef TOOLS_ENABLED if (!_load_editor_api_assembly()) { if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->printerr("Mono: Failed to load Editor API assembly\n"); + print_error("Mono: Failed to load Editor API assembly"); return false; } #endif } - if (!_load_project_assembly()) { - if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->printerr("Mono: Failed to load project assembly\n"); - return false; + return true; +} + +#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() { ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG); - if (OS::get_singleton()->is_stdout_verbose()) { - OS::get_singleton()->print("Mono: Loading scripts domain...\n"); - } + print_verbose("Mono: Loading scripts domain..."); scripts_domain = GDMonoUtils::create_domain("GodotEngine.ScriptsDomain"); @@ -422,9 +606,7 @@ Error GDMono::_unload_scripts_domain() { ERR_FAIL_NULL_V(scripts_domain, ERR_BUG); - if (OS::get_singleton()->is_stdout_verbose()) { - OS::get_singleton()->print("Mono: Unloading scripts domain...\n"); - } + print_verbose("Mono: Unloading scripts domain..."); _GodotSharp::get_singleton()->_dispose_callback(); @@ -441,23 +623,26 @@ 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; _GodotSharp::get_singleton()->_dispose_callback(); - MonoObject *ex = NULL; - mono_domain_try_unload(domain, &ex); + MonoException *exc = NULL; + mono_domain_try_unload(domain, (MonoObject **)&exc); - if (ex) { - ERR_PRINT("Exception thrown when unloading scripts domain:"); - mono_print_unhandled_exception(ex); + if (exc) { + ERR_PRINT("Exception thrown when unloading scripts domain"); + GDMonoUtils::debug_unhandled_exception(exc); return FAILED; } @@ -469,9 +654,7 @@ Error GDMono::_load_tools_domain() { ERR_FAIL_COND_V(tools_domain != NULL, ERR_BUG); - if (OS::get_singleton()->is_stdout_verbose()) { - OS::get_singleton()->print("Mono: Loading tools domain...\n"); - } + print_verbose("Mono: Loading tools domain..."); tools_domain = GDMonoUtils::create_domain("GodotEngine.ToolsDomain"); @@ -501,16 +684,77 @@ 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 + print_verbose("Mono: Glue disabled, ignoring script assemblies."); +#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); + + print_verbose("Mono: Unloading domain `" + domain_name + "`..."); + + 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)); + + MonoException *exc = NULL; + mono_domain_try_unload(p_domain, (MonoObject **)&exc); + + if (exc) { + ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`"); + GDMonoUtils::debug_unhandled_exception(exc); + return FAILED; + } + + return OK; +} + GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) { MonoImage *image = mono_class_get_image(p_raw_class); @@ -547,6 +791,21 @@ void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) { assemblies.erase(p_domain_id); } +void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) { + + // This method will be called by the runtime when a thrown exception is not handled. + // It won't be called when we manually treat a thrown exception as unhandled. + // We assume the exception was already printed before calling this hook. + +#ifdef DEBUG_ENABLED + GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc); + if (ScriptDebugger::get_singleton()) + ScriptDebugger::get_singleton()->idle_poll(); +#endif + abort(); + _UNREACHABLE_(); +} + GDMono::GDMono() { singleton = this; @@ -562,8 +821,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; @@ -603,7 +865,7 @@ GDMono::~GDMono() { GDMonoUtils::clear_cache(); - OS::get_singleton()->print("Mono: Runtime cleanup...\n"); + print_verbose("Mono: Runtime cleanup..."); runtime_initialized = false; mono_jit_cleanup(root_domain); diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 67251778c6..e0ec6ced5e 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,8 +157,15 @@ 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; } + static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data); + // Do not use these, unless you know what you're doing void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly); GDMonoAssembly **get_loaded_assembly(const String &p_name); @@ -128,7 +179,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 +196,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..27ce39b6d7 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -33,17 +33,51 @@ #include <mono/metadata/mono-debug.h> #include <mono/metadata/tokentype.h> -#include "list.h" -#include "os/file_access.h" -#include "os/os.h" +#include "core/list.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "../godotsharp_dirs.h" #include "gd_mono_class.h" bool GDMonoAssembly::no_search = false; +bool GDMonoAssembly::in_preload = false; + Vector<String> GDMonoAssembly::search_dirs; -MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_data) { +void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, void *user_data) { + + if (no_search) + return; + + // If our search and preload hooks fail to load the assembly themselves, the mono runtime still might. + // Just do Assembly.LoadFrom("/Full/Path/On/Disk.dll"); + // In this case, we wouldn't have the assembly known in GDMono, which causes crashes + // if any class inside the assembly is looked up by Godot. + // And causing a lookup like that is as easy as throwing an exception defined in it... + // No, we can't make the assembly load hooks smart enough because they get passed a MonoAssemblyName* only, + // not the disk path passed to say Assembly.LoadFrom(). + _wrap_mono_assembly(assembly); +} + +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 +94,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 +102,51 @@ 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) { +static _THREAD_LOCAL_(MonoImage *) image_corlib_loading = NULL; + +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) { @@ -116,15 +157,38 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse } } + { + // If we find the assembly here, we load it with `mono_assembly_load_from_full`, + // which in turn invokes load hooks before returning the MonoAssembly to us. + // One of the load hooks is `load_aot_module`. This hook can end up calling preload hooks + // again for the same assembly in certain in certain circumstances (the `do_load_image` part). + // If this is the case and we return NULL due to the no_search condition below, + // it will result in an internal crash later on. Therefore we need to return the assembly we didn't + // get yet from `mono_assembly_load_from_full`. Luckily we have the image, which already got it. + // This must be done here. If done in search hooks, it would cause `mono_assembly_load_from_full` + // to think another MonoAssembly for this assembly was already loaded, making it delete its own, + // when in fact both pointers were the same... This hooks thing is confusing. + if (image_corlib_loading) { + return mono_image_get_assembly(image_corlib_loading); + } + } + + if (no_search) + return NULL; + + no_search = true; + in_preload = true; + String name = mono_assembly_name_get_name(aname); bool has_extension = name.ends_with(".dll"); + GDMonoAssembly *res = NULL; 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; for (int i = 0; i < search_dirs.size(); i++) { const String &search_dir = search_dirs[i]; @@ -132,68 +196,92 @@ 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 NULL; + no_search = false; + in_preload = false; + + return res ? res->get_assembly() : 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::_wrap_mono_assembly(MonoAssembly *assembly) { + String name = mono_assembly_name_get_name(mono_assembly_get_name(assembly)); + + MonoImage *image = mono_assembly_get_image(assembly); + + GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, mono_image_get_filename(image))); + Error err = gdassembly->wrapper_for_image(image); + + if (err != OK) { + memdelete(gdassembly); + ERR_FAIL(); + } + + MonoDomain *domain = mono_domain_get(); + GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, gdassembly); } 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); + mono_install_assembly_load_hook(&assembly_load_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); ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ); - String image_filename(path); + String image_filename = ProjectSettings::get_singleton()->globalize_path(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,14 +301,17 @@ no_pdb: #endif - assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, false); + bool is_corlib_preload = in_preload && name == "mscorlib"; - ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN); + if (is_corlib_preload) + image_corlib_loading = image; - 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); - } + assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, refonly); + + if (is_corlib_preload) + image_corlib_loading = NULL; + + ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN); loaded = true; modified_time = last_modified_time; @@ -373,12 +464,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..2c6d367fc6 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; @@ -88,21 +89,30 @@ class GDMonoAssembly { #endif static bool no_search; + static bool in_preload; 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 void assembly_load_hook(MonoAssembly *assembly, 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); + static void _wrap_mono_assembly(MonoAssembly *assembly); 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 +125,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 66339d7ae6..4e515cde28 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -33,23 +33,35 @@ #include <mono/metadata/attrdefs.h> #include "gd_mono_assembly.h" +#include "gd_mono_marshal.h" -MonoType *GDMonoClass::get_raw_type(GDMonoClass *p_class) { +String GDMonoClass::get_full_name(MonoClass *p_mono_class) { + // mono_type_get_full_name is not exposed to embedders, but this seems to do the job + MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class)); - return mono_class_get_type(p_class->get_mono_ptr()); -} + MonoException *exc = NULL; + MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); -bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const { + return GDMonoMarshal::mono_string_to_godot(str); +} - return mono_class_is_assignable_from(mono_class, p_from->mono_class); +MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) { + return mono_class_get_type(p_mono_class); } String GDMonoClass::get_full_name() const { + return get_full_name(mono_class); +} - String res = namespace_name; - if (res.length()) - res += "."; - return res + class_name; +MonoType *GDMonoClass::get_mono_type() { + // Care, you cannot compare MonoType pointers + return get_mono_type(mono_class); +} + +bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const { + + return mono_class_is_assignable_from(mono_class, p_from->mono_class); } GDMonoClass *GDMonoClass::get_parent_class() { @@ -308,6 +320,12 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo return get_method(method); } +void *GDMonoClass::get_method_thunk(const StringName &p_name, int p_params_count) { + + GDMonoMethod *method = get_method(p_name, p_params_count); + return method ? method->get_thunk() : NULL; +} + GDMonoField *GDMonoClass::get_field(const StringName &p_name) { Map<StringName, GDMonoField *>::Element *result = fields.find(p_name); @@ -483,7 +501,7 @@ GDMonoClass::~GDMonoClass() { } } - deleted_methods[offset] = method; + deleted_methods.write[offset] = method; ++offset; memdelete(method); diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index 417c138594..f4e386549a 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -98,7 +98,11 @@ class GDMonoClass { GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly); public: - static MonoType *get_raw_type(GDMonoClass *p_class); + static String get_full_name(MonoClass *p_mono_class); + static MonoType *get_mono_type(MonoClass *p_mono_class); + + String get_full_name() const; + MonoType *get_mono_type(); bool is_assignable_from(GDMonoClass *p_from) const; @@ -108,8 +112,6 @@ public: _FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; } _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; } - String get_full_name() const; - GDMonoClass *get_parent_class(); #ifdef TOOLS_ENABLED @@ -131,6 +133,8 @@ public: GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count); GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace); + void *get_method_thunk(const StringName &p_name, int p_params_count = 0); + GDMonoField *get_field(const StringName &p_name); const Vector<GDMonoField *> &get_all_fields(); diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 3b91777ed4..d3a673dc1b 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -148,7 +148,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) SET_FROM_ARRAY_AND_BREAK(Array); @@ -200,6 +200,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + if (CACHED_CLASS(Dictionary) == type_class) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + if (CACHED_CLASS(Array) == type_class) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name()); ERR_FAIL(); } break; @@ -248,10 +260,13 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } case Variant::DICTIONARY: { - MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary()); + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + mono_field_set_value(p_object, mono_field, managed); + } break; + case Variant::ARRAY: { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); mono_field_set_value(p_object, mono_field, managed); } break; - case Variant::ARRAY: SET_FROM_ARRAY_AND_BREAK(Array); case Variant::POOL_BYTE_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolByteArray); case Variant::POOL_INT_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolIntArray); case Variant::POOL_REAL_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolRealArray); @@ -265,8 +280,28 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) { - MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary()); + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); + + MonoException *exc = NULL; + + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + exc = NULL; + + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class); mono_field_set_value(p_object, mono_field, managed); break; } diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index 2b5110f0b9..72a5439044 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -45,7 +45,8 @@ struct ManagedType { GDMonoClass *type_class; ManagedType() { - type_class = 0; + type_encoding = 0; + type_class = NULL; } }; diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index a1a79f957f..505c030ca1 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -32,8 +32,12 @@ #include "../csharp_script.h" #include "../mono_gc_handle.h" +#include "../utils/macros.h" +#include "../utils/thread_local.h" #include "gd_mono_utils.h" +#include <mono/metadata/exception.h> + namespace GDMonoInternals { void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { @@ -64,4 +68,11 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { return; } + +void unhandled_exception(MonoException *p_exc) { + mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well + abort(); + _UNREACHABLE_(); +} + } // namespace GDMonoInternals diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h index abec65e7d4..50cadcedf6 100644 --- a/modules/mono/mono_gd/gd_mono_internals.h +++ b/modules/mono/mono_gd/gd_mono_internals.h @@ -33,11 +33,20 @@ #include <mono/jit/jit.h> +#include "../utils/macros.h" + #include "core/object.h" namespace GDMonoInternals { void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged); -} + +/** + * Do not call this function directly. + * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead. + */ +_NO_RETURN_ void unhandled_exception(MonoException *p_exc); + +} // namespace GDMonoInternals #endif // GD_MONO_INTERNALS_H diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index eabea8dc3c..5224d309de 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -152,8 +152,7 @@ void GDMonoLog::initialize() { log_level_id = log_level_get_id(log_level); if (log_file) { - if (OS::get_singleton()->is_stdout_verbose()) - OS::get_singleton()->print(String("Mono: Logfile is " + log_file_path + "\n").utf8()); + print_verbose("Mono: Logfile is " + log_file_path); mono_trace_set_log_handler(gdmono_MonoLogCallback, this); } else { OS::get_singleton()->printerr("Mono: No log file, using default log handler\n"); diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index aa1a8e39c7..de91e71bab 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -120,7 +120,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) return Variant::ARRAY; @@ -162,12 +162,36 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { if (CACHED_CLASS(RID) == type_class) { return Variant::_RID; } + + if (CACHED_CLASS(Dictionary) == type_class) { + return Variant::DICTIONARY; + } + + if (CACHED_CLASS(Array) == type_class) { + return Variant::ARRAY; + } } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) { + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); + + MonoException *exc = NULL; + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { return Variant::DICTIONARY; } + + exc = NULL; + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + return Variant::ARRAY; + } } break; default: { @@ -216,6 +240,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var) { ManagedType type; type.type_encoding = MONO_TYPE_OBJECT; + // type.type_class is not needed when we specify the MONO_TYPE_OBJECT encoding return variant_to_mono_object(p_var, type); } @@ -315,7 +340,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) return (MonoObject *)Array_to_mono_array(p_var->operator Array()); @@ -360,6 +385,14 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty if (CACHED_CLASS(RID) == type_class) { return GDMonoUtils::create_managed_from(p_var->operator RID()); } + + if (CACHED_CLASS(Dictionary) == type_class) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); + } + + if (CACHED_CLASS(Array) == type_class) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); + } } break; case MONO_TYPE_OBJECT: { // Variant @@ -411,9 +444,9 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *()); } case Variant::DICTIONARY: - return Dictionary_to_mono_object(p_var->operator Dictionary()); + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); case Variant::ARRAY: - return (MonoObject *)Array_to_mono_array(p_var->operator Array()); + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); case Variant::POOL_BYTE_ARRAY: return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray()); case Variant::POOL_INT_ARRAY: @@ -433,8 +466,24 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) { - return Dictionary_to_mono_object(p_var->operator Dictionary()); + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); + + MonoException *exc = NULL; + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class); + } + + exc = NULL; + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class); } } break; } break; @@ -452,7 +501,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) { GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj)); ERR_FAIL_COND_V(!tclass, Variant()); - MonoType *raw_type = tclass->get_raw_type(tclass); + MonoType *raw_type = tclass->get_mono_type(); ManagedType type; @@ -531,7 +580,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) { case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) return mono_array_to_Array((MonoArray *)p_obj); @@ -579,11 +628,51 @@ Variant mono_object_to_variant(MonoObject *p_obj) { RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj)); return ptr ? Variant(*ptr) : Variant(); } + + if (CACHED_CLASS(Array) == type_class) { + MonoException *exc = NULL; + GDMonoUtils::Array_GetPtr get_ptr = CACHED_METHOD_THUNK(Array, GetPtr); + Array *ptr = get_ptr(p_obj, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return ptr ? Variant(*ptr) : Variant(); + } + + if (CACHED_CLASS(Dictionary) == type_class) { + MonoException *exc = NULL; + GDMonoUtils::Dictionary_GetPtr get_ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr); + Dictionary *ptr = get_ptr(p_obj, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return ptr ? Variant(*ptr) : Variant(); + } } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) { - return mono_object_to_Dictionary(p_obj); + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); + + MonoException *exc = NULL; + + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { + MonoException *exc = NULL; + MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return *unbox<Dictionary *>(ret); + } + + exc = NULL; + + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + MonoException *exc = NULL; + MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return *unbox<Array *>(ret); } } break; } @@ -606,11 +695,14 @@ MonoArray *Array_to_mono_array(const Array &p_array) { Array mono_array_to_Array(MonoArray *p_array) { Array ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); + ret.resize(length); for (int i = 0; i < length; i++) { MonoObject *elem = mono_array_get(p_array, MonoObject *, i); - ret.push_back(mono_object_to_variant(elem)); + ret[i] = mono_object_to_variant(elem); } return ret; @@ -630,11 +722,13 @@ MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array) { PoolIntArray mono_array_to_PoolIntArray(MonoArray *p_array) { PoolIntArray ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); - + ret.resize(length); for (int i = 0; i < length; i++) { int32_t elem = mono_array_get(p_array, int32_t, i); - ret.push_back(elem); + ret.set(i, elem); } return ret; @@ -652,11 +746,14 @@ MonoArray *PoolByteArray_to_mono_array(const PoolByteArray &p_array) { PoolByteArray mono_array_to_PoolByteArray(MonoArray *p_array) { PoolByteArray ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); + ret.resize(length); for (int i = 0; i < length; i++) { uint8_t elem = mono_array_get(p_array, uint8_t, i); - ret.push_back(elem); + ret.set(i, elem); } return ret; @@ -674,11 +771,14 @@ MonoArray *PoolRealArray_to_mono_array(const PoolRealArray &p_array) { PoolRealArray mono_array_to_PoolRealArray(MonoArray *p_array) { PoolRealArray ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); + ret.resize(length); for (int i = 0; i < length; i++) { real_t elem = mono_array_get(p_array, real_t, i); - ret.push_back(elem); + ret.set(i, elem); } return ret; @@ -697,11 +797,14 @@ MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) { PoolStringArray mono_array_to_PoolStringArray(MonoArray *p_array) { PoolStringArray ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); + ret.resize(length); for (int i = 0; i < length; i++) { MonoString *elem = mono_array_get(p_array, MonoString *, i); - ret.push_back(mono_string_to_godot(elem)); + ret.set(i, mono_string_to_godot(elem)); } return ret; @@ -728,12 +831,15 @@ MonoArray *PoolColorArray_to_mono_array(const PoolColorArray &p_array) { PoolColorArray mono_array_to_PoolColorArray(MonoArray *p_array) { PoolColorArray ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); + ret.resize(length); for (int i = 0; i < length; i++) { real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 4, i); MARSHALLED_IN(Color, raw_elem, elem); - ret.push_back(elem); + ret.set(i, elem); } return ret; @@ -758,12 +864,15 @@ MonoArray *PoolVector2Array_to_mono_array(const PoolVector2Array &p_array) { PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array) { PoolVector2Array ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); + ret.resize(length); for (int i = 0; i < length; i++) { real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 2, i); MARSHALLED_IN(Vector2, raw_elem, elem); - ret.push_back(elem); + ret.set(i, elem); } return ret; @@ -789,69 +898,17 @@ MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array) { PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) { PoolVector3Array ret; + if (!p_array) + return ret; int length = mono_array_length(p_array); + ret.resize(length); for (int i = 0; i < length; i++) { real_t *raw_elem = (real_t *)mono_array_addr_with_size(p_array, sizeof(real_t) * 3, i); MARSHALLED_IN(Vector3, raw_elem, elem); - ret.push_back(elem); - } - - return ret; -} - -MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) { - MonoArray *keys = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size()); - MonoArray *values = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size()); - - int i = 0; - const Variant *dkey = NULL; - while ((dkey = p_dict.next(dkey))) { - mono_array_set(keys, MonoObject *, i, variant_to_mono_object(dkey)); - mono_array_set(values, MonoObject *, i, variant_to_mono_object(p_dict[*dkey])); - i++; - } - - GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary); - - MonoObject *ex = NULL; - MonoObject *ret = arrays_to_dict(keys, values, &ex); - - if (ex) { - mono_print_unhandled_exception(ex); - ERR_FAIL_V(NULL); + ret.set(i, elem); } return ret; } - -Dictionary mono_object_to_Dictionary(MonoObject *p_dict) { - Dictionary ret; - - GDMonoUtils::MarshalUtils_DictToArrays dict_to_arrays = CACHED_METHOD_THUNK(MarshalUtils, DictionaryToArrays); - - MonoArray *keys = NULL; - MonoArray *values = NULL; - MonoObject *ex = NULL; - dict_to_arrays(p_dict, &keys, &values, &ex); - - if (ex) { - mono_print_unhandled_exception(ex); - ERR_FAIL_V(Dictionary()); - } - - int length = mono_array_length(keys); - - for (int i = 0; i < length; i++) { - MonoObject *key_obj = mono_array_get(keys, MonoObject *, i); - MonoObject *value_obj = mono_array_get(values, MonoObject *, i); - - Variant key = key_obj ? mono_object_to_variant(key_obj) : Variant(); - Variant value = value_obj ? mono_object_to_variant(value_obj) : Variant(); - - ret[key] = value; - } - - return ret; -} -} +} // namespace GDMonoMarshal diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 8fd437223f..464f584a0a 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -143,11 +143,6 @@ PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array); MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array); PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array); -// Dictionary - -MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict); -Dictionary mono_object_to_Dictionary(MonoObject *p_dict); - #ifdef YOLO_COPY #define MARSHALLED_OUT(m_t, m_in, m_out) m_t *m_out = (m_t *)&m_in; #define MARSHALLED_IN(m_t, m_in, m_out) m_t m_out = *reinterpret_cast<m_t *>(m_in); @@ -195,9 +190,9 @@ Dictionary mono_object_to_Dictionary(MonoObject *p_dict); // Transform #define MARSHALLED_OUT_Transform(m_in, m_out) real_t m_out[12] = { \ - m_in.basis[0].x, m_in.basis[1].x, m_in.basis[2].x, \ - m_in.basis[0].y, m_in.basis[1].y, m_in.basis[2].y, \ - m_in.basis[0].z, m_in.basis[1].z, m_in.basis[2].z, \ + m_in.basis[0].x, m_in.basis[0].y, m_in.basis[0].z, \ + m_in.basis[1].x, m_in.basis[1].y, m_in.basis[1].z, \ + m_in.basis[2].x, m_in.basis[2].y, m_in.basis[2].z, \ m_in.origin.x, m_in.origin.y, m_in.origin.z \ }; #define MARSHALLED_IN_Transform(m_in, m_out) Transform m_out( \ diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index ad52904945..630bda8b4e 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -95,7 +95,7 @@ void *GDMonoMethod::get_thunk() { return mono_method_get_unmanaged_thunk(mono_method); } -MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc) { +MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) { if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) { MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count()); @@ -104,28 +104,28 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, mono_array_set(params, MonoObject *, i, boxed_param); } - MonoObject *exc = NULL; - MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, &exc); + MonoException *exc = NULL; + MonoObject *ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc); if (exc) { ret = NULL; if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } return ret; } else { - MonoObject *exc = NULL; - mono_runtime_invoke(mono_method, p_object, NULL, &exc); + MonoException *exc = NULL; + GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc); if (exc) { if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } @@ -133,21 +133,21 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, } } -MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoObject **r_exc) { +MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) { ERR_FAIL_COND_V(get_parameters_count() > 0, NULL); return invoke_raw(p_object, NULL, r_exc); } -MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc) { - MonoObject *exc = NULL; - MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, &exc); +MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) { + MonoException *exc = NULL; + MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc); if (exc) { ret = NULL; if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index a173af83f4..444ec2a67d 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -71,9 +71,9 @@ public: void *get_thunk(); - MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc = NULL); - MonoObject *invoke(MonoObject *p_object, MonoObject **r_exc = NULL); - MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL); + MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL); + MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL); + MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL); String get_full_name(bool p_signature = false) const; String get_full_name_no_class() const; diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index 0fe527b199..ce66e0c8db 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -138,47 +138,44 @@ bool GDMonoProperty::has_setter() { return mono_property_get_set_method(mono_property) != NULL; } -void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc) { +void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) { MonoMethod *prop_method = mono_property_get_set_method(mono_property); - MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); mono_array_set(params, MonoObject *, 0, p_value); - - MonoObject *exc = NULL; - mono_runtime_invoke_array(prop_method, p_object, params, &exc); - + MonoException *exc = NULL; + GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc); if (exc) { if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } } -void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc) { - MonoObject *exc = NULL; - mono_property_set_value(mono_property, p_object, p_params, &exc); +void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) { + MonoException *exc = NULL; + GDMonoUtils::property_set_value(mono_property, p_object, p_params, &exc); if (exc) { if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } } -MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoObject **r_exc) { - MonoObject *exc = NULL; - MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, &exc); +MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) { + MonoException *exc = NULL; + MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, NULL, &exc); if (exc) { ret = NULL; if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index 2a0065e850..b3f1e2114a 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -62,9 +62,9 @@ public: _FORCE_INLINE_ ManagedType get_type() const { return type; } - void set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc = NULL); - void set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL); - MonoObject *get_value(MonoObject *p_object, MonoObject **r_exc = NULL); + void set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc = NULL); + void set_value(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL); + MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = NULL); bool get_bool_value(MonoObject *p_object); int get_int_value(MonoObject *p_object); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 42e307cf08..bebc3b863f 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -30,12 +30,15 @@ #include "gd_mono_utils.h" +#include <mono/metadata/exception.h> + #include "os/dir_access.h" #include "os/os.h" #include "project_settings.h" #include "reference.h" #include "../csharp_script.h" +#include "../utils/macros.h" #include "gd_mono.h" #include "gd_mono_class.h" #include "gd_mono_marshal.h" @@ -84,6 +87,8 @@ void MonoCache::clear_members() { method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL; #endif + class_KeyNotFoundException = NULL; + rawclass_Dictionary = NULL; class_Vector2 = NULL; @@ -104,6 +109,8 @@ void MonoCache::clear_members() { class_Control = NULL; class_Spatial = NULL; class_WeakRef = NULL; + class_Array = NULL; + class_Dictionary = NULL; class_MarshalUtils = NULL; #ifdef DEBUG_ENABLED @@ -120,6 +127,9 @@ void MonoCache::clear_members() { class_SyncAttribute = NULL; class_MasterAttribute = NULL; class_SlaveAttribute = NULL; + class_RemoteSyncAttribute = NULL; + class_MasterSyncAttribute = NULL; + class_SlaveSyncAttribute = NULL; class_GodotMethodAttribute = NULL; field_GodotMethodAttribute_methodName = NULL; @@ -128,8 +138,10 @@ void MonoCache::clear_members() { field_Image_ptr = NULL; field_RID_ptr = NULL; - methodthunk_MarshalUtils_DictionaryToArrays = NULL; - methodthunk_MarshalUtils_ArraysToDictionary = NULL; + methodthunk_Array_GetPtr = NULL; + methodthunk_Dictionary_GetPtr = NULL; + methodthunk_MarshalUtils_IsArrayGenericType = NULL; + methodthunk_MarshalUtils_IsDictionaryGenericType = NULL; methodthunk_SignalAwaiter_SignalCallback = NULL; methodthunk_SignalAwaiter_FailureCallback = NULL; methodthunk_GodotTaskScheduler_Activate = NULL; @@ -143,7 +155,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() { @@ -164,11 +176,13 @@ void update_corlib_cache() { #ifdef DEBUG_ENABLED CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace")); - CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method("GetFrames")->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_thunk("GetFrames")); CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true)); CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true)); #endif + CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException")); + mono_cache.corlib_cache_updated = true; } @@ -192,6 +206,8 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control)); CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial)); CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef)); + CACHE_CLASS_AND_CHECK(Array, GODOT_API_CLASS(Array)); + CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_CLASS(Dictionary)); CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); #ifdef DEBUG_ENABLED @@ -208,6 +224,9 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(SyncAttribute, GODOT_API_CLASS(SyncAttribute)); CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute)); CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute)); + CACHE_CLASS_AND_CHECK(RemoteSyncAttribute, GODOT_API_CLASS(RemoteSyncAttribute)); + CACHE_CLASS_AND_CHECK(MasterSyncAttribute, GODOT_API_CLASS(MasterSyncAttribute)); + CACHE_CLASS_AND_CHECK(SlaveSyncAttribute, GODOT_API_CLASS(SlaveSyncAttribute)); CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute)); CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); @@ -215,37 +234,24 @@ void update_godot_api_cache() { CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD)); CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryToArrays, (MarshalUtils_DictToArrays)CACHED_CLASS(MarshalUtils)->get_method("DictionaryToArrays", 3)->get_thunk()); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArraysToDictionary, (MarshalUtils_ArraysToDict)CACHED_CLASS(MarshalUtils)->get_method("ArraysToDictionary", 2)->get_thunk()); - CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)->get_thunk()); - CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk()); - CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_CLASS(Array)->get_method_thunk("GetPtr", 0)); + CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_CLASS(Dictionary)->get_method_thunk("GetPtr", 0)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsArrayGenericType, (IsArrayGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsArrayGenericType", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsDictionaryGenericType, (IsDictionaryGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsDictionaryGenericType", 1)); + CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("SignalCallback", 1)); + CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0)); + CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0)); #ifdef DEBUG_ENABLED - CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4)); #endif - { - /* - * TODO Right now we only support Dictionary<object, object>. - * It would be great if we could support other key/value types - * without forcing the user to copy the entries. - */ - GDMonoMethod *method_get_dict_type = CACHED_CLASS(MarshalUtils)->get_method("GetDictionaryType", 0); - ERR_FAIL_NULL(method_get_dict_type); - MonoReflectionType *dict_refl_type = (MonoReflectionType *)method_get_dict_type->invoke(NULL); - ERR_FAIL_NULL(dict_refl_type); - MonoType *dict_type = mono_reflection_type_get_type(dict_refl_type); - ERR_FAIL_NULL(dict_type); - - CACHE_RAW_MONO_CLASS_AND_CHECK(Dictionary, mono_class_from_mono_type(dict_type)); - } - + // TODO Move to CSharpLanguage::init() MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); - mono_runtime_object_init(task_scheduler); + GDMonoUtils::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() { @@ -295,6 +301,12 @@ MonoThread *get_current_thread() { return mono_thread_current(); } +void runtime_object_init(MonoObject *p_this_obj) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_runtime_object_init(p_this_obj); + GD_MONO_END_RUNTIME_INVOKE; +} + GDMonoClass *get_object_class(MonoObject *p_object) { return GDMono::get_singleton()->get_class(mono_object_get_class(p_object)); } @@ -305,7 +317,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) { @@ -321,7 +333,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()) @@ -349,7 +361,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object); // Construct - mono_runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object); return mono_object; } @@ -359,7 +371,7 @@ MonoObject *create_managed_from(const NodePath &p_from) { ERR_FAIL_NULL_V(mono_object, NULL); // Construct - mono_runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object); CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from))); @@ -371,13 +383,73 @@ MonoObject *create_managed_from(const RID &p_from) { ERR_FAIL_NULL_V(mono_object, NULL); // Construct - mono_runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object); CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from))); return mono_object; } +MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) { + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + ERR_FAIL_NULL_V(mono_object, NULL); + + // Search constructor that takes a pointer as parameter + MonoMethod *m; + void *iter = NULL; + while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) { + if (strcmp(mono_method_get_name(m), ".ctor") == 0) { + MonoMethodSignature *sig = mono_method_signature(m); + void *front = NULL; + if (mono_signature_get_param_count(sig) == 1 && + mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) { + break; + } + } + } + + CRASH_COND(m == NULL); + + Array *new_array = memnew(Array(p_from)); + void *args[1] = { &new_array }; + + MonoException *exc = NULL; + GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + return mono_object; +} + +MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) { + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + ERR_FAIL_NULL_V(mono_object, NULL); + + // Search constructor that takes a pointer as parameter + MonoMethod *m; + void *iter = NULL; + while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) { + if (strcmp(mono_method_get_name(m), ".ctor") == 0) { + MonoMethodSignature *sig = mono_method_signature(m); + void *front = NULL; + if (mono_signature_get_param_count(sig) == 1 && + mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) { + break; + } + } + } + + CRASH_COND(m == NULL); + + Dictionary *new_dict = memnew(Dictionary(p_from)); + void *args[1] = { &new_dict }; + + MonoException *exc = NULL; + GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + return mono_object; +} + MonoDomain *create_domain(const String &p_friendly_name) { MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL); @@ -391,10 +463,10 @@ MonoDomain *create_domain(const String &p_friendly_name) { return domain; } -String get_exception_name_and_message(MonoObject *p_ex) { +String get_exception_name_and_message(MonoException *p_exc) { String res; - MonoClass *klass = mono_object_get_class(p_ex); + MonoClass *klass = mono_object_get_class((MonoObject *)p_exc); MonoType *type = mono_class_get_type(klass); char *full_name = mono_type_full_name(type); @@ -404,56 +476,150 @@ String get_exception_name_and_message(MonoObject *p_ex) { res += ": "; MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); - MonoString *msg = (MonoString *)mono_property_get_value(prop, p_ex, NULL, NULL); + MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, NULL, NULL); res += GDMonoMarshal::mono_string_to_godot(msg); return res; } -void print_unhandled_exception(MonoObject *p_exc) { - print_unhandled_exception(p_exc, false); +void set_exception_message(MonoException *p_exc, String message) { + MonoClass *klass = mono_object_get_class((MonoObject *)p_exc); + MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); + MonoString *msg = GDMonoMarshal::mono_string_from_godot(message); + void *params[1] = { msg }; + property_set_value(prop, (MonoObject *)p_exc, params, NULL); } -void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) { - mono_print_unhandled_exception(p_exc); +void debug_print_unhandled_exception(MonoException *p_exc) { + print_unhandled_exception(p_exc); + debug_send_unhandled_exception_error(p_exc); +} + +void debug_send_unhandled_exception_error(MonoException *p_exc) { #ifdef DEBUG_ENABLED 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()); + _TLS_RECURSION_GUARD_; + + ScriptLanguage::StackInfo separator; + separator.file = String(); + separator.func = "--- " + RTR("End of inner exception stack trace") + " ---"; + separator.line = 0; - MonoBoolean need_file_info = true; - void *ctor_args[2] = { p_exc, &need_file_info }; + Vector<ScriptLanguage::StackInfo> si; + String exc_msg; - MonoObject *unexpected_exc = NULL; - CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc); + 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()); - if (unexpected_exc != NULL) { - mono_print_unhandled_exception(unexpected_exc); + MonoBoolean need_file_info = true; + void *ctor_args[2] = { p_exc, &need_file_info }; - 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(); + MonoException *unexpected_exc = NULL; + CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc); + + if (unexpected_exc) { + GDMonoInternals::unhandled_exception(unexpected_exc); + _UNREACHABLE_(); } - } - 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) { + _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); + + GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class()); + GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException"); + CRASH_COND(inner_exc_prop == NULL); + + MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc); + if (inner_exc != NULL) + si.insert(0, separator); + + p_exc = (MonoException *)inner_exc; + } 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 } +void debug_unhandled_exception(MonoException *p_exc) { +#ifdef DEBUG_ENABLED + GDMonoUtils::debug_send_unhandled_exception_error(p_exc); + if (ScriptDebugger::get_singleton()) + ScriptDebugger::get_singleton()->idle_poll(); +#endif + GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well + _UNREACHABLE_(); +} + +void print_unhandled_exception(MonoException *p_exc) { + mono_print_unhandled_exception((MonoObject *)p_exc); +} + +void set_pending_exception(MonoException *p_exc) { +#ifdef HAS_PENDING_EXCEPTIONS + if (get_runtime_invoke_count() == 0) { + debug_unhandled_exception(p_exc); + _UNREACHABLE_(); + } + + if (!mono_runtime_set_pending_exception(p_exc, false)) { + ERR_PRINTS("Exception thrown from managed code, but it could not be set as pending:"); + GDMonoUtils::debug_print_unhandled_exception(p_exc); + } +#else + debug_unhandled_exception(p_exc); + _UNREACHABLE_(); +#endif +} + +_THREAD_LOCAL_(int) +current_invoke_count = 0; + +MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; +} + +MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 1a34180d15..bf8860c85a 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -34,19 +34,31 @@ #include <mono/metadata/threads.h> #include "../mono_gc_handle.h" +#include "../utils/macros.h" +#include "../utils/thread_local.h" #include "gd_mono_header.h" #include "object.h" #include "reference.h" +#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \ + if (unlikely(m_exc != NULL)) { \ + GDMonoUtils::debug_unhandled_exception(m_exc); \ + _UNREACHABLE_(); \ + } + namespace GDMonoUtils { -typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **); -typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **); +typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **); +typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **); typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **); typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **); typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **); typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **); typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **); struct MonoCache { @@ -77,6 +89,8 @@ struct MonoCache { GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool; #endif + GDMonoClass *class_KeyNotFoundException; + MonoClass *rawclass_Dictionary; // ----------------------------------------------- @@ -98,6 +112,8 @@ struct MonoCache { GDMonoClass *class_Control; GDMonoClass *class_Spatial; GDMonoClass *class_WeakRef; + GDMonoClass *class_Array; + GDMonoClass *class_Dictionary; GDMonoClass *class_MarshalUtils; #ifdef DEBUG_ENABLED @@ -112,6 +128,9 @@ struct MonoCache { GDMonoClass *class_ToolAttribute; GDMonoClass *class_RemoteAttribute; GDMonoClass *class_SyncAttribute; + GDMonoClass *class_RemoteSyncAttribute; + GDMonoClass *class_MasterSyncAttribute; + GDMonoClass *class_SlaveSyncAttribute; GDMonoClass *class_MasterAttribute; GDMonoClass *class_SlaveAttribute; GDMonoClass *class_GodotMethodAttribute; @@ -122,8 +141,10 @@ struct MonoCache { GDMonoField *field_Image_ptr; GDMonoField *field_RID_ptr; - MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays; - MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary; + Array_GetPtr methodthunk_Array_GetPtr; + Dictionary_GetPtr methodthunk_Dictionary_GetPtr; + IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType; + IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType; SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback; SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback; GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate; @@ -170,6 +191,8 @@ _FORCE_INLINE_ bool is_main_thread() { return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current(); } +void runtime_object_init(MonoObject *p_this_obj); + GDMonoClass *get_object_class(MonoObject *p_object); GDMonoClass *type_get_proxy_class(const StringName &p_type); GDMonoClass *get_class_native_base(GDMonoClass *p_class); @@ -178,13 +201,42 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa MonoObject *create_managed_from(const NodePath &p_from); MonoObject *create_managed_from(const RID &p_from); +MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class); +MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class); MonoDomain *create_domain(const String &p_friendly_name); -String get_exception_name_and_message(MonoObject *p_ex); +String get_exception_name_and_message(MonoException *p_exc); +void set_exception_message(MonoException *p_exc, String message); + +void debug_print_unhandled_exception(MonoException *p_exc); +void debug_send_unhandled_exception_error(MonoException *p_exc); +_NO_RETURN_ void debug_unhandled_exception(MonoException *p_exc); +void print_unhandled_exception(MonoException *p_exc); + +/** + * Sets the exception as pending. The exception will be thrown when returning to managed code. + * If no managed method is being invoked by the runtime, the exception will be treated as + * an unhandled exception and the method will not return. + */ +void set_pending_exception(MonoException *p_exc); + +extern _THREAD_LOCAL_(int) current_invoke_count; + +_FORCE_INLINE_ int get_runtime_invoke_count() { + return current_invoke_count; +} +_FORCE_INLINE_ int &get_runtime_invoke_count_ref() { + return current_invoke_count; +} + +MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc); +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc); + +MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc); -void print_unhandled_exception(MonoObject *p_exc); -void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution); +void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc); +MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc); } // namespace GDMonoUtils @@ -203,4 +255,11 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution); #define REAL_T_MONOCLASS CACHED_CLASS_RAW(float) #endif +#define GD_MONO_BEGIN_RUNTIME_INVOKE \ + int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \ + _runtime_invoke_count_ref += 1; + +#define GD_MONO_END_RUNTIME_INVOKE \ + _runtime_invoke_count_ref -= 1; + #endif // GD_MONOUTILS_H diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/mono_reg_utils.py index 8ddddb3a24..c8ebb54ded 100644 --- a/modules/mono/mono_reg_utils.py +++ b/modules/mono/mono_reg_utils.py @@ -60,10 +60,10 @@ def _find_mono_in_reg_old(subkey, bits): def find_mono_root_dir(bits): root_dir = _find_mono_in_reg(r'SOFTWARE\Mono', bits) if root_dir is not None: - return root_dir + return str(root_dir) root_dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono', bits) if root_dir is not None: - return root_dir + return str(root_dir) return '' @@ -75,7 +75,7 @@ def find_msbuild_tools_path_reg(): vswhere = os.getenv('PROGRAMFILES') vswhere += r'\Microsoft Visual Studio\Installer\vswhere.exe' - vswhere_args = ['-latest', '-requires', 'Microsoft.Component.MSBuild'] + vswhere_args = ['-latest', '-products', '*', '-requires', 'Microsoft.Component.MSBuild'] try: lines = subprocess.check_output([vswhere] + vswhere_args).splitlines() diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index 4a7f574bdc..add1e506ea 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -100,11 +100,13 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback); - MonoObject *ex = NULL; - thunk(get_target(), signal_args, &ex); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + thunk(get_target(), signal_args, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::set_pending_exception(exc); ERR_FAIL_V(Variant()); } @@ -132,11 +134,13 @@ SignalAwaiterHandle::~SignalAwaiterHandle() { MonoObject *awaiter = get_target(); if (awaiter) { - MonoObject *ex = NULL; - thunk(awaiter, &ex); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + thunk(awaiter, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::set_pending_exception(exc); ERR_FAIL_V(); } } diff --git a/modules/mono/tls_configure.py b/modules/mono/tls_configure.py new file mode 100644 index 0000000000..622280b00b --- /dev/null +++ b/modules/mono/tls_configure.py @@ -0,0 +1,36 @@ +from __future__ import print_function + +def supported(result): + return 'supported' if result else 'not supported' + + +def check_cxx11_thread_local(conf): + print('Checking for `thread_local` support...', end=" ") + result = conf.TryCompile('thread_local int foo = 0; int main() { return foo; }', '.cpp') + print(supported(result)) + return bool(result) + + +def check_declspec_thread(conf): + print('Checking for `__declspec(thread)` support...', end=" ") + result = conf.TryCompile('__declspec(thread) int foo = 0; int main() { return foo; }', '.cpp') + print(supported(result)) + return bool(result) + + +def check_gcc___thread(conf): + print('Checking for `__thread` support...', end=" ") + result = conf.TryCompile('__thread int foo = 0; int main() { return foo; }', '.cpp') + print(supported(result)) + return bool(result) + + +def configure(conf): + if check_cxx11_thread_local(conf): + conf.env.Append(CPPDEFINES=['HAVE_CXX11_THREAD_LOCAL']) + else: + if conf.env.msvc: + if check_declspec_thread(conf): + conf.env.Append(CPPDEFINES=['HAVE_DECLSPEC_THREAD']) + elif check_gcc___thread(conf): + conf.env.Append(CPPDEFINES=['HAVE_GCC___THREAD']) diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h new file mode 100644 index 0000000000..337a86870e --- /dev/null +++ b/modules/mono/utils/macros.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* util_macros.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 UTIL_MACROS_H +#define UTIL_MACROS_H + +// noreturn + +#undef _NO_RETURN_ + +#ifdef __GNUC__ +#define _NO_RETURN_ __attribute__((noreturn)) +#elif _MSC_VER +#define _NO_RETURN_ __declspec(noreturn) +#else +#error Platform or compiler not supported +#endif + +// unreachable + +#if defined(_MSC_VER) +#define _UNREACHABLE_() __assume(0) +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 +#define _UNREACHABLE_() __builtin_unreachable() +#else +#define _UNREACHABLE_() \ + CRASH_NOW(); \ + do { \ + } while (true); +#endif + +#endif // UTIL_MACROS_H diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index 9bb8da8ac0..7b23cd7579 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -174,6 +174,8 @@ String find_msbuild_tools_path() { List<String> vswhere_args; vswhere_args.push_back("-latest"); + vswhere_args.push_back("-products"); + vswhere_args.push_back("*"); vswhere_args.push_back("-requires"); vswhere_args.push_back("Microsoft.Component.MSBuild"); diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/utils/thread_local.cpp new file mode 100644 index 0000000000..a0e28fca5f --- /dev/null +++ b/modules/mono/utils/thread_local.cpp @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* thread_local.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 "thread_local.h" + +#ifdef WINDOWS_ENABLED +#include <windows.h> +#else +#include <pthread.h> +#endif + +#include "core/os/memory.h" +#include "core/print_string.h" + +struct ThreadLocalStorage::Impl { + +#ifdef WINDOWS_ENABLED + DWORD dwFlsIndex; +#else + pthread_key_t key; +#endif + + void *get_value() const { +#ifdef WINDOWS_ENABLED + return FlsGetValue(dwFlsIndex); +#else + return pthread_getspecific(key); +#endif + } + + void set_value(void *p_value) const { +#ifdef WINDOWS_ENABLED + FlsSetValue(dwFlsIndex, p_value); +#else + pthread_setspecific(key, p_value); +#endif + } + +#ifdef WINDOWS_ENABLED +#define _CALLBACK_FUNC_ __stdcall +#else +#define _CALLBACK_FUNC_ +#endif + + Impl(void(_CALLBACK_FUNC_ *p_destr_callback_func)(void *)) { +#ifdef WINDOWS_ENABLED + dwFlsIndex = FlsAlloc(p_destr_callback_func); + ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES); +#else + pthread_key_create(&key, p_destr_callback_func); +#endif + } + + ~Impl() { +#ifdef WINDOWS_ENABLED + FlsFree(dwFlsIndex); +#else + pthread_key_delete(key); +#endif + } +}; + +void *ThreadLocalStorage::get_value() const { + return pimpl->get_value(); +} + +void ThreadLocalStorage::set_value(void *p_value) const { + pimpl->set_value(p_value); +} + +void ThreadLocalStorage::alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *)) { + pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback)); +} + +#undef _CALLBACK_FUNC_ + +void ThreadLocalStorage::free() { + memdelete(pimpl); + pimpl = NULL; +} diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h new file mode 100644 index 0000000000..84dae1d86b --- /dev/null +++ b/modules/mono/utils/thread_local.h @@ -0,0 +1,171 @@ +/*************************************************************************/ +/* thread_local.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 THREAD_LOCAL_H +#define THREAD_LOCAL_H + +#ifdef HAVE_CXX11_THREAD_LOCAL +#define _THREAD_LOCAL_(m_t) thread_local m_t +#else + +#if !defined(__GNUC__) && !defined(_MSC_VER) +#error Platform or compiler not supported +#endif + +#ifdef __GNUC__ + +#ifdef HAVE_GCC___THREAD +#define _THREAD_LOCAL_(m_t) __thread m_t +#else +#define USE_CUSTOM_THREAD_LOCAL +#endif + +#elif _MSC_VER + +#ifdef HAVE_DECLSPEC_THREAD +#define _THREAD_LOCAL_(m_t) __declspec(thread) m_t +#else +#define USE_CUSTOM_THREAD_LOCAL +#endif + +#endif // __GNUC__ _MSC_VER + +#endif // HAVE_CXX11_THREAD_LOCAL + +#ifdef USE_CUSTOM_THREAD_LOCAL +#define _THREAD_LOCAL_(m_t) ThreadLocal<m_t> +#endif + +#include "core/typedefs.h" + +#ifdef WINDOWS_ENABLED +#define _CALLBACK_FUNC_ __stdcall +#else +#define _CALLBACK_FUNC_ +#endif + +struct ThreadLocalStorage { + + void *get_value() const; + void set_value(void *p_value) const; + + void alloc(void(_CALLBACK_FUNC_ *p_dest_callback)(void *)); + void free(); + +private: + struct Impl; + Impl *pimpl; +}; + +template <typename T> +class ThreadLocal { + + ThreadLocalStorage storage; + + T init_val; + + static void _CALLBACK_FUNC_ destr_callback(void *tls_data) { + memdelete(static_cast<T *>(tls_data)); + } + + T *_tls_get_value() const { + void *tls_data = storage.get_value(); + + if (tls_data) + return static_cast<T *>(tls_data); + + T *data = memnew(T(init_val)); + + storage.set_value(data); + + return data; + } + +public: + ThreadLocal() : + ThreadLocal(T()) {} + + ThreadLocal(const T &p_init_val) : + init_val(p_init_val) { + storage.alloc(&destr_callback); + } + + ThreadLocal(const ThreadLocal &other) : + ThreadLocal(*other._tls_get_value()) {} + + ~ThreadLocal() { + storage.free(); + } + + _FORCE_INLINE_ T *operator&() const { + return _tls_get_value(); + } + + _FORCE_INLINE_ operator T &() const { + return *_tls_get_value(); + } + + _FORCE_INLINE_ ThreadLocal &operator=(const T &val) { + T *ptr = _tls_get_value(); + *ptr = val; + return *this; + } +}; + +struct FlagScopeGuard { + + FlagScopeGuard(bool &p_flag) : + flag(p_flag) { + flag = !flag; + } + + ~FlagScopeGuard() { + flag = !flag; + } + +private: + bool &flag; +}; + +#undef _CALLBACK_FUNC_ + +#define _TLS_RECURSION_GUARD_V_(m_ret) \ + static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \ + if (_recursion_flag_) \ + return m_ret; \ + FlagScopeGuard _recursion_guard_(_recursion_flag_); + +#define _TLS_RECURSION_GUARD_ \ + static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \ + if (_recursion_flag_) \ + return; \ + FlagScopeGuard _recursion_guard_(_recursion_flag_); + +#endif // THREAD_LOCAL_H |