Added mono module

This commit is contained in:
Ignacio Etcheverry 2017-10-02 23:24:00 +02:00
parent 29b44801b2
commit e36fb95c50
92 changed files with 19622 additions and 0 deletions

120
modules/mono/SCsub Normal file
View file

@ -0,0 +1,120 @@
#!/usr/bin/env python
Import('env')
def make_cs_files_header(src, dst):
with open(dst, 'wb') as header:
header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n')
header.write('#ifndef _CS_FILES_DATA_H\n')
header.write('#define _CS_FILES_DATA_H\n\n')
header.write('#include "map.h"\n')
header.write('#include "ustring.h"\n')
inserted_files = ''
import os
for file in os.listdir(src):
if file.endswith('.cs'):
with open(os.path.join(src, file), 'rb') as f:
buf = f.read()
decomp_size = len(buf)
import zlib
buf = zlib.compress(buf)
name = os.path.splitext(file)[0]
header.write('\nstatic const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n')
header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n')
header.write('static const unsigned char _cs_' + name + '_compressed[] = { ')
for i, buf_idx in enumerate(range(len(buf))):
if i > 0:
header.write(', ')
header.write(str(ord(buf[buf_idx])))
inserted_files += '\tr_files.insert(\"' + file + '\", ' \
'CompressedFile(_cs_' + name + '_compressed_size, ' \
'_cs_' + name + '_uncompressed_size, ' \
'_cs_' + name + '_compressed));\n'
header.write(' };\n')
header.write('\nstruct CompressedFile\n' '{\n'
'\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
'\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
'\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n'
'\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n'
'\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n'
)
header.write('#endif // _CS_FILES_DATA_H')
env.add_source_files(env.modules_sources, '*.cpp')
env.add_source_files(env.modules_sources, 'mono_gd/*.cpp')
env.add_source_files(env.modules_sources, 'utils/*.cpp')
if env['tools']:
env.add_source_files(env.modules_sources, 'editor/*.cpp')
make_cs_files_header('glue/cs_files', 'glue/cs_compressed.gen.h')
vars = Variables()
vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True))
vars.Update(env)
# Glue sources
if env['mono_glue']:
env.add_source_files(env.modules_sources, 'glue/*.cpp')
else:
env.Append(CPPDEFINES = [ 'MONO_GLUE_DISABLED' ])
if ARGUMENTS.get('yolo_copy', False):
env.Append(CPPDEFINES = [ 'YOLO_COPY' ])
# Build GodotSharpTools solution
import os
import subprocess
import mono_reg_utils as monoreg
def mono_build_solution(source, target, env):
if os.name == 'nt':
msbuild_tools_path = monoreg.find_msbuild_tools_path_reg()
if not msbuild_tools_path:
raise RuntimeError('Cannot find MSBuild Tools Path in the registry')
msbuild_path = os.path.join(msbuild_tools_path, 'MSBuild.exe')
else:
msbuild_path = 'msbuild'
output_path = os.path.abspath(os.path.join(str(target[0]), os.pardir))
msbuild_args = [
msbuild_path,
os.path.abspath(str(source[0])),
'/p:Configuration=Release',
'/p:OutputPath=' + output_path
]
msbuild_env = os.environ.copy()
# Needed when running from Developer Command Prompt for VS
if 'PLATFORM' in msbuild_env:
del msbuild_env['PLATFORM']
msbuild_alt_paths = [ 'xbuild' ]
while True:
try:
subprocess.check_call(msbuild_args, env = msbuild_env)
break
except subprocess.CalledProcessError:
raise RuntimeError('GodotSharpTools build failed')
except OSError:
if os.name != 'nt':
if not msbuild_alt_paths:
raise RuntimeError('Could not find commands msbuild or xbuild')
# Try xbuild
msbuild_args[0] = msbuild_alt_paths.pop(0)
else:
raise RuntimeError('Could not find command MSBuild.exe')
mono_sln_builder = Builder(action = mono_build_solution)
env.Append(BUILDERS = { 'MonoBuildSolution' : mono_sln_builder })
env.MonoBuildSolution(
os.path.join(Dir('#bin').abspath, 'GodotSharpTools.dll'),
'editor/GodotSharpTools/GodotSharpTools.sln'
)

143
modules/mono/config.py Normal file
View file

@ -0,0 +1,143 @@
import imp
import os
import sys
from shutil import copyfile
from SCons.Script import BoolVariable, Environment, Variables
monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py')
def find_file_in_dir(directory, files, prefix='', extension=''):
if not extension.startswith('.'):
extension = '.' + extension
for curfile in files:
if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
return curfile
return None
def can_build(platform):
if platform in ["javascript"]:
return False # Not yet supported
return True
def is_enabled():
# The module is disabled by default. Use module_mono_enabled=yes to enable it.
return False
def configure(env):
env.use_ptrcall = True
envvars = Variables()
envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
envvars.Update(env)
mono_static = env['mono_static']
mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
if env['platform'] == 'windows':
if mono_static:
raise RuntimeError('mono-static: Not supported on Windows')
if env['bits'] == '32':
if os.getenv('MONO32_PREFIX'):
mono_root = os.getenv('MONO32_PREFIX')
elif os.name == 'nt':
mono_root = monoreg.find_mono_root_dir()
else:
if os.getenv('MONO64_PREFIX'):
mono_root = os.getenv('MONO64_PREFIX')
elif os.name == 'nt':
mono_root = monoreg.find_mono_root_dir()
if mono_root is None:
raise RuntimeError('Mono installation directory not found')
mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path)
env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
if mono_lib_name is None:
raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
if os.getenv('VCINSTALLDIR'):
env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
else:
env.Append(LIBS=mono_lib_name)
mono_bin_path = os.path.join(mono_root, 'bin')
mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll')
mono_dll_src = os.path.join(mono_bin_path, mono_dll_name + '.dll')
mono_dll_dst = os.path.join('bin', mono_dll_name + '.dll')
copy_mono_dll = True
if not os.path.isdir('bin'):
os.mkdir('bin')
elif os.path.exists(mono_dll_dst):
copy_mono_dll = False
if copy_mono_dll:
copyfile(mono_dll_src, mono_dll_dst)
else:
mono_root = None
if env['bits'] == '32':
if os.getenv('MONO32_PREFIX'):
mono_root = os.getenv('MONO32_PREFIX')
else:
if os.getenv('MONO64_PREFIX'):
mono_root = os.getenv('MONO64_PREFIX')
if mono_root is not None:
mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path)
env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a')
if mono_lib is None:
raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
env.Append(CPPFLAGS=['-D_REENTRANT'])
if mono_static:
mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
if sys.platform == "darwin":
env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file])
elif sys.platform == "linux" or sys.platform == "linux2":
env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
else:
raise RuntimeError('mono-static: Not supported on this platform')
else:
env.Append(LIBS=[mono_lib])
env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
else:
if mono_static:
raise RuntimeError('mono-static: Not supported with pkg-config. Specify a mono prefix manually')
env.ParseConfig('pkg-config mono-2 --cflags --libs')
env.Append(LINKFLAGS='-rdynamic')
def get_doc_classes():
return ["@C#", "CSharpScript", "GodotSharp"]
def get_doc_path():
return "doc_classes"

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,338 @@
/*************************************************************************/
/* csharp_script.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef CSHARP_SCRIPT_H
#define CSHARP_SCRIPT_H
#include "io/resource_loader.h"
#include "io/resource_saver.h"
#include "script_language.h"
#include "self_list.h"
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
#include "mono_gd/gd_mono_header.h"
#include "mono_gd/gd_mono_internals.h"
class CSharpScript;
class CSharpInstance;
class CSharpLanguage;
#ifdef NO_SAFE_CAST
template <typename TScriptInstance, typename TScriptLanguage>
TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : NULL;
}
#else
template <typename TScriptInstance, typename TScriptLanguage>
TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
return dynamic_cast<TScriptInstance *>(p_inst);
}
#endif
#define CAST_CSHARP_INSTANCE(m_inst) (cast_script_instance<CSharpInstance, CSharpLanguage>(m_inst))
class CSharpScript : public Script {
GDCLASS(CSharpScript, Script)
friend class CSharpInstance;
friend class CSharpLanguage;
friend class CSharpScriptDepSort;
bool tool;
bool valid;
bool builtin;
GDMonoClass *base;
GDMonoClass *native;
GDMonoClass *script_class;
Ref<CSharpScript> base_cache; // TODO what's this for?
Set<Object *> instances;
String source;
StringName name;
SelfList<CSharpScript> script_list;
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
Set<PlaceHolderScriptInstance *> placeholders;
bool source_changed_cache;
bool exports_invalidated;
void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
#endif
#ifdef DEBUG_ENABLED
Map<ObjectID, List<Pair<StringName, Variant> > > pending_reload_state;
#endif
Map<StringName, PropertyInfo> member_info;
void _clear();
bool _update_exports();
CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error);
Variant _new(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class);
protected:
static void _bind_methods();
Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
virtual void _resource_path_changed();
public:
virtual bool can_instance() const;
virtual StringName get_instance_base_type() const;
virtual ScriptInstance *instance_create(Object *p_this);
virtual bool instance_has(const Object *p_this) const;
virtual bool has_source_code() const;
virtual String get_source_code() const;
virtual void set_source_code(const String &p_code);
virtual Error reload(bool p_keep_state = false);
/* TODO */ virtual bool has_script_signal(const StringName &p_signal) const { return false; }
/* TODO */ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const {}
/* TODO */ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
virtual void update_exports();
virtual bool is_tool() const { return tool; }
virtual Ref<Script> get_base_script() const;
virtual String get_node_type() const;
virtual ScriptLanguage *get_language() const;
/* TODO */ virtual void get_script_method_list(List<MethodInfo> *p_list) const {}
bool has_method(const StringName &p_method) const;
/* TODO */ MethodInfo get_method_info(const StringName &p_method) const { return MethodInfo(); }
virtual int get_member_line(const StringName &p_member) const;
Error load_source_code(const String &p_path);
StringName get_script_name() const;
CSharpScript();
~CSharpScript();
};
class CSharpInstance : public ScriptInstance {
friend class CSharpScript;
friend class CSharpLanguage;
Object *owner;
Ref<CSharpScript> script;
Ref<MonoGCHandle> gchandle;
bool base_ref;
bool ref_dying;
void _ml_call_reversed(GDMonoClass *klass, const StringName &p_method, const Variant **p_args, int p_argcount);
void _reference_owner_unsafe();
void _unreference_owner_unsafe();
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle);
public:
MonoObject *get_mono_object() const;
virtual bool set(const StringName &p_name, const Variant &p_value);
virtual bool get(const StringName &p_name, Variant &r_ret) const;
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const;
/* TODO */ virtual void get_method_list(List<MethodInfo> *p_list) const {}
virtual bool has_method(const StringName &p_method) const;
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
void mono_object_disposed();
void refcount_incremented();
bool refcount_decremented();
RPCMode get_rpc_mode(const StringName &p_method) const;
RPCMode get_rset_mode(const StringName &p_variable) const;
virtual void notification(int p_notification);
virtual Ref<Script> get_script() const;
virtual ScriptLanguage *get_language();
CSharpInstance();
~CSharpInstance();
};
class CSharpLanguage : public ScriptLanguage {
friend class CSharpScript;
friend class CSharpInstance;
static CSharpLanguage *singleton;
GDMono *gdmono;
SelfList<CSharpScript>::List script_list;
Mutex *lock;
Mutex *script_bind_lock;
Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > > to_reload;
Map<Object *, Ref<MonoGCHandle> > gchandle_bindings;
struct StringNameCache {
StringName _awaited_signal_callback;
StringName _set;
StringName _get;
StringName _notification;
StringName dotctor; // .ctor
StringNameCache();
};
StringNameCache string_names;
int lang_idx;
public:
_FORCE_INLINE_ int get_language_index() { return lang_idx; }
void set_language_index(int p_idx);
_FORCE_INLINE_ static CSharpLanguage *get_singleton() { return singleton; }
bool debug_break(const String &p_error, bool p_allow_continue = true);
bool debug_break_parse(const String &p_file, int p_line, const String &p_error);
#ifdef TOOLS_ENABLED
void reload_assemblies_if_needed(bool p_soft_reload);
#endif
virtual String get_name() const;
/* LANGUAGE FUNCTIONS */
virtual String get_type() const;
virtual String get_extension() const;
virtual Error execute_file(const String &p_path);
virtual void init();
virtual void finish();
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
/* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { return true; }
virtual Script *create_script() const;
virtual bool has_named_classes() const;
/* TODO? */ virtual int find_function(const String &p_function, const String &p_code) const { return -1; }
virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const;
/* TODO? */ Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, String &r_call_hint) { return ERR_UNAVAILABLE; }
/* TODO? */ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {}
/* TODO */ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) {}
/* DEBUGGER FUNCTIONS */
/* TODO */ virtual String debug_get_error() const { return ""; }
/* TODO */ virtual int debug_get_stack_level_count() const { return 1; }
/* TODO */ virtual int debug_get_stack_level_line(int p_level) const { return 1; }
/* TODO */ virtual String debug_get_stack_level_function(int p_level) const { return ""; }
/* TODO */ virtual String debug_get_stack_level_source(int p_level) const { return ""; }
/* TODO */ virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
/* TODO */ virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
/* TODO */ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
/* TODO */ virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) { return ""; }
/* TODO */ virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); }
/* PROFILING FUNCTIONS */
/* TODO */ virtual void profiling_start() {}
/* TODO */ virtual void profiling_stop() {}
/* TODO */ virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) { return 0; }
/* TODO */ virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) { return 0; }
virtual void frame();
/* TODO? */ virtual void get_public_functions(List<MethodInfo> *p_functions) const {}
/* TODO? */ virtual void get_public_constants(List<Pair<String, Variant> > *p_constants) const {}
virtual void reload_all_scripts();
virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload);
/* LOADER FUNCTIONS */
virtual void get_recognized_extensions(List<String> *p_extensions) const;
#ifdef TOOLS_ENABLED
virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col);
virtual bool overrides_external_editor();
#endif
/* THREAD ATTACHING */
virtual void thread_enter();
virtual void thread_exit();
// Don't use these. I'm watching you
virtual void *alloc_instance_binding_data(Object *p_object);
virtual void free_instance_binding_data(void *p_data);
CSharpLanguage();
~CSharpLanguage();
};
class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader {
public:
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
};
class ResourceFormatSaverCSharpScript : public ResourceFormatSaver {
public:
virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const;
virtual bool recognize(const RES &p_resource) const;
};
#endif // CSHARP_SCRIPT_H

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="@C#" category="Core" version="3.0.alpha.custom_build">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<demos>
</demos>
<methods>
</methods>
<constants>
</constants>
</class>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSharpScript" inherits="Script" category="Core" version="3.0.alpha.custom_build">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<demos>
</demos>
<methods>
<method name="new" qualifiers="vararg">
<return type="Object">
</return>
<description>
</description>
</method>
</methods>
<constants>
</constants>
</class>

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GodotSharp" inherits="Object" category="Core" version="3.0.alpha.custom_build">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<demos>
</demos>
<methods>
<method name="attach_thread">
<return type="void">
</return>
<description>
Attaches the current thread to the mono runtime.
</description>
</method>
<method name="detach_thread">
<return type="void">
</return>
<description>
Detaches the current thread from the mono runtime.
</description>
</method>
<method name="is_domain_loaded">
<return type="bool">
</return>
<description>
Returns whether the scripts domain is loaded.
</description>
</method>
<method name="is_finalizing_domain">
<return type="bool">
</return>
<description>
Returns whether the scripts domain is being finalized.
</description>
</method>
</methods>
<constants>
</constants>
</class>

View file

@ -0,0 +1,335 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security;
using Microsoft.Build.Framework;
namespace GodotSharpTools.Build
{
public class BuildInstance : IDisposable
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_BuildInstance_get_MSBuildPath();
private static string MSBuildPath
{
get { return godot_icall_BuildInstance_get_MSBuildPath(); }
}
private string solution;
private string config;
private Process process;
private int exitCode;
public int ExitCode { get { return exitCode; } }
public bool IsRunning { get { return process != null && !process.HasExited; } }
public BuildInstance(string solution, string config)
{
this.solution = solution;
this.config = config;
}
public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
{
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customProperties);
ProcessStartInfo startInfo = new ProcessStartInfo(MSBuildPath, compilerArgs);
// No console output, thanks
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
using (Process process = new Process())
{
process.StartInfo = startInfo;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
exitCode = process.ExitCode;
}
return true;
}
public bool BuildAsync(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
{
if (process != null)
throw new InvalidOperationException("Already in use");
string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customProperties);
ProcessStartInfo startInfo = new ProcessStartInfo("msbuild", compilerArgs);
// No console output, thanks
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
process = new Process();
process.StartInfo = startInfo;
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(BuildProcess_Exited);
process.Start();
return true;
}
private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties)
{
string arguments = string.Format("{0} /v:normal /t:Build /p:{1} /l:{2},{3};{4}",
solution,
"Configuration=" + config,
typeof(GodotBuildLogger).FullName,
loggerAssemblyPath,
loggerOutputDir
);
if (customProperties != null)
{
foreach (string customProperty in customProperties)
{
arguments += " /p:" + customProperty;
}
}
return arguments;
}
private void RemovePlatformVariable(StringDictionary environmentVariables)
{
// EnvironmentVariables is case sensitive? Seriously?
List<string> platformEnvironmentVariables = new List<string>();
foreach (string env in environmentVariables.Keys)
{
if (env.ToUpper() == "PLATFORM")
platformEnvironmentVariables.Add(env);
}
foreach (string env in platformEnvironmentVariables)
environmentVariables.Remove(env);
}
private void BuildProcess_Exited(object sender, System.EventArgs e)
{
exitCode = process.ExitCode;
godot_icall_BuildInstance_ExitCallback(solution, config, exitCode);
Dispose();
}
public void Dispose()
{
if (process != null)
{
process.Dispose();
process = null;
}
}
}
public class GodotBuildLogger : ILogger
{
public string Parameters { get; set; }
public LoggerVerbosity Verbosity { get; set; }
public void Initialize(IEventSource eventSource)
{
if (null == Parameters)
throw new LoggerException("Log directory was not set.");
string[] parameters = Parameters.Split(';');
string logDir = parameters[0];
if (String.IsNullOrEmpty(logDir))
throw new LoggerException("Log directory was not set.");
if (parameters.Length > 1)
throw new LoggerException("Too many parameters passed.");
string logFile = Path.Combine(logDir, "msbuild_log.txt");
string issuesFile = Path.Combine(logDir, "msbuild_issues.csv");
try
{
if (!Directory.Exists(logDir))
Directory.CreateDirectory(logDir);
this.logStreamWriter = new StreamWriter(logFile);
this.issuesStreamWriter = new StreamWriter(issuesFile);
}
catch (Exception ex)
{
if
(
ex is UnauthorizedAccessException
|| ex is ArgumentNullException
|| ex is PathTooLongException
|| ex is DirectoryNotFoundException
|| ex is NotSupportedException
|| ex is ArgumentException
|| ex is SecurityException
|| ex is IOException
)
{
throw new LoggerException("Failed to create log file: " + ex.Message);
}
else
{
// Unexpected failure
throw;
}
}
eventSource.ProjectStarted += new ProjectStartedEventHandler(eventSource_ProjectStarted);
eventSource.TaskStarted += new TaskStartedEventHandler(eventSource_TaskStarted);
eventSource.MessageRaised += new BuildMessageEventHandler(eventSource_MessageRaised);
eventSource.WarningRaised += new BuildWarningEventHandler(eventSource_WarningRaised);
eventSource.ErrorRaised += new BuildErrorEventHandler(eventSource_ErrorRaised);
eventSource.ProjectFinished += new ProjectFinishedEventHandler(eventSource_ProjectFinished);
}
void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
{
string line = String.Format("{0}({1},{2}): error {3}: {4}", e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message);
if (e.ProjectFile.Length > 0)
line += string.Format(" [{0}]", e.ProjectFile);
WriteLine(line);
string errorLine = String.Format(@"error,{0},{1},{2},{3},{4},{5}",
e.File.CsvEscape(), e.LineNumber, e.ColumnNumber,
e.Code.CsvEscape(), e.Message.CsvEscape(), e.ProjectFile.CsvEscape());
issuesStreamWriter.WriteLine(errorLine);
}
void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
{
string line = String.Format("{0}({1},{2}): warning {3}: {4}", e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, e.ProjectFile);
if (e.ProjectFile != null && e.ProjectFile.Length > 0)
line += string.Format(" [{0}]", e.ProjectFile);
WriteLine(line);
string warningLine = String.Format(@"warning,{0},{1},{2},{3},{4},{5}",
e.File.CsvEscape(), e.LineNumber, e.ColumnNumber,
e.Code.CsvEscape(), e.Message.CsvEscape(), e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty);
issuesStreamWriter.WriteLine(warningLine);
}
void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
{
// BuildMessageEventArgs adds Importance to BuildEventArgs
// Let's take account of the verbosity setting we've been passed in deciding whether to log the message
if ((e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal))
|| (e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal))
|| (e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed))
)
{
WriteLineWithSenderAndMessage(String.Empty, e);
}
}
void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
{
// TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName
// To keep this log clean, this logger will ignore these events.
}
void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
{
WriteLine(e.Message);
indent++;
}
void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
{
indent--;
WriteLine(e.Message);
}
/// <summary>
/// Write a line to the log, adding the SenderName
/// </summary>
private void WriteLineWithSender(string line, BuildEventArgs e)
{
if (0 == String.Compare(e.SenderName, "MSBuild", true /*ignore case*/))
{
// Well, if the sender name is MSBuild, let's leave it out for prettiness
WriteLine(line);
}
else
{
WriteLine(e.SenderName + ": " + line);
}
}
/// <summary>
/// Write a line to the log, adding the SenderName and Message
/// (these parameters are on all MSBuild event argument objects)
/// </summary>
private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e)
{
if (0 == String.Compare(e.SenderName, "MSBuild", true /*ignore case*/))
{
// Well, if the sender name is MSBuild, let's leave it out for prettiness
WriteLine(line + e.Message);
}
else
{
WriteLine(e.SenderName + ": " + line + e.Message);
}
}
private void WriteLine(string line)
{
for (int i = indent; i > 0; i--)
{
logStreamWriter.Write("\t");
}
logStreamWriter.WriteLine(line);
}
public void Shutdown()
{
logStreamWriter.Close();
issuesStreamWriter.Close();
}
public bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity)
{
return this.Verbosity >= checkVerbosity;
}
private StreamWriter logStreamWriter;
private StreamWriter issuesStreamWriter;
private int indent;
}
}

View file

@ -0,0 +1,58 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
namespace GodotSharpTools.Editor
{
public class MonoDevelopInstance
{
private Process process;
private string solutionFile;
public void Execute(string[] files)
{
bool newWindow = process == null || process.HasExited;
List<string> args = new List<string>();
args.Add("--ipc-tcp");
if (newWindow)
args.Add("\"" + Path.GetFullPath(solutionFile) + "\"");
foreach (var file in files)
{
int semicolonIndex = file.IndexOf(';');
string filePath = semicolonIndex < 0 ? file : file.Substring(0, semicolonIndex);
string cursor = semicolonIndex < 0 ? string.Empty : file.Substring(semicolonIndex);
args.Add("\"" + Path.GetFullPath(filePath.NormalizePath()) + cursor + "\"");
}
if (newWindow)
{
ProcessStartInfo startInfo = new ProcessStartInfo(MonoDevelopFile, string.Join(" ", args));
process = Process.Start(startInfo);
}
else
{
Process.Start(MonoDevelopFile, string.Join(" ", args));
}
}
public MonoDevelopInstance(string solutionFile)
{
this.solutionFile = solutionFile;
}
private static string MonoDevelopFile
{
get
{
return "monodevelop";
}
}
}
}

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>GodotSharpTools</RootNamespace>
<AssemblyName>GodotSharpTools</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="Microsoft.Build" />
<Reference Include="Microsoft.Build.Framework" />
</ItemGroup>
<ItemGroup>
<Compile Include="StringExtensions.cs" />
<Compile Include="Build\BuildSystem.cs" />
<Compile Include="Editor\MonoDevelopInstance.cs" />
<Compile Include="Project\ProjectExtensions.cs" />
<Compile Include="Project\ProjectGenerator.cs" />
<Compile Include="Project\ProjectUtils.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,17 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpTools", "GodotSharpTools.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,14 @@
<Properties StartupItem="GodotSharpTools.csproj">
<MonoDevelop.Ide.Workspace ActiveConfiguration="Debug" />
<MonoDevelop.Ide.Workbench ActiveDocument="Build/BuildSystem.cs">
<Files>
<File FileName="Build/ProjectExtensions.cs" Line="1" Column="1" />
<File FileName="Build/ProjectGenerator.cs" Line="1" Column="1" />
<File FileName="Build/BuildSystem.cs" Line="37" Column="14" />
</Files>
</MonoDevelop.Ide.Workbench>
<MonoDevelop.Ide.DebuggingService.Breakpoints>
<BreakpointStore />
</MonoDevelop.Ide.DebuggingService.Breakpoints>
<MonoDevelop.Ide.DebuggingService.PinnedWatches />
</Properties>

View file

@ -0,0 +1,49 @@
using System;
using Microsoft.Build.Construction;
namespace GodotSharpTools.Project
{
public static class ProjectExtensions
{
public static bool HasItem(this ProjectRootElement root, string itemType, string include)
{
string includeNormalized = include.NormalizePath();
foreach (var itemGroup in root.ItemGroups)
{
if (itemGroup.Condition.Length != 0)
continue;
foreach (var item in itemGroup.Items)
{
if (item.ItemType == itemType)
{
if (item.Include.NormalizePath() == includeNormalized)
return true;
}
}
}
return false;
}
public static void AddItemChecked(this ProjectRootElement root, string itemType, string include)
{
if (!root.HasItem(itemType, include))
{
root.AddItem(itemType, include);
}
}
public static Guid GetGuid(this ProjectRootElement root)
{
foreach (var property in root.Properties)
{
if (property.Name == "ProjectGuid")
return Guid.Parse(property.Value);
}
return Guid.Empty;
}
}
}

View file

@ -0,0 +1,216 @@
using System;
using System.IO;
using Microsoft.Build.Construction;
namespace GodotSharpTools.Project
{
public static class ProjectGenerator
{
public static string GenCoreApiProject(string dir, string[] compileItems)
{
string path = Path.Combine(dir, CoreApiProject + ".csproj");
ProjectPropertyGroupElement mainGroup;
var root = CreateLibraryProject(CoreApiProject, out mainGroup);
mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml"));
mainGroup.SetProperty("RootNamespace", "Godot");
GenAssemblyInfoFile(root, dir, CoreApiProject,
new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProject + "\")]" },
new string[] { "System.Runtime.CompilerServices" });
foreach (var item in compileItems)
{
root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\"));
}
root.Save(path);
return root.GetGuid().ToString().ToUpper();
}
public static string GenEditorApiProject(string dir, string coreApiHintPath, string[] compileItems)
{
string path = Path.Combine(dir, EditorApiProject + ".csproj");
ProjectPropertyGroupElement mainGroup;
var root = CreateLibraryProject(EditorApiProject, out mainGroup);
mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml"));
mainGroup.SetProperty("RootNamespace", "Godot");
GenAssemblyInfoFile(root, dir, EditorApiProject);
foreach (var item in compileItems)
{
root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\"));
}
var coreApiRef = root.AddItem("Reference", CoreApiProject);
coreApiRef.AddMetadata("HintPath", coreApiHintPath);
coreApiRef.AddMetadata("Private", "False");
root.Save(path);
return root.GetGuid().ToString().ToUpper();
}
public static string GenGameProject(string dir, string name, string[] compileItems)
{
string path = Path.Combine(dir, name + ".csproj");
ProjectPropertyGroupElement mainGroup;
var root = CreateLibraryProject(name, out mainGroup);
mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
var toolsGroup = root.AddPropertyGroup();
toolsGroup.Condition = " '$(Configuration)|$(Platform)' == 'Tools|AnyCPU' ";
toolsGroup.AddProperty("DebugSymbols", "true");
toolsGroup.AddProperty("DebugType", "full");
toolsGroup.AddProperty("Optimize", "false");
toolsGroup.AddProperty("DefineConstants", "DEBUG;TOOLS;");
toolsGroup.AddProperty("ErrorReport", "prompt");
toolsGroup.AddProperty("WarningLevel", "4");
toolsGroup.AddProperty("ConsolePause", "false");
var coreApiRef = root.AddItem("Reference", CoreApiProject);
coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProject + ".dll"));
coreApiRef.AddMetadata("Private", "False");
var editorApiRef = root.AddItem("Reference", EditorApiProject);
editorApiRef.Condition = " '$(Configuration)' == 'Tools' ";
editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProject + ".dll"));
editorApiRef.AddMetadata("Private", "False");
GenAssemblyInfoFile(root, dir, name);
foreach (var item in compileItems)
{
root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\"));
}
root.Save(path);
return root.GetGuid().ToString().ToUpper();
}
public static void GenAssemblyInfoFile(ProjectRootElement root, string dir, string name, string[] assemblyLines = null, string[] usingDirectives = null)
{
string propertiesDir = Path.Combine(dir, "Properties");
if (!Directory.Exists(propertiesDir))
Directory.CreateDirectory(propertiesDir);
string usingDirectivesText = string.Empty;
if (usingDirectives != null)
{
foreach (var usingDirective in usingDirectives)
usingDirectivesText += "\nusing " + usingDirective + ";";
}
string assemblyLinesText = string.Empty;
if (assemblyLines != null)
{
foreach (var assemblyLine in assemblyLines)
assemblyLinesText += string.Join("\n", assemblyLines) + "\n";
}
string content = string.Format(assemblyInfoTemplate, usingDirectivesText, name, assemblyLinesText);
string assemblyInfoFile = Path.Combine(propertiesDir, "AssemblyInfo.cs");
File.WriteAllText(assemblyInfoFile, content);
root.AddItem("Compile", assemblyInfoFile.RelativeToPath(dir).Replace("/", "\\"));
}
public static ProjectRootElement CreateLibraryProject(string name, out ProjectPropertyGroupElement mainGroup)
{
var root = ProjectRootElement.Create();
root.DefaultTargets = "Build";
mainGroup = root.AddPropertyGroup();
mainGroup.AddProperty("Configuration", "Debug").Condition = " '$(Configuration)' == '' ";
mainGroup.AddProperty("Platform", "AnyCPU").Condition = " '$(Platform)' == '' ";
mainGroup.AddProperty("ProjectGuid", "{" + Guid.NewGuid().ToString().ToUpper() + "}");
mainGroup.AddProperty("OutputType", "Library");
mainGroup.AddProperty("OutputPath", Path.Combine("bin", "$(Configuration)"));
mainGroup.AddProperty("RootNamespace", name);
mainGroup.AddProperty("AssemblyName", name);
mainGroup.AddProperty("TargetFrameworkVersion", "v4.5");
var debugGroup = root.AddPropertyGroup();
debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ";
debugGroup.AddProperty("DebugSymbols", "true");
debugGroup.AddProperty("DebugType", "full");
debugGroup.AddProperty("Optimize", "false");
debugGroup.AddProperty("DefineConstants", "DEBUG;");
debugGroup.AddProperty("ErrorReport", "prompt");
debugGroup.AddProperty("WarningLevel", "4");
debugGroup.AddProperty("ConsolePause", "false");
var releaseGroup = root.AddPropertyGroup();
releaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ";
releaseGroup.AddProperty("DebugType", "full");
releaseGroup.AddProperty("Optimize", "true");
releaseGroup.AddProperty("ErrorReport", "prompt");
releaseGroup.AddProperty("WarningLevel", "4");
releaseGroup.AddProperty("ConsolePause", "false");
// References
var referenceGroup = root.AddItemGroup();
referenceGroup.AddItem("Reference", "System");
root.AddImport(Path.Combine("$(MSBuildBinPath)", "Microsoft.CSharp.targets").Replace("/", "\\"));
return root;
}
private static void AddItems(ProjectRootElement elem, string groupName, params string[] items)
{
var group = elem.AddItemGroup();
foreach (var item in items)
{
group.AddItem(groupName, item);
}
}
public const string CoreApiProject = "GodotSharp";
public const string EditorApiProject = "GodotSharpEditor";
private const string assemblyInfoTemplate =
@"using System.Reflection;{0}
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle(""{1}"")]
[assembly: AssemblyDescription("""")]
[assembly: AssemblyConfiguration("""")]
[assembly: AssemblyCompany("""")]
[assembly: AssemblyProduct("""")]
[assembly: AssemblyCopyright("""")]
[assembly: AssemblyTrademark("""")]
[assembly: AssemblyCulture("""")]
// The assembly version has the format ""{{Major}}.{{Minor}}.{{Build}}.{{Revision}}"".
// The form ""{{Major}}.{{Minor}}.*"" will automatically update the build and revision,
// and ""{{Major}}.{{Minor}}.{{Build}}.*"" will update just the revision.
[assembly: AssemblyVersion(""1.0.*"")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("""")]
{2}";
}
}

View file

@ -0,0 +1,17 @@
using System;
using System.IO;
using Microsoft.Build.Construction;
namespace GodotSharpTools.Project
{
public static class ProjectUtils
{
public static void AddItemToProjectChecked(string projectPath, string itemType, string include)
{
var dir = Directory.GetParent(projectPath).FullName;
var root = ProjectRootElement.Open(projectPath);
root.AddItemChecked(itemType, include.RelativeToPath(dir).Replace("/", "\\"));
root.Save();
}
}
}

View file

@ -0,0 +1,27 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("GodotSharpTools")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("ignacio")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

View file

@ -0,0 +1,52 @@
using System;
using System.IO;
namespace GodotSharpTools
{
public static class StringExtensions
{
public static string RelativeToPath(this string path, string dir)
{
// Make sure the directory ends with a path separator
dir = Path.Combine(dir, " ").TrimEnd();
if (Path.DirectorySeparatorChar == '\\')
dir = dir.Replace("/", "\\") + "\\";
Uri fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute);
Uri relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute);
return relRoot.MakeRelativeUri(fullPath).ToString();
}
public static string NormalizePath(this string path)
{
bool rooted = path.IsAbsolutePath();
path = path.Replace('\\', '/');
string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim();
return rooted ? Path.DirectorySeparatorChar.ToString() + path : path;
}
private static readonly string driveRoot = Path.GetPathRoot(Environment.CurrentDirectory);
public static bool IsAbsolutePath(this string path)
{
return path.StartsWith("/") || path.StartsWith("\\") || path.StartsWith(driveRoot);
}
public static string CsvEscape(this string value, char delimiter = ',')
{
bool hasSpecialChar = value.IndexOfAny(new char[] { '\"', '\n', '\r', delimiter }) != -1;
if (hasSpecialChar)
return "\"" + value.Replace("\"", "\"\"") + "\"";
return value;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,429 @@
/*************************************************************************/
/* bindings_generator.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef BINDINGS_GENERATOR_H
#define BINDINGS_GENERATOR_H
#include "class_db.h"
#include "editor/doc/doc_data.h"
#include "editor/editor_help.h"
#ifdef DEBUG_METHODS_ENABLED
#include "ustring.h"
class BindingsGenerator {
struct ArgumentInterface {
enum DefaultParamMode {
CONSTANT,
NULLABLE_VAL,
NULLABLE_REF
};
String type;
String name;
String default_argument;
DefaultParamMode def_param_mode;
ArgumentInterface() {
def_param_mode = CONSTANT;
}
};
struct MethodInterface {
String name;
/**
* Name of the C# method
*/
String proxy_name;
/**
* [TypeInterface::name] of the return type
*/
String return_type;
/**
* Determines if the method has a variable number of arguments (VarArg)
*/
bool is_vararg;
/**
* Virtual methods ("virtual" as defined by the Godot API) are methods that by default do nothing,
* but can be overridden by the user to add custom functionality.
* e.g.: _ready, _process, etc.
*/
bool is_virtual;
/**
* Determines if the call should fallback to Godot's object.Call(string, params) in C#.
*/
bool requires_object_call;
/**
* Determines if the method visibility is `internal` (visible only to files in the same assembly).
* Currently, we only use this for methods that are not meant to be exposed,
* but are required by properties as getters or setters.
* Methods that are not meant to be exposed are those that begin with underscore and are not virtual.
*/
bool is_internal;
List<ArgumentInterface> arguments;
const DocData::MethodDoc *method_doc;
void add_argument(const ArgumentInterface &argument) {
arguments.push_back(argument);
}
MethodInterface() {
return_type = "void";
is_vararg = false;
is_virtual = false;
requires_object_call = false;
is_internal = false;
method_doc = NULL;
}
};
struct TypeInterface {
/**
* Identifier name for this type.
* Also used to format [c_out].
*/
String name;
/**
* Identifier name of the base class.
*/
String base_name;
/**
* Name of the C# class
*/
String proxy_name;
ClassDB::APIType api_type;
bool is_object_type;
bool is_singleton;
bool is_reference;
/**
* Used only by Object-derived types.
* Determines if this type is not virtual (incomplete).
* e.g.: CanvasItem cannot be instantiated.
*/
bool is_instantiable;
/**
* Used only by Object-derived types.
* Determines if the C# class owns the native handle and must free it somehow when disposed.
* e.g.: Reference types must notify when the C# instance is disposed, for proper refcounting.
*/
bool memory_own;
/**
* Determines if the file must have a using directive for System.Collections.Generic
* e.g.: When the generated class makes use of Dictionary
*/
bool requires_collections;
// !! The comments of the following fields make reference to other fields via square brackets, e.g.: [field_name]
// !! When renaming those fields, make sure to rename their references in the comments
// --- C INTERFACE ---
static const char *DEFAULT_VARARG_C_IN;
/**
* One or more statements that manipulate the parameter before being passed as argument of a ptrcall.
* If the statement adds a local that must be passed as the argument instead of the parameter,
* the name of that local must be specified with [c_arg_in].
* For variadic methods, this field is required and, if empty, [DEFAULT_VARARG_C_IN] is used instead.
* Formatting elements:
* %0: [c_type] of the parameter
* %1: name of the parameter
*/
String c_in;
/**
* Determines the name of the variable that will be passed as argument to a ptrcall.
* By default the value equals the name of the parameter,
* this varies for types that require special manipulation via [c_in].
* Formatting elements:
* %0 or %s: name of the parameter
*/
String c_arg_in;
/**
* One or more statements that determine how a variable of this type is returned from a function.
* It must contain the return statement(s).
* Formatting elements:
* %0: [c_type_out] of the return type
* %1: name of the variable to be returned
* %2: [name] of the return type
*/
String c_out;
/**
* The actual expected type, as seen (in most cases) in Variant copy constructors
* Used for the type of the return variable and to format [c_in].
* The value must be the following depending of the type:
* Object-derived types: Object*
* Other types: [name]
* -- Exceptions --
* VarArg (fictitious type to represent variable arguments): Array
* float: double (because ptrcall only supports double)
* int: int64_t (because ptrcall only supports int64_t and uint64_t)
* Reference types override this for the type of the return variable: Ref<Reference>
*/
String c_type;
/**
* Determines the type used for parameters in function signatures.
*/
String c_type_in;
/**
* Determines the return type used for function signatures.
* Also used to construct a default value to return in case of errors,
* and to format [c_out].
*/
String c_type_out;
// --- C# INTERFACE ---
/**
* An expression that overrides the way the parameter is passed to the internal call.
* If empty, the parameter is passed as is.
* Formatting elements:
* %0 or %s: name of the parameter
*/
String cs_in;
/**
* One or more statements that determine how a variable of this type is returned from a method.
* It must contain the return statement(s).
* Formatting elements:
* %0 or %s: name of the variable to be returned
*/
String cs_out;
/**
* Type used for method signatures, both for parameters and the return type.
* Same as [proxy_name] except for variable arguments (VarArg).
*/
String cs_type;
/**
* Type used for parameters of internal call methods.
*/
String im_type_in;
/**
* Type used for the return type of internal call methods.
* If [cs_out] is not empty and the method return type is not void,
* it is also used for the type of the return variable.
*/
String im_type_out;
const DocData::ClassDoc *class_doc;
List<MethodInterface> methods;
const MethodInterface *find_method_by_name(const String &p_name) const {
for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) {
if (E->get().name == p_name)
return &E->get();
}
return NULL;
}
static TypeInterface create_value_type(const String &p_name) {
TypeInterface itype;
itype.name = p_name;
itype.proxy_name = p_name;
itype.c_type = itype.name;
itype.c_type_in = "void*";
itype.c_type_out = "MonoObject*";
itype.cs_type = itype.proxy_name;
itype.im_type_in = "ref " + itype.proxy_name;
itype.im_type_out = itype.proxy_name;
itype.class_doc = &EditorHelp::get_doc_data()->class_list[itype.proxy_name];
return itype;
}
static TypeInterface create_object_type(const String &p_name, ClassDB::APIType p_api_type) {
TypeInterface itype;
itype.name = p_name;
itype.proxy_name = p_name.begins_with("_") ? p_name.substr(1, p_name.length()) : p_name;
itype.api_type = p_api_type;
itype.is_object_type = true;
itype.class_doc = &EditorHelp::get_doc_data()->class_list[itype.proxy_name];
return itype;
}
static void create_placeholder_type(TypeInterface &r_itype, const String &p_name) {
r_itype.name = p_name;
r_itype.proxy_name = p_name;
r_itype.c_type = r_itype.name;
r_itype.c_type_in = "MonoObject*";
r_itype.c_type_out = "MonoObject*";
r_itype.cs_type = r_itype.proxy_name;
r_itype.im_type_in = r_itype.proxy_name;
r_itype.im_type_out = r_itype.proxy_name;
}
TypeInterface() {
api_type = ClassDB::API_NONE;
is_object_type = false;
is_singleton = false;
is_reference = false;
is_instantiable = false;
memory_own = false;
requires_collections = false;
c_arg_in = "%s";
class_doc = NULL;
}
};
struct InternalCall {
String name;
String im_type_out; // Return type for the C# method declaration. Also used as companion of [unique_siq]
String im_sig; // Signature for the C# method declaration
String unique_sig; // Unique signature to avoid duplicates in containers
bool editor_only;
InternalCall() {}
InternalCall(const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) {
name = p_name;
im_type_out = p_im_type_out;
im_sig = p_im_sig;
unique_sig = p_unique_sig;
editor_only = false;
}
InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) {
name = p_name;
im_type_out = p_im_type_out;
im_sig = p_im_sig;
unique_sig = p_unique_sig;
editor_only = api_type == ClassDB::API_EDITOR;
}
inline bool operator==(const InternalCall &p_a) const {
return p_a.unique_sig == unique_sig;
}
};
static bool verbose_output;
Map<String, TypeInterface> placeholder_types;
Map<String, TypeInterface> builtin_types;
Map<String, TypeInterface> obj_types;
Map<String, String> extra_members;
List<InternalCall> method_icalls;
Map<const MethodInterface *, const InternalCall *> method_icalls_map;
List<InternalCall> core_custom_icalls;
List<InternalCall> editor_custom_icalls;
const List<InternalCall>::Element *find_icall_by_name(const String &p_name, const List<InternalCall> &p_list) {
const List<InternalCall>::Element *it = p_list.front();
while (it) {
if (it->get().name == p_name) return it;
it = it->next();
}
return NULL;
}
inline String get_unique_sig(const TypeInterface &p_type) {
if (p_type.is_reference)
return "Ref";
else if (p_type.is_object_type)
return "Obj";
return p_type.name;
}
void _generate_header_icalls();
void _generate_method_icalls(const TypeInterface &p_itype);
const TypeInterface *_get_type_by_name_or_null(const String &p_name);
const TypeInterface *_get_type_by_name_or_placeholder(const String &p_name);
void _default_argument_from_variant(const Variant &p_var, ArgumentInterface &r_iarg);
void _populate_builtin_type(TypeInterface &r_type, Variant::Type vtype);
void _populate_object_type_interfaces();
void _populate_builtin_type_interfaces();
Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file);
Error _save_file(const String &path, const List<String> &content);
BindingsGenerator();
BindingsGenerator(const BindingsGenerator &);
BindingsGenerator &operator=(const BindingsGenerator &);
public:
Error generate_cs_core_project(const String &p_output_dir, bool p_verbose_output = true);
Error generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output = true);
Error generate_glue(const String &p_output_dir);
static BindingsGenerator &get_singleton() {
static BindingsGenerator singleton;
return singleton;
}
static void handle_cmdline_args(const List<String> &p_cmdline_args);
};
#endif
#endif // BINDINGS_GENERATOR_H

View file

@ -0,0 +1,120 @@
/*************************************************************************/
/* csharp_project.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "csharp_project.h"
#include "os/os.h"
#include "project_settings.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_marshal.h"
namespace CSharpProject {
String generate_core_api_project(const String &p_dir, const Vector<String> &p_files) {
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator");
Variant dir = p_dir;
Variant compile_items = p_files;
const Variant *args[2] = { &dir, &compile_items };
MonoObject *ex = NULL;
MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &ex);
if (ex) {
mono_print_unhandled_exception(ex);
ERR_FAIL_V(String());
}
return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : "";
}
String generate_editor_api_project(const String &p_dir, const String &p_core_dll_path, const Vector<String> &p_files) {
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator");
Variant dir = p_dir;
Variant core_dll_path = p_core_dll_path;
Variant compile_items = p_files;
const Variant *args[3] = { &dir, &core_dll_path, &compile_items };
MonoObject *ex = NULL;
MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &ex);
if (ex) {
mono_print_unhandled_exception(ex);
ERR_FAIL_V(String());
}
return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : "";
}
String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files) {
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator");
Variant dir = p_dir;
Variant name = p_name;
Variant compile_items = p_files;
const Variant *args[3] = { &dir, &name, &compile_items };
MonoObject *ex = NULL;
MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &ex);
if (ex) {
mono_print_unhandled_exception(ex);
ERR_FAIL_V(String());
}
return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : "";
}
void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) {
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils");
Variant project_path = p_project_path;
Variant item_type = p_item_type;
Variant include = p_include;
const Variant *args[3] = { &project_path, &item_type, &include };
MonoObject *ex = NULL;
klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &ex);
if (ex) {
mono_print_unhandled_exception(ex);
ERR_FAIL();
}
}
} // CSharpProject

View file

@ -0,0 +1,44 @@
/*************************************************************************/
/* csharp_project.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef CSHARP_PROJECT_H
#define CSHARP_PROJECT_H
#include "ustring.h"
namespace CSharpProject {
String generate_core_api_project(const String &p_dir, const Vector<String> &p_files = Vector<String>());
String generate_editor_api_project(const String &p_dir, const String &p_core_dll_path, const Vector<String> &p_files = Vector<String>());
String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files = Vector<String>());
void add_item(const String &p_project_path, const String &p_item_type, const String &p_include);
}
#endif // CSHARP_PROJECT_H

View file

@ -0,0 +1,440 @@
/*************************************************************************/
/* godotsharp_builds.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "godotsharp_builds.h"
#include "../godotsharp_dirs.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"
#include "main/main.h"
void godot_icall_BuildInstance_ExitCallback(MonoString *p_solution, MonoString *p_config, int p_exit_code) {
String solution = GDMonoMarshal::mono_string_to_godot(p_solution);
String config = GDMonoMarshal::mono_string_to_godot(p_config);
GodotSharpBuilds::get_singleton()->build_exit_callback(MonoBuildInfo(solution, config), p_exit_code);
}
MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool")));
#ifdef WINDOWS_ENABLED
switch (build_tool) {
case GodotSharpBuilds::MSBUILD: {
static String msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path();
if (msbuild_tools_path.length()) {
if (!msbuild_tools_path.ends_with("\\"))
msbuild_tools_path += "\\";
return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
}
OS::get_singleton()->print("Cannot find System's MSBuild. Trying with Mono's...\n");
}
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);
}
return GDMonoMarshal::mono_string_from_godot(msbuild_path);
}
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 xbuild ('mono/builds/build_tool'). Tried with path: " + xbuild_path);
}
return GDMonoMarshal::mono_string_from_godot(xbuild_path);
}
default:
ERR_EXPLAIN("You don't deserve to live");
CRASH_NOW();
}
#else
static bool msbuild_found = path_which("msbuild").length();
if (build_tool != GodotSharpBuilds::XBUILD && !msbuild_found) {
WARN_PRINT("Cannot find msbuild ('mono/builds/build_tool').");
}
return GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? "msbuild" : "xbuild");
#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_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath);
}
void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
GodotSharpEditor::get_singleton()->show_error_dialog(p_message, "Build error");
MonoBottomPanel::get_singleton()->show_build_tab();
}
bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config) {
String api_sln_file = p_api_sln_dir.plus_file(p_name + ".sln");
String api_assembly_dir = p_api_sln_dir.plus_file("bin").plus_file(p_config);
String api_assembly_file = api_assembly_dir.plus_file(p_name + ".dll");
if (!FileAccess::exists(api_assembly_file)) {
MonoBuildInfo api_build_info(api_sln_file, p_config);
api_build_info.custom_props.push_back("NoWarn=1591"); // Ignore missing documentation warnings
if (!GodotSharpBuilds::get_singleton()->build(api_build_info)) {
show_build_error_dialog("Failed to build " + p_name + " solution.");
return false;
}
}
return true;
}
bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name) {
String assembly_file = p_assembly_name + ".dll";
String assembly_src = p_src_dir.plus_file(assembly_file);
String assembly_dst = p_dst_dir.plus_file(assembly_file);
if (!FileAccess::exists(assembly_dst) || FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst)) {
DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
String xml_file = p_assembly_name + ".xml";
if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK)
WARN_PRINTS("Failed to copy " + xml_file);
String pdb_file = p_assembly_name + ".pdb";
if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK)
WARN_PRINTS("Failed to copy " + pdb_file);
Error err = da->copy(assembly_src, assembly_dst);
memdelete(da);
if (err != OK) {
show_build_error_dialog("Failed to copy " API_ASSEMBLY_NAME ".dll");
return false;
}
}
return true;
}
bool GodotSharpBuilds::make_api_sln(GodotSharpBuilds::APIType p_api_type) {
String api_name = p_api_type == API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
String api_build_config = "Release";
EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 4);
pr.step("Generating " + api_name + " solution");
uint64_t core_hash = GDMono::get_singleton()->get_api_core_hash();
uint64_t editor_hash = GDMono::get_singleton()->get_api_editor_hash();
String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(API_ASSEMBLY_NAME "_" + itos(core_hash));
String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir().plus_file(EDITOR_API_ASSEMBLY_NAME "_" + itos(editor_hash));
String api_sln_dir = p_api_type == API_CORE ? core_api_sln_dir : editor_api_sln_dir;
String api_sln_file = api_sln_dir.plus_file(api_name + ".sln");
if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
String core_api_assembly;
if (p_api_type == API_EDITOR) {
core_api_assembly = core_api_sln_dir.plus_file("bin")
.plus_file(api_build_config)
.plus_file(API_ASSEMBLY_NAME ".dll");
}
#ifndef DEBUG_METHODS_ENABLED
#error "How am I supposed to generate the bindings?"
#endif
BindingsGenerator &gen = BindingsGenerator::get_singleton();
bool gen_verbose = OS::get_singleton()->is_stdout_verbose();
Error err = p_api_type == API_CORE ?
gen.generate_cs_core_project(api_sln_dir, gen_verbose) :
gen.generate_cs_editor_project(api_sln_dir, core_api_assembly, gen_verbose);
if (err != OK) {
show_build_error_dialog("Failed to generate " + api_name + " solution. Error: " + itos(err));
return false;
}
}
pr.step("Building " + api_name + " solution");
if (!GodotSharpBuilds::build_api_sln(api_name, api_sln_dir, api_build_config))
return false;
pr.step("Copying " + api_name + " assembly");
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
// Create assemblies directory if needed
if (!DirAccess::exists(res_assemblies_dir)) {
DirAccess *da = DirAccess::create_for_path(res_assemblies_dir);
Error err = da->make_dir_recursive(res_assemblies_dir);
memdelete(da);
if (err != OK) {
show_build_error_dialog("Failed to create assemblies directory. Error: " + itos(err));
return false;
}
}
// Copy the built assembly to the assemblies directory
String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config);
if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name))
return false;
pr.step("Done");
return true;
}
bool godotsharp_build_callback() {
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
return true; // No solution to build
if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE))
return false;
if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR))
return false;
EditorProgress pr("mono_project_debug_build", "Building project solution...", 2);
pr.step("Building project solution");
MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), "Tools");
if (!GodotSharpBuilds::get_singleton()->build(build_info)) {
GodotSharpBuilds::show_build_error_dialog("Failed to build project solution");
return false;
}
pr.step("Done");
return true;
}
GodotSharpBuilds *GodotSharpBuilds::singleton = NULL;
void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) {
BuildProcess *match = builds.getptr(p_build_info);
ERR_FAIL_COND(!match);
BuildProcess &bp = *match;
bp.on_exit(p_exit_code);
}
void GodotSharpBuilds::restart_build(MonoBuildTab *p_build_tab) {
}
void GodotSharpBuilds::stop_build(MonoBuildTab *p_build_tab) {
}
bool GodotSharpBuilds::build(const MonoBuildInfo &p_build_info) {
BuildProcess *match = builds.getptr(p_build_info);
if (match) {
BuildProcess &bp = *match;
bp.start(true);
return bp.exit_code == 0;
} else {
BuildProcess bp = BuildProcess(p_build_info);
bp.start(true);
builds.set(p_build_info, bp);
return bp.exit_code == 0;
}
}
bool GodotSharpBuilds::build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) {
BuildProcess *match = builds.getptr(p_build_info);
if (match) {
BuildProcess &bp = *match;
bp.start();
return !bp.exited; // failed to start
} else {
BuildProcess bp = BuildProcess(p_build_info, p_callback);
bp.start();
builds.set(p_build_info, bp);
return !bp.exited; // failed to start
}
}
GodotSharpBuilds::GodotSharpBuilds() {
singleton = this;
EditorNode::get_singleton()->add_build_callback(&godotsharp_build_callback);
// Build tool settings
EditorSettings *ed_settings = EditorSettings::get_singleton();
if (!ed_settings->has("mono/builds/build_tool")) {
ed_settings->set("mono/builds/build_tool", MSBUILD);
}
ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM, "MSBuild (System),MSBuild (Mono),xbuild"));
}
GodotSharpBuilds::~GodotSharpBuilds() {
singleton = NULL;
}
void GodotSharpBuilds::BuildProcess::on_exit(int p_exit_code) {
exited = true;
exit_code = p_exit_code;
build_tab->on_build_exit(p_exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR);
build_instance.unref();
if (exit_callback)
exit_callback(exit_code);
}
void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
exit_code = -1;
String logs_dir = GodotSharpDirs::get_build_logs_dir().plus_file(build_info.solution.md5_text() + "_" + build_info.configuration);
if (build_tab) {
build_tab->on_build_start();
} else {
build_tab = memnew(MonoBuildTab(build_info, logs_dir));
MonoBottomPanel::get_singleton()->add_build_tab(build_tab);
}
if (p_blocking) {
// Required in order to update the build tasks list
Main::iteration();
}
if (!exited) {
ERR_PRINT("BuildProcess::start called, but process still running");
exited = true;
build_tab->on_build_exec_failed("!exited");
return;
}
exited = false;
// Remove old issues file
String issues_file = "msbuild_issues.csv";
DirAccessRef d = DirAccess::create_for_path(logs_dir);
if (d->file_exists(issues_file)) {
Error err = d->remove(issues_file);
if (err != OK) {
ERR_PRINTS("Cannot remove file: " + logs_dir.plus_file(issues_file));
exited = true;
build_tab->on_build_exec_failed("Cannot remove file: " + issues_file);
return;
}
}
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Build", "BuildInstance");
MonoObject *mono_object = mono_object_new(mono_domain_get(), klass->get_raw());
// Construct
Variant solution = build_info.solution;
Variant config = build_info.configuration;
const Variant *ctor_args[2] = { &solution, &config };
MonoObject *ex = NULL;
GDMonoMethod *ctor = klass->get_method(".ctor", 2);
ctor->invoke(mono_object, ctor_args, &ex);
if (ex) {
exited = true;
build_tab->on_build_exec_failed("The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex));
ERR_FAIL();
}
// Call Build
Variant logger_assembly = OS::get_singleton()->get_executable_path().get_base_dir().plus_file(EDITOR_TOOLS_ASSEMBLY_NAME) + ".dll";
Variant logger_output_dir = logs_dir;
Variant custom_props = build_info.custom_props;
const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props };
ex = NULL;
GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3);
build_method->invoke(mono_object, args, &ex);
if (ex) {
exited = true;
build_tab->on_build_exec_failed("The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex));
ERR_FAIL();
}
// Build returned
if (p_blocking) {
exited = true;
exit_code = klass->get_field("exitCode")->get_int_value(mono_object);
build_tab->on_build_exit(exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR);
} else {
build_instance = MonoGCHandle::create_strong(mono_object);
exited = false;
}
}
GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) {
build_info = p_build_info;
build_tab = NULL;
exit_callback = p_callback;
exited = true;
exit_code = -1;
}

View file

@ -0,0 +1,96 @@
/*************************************************************************/
/* godotsharp_builds.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GODOTSHARP_BUILDS_H
#define GODOTSHARP_BUILDS_H
#include "mono_bottom_panel.h"
#include "mono_build_info.h"
typedef void (*GodotSharpBuild_ExitCallback)(int);
class GodotSharpBuilds {
private:
struct BuildProcess {
Ref<MonoGCHandle> build_instance;
MonoBuildInfo build_info;
MonoBuildTab *build_tab;
GodotSharpBuild_ExitCallback exit_callback;
bool exited;
int exit_code;
void on_exit(int p_exit_code);
void start(bool p_blocking = false);
BuildProcess() {}
BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
};
HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds;
static GodotSharpBuilds *singleton;
friend class GDMono;
static void _register_internal_calls();
public:
enum APIType {
API_CORE,
API_EDITOR
};
enum BuildTool {
MSBUILD,
MSBUILD_MONO,
XBUILD
};
_FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; }
static void show_build_error_dialog(const String &p_message);
void build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code);
void restart_build(MonoBuildTab *p_build_tab);
void stop_build(MonoBuildTab *p_build_tab);
bool build(const MonoBuildInfo &p_build_info);
bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
static bool build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config);
static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name);
static bool make_api_sln(APIType p_api_type);
GodotSharpBuilds();
~GodotSharpBuilds();
};
#endif // GODOTSHARP_BUILDS_H

View file

@ -0,0 +1,256 @@
/*************************************************************************/
/* godotsharp_editor.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "godotsharp_editor.h"
#include "core/os/os.h"
#include "core/project_settings.h"
#include "scene/gui/control.h"
#include "scene/main/node.h"
#include "../csharp_script.h"
#include "../godotsharp_dirs.h"
#include "../mono_gd/gd_mono.h"
#include "../utils/path_utils.h"
#include "bindings_generator.h"
#include "csharp_project.h"
#include "net_solution.h"
#ifdef WINDOWS_ENABLED
#include "../utils/mono_reg_utils.h"
#endif
class MonoReloadNode : public Node {
GDCLASS(MonoReloadNode, Node)
protected:
void _notification(int p_what) {
switch (p_what) {
case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true);
} break;
default: {
} break;
};
}
};
GodotSharpEditor *GodotSharpEditor::singleton = NULL;
bool GodotSharpEditor::_create_project_solution() {
EditorProgress pr("create_csharp_solution", "Generating solution...", 2);
pr.step("Generating C# project...");
String path = OS::get_singleton()->get_resource_dir();
String name = ProjectSettings::get_singleton()->get("application/config/name");
String guid = CSharpProject::generate_game_project(path, name);
if (guid.length()) {
NETSolution solution(name);
if (!solution.set_path(path)) {
show_error_dialog("Failed to create solution.");
return false;
}
Vector<String> extra_configs;
extra_configs.push_back("Tools");
solution.add_new_project(name, guid, extra_configs);
Error sln_error = solution.save();
if (sln_error != OK) {
show_error_dialog("Failed to save solution.");
return false;
}
if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_CORE))
return false;
if (!GodotSharpBuilds::make_api_sln(GodotSharpBuilds::API_EDITOR))
return false;
pr.step("Done");
// Here, after all calls to progress_task_step
call_deferred("_remove_create_sln_menu_option");
} else {
show_error_dialog("Failed to create C# project.");
}
return true;
}
void GodotSharpEditor::_remove_create_sln_menu_option() {
menu_popup->remove_item(menu_popup->get_item_index(MENU_CREATE_SLN));
if (menu_popup->get_item_count() == 0)
menu_button->hide();
bottom_panel_btn->show();
}
void GodotSharpEditor::_menu_option_pressed(int p_id) {
switch (p_id) {
case MENU_CREATE_SLN: {
_create_project_solution();
} break;
default:
ERR_FAIL();
}
}
void GodotSharpEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_create_project_solution"), &GodotSharpEditor::_create_project_solution);
ClassDB::bind_method(D_METHOD("_remove_create_sln_menu_option"), &GodotSharpEditor::_remove_create_sln_menu_option);
ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed);
}
void GodotSharpEditor::show_error_dialog(const String &p_message, const String &p_title) {
error_dialog->set_title(p_title);
error_dialog->set_text(p_message);
error_dialog->popup_centered_minsize();
}
Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) {
ExternalEditor editor = ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor")));
switch (editor) {
case EDITOR_CODE: {
List<String> args;
args.push_back(ProjectSettings::get_singleton()->get_resource_path());
String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
if (p_line >= 0) {
args.push_back("-g");
args.push_back(script_path + ":" + itos(p_line) + ":" + itos(p_col));
} else {
args.push_back(script_path);
}
static String program = path_which("code");
Error err = OS::get_singleton()->execute(program.length() ? program : "code", args, false);
if (err != OK) {
ERR_PRINT("GodotSharp: Could not execute external editor");
return err;
}
} break;
case EDITOR_MONODEVELOP: {
if (!monodevel_instance)
monodevel_instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path()));
String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
monodevel_instance->execute(script_path);
} break;
case EDITOR_VISUAL_STUDIO:
// TODO
// devenv <PathToSolutionFolder>
// devenv /edit <PathToCsFile> /command "edit.goto <Line>"
// HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\SxS\VS7
default:
return ERR_UNAVAILABLE;
}
return OK;
}
bool GodotSharpEditor::overrides_external_editor() {
return ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor"))) != EDITOR_NONE;
}
GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
singleton = this;
monodevel_instance = NULL;
editor = p_editor;
error_dialog = memnew(AcceptDialog);
editor->get_gui_base()->add_child(error_dialog);
bottom_panel_btn = editor->add_bottom_panel_item("Mono", memnew(MonoBottomPanel(editor)));
godotsharp_builds = memnew(GodotSharpBuilds);
editor->add_child(memnew(MonoReloadNode));
menu_button = memnew(MenuButton);
menu_button->set_text("Mono");
menu_popup = menu_button->get_popup();
String sln_path = GodotSharpDirs::get_project_sln_path();
String csproj_path = GodotSharpDirs::get_project_csproj_path();
if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) {
bottom_panel_btn->hide();
menu_popup->add_item("Create C# solution", MENU_CREATE_SLN);
}
menu_popup->connect("id_pressed", this, "_menu_option_pressed");
if (menu_popup->get_item_count() == 0)
menu_button->hide();
editor->get_menu_hb()->add_child(menu_button);
// External editor settings
EditorSettings *ed_settings = EditorSettings::get_singleton();
if (!ed_settings->has("mono/editor/external_editor")) {
ed_settings->set("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,Visual Studio Code"));
}
GodotSharpEditor::~GodotSharpEditor() {
singleton = NULL;
memdelete(godotsharp_builds);
if (monodevel_instance) {
memdelete(monodevel_instance);
monodevel_instance = NULL;
}
}

View file

@ -0,0 +1,87 @@
/*************************************************************************/
/* godotsharp_editor.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GODOTSHARP_EDITOR_H
#define GODOTSHARP_EDITOR_H
#include "godotsharp_builds.h"
#include "monodevelop_instance.h"
class GodotSharpEditor : public Node {
GDCLASS(GodotSharpEditor, Object)
EditorNode *editor;
MenuButton *menu_button;
PopupMenu *menu_popup;
AcceptDialog *error_dialog;
ToolButton *bottom_panel_btn;
GodotSharpBuilds *godotsharp_builds;
MonoDevelopInstance *monodevel_instance;
bool _create_project_solution();
void _remove_create_sln_menu_option();
void _menu_option_pressed(int p_id);
static GodotSharpEditor *singleton;
protected:
static void _bind_methods();
public:
enum MenuOptions {
MENU_CREATE_SLN
};
enum ExternalEditor {
EDITOR_NONE,
EDITOR_MONODEVELOP,
EDITOR_VISUAL_STUDIO,
EDITOR_CODE,
};
_FORCE_INLINE_ static GodotSharpEditor *get_singleton() { return singleton; }
void show_error_dialog(const String &p_message, const String &p_title = "Error");
Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col);
bool overrides_external_editor();
GodotSharpEditor(EditorNode *p_editor);
~GodotSharpEditor();
};
#endif // GODOTSHARP_EDITOR_H

View file

@ -0,0 +1,441 @@
/*************************************************************************/
/* mono_bottom_panel.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "mono_bottom_panel.h"
#include "../csharp_script.h"
#include "godotsharp_editor.h"
MonoBottomPanel *MonoBottomPanel::singleton = NULL;
void MonoBottomPanel::_update_build_tabs_list() {
build_tabs_list->clear();
int current_tab = build_tabs->get_current_tab();
bool no_current_tab = current_tab < 0 || current_tab >= build_tabs->get_tab_count();
for (int i = 0; i < build_tabs->get_child_count(); i++) {
MonoBuildTab *tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(i));
if (tab) {
String item_name = tab->build_info.solution.get_file().get_basename();
item_name += " [" + tab->build_info.configuration + "]";
build_tabs_list->add_item(item_name, tab->get_icon_texture());
String item_tooltip = String("Solution: ") + tab->build_info.solution;
item_tooltip += String("\nConfiguration: ") + tab->build_info.configuration;
item_tooltip += String("\nStatus: ");
if (tab->build_exited) {
item_tooltip += tab->build_result == MonoBuildTab::RESULT_SUCCESS ? "Succeeded" : "Errored";
} else {
item_tooltip += "Running";
}
if (!tab->build_exited || !tab->build_result == MonoBuildTab::RESULT_SUCCESS) {
item_tooltip += "\nErrors: " + itos(tab->error_count);
}
item_tooltip += "\nWarnings: " + itos(tab->warning_count);
build_tabs_list->set_item_tooltip(i, item_tooltip);
if (no_current_tab || current_tab == i) {
build_tabs_list->select(i);
_build_tab_item_selected(i);
}
}
}
}
void MonoBottomPanel::add_build_tab(MonoBuildTab *p_build_tab) {
build_tabs->add_child(p_build_tab);
raise_build_tab(p_build_tab);
}
void MonoBottomPanel::raise_build_tab(MonoBuildTab *p_build_tab) {
ERR_FAIL_COND(p_build_tab->get_parent() != build_tabs);
build_tabs->move_child(p_build_tab, 0);
_update_build_tabs_list();
}
void MonoBottomPanel::show_build_tab() {
for (int i = 0; i < panel_tabs->get_tab_count(); i++) {
if (panel_tabs->get_tab_control(i) == panel_builds_tab) {
panel_tabs->set_current_tab(i);
editor->make_bottom_panel_item_visible(this);
return;
}
}
ERR_PRINT("Builds tab not found");
}
void MonoBottomPanel::_build_tab_item_selected(int p_idx) {
ERR_FAIL_INDEX(p_idx, build_tabs->get_tab_count());
build_tabs->set_current_tab(p_idx);
}
void MonoBottomPanel::_build_tab_changed(int p_idx) {
if (p_idx < 0 || p_idx >= build_tabs->get_tab_count()) {
warnings_btn->set_visible(false);
errors_btn->set_visible(false);
} else {
warnings_btn->set_visible(true);
errors_btn->set_visible(true);
}
}
void MonoBottomPanel::_warnings_toggled(bool p_pressed) {
int current_tab = build_tabs->get_current_tab();
ERR_FAIL_INDEX(current_tab, build_tabs->get_tab_count());
MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(current_tab));
build_tab->warnings_visible = p_pressed;
build_tab->_update_issues_list();
}
void MonoBottomPanel::_errors_toggled(bool p_pressed) {
int current_tab = build_tabs->get_current_tab();
ERR_FAIL_INDEX(current_tab, build_tabs->get_tab_count());
MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(current_tab));
build_tab->errors_visible = p_pressed;
build_tab->_update_issues_list();
}
void MonoBottomPanel::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
panel_tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles"));
panel_tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles"));
panel_tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles"));
} break;
}
}
void MonoBottomPanel::_bind_methods() {
ClassDB::bind_method(D_METHOD("_warnings_toggled", "pressed"), &MonoBottomPanel::_warnings_toggled);
ClassDB::bind_method(D_METHOD("_errors_toggled", "pressed"), &MonoBottomPanel::_errors_toggled);
ClassDB::bind_method(D_METHOD("_build_tab_item_selected", "idx"), &MonoBottomPanel::_build_tab_item_selected);
ClassDB::bind_method(D_METHOD("_build_tab_changed", "idx"), &MonoBottomPanel::_build_tab_changed);
}
MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) {
singleton = this;
editor = p_editor;
set_v_size_flags(SIZE_EXPAND_FILL);
set_anchors_and_margins_preset(Control::PRESET_WIDE);
panel_tabs = memnew(TabContainer);
panel_tabs->set_tab_align(TabContainer::ALIGN_LEFT);
panel_tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles"));
panel_tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles"));
panel_tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles"));
panel_tabs->set_custom_minimum_size(Size2(0, 228) * EDSCALE);
panel_tabs->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(panel_tabs);
{ // Builds
panel_builds_tab = memnew(VBoxContainer);
panel_builds_tab->set_name(TTR("Builds"));
panel_builds_tab->set_h_size_flags(SIZE_EXPAND_FILL);
panel_tabs->add_child(panel_builds_tab);
HBoxContainer *toolbar_hbc = memnew(HBoxContainer);
toolbar_hbc->set_h_size_flags(SIZE_EXPAND_FILL);
panel_builds_tab->add_child(toolbar_hbc);
toolbar_hbc->add_spacer();
warnings_btn = memnew(ToolButton);
warnings_btn->set_text("Warnings");
warnings_btn->set_toggle_mode(true);
warnings_btn->set_pressed(true);
warnings_btn->set_visible(false);
warnings_btn->set_focus_mode(FOCUS_NONE);
warnings_btn->connect("toggled", this, "_warnings_toggled");
toolbar_hbc->add_child(warnings_btn);
errors_btn = memnew(ToolButton);
errors_btn->set_text("Errors");
errors_btn->set_toggle_mode(true);
errors_btn->set_pressed(true);
errors_btn->set_visible(false);
errors_btn->set_focus_mode(FOCUS_NONE);
errors_btn->connect("toggled", this, "_errors_toggled");
toolbar_hbc->add_child(errors_btn);
HSplitContainer *hsc = memnew(HSplitContainer);
hsc->set_h_size_flags(SIZE_EXPAND_FILL);
hsc->set_v_size_flags(SIZE_EXPAND_FILL);
panel_builds_tab->add_child(hsc);
build_tabs_list = memnew(ItemList);
build_tabs_list->set_h_size_flags(SIZE_EXPAND_FILL);
build_tabs_list->connect("item_selected", this, "_build_tab_item_selected");
hsc->add_child(build_tabs_list);
build_tabs = memnew(TabContainer);
build_tabs->set_tab_align(TabContainer::ALIGN_LEFT);
build_tabs->set_h_size_flags(SIZE_EXPAND_FILL);
build_tabs->set_tabs_visible(false);
build_tabs->connect("tab_changed", this, "_build_tab_changed");
hsc->add_child(build_tabs);
}
}
MonoBottomPanel::~MonoBottomPanel() {
singleton = NULL;
}
void MonoBuildTab::_load_issues_from_file(const String &p_csv_file) {
FileAccessRef f = FileAccess::open(p_csv_file, FileAccess::READ);
if (!f)
return;
while (!f->eof_reached()) {
Vector<String> csv_line = f->get_csv_line();
if (csv_line.size() == 1 && csv_line[0].empty())
return;
ERR_CONTINUE(csv_line.size() != 7);
BuildIssue issue;
issue.warning = csv_line[0] == "warning";
issue.file = csv_line[1];
issue.line = csv_line[2].to_int();
issue.column = csv_line[3].to_int();
issue.code = csv_line[4];
issue.message = csv_line[5];
issue.project_file = csv_line[6];
if (issue.warning)
warning_count += 1;
else
error_count += 1;
issues.push_back(issue);
}
}
void MonoBuildTab::_update_issues_list() {
issues_list->clear();
Ref<Texture> warning_icon = get_icon("Warning", "EditorIcons");
Ref<Texture> error_icon = get_icon("Error", "EditorIcons");
for (int i = 0; i < issues.size(); i++) {
const BuildIssue &issue = issues[i];
if (!(issue.warning ? warnings_visible : errors_visible))
continue;
String tooltip;
tooltip += String("Message: ") + issue.message;
tooltip += String("\nCode: ") + issue.code;
tooltip += String("\nType: ") + (issue.warning ? "warning" : "error");
String text;
if (issue.file.length()) {
String sline = String::num_int64(issue.line);
String scolumn = String::num_int64(issue.column);
text += issue.file + "(";
text += sline + ",";
text += scolumn + "): ";
tooltip += "\nFile: " + issue.file;
tooltip += "\nLine: " + sline;
tooltip += "\nColumn: " + scolumn;
}
if (issue.project_file.length()) {
tooltip += "\nProject: " + issue.project_file;
}
text += issue.message;
int line_break_idx = text.find("\n");
issues_list->add_item(line_break_idx == -1 ? text : text.substr(0, line_break_idx),
issue.warning ? warning_icon : error_icon);
int index = issues_list->get_item_count() - 1;
issues_list->set_item_tooltip(index, tooltip);
issues_list->set_item_metadata(index, i);
}
}
Ref<Texture> MonoBuildTab::get_icon_texture() const {
// FIXME these icons were removed... find something better
if (build_exited) {
if (build_result == RESULT_ERROR) {
return get_icon("DependencyChangedHl", "EditorIcons");
} else {
return get_icon("DependencyOkHl", "EditorIcons");
}
} else {
return get_icon("GraphTime", "EditorIcons");
}
}
MonoBuildInfo MonoBuildTab::get_build_info() {
return build_info;
}
void MonoBuildTab::on_build_start() {
build_exited = false;
issues.clear();
warning_count = 0;
error_count = 0;
_update_issues_list();
MonoBottomPanel::get_singleton()->raise_build_tab(this);
}
void MonoBuildTab::on_build_exit(BuildResult result) {
build_exited = true;
build_result = result;
_load_issues_from_file(logs_dir.plus_file("msbuild_issues.csv"));
_update_issues_list();
MonoBottomPanel::get_singleton()->raise_build_tab(this);
}
void MonoBuildTab::on_build_exec_failed(const String &p_cause, const String &p_detailed) {
build_exited = true;
build_result = RESULT_ERROR;
issues_list->clear();
String tooltip;
tooltip += "Message: " + (p_detailed.length() ? p_detailed : p_cause);
tooltip += "\nType: error";
int line_break_idx = p_cause.find("\n");
issues_list->add_item(line_break_idx == -1 ? p_cause : p_cause.substr(0, line_break_idx),
get_icon("Error", "EditorIcons"));
int index = issues_list->get_item_count() - 1;
issues_list->set_item_tooltip(index, tooltip);
MonoBottomPanel::get_singleton()->raise_build_tab(this);
}
void MonoBuildTab::restart_build() {
ERR_FAIL_COND(!build_exited);
GodotSharpBuilds::get_singleton()->restart_build(this);
}
void MonoBuildTab::stop_build() {
ERR_FAIL_COND(build_exited);
GodotSharpBuilds::get_singleton()->stop_build(this);
}
void MonoBuildTab::_issue_activated(int p_idx) {
ERR_FAIL_INDEX(p_idx, issues.size());
const BuildIssue &issue = issues[p_idx];
if (issue.project_file.empty() && issue.file.empty())
return;
String project_dir = issue.project_file.length() ? issue.project_file.get_base_dir() : build_info.solution.get_base_dir();
String file = project_dir.simplify_path().plus_file(issue.file.simplify_path());
if (!FileAccess::exists(file))
return;
file = ProjectSettings::get_singleton()->localize_path(file);
if (file.begins_with("res://")) {
Ref<Script> script = ResourceLoader::load(file, CSharpLanguage::get_singleton()->get_type());
if (script.is_valid() && ScriptEditor::get_singleton()->edit(script, issue.line, issue.column)) {
EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT);
}
}
}
void MonoBuildTab::_bind_methods() {
ClassDB::bind_method("_issue_activated", &MonoBuildTab::_issue_activated);
}
MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) {
build_info = p_build_info;
logs_dir = p_logs_dir;
build_exited = false;
issues_list = memnew(ItemList);
issues_list->set_v_size_flags(SIZE_EXPAND_FILL);
issues_list->connect("item_activated", this, "_issue_activated");
add_child(issues_list);
error_count = 0;
warning_count = 0;
errors_visible = true;
warnings_visible = true;
}

View file

@ -0,0 +1,145 @@
/*************************************************************************/
/* mono_bottom_panel.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef MONO_BOTTOM_PANEL_H
#define MONO_BOTTOM_PANEL_H
#include "editor/editor_node.h"
#include "scene/gui/control.h"
#include "mono_build_info.h"
class MonoBuildTab;
class MonoBottomPanel : public VBoxContainer {
GDCLASS(MonoBottomPanel, VBoxContainer)
EditorNode *editor;
TabContainer *panel_tabs;
VBoxContainer *panel_builds_tab;
ItemList *build_tabs_list;
TabContainer *build_tabs;
Button *warnings_btn;
Button *errors_btn;
void _update_build_tabs_list();
void _build_tab_item_selected(int p_idx);
void _build_tab_changed(int p_idx);
void _warnings_toggled(bool p_pressed);
void _errors_toggled(bool p_pressed);
static MonoBottomPanel *singleton;
protected:
void _notification(int p_what);
static void _bind_methods();
public:
_FORCE_INLINE_ static MonoBottomPanel *get_singleton() { return singleton; }
void add_build_tab(MonoBuildTab *p_build_tab);
void raise_build_tab(MonoBuildTab *p_build_tab);
void show_build_tab();
MonoBottomPanel(EditorNode *p_editor = NULL);
~MonoBottomPanel();
};
class MonoBuildTab : public VBoxContainer {
GDCLASS(MonoBuildTab, VBoxContainer)
public:
enum BuildResult {
RESULT_ERROR,
RESULT_SUCCESS
};
struct BuildIssue {
bool warning;
String file;
int line;
int column;
String code;
String message;
String project_file;
};
private:
friend class MonoBottomPanel;
bool build_exited;
BuildResult build_result;
Vector<BuildIssue> issues;
ItemList *issues_list;
int error_count;
int warning_count;
bool errors_visible;
bool warnings_visible;
String logs_dir;
MonoBuildInfo build_info;
void _load_issues_from_file(const String &p_csv_file);
void _update_issues_list();
void _issue_activated(int p_idx);
protected:
static void _bind_methods();
public:
Ref<Texture> get_icon_texture() const;
MonoBuildInfo get_build_info();
void on_build_start();
void on_build_exit(BuildResult result);
void on_build_exec_failed(const String &p_cause, const String &p_detailed = String());
void restart_build();
void stop_build();
MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir);
};
#endif // MONO_BOTTOM_PANEL_H

View file

@ -0,0 +1,64 @@
/*************************************************************************/
/* mono_build_info.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef MONO_BUILD_INFO_H
#define MONO_BUILD_INFO_H
#include "../mono_gd/gd_mono_utils.h"
struct MonoBuildInfo {
struct Hasher {
static _FORCE_INLINE_ uint32_t hash(const MonoBuildInfo &p_key) {
uint32_t hash = 0;
GDMonoUtils::hash_combine(hash, p_key.solution.hash());
GDMonoUtils::hash_combine(hash, p_key.configuration.hash());
return hash;
}
};
String solution;
String configuration;
Vector<String> custom_props;
MonoBuildInfo() {}
MonoBuildInfo(const String &p_solution, const String &p_config) {
solution = p_solution;
configuration = p_config;
}
bool operator==(const MonoBuildInfo &p_b) const {
return p_b.solution == solution && p_b.configuration == configuration;
}
};
#endif // MONO_BUILD_INFO_H

View file

@ -0,0 +1,81 @@
/*************************************************************************/
/* monodevelop_instance.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "monodevelop_instance.h"
#include "../mono_gd/gd_mono.h"
#include "../mono_gd/gd_mono_class.h"
void MonoDevelopInstance::execute(const Vector<String> &p_files) {
ERR_FAIL_NULL(execute_method);
ERR_FAIL_COND(gc_handle.is_null());
MonoObject *ex = NULL;
Variant files = p_files;
const Variant *args[1] = { &files };
execute_method->invoke(gc_handle->get_target(), args, &ex);
if (ex) {
mono_print_unhandled_exception(ex);
ERR_FAIL();
}
}
void MonoDevelopInstance::execute(const String &p_file) {
Vector<String> files;
files.push_back(p_file);
execute(files);
}
MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) {
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Editor", "MonoDevelopInstance");
MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_raw());
GDMonoMethod *ctor = klass->get_method(".ctor", 1);
MonoObject *ex = NULL;
Variant solution = p_solution;
const Variant *args[1] = { &solution };
ctor->invoke(obj, args, &ex);
if (ex) {
mono_print_unhandled_exception(ex);
ERR_FAIL();
}
gc_handle = MonoGCHandle::create_strong(obj);
execute_method = klass->get_method("Execute", 1);
}

View file

@ -0,0 +1,50 @@
/*************************************************************************/
/* monodevelop_instance.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef MONODEVELOP_INSTANCE_H
#define MONODEVELOP_INSTANCE_H
#include "reference.h"
#include "../mono_gc_handle.h"
#include "../mono_gd/gd_mono_method.h"
class MonoDevelopInstance {
Ref<MonoGCHandle> gc_handle;
GDMonoMethod *execute_method;
public:
void execute(const Vector<String> &p_files);
void execute(const String &p_files);
MonoDevelopInstance(const String &p_solution);
};
#endif // MONODEVELOP_INSTANCE_H

View file

@ -0,0 +1,130 @@
/*************************************************************************/
/* net_solution.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "net_solution.h"
#include "os/dir_access.h"
#include "os/file_access.h"
#include "../utils/path_utils.h"
#include "../utils/string_utils.h"
#include "csharp_project.h"
#define SOLUTION_TEMPLATE \
"Microsoft Visual Studio Solution File, Format Version 12.00\n" \
"# Visual Studio 2012\n" \
"%0\n" \
"Global\n" \
"\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n" \
"%1\n" \
"\tEndGlobalSection\n" \
"\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n" \
"%2\n" \
"\tEndGlobalSection\n" \
"EndGlobal\n"
#define PROJECT_DECLARATION "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"%0\", \"%1\", \"{%2}\"\nEndProject"
#define SOLUTION_PLATFORMS_CONFIG "\t\%0|Any CPU = %0|Any CPU"
#define PROJECT_PLATFORMS_CONFIG \
"\t\t{%0}.%1|Any CPU.ActiveCfg = %1|Any CPU\n" \
"\t\t{%0}.%1|Any CPU.Build.0 = %1|Any CPU"
void NETSolution::add_new_project(const String &p_name, const String &p_guid, const Vector<String> &p_extra_configs) {
if (projects.has(p_name))
WARN_PRINT("Overriding existing project.");
ProjectInfo procinfo;
procinfo.guid = p_guid;
procinfo.configs.push_back("Debug");
procinfo.configs.push_back("Release");
for (int i = 0; i < p_extra_configs.size(); i++) {
procinfo.configs.push_back(p_extra_configs[i]);
}
projects[p_name] = procinfo;
}
Error NETSolution::save() {
bool dir_exists = DirAccess::exists(path);
ERR_EXPLAIN("The directory does not exist.");
ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH);
String projs_decl;
String sln_platform_cfg;
String proj_platform_cfg;
for (Map<String, ProjectInfo>::Element *E = projects.front(); E; E = E->next()) {
const String &name = E->key();
const ProjectInfo &procinfo = E->value();
projs_decl += sformat(PROJECT_DECLARATION, name, name + ".csproj", procinfo.guid);
for (int i = 0; i < procinfo.configs.size(); i++) {
const String &config = procinfo.configs[i];
if (i != 0) {
sln_platform_cfg += "\n";
proj_platform_cfg += "\n";
}
sln_platform_cfg += sformat(SOLUTION_PLATFORMS_CONFIG, config);
proj_platform_cfg += sformat(PROJECT_PLATFORMS_CONFIG, procinfo.guid, config);
}
}
String content = sformat(SOLUTION_TEMPLATE, projs_decl, sln_platform_cfg, proj_platform_cfg);
FileAccessRef file = FileAccess::open(path_join(path, name + ".sln"), FileAccess::WRITE);
ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE);
file->store_string(content);
file->close();
return OK;
}
bool NETSolution::set_path(const String &p_existing_path) {
if (p_existing_path.is_abs_path()) {
path = p_existing_path;
} else {
String abspath;
if (!rel_path_to_abs(p_existing_path, abspath))
return false;
path = abspath;
}
return true;
}
NETSolution::NETSolution(const String &p_name) {
name = p_name;
}

View file

@ -0,0 +1,57 @@
/*************************************************************************/
/* net_solution.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef NET_SOLUTION_H
#define NET_SOLUTION_H
#include "map.h"
#include "ustring.h"
struct NETSolution {
String name;
void add_new_project(const String &p_name, const String &p_guid, const Vector<String> &p_extra_configs = Vector<String>());
Error save();
bool set_path(const String &p_existing_path);
NETSolution(const String &p_name);
private:
struct ProjectInfo {
String guid;
Vector<String> configs;
};
String path;
Map<String, ProjectInfo> projects;
};
#endif // NET_SOLUTION_H

View file

@ -0,0 +1,475 @@
using System;
using System.Runtime.InteropServices;
namespace Godot
{
[StructLayout(LayoutKind.Sequential)]
public struct Basis : IEquatable<Basis>
{
private static readonly Basis identity = new Basis
(
new Vector3(1f, 0f, 0f),
new Vector3(0f, 1f, 0f),
new Vector3(0f, 0f, 1f)
);
private static readonly Basis[] orthoBases = new Basis[24]
{
new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f),
new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f),
new Basis(-1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f),
new Basis(0f, 1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f),
new Basis(1f, 0f, 0f, 0f, 0f, -1f, 0f, 1f, 0f),
new Basis(0f, 0f, 1f, 1f, 0f, 0f, 0f, 1f, 0f),
new Basis(-1f, 0f, 0f, 0f, 0f, 1f, 0f, 1f, 0f),
new Basis(0f, 0f, -1f, -1f, 0f, 0f, 0f, 1f, 0f),
new Basis(1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, -1f),
new Basis(0f, 1f, 0f, 1f, 0f, 0f, 0f, 0f, -1f),
new Basis(-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, -1f),
new Basis(0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, -1f),
new Basis(1f, 0f, 0f, 0f, 0f, 1f, 0f, -1f, 0f),
new Basis(0f, 0f, -1f, 1f, 0f, 0f, 0f, -1f, 0f),
new Basis(-1f, 0f, 0f, 0f, 0f, -1f, 0f, -1f, 0f),
new Basis(0f, 0f, 1f, -1f, 0f, 0f, 0f, -1f, 0f),
new Basis(0f, 0f, 1f, 0f, 1f, 0f, -1f, 0f, 0f),
new Basis(0f, -1f, 0f, 0f, 0f, 1f, -1f, 0f, 0f),
new Basis(0f, 0f, -1f, 0f, -1f, 0f, -1f, 0f, 0f),
new Basis(0f, 1f, 0f, 0f, 0f, -1f, -1f, 0f, 0f),
new Basis(0f, 0f, 1f, 0f, -1f, 0f, 1f, 0f, 0f),
new Basis(0f, 1f, 0f, 0f, 0f, 1f, 1f, 0f, 0f),
new Basis(0f, 0f, -1f, 0f, 1f, 0f, 1f, 0f, 0f),
new Basis(0f, -1f, 0f, 0f, 0f, -1f, 1f, 0f, 0f)
};
public Vector3 x;
public Vector3 y;
public Vector3 z;
public static Basis Identity
{
get { return identity; }
}
public Vector3 Scale
{
get
{
return new Vector3
(
new Vector3(this[0, 0], this[1, 0], this[2, 0]).length(),
new Vector3(this[0, 1], this[1, 1], this[2, 1]).length(),
new Vector3(this[0, 2], this[1, 2], this[2, 2]).length()
);
}
}
public Vector3 this[int index]
{
get
{
switch (index)
{
case 0:
return x;
case 1:
return y;
case 2:
return z;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
x = value;
return;
case 1:
y = value;
return;
case 2:
z = value;
return;
default:
throw new IndexOutOfRangeException();
}
}
}
public float this[int index, int axis]
{
get
{
switch (index)
{
case 0:
return x[axis];
case 1:
return y[axis];
case 2:
return z[axis];
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
x[axis] = value;
return;
case 1:
y[axis] = value;
return;
case 2:
z[axis] = value;
return;
default:
throw new IndexOutOfRangeException();
}
}
}
internal static Basis create_from_axes(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
{
return new Basis
(
new Vector3(xAxis.x, yAxis.x, zAxis.x),
new Vector3(xAxis.y, yAxis.y, zAxis.y),
new Vector3(xAxis.z, yAxis.z, zAxis.z)
);
}
public float determinant()
{
return this[0, 0] * (this[1, 1] * this[2, 2] - this[2, 1] * this[1, 2]) -
this[1, 0] * (this[0, 1] * this[2, 2] - this[2, 1] * this[0, 2]) +
this[2, 0] * (this[0, 1] * this[1, 2] - this[1, 1] * this[0, 2]);
}
public Vector3 get_axis(int axis)
{
return new Vector3(this[0, axis], this[1, axis], this[2, axis]);
}
public Vector3 get_euler()
{
Basis m = this.orthonormalized();
Vector3 euler;
euler.y = Mathf.asin(m.x[2]);
if (euler.y < Mathf.PI * 0.5f)
{
if (euler.y > -Mathf.PI * 0.5f)
{
euler.x = Mathf.atan2(-m.y[2], m.z[2]);
euler.z = Mathf.atan2(-m.x[1], m.x[0]);
}
else
{
euler.z = 0.0f;
euler.x = euler.z - Mathf.atan2(m.y[0], m.y[1]);
}
}
else
{
euler.z = 0f;
euler.x = Mathf.atan2(m.x[1], m.y[1]) - euler.z;
}
return euler;
}
public int get_orthogonal_index()
{
Basis orth = this;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
float v = orth[i, j];
if (v > 0.5f)
v = 1.0f;
else if (v < -0.5f)
v = -1.0f;
else
v = 0f;
orth[i, j] = v;
}
}
for (int i = 0; i < 24; i++)
{
if (orthoBases[i] == orth)
return i;
}
return 0;
}
public Basis inverse()
{
Basis inv = this;
float[] co = new float[3]
{
inv[1, 1] * inv[2, 2] - inv[1, 2] * inv[2, 1],
inv[1, 2] * inv[2, 0] - inv[1, 0] * inv[2, 2],
inv[1, 0] * inv[2, 1] - inv[1, 1] * inv[2, 0]
};
float det = inv[0, 0] * co[0] + inv[0, 1] * co[1] + inv[0, 2] * co[2];
if (det == 0)
{
return new Basis
(
float.NaN, float.NaN, float.NaN,
float.NaN, float.NaN, float.NaN,
float.NaN, float.NaN, float.NaN
);
}
float s = 1.0f / det;
inv = new Basis
(
co[0] * s,
inv[0, 2] * inv[2, 1] - inv[0, 1] * inv[2, 2] * s,
inv[0, 1] * inv[1, 2] - inv[0, 2] * inv[1, 1] * s,
co[1] * s,
inv[0, 0] * inv[2, 2] - inv[0, 2] * inv[2, 0] * s,
inv[0, 2] * inv[1, 0] - inv[0, 0] * inv[1, 2] * s,
co[2] * s,
inv[0, 1] * inv[2, 0] - inv[0, 0] * inv[2, 1] * s,
inv[0, 0] * inv[1, 1] - inv[0, 1] * inv[1, 0] * s
);
return inv;
}
public Basis orthonormalized()
{
Vector3 xAxis = get_axis(0);
Vector3 yAxis = get_axis(1);
Vector3 zAxis = get_axis(2);
xAxis.normalize();
yAxis = (yAxis - xAxis * (xAxis.dot(yAxis)));
yAxis.normalize();
zAxis = (zAxis - xAxis * (xAxis.dot(zAxis)) - yAxis * (yAxis.dot(zAxis)));
zAxis.normalize();
return Basis.create_from_axes(xAxis, yAxis, zAxis);
}
public Basis rotated(Vector3 axis, float phi)
{
return this * new Basis(axis, phi);
}
public Basis scaled(Vector3 scale)
{
Basis m = this;
m[0, 0] *= scale.x;
m[1, 0] *= scale.x;
m[2, 0] *= scale.x;
m[0, 1] *= scale.y;
m[1, 1] *= scale.y;
m[2, 1] *= scale.y;
m[0, 2] *= scale.z;
m[1, 2] *= scale.z;
m[2, 2] *= scale.z;
return m;
}
public float tdotx(Vector3 with)
{
return this[0, 0] * with[0] + this[1, 0] * with[1] + this[2, 0] * with[2];
}
public float tdoty(Vector3 with)
{
return this[0, 1] * with[0] + this[1, 1] * with[1] + this[2, 1] * with[2];
}
public float tdotz(Vector3 with)
{
return this[0, 2] * with[0] + this[1, 2] * with[1] + this[2, 2] * with[2];
}
public Basis transposed()
{
Basis tr = this;
float temp = this[0, 1];
this[0, 1] = this[1, 0];
this[1, 0] = temp;
temp = this[0, 2];
this[0, 2] = this[2, 0];
this[2, 0] = temp;
temp = this[1, 2];
this[1, 2] = this[2, 1];
this[2, 1] = temp;
return tr;
}
public Vector3 xform(Vector3 v)
{
return new Vector3
(
this[0].dot(v),
this[1].dot(v),
this[2].dot(v)
);
}
public Vector3 xform_inv(Vector3 v)
{
return new Vector3
(
(this[0, 0] * v.x) + (this[1, 0] * v.y) + (this[2, 0] * v.z),
(this[0, 1] * v.x) + (this[1, 1] * v.y) + (this[2, 1] * v.z),
(this[0, 2] * v.x) + (this[1, 2] * v.y) + (this[2, 2] * v.z)
);
}
public Basis(Quat quat)
{
float s = 2.0f / quat.length_squared();
float xs = quat.x * s;
float ys = quat.y * s;
float zs = quat.z * s;
float wx = quat.w * xs;
float wy = quat.w * ys;
float wz = quat.w * zs;
float xx = quat.x * xs;
float xy = quat.x * ys;
float xz = quat.x * zs;
float yy = quat.y * ys;
float yz = quat.y * zs;
float zz = quat.z * zs;
this.x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy);
this.y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx);
this.z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy));
}
public Basis(Vector3 axis, float phi)
{
Vector3 axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
float cosine = Mathf.cos(phi);
float sine = Mathf.sin(phi);
this.x = new Vector3
(
axis_sq.x + cosine * (1.0f - axis_sq.x),
axis.x * axis.y * (1.0f - cosine) - axis.z * sine,
axis.z * axis.x * (1.0f - cosine) + axis.y * sine
);
this.y = new Vector3
(
axis.x * axis.y * (1.0f - cosine) + axis.z * sine,
axis_sq.y + cosine * (1.0f - axis_sq.y),
axis.y * axis.z * (1.0f - cosine) - axis.x * sine
);
this.z = new Vector3
(
axis.z * axis.x * (1.0f - cosine) - axis.y * sine,
axis.y * axis.z * (1.0f - cosine) + axis.x * sine,
axis_sq.z + cosine * (1.0f - axis_sq.z)
);
}
public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
{
this.x = xAxis;
this.y = yAxis;
this.z = zAxis;
}
public Basis(float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz)
{
this.x = new Vector3(xx, xy, xz);
this.y = new Vector3(yx, yy, yz);
this.z = new Vector3(zx, zy, zz);
}
public static Basis operator *(Basis left, Basis right)
{
return new Basis
(
right.tdotx(left[0]), right.tdoty(left[0]), right.tdotz(left[0]),
right.tdotx(left[1]), right.tdoty(left[1]), right.tdotz(left[1]),
right.tdotx(left[2]), right.tdoty(left[2]), right.tdotz(left[2])
);
}
public static bool operator ==(Basis left, Basis right)
{
return left.Equals(right);
}
public static bool operator !=(Basis left, Basis right)
{
return !left.Equals(right);
}
public override bool Equals(object obj)
{
if (obj is Basis)
{
return Equals((Basis)obj);
}
return false;
}
public bool Equals(Basis other)
{
return x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z);
}
public override int GetHashCode()
{
return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1}, {2})", new object[]
{
this.x.ToString(),
this.y.ToString(),
this.z.ToString()
});
}
public string ToString(string format)
{
return String.Format("({0}, {1}, {2})", new object[]
{
this.x.ToString(format),
this.y.ToString(format),
this.z.ToString(format)
});
}
}
}

View file

@ -0,0 +1,590 @@
using System;
namespace Godot
{
public struct Color : IEquatable<Color>
{
public float r;
public float g;
public float b;
public float a;
public int r8
{
get
{
return (int)(r * 255.0f);
}
}
public int g8
{
get
{
return (int)(g * 255.0f);
}
}
public int b8
{
get
{
return (int)(b * 255.0f);
}
}
public int a8
{
get
{
return (int)(a * 255.0f);
}
}
public float h
{
get
{
float max = Mathf.max(r, Mathf.max(g, b));
float min = Mathf.min(r, Mathf.min(g, b));
float delta = max - min;
if (delta == 0)
return 0;
float h;
if (r == max)
h = (g - b) / delta; // Between yellow & magenta
else if (g == max)
h = 2 + (b - r) / delta; // Between cyan & yellow
else
h = 4 + (r - g) / delta; // Between magenta & cyan
h /= 6.0f;
if (h < 0)
h += 1.0f;
return h;
}
set
{
this = from_hsv(value, s, v);
}
}
public float s
{
get
{
float max = Mathf.max(r, Mathf.max(g, b));
float min = Mathf.min(r, Mathf.min(g, b));
float delta = max - min;
return max != 0 ? delta / max : 0;
}
set
{
this = from_hsv(h, value, v);
}
}
public float v
{
get
{
return Mathf.max(r, Mathf.max(g, b));
}
set
{
this = from_hsv(h, s, value);
}
}
private static readonly Color black = new Color(0f, 0f, 0f, 1.0f);
public Color Black
{
get
{
return black;
}
}
public float this [int index]
{
get
{
switch (index)
{
case 0:
return r;
case 1:
return g;
case 2:
return b;
case 3:
return a;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
r = value;
return;
case 1:
g = value;
return;
case 2:
b = value;
return;
case 3:
a = value;
return;
default:
throw new IndexOutOfRangeException();
}
}
}
public static void to_hsv(Color color, out float hue, out float saturation, out float value)
{
int max = Mathf.max(color.r8, Mathf.max(color.g8, color.b8));
int min = Mathf.min(color.r8, Mathf.min(color.g8, color.b8));
float delta = max - min;
if (delta == 0)
{
hue = 0;
}
else
{
if (color.r == max)
hue = (color.g - color.b) / delta; // Between yellow & magenta
else if (color.g == max)
hue = 2 + (color.b - color.r) / delta; // Between cyan & yellow
else
hue = 4 + (color.r - color.g) / delta; // Between magenta & cyan
hue /= 6.0f;
if (hue < 0)
hue += 1.0f;
}
saturation = (max == 0) ? 0 : 1f - (1f * min / max);
value = max / 255f;
}
public static Color from_hsv(float hue, float saturation, float value, float alpha = 1.0f)
{
if (saturation == 0)
{
// acp_hromatic (grey)
return new Color(value, value, value, alpha);
}
int i;
float f, p, q, t;
hue *= 6.0f;
hue %= 6f;
i = (int)hue;
f = hue - i;
p = value * (1 - saturation);
q = value * (1 - saturation * f);
t = value * (1 - saturation * (1 - f));
switch (i)
{
case 0: // Red is the dominant color
return new Color(value, t, p, alpha);
case 1: // Green is the dominant color
return new Color(q, value, p, alpha);
case 2:
return new Color(p, value, t, alpha);
case 3: // Blue is the dominant color
return new Color(p, q, value, alpha);
case 4:
return new Color(t, p, value, alpha);
default: // (5) Red is the dominant color
return new Color(value, p, q, alpha);
}
}
public Color blend(Color over)
{
Color res;
float sa = 1.0f - over.a;
res.a = a * sa + over.a;
if (res.a == 0)
{
return new Color(0, 0, 0, 0);
}
else
{
res.r = (r * a * sa + over.r * over.a) / res.a;
res.g = (g * a * sa + over.g * over.a) / res.a;
res.b = (b * a * sa + over.b * over.a) / res.a;
}
return res;
}
public Color contrasted()
{
return new Color(
(r + 0.5f) % 1.0f,
(g + 0.5f) % 1.0f,
(b + 0.5f) % 1.0f
);
}
public float gray()
{
return (r + g + b) / 3.0f;
}
public Color inverted()
{
return new Color(
1.0f - r,
1.0f - g,
1.0f - b
);
}
public Color linear_interpolate(Color b, float t)
{
Color res = this;
res.r += (t * (b.r - this.r));
res.g += (t * (b.g - this.g));
res.b += (t * (b.b - this.b));
res.a += (t * (b.a - this.a));
return res;
}
public int to_32()
{
int c = (byte)(a * 255);
c <<= 8;
c |= (byte)(r * 255);
c <<= 8;
c |= (byte)(g * 255);
c <<= 8;
c |= (byte)(b * 255);
return c;
}
public int to_ARGB32()
{
int c = (byte)(a * 255);
c <<= 8;
c |= (byte)(r * 255);
c <<= 8;
c |= (byte)(g * 255);
c <<= 8;
c |= (byte)(b * 255);
return c;
}
public string to_html(bool include_alpha = true)
{
String txt = string.Empty;
txt += _to_hex(r);
txt += _to_hex(g);
txt += _to_hex(b);
if (include_alpha)
txt = _to_hex(a) + txt;
return txt;
}
public Color(float r, float g, float b, float a = 1.0f)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
public Color(int rgba)
{
this.a = (rgba & 0xFF) / 255.0f;
rgba >>= 8;
this.b = (rgba & 0xFF) / 255.0f;
rgba >>= 8;
this.g = (rgba & 0xFF) / 255.0f;
rgba >>= 8;
this.r = (rgba & 0xFF) / 255.0f;
}
private static float _parse_col(string str, int ofs)
{
int ig = 0;
for (int i = 0; i < 2; i++)
{
int c = str[i + ofs];
int v = 0;
if (c >= '0' && c <= '9')
{
v = c - '0';
}
else if (c >= 'a' && c <= 'f')
{
v = c - 'a';
v += 10;
}
else if (c >= 'A' && c <= 'F')
{
v = c - 'A';
v += 10;
}
else
{
return -1;
}
if (i == 0)
ig += v * 16;
else
ig += v;
}
return ig;
}
private String _to_hex(float val)
{
int v = (int)Mathf.clamp(val * 255.0f, 0, 255);
string ret = string.Empty;
for (int i = 0; i < 2; i++)
{
char[] c = { (char)0, (char)0 };
int lv = v & 0xF;
if (lv < 10)
c[0] = (char)('0' + lv);
else
c[0] = (char)('a' + lv - 10);
v >>= 4;
ret = c + ret;
}
return ret;
}
internal static bool html_is_valid(string color)
{
if (color.Length == 0)
return false;
if (color[0] == '#')
color = color.Substring(1, color.Length - 1);
bool alpha = false;
if (color.Length == 8)
alpha = true;
else if (color.Length == 6)
alpha = false;
else
return false;
if (alpha)
{
if ((int)_parse_col(color, 0) < 0)
return false;
}
int from = alpha ? 2 : 0;
if ((int)_parse_col(color, from + 0) < 0)
return false;
if ((int)_parse_col(color, from + 2) < 0)
return false;
if ((int)_parse_col(color, from + 4) < 0)
return false;
return true;
}
public static Color Color8(byte r8, byte g8, byte b8, byte a8)
{
return new Color((float)r8 / 255f, (float)g8 / 255f, (float)b8 / 255f, (float)a8 / 255f);
}
public Color(string rgba)
{
if (rgba.Length == 0)
{
r = 0f;
g = 0f;
b = 0f;
a = 1.0f;
return;
}
if (rgba[0] == '#')
rgba = rgba.Substring(1);
bool alpha = false;
if (rgba.Length == 8)
{
alpha = true;
}
else if (rgba.Length == 6)
{
alpha = false;
}
else
{
throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba);
}
if (alpha)
{
a = _parse_col(rgba, 0);
if (a < 0)
throw new ArgumentOutOfRangeException("Invalid color code. Alpha is " + a + " but zero or greater is expected: " + rgba);
}
else
{
a = 1.0f;
}
int from = alpha ? 2 : 0;
r = _parse_col(rgba, from + 0);
if (r < 0)
throw new ArgumentOutOfRangeException("Invalid color code. Red is " + r + " but zero or greater is expected: " + rgba);
g = _parse_col(rgba, from + 2);
if (g < 0)
throw new ArgumentOutOfRangeException("Invalid color code. Green is " + g + " but zero or greater is expected: " + rgba);
b = _parse_col(rgba, from + 4);
if (b < 0)
throw new ArgumentOutOfRangeException("Invalid color code. Blue is " + b + " but zero or greater is expected: " + rgba);
}
public static bool operator ==(Color left, Color right)
{
return left.Equals(right);
}
public static bool operator !=(Color left, Color right)
{
return !left.Equals(right);
}
public static bool operator <(Color left, Color right)
{
if (left.r == right.r)
{
if (left.g == right.g)
{
if (left.b == right.b)
return (left.a < right.a);
else
return (left.b < right.b);
}
else
{
return left.g < right.g;
}
}
return left.r < right.r;
}
public static bool operator >(Color left, Color right)
{
if (left.r == right.r)
{
if (left.g == right.g)
{
if (left.b == right.b)
return (left.a > right.a);
else
return (left.b > right.b);
}
else
{
return left.g > right.g;
}
}
return left.r > right.r;
}
public override bool Equals(object obj)
{
if (obj is Color)
{
return Equals((Color)obj);
}
return false;
}
public bool Equals(Color other)
{
return r == other.r && g == other.g && b == other.b && a == other.a;
}
public override int GetHashCode()
{
return r.GetHashCode() ^ g.GetHashCode() ^ b.GetHashCode() ^ a.GetHashCode();
}
public override string ToString()
{
return String.Format("{0},{1},{2},{3}", new object[]
{
this.r.ToString(),
this.g.ToString(),
this.b.ToString(),
this.a.ToString()
});
}
public string ToString(string format)
{
return String.Format("{0},{1},{2},{3}", new object[]
{
this.r.ToString(format),
this.g.ToString(format),
this.b.ToString(format),
this.a.ToString(format)
});
}
}
}

View file

@ -0,0 +1,48 @@
namespace Godot
{
public enum Error : int
{
OK = 0,
FAILED = 1,
ERR_UNAVAILABLE = 2,
ERR_UNCONFIGURED = 3,
ERR_UNAUTHORIZED = 4,
ERR_PARAMETER_RANGE_ERROR = 5,
ERR_OUT_OF_MEMORY = 6,
ERR_FILE_NOT_FOUND = 7,
ERR_FILE_BAD_DRIVE = 8,
ERR_FILE_BAD_PATH = 9,
ERR_FILE_NO_PERMISSION = 10,
ERR_FILE_ALREADY_IN_USE = 11,
ERR_FILE_CANT_OPEN = 12,
ERR_FILE_CANT_WRITE = 13,
ERR_FILE_CANT_READ = 14,
ERR_FILE_UNRECOGNIZED = 15,
ERR_FILE_CORRUPT = 16,
ERR_FILE_MISSING_DEPENDENCIES = 17,
ERR_FILE_EOF = 18,
ERR_CANT_OPEN = 19,
ERR_CANT_CREATE = 20,
ERR_PARSE_ERROR = 43,
ERROR_QUERY_FAILED = 21,
ERR_ALREADY_IN_USE = 22,
ERR_LOCKED = 23,
ERR_TIMEOUT = 24,
ERR_CANT_AQUIRE_RESOURCE = 28,
ERR_INVALID_DATA = 30,
ERR_INVALID_PARAMETER = 31,
ERR_ALREADY_EXISTS = 32,
ERR_DOES_NOT_EXIST = 33,
ERR_DATABASE_CANT_READ = 34,
ERR_DATABASE_CANT_WRITE = 35,
ERR_COMPILATION_FAILED = 36,
ERR_METHOD_NOT_FOUND = 37,
ERR_LINK_FAILED = 38,
ERR_SCRIPT_FAILED = 39,
ERR_CYCLIC_LINK = 40,
ERR_BUSY = 44,
ERR_HELP = 46,
ERR_BUG = 47,
ERR_WTF = 49
}
}

View file

@ -0,0 +1,19 @@
using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ExportAttribute : Attribute
{
private int hint;
private string hint_string;
private int usage;
public ExportAttribute(int hint = GD.PROPERTY_HINT_NONE, string hint_string = "", int usage = GD.PROPERTY_USAGE_DEFAULT)
{
this.hint = hint;
this.hint_string = hint_string;
this.usage = usage;
}
}
}

View file

@ -0,0 +1,191 @@
using System;
namespace Godot
{
public static class GD
{
/*{GodotGlobalConstants}*/
public static object bytes2var(byte[] bytes)
{
return NativeCalls.godot_icall_Godot_bytes2var(bytes);
}
public static object convert(object what, int type)
{
return NativeCalls.godot_icall_Godot_convert(what, type);
}
public static float db2linear(float db)
{
return (float)Math.Exp(db * 0.11512925464970228420089957273422);
}
public static float dectime(float value, float amount, float step)
{
float sgn = value < 0 ? -1.0f : 1.0f;
float val = Mathf.abs(value);
val -= amount * step;
if (val < 0.0f)
val = 0.0f;
return val * sgn;
}
public static FuncRef funcref(Object instance, string funcname)
{
var ret = new FuncRef();
ret.SetInstance(instance);
ret.SetFunction(funcname);
return ret;
}
public static int hash(object var)
{
return NativeCalls.godot_icall_Godot_hash(var);
}
public static Object instance_from_id(int instance_id)
{
return NativeCalls.godot_icall_Godot_instance_from_id(instance_id);
}
public static double linear2db(double linear)
{
return Math.Log(linear) * 8.6858896380650365530225783783321;
}
public static Resource load(string path)
{
return ResourceLoader.Load(path);
}
public static void print(params object[] what)
{
NativeCalls.godot_icall_Godot_print(what);
}
public static void print_stack()
{
print(System.Environment.StackTrace);
}
public static void printerr(params object[] what)
{
NativeCalls.godot_icall_Godot_printerr(what);
}
public static void printraw(params object[] what)
{
NativeCalls.godot_icall_Godot_printraw(what);
}
public static void prints(params object[] what)
{
NativeCalls.godot_icall_Godot_prints(what);
}
public static void printt(params object[] what)
{
NativeCalls.godot_icall_Godot_printt(what);
}
public static int[] range(int length)
{
int[] ret = new int[length];
for (int i = 0; i < length; i++)
{
ret[i] = i;
}
return ret;
}
public static int[] range(int from, int to)
{
if (to < from)
return new int[0];
int[] ret = new int[to - from];
for (int i = from; i < to; i++)
{
ret[i - from] = i;
}
return ret;
}
public static int[] range(int from, int to, int increment)
{
if (to < from && increment > 0)
return new int[0];
if (to > from && increment < 0)
return new int[0];
// Calculate count
int count = 0;
if (increment > 0)
count = ((to - from - 1) / increment) + 1;
else
count = ((from - to - 1) / -increment) + 1;
int[] ret = new int[count];
if (increment > 0)
{
int idx = 0;
for (int i = from; i < to; i += increment)
{
ret[idx++] = i;
}
}
else
{
int idx = 0;
for (int i = from; i > to; i += increment)
{
ret[idx++] = i;
}
}
return ret;
}
public static void seed(int seed)
{
NativeCalls.godot_icall_Godot_seed(seed);
}
public static string str(params object[] what)
{
return NativeCalls.godot_icall_Godot_str(what);
}
public static object str2var(string str)
{
return NativeCalls.godot_icall_Godot_str2var(str);
}
public static bool type_exists(string type)
{
return NativeCalls.godot_icall_Godot_type_exists(type);
}
public static byte[] var2bytes(object var)
{
return NativeCalls.godot_icall_Godot_var2bytes(var);
}
public static string var2str(object var)
{
return NativeCalls.godot_icall_Godot_var2str(var);
}
public static WeakRef weakref(Object obj)
{
return NativeCalls.godot_icall_Godot_weakref(Object.GetPtr(obj));
}
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
internal class GodotMethodAttribute : Attribute
{
private string methodName;
public string MethodName { get { return methodName; } }
public GodotMethodAttribute(string methodName)
{
this.methodName = methodName;
}
}
}

View file

@ -0,0 +1,26 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace Godot
{
public class GodotSynchronizationContext : SynchronizationContext
{
private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> queue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
public override void Post(SendOrPostCallback d, object state)
{
queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
}
public void ExecutePendingContinuations()
{
KeyValuePair<SendOrPostCallback, object> workItem;
while (queue.TryTake(out workItem))
{
workItem.Key(workItem.Value);
}
}
}
}

View file

@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Godot
{
public class GodotTaskScheduler : TaskScheduler
{
private GodotSynchronizationContext Context { get; set; }
private readonly LinkedList<Task> _tasks = new LinkedList<Task>();
public GodotTaskScheduler()
{
Context = new GodotSynchronizationContext();
}
protected sealed override void QueueTask(Task task)
{
lock (_tasks)
{
_tasks.AddLast(task);
}
}
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
if (SynchronizationContext.Current != Context)
{
return false;
}
if (taskWasPreviouslyQueued)
{
TryDequeue(task);
}
return base.TryExecuteTask(task);
}
protected sealed override bool TryDequeue(Task task)
{
lock (_tasks)
{
return _tasks.Remove(task);
}
}
protected sealed override IEnumerable<Task> GetScheduledTasks()
{
lock (_tasks)
{
return _tasks.ToArray();
}
}
public void Activate()
{
SynchronizationContext.SetSynchronizationContext(Context);
ExecuteQueuedTasks();
Context.ExecutePendingContinuations();
}
private void ExecuteQueuedTasks()
{
while (true)
{
Task task;
lock (_tasks)
{
if (_tasks.Any())
{
task = _tasks.First.Value;
_tasks.RemoveFirst();
}
else
{
break;
}
}
if (task != null)
{
if (!TryExecuteTask(task))
{
throw new InvalidOperationException();
}
}
}
}
}
}

View file

@ -0,0 +1,12 @@
namespace Godot
{
public interface IAwaitable
{
IAwaiter GetAwaiter();
}
public interface IAwaitable<out TResult>
{
IAwaiter<TResult> GetAwaiter();
}
}

View file

@ -0,0 +1,19 @@
using System;
using System.Runtime.CompilerServices;
namespace Godot
{
public interface IAwaiter : INotifyCompletion
{
bool IsCompleted { get; }
void GetResult();
}
public interface IAwaiter<out TResult> : INotifyCompletion
{
bool IsCompleted { get; }
TResult GetResult();
}
}

View file

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
namespace Godot
{
internal static class MarshalUtils
{
private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values)
{
Dictionary<object, object> ret = new Dictionary<object, object>();
for (int i = 0; i < keys.Length; i++)
{
ret.Add(keys[i], values[i]);
}
return ret;
}
private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo)
{
Dictionary<object, object>.KeyCollection keys = from.Keys;
keysTo = new object[keys.Count];
keys.CopyTo(keysTo, 0);
Dictionary<object, object>.ValueCollection values = from.Values;
valuesTo = new object[values.Count];
values.CopyTo(valuesTo, 0);
}
private static Type GetDictionaryType()
{
return typeof(Dictionary<object, object>);
}
}
}

View file

@ -0,0 +1,234 @@
using System;
namespace Godot
{
public static class Mathf
{
public const float PI = 3.14159274f;
public const float Epsilon = 1e-06f;
private const float Deg2RadConst = 0.0174532924f;
private const float Rad2DegConst = 57.29578f;
public static float abs(float s)
{
return Math.Abs(s);
}
public static float acos(float s)
{
return (float)Math.Acos(s);
}
public static float asin(float s)
{
return (float)Math.Asin(s);
}
public static float atan(float s)
{
return (float)Math.Atan(s);
}
public static float atan2(float x, float y)
{
return (float)Math.Atan2(x, y);
}
public static float ceil(float s)
{
return (float)Math.Ceiling(s);
}
public static float clamp(float val, float min, float max)
{
if (val < min)
{
return min;
}
else if (val > max)
{
return max;
}
return val;
}
public static float cos(float s)
{
return (float)Math.Cos(s);
}
public static float cosh(float s)
{
return (float)Math.Cosh(s);
}
public static int decimals(float step)
{
return decimals(step);
}
public static int decimals(decimal step)
{
return BitConverter.GetBytes(decimal.GetBits(step)[3])[2];
}
public static float deg2rad(float deg)
{
return deg * Deg2RadConst;
}
public static float ease(float s, float curve)
{
if (s < 0f)
{
s = 0f;
}
else if (s > 1.0f)
{
s = 1.0f;
}
if (curve > 0f)
{
if (curve < 1.0f)
{
return 1.0f - pow(1.0f - s, 1.0f / curve);
}
return pow(s, curve);
}
else if (curve < 0f)
{
if (s < 0.5f)
{
return pow(s * 2.0f, -curve) * 0.5f;
}
return (1.0f - pow(1.0f - (s - 0.5f) * 2.0f, -curve)) * 0.5f + 0.5f;
}
return 0f;
}
public static float exp(float s)
{
return (float)Math.Exp(s);
}
public static float floor(float s)
{
return (float)Math.Floor(s);
}
public static float fposmod(float x, float y)
{
if (x >= 0f)
{
return x % y;
}
else
{
return y - (-x % y);
}
}
public static float lerp(float from, float to, float weight)
{
return from + (to - from) * clamp(weight, 0f, 1f);
}
public static float log(float s)
{
return (float)Math.Log(s);
}
public static int max(int a, int b)
{
return (a > b) ? a : b;
}
public static float max(float a, float b)
{
return (a > b) ? a : b;
}
public static int min(int a, int b)
{
return (a < b) ? a : b;
}
public static float min(float a, float b)
{
return (a < b) ? a : b;
}
public static int nearest_po2(int val)
{
val--;
val |= val >> 1;
val |= val >> 2;
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
val++;
return val;
}
public static float pow(float x, float y)
{
return (float)Math.Pow(x, y);
}
public static float rad2deg(float rad)
{
return rad * Rad2DegConst;
}
public static float round(float s)
{
return (float)Math.Round(s);
}
public static float sign(float s)
{
return (s < 0f) ? -1f : 1f;
}
public static float sin(float s)
{
return (float)Math.Sin(s);
}
public static float sinh(float s)
{
return (float)Math.Sinh(s);
}
public static float sqrt(float s)
{
return (float)Math.Sqrt(s);
}
public static float stepify(float s, float step)
{
if (step != 0f)
{
s = floor(s / step + 0.5f) * step;
}
return s;
}
public static float tan(float s)
{
return (float)Math.Tan(s);
}
public static float tanh(float s)
{
return (float)Math.Tanh(s);
}
}
}

View file

@ -0,0 +1,209 @@
using System;
namespace Godot
{
public struct Plane : IEquatable<Plane>
{
Vector3 normal;
public float x
{
get
{
return normal.x;
}
set
{
normal.x = value;
}
}
public float y
{
get
{
return normal.y;
}
set
{
normal.y = value;
}
}
public float z
{
get
{
return normal.z;
}
set
{
normal.z = value;
}
}
float d;
public Vector3 Center
{
get
{
return normal * d;
}
}
public float distance_to(Vector3 point)
{
return normal.dot(point) - d;
}
public Vector3 get_any_point()
{
return normal * d;
}
public bool has_point(Vector3 point, float epsilon = Mathf.Epsilon)
{
float dist = normal.dot(point) - d;
return Mathf.abs(dist) <= epsilon;
}
public Vector3 intersect_3(Plane b, Plane c)
{
float denom = normal.cross(b.normal).dot(c.normal);
if (Mathf.abs(denom) <= Mathf.Epsilon)
return new Vector3();
Vector3 result = (b.normal.cross(c.normal) * this.d) +
(c.normal.cross(normal) * b.d) +
(normal.cross(b.normal) * c.d);
return result / denom;
}
public Vector3 intersect_ray(Vector3 from, Vector3 dir)
{
float den = normal.dot(dir);
if (Mathf.abs(den) <= Mathf.Epsilon)
return new Vector3();
float dist = (normal.dot(from) - d) / den;
// This is a ray, before the emiting pos (from) does not exist
if (dist > Mathf.Epsilon)
return new Vector3();
return from + dir * -dist;
}
public Vector3 intersect_segment(Vector3 begin, Vector3 end)
{
Vector3 segment = begin - end;
float den = normal.dot(segment);
if (Mathf.abs(den) <= Mathf.Epsilon)
return new Vector3();
float dist = (normal.dot(begin) - d) / den;
if (dist < -Mathf.Epsilon || dist > (1.0f + Mathf.Epsilon))
return new Vector3();
return begin + segment * -dist;
}
public bool is_point_over(Vector3 point)
{
return normal.dot(point) > d;
}
public Plane normalized()
{
float len = normal.length();
if (len == 0)
return new Plane(0, 0, 0, 0);
return new Plane(normal / len, d / len);
}
public Vector3 project(Vector3 point)
{
return point - normal * distance_to(point);
}
public Plane(float a, float b, float c, float d)
{
normal = new Vector3(a, b, c);
this.d = d;
}
public Plane(Vector3 normal, float d)
{
this.normal = normal;
this.d = d;
}
public Plane(Vector3 v1, Vector3 v2, Vector3 v3)
{
normal = (v1 - v3).cross(v1 - v2);
normal.normalize();
d = normal.dot(v1);
}
public static Plane operator -(Plane plane)
{
return new Plane(-plane.normal, -plane.d);
}
public static bool operator ==(Plane left, Plane right)
{
return left.Equals(right);
}
public static bool operator !=(Plane left, Plane right)
{
return !left.Equals(right);
}
public override bool Equals(object obj)
{
if (obj is Plane)
{
return Equals((Plane)obj);
}
return false;
}
public bool Equals(Plane other)
{
return normal == other.normal && d == other.d;
}
public override int GetHashCode()
{
return normal.GetHashCode() ^ d.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1})", new object[]
{
this.normal.ToString(),
this.d.ToString()
});
}
public string ToString(string format)
{
return String.Format("({0}, {1})", new object[]
{
this.normal.ToString(format),
this.d.ToString(format)
});
}
}
}

View file

@ -0,0 +1,328 @@
using System;
using System.Runtime.InteropServices;
namespace Godot
{
[StructLayout(LayoutKind.Sequential)]
public struct Quat : IEquatable<Quat>
{
private static readonly Quat identity = new Quat(0f, 0f, 0f, 1f);
public float x;
public float y;
public float z;
public float w;
public static Quat Identity
{
get { return identity; }
}
public float this[int index]
{
get
{
switch (index)
{
case 0:
return x;
case 1:
return y;
case 2:
return z;
case 3:
return w;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
case 3:
w = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
}
public Quat cubic_slerp(Quat b, Quat preA, Quat postB, float t)
{
float t2 = (1.0f - t) * t * 2f;
Quat sp = slerp(b, t);
Quat sq = preA.slerpni(postB, t);
return sp.slerpni(sq, t2);
}
public float dot(Quat b)
{
return x * b.x + y * b.y + z * b.z + w * b.w;
}
public Quat inverse()
{
return new Quat(-x, -y, -z, w);
}
public float length()
{
return Mathf.sqrt(length_squared());
}
public float length_squared()
{
return dot(this);
}
public Quat normalized()
{
return this / length();
}
public void set(float x, float y, float z, float w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
public Quat slerp(Quat b, float t)
{
// Calculate cosine
float cosom = x * b.x + y * b.y + z * b.z + w * b.w;
float[] to1 = new float[4];
// Adjust signs if necessary
if (cosom < 0.0)
{
cosom = -cosom; to1[0] = -b.x;
to1[1] = -b.y;
to1[2] = -b.z;
to1[3] = -b.w;
}
else
{
to1[0] = b.x;
to1[1] = b.y;
to1[2] = b.z;
to1[3] = b.w;
}
float sinom, scale0, scale1;
// Calculate coefficients
if ((1.0 - cosom) > Mathf.Epsilon)
{
// Standard case (Slerp)
float omega = Mathf.acos(cosom);
sinom = Mathf.sin(omega);
scale0 = Mathf.sin((1.0f - t) * omega) / sinom;
scale1 = Mathf.sin(t * omega) / sinom;
}
else
{
// Quaternions are very close so we can do a linear interpolation
scale0 = 1.0f - t;
scale1 = t;
}
// Calculate final values
return new Quat
(
scale0 * x + scale1 * to1[0],
scale0 * y + scale1 * to1[1],
scale0 * z + scale1 * to1[2],
scale0 * w + scale1 * to1[3]
);
}
public Quat slerpni(Quat b, float t)
{
float dot = this.dot(b);
if (Mathf.abs(dot) > 0.9999f)
{
return this;
}
float theta = Mathf.acos(dot);
float sinT = 1.0f / Mathf.sin(theta);
float newFactor = Mathf.sin(t * theta) * sinT;
float invFactor = Mathf.sin((1.0f - t) * theta) * sinT;
return new Quat
(
invFactor * this.x + newFactor * b.x,
invFactor * this.y + newFactor * b.y,
invFactor * this.z + newFactor * b.z,
invFactor * this.w + newFactor * b.w
);
}
public Vector3 xform(Vector3 v)
{
Quat q = this * v;
q *= this.inverse();
return new Vector3(q.x, q.y, q.z);
}
public Quat(float x, float y, float z, float w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
public Quat(Vector3 axis, float angle)
{
float d = axis.length();
if (d == 0f)
{
x = 0f;
y = 0f;
z = 0f;
w = 0f;
}
else
{
float s = Mathf.sin(-angle * 0.5f) / d;
x = axis.x * s;
y = axis.y * s;
z = axis.z * s;
w = Mathf.cos(-angle * 0.5f);
}
}
public static Quat operator *(Quat left, Quat right)
{
return new Quat
(
left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y,
left.w * right.y + left.y * right.w + left.z * right.x - left.x * right.z,
left.w * right.z + left.z * right.w + left.x * right.y - left.y * right.x,
left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z
);
}
public static Quat operator +(Quat left, Quat right)
{
return new Quat(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w);
}
public static Quat operator -(Quat left, Quat right)
{
return new Quat(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w);
}
public static Quat operator -(Quat left)
{
return new Quat(-left.x, -left.y, -left.z, -left.w);
}
public static Quat operator *(Quat left, Vector3 right)
{
return new Quat
(
left.w * right.x + left.y * right.z - left.z * right.y,
left.w * right.y + left.z * right.x - left.x * right.z,
left.w * right.z + left.x * right.y - left.y * right.x,
-left.x * right.x - left.y * right.y - left.z * right.z
);
}
public static Quat operator *(Vector3 left, Quat right)
{
return new Quat
(
right.w * left.x + right.y * left.z - right.z * left.y,
right.w * left.y + right.z * left.x - right.x * left.z,
right.w * left.z + right.x * left.y - right.y * left.x,
-right.x * left.x - right.y * left.y - right.z * left.z
);
}
public static Quat operator *(Quat left, float right)
{
return new Quat(left.x * right, left.y * right, left.z * right, left.w * right);
}
public static Quat operator *(float left, Quat right)
{
return new Quat(right.x * left, right.y * left, right.z * left, right.w * left);
}
public static Quat operator /(Quat left, float right)
{
return left * (1.0f / right);
}
public static bool operator ==(Quat left, Quat right)
{
return left.Equals(right);
}
public static bool operator !=(Quat left, Quat right)
{
return !left.Equals(right);
}
public override bool Equals(object obj)
{
if (obj is Vector2)
{
return Equals((Vector2)obj);
}
return false;
}
public bool Equals(Quat other)
{
return x == other.x && y == other.y && z == other.z && w == other.w;
}
public override int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1}, {2}, {3})", new object[]
{
this.x.ToString(),
this.y.ToString(),
this.z.ToString(),
this.w.ToString()
});
}
public string ToString(string format)
{
return String.Format("({0}, {1}, {2}, {3})", new object[]
{
this.x.ToString(format),
this.y.ToString(format),
this.z.ToString(format),
this.w.ToString(format)
});
}
}
}

View file

@ -0,0 +1,16 @@
using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
public class RemoteAttribute : Attribute {}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
public class SyncAttribute : Attribute {}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
public class MasterAttribute : Attribute {}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
public class SlaveAttribute : Attribute {}
}

View file

@ -0,0 +1,233 @@
using System;
using System.Runtime.InteropServices;
namespace Godot
{
[StructLayout(LayoutKind.Sequential)]
public struct Rect2 : IEquatable<Rect2>
{
private Vector2 position;
private Vector2 size;
public Vector2 Position
{
get { return position; }
set { position = value; }
}
public Vector2 Size
{
get { return size; }
set { size = value; }
}
public Vector2 End
{
get { return position + size; }
}
public float Area
{
get { return get_area(); }
}
public Rect2 clip(Rect2 b)
{
Rect2 newRect = b;
if (!intersects(newRect))
return new Rect2();
newRect.position.x = Mathf.max(b.position.x, position.x);
newRect.position.y = Mathf.max(b.position.y, position.y);
Vector2 bEnd = b.position + b.size;
Vector2 end = position + size;
newRect.size.x = Mathf.min(bEnd.x, end.x) - newRect.position.x;
newRect.size.y = Mathf.min(bEnd.y, end.y) - newRect.position.y;
return newRect;
}
public bool encloses(Rect2 b)
{
return (b.position.x >= position.x) && (b.position.y >= position.y) &&
((b.position.x + b.size.x) < (position.x + size.x)) &&
((b.position.y + b.size.y) < (position.y + size.y));
}
public Rect2 expand(Vector2 to)
{
Rect2 expanded = this;
Vector2 begin = expanded.position;
Vector2 end = expanded.position + expanded.size;
if (to.x < begin.x)
begin.x = to.x;
if (to.y < begin.y)
begin.y = to.y;
if (to.x > end.x)
end.x = to.x;
if (to.y > end.y)
end.y = to.y;
expanded.position = begin;
expanded.size = end - begin;
return expanded;
}
public float get_area()
{
return size.x * size.y;
}
public Rect2 grow(float by)
{
Rect2 g = this;
g.position.x -= by;
g.position.y -= by;
g.size.x += by * 2;
g.size.y += by * 2;
return g;
}
public Rect2 grow_individual(float left, float top, float right, float bottom)
{
Rect2 g = this;
g.position.x -= left;
g.position.y -= top;
g.size.x += left + right;
g.size.y += top + bottom;
return g;
}
public Rect2 grow_margin(int margin, float by)
{
Rect2 g = this;
g.grow_individual((GD.MARGIN_LEFT == margin) ? by : 0,
(GD.MARGIN_TOP == margin) ? by : 0,
(GD.MARGIN_RIGHT == margin) ? by : 0,
(GD.MARGIN_BOTTOM == margin) ? by : 0);
return g;
}
public bool has_no_area()
{
return size.x <= 0 || size.y <= 0;
}
public bool has_point(Vector2 point)
{
if (point.x < position.x)
return false;
if (point.y < position.y)
return false;
if (point.x >= (position.x + size.x))
return false;
if (point.y >= (position.y + size.y))
return false;
return true;
}
public bool intersects(Rect2 b)
{
if (position.x > (b.position.x + b.size.x))
return false;
if ((position.x + size.x) < b.position.x)
return false;
if (position.y > (b.position.y + b.size.y))
return false;
if ((position.y + size.y) < b.position.y)
return false;
return true;
}
public Rect2 merge(Rect2 b)
{
Rect2 newRect;
newRect.position.x = Mathf.min(b.position.x, position.x);
newRect.position.y = Mathf.min(b.position.y, position.y);
newRect.size.x = Mathf.max(b.position.x + b.size.x, position.x + size.x);
newRect.size.y = Mathf.max(b.position.y + b.size.y, position.y + size.y);
newRect.size = newRect.size - newRect.position; // Make relative again
return newRect;
}
public Rect2(Vector2 position, Vector2 size)
{
this.position = position;
this.size = size;
}
public Rect2(float x, float y, float width, float height)
{
this.position = new Vector2(x, y);
this.size = new Vector2(width, height);
}
public static bool operator ==(Rect2 left, Rect2 right)
{
return left.Equals(right);
}
public static bool operator !=(Rect2 left, Rect2 right)
{
return !left.Equals(right);
}
public override bool Equals(object obj)
{
if (obj is Rect2)
{
return Equals((Rect2)obj);
}
return false;
}
public bool Equals(Rect2 other)
{
return position.Equals(other.position) && size.Equals(other.size);
}
public override int GetHashCode()
{
return position.GetHashCode() ^ size.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1})", new object[]
{
this.position.ToString(),
this.size.ToString()
});
}
public string ToString(string format)
{
return String.Format("({0}, {1})", new object[]
{
this.position.ToString(format),
this.size.ToString(format)
});
}
}
}

View file

@ -0,0 +1,477 @@
using System;
// file: core/math/rect3.h
// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
// file: core/math/rect3.cpp
// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0
// file: core/variant_call.cpp
// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
namespace Godot
{
public struct Rect3 : IEquatable<Rect3>
{
private Vector3 position;
private Vector3 size;
public Vector3 Position
{
get
{
return position;
}
}
public Vector3 Size
{
get
{
return size;
}
}
public Vector3 End
{
get
{
return position + size;
}
}
public bool encloses(Rect3 with)
{
Vector3 src_min = position;
Vector3 src_max = position + size;
Vector3 dst_min = with.position;
Vector3 dst_max = with.position + with.size;
return ((src_min.x <= dst_min.x) &&
(src_max.x > dst_max.x) &&
(src_min.y <= dst_min.y) &&
(src_max.y > dst_max.y) &&
(src_min.z <= dst_min.z) &&
(src_max.z > dst_max.z));
}
public Rect3 expand(Vector3 to_point)
{
Vector3 begin = position;
Vector3 end = position + size;
if (to_point.x < begin.x)
begin.x = to_point.x;
if (to_point.y < begin.y)
begin.y = to_point.y;
if (to_point.z < begin.z)
begin.z = to_point.z;
if (to_point.x > end.x)
end.x = to_point.x;
if (to_point.y > end.y)
end.y = to_point.y;
if (to_point.z > end.z)
end.z = to_point.z;
return new Rect3(begin, end - begin);
}
public float get_area()
{
return size.x * size.y * size.z;
}
public Vector3 get_endpoint(int idx)
{
switch (idx)
{
case 0:
return new Vector3(position.x, position.y, position.z);
case 1:
return new Vector3(position.x, position.y, position.z + size.z);
case 2:
return new Vector3(position.x, position.y + size.y, position.z);
case 3:
return new Vector3(position.x, position.y + size.y, position.z + size.z);
case 4:
return new Vector3(position.x + size.x, position.y, position.z);
case 5:
return new Vector3(position.x + size.x, position.y, position.z + size.z);
case 6:
return new Vector3(position.x + size.x, position.y + size.y, position.z);
case 7:
return new Vector3(position.x + size.x, position.y + size.y, position.z + size.z);
default:
throw new ArgumentOutOfRangeException(nameof(idx), String.Format("Index is {0}, but a value from 0 to 7 is expected.", idx));
}
}
public Vector3 get_longest_axis()
{
Vector3 axis = new Vector3(1f, 0f, 0f);
float max_size = size.x;
if (size.y > max_size)
{
axis = new Vector3(0f, 1f, 0f);
max_size = size.y;
}
if (size.z > max_size)
{
axis = new Vector3(0f, 0f, 1f);
max_size = size.z;
}
return axis;
}
public Vector3.Axis get_longest_axis_index()
{
Vector3.Axis axis = Vector3.Axis.X;
float max_size = size.x;
if (size.y > max_size)
{
axis = Vector3.Axis.Y;
max_size = size.y;
}
if (size.z > max_size)
{
axis = Vector3.Axis.Z;
max_size = size.z;
}
return axis;
}
public float get_longest_axis_size()
{
float max_size = size.x;
if (size.y > max_size)
max_size = size.y;
if (size.z > max_size)
max_size = size.z;
return max_size;
}
public Vector3 get_shortest_axis()
{
Vector3 axis = new Vector3(1f, 0f, 0f);
float max_size = size.x;
if (size.y < max_size)
{
axis = new Vector3(0f, 1f, 0f);
max_size = size.y;
}
if (size.z < max_size)
{
axis = new Vector3(0f, 0f, 1f);
max_size = size.z;
}
return axis;
}
public Vector3.Axis get_shortest_axis_index()
{
Vector3.Axis axis = Vector3.Axis.X;
float max_size = size.x;
if (size.y < max_size)
{
axis = Vector3.Axis.Y;
max_size = size.y;
}
if (size.z < max_size)
{
axis = Vector3.Axis.Z;
max_size = size.z;
}
return axis;
}
public float get_shortest_axis_size()
{
float max_size = size.x;
if (size.y < max_size)
max_size = size.y;
if (size.z < max_size)
max_size = size.z;
return max_size;
}
public Vector3 get_support(Vector3 dir)
{
Vector3 half_extents = size * 0.5f;
Vector3 ofs = position + half_extents;
return ofs + new Vector3(
(dir.x > 0f) ? -half_extents.x : half_extents.x,
(dir.y > 0f) ? -half_extents.y : half_extents.y,
(dir.z > 0f) ? -half_extents.z : half_extents.z);
}
public Rect3 grow(float by)
{
Rect3 res = this;
res.position.x -= by;
res.position.y -= by;
res.position.z -= by;
res.size.x += 2.0f * by;
res.size.y += 2.0f * by;
res.size.z += 2.0f * by;
return res;
}
public bool has_no_area()
{
return size.x <= 0f || size.y <= 0f || size.z <= 0f;
}
public bool has_no_surface()
{
return size.x <= 0f && size.y <= 0f && size.z <= 0f;
}
public bool has_point(Vector3 point)
{
if (point.x < position.x)
return false;
if (point.y < position.y)
return false;
if (point.z < position.z)
return false;
if (point.x > position.x + size.x)
return false;
if (point.y > position.y + size.y)
return false;
if (point.z > position.z + size.z)
return false;
return true;
}
public Rect3 intersection(Rect3 with)
{
Vector3 src_min = position;
Vector3 src_max = position + size;
Vector3 dst_min = with.position;
Vector3 dst_max = with.position + with.size;
Vector3 min, max;
if (src_min.x > dst_max.x || src_max.x < dst_min.x)
{
return new Rect3();
}
else
{
min.x = (src_min.x > dst_min.x) ? src_min.x : dst_min.x;
max.x = (src_max.x < dst_max.x) ? src_max.x : dst_max.x;
}
if (src_min.y > dst_max.y || src_max.y < dst_min.y)
{
return new Rect3();
}
else
{
min.y = (src_min.y > dst_min.y) ? src_min.y : dst_min.y;
max.y = (src_max.y < dst_max.y) ? src_max.y : dst_max.y;
}
if (src_min.z > dst_max.z || src_max.z < dst_min.z)
{
return new Rect3();
}
else
{
min.z = (src_min.z > dst_min.z) ? src_min.z : dst_min.z;
max.z = (src_max.z < dst_max.z) ? src_max.z : dst_max.z;
}
return new Rect3(min, max - min);
}
public bool intersects(Rect3 with)
{
if (position.x >= (with.position.x + with.size.x))
return false;
if ((position.x + size.x) <= with.position.x)
return false;
if (position.y >= (with.position.y + with.size.y))
return false;
if ((position.y + size.y) <= with.position.y)
return false;
if (position.z >= (with.position.z + with.size.z))
return false;
if ((position.z + size.z) <= with.position.z)
return false;
return true;
}
public bool intersects_plane(Plane plane)
{
Vector3[] points =
{
new Vector3(position.x, position.y, position.z),
new Vector3(position.x, position.y, position.z + size.z),
new Vector3(position.x, position.y + size.y, position.z),
new Vector3(position.x, position.y + size.y, position.z + size.z),
new Vector3(position.x + size.x, position.y, position.z),
new Vector3(position.x + size.x, position.y, position.z + size.z),
new Vector3(position.x + size.x, position.y + size.y, position.z),
new Vector3(position.x + size.x, position.y + size.y, position.z + size.z),
};
bool over = false;
bool under = false;
for (int i = 0; i < 8; i++)
{
if (plane.distance_to(points[i]) > 0)
over = true;
else
under = true;
}
return under && over;
}
public bool intersects_segment(Vector3 from, Vector3 to)
{
float min = 0f;
float max = 1f;
for (int i = 0; i < 3; i++)
{
float seg_from = from[i];
float seg_to = to[i];
float box_begin = position[i];
float box_end = box_begin + size[i];
float cmin, cmax;
if (seg_from < seg_to)
{
if (seg_from > box_end || seg_to < box_begin)
return false;
float length = seg_to - seg_from;
cmin = seg_from < box_begin ? (box_begin - seg_from) / length : 0f;
cmax = seg_to > box_end ? (box_end - seg_from) / length : 1f;
}
else
{
if (seg_to > box_end || seg_from < box_begin)
return false;
float length = seg_to - seg_from;
cmin = seg_from > box_end ? (box_end - seg_from) / length : 0f;
cmax = seg_to < box_begin ? (box_begin - seg_from) / length : 1f;
}
if (cmin > min)
{
min = cmin;
}
if (cmax < max)
max = cmax;
if (max < min)
return false;
}
return true;
}
public Rect3 merge(Rect3 with)
{
Vector3 beg_1 = position;
Vector3 beg_2 = with.position;
Vector3 end_1 = new Vector3(size.x, size.y, size.z) + beg_1;
Vector3 end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2;
Vector3 min = new Vector3(
(beg_1.x < beg_2.x) ? beg_1.x : beg_2.x,
(beg_1.y < beg_2.y) ? beg_1.y : beg_2.y,
(beg_1.z < beg_2.z) ? beg_1.z : beg_2.z
);
Vector3 max = new Vector3(
(end_1.x > end_2.x) ? end_1.x : end_2.x,
(end_1.y > end_2.y) ? end_1.y : end_2.y,
(end_1.z > end_2.z) ? end_1.z : end_2.z
);
return new Rect3(min, max - min);
}
public Rect3(Vector3 position, Vector3 size)
{
this.position = position;
this.size = size;
}
public static bool operator ==(Rect3 left, Rect3 right)
{
return left.Equals(right);
}
public static bool operator !=(Rect3 left, Rect3 right)
{
return !left.Equals(right);
}
public override bool Equals(object obj)
{
if (obj is Rect3)
{
return Equals((Rect3)obj);
}
return false;
}
public bool Equals(Rect3 other)
{
return position == other.position && size == other.size;
}
public override int GetHashCode()
{
return position.GetHashCode() ^ size.GetHashCode();
}
public override string ToString()
{
return String.Format("{0} - {1}", new object[]
{
this.position.ToString(),
this.size.ToString()
});
}
public string ToString(string format)
{
return String.Format("{0} - {1}", new object[]
{
this.position.ToString(format),
this.size.ToString(format)
});
}
}
}

View file

@ -0,0 +1,59 @@
using System;
namespace Godot
{
public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]>
{
private bool completed = false;
private object[] result = null;
private Action action = null;
public SignalAwaiter(Godot.Object source, string signal, Godot.Object target)
{
NativeCalls.godot_icall_Object_connect_signal_awaiter(
Godot.Object.GetPtr(source),
signal, Godot.Object.GetPtr(target), this
);
}
public bool IsCompleted
{
get
{
return completed;
}
}
public void OnCompleted(Action action)
{
this.action = action;
}
public object[] GetResult()
{
return result;
}
public IAwaiter<object[]> GetAwaiter()
{
return this;
}
internal void SignalCallback(object[] args)
{
completed = true;
result = args;
if (action != null)
{
action();
}
}
internal void FailureCallback()
{
action = null;
completed = true;
}
}
}

View file

@ -0,0 +1,962 @@
//using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Security;
using System.Text;
using System.Text.RegularExpressions;
namespace Godot
{
public static class StringExtensions
{
private static int get_slice_count(this string instance, string splitter)
{
if (instance.empty() || splitter.empty())
return 0;
int pos = 0;
int slices = 1;
while ((pos = instance.find(splitter, pos)) >= 0)
{
slices++;
pos += splitter.Length;
}
return slices;
}
private static string get_slicec(this string instance, char splitter, int slice)
{
if (!instance.empty() && slice >= 0)
{
int i = 0;
int prev = 0;
int count = 0;
while (true)
{
if (instance[i] == 0 || instance[i] == splitter)
{
if (slice == count)
{
return instance.Substring(prev, i - prev);
}
else
{
count++;
prev = i + 1;
}
}
i++;
}
}
return string.Empty;
}
// <summary>
// If the string is a path to a file, return the path to the file without the extension.
// </summary>
public static string basename(this string instance)
{
int index = instance.LastIndexOf('.');
if (index > 0)
return instance.Substring(0, index);
return instance;
}
// <summary>
// Return true if the strings begins with the given string.
// </summary>
public static bool begins_with(this string instance, string text)
{
return instance.StartsWith(text);
}
// <summary>
// Return the bigrams (pairs of consecutive letters) of this string.
// </summary>
public static string[] bigrams(this string instance)
{
string[] b = new string[instance.Length - 1];
for (int i = 0; i < b.Length; i++)
{
b[i] = instance.Substring(i, 2);
}
return b;
}
// <summary>
// Return a copy of the string with special characters escaped using the C language standard.
// </summary>
public static string c_escape(this string instance)
{
StringBuilder sb = new StringBuilder(string.Copy(instance));
sb.Replace("\\", "\\\\");
sb.Replace("\a", "\\a");
sb.Replace("\b", "\\b");
sb.Replace("\f", "\\f");
sb.Replace("\n", "\\n");
sb.Replace("\r", "\\r");
sb.Replace("\t", "\\t");
sb.Replace("\v", "\\v");
sb.Replace("\'", "\\'");
sb.Replace("\"", "\\\"");
sb.Replace("?", "\\?");
return sb.ToString();
}
// <summary>
// Return a copy of the string with escaped characters replaced by their meanings according to the C language standard.
// </summary>
public static string c_unescape(this string instance)
{
StringBuilder sb = new StringBuilder(string.Copy(instance));
sb.Replace("\\a", "\a");
sb.Replace("\\b", "\b");
sb.Replace("\\f", "\f");
sb.Replace("\\n", "\n");
sb.Replace("\\r", "\r");
sb.Replace("\\t", "\t");
sb.Replace("\\v", "\v");
sb.Replace("\\'", "\'");
sb.Replace("\\\"", "\"");
sb.Replace("\\?", "?");
sb.Replace("\\\\", "\\");
return sb.ToString();
}
// <summary>
// Change the case of some letters. Replace underscores with spaces, convert all letters to lowercase then capitalize first and every letter following the space character. For [code]capitalize camelCase mixed_with_underscores[/code] it will return [code]Capitalize Camelcase Mixed With Underscores[/code].
// </summary>
public static string capitalize(this string instance)
{
string aux = instance.Replace("_", " ").ToLower();
string cap = string.Empty;
for (int i = 0; i < aux.get_slice_count(" "); i++)
{
string slice = aux.get_slicec(' ', i);
if (slice.Length > 0)
{
slice = char.ToUpper(slice[0]) + slice.Substring(1);
if (i > 0)
cap += " ";
cap += slice;
}
}
return cap;
}
// <summary>
// Perform a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
// </summary>
public static int casecmp_to(this string instance, string to)
{
if (instance.empty())
return to.empty() ? 0 : -1;
if (to.empty())
return 1;
int instance_idx = 0;
int to_idx = 0;
while (true)
{
if (to[to_idx] == 0 && instance[instance_idx] == 0)
return 0; // We're equal
else if (instance[instance_idx] == 0)
return -1; // If this is empty, and the other one is not, then we're less... I think?
else if (to[to_idx] == 0)
return 1; // Otherwise the other one is smaller...
else if (instance[instance_idx] < to[to_idx]) // More than
return -1;
else if (instance[instance_idx] > to[to_idx]) // Less than
return 1;
instance_idx++;
to_idx++;
}
}
// <summary>
// Return true if the string is empty.
// </summary>
public static bool empty(this string instance)
{
return string.IsNullOrEmpty(instance);
}
// <summary>
// Return true if the strings ends with the given string.
// </summary>
public static bool ends_with(this string instance, string text)
{
return instance.EndsWith(text);
}
// <summary>
// Erase [code]chars[/code] characters from the string starting from [code]pos[/code].
// </summary>
public static void erase(this StringBuilder instance, int pos, int chars)
{
instance.Remove(pos, chars);
}
// <summary>
// If the string is a path to a file, return the extension.
// </summary>
public static string extension(this string instance)
{
int pos = instance.find_last(".");
if (pos < 0)
return instance;
return instance.Substring(pos + 1, instance.Length);
}
// <summary>
// Find the first occurrence of a substring, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed.
// </summary>
public static int find(this string instance, string what, int from = 0)
{
return instance.IndexOf(what, StringComparison.OrdinalIgnoreCase);
}
// <summary>
// Find the last occurrence of a substring, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed.
// </summary>
public static int find_last(this string instance, string what)
{
return instance.LastIndexOf(what, StringComparison.OrdinalIgnoreCase);
}
// <summary>
// Find the first occurrence of a substring but search as case-insensitive, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed.
// </summary>
public static int findn(this string instance, string what, int from = 0)
{
return instance.IndexOf(what, StringComparison.Ordinal);
}
// <summary>
// If the string is a path to a file, return the base directory.
// </summary>
public static string get_base_dir(this string instance)
{
int basepos = instance.find("://");
string rs = string.Empty;
string @base = string.Empty;
if (basepos != -1)
{
int end = basepos + 3;
rs = instance.Substring(end, instance.Length);
@base = instance.Substring(0, end);
}
else
{
if (instance.begins_with("/"))
{
rs = instance.Substring(1, instance.Length);
@base = "/";
}
else
{
rs = instance;
}
}
int sep = Mathf.max(rs.find_last("/"), rs.find_last("\\"));
if (sep == -1)
return @base;
return @base + rs.substr(0, sep);
}
// <summary>
// If the string is a path to a file, return the file and ignore the base directory.
// </summary>
public static string get_file(this string instance)
{
int sep = Mathf.max(instance.find_last("/"), instance.find_last("\\"));
if (sep == -1)
return instance;
return instance.Substring(sep + 1, instance.Length);
}
// <summary>
// Hash the string and return a 32 bits integer.
// </summary>
public static int hash(this string instance)
{
int index = 0;
int hashv = 5381;
int c;
while ((c = (int)instance[index++]) != 0)
hashv = ((hashv << 5) + hashv) + c; // hash * 33 + c
return hashv;
}
// <summary>
// Convert a string containing an hexadecimal number into an int.
// </summary>
public static int hex_to_int(this string instance)
{
int sign = 1;
if (instance[0] == '-')
{
sign = -1;
instance = instance.Substring(1);
}
if (!instance.StartsWith("0x"))
return 0;
return sign * int.Parse(instance.Substring(2), NumberStyles.HexNumber);
}
// <summary>
// Insert a substring at a given position.
// </summary>
public static string insert(this string instance, int pos, string what)
{
return instance.Insert(pos, what);
}
// <summary>
// If the string is a path to a file or directory, return true if the path is absolute.
// </summary>
public static bool is_abs_path(this string instance)
{
return System.IO.Path.IsPathRooted(instance);
}
// <summary>
// If the string is a path to a file or directory, return true if the path is relative.
// </summary>
public static bool is_rel_path(this string instance)
{
return !System.IO.Path.IsPathRooted(instance);
}
// <summary>
// Check whether this string is a subsequence of the given string.
// </summary>
public static bool is_subsequence_of(this string instance, string text, bool case_insensitive)
{
int len = instance.Length;
if (len == 0)
return true; // Technically an empty string is subsequence of any string
if (len > text.Length)
return false;
int src = 0;
int tgt = 0;
while (instance[src] != 0 && text[tgt] != 0)
{
bool match = false;
if (case_insensitive)
{
char srcc = char.ToLower(instance[src]);
char tgtc = char.ToLower(text[tgt]);
match = srcc == tgtc;
}
else
{
match = instance[src] == text[tgt];
}
if (match)
{
src++;
if (instance[src] == 0)
return true;
}
tgt++;
}
return false;
}
// <summary>
// Check whether this string is a subsequence of the given string, considering case.
// </summary>
public static bool is_subsequence_of(this string instance, string text)
{
return instance.is_subsequence_of(text, false);
}
// <summary>
// Check whether this string is a subsequence of the given string, without considering case.
// </summary>
public static bool is_subsequence_ofi(this string instance, string text)
{
return instance.is_subsequence_of(text, true);
}
// <summary>
// Check whether the string contains a valid float.
// </summary>
public static bool is_valid_float(this string instance)
{
float f;
return float.TryParse(instance, out f);
}
// <summary>
// Check whether the string contains a valid color in HTML notation.
// </summary>
public static bool is_valid_html_color(this string instance)
{
return Color.html_is_valid(instance);
}
// <summary>
// Check whether the string is a valid identifier. As is common in programming languages, a valid identifier may contain only letters, digits and underscores (_) and the first character may not be a digit.
// </summary>
public static bool is_valid_identifier(this string instance)
{
int len = instance.Length;
if (len == 0)
return false;
for (int i = 0; i < len; i++)
{
if (i == 0)
{
if (instance[0] >= '0' && instance[0] <= '9')
return false; // Don't start with number plz
}
bool valid_char = (instance[i] >= '0' && instance[i] <= '9') || (instance[i] >= 'a' && instance[i] <= 'z') || (instance[i] >= 'A' && instance[i] <= 'Z') || instance[i] == '_';
if (!valid_char)
return false;
}
return true;
}
// <summary>
// Check whether the string contains a valid integer.
// </summary>
public static bool is_valid_integer(this string instance)
{
int f;
return int.TryParse(instance, out f);
}
// <summary>
// Check whether the string contains a valid IP address.
// </summary>
public static bool is_valid_ip_address(this string instance)
{
string[] ip = instance.split(".");
if (ip.Length != 4)
return false;
for (int i = 0; i < ip.Length; i++)
{
string n = ip[i];
if (!n.is_valid_integer())
return false;
int val = n.to_int();
if (val < 0 || val > 255)
return false;
}
return true;
}
// <summary>
// Return a copy of the string with special characters escaped using the JSON standard.
// </summary>
public static string json_escape(this string instance)
{
StringBuilder sb = new StringBuilder(string.Copy(instance));
sb.Replace("\\", "\\\\");
sb.Replace("\b", "\\b");
sb.Replace("\f", "\\f");
sb.Replace("\n", "\\n");
sb.Replace("\r", "\\r");
sb.Replace("\t", "\\t");
sb.Replace("\v", "\\v");
sb.Replace("\"", "\\\"");
return sb.ToString();
}
// <summary>
// Return an amount of characters from the left of the string.
// </summary>
public static string left(this string instance, int pos)
{
if (pos <= 0)
return string.Empty;
if (pos >= instance.Length)
return instance;
return instance.Substring(0, pos);
}
/// <summary>
/// Return the length of the string in characters.
/// </summary>
public static int length(this string instance)
{
return instance.Length;
}
// <summary>
// Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'.
// </summary>
public static bool expr_match(this string instance, string expr, bool case_sensitive)
{
if (expr.Length == 0 || instance.Length == 0)
return false;
switch (expr[0])
{
case '\0':
return instance[0] == 0;
case '*':
return expr_match(expr + 1, instance, case_sensitive) || (instance[0] != 0 && expr_match(expr, instance + 1, case_sensitive));
case '?':
return instance[0] != 0 && instance[0] != '.' && expr_match(expr + 1, instance + 1, case_sensitive);
default:
return (case_sensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) &&
expr_match(expr + 1, instance + 1, case_sensitive);
}
}
// <summary>
// Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]).
// </summary>
public static bool match(this string instance, string expr)
{
return instance.expr_match(expr, true);
}
// <summary>
// Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]).
// </summary>
public static bool matchn(this string instance, string expr)
{
return instance.expr_match(expr, false);
}
// <summary>
// Return the MD5 hash of the string as an array of bytes.
// </summary>
public static byte[] md5_buffer(this string instance)
{
return NativeCalls.godot_icall_String_md5_buffer(instance);
}
// <summary>
// Return the MD5 hash of the string as a string.
// </summary>
public static string md5_text(this string instance)
{
return NativeCalls.godot_icall_String_md5_text(instance);
}
// <summary>
// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
// </summary>
public static int nocasecmp_to(this string instance, string to)
{
if (instance.empty())
return to.empty() ? 0 : -1;
if (to.empty())
return 1;
int instance_idx = 0;
int to_idx = 0;
while (true)
{
if (to[to_idx] == 0 && instance[instance_idx] == 0)
return 0; // We're equal
else if (instance[instance_idx] == 0)
return -1; // If this is empty, and the other one is not, then we're less... I think?
else if (to[to_idx] == 0)
return 1; // Otherwise the other one is smaller..
else if (char.ToUpper(instance[instance_idx]) < char.ToUpper(to[to_idx])) // More than
return -1;
else if (char.ToUpper(instance[instance_idx]) > char.ToUpper(to[to_idx])) // Less than
return 1;
instance_idx++;
to_idx++;
}
}
// <summary>
// Return the character code at position [code]at[/code].
// </summary>
public static int ord_at(this string instance, int at)
{
return instance[at];
}
// <summary>
// Format a number to have an exact number of [code]digits[/code] after the decimal point.
// </summary>
public static string pad_decimals(this string instance, int digits)
{
int c = instance.find(".");
if (c == -1)
{
if (digits <= 0)
return instance;
instance += ".";
c = instance.Length - 1;
}
else
{
if (digits <= 0)
return instance.Substring(0, c);
}
if (instance.Length - (c + 1) > digits)
{
instance = instance.Substring(0, c + digits + 1);
}
else
{
while (instance.Length - (c + 1) < digits)
{
instance += "0";
}
}
return instance;
}
// <summary>
// Format a number to have an exact number of [code]digits[/code] before the decimal point.
// </summary>
public static string pad_zeros(this string instance, int digits)
{
string s = instance;
int end = s.find(".");
if (end == -1)
end = s.Length;
if (end == 0)
return s;
int begin = 0;
while (begin < end && (s[begin] < '0' || s[begin] > '9'))
{
begin++;
}
if (begin >= end)
return s;
while (end - begin < digits)
{
s = s.Insert(begin, "0");
end++;
}
return s;
}
// <summary>
// Decode a percent-encoded string. See [method percent_encode].
// </summary>
public static string percent_decode(this string instance)
{
return Uri.UnescapeDataString(instance);
}
// <summary>
// Percent-encode a string. This is meant to encode parameters in a URL when sending a HTTP GET request and bodies of form-urlencoded POST request.
// </summary>
public static string percent_encode(this string instance)
{
return Uri.EscapeDataString(instance);
}
// <summary>
// If the string is a path, this concatenates [code]file[/code] at the end of the string as a subpath. E.g. [code]"this/is".plus_file("path") == "this/is/path"[/code].
// </summary>
public static string plus_file(this string instance, string file)
{
if (instance.Length > 0 && instance[instance.Length - 1] == '/')
return instance + file;
else
return instance + "/" + file;
}
// <summary>
// Replace occurrences of a substring for different ones inside the string.
// </summary>
public static string replace(this string instance, string what, string forwhat)
{
return instance.Replace(what, forwhat);
}
// <summary>
// Replace occurrences of a substring for different ones inside the string, but search case-insensitive.
// </summary>
public static string replacen(this string instance, string what, string forwhat)
{
return Regex.Replace(instance, what, forwhat, RegexOptions.IgnoreCase);
}
// <summary>
// Perform a search for a substring, but start from the end of the string instead of the beginning.
// </summary>
public static int rfind(this string instance, string what, int from = -1)
{
return NativeCalls.godot_icall_String_rfind(instance, what, from);
}
// <summary>
// Perform a search for a substring, but start from the end of the string instead of the beginning. Also search case-insensitive.
// </summary>
public static int rfindn(this string instance, string what, int from = -1)
{
return NativeCalls.godot_icall_String_rfindn(instance, what, from);
}
// <summary>
// Return the right side of the string from a given position.
// </summary>
public static string right(this string instance, int pos)
{
if (pos >= instance.Length)
return instance;
if (pos < 0)
return string.Empty;
return instance.Substring(pos, (instance.Length - pos));
}
public static byte[] sha256_buffer(this string instance)
{
return NativeCalls.godot_icall_String_sha256_buffer(instance);
}
// <summary>
// Return the SHA-256 hash of the string as a string.
// </summary>
public static string sha256_text(this string instance)
{
return NativeCalls.godot_icall_String_sha256_text(instance);
}
// <summary>
// Return the similarity index of the text compared to this string. 1 means totally similar and 0 means totally dissimilar.
// </summary>
public static float similarity(this string instance, string text)
{
if (instance == text)
{
// Equal strings are totally similar
return 1.0f;
}
if (instance.Length < 2 || text.Length < 2)
{
// No way to calculate similarity without a single bigram
return 0.0f;
}
string[] src_bigrams = instance.bigrams();
string[] tgt_bigrams = text.bigrams();
int src_size = src_bigrams.Length;
int tgt_size = tgt_bigrams.Length;
float sum = src_size + tgt_size;
float inter = 0;
for (int i = 0; i < src_size; i++)
{
for (int j = 0; j < tgt_size; j++)
{
if (src_bigrams[i] == tgt_bigrams[j])
{
inter++;
break;
}
}
}
return (2.0f * inter) / sum;
}
// <summary>
// Split the string by a divisor string, return an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",".
// </summary>
public static string[] split(this string instance, string divisor, bool allow_empty = true)
{
return instance.Split(new string[] { divisor }, StringSplitOptions.RemoveEmptyEntries);
}
// <summary>
// Split the string in floats by using a divisor string, return an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",".
// </summary>
public static float[] split_floats(this string instance, string divisor, bool allow_empty = true)
{
List<float> ret = new List<float>();
int from = 0;
int len = instance.Length;
while (true)
{
int end = instance.find(divisor, from);
if (end < 0)
end = len;
if (allow_empty || (end > from))
ret.Add(float.Parse(instance.Substring(from)));
if (end == len)
break;
from = end + divisor.Length;
}
return ret.ToArray();
}
private static readonly char[] non_printable = {
(char)00, (char)01, (char)02, (char)03, (char)04, (char)05,
(char)06, (char)07, (char)08, (char)09, (char)10, (char)11,
(char)12, (char)13, (char)14, (char)15, (char)16, (char)17,
(char)18, (char)19, (char)20, (char)21, (char)22, (char)23,
(char)24, (char)25, (char)26, (char)27, (char)28, (char)29,
(char)30, (char)31, (char)32
};
// <summary>
// Return a copy of the string stripped of any non-printable character at the beginning and the end. The optional arguments are used to toggle stripping on the left and right edges respectively.
// </summary>
public static string strip_edges(this string instance, bool left = true, bool right = true)
{
if (left)
{
if (right)
return instance.Trim(non_printable);
else
return instance.TrimStart(non_printable);
}
else
{
return instance.TrimEnd(non_printable);
}
}
// <summary>
// Return part of the string from the position [code]from[/code], with length [code]len[/code].
// </summary>
public static string substr(this string instance, int from, int len)
{
return instance.Substring(from, len);
}
// <summary>
// Convert the String (which is a character array) to PoolByteArray (which is an array of bytes). The conversion is speeded up in comparison to to_utf8() with the assumption that all the characters the String contains are only ASCII characters.
// </summary>
public static byte[] to_ascii(this string instance)
{
return Encoding.ASCII.GetBytes(instance);
}
// <summary>
// Convert a string, containing a decimal number, into a [code]float[/code].
// </summary>
public static float to_float(this string instance)
{
return float.Parse(instance);
}
// <summary>
// Convert a string, containing an integer number, into an [code]int[/code].
// </summary>
public static int to_int(this string instance)
{
return int.Parse(instance);
}
// <summary>
// Return the string converted to lowercase.
// </summary>
public static string to_lower(this string instance)
{
return instance.ToLower();
}
// <summary>
// Return the string converted to uppercase.
// </summary>
public static string to_upper(this string instance)
{
return instance.ToUpper();
}
// <summary>
// Convert the String (which is an array of characters) to PoolByteArray (which is an array of bytes). The conversion is a bit slower than to_ascii(), but supports all UTF-8 characters. Therefore, you should prefer this function over to_ascii().
// </summary>
public static byte[] to_utf8(this string instance)
{
return Encoding.UTF8.GetBytes(instance);
}
// <summary>
// Return a copy of the string with special characters escaped using the XML standard.
// </summary>
public static string xml_escape(this string instance)
{
return SecurityElement.Escape(instance);
}
// <summary>
// Return a copy of the string with escaped characters replaced by their meanings according to the XML standard.
// </summary>
public static string xml_unescape(this string instance)
{
return SecurityElement.FromString(instance).Text;
}
}
}

View file

@ -0,0 +1,7 @@
using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Class)]
public class ToolAttribute : Attribute {}
}

View file

@ -0,0 +1,168 @@
using System;
using System.Runtime.InteropServices;
namespace Godot
{
[StructLayout(LayoutKind.Sequential)]
public struct Transform : IEquatable<Transform>
{
public Basis basis;
public Vector3 origin;
public Transform affine_inverse()
{
Basis basisInv = basis.inverse();
return new Transform(basisInv, basisInv.xform(-origin));
}
public Transform inverse()
{
Basis basisTr = basis.transposed();
return new Transform(basisTr, basisTr.xform(-origin));
}
public Transform looking_at(Vector3 target, Vector3 up)
{
Transform t = this;
t.set_look_at(origin, target, up);
return t;
}
public Transform orthonormalized()
{
return new Transform(basis.orthonormalized(), origin);
}
public Transform rotated(Vector3 axis, float phi)
{
return this * new Transform(new Basis(axis, phi), new Vector3());
}
public Transform scaled(Vector3 scale)
{
return new Transform(basis.scaled(scale), origin * scale);
}
public void set_look_at(Vector3 eye, Vector3 target, Vector3 up)
{
// Make rotation matrix
// Z vector
Vector3 zAxis = eye - target;
zAxis.normalize();
Vector3 yAxis = up;
Vector3 xAxis = yAxis.cross(zAxis);
// Recompute Y = Z cross X
yAxis = zAxis.cross(xAxis);
xAxis.normalize();
yAxis.normalize();
basis = Basis.create_from_axes(xAxis, yAxis, zAxis);
origin = eye;
}
public Transform translated(Vector3 ofs)
{
return new Transform(basis, new Vector3
(
origin[0] += basis[0].dot(ofs),
origin[1] += basis[1].dot(ofs),
origin[2] += basis[2].dot(ofs)
));
}
public Vector3 xform(Vector3 v)
{
return new Vector3
(
basis[0].dot(v) + origin.x,
basis[1].dot(v) + origin.y,
basis[2].dot(v) + origin.z
);
}
public Vector3 xform_inv(Vector3 v)
{
Vector3 vInv = v - origin;
return new Vector3
(
(basis[0, 0] * vInv.x) + (basis[1, 0] * vInv.y) + (basis[2, 0] * vInv.z),
(basis[0, 1] * vInv.x) + (basis[1, 1] * vInv.y) + (basis[2, 1] * vInv.z),
(basis[0, 2] * vInv.x) + (basis[1, 2] * vInv.y) + (basis[2, 2] * vInv.z)
);
}
public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin)
{
this.basis = Basis.create_from_axes(xAxis, yAxis, zAxis);
this.origin = origin;
}
public Transform(Basis basis, Vector3 origin)
{
this.basis = basis;
this.origin = origin;
}
public static Transform operator *(Transform left, Transform right)
{
left.origin = left.xform(right.origin);
left.basis *= right.basis;
return left;
}
public static bool operator ==(Transform left, Transform right)
{
return left.Equals(right);
}
public static bool operator !=(Transform left, Transform right)
{
return !left.Equals(right);
}
public override bool Equals(object obj)
{
if (obj is Transform)
{
return Equals((Transform)obj);
}
return false;
}
public bool Equals(Transform other)
{
return basis.Equals(other.basis) && origin.Equals(other.origin);
}
public override int GetHashCode()
{
return basis.GetHashCode() ^ origin.GetHashCode();
}
public override string ToString()
{
return String.Format("{0} - {1}", new object[]
{
this.basis.ToString(),
this.origin.ToString()
});
}
public string ToString(string format)
{
return String.Format("{0} - {1}", new object[]
{
this.basis.ToString(format),
this.origin.ToString(format)
});
}
}
}

View file

@ -0,0 +1,356 @@
using System;
using System.Runtime.InteropServices;
namespace Godot
{
[StructLayout(LayoutKind.Sequential)]
public struct Transform2D : IEquatable<Transform2D>
{
private static readonly Transform2D identity = new Transform2D
(
new Vector2(1f, 0f),
new Vector2(0f, 1f),
new Vector2(0f, 0f)
);
public Vector2 x;
public Vector2 y;
public Vector2 o;
public static Transform2D Identity
{
get { return identity; }
}
public Vector2 Origin
{
get { return o; }
}
public float Rotation
{
get { return Mathf.atan2(y.x, o.y); }
}
public Vector2 Scale
{
get { return new Vector2(x.length(), y.length()); }
}
public Vector2 this[int index]
{
get
{
switch (index)
{
case 0:
return x;
case 1:
return y;
case 2:
return o;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
x = value;
return;
case 1:
y = value;
return;
case 2:
o = value;
return;
default:
throw new IndexOutOfRangeException();
}
}
}
public float this[int index, int axis]
{
get
{
switch (index)
{
case 0:
return x[axis];
case 1:
return y[axis];
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
x[axis] = value;
return;
case 1:
y[axis] = value;
return;
default:
throw new IndexOutOfRangeException();
}
}
}
public Transform2D affine_inverse()
{
Transform2D inv = this;
float det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1];
if (det == 0)
{
return new Transform2D
(
float.NaN, float.NaN,
float.NaN, float.NaN,
float.NaN, float.NaN
);
}
float idet = 1.0f / det;
float temp = this[0, 0];
this[0, 0] = this[1, 1];
this[1, 1] = temp;
this[0] *= new Vector2(idet, -idet);
this[1] *= new Vector2(-idet, idet);
this[2] = basis_xform(-this[2]);
return inv;
}
public Vector2 basis_xform(Vector2 v)
{
return new Vector2(tdotx(v), tdoty(v));
}
public Vector2 basis_xform_inv(Vector2 v)
{
return new Vector2(x.dot(v), y.dot(v));
}
public Transform2D interpolate_with(Transform2D m, float c)
{
float r1 = Rotation;
float r2 = m.Rotation;
Vector2 s1 = Scale;
Vector2 s2 = m.Scale;
// Slerp rotation
Vector2 v1 = new Vector2(Mathf.cos(r1), Mathf.sin(r1));
Vector2 v2 = new Vector2(Mathf.cos(r2), Mathf.sin(r2));
float dot = v1.dot(v2);
// Clamp dot to [-1, 1]
dot = (dot < -1.0f) ? -1.0f : ((dot > 1.0f) ? 1.0f : dot);
Vector2 v = new Vector2();
if (dot > 0.9995f)
{
// Linearly interpolate to avoid numerical precision issues
v = v1.linear_interpolate(v2, c).normalized();
}
else
{
float angle = c * Mathf.acos(dot);
Vector2 v3 = (v2 - v1 * dot).normalized();
v = v1 * Mathf.cos(angle) + v3 * Mathf.sin(angle);
}
// Extract parameters
Vector2 p1 = Origin;
Vector2 p2 = m.Origin;
// Construct matrix
Transform2D res = new Transform2D(Mathf.atan2(v.y, v.x), p1.linear_interpolate(p2, c));
Vector2 scale = s1.linear_interpolate(s2, c);
res.x *= scale;
res.y *= scale;
return res;
}
public Transform2D inverse()
{
Transform2D inv = this;
// Swap
float temp = inv.x.y;
inv.x.y = inv.y.x;
inv.y.x = temp;
inv.o = inv.basis_xform(-inv.o);
return inv;
}
public Transform2D orthonormalized()
{
Transform2D on = this;
Vector2 onX = on.x;
Vector2 onY = on.y;
onX.normalize();
onY = onY - onX * (onX.dot(onY));
onY.normalize();
on.x = onX;
on.y = onY;
return on;
}
public Transform2D rotated(float phi)
{
return this * new Transform2D(phi, new Vector2());
}
public Transform2D scaled(Vector2 scale)
{
Transform2D copy = this;
copy.x *= scale;
copy.y *= scale;
copy.o *= scale;
return copy;
}
private float tdotx(Vector2 with)
{
return this[0, 0] * with[0] + this[1, 0] * with[1];
}
private float tdoty(Vector2 with)
{
return this[0, 1] * with[0] + this[1, 1] * with[1];
}
public Transform2D translated(Vector2 offset)
{
Transform2D copy = this;
copy.o += copy.basis_xform(offset);
return copy;
}
public Vector2 xform(Vector2 v)
{
return new Vector2(tdotx(v), tdoty(v)) + o;
}
public Vector2 xform_inv(Vector2 v)
{
Vector2 vInv = v - o;
return new Vector2(x.dot(vInv), y.dot(vInv));
}
public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin)
{
this.x = xAxis;
this.y = yAxis;
this.o = origin;
}
public Transform2D(float xx, float xy, float yx, float yy, float ox, float oy)
{
this.x = new Vector2(xx, xy);
this.y = new Vector2(yx, yy);
this.o = new Vector2(ox, oy);
}
public Transform2D(float rot, Vector2 pos)
{
float cr = Mathf.cos(rot);
float sr = Mathf.sin(rot);
x.x = cr;
y.y = cr;
x.y = -sr;
y.x = sr;
o = pos;
}
public static Transform2D operator *(Transform2D left, Transform2D right)
{
left.o = left.xform(right.o);
float x0, x1, y0, y1;
x0 = left.tdotx(right.x);
x1 = left.tdoty(right.x);
y0 = left.tdotx(right.y);
y1 = left.tdoty(right.y);
left.x.x = x0;
left.x.y = x1;
left.y.x = y0;
left.y.y = y1;
return left;
}
public static bool operator ==(Transform2D left, Transform2D right)
{
return left.Equals(right);
}
public static bool operator !=(Transform2D left, Transform2D right)
{
return !left.Equals(right);
}
public override bool Equals(object obj)
{
if (obj is Transform2D)
{
return Equals((Transform2D)obj);
}
return false;
}
public bool Equals(Transform2D other)
{
return x.Equals(other.x) && y.Equals(other.y) && o.Equals(other.o);
}
public override int GetHashCode()
{
return x.GetHashCode() ^ y.GetHashCode() ^ o.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1}, {2})", new object[]
{
this.x.ToString(),
this.y.ToString(),
this.o.ToString()
});
}
public string ToString(string format)
{
return String.Format("({0}, {1}, {2})", new object[]
{
this.x.ToString(format),
this.y.ToString(format),
this.o.ToString(format)
});
}
}
}

View file

@ -0,0 +1,362 @@
using System;
using System.Runtime.InteropServices;
// file: core/math/math_2d.h
// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
// file: core/math/math_2d.cpp
// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
// file: core/variant_call.cpp
// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
namespace Godot
{
[StructLayout(LayoutKind.Sequential)]
public struct Vector2 : IEquatable<Vector2>
{
public float x;
public float y;
public float this[int index]
{
get
{
switch (index)
{
case 0:
return x;
case 1:
return y;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
x = value;
return;
case 1:
y = value;
return;
default:
throw new IndexOutOfRangeException();
}
}
}
internal void normalize()
{
float length = x * x + y * y;
if (length != 0f)
{
length = Mathf.sqrt(length);
x /= length;
y /= length;
}
}
private float cross(Vector2 b)
{
return x * b.y - y * b.x;
}
public Vector2 abs()
{
return new Vector2(Mathf.abs(x), Mathf.abs(y));
}
public float angle()
{
return Mathf.atan2(y, x);
}
public float angle_to(Vector2 to)
{
return Mathf.atan2(cross(to), dot(to));
}
public float angle_to_point(Vector2 to)
{
return Mathf.atan2(x - to.x, y - to.y);
}
public float aspect()
{
return x / y;
}
public Vector2 bounce(Vector2 n)
{
return -reflect(n);
}
public Vector2 clamped(float length)
{
Vector2 v = this;
float l = this.length();
if (l > 0 && length < l)
{
v /= l;
v *= length;
}
return v;
}
public Vector2 cubic_interpolate(Vector2 b, Vector2 preA, Vector2 postB, float t)
{
Vector2 p0 = preA;
Vector2 p1 = this;
Vector2 p2 = b;
Vector2 p3 = postB;
float t2 = t * t;
float t3 = t2 * t;
return 0.5f * ((p1 * 2.0f) +
(-p0 + p2) * t +
(2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 +
(-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
}
public float distance_squared_to(Vector2 to)
{
return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y);
}
public float distance_to(Vector2 to)
{
return Mathf.sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y));
}
public float dot(Vector2 with)
{
return x * with.x + y * with.y;
}
public Vector2 floor()
{
return new Vector2(Mathf.floor(x), Mathf.floor(y));
}
public bool is_normalized()
{
return Mathf.abs(length_squared() - 1.0f) < Mathf.Epsilon;
}
public float length()
{
return Mathf.sqrt(x * x + y * y);
}
public float length_squared()
{
return x * x + y * y;
}
public Vector2 linear_interpolate(Vector2 b, float t)
{
Vector2 res = this;
res.x += (t * (b.x - x));
res.y += (t * (b.y - y));
return res;
}
public Vector2 normalized()
{
Vector2 result = this;
result.normalize();
return result;
}
public Vector2 reflect(Vector2 n)
{
return 2.0f * n * dot(n) - this;
}
public Vector2 rotated(float phi)
{
float rads = angle() + phi;
return new Vector2(Mathf.cos(rads), Mathf.sin(rads)) * length();
}
public Vector2 slide(Vector2 n)
{
return this - n * dot(n);
}
public Vector2 snapped(Vector2 by)
{
return new Vector2(Mathf.stepify(x, by.x), Mathf.stepify(y, by.y));
}
public Vector2 tangent()
{
return new Vector2(y, -x);
}
public Vector2(float x, float y)
{
this.x = x;
this.y = y;
}
public static Vector2 operator +(Vector2 left, Vector2 right)
{
left.x += right.x;
left.y += right.y;
return left;
}
public static Vector2 operator -(Vector2 left, Vector2 right)
{
left.x -= right.x;
left.y -= right.y;
return left;
}
public static Vector2 operator -(Vector2 vec)
{
vec.x = -vec.x;
vec.y = -vec.y;
return vec;
}
public static Vector2 operator *(Vector2 vec, float scale)
{
vec.x *= scale;
vec.y *= scale;
return vec;
}
public static Vector2 operator *(float scale, Vector2 vec)
{
vec.x *= scale;
vec.y *= scale;
return vec;
}
public static Vector2 operator *(Vector2 left, Vector2 right)
{
left.x *= right.x;
left.y *= right.y;
return left;
}
public static Vector2 operator /(Vector2 vec, float scale)
{
vec.x /= scale;
vec.y /= scale;
return vec;
}
public static Vector2 operator /(Vector2 left, Vector2 right)
{
left.x /= right.x;
left.y /= right.y;
return left;
}
public static bool operator ==(Vector2 left, Vector2 right)
{
return left.Equals(right);
}
public static bool operator !=(Vector2 left, Vector2 right)
{
return !left.Equals(right);
}
public static bool operator <(Vector2 left, Vector2 right)
{
if (left.x.Equals(right.x))
{
return left.y < right.y;
}
else
{
return left.x < right.x;
}
}
public static bool operator >(Vector2 left, Vector2 right)
{
if (left.x.Equals(right.x))
{
return left.y > right.y;
}
else
{
return left.x > right.x;
}
}
public static bool operator <=(Vector2 left, Vector2 right)
{
if (left.x.Equals(right.x))
{
return left.y <= right.y;
}
else
{
return left.x <= right.x;
}
}
public static bool operator >=(Vector2 left, Vector2 right)
{
if (left.x.Equals(right.x))
{
return left.y >= right.y;
}
else
{
return left.x >= right.x;
}
}
public override bool Equals(object obj)
{
if (obj is Vector2)
{
return Equals((Vector2)obj);
}
return false;
}
public bool Equals(Vector2 other)
{
return x == other.x && y == other.y;
}
public override int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1})", new object[]
{
this.x.ToString(),
this.y.ToString()
});
}
public string ToString(string format)
{
return String.Format("({0}, {1})", new object[]
{
this.x.ToString(format),
this.y.ToString(format)
});
}
}
}

View file

@ -0,0 +1,420 @@
using System;
using System.Runtime.InteropServices;
// file: core/math/vector3.h
// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0
// file: core/math/vector3.cpp
// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
// file: core/variant_call.cpp
// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
namespace Godot
{
[StructLayout(LayoutKind.Sequential)]
public struct Vector3 : IEquatable<Vector3>
{
public enum Axis
{
X = 0,
Y,
Z
}
public float x;
public float y;
public float z;
public float this[int index]
{
get
{
switch (index)
{
case 0:
return x;
case 1:
return y;
case 2:
return z;
default:
throw new IndexOutOfRangeException();
}
}
set
{
switch (index)
{
case 0:
x = value;
return;
case 1:
y = value;
return;
case 2:
z = value;
return;
default:
throw new IndexOutOfRangeException();
}
}
}
internal void normalize()
{
float length = this.length();
if (length == 0f)
{
x = y = z = 0f;
}
else
{
x /= length;
y /= length;
z /= length;
}
}
public Vector3 abs()
{
return new Vector3(Mathf.abs(x), Mathf.abs(y), Mathf.abs(z));
}
public float angle_to(Vector3 to)
{
return Mathf.atan2(cross(to).length(), dot(to));
}
public Vector3 bounce(Vector3 n)
{
return -reflect(n);
}
public Vector3 ceil()
{
return new Vector3(Mathf.ceil(x), Mathf.ceil(y), Mathf.ceil(z));
}
public Vector3 cross(Vector3 b)
{
return new Vector3
(
(y * b.z) - (z * b.y),
(z * b.x) - (x * b.z),
(x * b.y) - (y * b.x)
);
}
public Vector3 cubic_interpolate(Vector3 b, Vector3 preA, Vector3 postB, float t)
{
Vector3 p0 = preA;
Vector3 p1 = this;
Vector3 p2 = b;
Vector3 p3 = postB;
float t2 = t * t;
float t3 = t2 * t;
return 0.5f * (
(p1 * 2.0f) + (-p0 + p2) * t +
(2.0f * p0 - 5.0f * p1 + 4f * p2 - p3) * t2 +
(-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3
);
}
public float distance_squared_to(Vector3 b)
{
return (b - this).length_squared();
}
public float distance_to(Vector3 b)
{
return (b - this).length();
}
public float dot(Vector3 b)
{
return x * b.x + y * b.y + z * b.z;
}
public Vector3 floor()
{
return new Vector3(Mathf.floor(x), Mathf.floor(y), Mathf.floor(z));
}
public Vector3 inverse()
{
return new Vector3(1.0f / x, 1.0f / y, 1.0f / z);
}
public bool is_normalized()
{
return Mathf.abs(length_squared() - 1.0f) < Mathf.Epsilon;
}
public float length()
{
float x2 = x * x;
float y2 = y * y;
float z2 = z * z;
return Mathf.sqrt(x2 + y2 + z2);
}
public float length_squared()
{
float x2 = x * x;
float y2 = y * y;
float z2 = z * z;
return x2 + y2 + z2;
}
public Vector3 linear_interpolate(Vector3 b, float t)
{
return new Vector3
(
x + (t * (b.x - x)),
y + (t * (b.y - y)),
z + (t * (b.z - z))
);
}
public Axis max_axis()
{
return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X);
}
public Axis min_axis()
{
return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z);
}
public Vector3 normalized()
{
Vector3 v = this;
v.normalize();
return v;
}
public Basis outer(Vector3 b)
{
return new Basis(
new Vector3(x * b.x, x * b.y, x * b.z),
new Vector3(y * b.x, y * b.y, y * b.z),
new Vector3(z * b.x, z * b.y, z * b.z)
);
}
public Vector3 reflect(Vector3 n)
{
#if DEBUG
if (!n.is_normalized())
throw new ArgumentException(String.Format("{0} is not normalized", n), nameof(n));
#endif
return 2.0f * n * dot(n) - this;
}
public Vector3 rotated(Vector3 axis, float phi)
{
return new Basis(axis, phi).xform(this);
}
public Vector3 slide(Vector3 n)
{
return this - n * dot(n);
}
public Vector3 snapped(Vector3 by)
{
return new Vector3
(
Mathf.stepify(x, by.x),
Mathf.stepify(y, by.y),
Mathf.stepify(z, by.z)
);
}
public Basis to_diagonal_matrix()
{
return new Basis(
x, 0f, 0f,
0f, y, 0f,
0f, 0f, z
);
}
public Vector3(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
public static Vector3 operator +(Vector3 left, Vector3 right)
{
left.x += right.x;
left.y += right.y;
left.z += right.z;
return left;
}
public static Vector3 operator -(Vector3 left, Vector3 right)
{
left.x -= right.x;
left.y -= right.y;
left.z -= right.z;
return left;
}
public static Vector3 operator -(Vector3 vec)
{
vec.x = -vec.x;
vec.y = -vec.y;
vec.z = -vec.z;
return vec;
}
public static Vector3 operator *(Vector3 vec, float scale)
{
vec.x *= scale;
vec.y *= scale;
vec.z *= scale;
return vec;
}
public static Vector3 operator *(float scale, Vector3 vec)
{
vec.x *= scale;
vec.y *= scale;
vec.z *= scale;
return vec;
}
public static Vector3 operator *(Vector3 left, Vector3 right)
{
left.x *= right.x;
left.y *= right.y;
left.z *= right.z;
return left;
}
public static Vector3 operator /(Vector3 vec, float scale)
{
vec.x /= scale;
vec.y /= scale;
vec.z /= scale;
return vec;
}
public static Vector3 operator /(Vector3 left, Vector3 right)
{
left.x /= right.x;
left.y /= right.y;
left.z /= right.z;
return left;
}
public static bool operator ==(Vector3 left, Vector3 right)
{
return left.Equals(right);
}
public static bool operator !=(Vector3 left, Vector3 right)
{
return !left.Equals(right);
}
public static bool operator <(Vector3 left, Vector3 right)
{
if (left.x == right.x)
{
if (left.y == right.y)
return left.z < right.z;
else
return left.y < right.y;
}
return left.x < right.x;
}
public static bool operator >(Vector3 left, Vector3 right)
{
if (left.x == right.x)
{
if (left.y == right.y)
return left.z > right.z;
else
return left.y > right.y;
}
return left.x > right.x;
}
public static bool operator <=(Vector3 left, Vector3 right)
{
if (left.x == right.x)
{
if (left.y == right.y)
return left.z <= right.z;
else
return left.y < right.y;
}
return left.x < right.x;
}
public static bool operator >=(Vector3 left, Vector3 right)
{
if (left.x == right.x)
{
if (left.y == right.y)
return left.z >= right.z;
else
return left.y > right.y;
}
return left.x > right.x;
}
public override bool Equals(object obj)
{
if (obj is Vector3)
{
return Equals((Vector3)obj);
}
return false;
}
public bool Equals(Vector3 other)
{
return x == other.x && y == other.y && z == other.z;
}
public override int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1}, {2})", new object[]
{
this.x.ToString(),
this.y.ToString(),
this.z.ToString()
});
}
public string ToString(string format)
{
return String.Format("({0}, {1}, {2})", new object[]
{
this.x.ToString(format),
this.y.ToString(format),
this.z.ToString(format)
});
}
}
}

View file

@ -0,0 +1,302 @@
/*************************************************************************/
/* glue_header.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_internals.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../signal_awaiter_utils.h"
#include "bind/core_bind.h"
#include "class_db.h"
#include "io/marshalls.h"
#include "object.h"
#include "os/os.h"
#include "project_settings.h"
#include "reference.h"
#include "variant_parser.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
#endif
#define GODOTSHARP_INSTANCE_OBJECT(m_instance, m_type) \
static ClassDB::ClassInfo *ci = NULL; \
if (!ci) { \
ci = ClassDB::classes.getptr(m_type); \
} \
Object *m_instance = ci->creation_func();
void godot_icall_Object_Dtor(Object *ptr) {
ERR_FAIL_NULL(ptr);
_GodotSharp::get_singleton()->queue_dispose(ptr);
}
// -- ClassDB --
MethodBind *godot_icall_ClassDB_get_method(MonoString *p_type, MonoString *p_method) {
StringName type(GDMonoMarshal::mono_string_to_godot(p_type));
StringName method(GDMonoMarshal::mono_string_to_godot(p_method));
return ClassDB::get_method(type, method);
}
// -- SignalAwaiter --
Error godot_icall_Object_connect_signal_awaiter(Object *p_source, MonoString *p_signal, Object *p_target, MonoObject *p_awaiter) {
String signal = GDMonoMarshal::mono_string_to_godot(p_signal);
return SignalAwaiterUtils::connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
}
// -- NodePath --
NodePath *godot_icall_NodePath_Ctor(MonoString *p_path) {
return memnew(NodePath(GDMonoMarshal::mono_string_to_godot(p_path)));
}
void godot_icall_NodePath_Dtor(NodePath *p_ptr) {
ERR_FAIL_NULL(p_ptr);
_GodotSharp::get_singleton()->queue_dispose(p_ptr);
}
MonoString *godot_icall_NodePath_operator_String(NodePath *p_np) {
return GDMonoMarshal::mono_string_from_godot(p_np->operator String());
}
MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) {
Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer();
// TODO Check possible Array/Vector<uint8_t> problem?
return GDMonoMarshal::Array_to_mono_array(Variant(ret));
}
// -- RID --
RID *godot_icall_RID_Ctor(Object *p_from) {
Resource *res_from = Object::cast_to<Resource>(p_from);
if (res_from)
return memnew(RID(res_from->get_rid()));
return memnew(RID);
}
void godot_icall_RID_Dtor(RID *p_ptr) {
ERR_FAIL_NULL(p_ptr);
_GodotSharp::get_singleton()->queue_dispose(p_ptr);
}
// -- String --
MonoString *godot_icall_String_md5_text(MonoString *p_str) {
String ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_text();
return GDMonoMarshal::mono_string_from_godot(ret);
}
int godot_icall_String_rfind(MonoString *p_str, MonoString *p_what, int p_from) {
String what = GDMonoMarshal::mono_string_to_godot(p_what);
return GDMonoMarshal::mono_string_to_godot(p_str).rfind(what, p_from);
}
int godot_icall_String_rfindn(MonoString *p_str, MonoString *p_what, int p_from) {
String what = GDMonoMarshal::mono_string_to_godot(p_what);
return GDMonoMarshal::mono_string_to_godot(p_str).rfindn(what, p_from);
}
MonoArray *godot_icall_String_sha256_buffer(MonoString *p_str) {
Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_buffer();
return GDMonoMarshal::Array_to_mono_array(Variant(ret));
}
MonoString *godot_icall_String_sha256_text(MonoString *p_str) {
String ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_text();
return GDMonoMarshal::mono_string_from_godot(ret);
}
// -- Global Scope --
MonoObject *godot_icall_Godot_bytes2var(MonoArray *p_bytes) {
Variant ret;
PoolByteArray varr = GDMonoMarshal::mono_array_to_PoolByteArray(p_bytes);
PoolByteArray::Read r = varr.read();
Error err = decode_variant(ret, r.ptr(), varr.size(), NULL);
if (err != OK) {
ret = RTR("Not enough bytes for decoding bytes, or invalid format.");
}
return GDMonoMarshal::variant_to_mono_object(ret);
}
MonoObject *godot_icall_Godot_convert(MonoObject *p_what, int p_type) {
Variant what = GDMonoMarshal::mono_object_to_variant(p_what);
const Variant *args[1] = { &what };
Variant::CallError ce;
Variant ret = Variant::construct(Variant::Type(p_type), args, 1, ce);
ERR_FAIL_COND_V(ce.error != Variant::CallError::CALL_OK, NULL);
return GDMonoMarshal::variant_to_mono_object(ret);
}
int godot_icall_Godot_hash(MonoObject *p_var) {
return GDMonoMarshal::mono_object_to_variant(p_var).hash();
}
MonoObject *godot_icall_Godot_instance_from_id(int p_instance_id) {
return GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(p_instance_id));
}
void godot_icall_Godot_print(MonoArray *p_what) {
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
String str;
for (int i = 0; i < what.size(); i++)
str += what[i].operator String();
print_line(str);
}
void godot_icall_Godot_printerr(MonoArray *p_what) {
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
String str;
for (int i = 0; i < what.size(); i++)
str += what[i].operator String();
OS::get_singleton()->printerr("%s\n", str.utf8().get_data());
}
void godot_icall_Godot_printraw(MonoArray *p_what) {
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
String str;
for (int i = 0; i < what.size(); i++)
str += what[i].operator String();
OS::get_singleton()->print("%s", str.utf8().get_data());
}
void godot_icall_Godot_prints(MonoArray *p_what) {
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
String str;
for (int i = 0; i < what.size(); i++) {
if (i)
str += " ";
str += what[i].operator String();
}
print_line(str);
}
void godot_icall_Godot_printt(MonoArray *p_what) {
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
String str;
for (int i = 0; i < what.size(); i++) {
if (i)
str += "\t";
str += what[i].operator String();
}
print_line(str);
}
void godot_icall_Godot_seed(int p_seed) {
Math::seed(p_seed);
}
MonoString *godot_icall_Godot_str(MonoArray *p_what) {
String str;
Array what = GDMonoMarshal::mono_array_to_Array(p_what);
for (int i = 0; i < what.size(); i++) {
String os = what[i].operator String();
if (i == 0)
str = os;
else
str += os;
}
return GDMonoMarshal::mono_string_from_godot(str);
}
MonoObject *godot_icall_Godot_str2var(MonoString *p_str) {
Variant ret;
VariantParser::StreamString ss;
ss.s = GDMonoMarshal::mono_string_to_godot(p_str);
String errs;
int line;
Error err = VariantParser::parse(&ss, ret, errs, line);
if (err != OK) {
String err_str = "Parse error at line " + itos(line) + ": " + errs;
ERR_PRINTS(err_str);
ret = err_str;
}
return GDMonoMarshal::variant_to_mono_object(ret);
}
bool godot_icall_Godot_type_exists(MonoString *p_type) {
return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type));
}
MonoArray *godot_icall_Godot_var2bytes(MonoObject *p_var) {
Variant var = GDMonoMarshal::mono_object_to_variant(p_var);
PoolByteArray barr;
int len;
Error err = encode_variant(var, NULL, len);
ERR_EXPLAIN("Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).");
ERR_FAIL_COND_V(err != OK, NULL);
barr.resize(len);
{
PoolByteArray::Write w = barr.write();
encode_variant(var, w.ptr(), len);
}
return GDMonoMarshal::PoolByteArray_to_mono_array(barr);
}
MonoString *godot_icall_Godot_var2str(MonoObject *p_var) {
String vars;
VariantWriter::write_to_string(GDMonoMarshal::mono_object_to_variant(p_var), vars);
return GDMonoMarshal::mono_string_from_godot(vars);
}
MonoObject *godot_icall_Godot_weakref(Object *p_obj) {
if (!p_obj)
return NULL;
Ref<WeakRef> wref;
Reference *ref = Object::cast_to<Reference>(p_obj);
if (ref) {
REF r = ref;
if (!r.is_valid())
return NULL;
wref.instance();
wref->set_ref(r);
} else {
wref.instance();
wref->set_obj(p_obj);
}
return GDMonoUtils::create_managed_for_godot_object(CACHED_CLASS(WeakRef), Reference::get_class_static(), Object::cast_to<Object>(wref.ptr()));
}

View file

@ -0,0 +1,41 @@
/*************************************************************************/
/* godotsharp_defs.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GODOTSHARP_DEFS_H
#define GODOTSHARP_DEFS_H
#define BINDINGS_NAMESPACE "Godot"
#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
#define BINDINGS_PTR_FIELD "ptr"
#define BINDINGS_NATIVE_NAME_FIELD "nativeName"
#define API_ASSEMBLY_NAME "GodotSharp"
#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
#define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools"
#endif // GODOTSHARP_DEFS_H

View file

@ -0,0 +1,185 @@
/*************************************************************************/
/* godotsharp_dirs.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "godotsharp_dirs.h"
#include "os/os.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#include "project_settings.h"
#include "version.h"
#endif
namespace GodotSharpDirs {
String _get_expected_build_config() {
#ifdef TOOLS_ENABLED
return "Tools";
#else
#ifdef DEBUG_ENABLED
return "Debug";
#else
return "Release";
#endif
#endif
}
String _get_mono_user_dir() {
#ifdef TOOLS_ENABLED
if (EditorSettings::get_singleton()) {
return EditorSettings::get_singleton()->get_settings_path().plus_file("mono");
} else {
String settings_path;
if (OS::get_singleton()->has_environment("APPDATA")) {
String app_data = OS::get_singleton()->get_environment("APPDATA").replace("\\", "/");
settings_path = app_data.plus_file(String(_MKSTR(VERSION_SHORT_NAME)).capitalize());
} else if (OS::get_singleton()->has_environment("HOME")) {
String home = OS::get_singleton()->get_environment("HOME");
settings_path = home.plus_file("." + String(_MKSTR(VERSION_SHORT_NAME)).to_lower());
}
return settings_path.plus_file("mono");
}
#else
return OS::get_singleton()->get_data_dir().plus_file("mono");
#endif
}
class _GodotSharpDirs {
public:
String res_data_dir;
String res_metadata_dir;
String res_assemblies_dir;
String res_config_dir;
String res_temp_dir;
String res_temp_assemblies_base_dir;
String res_temp_assemblies_dir;
String mono_user_dir;
String mono_logs_dir;
#ifdef TOOLS_ENABLED
String mono_solutions_dir;
String build_logs_dir;
String sln_filepath;
String csproj_filepath;
#endif
private:
_GodotSharpDirs() {
res_data_dir = "res://.mono";
res_metadata_dir = res_data_dir.plus_file("metadata");
res_assemblies_dir = res_data_dir.plus_file("assemblies");
res_config_dir = res_data_dir.plus_file("etc").plus_file("mono");
// TODO use paths from csproj
res_temp_dir = res_data_dir.plus_file("temp");
res_temp_assemblies_base_dir = res_temp_dir.plus_file("bin");
res_temp_assemblies_dir = res_temp_assemblies_base_dir.plus_file(_get_expected_build_config());
mono_user_dir = _get_mono_user_dir();
mono_logs_dir = mono_user_dir.plus_file("mono_logs");
#ifdef TOOLS_ENABLED
mono_solutions_dir = mono_user_dir.plus_file("solutions");
build_logs_dir = mono_user_dir.plus_file("build_logs");
String base_path = String("res://") + ProjectSettings::get_singleton()->get("application/config/name");
sln_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".sln");
csproj_filepath = ProjectSettings::get_singleton()->globalize_path(base_path + ".csproj");
#endif
}
_GodotSharpDirs(const _GodotSharpDirs &);
_GodotSharpDirs &operator=(const _GodotSharpDirs &);
public:
static _GodotSharpDirs &get_singleton() {
static _GodotSharpDirs singleton;
return singleton;
}
};
String get_res_data_dir() {
return _GodotSharpDirs::get_singleton().res_data_dir;
}
String get_res_metadata_dir() {
return _GodotSharpDirs::get_singleton().res_metadata_dir;
}
String get_res_assemblies_dir() {
return _GodotSharpDirs::get_singleton().res_assemblies_dir;
}
String get_res_config_dir() {
return _GodotSharpDirs::get_singleton().res_config_dir;
}
String get_res_temp_dir() {
return _GodotSharpDirs::get_singleton().res_temp_dir;
}
String get_res_temp_assemblies_base_dir() {
return _GodotSharpDirs::get_singleton().res_temp_assemblies_base_dir;
}
String get_res_temp_assemblies_dir() {
return _GodotSharpDirs::get_singleton().res_temp_assemblies_dir;
}
String get_mono_user_dir() {
return _GodotSharpDirs::get_singleton().mono_user_dir;
}
String get_mono_logs_dir() {
return _GodotSharpDirs::get_singleton().mono_logs_dir;
}
#ifdef TOOLS_ENABLED
String get_mono_solutions_dir() {
return _GodotSharpDirs::get_singleton().mono_solutions_dir;
}
String get_build_logs_dir() {
return _GodotSharpDirs::get_singleton().build_logs_dir;
}
String get_project_sln_path() {
return _GodotSharpDirs::get_singleton().sln_filepath;
}
String get_project_csproj_path() {
return _GodotSharpDirs::get_singleton().csproj_filepath;
}
#endif
}

View file

@ -0,0 +1,58 @@
/*************************************************************************/
/* godotsharp_dirs.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GODOTSHARP_DIRS_H
#define GODOTSHARP_DIRS_H
#include "ustring.h"
namespace GodotSharpDirs {
String get_res_data_dir();
String get_res_metadata_dir();
String get_res_assemblies_dir();
String get_res_config_dir();
String get_res_temp_dir();
String get_res_temp_assemblies_base_dir();
String get_res_temp_assemblies_dir();
String get_mono_user_dir();
String get_mono_logs_dir();
#ifdef TOOLS_ENABLED
String get_mono_solutions_dir();
String get_build_logs_dir();
String get_custom_project_settings_dir();
#endif
String get_project_sln_path();
String get_project_csproj_path();
}
#endif // GODOTSHARP_DIRS_H

View file

@ -0,0 +1,77 @@
/*************************************************************************/
/* mono_gc_handle.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) {
return mono_gchandle_new(
p_object,
false /* do not pin the object */
);
}
uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) {
return mono_gchandle_new_weakref(
p_object,
true /* track_resurrection: allows us to invoke _notification(NOTIFICATION_PREDELETE) while disposing */
);
}
Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) {
return memnew(MonoGCHandle(make_strong_handle(p_object)));
}
Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) {
return memnew(MonoGCHandle(make_weak_handle(p_object)));
}
void MonoGCHandle::release() {
if (!released && GDMono::get_singleton()->is_runtime_initialized()) {
mono_gchandle_free(handle);
released = true;
}
}
MonoGCHandle::MonoGCHandle(uint32_t p_handle) {
released = false;
handle = p_handle;
}
MonoGCHandle::~MonoGCHandle() {
release();
}

View file

@ -0,0 +1,63 @@
/*************************************************************************/
/* mono_gc_handle.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef CSHARP_GC_HANDLE_H
#define CSHARP_GC_HANDLE_H
#include <mono/jit/jit.h>
#include "reference.h"
class MonoGCHandle : public Reference {
GDCLASS(MonoGCHandle, Reference)
bool released;
uint32_t handle;
public:
static uint32_t make_strong_handle(MonoObject *p_object);
static uint32_t make_weak_handle(MonoObject *p_object);
static Ref<MonoGCHandle> create_strong(MonoObject *p_object);
static Ref<MonoGCHandle> create_weak(MonoObject *p_object);
_FORCE_INLINE_ MonoObject *get_target() const { return released ? NULL : mono_gchandle_get_target(handle); }
_FORCE_INLINE_ void set_handle(uint32_t p_handle) {
handle = p_handle;
released = false;
}
void release();
MonoGCHandle(uint32_t p_handle);
~MonoGCHandle();
};
#endif // CSHARP_GC_HANDLE_H

View file

@ -0,0 +1,771 @@
/*************************************************************************/
/* gd_mono.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_mono.h"
#include <mono/metadata/mono-config.h>
#include <mono/metadata/mono-debug.h>
#include "os/dir_access.h"
#include "os/file_access.h"
#include "os/os.h"
#include "os/thread.h"
#include "project_settings.h"
#include "../csharp_script.h"
#include "../utils/path_utils.h"
#include "gd_mono_utils.h"
#ifdef TOOLS_ENABLED
#include "../editor/godotsharp_editor.h"
#endif
#ifdef MONO_PRINT_HANDLER_ENABLED
void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) {
if (is_stdout) {
OS::get_singleton()->print(string);
} else {
OS::get_singleton()->printerr(string);
}
}
#endif
GDMono *GDMono::singleton = NULL;
#ifdef DEBUG_ENABLED
static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
do {
if (mono_is_debugger_attached())
return true;
int last_tick = OS::get_singleton()->get_ticks_msec();
OS::get_singleton()->delay_usec((p_msecs < 25 ? p_msecs : 25) * 1000);
int tdiff = OS::get_singleton()->get_ticks_msec() - last_tick;
if (tdiff > p_msecs) {
p_msecs = 0;
} else {
p_msecs -= tdiff;
}
} while (p_msecs > 0);
return mono_is_debugger_attached();
}
#endif
#ifdef TOOLS_ENABLED
// temporary workaround. should be provided from Main::setup/setup2 instead
bool _is_project_manager_requested() {
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
const String &arg = E->get();
if (arg == "-p" || arg == "--project-manager")
return true;
}
return false;
}
#endif
#ifdef DEBUG_ENABLED
void gdmono_debug_init() {
mono_debug_init(MONO_DEBUG_FORMAT_MONO);
int da_port = GLOBAL_DEF("mono/debugger_agent/port", 23685);
bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() ||
ProjectSettings::get_singleton()->get_resource_path().empty() ||
_is_project_manager_requested()) {
return;
}
#endif
CharString da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
.utf8();
// --debugger-agent=help
const char *options[] = {
"--soft-breakpoints",
da_args.get_data()
};
mono_jit_parse_options(2, (char **)options);
}
#endif
void GDMono::initialize() {
ERR_FAIL_NULL(Engine::get_singleton());
OS::get_singleton()->print("Initializing mono...\n");
#ifdef DEBUG_METHODS_ENABLED
_initialize_and_check_api_hashes();
#endif
GDMonoLog::get_singleton()->initialize();
#ifdef MONO_PRINT_HANDLER_ENABLED
mono_trace_set_print_handler(gdmono_MonoPrintCallback);
mono_trace_set_printerr_handler(gdmono_MonoPrintCallback);
#endif
#ifdef WINDOWS_ENABLED
mono_reg_info = MonoRegUtils::find_mono();
CharString assembly_dir;
CharString config_dir;
if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
assembly_dir = mono_reg_info.assembly_dir.utf8();
}
if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
config_dir = mono_reg_info.config_dir.utf8();
}
mono_set_dirs(assembly_dir.length() ? assembly_dir.get_data() : NULL,
config_dir.length() ? config_dir.get_data() : NULL);
#else
mono_set_dirs(NULL, NULL);
#endif
GDMonoAssembly::initialize();
#ifdef DEBUG_ENABLED
gdmono_debug_init();
#endif
mono_config_parse(NULL);
root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
ERR_EXPLAIN("Mono: Failed to initialize runtime");
ERR_FAIL_NULL(root_domain);
GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread());
runtime_initialized = true;
OS::get_singleton()->print("Mono: Runtime initialized\n");
// mscorlib assembly MUST be present at initialization
ERR_EXPLAIN("Mono: Failed to load mscorlib assembly");
ERR_FAIL_COND(!_load_corlib_assembly());
#ifdef TOOLS_ENABLED
// The tools domain must be loaded here, before the scripts domain.
// Otherwise domain unload on the scripts domain will hang indefinitely.
ERR_EXPLAIN("Mono: Failed to load tools domain");
ERR_FAIL_COND(_load_tools_domain() != OK);
// TODO move to editor init callback, and do it lazily when required before editor init (e.g.: bindings generation)
ERR_EXPLAIN("Mono: Failed to load Editor Tools assembly");
ERR_FAIL_COND(!_load_editor_tools_assembly());
#endif
ERR_EXPLAIN("Mono: Failed to load scripts domain");
ERR_FAIL_COND(_load_scripts_domain() != OK);
#ifdef DEBUG_ENABLED
bool debugger_attached = _wait_for_debugger_msecs(500);
if (!debugger_attached && OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->printerr("Mono: Debugger wait timeout\n");
#endif
_register_internal_calls();
// The following assemblies are not required at initialization
_load_all_script_assemblies();
OS::get_singleton()->print("Mono: EVERYTHING OK\n");
}
#ifndef MONO_GLUE_DISABLED
namespace GodotSharpBindings {
uint64_t get_core_api_hash();
uint64_t get_editor_api_hash();
void register_generated_icalls();
} // namespace GodotSharpBindings
#endif
void GDMono::_register_internal_calls() {
#ifndef MONO_GLUE_DISABLED
GodotSharpBindings::register_generated_icalls();
#endif
#ifdef TOOLS_ENABLED
GodotSharpBuilds::_register_internal_calls();
#endif
}
#ifdef DEBUG_METHODS_ENABLED
void GDMono::_initialize_and_check_api_hashes() {
api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE);
#ifndef MONO_GLUE_DISABLED
if (api_core_hash != GodotSharpBindings::get_core_api_hash()) {
ERR_PRINT("Mono: Core API hash mismatch!");
}
#endif
#ifdef TOOLS_ENABLED
api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR);
#ifndef MONO_GLUE_DISABLED
if (api_editor_hash != GodotSharpBindings::get_editor_api_hash()) {
ERR_PRINT("Mono: Editor API hash mismatch!");
}
#endif
#endif // TOOLS_ENABLED
}
#endif // DEBUG_METHODS_ENABLED
void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) {
assemblies[p_domain_id][p_assembly->get_name()] = p_assembly;
}
bool GDMono::_load_assembly(const String &p_name, GDMonoAssembly **r_assembly) {
CRASH_COND(!r_assembly);
if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + "...\n").utf8());
MonoImageOpenStatus status;
MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
MonoAssembly *assembly = mono_assembly_load_full(aname, NULL, &status, false);
mono_assembly_name_free(aname);
if (!assembly)
return false;
uint32_t domain_id = mono_domain_get_id(mono_domain_get());
GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name);
if (stored_assembly) {
// Loaded by our preload hook (status is not initialized when returning from a preload hook)
ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false);
*r_assembly = *stored_assembly;
} else {
ERR_FAIL_COND_V(status != MONO_IMAGE_OK, false);
MonoImage *assembly_image = mono_assembly_get_image(assembly);
ERR_FAIL_NULL_V(assembly_image, false);
const char *path = mono_image_get_filename(assembly_image);
*r_assembly = memnew(GDMonoAssembly(p_name, path));
Error error = (*r_assembly)->wrapper_for_image(assembly_image);
if (error != OK) {
memdelete(*r_assembly);
*r_assembly = NULL;
ERR_FAIL_V(false);
}
}
if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->print(String("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8());
return true;
}
bool GDMono::_load_corlib_assembly() {
if (corlib_assembly)
return true;
bool success = _load_assembly("mscorlib", &corlib_assembly);
if (success)
GDMonoUtils::update_corlib_cache();
return success;
}
bool GDMono::_load_core_api_assembly() {
if (api_assembly)
return true;
bool success = _load_assembly(API_ASSEMBLY_NAME, &api_assembly);
if (success)
GDMonoUtils::update_godot_api_cache();
return success;
}
#ifdef TOOLS_ENABLED
bool GDMono::_load_editor_api_assembly() {
if (editor_api_assembly)
return true;
return _load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
}
#endif
#ifdef TOOLS_ENABLED
bool GDMono::_load_editor_tools_assembly() {
if (editor_tools_assembly)
return true;
_GDMONO_SCOPE_DOMAIN_(tools_domain)
return _load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly);
}
#endif
bool GDMono::_load_project_assembly() {
if (project_assembly)
return true;
String project_assembly_name = ProjectSettings::get_singleton()->get("application/config/name");
bool success = _load_assembly(project_assembly_name, &project_assembly);
if (success)
mono_assembly_set_main(project_assembly->get_assembly());
return success;
}
bool GDMono::_load_all_script_assemblies() {
#ifndef MONO_GLUE_DISABLED
if (!_load_core_api_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->printerr("Mono: Failed to load Core API assembly\n");
return false;
} else {
#ifdef TOOLS_ENABLED
if (!_load_editor_api_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->printerr("Mono: Failed to load Editor API assembly\n");
return false;
}
#endif
}
if (!_load_project_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->printerr("Mono: Failed to load project assembly\n");
return false;
}
return true;
#else
if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->print("Mono: Glue disbled, ignoring script assemblies\n");
return true;
#endif
}
Error GDMono::_load_scripts_domain() {
ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG);
if (OS::get_singleton()->is_stdout_verbose()) {
OS::get_singleton()->print("Mono: Loading scripts domain...\n");
}
scripts_domain = GDMonoUtils::create_domain("GodotEngine.ScriptsDomain");
ERR_EXPLAIN("Mono: Could not create scripts app domain");
ERR_FAIL_NULL_V(scripts_domain, ERR_CANT_CREATE);
mono_domain_set(scripts_domain, true);
return OK;
}
Error GDMono::_unload_scripts_domain() {
ERR_FAIL_NULL_V(scripts_domain, ERR_BUG);
if (OS::get_singleton()->is_stdout_verbose()) {
OS::get_singleton()->print("Mono: Unloading scripts domain...\n");
}
_GodotSharp::get_singleton()->_dispose_callback();
if (mono_domain_get() != root_domain)
mono_domain_set(root_domain, true);
finalizing_scripts_domain = true;
mono_domain_finalize(scripts_domain, 2000);
finalizing_scripts_domain = false;
_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
api_assembly = NULL;
project_assembly = NULL;
#ifdef TOOLS_ENABLED
editor_api_assembly = NULL;
#endif
MonoDomain *domain = scripts_domain;
scripts_domain = NULL;
_GodotSharp::get_singleton()->_dispose_callback();
MonoObject *ex = NULL;
mono_domain_try_unload(domain, &ex);
if (ex) {
ERR_PRINT("Exception thrown when unloading scripts domain:");
mono_print_unhandled_exception(ex);
return FAILED;
}
return OK;
}
#ifdef TOOLS_ENABLED
Error GDMono::_load_tools_domain() {
ERR_FAIL_COND_V(tools_domain != NULL, ERR_BUG);
if (OS::get_singleton()->is_stdout_verbose()) {
OS::get_singleton()->print("Mono: Loading tools domain...\n");
}
tools_domain = GDMonoUtils::create_domain("GodotEngine.ToolsDomain");
ERR_EXPLAIN("Mono: Could not create tools app domain");
ERR_FAIL_NULL_V(tools_domain, ERR_CANT_CREATE);
return OK;
}
#endif
#ifdef TOOLS_ENABLED
Error GDMono::reload_scripts_domain() {
ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG);
if (scripts_domain) {
Error err = _unload_scripts_domain();
if (err != OK) {
ERR_PRINT("Mono: Failed to unload scripts domain");
return err;
}
}
Error err = _load_scripts_domain();
if (err != OK) {
ERR_PRINT("Mono: Failed to load scripts domain");
return err;
}
if (!_load_all_script_assemblies()) {
if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->printerr("Mono: Failed to load script assemblies\n");
return ERR_CANT_OPEN;
}
return OK;
}
#endif
GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
MonoImage *image = mono_class_get_image(p_raw_class);
if (image == corlib_assembly->get_image())
return corlib_assembly->get_class(p_raw_class);
uint32_t domain_id = mono_domain_get_id(mono_domain_get());
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
const String *k = NULL;
while ((k = domain_assemblies.next(k))) {
GDMonoAssembly *assembly = domain_assemblies.get(*k);
if (assembly->get_image() == image) {
GDMonoClass *klass = assembly->get_class(p_raw_class);
if (klass)
return klass;
}
}
return NULL;
}
void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
const String *k = NULL;
while ((k = domain_assemblies.next(k))) {
memdelete(domain_assemblies.get(*k));
}
assemblies.erase(p_domain_id);
}
GDMono::GDMono() {
singleton = this;
gdmono_log = memnew(GDMonoLog);
runtime_initialized = false;
finalizing_scripts_domain = false;
root_domain = NULL;
scripts_domain = NULL;
#ifdef TOOLS_ENABLED
tools_domain = NULL;
#endif
corlib_assembly = NULL;
api_assembly = NULL;
project_assembly = NULL;
#ifdef TOOLS_ENABLED
editor_api_assembly = NULL;
editor_tools_assembly = NULL;
#endif
#ifdef DEBUG_METHODS_ENABLED
api_core_hash = 0;
#ifdef TOOLS_ENABLED
api_editor_hash = 0;
#endif
#endif
}
GDMono::~GDMono() {
if (runtime_initialized) {
if (scripts_domain) {
Error err = _unload_scripts_domain();
if (err != OK) {
WARN_PRINT("Mono: Failed to unload scripts domain");
}
}
const uint32_t *k = NULL;
while ((k = assemblies.next(k))) {
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies.get(*k);
const String *kk = NULL;
while ((kk = domain_assemblies.next(kk))) {
memdelete(domain_assemblies.get(*kk));
}
}
assemblies.clear();
GDMonoUtils::clear_cache();
OS::get_singleton()->print("Mono: Runtime cleanup...\n");
runtime_initialized = false;
mono_jit_cleanup(root_domain);
}
if (gdmono_log)
memdelete(gdmono_log);
}
_GodotSharp *_GodotSharp::singleton = NULL;
void _GodotSharp::_dispose_object(Object *p_object) {
if (p_object->get_script_instance()) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_object->get_script_instance());
if (cs_instance) {
cs_instance->mono_object_disposed();
return;
}
}
// Unsafe refcount decrement. The managed instance also counts as a reference.
// See: CSharpLanguage::alloc_instance_binding_data(Object *p_object)
if (Object::cast_to<Reference>(p_object)->unreference()) {
memdelete(p_object);
}
}
void _GodotSharp::_dispose_callback() {
#ifndef NO_THREADS
queue_mutex->lock();
#endif
for (List<Object *>::Element *E = obj_delete_queue.front(); E; E = E->next()) {
_dispose_object(E->get());
}
for (List<NodePath *>::Element *E = np_delete_queue.front(); E; E = E->next()) {
memdelete(E->get());
}
for (List<RID *>::Element *E = rid_delete_queue.front(); E; E = E->next()) {
memdelete(E->get());
}
obj_delete_queue.clear();
np_delete_queue.clear();
rid_delete_queue.clear();
queue_empty = true;
#ifndef NO_THREADS
queue_mutex->unlock();
#endif
}
void _GodotSharp::attach_thread() {
GDMonoUtils::attach_current_thread();
}
void _GodotSharp::detach_thread() {
GDMonoUtils::detach_current_thread();
}
bool _GodotSharp::is_finalizing_domain() {
return GDMono::get_singleton()->is_finalizing_scripts_domain();
}
bool _GodotSharp::is_domain_loaded() {
return GDMono::get_singleton()->get_scripts_domain() != NULL;
}
#define ENQUEUE_FOR_DISPOSAL(m_queue, m_inst) \
m_queue.push_back(m_inst); \
if (queue_empty) { \
queue_empty = false; \
call_deferred("_dispose_callback"); \
}
void _GodotSharp::queue_dispose(Object *p_object) {
if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
_dispose_object(p_object);
} else {
#ifndef NO_THREADS
queue_mutex->lock();
#endif
ENQUEUE_FOR_DISPOSAL(obj_delete_queue, p_object);
#ifndef NO_THREADS
queue_mutex->unlock();
#endif
}
}
void _GodotSharp::queue_dispose(NodePath *p_node_path) {
if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
memdelete(p_node_path);
} else {
#ifndef NO_THREADS
queue_mutex->lock();
#endif
ENQUEUE_FOR_DISPOSAL(np_delete_queue, p_node_path);
#ifndef NO_THREADS
queue_mutex->unlock();
#endif
}
}
void _GodotSharp::queue_dispose(RID *p_rid) {
if (Thread::get_main_id() == Thread::get_caller_id() && !GDMono::get_singleton()->is_finalizing_scripts_domain()) {
memdelete(p_rid);
} else {
#ifndef NO_THREADS
queue_mutex->lock();
#endif
ENQUEUE_FOR_DISPOSAL(rid_delete_queue, p_rid);
#ifndef NO_THREADS
queue_mutex->unlock();
#endif
}
}
void _GodotSharp::_bind_methods() {
ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread);
ClassDB::bind_method(D_METHOD("detach_thread"), &_GodotSharp::detach_thread);
ClassDB::bind_method(D_METHOD("is_finalizing_domain"), &_GodotSharp::is_finalizing_domain);
ClassDB::bind_method(D_METHOD("is_domain_loaded"), &_GodotSharp::is_domain_loaded);
ClassDB::bind_method(D_METHOD("_dispose_callback"), &_GodotSharp::_dispose_callback);
}
_GodotSharp::_GodotSharp() {
singleton = this;
queue_empty = true;
#ifndef NO_THREADS
queue_mutex = Mutex::create();
#endif
}
_GodotSharp::~_GodotSharp() {
singleton = NULL;
if (queue_mutex) {
memdelete(queue_mutex);
}
}

View file

@ -0,0 +1,224 @@
/*************************************************************************/
/* gd_mono.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_MONO_H
#define GD_MONO_H
#include "../godotsharp_defs.h"
#include "gd_mono_assembly.h"
#include "gd_mono_log.h"
#ifdef WINDOWS_ENABLED
#include "../utils/mono_reg_utils.h"
#endif
#define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain()
#ifdef TOOLS_ENABLED
#define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain()
#endif
class GDMono {
bool runtime_initialized;
bool finalizing_scripts_domain;
MonoDomain *root_domain;
MonoDomain *scripts_domain;
#ifdef TOOLS_ENABLED
MonoDomain *tools_domain;
#endif
GDMonoAssembly *corlib_assembly;
GDMonoAssembly *api_assembly;
GDMonoAssembly *project_assembly;
#ifdef TOOLS_ENABLED
GDMonoAssembly *editor_api_assembly;
GDMonoAssembly *editor_tools_assembly;
#endif
HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
void _domain_assemblies_cleanup(uint32_t p_domain_id);
bool _load_corlib_assembly();
bool _load_core_api_assembly();
#ifdef TOOLS_ENABLED
bool _load_editor_api_assembly();
bool _load_editor_tools_assembly();
#endif
bool _load_project_assembly();
bool _load_all_script_assemblies();
void _register_internal_calls();
Error _load_scripts_domain();
Error _unload_scripts_domain();
#ifdef TOOLS_ENABLED
Error _load_tools_domain();
#endif
#ifdef DEBUG_METHODS_ENABLED
uint64_t api_core_hash;
#ifdef TOOLS_ENABLED
uint64_t api_editor_hash;
#endif
void _initialize_and_check_api_hashes();
#endif
bool _load_assembly(const String &p_name, GDMonoAssembly **r_assembly);
GDMonoLog *gdmono_log;
#ifdef WINDOWS_ENABLED
MonoRegInfo mono_reg_info;
#endif
protected:
static GDMono *singleton;
public:
#ifdef DEBUG_METHODS_ENABLED
uint64_t get_api_core_hash() { return api_core_hash; }
#ifdef TOOLS_ENABLED
uint64_t get_api_editor_hash() { return api_editor_hash; }
#endif
#endif
enum MemberVisibility {
PRIVATE,
PROTECTED_AND_INTERNAL, // FAM_AND_ASSEM
INTERNAL, // ASSEMBLY
PROTECTED, // FAMILY
PUBLIC
};
static GDMono *get_singleton() { return singleton; }
void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);
_FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized; }
_FORCE_INLINE_ bool is_finalizing_scripts_domain() const { return finalizing_scripts_domain; }
_FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }
#ifdef TOOLS_ENABLED
_FORCE_INLINE_ MonoDomain *get_tools_domain() { return tools_domain; }
#endif
_FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_api_assembly() const { return api_assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
#ifdef TOOLS_ENABLED
_FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_editor_tools_assembly() const { return editor_tools_assembly; }
#endif
#ifdef WINDOWS_ENABLED
const MonoRegInfo &get_mono_reg_info() { return mono_reg_info; }
#endif
GDMonoClass *get_class(MonoClass *p_raw_class);
#ifdef TOOLS_ENABLED
Error reload_scripts_domain();
#endif
void initialize();
GDMono();
~GDMono();
};
class GDMonoScopeDomain {
MonoDomain *prev_domain;
public:
GDMonoScopeDomain(MonoDomain *p_domain) {
MonoDomain *prev_domain = mono_domain_get();
if (prev_domain != p_domain) {
this->prev_domain = prev_domain;
mono_domain_set(p_domain, false);
} else {
this->prev_domain = NULL;
}
}
~GDMonoScopeDomain() {
if (prev_domain)
mono_domain_set(prev_domain, false);
}
};
#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \
GDMonoScopeDomain __gdmono__scope__domain__(m_mono_domain); \
(void)__gdmono__scope__domain__;
class _GodotSharp : public Object {
GDCLASS(_GodotSharp, Object)
friend class GDMono;
void _dispose_object(Object *p_object);
void _dispose_callback();
List<Object *> obj_delete_queue;
List<NodePath *> np_delete_queue;
List<RID *> rid_delete_queue;
bool queue_empty;
#ifndef NO_THREADS
Mutex *queue_mutex;
#endif
protected:
static _GodotSharp *singleton;
static void _bind_methods();
public:
static _GodotSharp *get_singleton() { return singleton; }
void attach_thread();
void detach_thread();
bool is_finalizing_domain();
bool is_domain_loaded();
void queue_dispose(Object *p_object);
void queue_dispose(NodePath *p_node_path);
void queue_dispose(RID *p_rid);
_GodotSharp();
~_GodotSharp();
};
#endif // GD_MONO_H

View file

@ -0,0 +1,327 @@
/*************************************************************************/
/* gd_mono_assembly.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_mono_assembly.h"
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/tokentype.h>
#include "list.h"
#include "os/file_access.h"
#include "os/os.h"
#include "../godotsharp_dirs.h"
#include "gd_mono_class.h"
MonoAssembly *gdmono_load_assembly_from(const String &p_name, const String &p_path) {
MonoDomain *domain = mono_domain_get();
GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
Error err = assembly->load(domain);
ERR_FAIL_COND_V(err != OK, NULL);
GDMono::get_singleton()->add_assembly(mono_domain_get_id(domain), assembly);
return assembly->get_assembly();
}
MonoAssembly *gdmono_MonoAssemblyPreLoad(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
(void)user_data; // UNUSED
MonoAssembly *assembly_loaded = mono_assembly_loaded(aname);
if (assembly_loaded) // Already loaded
return assembly_loaded;
static Vector<String> search_dirs;
if (search_dirs.empty()) {
search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
search_dirs.push_back(OS::get_singleton()->get_resource_dir());
search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
const char *rootdir = mono_assembly_getrootdir();
if (rootdir) {
search_dirs.push_back(String(rootdir).plus_file("mono").plus_file("4.5"));
}
while (assemblies_path) {
if (*assemblies_path)
search_dirs.push_back(*assemblies_path);
++assemblies_path;
}
}
String name = mono_assembly_name_get_name(aname);
bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
String path;
for (int i = 0; i < search_dirs.size(); i++) {
const String &search_dir = search_dirs[i];
if (has_extension) {
path = search_dir.plus_file(name);
if (FileAccess::exists(path))
return gdmono_load_assembly_from(name.get_basename(), path);
} else {
path = search_dir.plus_file(name + ".dll");
if (FileAccess::exists(path))
return gdmono_load_assembly_from(name, path);
path = search_dir.plus_file(name + ".exe");
if (FileAccess::exists(path))
return gdmono_load_assembly_from(name, path);
}
}
return NULL;
}
void GDMonoAssembly::initialize() {
mono_install_assembly_preload_hook(&gdmono_MonoAssemblyPreLoad, NULL);
}
Error GDMonoAssembly::load(MonoDomain *p_domain) {
ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
uint64_t last_modified_time = FileAccess::get_modified_time(path);
Vector<uint8_t> data = FileAccess::get_file_as_array(path);
ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ);
String image_filename(path);
MonoImageOpenStatus status;
image = mono_image_open_from_data_with_name(
(char *)&data[0], data.size(),
true, &status, false,
image_filename.utf8().get_data());
ERR_FAIL_COND_V(status != MONO_IMAGE_OK || image == NULL, ERR_FILE_CANT_OPEN);
#ifdef DEBUG_ENABLED
String pdb_path(path + ".pdb");
if (!FileAccess::exists(pdb_path)) {
pdb_path = path.get_basename() + ".pdb"; // without .dll
if (!FileAccess::exists(pdb_path))
goto no_pdb;
}
pdb_data.clear();
pdb_data = FileAccess::get_file_as_array(pdb_path);
mono_debug_open_image_from_memory(image, &pdb_data[0], pdb_data.size());
no_pdb:
#endif
assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, false);
ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN);
if (mono_image_get_entry_point(image)) {
// TODO should this be removed? do we want to call main? what other effects does this have?
mono_jit_exec(p_domain, assembly, 0, NULL);
}
loaded = true;
modified_time = last_modified_time;
return OK;
}
Error GDMonoAssembly::wrapper_for_image(MonoImage *p_image) {
ERR_FAIL_COND_V(loaded, ERR_FILE_ALREADY_IN_USE);
assembly = mono_image_get_assembly(p_image);
ERR_FAIL_NULL_V(assembly, FAILED);
image = p_image;
mono_image_addref(image);
loaded = true;
return OK;
}
void GDMonoAssembly::unload() {
ERR_FAIL_COND(!loaded);
#ifdef DEBUG_ENABLED
if (pdb_data.size()) {
mono_debug_close_image(image);
pdb_data.clear();
}
#endif
for (Map<MonoClass *, GDMonoClass *>::Element *E = cached_raw.front(); E; E = E->next()) {
memdelete(E->value());
}
cached_classes.clear();
cached_raw.clear();
mono_image_close(image);
assembly = NULL;
image = NULL;
loaded = false;
}
GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) {
ERR_FAIL_COND_V(!loaded, NULL);
ClassKey key(p_namespace, p_name);
GDMonoClass **match = cached_classes.getptr(key);
if (match)
return *match;
MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8());
if (!mono_class)
return NULL;
GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this));
cached_classes[key] = wrapped_class;
cached_raw[mono_class] = wrapped_class;
return wrapped_class;
}
GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
ERR_FAIL_COND_V(!loaded, NULL);
Map<MonoClass *, GDMonoClass *>::Element *match = cached_raw.find(p_mono_class);
if (match)
return match->value();
StringName namespace_name = mono_class_get_namespace(p_mono_class);
StringName class_name = mono_class_get_name(p_mono_class);
GDMonoClass *wrapped_class = memnew(GDMonoClass(namespace_name, class_name, p_mono_class, this));
cached_classes[ClassKey(namespace_name, class_name)] = wrapped_class;
cached_raw[p_mono_class] = wrapped_class;
return wrapped_class;
}
GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) {
GDMonoClass *match = NULL;
if (gdobject_class_cache_updated) {
Map<StringName, GDMonoClass *>::Element *result = gdobject_class_cache.find(p_class);
if (result)
match = result->get();
} else {
List<GDMonoClass *> nested_classes;
int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF);
for (int i = 1; i < rows; i++) {
MonoClass *mono_class = mono_class_get(image, (i + 1) | MONO_TOKEN_TYPE_DEF);
if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class))
continue;
GDMonoClass *current = get_class(mono_class);
if (!current)
continue;
nested_classes.push_back(current);
if (!match && current->get_name() == p_class)
match = current;
while (!nested_classes.empty()) {
GDMonoClass *current_nested = nested_classes.front()->get();
nested_classes.pop_back();
void *iter = NULL;
while (true) {
MonoClass *raw_nested = mono_class_get_nested_types(current_nested->get_raw(), &iter);
if (!raw_nested)
break;
GDMonoClass *nested_class = get_class(raw_nested);
if (nested_class) {
gdobject_class_cache.insert(nested_class->get_name(), nested_class);
nested_classes.push_back(nested_class);
}
}
}
gdobject_class_cache.insert(current->get_name(), current);
}
gdobject_class_cache_updated = true;
}
return match;
}
GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) {
loaded = false;
gdobject_class_cache_updated = false;
name = p_name;
path = p_path;
modified_time = 0;
assembly = NULL;
image = NULL;
}
GDMonoAssembly::~GDMonoAssembly() {
if (loaded)
unload();
}

View file

@ -0,0 +1,113 @@
/*************************************************************************/
/* gd_mono_assembly.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_MONO_ASSEMBLY_H
#define GD_MONO_ASSEMBLY_H
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include "gd_mono_utils.h"
#include "hash_map.h"
#include "map.h"
#include "ustring.h"
class GDMonoAssembly {
struct ClassKey {
struct Hasher {
static _FORCE_INLINE_ uint32_t hash(const ClassKey &p_key) {
uint32_t hash = 0;
GDMonoUtils::hash_combine(hash, p_key.namespace_name.hash());
GDMonoUtils::hash_combine(hash, p_key.class_name.hash());
return hash;
}
};
_FORCE_INLINE_ bool operator==(const ClassKey &p_a) const {
return p_a.class_name == class_name && p_a.namespace_name == namespace_name;
}
ClassKey() {}
ClassKey(const StringName &p_namespace_name, const StringName &p_class_name) {
namespace_name = p_namespace_name;
class_name = p_class_name;
}
StringName namespace_name;
StringName class_name;
};
MonoAssembly *assembly;
MonoImage *image;
bool loaded;
String name;
String path;
uint64_t modified_time;
HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes;
Map<MonoClass *, GDMonoClass *> cached_raw;
bool gdobject_class_cache_updated;
Map<StringName, GDMonoClass *> gdobject_class_cache;
#ifdef DEBUG_ENABLED
Vector<uint8_t> pdb_data;
#endif
friend class GDMono;
static void initialize();
public:
Error load(MonoDomain *p_domain);
Error wrapper_for_image(MonoImage *p_image);
void unload();
_FORCE_INLINE_ bool is_loaded() const { return loaded; }
_FORCE_INLINE_ MonoImage *get_image() const { return image; }
_FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; }
_FORCE_INLINE_ String get_name() const { return name; }
_FORCE_INLINE_ String get_path() const { return path; }
_FORCE_INLINE_ uint64_t get_modified_time() const { return modified_time; }
GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_class);
GDMonoClass *get_class(MonoClass *p_mono_class);
GDMonoClass *get_object_derived_class(const StringName &p_class);
GDMonoAssembly(const String &p_name, const String &p_path = String());
~GDMonoAssembly();
};
#endif // GD_MONO_ASSEMBLY_H

View file

@ -0,0 +1,381 @@
/*************************************************************************/
/* gd_mono_class.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_mono_class.h"
#include <mono/metadata/attrdefs.h>
#include "gd_mono_assembly.h"
MonoType *GDMonoClass::get_raw_type(GDMonoClass *p_class) {
return mono_class_get_type(p_class->get_raw());
}
bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
return mono_class_is_assignable_from(mono_class, p_from->mono_class);
}
GDMonoClass *GDMonoClass::get_parent_class() {
if (assembly) {
MonoClass *parent_mono_class = mono_class_get_parent(mono_class);
if (parent_mono_class) {
return GDMono::get_singleton()->get_class(parent_mono_class);
}
}
return NULL;
}
bool GDMonoClass::has_method(const StringName &p_name) {
return get_method(p_name) != NULL;
}
bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) {
#ifdef DEBUG_ENABLED
ERR_FAIL_NULL_V(p_attr_class, false);
#endif
if (!attrs_fetched)
fetch_attributes();
if (!attributes)
return false;
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw());
}
MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) {
#ifdef DEBUG_ENABLED
ERR_FAIL_NULL_V(p_attr_class, NULL);
#endif
if (!attrs_fetched)
fetch_attributes();
if (!attributes)
return NULL;
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw());
}
void GDMonoClass::fetch_attributes() {
ERR_FAIL_COND(attributes != NULL);
attributes = mono_custom_attrs_from_class(get_raw());
attrs_fetched = true;
}
void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base) {
CRASH_COND(!CACHED_CLASS(GodotObject)->is_assignable_from(this));
if (methods_fetched)
return;
void *iter = NULL;
MonoMethod *raw_method = NULL;
while ((raw_method = mono_class_get_methods(get_raw(), &iter)) != NULL) {
StringName name = mono_method_get_name(raw_method);
GDMonoMethod *method = get_method(raw_method, name);
ERR_CONTINUE(!method);
if (method->get_name() != name) {
#ifdef DEBUG_ENABLED
String fullname = method->get_ret_type_full_name() + " " + name + "(" + method->get_signature_desc(true) + ")";
WARN_PRINTS("Method `" + fullname + "` is hidden by Godot API method. Should be `" +
method->get_full_name_no_class() + "`. In class `" + namespace_name + "." + class_name + "`.");
#endif
continue;
}
#ifdef DEBUG_ENABLED
// For debug builds, we also fetched from native base classes as well before if this is not a native base class.
// This allows us to warn the user here if he is using snake_case by mistake.
if (p_native_base != this) {
GDMonoClass *native_top = p_native_base;
while (native_top) {
GDMonoMethod *m = native_top->get_method(name, method->get_parameters_count());
if (m && m->get_name() != name) {
// found
String fullname = m->get_ret_type_full_name() + " " + name + "(" + m->get_signature_desc(true) + ")";
WARN_PRINTS("Method `" + fullname + "` should be `" + m->get_full_name_no_class() +
"`. In class `" + namespace_name + "." + class_name + "`.");
break;
}
if (native_top == CACHED_CLASS(GodotObject))
break;
native_top = native_top->get_parent_class();
}
}
#endif
uint32_t flags = mono_method_get_flags(method->mono_method, NULL);
if (!(flags & MONO_METHOD_ATTR_VIRTUAL))
continue;
// Virtual method of Godot Object derived type, let's try to find GodotMethod attribute
GDMonoClass *top = p_native_base;
while (top) {
GDMonoMethod *base_method = top->get_method(name, method->get_parameters_count());
if (base_method && base_method->has_attribute(CACHED_CLASS(GodotMethodAttribute))) {
// Found base method with GodotMethod attribute.
// We get the original API method name from this attribute.
// This name must point to the virtual method.
MonoObject *attr = base_method->get_attribute(CACHED_CLASS(GodotMethodAttribute));
StringName godot_method_name = CACHED_FIELD(GodotMethodAttribute, methodName)->get_string_value(attr);
#ifdef DEBUG_ENABLED
CRASH_COND(godot_method_name == StringName());
#endif
MethodKey key = MethodKey(godot_method_name, method->get_parameters_count());
GDMonoMethod **existing_method = methods.getptr(key);
if (existing_method)
memdelete(*existing_method); // Must delete old one
methods.set(key, method);
break;
}
if (top == CACHED_CLASS(GodotObject))
break;
top = top->get_parent_class();
}
}
methods_fetched = true;
}
GDMonoMethod *GDMonoClass::get_method(const StringName &p_name) {
ERR_FAIL_COND_V(!methods_fetched, NULL);
const MethodKey *k = NULL;
while ((k = methods.next(k))) {
if (k->name == p_name)
return methods.get(*k);
}
return NULL;
}
GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) {
MethodKey key = MethodKey(p_name, p_params_count);
GDMonoMethod **match = methods.getptr(key);
if (match)
return *match;
if (methods_fetched)
return NULL;
MonoMethod *raw_method = mono_class_get_method_from_name(mono_class, String(p_name).utf8().get_data(), p_params_count);
if (raw_method) {
GDMonoMethod *method = memnew(GDMonoMethod(p_name, raw_method));
methods.set(key, method);
return method;
}
return NULL;
}
GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method) {
MonoMethodSignature *sig = mono_method_signature(p_raw_method);
int params_count = mono_signature_get_param_count(sig);
StringName method_name = mono_method_get_name(p_raw_method);
return get_method(p_raw_method, method_name, params_count);
}
GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name) {
MonoMethodSignature *sig = mono_method_signature(p_raw_method);
int params_count = mono_signature_get_param_count(sig);
return get_method(p_raw_method, p_name, params_count);
}
GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count) {
ERR_FAIL_NULL_V(p_raw_method, NULL);
MethodKey key = MethodKey(p_name, p_params_count);
GDMonoMethod **match = methods.getptr(key);
if (match)
return *match;
GDMonoMethod *method = memnew(GDMonoMethod(p_name, p_raw_method));
methods.set(key, method);
return method;
}
GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, bool p_include_namespace) {
MonoMethodDesc *desc = mono_method_desc_new(p_description.utf8().get_data(), p_include_namespace);
MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class);
mono_method_desc_free(desc);
return get_method(method);
}
GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
Map<StringName, GDMonoField *>::Element *result = fields.find(p_name);
if (result)
return result->value();
if (fields_fetched)
return NULL;
MonoClassField *raw_field = mono_class_get_field_from_name(mono_class, String(p_name).utf8().get_data());
if (raw_field) {
GDMonoField *field = memnew(GDMonoField(raw_field, this));
fields.insert(p_name, field);
return field;
}
return NULL;
}
const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
if (fields_fetched)
return fields_list;
void *iter = NULL;
MonoClassField *raw_field = NULL;
while ((raw_field = mono_class_get_fields(get_raw(), &iter)) != NULL) {
StringName name = mono_field_get_name(raw_field);
Map<StringName, GDMonoField *>::Element *match = fields.find(name);
if (match) {
fields_list.push_back(match->get());
} else {
GDMonoField *field = memnew(GDMonoField(raw_field, this));
fields.insert(name, field);
fields_list.push_back(field);
}
}
fields_fetched = true;
return fields_list;
}
GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) {
namespace_name = p_namespace;
class_name = p_name;
mono_class = p_class;
assembly = p_assembly;
attrs_fetched = false;
attributes = NULL;
methods_fetched = false;
fields_fetched = false;
}
GDMonoClass::~GDMonoClass() {
if (attributes) {
mono_custom_attrs_free(attributes);
}
for (Map<StringName, GDMonoField *>::Element *E = fields.front(); E; E = E->next()) {
memdelete(E->value());
}
{
// Ugly workaround...
// We may have duplicated values, because we redirect snake_case methods to PascalCasel (only Godot API methods).
// This way, we end with both the snake_case name and the PascalCasel name paired with the same method.
// Therefore, we must avoid deleting the same pointer twice.
int offset = 0;
Vector<GDMonoMethod *> deleted_methods;
deleted_methods.resize(methods.size());
const MethodKey *k = NULL;
while ((k = methods.next(k))) {
GDMonoMethod *method = methods.get(*k);
if (method) {
for (int i = 0; i < offset; i++) {
if (deleted_methods[i] == method) {
// Already deleted
goto already_deleted;
}
}
deleted_methods[offset] = method;
++offset;
memdelete(method);
}
already_deleted:;
}
methods.clear();
}
}

View file

@ -0,0 +1,124 @@
/*************************************************************************/
/* gd_mono_class.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_MONO_CLASS_H
#define GD_MONO_CLASS_H
#include <mono/metadata/debug-helpers.h>
#include "map.h"
#include "ustring.h"
#include "gd_mono_field.h"
#include "gd_mono_header.h"
#include "gd_mono_method.h"
#include "gd_mono_utils.h"
class GDMonoClass {
struct MethodKey {
struct Hasher {
static _FORCE_INLINE_ uint32_t hash(const MethodKey &p_key) {
uint32_t hash = 0;
GDMonoUtils::hash_combine(hash, p_key.name.hash());
GDMonoUtils::hash_combine(hash, HashMapHasherDefault::hash(p_key.params_count));
return hash;
}
};
_FORCE_INLINE_ bool operator==(const MethodKey &p_a) const {
return p_a.params_count == params_count && p_a.name == name;
}
MethodKey() {}
MethodKey(const StringName &p_name, int p_params_count) {
name = p_name;
params_count = p_params_count;
}
StringName name;
int params_count;
};
StringName namespace_name;
StringName class_name;
MonoClass *mono_class;
GDMonoAssembly *assembly;
bool attrs_fetched;
MonoCustomAttrInfo *attributes;
bool methods_fetched;
HashMap<MethodKey, GDMonoMethod *, MethodKey::Hasher> methods;
bool fields_fetched;
Map<StringName, GDMonoField *> fields;
Vector<GDMonoField *> fields_list;
friend class GDMonoAssembly;
GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly);
public:
static MonoType *get_raw_type(GDMonoClass *p_class);
bool is_assignable_from(GDMonoClass *p_from) const;
_FORCE_INLINE_ StringName get_namespace() const { return namespace_name; }
_FORCE_INLINE_ StringName get_name() const { return class_name; }
_FORCE_INLINE_ MonoClass *get_raw() const { return mono_class; }
_FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
GDMonoClass *get_parent_class();
bool has_method(const StringName &p_name);
bool has_attribute(GDMonoClass *p_attr_class);
MonoObject *get_attribute(GDMonoClass *p_attr_class);
void fetch_attributes();
void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base);
GDMonoMethod *get_method(const StringName &p_name);
GDMonoMethod *get_method(const StringName &p_name, int p_params_count);
GDMonoMethod *get_method(MonoMethod *p_raw_method);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count);
GDMonoMethod *get_method_with_desc(const String &p_description, bool p_includes_namespace);
GDMonoField *get_field(const StringName &p_name);
const Vector<GDMonoField *> &get_all_fields();
~GDMonoClass();
};
#endif // GD_MONO_CLASS_H

View file

@ -0,0 +1,362 @@
/*************************************************************************/
/* gd_mono_field.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_mono_field.h"
#include <mono/metadata/attrdefs.h>
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
mono_field_set_value(p_object, mono_field, &p_ptr);
}
void GDMonoField::set_value(MonoObject *p_object, const Variant &p_value) {
#define SET_FROM_STRUCT_AND_BREAK(m_type) \
{ \
const m_type &val = p_value.operator m_type(); \
MARSHALLED_OUT(m_type, val, raw); \
mono_field_set_value(p_object, mono_field, raw); \
break; \
}
#define SET_FROM_PRIMITIVE(m_type) \
{ \
m_type val = p_value.operator m_type(); \
mono_field_set_value(p_object, mono_field, &val); \
}
#define SET_FROM_ARRAY_AND_BREAK(m_type) \
{ \
MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator m_type()); \
mono_field_set_value(p_object, mono_field, &managed); \
break; \
}
switch (type.type_encoding) {
case MONO_TYPE_BOOLEAN: {
SET_FROM_PRIMITIVE(bool);
} break;
case MONO_TYPE_I1: {
SET_FROM_PRIMITIVE(signed char);
} break;
case MONO_TYPE_I2: {
SET_FROM_PRIMITIVE(signed short);
} break;
case MONO_TYPE_I4: {
SET_FROM_PRIMITIVE(signed int);
} break;
case MONO_TYPE_I8: {
SET_FROM_PRIMITIVE(int64_t);
} break;
case MONO_TYPE_U1: {
SET_FROM_PRIMITIVE(unsigned char);
} break;
case MONO_TYPE_U2: {
SET_FROM_PRIMITIVE(unsigned short);
} break;
case MONO_TYPE_U4: {
SET_FROM_PRIMITIVE(unsigned int);
} break;
case MONO_TYPE_U8: {
SET_FROM_PRIMITIVE(uint64_t);
} break;
case MONO_TYPE_R4: {
SET_FROM_PRIMITIVE(float);
} break;
case MONO_TYPE_R8: {
SET_FROM_PRIMITIVE(double);
} break;
case MONO_TYPE_STRING: {
MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
mono_field_set_value(p_object, mono_field, mono_string);
} break;
case MONO_TYPE_VALUETYPE: {
GDMonoClass *tclass = type.type_class;
if (tclass == CACHED_CLASS(Vector2))
SET_FROM_STRUCT_AND_BREAK(Vector2);
if (tclass == CACHED_CLASS(Rect2))
SET_FROM_STRUCT_AND_BREAK(Rect2);
if (tclass == CACHED_CLASS(Transform2D))
SET_FROM_STRUCT_AND_BREAK(Transform2D);
if (tclass == CACHED_CLASS(Vector3))
SET_FROM_STRUCT_AND_BREAK(Vector3);
if (tclass == CACHED_CLASS(Basis))
SET_FROM_STRUCT_AND_BREAK(Basis);
if (tclass == CACHED_CLASS(Quat))
SET_FROM_STRUCT_AND_BREAK(Quat);
if (tclass == CACHED_CLASS(Transform))
SET_FROM_STRUCT_AND_BREAK(Transform);
if (tclass == CACHED_CLASS(Rect3))
SET_FROM_STRUCT_AND_BREAK(Rect3);
if (tclass == CACHED_CLASS(Color))
SET_FROM_STRUCT_AND_BREAK(Color);
if (tclass == CACHED_CLASS(Plane))
SET_FROM_STRUCT_AND_BREAK(Plane);
ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + tclass->get_name());
ERR_FAIL();
} break;
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class));
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
SET_FROM_ARRAY_AND_BREAK(Array);
if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
SET_FROM_ARRAY_AND_BREAK(PoolByteArray);
if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
SET_FROM_ARRAY_AND_BREAK(PoolIntArray);
if (array_type->eklass == REAL_T_MONOCLASS)
SET_FROM_ARRAY_AND_BREAK(PoolRealArray);
if (array_type->eklass == CACHED_CLASS_RAW(String))
SET_FROM_ARRAY_AND_BREAK(PoolStringArray);
if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
SET_FROM_ARRAY_AND_BREAK(PoolVector2Array);
if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
SET_FROM_ARRAY_AND_BREAK(PoolVector3Array);
if (array_type->eklass == CACHED_CLASS_RAW(Color))
SET_FROM_ARRAY_AND_BREAK(PoolColorArray);
ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed array of unmarshallable element type.");
ERR_FAIL();
} break;
case MONO_TYPE_CLASS: {
GDMonoClass *type_class = type.type_class;
// GodotObject
if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *());
mono_field_set_value(p_object, mono_field, &managed);
break;
}
if (CACHED_CLASS(NodePath) == type_class) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
mono_field_set_value(p_object, mono_field, &managed);
break;
}
if (CACHED_CLASS(RID) == type_class) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator RID());
mono_field_set_value(p_object, mono_field, &managed);
break;
}
ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
ERR_FAIL();
} break;
case MONO_TYPE_OBJECT: {
GDMonoClass *type_class = type.type_class;
// Variant
switch (p_value.get_type()) {
case Variant::BOOL: {
SET_FROM_PRIMITIVE(bool);
} break;
case Variant::INT: {
SET_FROM_PRIMITIVE(int);
} break;
case Variant::REAL: {
#ifdef REAL_T_IS_DOUBLE
SET_FROM_PRIMITIVE(double);
#else
SET_FROM_PRIMITIVE(float);
#endif
} break;
case Variant::STRING: {
MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
mono_field_set_value(p_object, mono_field, mono_string);
} break;
case Variant::VECTOR2: SET_FROM_STRUCT_AND_BREAK(Vector2);
case Variant::RECT2: SET_FROM_STRUCT_AND_BREAK(Rect2);
case Variant::VECTOR3: SET_FROM_STRUCT_AND_BREAK(Vector3);
case Variant::TRANSFORM2D: SET_FROM_STRUCT_AND_BREAK(Transform2D);
case Variant::PLANE: SET_FROM_STRUCT_AND_BREAK(Plane);
case Variant::QUAT: SET_FROM_STRUCT_AND_BREAK(Quat);
case Variant::RECT3: SET_FROM_STRUCT_AND_BREAK(Rect3);
case Variant::BASIS: SET_FROM_STRUCT_AND_BREAK(Basis);
case Variant::TRANSFORM: SET_FROM_STRUCT_AND_BREAK(Transform);
case Variant::COLOR: SET_FROM_STRUCT_AND_BREAK(Color);
case Variant::NODE_PATH: {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
mono_field_set_value(p_object, mono_field, &managed);
} break;
case Variant::_RID: {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator RID());
mono_field_set_value(p_object, mono_field, &managed);
} break;
case Variant::OBJECT: {
MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *());
mono_field_set_value(p_object, mono_field, managed);
break;
}
case Variant::DICTIONARY: {
MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
mono_field_set_value(p_object, mono_field, &managed);
} break;
case Variant::ARRAY: SET_FROM_ARRAY_AND_BREAK(Array);
case Variant::POOL_BYTE_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolByteArray);
case Variant::POOL_INT_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolIntArray);
case Variant::POOL_REAL_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolRealArray);
case Variant::POOL_STRING_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolStringArray);
case Variant::POOL_VECTOR2_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolVector2Array);
case Variant::POOL_VECTOR3_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolVector3Array);
case Variant::POOL_COLOR_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolColorArray);
#undef SET_FROM_ARRAY_AND_BREAK
default: break;
}
} break;
case MONO_TYPE_GENERICINST: {
if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_raw()) {
MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
mono_field_set_value(p_object, mono_field, &managed);
break;
}
} break;
default: {
ERR_PRINTS(String() + "Attempted to set the value of a field of unexpected type encoding: " + itos(type.type_encoding));
} break;
}
#undef SET_FROM_STRUCT_AND_BREAK
#undef SET_FROM_PRIMITIVE
}
bool GDMonoField::get_bool_value(MonoObject *p_object) {
return UNBOX_BOOLEAN(get_value(p_object));
}
int GDMonoField::get_int_value(MonoObject *p_object) {
return UNBOX_INT32(get_value(p_object));
}
String GDMonoField::get_string_value(MonoObject *p_object) {
MonoObject *val = get_value(p_object);
return val ? GDMonoMarshal::mono_string_to_godot((MonoString *)val) : String();
}
bool GDMonoField::has_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, false);
if (!attrs_fetched)
fetch_attributes();
if (!attributes)
return false;
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw());
}
MonoObject *GDMonoField::get_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, NULL);
if (!attrs_fetched)
fetch_attributes();
if (!attributes)
return NULL;
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw());
}
void GDMonoField::fetch_attributes() {
ERR_FAIL_COND(attributes != NULL);
attributes = mono_custom_attrs_from_field(owner->get_raw(), get_raw());
attrs_fetched = true;
}
bool GDMonoField::is_static() {
return mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_STATIC;
}
GDMono::MemberVisibility GDMonoField::get_visibility() {
switch (mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_FIELD_ACCESS_MASK) {
case MONO_FIELD_ATTR_PRIVATE:
return GDMono::PRIVATE;
case MONO_FIELD_ATTR_FAM_AND_ASSEM:
return GDMono::PROTECTED_AND_INTERNAL;
case MONO_FIELD_ATTR_ASSEMBLY:
return GDMono::INTERNAL;
case MONO_FIELD_ATTR_FAMILY:
return GDMono::PROTECTED;
case MONO_FIELD_ATTR_PUBLIC:
return GDMono::PUBLIC;
default:
ERR_FAIL_V(GDMono::PRIVATE);
}
}
GDMonoField::GDMonoField(MonoClassField *p_raw_field, GDMonoClass *p_owner) {
owner = p_owner;
mono_field = p_raw_field;
name = mono_field_get_name(mono_field);
MonoType *field_type = mono_field_get_type(mono_field);
type.type_encoding = mono_type_get_type(field_type);
MonoClass *field_type_class = mono_class_from_mono_type(field_type);
type.type_class = GDMono::get_singleton()->get_class(field_type_class);
attrs_fetched = false;
attributes = NULL;
}
GDMonoField::~GDMonoField() {
if (attributes) {
mono_custom_attrs_free(attributes);
}
}

View file

@ -0,0 +1,74 @@
/*************************************************************************/
/* gd_mono_field.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GDMONOFIELD_H
#define GDMONOFIELD_H
#include "gd_mono.h"
#include "gd_mono_header.h"
class GDMonoField {
GDMonoClass *owner;
MonoClassField *mono_field;
String name;
ManagedType type;
bool attrs_fetched;
MonoCustomAttrInfo *attributes;
public:
_FORCE_INLINE_ String get_name() const { return name; }
_FORCE_INLINE_ ManagedType get_type() const { return type; }
_FORCE_INLINE_ MonoClassField *get_raw() const { return mono_field; }
void set_value_raw(MonoObject *p_object, void *p_ptr);
void set_value(MonoObject *p_object, const Variant &p_value);
_FORCE_INLINE_ MonoObject *get_value(MonoObject *p_object) {
return mono_field_get_value_object(mono_domain_get(), mono_field, p_object);
}
bool get_bool_value(MonoObject *p_object);
int get_int_value(MonoObject *p_object);
String get_string_value(MonoObject *p_object);
bool has_attribute(GDMonoClass *p_attr_class);
MonoObject *get_attribute(GDMonoClass *p_attr_class);
void fetch_attributes();
bool is_static();
GDMono::MemberVisibility get_visibility();
GDMonoField(MonoClassField *p_raw_field, GDMonoClass *p_owner);
~GDMonoField();
};
#endif // GDMONOFIELD_H

View file

@ -0,0 +1,59 @@
/*************************************************************************/
/* gd_mono_header.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_MONO_HEADER_H
#define GD_MONO_HEADER_H
#include "int_types.h"
class GDMonoAssembly;
class GDMonoClass;
class GDMonoMethod;
class GDMonoField;
struct ManagedType {
int type_encoding;
GDMonoClass *type_class;
ManagedType() {
type_class = 0;
}
};
typedef union {
uint32_t _uint32;
float _float;
} mono_float;
typedef union {
uint64_t _uint64;
float _double;
} mono_double;
#endif // GD_MONO_HEADER_H

View file

@ -0,0 +1,66 @@
/*************************************************************************/
/* godotsharp_internals.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_mono_internals.h"
#include "../csharp_script.h"
#include "../mono_gc_handle.h"
#include "gd_mono_utils.h"
namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
// This method should not fail
CRASH_COND(!unmanaged);
// All mono objects created from the managed world (e.g.: `new Player()`)
// need to have a CSharpScript in order for their methods to be callable from the unmanaged side
Reference *ref = Object::cast_to<Reference>(unmanaged);
GDMonoClass *klass = GDMonoUtils::get_object_class(managed);
CRASH_COND(!klass);
Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) :
MonoGCHandle::create_strong(managed);
Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass);
CRASH_COND(script.is_null());
ScriptInstance *si = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
unmanaged->set_script_and_instance(script.get_ref_ptr(), si);
return;
}
}

View file

@ -0,0 +1,42 @@
/*************************************************************************/
/* godotsharp_internals.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_MONO_INTERNALS_H
#define GD_MONO_INTERNALS_H
#include <mono/jit/jit.h>
#include "core/object.h"
namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
}
#endif // GD_MONO_INTERNALS_H

View file

@ -0,0 +1,175 @@
/*************************************************************************/
/* gd_mono_log.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_mono_log.h"
#include <mono/utils/mono-logger.h>
#include <stdlib.h> // abort
#include "os/dir_access.h"
#include "os/os.h"
#include "../godotsharp_dirs.h"
static int log_level_get_id(const char *p_log_level) {
const char *valid_log_levels[] = { "error", "critical", "warning", "message", "info", "debug", NULL };
int i = 0;
while (valid_log_levels[i]) {
if (!strcmp(valid_log_levels[i], p_log_level))
return i;
i++;
}
return -1;
}
void gdmono_MonoLogCallback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) {
FileAccess *f = GDMonoLog::get_singleton()->get_log_file();
if (GDMonoLog::get_singleton()->get_log_level_id() >= log_level_get_id(log_level)) {
String text(message);
text += " (in domain ";
text += log_domain;
if (log_level) {
text += ", ";
text += log_level;
}
text += ")\n";
f->seek_end();
f->store_string(text);
}
if (fatal) {
ERR_PRINTS("Mono: FALTAL ERROR, ABORTING! Logfile: " + GDMonoLog::get_singleton()->get_log_file_path() + "\n");
abort();
}
}
GDMonoLog *GDMonoLog::singleton = NULL;
bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) {
if (!DirAccess::exists(p_logs_dir)) {
DirAccessRef diraccess = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND_V(!diraccess, false);
Error logs_mkdir_err = diraccess->make_dir_recursive(p_logs_dir);
ERR_EXPLAIN("Failed to create mono logs directory");
ERR_FAIL_COND_V(logs_mkdir_err != OK, false);
}
return true;
}
void GDMonoLog::_open_log_file(const String &p_file_path) {
log_file = FileAccess::open(p_file_path, FileAccess::WRITE);
ERR_EXPLAIN("Failed to create log file");
ERR_FAIL_COND(!log_file);
}
void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) {
static const uint64_t MAX_SECS = 5 * 86400;
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND(!da);
Error err = da->change_dir(p_logs_dir);
ERR_FAIL_COND(err != OK);
ERR_FAIL_COND(da->list_dir_begin() != OK);
String current;
while ((current = da->get_next()).length()) {
if (da->current_is_dir())
continue;
if (!current.ends_with(".txt"))
continue;
String name = current.get_basename();
uint64_t unixtime = (uint64_t)name.to_int64();
if (OS::get_singleton()->get_unix_time() - unixtime > MAX_SECS) {
da->remove(current);
}
}
da->list_dir_end();
}
void GDMonoLog::initialize() {
#ifdef DEBUG_ENABLED
const char *log_level = "debug";
#else
const char *log_level = "warning";
#endif
String logs_dir = GodotSharpDirs::get_mono_logs_dir();
if (_try_create_logs_dir(logs_dir)) {
_delete_old_log_files(logs_dir);
log_file_path = logs_dir.plus_file(String::num_int64(OS::get_singleton()->get_unix_time()) + ".txt");
_open_log_file(log_file_path);
}
mono_trace_set_level_string(log_level);
log_level_id = log_level_get_id(log_level);
if (log_file) {
if (OS::get_singleton()->is_stdout_verbose())
OS::get_singleton()->print(String("Mono: Logfile is " + log_file_path + "\n").utf8());
mono_trace_set_log_handler(gdmono_MonoLogCallback, this);
} else {
OS::get_singleton()->printerr("Mono: No log file, using default log handler\n");
}
}
GDMonoLog::GDMonoLog() {
singleton = this;
log_level_id = -1;
}
GDMonoLog::~GDMonoLog() {
singleton = NULL;
if (log_file) {
log_file->close();
memdelete(log_file);
}
}

View file

@ -0,0 +1,61 @@
/*************************************************************************/
/* gd_mono_log.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_MONO_LOG_H
#define GD_MONO_LOG_H
#include "os/file_access.h"
class GDMonoLog {
int log_level_id;
FileAccess *log_file;
String log_file_path;
bool _try_create_logs_dir(const String &p_logs_dir);
void _open_log_file(const String &p_file_path);
void _delete_old_log_files(const String &p_logs_dir);
static GDMonoLog *singleton;
public:
_FORCE_INLINE_ static GDMonoLog *get_singleton() { return singleton; }
void initialize();
_FORCE_INLINE_ FileAccess *get_log_file() { return log_file; }
_FORCE_INLINE_ String get_log_file_path() { return log_file_path; }
_FORCE_INLINE_ int get_log_level_id() { return log_level_id; }
GDMonoLog();
~GDMonoLog();
};
#endif // GD_MONO_LOG_H

View file

@ -0,0 +1,856 @@
/*************************************************************************/
/* gd_mono_marshal.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_mono_marshal.h"
#include "gd_mono.h"
#include "gd_mono_class.h"
namespace GDMonoMarshal {
#define RETURN_BOXED_STRUCT(m_t, m_var_in) \
{ \
const m_t &m_in = m_var_in->operator m_t(); \
MARSHALLED_OUT(m_t, m_in, raw); \
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_t), raw); \
}
#define RETURN_UNBOXED_STRUCT(m_t, m_var_in) \
{ \
float *raw = UNBOX_FLOAT_PTR(m_var_in); \
MARSHALLED_IN(m_t, raw, ret); \
return ret; \
}
Variant::Type managed_to_variant_type(const ManagedType &p_type) {
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN:
return Variant::BOOL;
case MONO_TYPE_I1:
return Variant::INT;
case MONO_TYPE_I2:
return Variant::INT;
case MONO_TYPE_I4:
return Variant::INT;
case MONO_TYPE_I8:
return Variant::INT;
case MONO_TYPE_U1:
return Variant::INT;
case MONO_TYPE_U2:
return Variant::INT;
case MONO_TYPE_U4:
return Variant::INT;
case MONO_TYPE_U8:
return Variant::INT;
case MONO_TYPE_R4:
return Variant::REAL;
case MONO_TYPE_R8:
return Variant::REAL;
case MONO_TYPE_STRING: {
return Variant::STRING;
} break;
case MONO_TYPE_VALUETYPE: {
GDMonoClass *tclass = p_type.type_class;
if (tclass == CACHED_CLASS(Vector2))
return Variant::VECTOR2;
if (tclass == CACHED_CLASS(Rect2))
return Variant::RECT2;
if (tclass == CACHED_CLASS(Transform2D))
return Variant::TRANSFORM2D;
if (tclass == CACHED_CLASS(Vector3))
return Variant::VECTOR3;
if (tclass == CACHED_CLASS(Basis))
return Variant::BASIS;
if (tclass == CACHED_CLASS(Quat))
return Variant::QUAT;
if (tclass == CACHED_CLASS(Transform))
return Variant::TRANSFORM;
if (tclass == CACHED_CLASS(Rect3))
return Variant::RECT3;
if (tclass == CACHED_CLASS(Color))
return Variant::COLOR;
if (tclass == CACHED_CLASS(Plane))
return Variant::PLANE;
} break;
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return Variant::ARRAY;
if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
return Variant::POOL_BYTE_ARRAY;
if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
return Variant::POOL_INT_ARRAY;
if (array_type->eklass == REAL_T_MONOCLASS)
return Variant::POOL_REAL_ARRAY;
if (array_type->eklass == CACHED_CLASS_RAW(String))
return Variant::POOL_STRING_ARRAY;
if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
return Variant::POOL_VECTOR2_ARRAY;
if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
return Variant::POOL_VECTOR3_ARRAY;
if (array_type->eklass == CACHED_CLASS_RAW(Color))
return Variant::POOL_COLOR_ARRAY;
} break;
case MONO_TYPE_CLASS: {
GDMonoClass *type_class = p_type.type_class;
// GodotObject
if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
return Variant::OBJECT;
}
if (CACHED_CLASS(NodePath) == type_class) {
return Variant::NODE_PATH;
}
if (CACHED_CLASS(RID) == type_class) {
return Variant::_RID;
}
} break;
case MONO_TYPE_GENERICINST: {
if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) {
return Variant::DICTIONARY;
}
} break;
}
// No error, the caller will decide what to do in this case
return Variant::NIL;
}
String mono_to_utf8_string(MonoString *p_mono_string) {
MonoError error;
char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error);
ERR_EXPLAIN("Conversion of MonoString to UTF8 failed.");
ERR_FAIL_COND_V(!mono_error_ok(&error), String());
String ret = String::utf8(utf8);
mono_free(utf8);
return ret;
}
String mono_to_utf16_string(MonoString *p_mono_string) {
int len = mono_string_length(p_mono_string);
String ret;
if (len == 0)
return ret;
ret.resize(len + 1);
ret.set(len, 0);
CharType *src = (CharType *)mono_string_chars(p_mono_string);
CharType *dst = &(ret.operator[](0));
for (int i = 0; i < len; i++) {
dst[i] = src[i];
}
return ret;
}
MonoObject *variant_to_mono_object(const Variant *p_var) {
ManagedType type;
type.type_encoding = MONO_TYPE_OBJECT;
return variant_to_mono_object(p_var, type);
}
MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) {
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN: {
MonoBoolean val = p_var->operator bool();
return BOX_BOOLEAN(val);
}
case MONO_TYPE_I1: {
char val = p_var->operator signed char();
return BOX_INT8(val);
}
case MONO_TYPE_I2: {
short val = p_var->operator signed short();
return BOX_INT16(val);
}
case MONO_TYPE_I4: {
int val = p_var->operator signed int();
return BOX_INT32(val);
}
case MONO_TYPE_I8: {
int64_t val = p_var->operator int64_t();
return BOX_INT64(val);
}
case MONO_TYPE_U1: {
char val = p_var->operator unsigned char();
return BOX_UINT8(val);
}
case MONO_TYPE_U2: {
short val = p_var->operator unsigned short();
return BOX_UINT16(val);
}
case MONO_TYPE_U4: {
int val = p_var->operator unsigned int();
return BOX_UINT32(val);
}
case MONO_TYPE_U8: {
uint64_t val = p_var->operator uint64_t();
return BOX_UINT64(val);
}
case MONO_TYPE_R4: {
float val = p_var->operator float();
return BOX_FLOAT(val);
}
case MONO_TYPE_R8: {
double val = p_var->operator double();
return BOX_DOUBLE(val);
}
case MONO_TYPE_STRING: {
return (MonoObject *)mono_string_from_godot(p_var->operator String());
} break;
case MONO_TYPE_VALUETYPE: {
GDMonoClass *tclass = p_type.type_class;
if (tclass == CACHED_CLASS(Vector2))
RETURN_BOXED_STRUCT(Vector2, p_var);
if (tclass == CACHED_CLASS(Rect2))
RETURN_BOXED_STRUCT(Rect2, p_var);
if (tclass == CACHED_CLASS(Transform2D))
RETURN_BOXED_STRUCT(Transform2D, p_var);
if (tclass == CACHED_CLASS(Vector3))
RETURN_BOXED_STRUCT(Vector3, p_var);
if (tclass == CACHED_CLASS(Basis))
RETURN_BOXED_STRUCT(Basis, p_var);
if (tclass == CACHED_CLASS(Quat))
RETURN_BOXED_STRUCT(Quat, p_var);
if (tclass == CACHED_CLASS(Transform))
RETURN_BOXED_STRUCT(Transform, p_var);
if (tclass == CACHED_CLASS(Rect3))
RETURN_BOXED_STRUCT(Rect3, p_var);
if (tclass == CACHED_CLASS(Color))
RETURN_BOXED_STRUCT(Color, p_var);
if (tclass == CACHED_CLASS(Plane))
RETURN_BOXED_STRUCT(Plane, p_var);
} break;
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return (MonoObject *)Array_to_mono_array(p_var->operator Array());
if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray());
if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
return (MonoObject *)PoolIntArray_to_mono_array(p_var->operator PoolIntArray());
if (array_type->eklass == REAL_T_MONOCLASS)
return (MonoObject *)PoolRealArray_to_mono_array(p_var->operator PoolRealArray());
if (array_type->eklass == CACHED_CLASS_RAW(String))
return (MonoObject *)PoolStringArray_to_mono_array(p_var->operator PoolStringArray());
if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
return (MonoObject *)PoolVector2Array_to_mono_array(p_var->operator PoolVector2Array());
if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
return (MonoObject *)PoolVector3Array_to_mono_array(p_var->operator PoolVector3Array());
if (array_type->eklass == CACHED_CLASS_RAW(Color))
return (MonoObject *)PoolColorArray_to_mono_array(p_var->operator PoolColorArray());
ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed array of unmarshallable element type.");
ERR_FAIL_V(NULL);
} break;
case MONO_TYPE_CLASS: {
GDMonoClass *type_class = p_type.type_class;
// GodotObject
if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
}
if (CACHED_CLASS(NodePath) == type_class) {
return GDMonoUtils::create_managed_from(p_var->operator NodePath());
}
if (CACHED_CLASS(RID) == type_class) {
return GDMonoUtils::create_managed_from(p_var->operator RID());
}
} break;
case MONO_TYPE_OBJECT: {
// Variant
switch (p_var->get_type()) {
case Variant::BOOL: {
MonoBoolean val = p_var->operator bool();
return BOX_BOOLEAN(val);
}
case Variant::INT: {
int val = p_var->operator signed int();
return BOX_INT32(val);
}
case Variant::REAL: {
#ifdef REAL_T_IS_DOUBLE
double val = p_var->operator double();
return BOX_DOUBLE(val);
#else
float val = p_var->operator float();
return BOX_FLOAT(val);
#endif
}
case Variant::STRING:
return (MonoObject *)mono_string_from_godot(p_var->operator String());
case Variant::VECTOR2:
RETURN_BOXED_STRUCT(Vector2, p_var);
case Variant::RECT2:
RETURN_BOXED_STRUCT(Rect2, p_var);
case Variant::VECTOR3:
RETURN_BOXED_STRUCT(Vector3, p_var);
case Variant::TRANSFORM2D:
RETURN_BOXED_STRUCT(Transform2D, p_var);
case Variant::PLANE:
RETURN_BOXED_STRUCT(Plane, p_var);
case Variant::QUAT:
RETURN_BOXED_STRUCT(Quat, p_var);
case Variant::RECT3:
RETURN_BOXED_STRUCT(Rect3, p_var);
case Variant::BASIS:
RETURN_BOXED_STRUCT(Basis, p_var);
case Variant::TRANSFORM:
RETURN_BOXED_STRUCT(Transform, p_var);
case Variant::COLOR:
RETURN_BOXED_STRUCT(Color, p_var);
case Variant::NODE_PATH:
return GDMonoUtils::create_managed_from(p_var->operator NodePath());
case Variant::_RID:
return GDMonoUtils::create_managed_from(p_var->operator RID());
case Variant::OBJECT: {
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
}
case Variant::DICTIONARY:
return Dictionary_to_mono_object(p_var->operator Dictionary());
case Variant::ARRAY:
return (MonoObject *)Array_to_mono_array(p_var->operator Array());
case Variant::POOL_BYTE_ARRAY:
return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray());
case Variant::POOL_INT_ARRAY:
return (MonoObject *)PoolIntArray_to_mono_array(p_var->operator PoolIntArray());
case Variant::POOL_REAL_ARRAY:
return (MonoObject *)PoolRealArray_to_mono_array(p_var->operator PoolRealArray());
case Variant::POOL_STRING_ARRAY:
return (MonoObject *)PoolStringArray_to_mono_array(p_var->operator PoolStringArray());
case Variant::POOL_VECTOR2_ARRAY:
return (MonoObject *)PoolVector2Array_to_mono_array(p_var->operator PoolVector2Array());
case Variant::POOL_VECTOR3_ARRAY:
return (MonoObject *)PoolVector3Array_to_mono_array(p_var->operator PoolVector3Array());
case Variant::POOL_COLOR_ARRAY:
return (MonoObject *)PoolColorArray_to_mono_array(p_var->operator PoolColorArray());
default:
return NULL;
}
break;
case MONO_TYPE_GENERICINST: {
if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) {
return Dictionary_to_mono_object(p_var->operator Dictionary());
}
} break;
} break;
}
ERR_EXPLAIN(String() + "Attempted to convert Variant to an unmarshallable managed type. Name: \'" +
p_type.type_class->get_name() + "\' Encoding: " + itos(p_type.type_encoding));
ERR_FAIL_V(NULL);
}
Variant mono_object_to_variant(MonoObject *p_obj) {
if (!p_obj)
return Variant();
GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj));
ERR_FAIL_COND_V(!tclass, Variant());
MonoType *raw_type = tclass->get_raw_type(tclass);
ManagedType type;
type.type_encoding = mono_type_get_type(raw_type);
type.type_class = tclass;
return mono_object_to_variant(p_obj, type);
}
Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type) {
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN:
return (bool)UNBOX_BOOLEAN(p_obj);
case MONO_TYPE_I1:
return UNBOX_INT8(p_obj);
case MONO_TYPE_I2:
return UNBOX_INT16(p_obj);
case MONO_TYPE_I4:
return UNBOX_INT32(p_obj);
case MONO_TYPE_I8:
return UNBOX_INT64(p_obj);
case MONO_TYPE_U1:
return UNBOX_UINT8(p_obj);
case MONO_TYPE_U2:
return UNBOX_UINT16(p_obj);
case MONO_TYPE_U4:
return UNBOX_UINT32(p_obj);
case MONO_TYPE_U8:
return UNBOX_UINT64(p_obj);
case MONO_TYPE_R4:
return UNBOX_FLOAT(p_obj);
case MONO_TYPE_R8:
return UNBOX_DOUBLE(p_obj);
case MONO_TYPE_STRING: {
String str = mono_string_to_godot((MonoString *)p_obj);
return str;
} break;
case MONO_TYPE_VALUETYPE: {
GDMonoClass *tclass = p_type.type_class;
if (tclass == CACHED_CLASS(Vector2))
RETURN_UNBOXED_STRUCT(Vector2, p_obj);
if (tclass == CACHED_CLASS(Rect2))
RETURN_UNBOXED_STRUCT(Rect2, p_obj);
if (tclass == CACHED_CLASS(Transform2D))
RETURN_UNBOXED_STRUCT(Transform2D, p_obj);
if (tclass == CACHED_CLASS(Vector3))
RETURN_UNBOXED_STRUCT(Vector3, p_obj);
if (tclass == CACHED_CLASS(Basis))
RETURN_UNBOXED_STRUCT(Basis, p_obj);
if (tclass == CACHED_CLASS(Quat))
RETURN_UNBOXED_STRUCT(Quat, p_obj);
if (tclass == CACHED_CLASS(Transform))
RETURN_UNBOXED_STRUCT(Transform, p_obj);
if (tclass == CACHED_CLASS(Rect3))
RETURN_UNBOXED_STRUCT(Rect3, p_obj);
if (tclass == CACHED_CLASS(Color))
RETURN_UNBOXED_STRUCT(Color, p_obj);
if (tclass == CACHED_CLASS(Plane))
RETURN_UNBOXED_STRUCT(Plane, p_obj);
} break;
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return mono_array_to_Array((MonoArray *)p_obj);
if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
return mono_array_to_PoolByteArray((MonoArray *)p_obj);
if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
return mono_array_to_PoolIntArray((MonoArray *)p_obj);
if (array_type->eklass == REAL_T_MONOCLASS)
return mono_array_to_PoolRealArray((MonoArray *)p_obj);
if (array_type->eklass == CACHED_CLASS_RAW(String))
return mono_array_to_PoolStringArray((MonoArray *)p_obj);
if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
return mono_array_to_PoolVector2Array((MonoArray *)p_obj);
if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
return mono_array_to_PoolVector3Array((MonoArray *)p_obj);
if (array_type->eklass == CACHED_CLASS_RAW(Color))
return mono_array_to_PoolColorArray((MonoArray *)p_obj);
ERR_EXPLAIN(String() + "Attempted to convert a managed array of unmarshallable element type to Variant.");
ERR_FAIL_V(Variant());
} break;
case MONO_TYPE_CLASS: {
GDMonoClass *type_class = p_type.type_class;
// GodotObject
if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
GDMonoField *ptr_field = CACHED_FIELD(GodotObject, ptr);
ERR_FAIL_NULL_V(ptr_field, Variant());
void *ptr_to_unmanaged = UNBOX_PTR(ptr_field->get_value(p_obj));
if (!ptr_to_unmanaged) // IntPtr.Zero
return Variant();
Object *object_ptr = static_cast<Object *>(ptr_to_unmanaged);
if (!object_ptr)
return Variant();
return object_ptr;
}
if (CACHED_CLASS(NodePath) == type_class) {
return UNBOX_PTR(CACHED_FIELD(NodePath, ptr)->get_value(p_obj));
}
if (CACHED_CLASS(RID) == type_class) {
return UNBOX_PTR(CACHED_FIELD(RID, ptr)->get_value(p_obj));
}
} break;
case MONO_TYPE_GENERICINST: {
if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) {
return mono_object_to_Dictionary(p_obj);
}
} break;
}
ERR_EXPLAIN(String() + "Attempted to convert an unmarshallable managed type to Variant. Name: \'" +
p_type.type_class->get_name() + "\' Encoding: " + itos(p_type.type_encoding));
ERR_FAIL_V(Variant());
}
MonoArray *Array_to_mono_array(const Array &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
MonoObject *boxed = variant_to_mono_object(p_array[i]);
mono_array_set(ret, MonoObject *, i, boxed);
}
return ret;
}
Array mono_array_to_Array(MonoArray *p_array) {
Array ret;
int length = mono_array_length(p_array);
for (int i = 0; i < length; i++) {
MonoObject *elem = mono_array_get(p_array, MonoObject *, i);
ret.push_back(mono_object_to_variant(elem));
}
return ret;
}
// TODO Optimize reading/writing from/to PoolArrays
MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
mono_array_set(ret, int32_t, i, p_array[i]);
}
return ret;
}
PoolIntArray mono_array_to_PoolIntArray(MonoArray *p_array) {
PoolIntArray ret;
int length = mono_array_length(p_array);
for (int i = 0; i < length; i++) {
int32_t elem = mono_array_get(p_array, int32_t, i);
ret.push_back(elem);
}
return ret;
}
MonoArray *PoolByteArray_to_mono_array(const PoolByteArray &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
mono_array_set(ret, uint8_t, i, p_array[i]);
}
return ret;
}
PoolByteArray mono_array_to_PoolByteArray(MonoArray *p_array) {
PoolByteArray ret;
int length = mono_array_length(p_array);
for (int i = 0; i < length; i++) {
uint8_t elem = mono_array_get(p_array, uint8_t, i);
ret.push_back(elem);
}
return ret;
}
MonoArray *PoolRealArray_to_mono_array(const PoolRealArray &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), REAL_T_MONOCLASS, p_array.size());
for (int i = 0; i < p_array.size(); i++) {
mono_array_set(ret, real_t, i, p_array[i]);
}
return ret;
}
PoolRealArray mono_array_to_PoolRealArray(MonoArray *p_array) {
PoolRealArray ret;
int length = mono_array_length(p_array);
for (int i = 0; i < length; i++) {
real_t elem = mono_array_get(p_array, real_t, i);
ret.push_back(elem);
}
return ret;
}
MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
MonoString *boxed = mono_string_from_godot(p_array[i]);
mono_array_set(ret, MonoString *, i, boxed);
}
return ret;
}
PoolStringArray mono_array_to_PoolStringArray(MonoArray *p_array) {
PoolStringArray ret;
int length = mono_array_length(p_array);
for (int i = 0; i < length; i++) {
MonoString *elem = mono_array_get(p_array, MonoString *, i);
ret.push_back(mono_string_to_godot(elem));
}
return ret;
}
MonoArray *PoolColorArray_to_mono_array(const PoolColorArray &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
#ifdef YOLOCOPY
mono_array_set(ret, Color, i, p_array[i]);
#else
real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 4, i);
const Color &elem = p_array[i];
raw[0] = elem.r;
raw[4] = elem.g;
raw[8] = elem.b;
raw[12] = elem.a;
#endif
}
return ret;
}
PoolColorArray mono_array_to_PoolColorArray(MonoArray *p_array) {
PoolColorArray ret;
int length = mono_array_length(p_array);
for (int i = 0; i < length; i++) {
real_t *raw_elem = mono_array_get(p_array, real_t *, i);
MARSHALLED_IN(Color, raw_elem, elem);
ret.push_back(elem);
}
return ret;
}
MonoArray *PoolVector2Array_to_mono_array(const PoolVector2Array &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
#ifdef YOLOCOPY
mono_array_set(ret, Vector2, i, p_array[i]);
#else
real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 2, i);
const Vector2 &elem = p_array[i];
raw[0] = elem.x;
raw[4] = elem.y;
#endif
}
return ret;
}
PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array) {
PoolVector2Array ret;
int length = mono_array_length(p_array);
for (int i = 0; i < length; i++) {
real_t *raw_elem = mono_array_get(p_array, real_t *, i);
MARSHALLED_IN(Vector2, raw_elem, elem);
ret.push_back(elem);
}
return ret;
}
MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), p_array.size());
for (int i = 0; i < p_array.size(); i++) {
#ifdef YOLOCOPY
mono_array_set(ret, Vector3, i, p_array[i]);
#else
real_t *raw = (real_t *)mono_array_addr_with_size(ret, sizeof(real_t) * 3, i);
const Vector3 &elem = p_array[i];
raw[0] = elem.x;
raw[4] = elem.y;
raw[8] = elem.z;
#endif
}
return ret;
}
PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
PoolVector3Array ret;
int length = mono_array_length(p_array);
for (int i = 0; i < length; i++) {
real_t *raw_elem = mono_array_get(p_array, real_t *, i);
MARSHALLED_IN(Vector3, raw_elem, elem);
ret.push_back(elem);
}
return ret;
}
MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) {
MonoArray *keys = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
MonoArray *values = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
int i = 0;
const Variant *dkey = NULL;
while ((dkey = p_dict.next(dkey))) {
mono_array_set(keys, MonoObject *, i, variant_to_mono_object(dkey));
mono_array_set(values, MonoObject *, i, variant_to_mono_object(p_dict[*dkey]));
i++;
}
GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary);
MonoObject *ex = NULL;
MonoObject *ret = arrays_to_dict(keys, values, &ex);
if (ex) {
mono_print_unhandled_exception(ex);
ERR_FAIL_V(NULL);
}
return ret;
}
Dictionary mono_object_to_Dictionary(MonoObject *p_dict) {
Dictionary ret;
GDMonoUtils::MarshalUtils_DictToArrays dict_to_arrays = CACHED_METHOD_THUNK(MarshalUtils, DictionaryToArrays);
MonoArray *keys = NULL;
MonoArray *values = NULL;
MonoObject *ex = NULL;
dict_to_arrays(p_dict, &keys, &values, &ex);
if (ex) {
mono_print_unhandled_exception(ex);
ERR_FAIL_V(Dictionary());
}
int length = mono_array_length(keys);
for (int i = 0; i < length; i++) {
MonoObject *key_obj = mono_array_get(keys, MonoObject *, i);
MonoObject *value_obj = mono_array_get(values, MonoObject *, i);
Variant key = key_obj ? mono_object_to_variant(key_obj) : Variant();
Variant value = value_obj ? mono_object_to_variant(value_obj) : Variant();
ret[key] = value;
}
return ret;
}
}

View file

@ -0,0 +1,229 @@
/*************************************************************************/
/* gd_mono_marshal.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GDMONOMARSHAL_H
#define GDMONOMARSHAL_H
#include "gd_mono.h"
#include "gd_mono_utils.h"
#include "variant.h"
namespace GDMonoMarshal {
#define UNBOX_CHAR_PTR(x) (char *)mono_object_unbox(x)
#define UNBOX_FLOAT_PTR(x) (float *)mono_object_unbox(x)
#define UNBOX_DOUBLE(x) *(double *)mono_object_unbox(x)
#define UNBOX_FLOAT(x) *(float *)mono_object_unbox(x)
#define UNBOX_INT64(x) *(int64_t *)mono_object_unbox(x)
#define UNBOX_INT32(x) *(int32_t *)mono_object_unbox(x)
#define UNBOX_INT16(x) *(int16_t *)mono_object_unbox(x)
#define UNBOX_INT8(x) *(int8_t *)mono_object_unbox(x)
#define UNBOX_UINT64(x) *(uint64_t *)mono_object_unbox(x)
#define UNBOX_UINT32(x) *(uint32_t *)mono_object_unbox(x)
#define UNBOX_UINT16(x) *(uint16_t *)mono_object_unbox(x)
#define UNBOX_UINT8(x) *(uint8_t *)mono_object_unbox(x)
#define UNBOX_BOOLEAN(x) *(MonoBoolean *)mono_object_unbox(x)
#define UNBOX_PTR(x) mono_object_unbox(x)
#define BOX_DOUBLE(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(double), &x)
#define BOX_FLOAT(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(float), &x)
#define BOX_INT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int64_t), &x)
#define BOX_INT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int32_t), &x)
#define BOX_INT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int16_t), &x)
#define BOX_INT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int8_t), &x)
#define BOX_UINT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint64_t), &x)
#define BOX_UINT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint32_t), &x)
#define BOX_UINT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint16_t), &x)
#define BOX_UINT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), &x)
#define BOX_BOOLEAN(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(bool), &x)
#define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x)
Variant::Type managed_to_variant_type(const ManagedType &p_type);
// String
String mono_to_utf8_string(MonoString *p_mono_string);
String mono_to_utf16_string(MonoString *p_mono_string);
_FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) {
if (sizeof(CharType) == 2)
return mono_to_utf16_string(p_mono_string);
return mono_to_utf8_string(p_mono_string);
}
_FORCE_INLINE_ MonoString *mono_from_utf8_string(const String &p_string) {
return mono_string_new(mono_domain_get(), p_string.utf8().get_data());
}
_FORCE_INLINE_ MonoString *mono_from_utf16_string(const String &p_string) {
return mono_string_from_utf16((mono_unichar2 *)p_string.c_str());
}
_FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) {
if (sizeof(CharType) == 2)
return mono_from_utf16_string(p_string);
return mono_from_utf8_string(p_string);
}
// Variant
MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type);
MonoObject *variant_to_mono_object(const Variant *p_var);
_FORCE_INLINE_ MonoObject *variant_to_mono_object(Variant p_var) {
return variant_to_mono_object(&p_var);
}
Variant mono_object_to_variant(MonoObject *p_obj);
Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type);
// Array
MonoArray *Array_to_mono_array(const Array &p_array);
Array mono_array_to_Array(MonoArray *p_array);
// PoolIntArray
MonoArray *PoolIntArray_to_mono_array(const PoolIntArray &p_array);
PoolIntArray mono_array_to_PoolIntArray(MonoArray *p_array);
// PoolByteArray
MonoArray *PoolByteArray_to_mono_array(const PoolByteArray &p_array);
PoolByteArray mono_array_to_PoolByteArray(MonoArray *p_array);
// PoolRealArray
MonoArray *PoolRealArray_to_mono_array(const PoolRealArray &p_array);
PoolRealArray mono_array_to_PoolRealArray(MonoArray *p_array);
// PoolStringArray
MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array);
PoolStringArray mono_array_to_PoolStringArray(MonoArray *p_array);
// PoolColorArray
MonoArray *PoolColorArray_to_mono_array(const PoolColorArray &p_array);
PoolColorArray mono_array_to_PoolColorArray(MonoArray *p_array);
// PoolVector2Array
MonoArray *PoolVector2Array_to_mono_array(const PoolVector2Array &p_array);
PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array);
// PoolVector3Array
MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array);
PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array);
// Dictionary
MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict);
Dictionary mono_object_to_Dictionary(MonoObject *p_dict);
#ifdef YOLO_COPY
#define MARSHALLED_OUT(m_t, m_in, m_out) m_t *m_out = (m_t *)&m_in;
#define MARSHALLED_IN(m_t, m_in, m_out) m_t m_out = *reinterpret_cast<m_t *>(m_in);
#else
// Expects m_in to be of type float*
#define MARSHALLED_OUT(m_t, m_in, m_out) MARSHALLED_OUT_##m_t(m_in, m_out)
#define MARSHALLED_IN(m_t, m_in, m_out) MARSHALLED_IN_##m_t(m_in, m_out)
// Vector2
#define MARSHALLED_OUT_Vector2(m_in, m_out) real_t m_out[2] = { m_in.x, m_in.y };
#define MARSHALLED_IN_Vector2(m_in, m_out) Vector2 m_out(m_in[0], m_in[1]);
// Rect2
#define MARSHALLED_OUT_Rect2(m_in, m_out) real_t m_out[4] = { m_in.position.x, m_in.position.y, m_in.size.width, m_in.size.height };
#define MARSHALLED_IN_Rect2(m_in, m_out) Rect2 m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
// Transform2D
#define MARSHALLED_OUT_Transform2D(m_in, m_out) real_t m_out[6] = { m_in[0].x, m_in[0].y, m_in[1].x, m_in[1].y, m_in[2].x, m_in[2].y };
#define MARSHALLED_IN_Transform2D(m_in, m_out) Transform2D m_out(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5]);
// Vector3
#define MARSHALLED_OUT_Vector3(m_in, m_out) real_t m_out[3] = { m_in.x, m_in.y, m_in.z };
#define MARSHALLED_IN_Vector3(m_in, m_out) Vector3 m_out(m_in[0], m_in[1], m_in[2]);
// Basis
#define MARSHALLED_OUT_Basis(m_in, m_out) real_t m_out[9] = { \
m_in[0].x, m_in[0].y, m_in[0].z, \
m_in[1].x, m_in[1].y, m_in[1].z, \
m_in[2].x, m_in[2].y, m_in[2].z \
};
#define MARSHALLED_IN_Basis(m_in, m_out) Basis m_out(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5], m_in[6], m_in[7], m_in[8]);
// Quat
#define MARSHALLED_OUT_Quat(m_in, m_out) real_t m_out[4] = { m_in.x, m_in.y, m_in.z, m_in.w };
#define MARSHALLED_IN_Quat(m_in, m_out) Quat m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
// Transform
#define MARSHALLED_OUT_Transform(m_in, m_out) real_t m_out[12] = { \
m_in.basis[0].x, m_in.basis[0].y, m_in.basis[0].z, \
m_in.basis[1].x, m_in.basis[1].y, m_in.basis[1].z, \
m_in.basis[2].x, m_in.basis[2].y, m_in.basis[2].z, \
m_in.origin.x, m_in.origin.y, m_in.origin.z \
};
#define MARSHALLED_IN_Transform(m_in, m_out) Transform m_out( \
Basis(m_in[0], m_in[1], m_in[2], m_in[3], m_in[4], m_in[5], m_in[6], m_in[7], m_in[8]), \
Vector3(m_in[9], m_in[10], m_in[11]));
// Rect3
#define MARSHALLED_OUT_Rect3(m_in, m_out) real_t m_out[6] = { m_in.position.x, m_in.position.y, m_in.position.z, m_in.size.x, m_in.size.y, m_in.size.z };
#define MARSHALLED_IN_Rect3(m_in, m_out) Rect3 m_out(Vector3(m_in[0], m_in[1], m_in[2]), Vector3(m_in[3], m_in[4], m_in[5]));
// Color
#define MARSHALLED_OUT_Color(m_in, m_out) real_t m_out[4] = { m_in.r, m_in.g, m_in.b, m_in.a };
#define MARSHALLED_IN_Color(m_in, m_out) Color m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
// Plane
#define MARSHALLED_OUT_Plane(m_in, m_out) real_t m_out[4] = { m_in.normal.x, m_in.normal.y, m_in.normal.z, m_in.d };
#define MARSHALLED_IN_Plane(m_in, m_out) Plane m_out(m_in[0], m_in[1], m_in[2], m_in[3]);
#endif
} // GDMonoMarshal
#endif // GDMONOMARSHAL_H

View file

@ -0,0 +1,192 @@
/*************************************************************************/
/* gd_mono_method.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_mono_method.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
void GDMonoMethod::_update_signature() {
// Apparently MonoMethodSignature needs not to be freed.
// mono_method_signature caches the result, we don't need to cache it ourselves.
MonoMethodSignature *method_sig = mono_method_signature(mono_method);
_update_signature(method_sig);
}
void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) {
is_instance = mono_signature_is_instance(p_method_sig);
params_count = mono_signature_get_param_count(p_method_sig);
MonoType *ret_type = mono_signature_get_return_type(p_method_sig);
if (ret_type) {
return_type.type_encoding = mono_type_get_type(ret_type);
if (return_type.type_encoding != MONO_TYPE_VOID) {
MonoClass *ret_type_class = mono_class_from_mono_type(ret_type);
return_type.type_class = GDMono::get_singleton()->get_class(ret_type_class);
}
}
void *iter = NULL;
MonoType *param_raw_type;
while ((param_raw_type = mono_signature_get_params(p_method_sig, &iter)) != NULL) {
ManagedType param_type;
param_type.type_encoding = mono_type_get_type(param_raw_type);
if (param_type.type_encoding != MONO_TYPE_VOID) {
MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type);
param_type.type_class = GDMono::get_singleton()->get_class(param_type_class);
}
param_types.push_back(param_type);
}
}
void *GDMonoMethod::get_thunk() {
return mono_method_get_unmanaged_thunk(mono_method);
}
MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc) {
if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) {
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count());
for (int i = 0; i < params_count; i++) {
MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]);
mono_array_set(params, MonoObject *, i, boxed_param);
}
return mono_runtime_invoke_array(mono_method, p_object, params, r_exc);
} else {
mono_runtime_invoke(mono_method, p_object, NULL, r_exc);
return NULL;
}
}
MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoObject **r_exc) {
ERR_FAIL_COND_V(get_parameters_count() > 0, NULL);
return invoke_raw(p_object, NULL, r_exc);
}
MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc) {
return mono_runtime_invoke(mono_method, p_object, p_params, r_exc);
}
bool GDMonoMethod::has_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, false);
if (!attrs_fetched)
fetch_attributes();
if (!attributes)
return false;
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_raw());
}
MonoObject *GDMonoMethod::get_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, NULL);
if (!attrs_fetched)
fetch_attributes();
if (!attributes)
return NULL;
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_raw());
}
void GDMonoMethod::fetch_attributes() {
ERR_FAIL_COND(attributes != NULL);
attributes = mono_custom_attrs_from_method(mono_method);
attrs_fetched = true;
}
String GDMonoMethod::get_full_name(bool p_signature) const {
char *res = mono_method_full_name(mono_method, p_signature);
String full_name(res);
mono_free(res);
return full_name;
}
String GDMonoMethod::get_full_name_no_class() const {
String res;
MonoMethodSignature *method_sig = mono_method_signature(mono_method);
char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig));
res += ret_str;
mono_free(ret_str);
res += " ";
res += name;
res += "(";
char *sig_desc = mono_signature_get_desc(method_sig, true);
res += sig_desc;
mono_free(sig_desc);
res += ")";
return res;
}
String GDMonoMethod::get_ret_type_full_name() const {
MonoMethodSignature *method_sig = mono_method_signature(mono_method);
char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig));
String res = ret_str;
mono_free(ret_str);
return res;
}
String GDMonoMethod::get_signature_desc(bool p_namespaces) const {
MonoMethodSignature *method_sig = mono_method_signature(mono_method);
char *sig_desc = mono_signature_get_desc(method_sig, p_namespaces);
String res = sig_desc;
mono_free(sig_desc);
return res;
}
GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) {
name = p_name;
mono_method = p_method;
attrs_fetched = false;
attributes = NULL;
_update_signature();
}
GDMonoMethod::~GDMonoMethod() {
if (attributes) {
mono_custom_attrs_free(attributes);
}
}

View file

@ -0,0 +1,81 @@
/*************************************************************************/
/* gd_mono_method.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_MONO_METHOD_H
#define GD_MONO_METHOD_H
#include "gd_mono.h"
#include "gd_mono_header.h"
class GDMonoMethod {
StringName name;
bool is_instance;
int params_count;
ManagedType return_type;
Vector<ManagedType> param_types;
bool attrs_fetched;
MonoCustomAttrInfo *attributes;
void _update_signature();
void _update_signature(MonoMethodSignature *p_method_sig);
friend class GDMonoClass;
MonoMethod *mono_method;
public:
_FORCE_INLINE_ StringName get_name() { return name; }
_FORCE_INLINE_ bool is_static() { return !is_instance; }
_FORCE_INLINE_ int get_parameters_count() { return params_count; }
_FORCE_INLINE_ ManagedType get_return_type() { return return_type; }
void *get_thunk();
MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc = NULL);
MonoObject *invoke(MonoObject *p_object, MonoObject **r_exc = NULL);
MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL);
bool has_attribute(GDMonoClass *p_attr_class);
MonoObject *get_attribute(GDMonoClass *p_attr_class);
void fetch_attributes();
String get_full_name(bool p_signature = false) const;
String get_full_name_no_class() const;
String get_ret_type_full_name() const;
String get_signature_desc(bool p_namespaces = false) const;
GDMonoMethod(StringName p_name, MonoMethod *p_method);
~GDMonoMethod();
};
#endif // GD_MONO_METHOD_H

View file

@ -0,0 +1,367 @@
/*************************************************************************/
/* gd_mono_utils.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gd_mono_utils.h"
#include "os/dir_access.h"
#include "project_settings.h"
#include "reference.h"
#include "../csharp_script.h"
#include "gd_mono.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
namespace GDMonoUtils {
MonoCache mono_cache;
#define CACHE_AND_CHECK(m_var, m_val) \
{ \
m_var = m_val; \
if (!m_var) ERR_PRINT("Mono Cache: Member " #m_var " is null. This is really bad!"); \
}
#define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_class, m_val)
#define CACHE_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_ns##_##m_class, m_val)
#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.rawclass_##m_class, m_val)
#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.field_##m_class##_##m_field, m_val)
#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val)
void MonoCache::clear_members() {
class_MonoObject = NULL;
class_bool = NULL;
class_int8_t = NULL;
class_int16_t = NULL;
class_int32_t = NULL;
class_int64_t = NULL;
class_uint8_t = NULL;
class_uint16_t = NULL;
class_uint32_t = NULL;
class_uint64_t = NULL;
class_float = NULL;
class_double = NULL;
class_String = NULL;
class_IntPtr = NULL;
rawclass_Dictionary = NULL;
class_Vector2 = NULL;
class_Rect2 = NULL;
class_Transform2D = NULL;
class_Vector3 = NULL;
class_Basis = NULL;
class_Quat = NULL;
class_Transform = NULL;
class_Rect3 = NULL;
class_Color = NULL;
class_Plane = NULL;
class_NodePath = NULL;
class_RID = NULL;
class_GodotObject = NULL;
class_Node = NULL;
class_Control = NULL;
class_Spatial = NULL;
class_WeakRef = NULL;
class_MarshalUtils = NULL;
class_ExportAttribute = NULL;
field_ExportAttribute_hint = NULL;
field_ExportAttribute_hint_string = NULL;
field_ExportAttribute_usage = NULL;
class_ToolAttribute = NULL;
class_RemoteAttribute = NULL;
class_SyncAttribute = NULL;
class_MasterAttribute = NULL;
class_SlaveAttribute = NULL;
class_GodotMethodAttribute = NULL;
field_GodotMethodAttribute_methodName = NULL;
field_GodotObject_ptr = NULL;
field_NodePath_ptr = NULL;
field_Image_ptr = NULL;
field_RID_ptr = NULL;
methodthunk_MarshalUtils_DictionaryToArrays = NULL;
methodthunk_MarshalUtils_ArraysToDictionary = NULL;
methodthunk_GodotObject__AwaitedSignalCallback = NULL;
methodthunk_SignalAwaiter_FailureCallback = NULL;
methodthunk_GodotTaskScheduler_Activate = NULL;
task_scheduler_handle = Ref<MonoGCHandle>();
}
#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
void update_corlib_cache() {
CACHE_CLASS_AND_CHECK(MonoObject, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_object_class()));
CACHE_CLASS_AND_CHECK(bool, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_boolean_class()));
CACHE_CLASS_AND_CHECK(int8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_sbyte_class()));
CACHE_CLASS_AND_CHECK(int16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int16_class()));
CACHE_CLASS_AND_CHECK(int32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int32_class()));
CACHE_CLASS_AND_CHECK(int64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int64_class()));
CACHE_CLASS_AND_CHECK(uint8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_byte_class()));
CACHE_CLASS_AND_CHECK(uint16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint16_class()));
CACHE_CLASS_AND_CHECK(uint32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint32_class()));
CACHE_CLASS_AND_CHECK(uint64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint64_class()));
CACHE_CLASS_AND_CHECK(float, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_single_class()));
CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class()));
CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class()));
CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class()));
}
void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2));
CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2));
CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D));
CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3));
CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis));
CACHE_CLASS_AND_CHECK(Quat, GODOT_API_CLASS(Quat));
CACHE_CLASS_AND_CHECK(Transform, GODOT_API_CLASS(Transform));
CACHE_CLASS_AND_CHECK(Rect3, GODOT_API_CLASS(Rect3));
CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color));
CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane));
CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath));
CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(NodePath));
CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node));
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
// Attributes
CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint_string, CACHED_CLASS(ExportAttribute)->get_field("hint_string"));
CACHE_FIELD_AND_CHECK(ExportAttribute, usage, CACHED_CLASS(ExportAttribute)->get_field("usage"));
CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute));
CACHE_CLASS_AND_CHECK(SyncAttribute, GODOT_API_CLASS(SyncAttribute));
CACHE_CLASS_AND_CHECK(MasterAttribute, GODOT_API_CLASS(MasterAttribute));
CACHE_CLASS_AND_CHECK(SlaveAttribute, GODOT_API_CLASS(SlaveAttribute));
CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute));
CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD));
CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD));
CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryToArrays, (MarshalUtils_DictToArrays)CACHED_CLASS(MarshalUtils)->get_method("DictionaryToArrays", 3)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArraysToDictionary, (MarshalUtils_ArraysToDict)CACHED_CLASS(MarshalUtils)->get_method("ArraysToDictionary", 2)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(GodotObject, _AwaitedSignalCallback, (GodotObject__AwaitedSignalCallback)CACHED_CLASS(GodotObject)->get_method("_AwaitedSignalCallback", 2)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk());
{
/*
* TODO Right now we only support Dictionary<object, object>.
* It would be great if we could support other key/value types
* without forcing the user to copy the entries.
*/
GDMonoMethod *method_get_dict_type = CACHED_CLASS(MarshalUtils)->get_method("GetDictionaryType", 0);
ERR_FAIL_NULL(method_get_dict_type);
MonoReflectionType *dict_refl_type = (MonoReflectionType *)method_get_dict_type->invoke(NULL);
ERR_FAIL_NULL(dict_refl_type);
MonoType *dict_type = mono_reflection_type_get_type(dict_refl_type);
ERR_FAIL_NULL(dict_type);
CACHE_RAW_MONO_CLASS_AND_CHECK(Dictionary, mono_class_from_mono_type(dict_type));
}
MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_raw());
mono_runtime_object_init(task_scheduler);
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
}
void clear_cache() {
mono_cache.cleanup();
mono_cache.clear_members();
}
MonoObject *unmanaged_get_managed(Object *unmanaged) {
if (unmanaged) {
if (unmanaged->get_script_instance()) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance());
if (cs_instance) {
return cs_instance->get_mono_object();
}
}
// Only called if the owner does not have a CSharpInstance
void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
if (data) {
return ((Map<Object *, Ref<MonoGCHandle> >::Element *)data)->value()->get_target();
}
}
return NULL;
}
void set_main_thread(MonoThread *p_thread) {
mono_thread_set_main(p_thread);
}
void attach_current_thread() {
ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN);
ERR_FAIL_NULL(mono_thread);
}
void detach_current_thread() {
ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
MonoThread *mono_thread = mono_thread_current();
ERR_FAIL_NULL(mono_thread);
mono_thread_detach(mono_thread);
}
MonoThread *get_current_thread() {
return mono_thread_current();
}
GDMonoClass *get_object_class(MonoObject *p_object) {
return GDMono::get_singleton()->get_class(mono_object_get_class(p_object));
}
GDMonoClass *type_get_proxy_class(const StringName &p_type) {
String class_name = p_type;
if (class_name[0] == '_')
class_name = class_name.substr(1, class_name.length());
GDMonoClass *klass = GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
#ifdef TOOLS_ENABLED
if (!klass) {
return GDMono::get_singleton()->get_editor_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
}
#endif
return klass;
}
GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
GDMonoClass *klass = p_class;
do {
const GDMonoAssembly *assembly = klass->get_assembly();
if (assembly == GDMono::get_singleton()->get_api_assembly())
return klass;
#ifdef TOOLS_ENABLED
if (assembly == GDMono::get_singleton()->get_editor_api_assembly())
return klass;
#endif
} while ((klass = klass->get_parent_class()) != NULL);
return NULL;
}
MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) {
String object_type = p_object->get_class_name();
if (object_type[0] == '_')
object_type = object_type.substr(1, object_type.length());
if (!ClassDB::is_parent_class(object_type, p_native)) {
ERR_EXPLAIN("Type inherits from native type '" + p_native + "', so it can't be instanced in object of type: '" + p_object->get_class() + "'");
ERR_FAIL_V(NULL);
}
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_raw());
ERR_FAIL_NULL_V(mono_object, NULL);
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
// Construct
mono_runtime_object_init(mono_object);
return mono_object;
}
MonoObject *create_managed_from(const NodePath &p_from) {
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(NodePath));
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
mono_runtime_object_init(mono_object);
CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from)));
return mono_object;
}
MonoObject *create_managed_from(const RID &p_from) {
MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(RID));
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
mono_runtime_object_init(mono_object);
CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from)));
return mono_object;
}
MonoDomain *create_domain(const String &p_friendly_name) {
MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL);
if (domain) {
// Workaround to avoid this exception:
// System.Configuration.ConfigurationErrorsException: Error Initializing the configuration system.
// ---> System.ArgumentException: The 'ExeConfigFilename' argument cannot be null.
mono_domain_set_config(domain, ".", "");
}
return domain;
}
String get_exception_name_and_message(MonoObject *p_ex) {
String res;
MonoClass *klass = mono_object_get_class(p_ex);
MonoType *type = mono_class_get_type(klass);
char *full_name = mono_type_full_name(type);
res += full_name;
mono_free(full_name);
res += ": ";
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
MonoString *msg = (MonoString *)mono_property_get_value(prop, p_ex, NULL, NULL);
res += GDMonoMarshal::mono_string_to_godot(msg);
return res;
}
}

View file

@ -0,0 +1,182 @@
/*************************************************************************/
/* gd_mono_utils.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GD_MONOUTILS_H
#define GD_MONOUTILS_H
#include <mono/metadata/threads.h>
#include "../mono_gc_handle.h"
#include "gd_mono_header.h"
#include "object.h"
#include "reference.h"
namespace GDMonoUtils {
typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **);
typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **);
typedef MonoObject *(*GodotObject__AwaitedSignalCallback)(MonoObject *, MonoArray **, MonoObject *, MonoObject **);
typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **);
typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **);
struct MonoCache {
// Format for cached classes in the Godot namespace: class_<Class>
// Macro: CACHED_CLASS(<Class>)
// Format for cached classes in a different namespace: class_<Namespace>_<Class>
// Macro: CACHED_NS_CLASS(<Namespace>, <Class>)
// -----------------------------------------------
// corlib classes
// Let's use the no-namespace format for these too
GDMonoClass *class_MonoObject;
GDMonoClass *class_bool;
GDMonoClass *class_int8_t;
GDMonoClass *class_int16_t;
GDMonoClass *class_int32_t;
GDMonoClass *class_int64_t;
GDMonoClass *class_uint8_t;
GDMonoClass *class_uint16_t;
GDMonoClass *class_uint32_t;
GDMonoClass *class_uint64_t;
GDMonoClass *class_float;
GDMonoClass *class_double;
GDMonoClass *class_String;
GDMonoClass *class_IntPtr;
MonoClass *rawclass_Dictionary;
// -----------------------------------------------
GDMonoClass *class_Vector2;
GDMonoClass *class_Rect2;
GDMonoClass *class_Transform2D;
GDMonoClass *class_Vector3;
GDMonoClass *class_Basis;
GDMonoClass *class_Quat;
GDMonoClass *class_Transform;
GDMonoClass *class_Rect3;
GDMonoClass *class_Color;
GDMonoClass *class_Plane;
GDMonoClass *class_NodePath;
GDMonoClass *class_RID;
GDMonoClass *class_GodotObject;
GDMonoClass *class_Node;
GDMonoClass *class_Control;
GDMonoClass *class_Spatial;
GDMonoClass *class_WeakRef;
GDMonoClass *class_MarshalUtils;
GDMonoClass *class_ExportAttribute;
GDMonoField *field_ExportAttribute_hint;
GDMonoField *field_ExportAttribute_hint_string;
GDMonoField *field_ExportAttribute_usage;
GDMonoClass *class_ToolAttribute;
GDMonoClass *class_RemoteAttribute;
GDMonoClass *class_SyncAttribute;
GDMonoClass *class_MasterAttribute;
GDMonoClass *class_SlaveAttribute;
GDMonoClass *class_GodotMethodAttribute;
GDMonoField *field_GodotMethodAttribute_methodName;
GDMonoField *field_GodotObject_ptr;
GDMonoField *field_NodePath_ptr;
GDMonoField *field_Image_ptr;
GDMonoField *field_RID_ptr;
MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays;
MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary;
GodotObject__AwaitedSignalCallback methodthunk_GodotObject__AwaitedSignalCallback;
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
Ref<MonoGCHandle> task_scheduler_handle;
void clear_members();
void cleanup() {}
MonoCache() {
clear_members();
}
};
extern MonoCache mono_cache;
void update_corlib_cache();
void update_godot_api_cache();
void clear_cache();
_FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) {
p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2);
}
/**
* If the object has a csharp script, returns the target of the gchandle stored in the script instance
* Otherwise returns a newly constructed MonoObject* which is attached to the object
* Returns NULL on error
*/
MonoObject *unmanaged_get_managed(Object *unmanaged);
void set_main_thread(MonoThread *p_thread);
void attach_current_thread();
void detach_current_thread();
MonoThread *get_current_thread();
GDMonoClass *get_object_class(MonoObject *p_object);
GDMonoClass *type_get_proxy_class(const StringName &p_type);
GDMonoClass *get_class_native_base(GDMonoClass *p_class);
MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object);
MonoObject *create_managed_from(const NodePath &p_from);
MonoObject *create_managed_from(const RID &p_from);
MonoDomain *create_domain(const String &p_friendly_name);
String get_exception_name_and_message(MonoObject *p_ex);
} // GDMonoUtils
#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field("nativeName")->get_value(NULL)))
#define CACHED_CLASS(m_class) (GDMonoUtils::mono_cache.class_##m_class)
#define CACHED_CLASS_RAW(m_class) (GDMonoUtils::mono_cache.class_##m_class->get_raw())
#define CACHED_NS_CLASS(m_ns, m_class) (GDMonoUtils::mono_cache.class_##m_ns##_##m_class)
#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoUtils::mono_cache.rawclass_##m_class)
#define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field)
#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method)
#ifdef REAL_T_IS_DOUBLE
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(double)
#else
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float)
#endif
#endif // GD_MONOUTILS_H

View file

@ -0,0 +1,54 @@
import os
if os.name == 'nt':
import _winreg as winreg
def _reg_open_key(key, subkey):
try:
return winreg.OpenKey(key, subkey)
except (WindowsError, EnvironmentError) as e:
import platform
if platform.architecture()[0] == '32bit':
bitness_sam = winreg.KEY_WOW64_64KEY
else:
bitness_sam = winreg.KEY_WOW64_32KEY
return winreg.OpenKey(key, subkey, 0, winreg.KEY_READ | bitness_sam)
def _find_mono_in_reg(subkey):
try:
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
value, regtype = winreg.QueryValueEx(hKey, 'SdkInstallRoot')
return value
except (WindowsError, EnvironmentError) as e:
return None
def _find_mono_in_reg_old(subkey):
try:
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
default_clr, regtype = winreg.QueryValueEx(hKey, 'DefaultCLR')
if default_clr:
return _find_mono_in_reg(subkey + '\\' + default_clr)
return None
except (WindowsError, EnvironmentError):
return None
def find_mono_root_dir():
dir = _find_mono_in_reg(r'SOFTWARE\Mono')
if dir:
return dir
dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono')
if dir:
return dir
return None
def find_msbuild_tools_path_reg():
try:
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0') as hKey:
value, regtype = winreg.QueryValueEx(hKey, 'MSBuildToolsPath')
return value
except (WindowsError, EnvironmentError) as e:
return None

View file

@ -0,0 +1,71 @@
/*************************************************************************/
/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "register_types.h"
#include "project_settings.h"
#include "csharp_script.h"
CSharpLanguage *script_language_cs = NULL;
ResourceFormatLoaderCSharpScript *resource_loader_cs = NULL;
ResourceFormatSaverCSharpScript *resource_saver_cs = NULL;
_GodotSharp *_godotsharp = NULL;
void register_mono_types() {
ClassDB::register_class<CSharpScript>();
_godotsharp = memnew(_GodotSharp);
ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("GodotSharp", _GodotSharp::get_singleton()));
script_language_cs = memnew(CSharpLanguage);
script_language_cs->set_language_index(ScriptServer::get_language_count());
ScriptServer::register_language(script_language_cs);
resource_loader_cs = memnew(ResourceFormatLoaderCSharpScript);
ResourceLoader::add_resource_format_loader(resource_loader_cs);
resource_saver_cs = memnew(ResourceFormatSaverCSharpScript);
ResourceSaver::add_resource_format_saver(resource_saver_cs);
}
void unregister_mono_types() {
ScriptServer::unregister_language(script_language_cs);
if (script_language_cs)
memdelete(script_language_cs);
if (resource_loader_cs)
memdelete(resource_loader_cs);
if (resource_saver_cs)
memdelete(resource_saver_cs);
if (_godotsharp)
memdelete(_godotsharp);
}

View file

@ -0,0 +1,31 @@
/*************************************************************************/
/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
void register_mono_types();
void unregister_mono_types();

View file

@ -0,0 +1,77 @@
/*************************************************************************/
/* signal_awaiter_utils.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "signal_awaiter_utils.h"
#include "mono_gd/gd_mono_utils.h"
namespace SignalAwaiterUtils {
Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter) {
ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA);
ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA);
uint32_t awaiter_handle = MonoGCHandle::make_strong_handle(p_awaiter);
Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(awaiter_handle));
Vector<Variant> binds;
binds.push_back(sa_con);
Error err = p_source->connect(p_signal, p_target, "_AwaitedSignalCallback", binds, Object::CONNECT_ONESHOT);
if (err != OK) {
// set it as completed to prevent it from calling the failure callback when deleted
// the awaiter will be aware of the failure by checking the returned error
sa_con->set_completed(true);
}
return err;
}
}
SignalAwaiterHandle::SignalAwaiterHandle(uint32_t p_handle)
: MonoGCHandle(p_handle) {
}
SignalAwaiterHandle::~SignalAwaiterHandle() {
if (!completed) {
GDMonoUtils::SignalAwaiter_FailureCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback);
MonoObject *awaiter = get_target();
if (awaiter) {
MonoObject *ex = NULL;
thunk(awaiter, &ex);
if (ex) {
mono_print_unhandled_exception(ex);
ERR_FAIL_V();
}
}
}
}

View file

@ -0,0 +1,53 @@
/*************************************************************************/
/* signal_awaiter_utils.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef SIGNAL_AWAITER_UTILS_H
#define SIGNAL_AWAITER_UTILS_H
#include "mono_gc_handle.h"
#include "reference.h"
namespace SignalAwaiterUtils {
Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p_target, MonoObject *p_awaiter);
}
class SignalAwaiterHandle : public MonoGCHandle {
bool completed;
public:
_FORCE_INLINE_ bool is_completed() { return completed; }
_FORCE_INLINE_ void set_completed(bool p_completed) { completed = p_completed; }
SignalAwaiterHandle(uint32_t p_handle);
~SignalAwaiterHandle();
};
#endif // SIGNAL_AWAITER_UTILS_H

View file

@ -0,0 +1,228 @@
/*************************************************************************/
/* mono_reg_utils.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "mono_reg_utils.h"
#ifdef WINDOWS_ENABLED
#include "os/os.h"
// Here, after os/os.h
#include <windows.h>
namespace MonoRegUtils {
template <int>
REGSAM bitness_sam_impl();
template <>
REGSAM bitness_sam_impl<4>() {
return KEY_WOW64_64KEY;
}
template <>
REGSAM bitness_sam_impl<8>() {
return KEY_WOW64_32KEY;
}
REGSAM _get_bitness_sam() {
return bitness_sam_impl<sizeof(size_t)>();
}
LONG _RegOpenKey(HKEY hKey, LPCWSTR lpSubKey, PHKEY phkResult) {
LONG res = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ, phkResult);
if (res != ERROR_SUCCESS)
res = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ | _get_bitness_sam(), phkResult);
return res;
}
LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value) {
Vector<WCHAR> buffer;
buffer.resize(512);
DWORD dwBufferSize = buffer.size();
LONG res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, NULL, (LPBYTE)buffer.ptr(), &dwBufferSize);
if (res == ERROR_MORE_DATA) {
// dwBufferSize now contains the actual size
Vector<WCHAR> buffer;
buffer.resize(dwBufferSize);
res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, NULL, (LPBYTE)buffer.ptr(), &dwBufferSize);
}
if (res == ERROR_SUCCESS) {
r_value = String(buffer.ptr(), buffer.size());
} else {
r_value = String();
}
return res;
}
LONG _find_mono_in_reg(const String &p_subkey, MonoRegInfo &r_info, bool p_old_reg = false) {
HKEY hKey;
LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey);
if (res != ERROR_SUCCESS)
goto cleanup;
if (!p_old_reg) {
res = _RegKeyQueryString(hKey, "Version", r_info.version);
if (res != ERROR_SUCCESS)
goto cleanup;
}
res = _RegKeyQueryString(hKey, "SdkInstallRoot", r_info.install_root_dir);
if (res != ERROR_SUCCESS)
goto cleanup;
res = _RegKeyQueryString(hKey, "FrameworkAssemblyDirectory", r_info.assembly_dir);
if (res != ERROR_SUCCESS)
goto cleanup;
res = _RegKeyQueryString(hKey, "MonoConfigDir", r_info.config_dir);
if (res != ERROR_SUCCESS)
goto cleanup;
if (r_info.install_root_dir.ends_with("\\"))
r_info.bin_dir = r_info.install_root_dir + "bin";
else
r_info.bin_dir = r_info.install_root_dir + "\\bin";
cleanup:
RegCloseKey(hKey);
return res;
}
LONG _find_mono_in_reg_old(const String &p_subkey, MonoRegInfo &r_info) {
String default_clr;
HKEY hKey;
LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey);
if (res != ERROR_SUCCESS)
goto cleanup;
res = _RegKeyQueryString(hKey, "DefaultCLR", default_clr);
if (res == ERROR_SUCCESS && default_clr.length()) {
r_info.version = default_clr;
res = _find_mono_in_reg(p_subkey + "\\" + default_clr, r_info, true);
}
cleanup:
RegCloseKey(hKey);
return res;
}
MonoRegInfo find_mono() {
MonoRegInfo info;
if (_find_mono_in_reg("Software\\Mono", info) == ERROR_SUCCESS)
return info;
if (_find_mono_in_reg_old("Software\\Novell\\Mono", info) == ERROR_SUCCESS)
return info;
ERR_PRINT("Cannot find mono in the registry");
return MonoRegInfo();
}
String find_msbuild_tools_path() {
String msbuild_tools_path;
// Try to find 15.0 with vswhere
String vswhere_path = OS::get_singleton()->get_environment(sizeof(size_t) == 8 ? "ProgramFiles(x86)" : "ProgramFiles");
vswhere_path += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
List<String> vswhere_args;
vswhere_args.push_back("-latest");
vswhere_args.push_back("-requires");
vswhere_args.push_back("Microsoft.Component.MSBuild");
String output;
int exit_code;
OS::get_singleton()->execute(vswhere_path, vswhere_args, true, NULL, &output, &exit_code);
if (exit_code == 0) {
Vector<String> lines = output.split("\n");
for (int i = 0; i < lines.size(); i++) {
const String &line = lines[i];
int sep_idx = line.find(":");
if (sep_idx > 0) {
String key = line.substr(0, sep_idx); // No need to trim
if (key == "installationPath") {
String val = line.substr(sep_idx + 1, line.length()).strip_edges();
ERR_BREAK(val.empty());
if (!val.ends_with("\\")) {
val += "\\";
}
return val + "MSBuild\\15.0\\Bin";
}
}
}
}
// Try to find 14.0 in the Registry
HKEY hKey;
LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\14.0", &hKey);
if (res != ERROR_SUCCESS)
goto cleanup;
res = _RegKeyQueryString(hKey, "MSBuildToolsPath", msbuild_tools_path);
if (res != ERROR_SUCCESS)
goto cleanup;
cleanup:
RegCloseKey(hKey);
return msbuild_tools_path;
}
} // namespace MonoRegUtils
#endif WINDOWS_ENABLED

View file

@ -0,0 +1,54 @@
/*************************************************************************/
/* mono_reg_utils.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef MONO_REG_UTILS_H
#define MONO_REG_UTILS_H
#ifdef WINDOWS_ENABLED
#include "ustring.h"
struct MonoRegInfo {
String version;
String install_root_dir;
String assembly_dir;
String config_dir;
String bin_dir;
};
namespace MonoRegUtils {
MonoRegInfo find_mono();
String find_msbuild_tools_path();
} // MonoRegUtils
#endif // WINDOWS_ENABLED
#endif // MONO_REG_UTILS_H

View file

@ -0,0 +1,111 @@
/*************************************************************************/
/* path_utils.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "path_utils.h"
#include "os/dir_access.h"
#include "os/file_access.h"
#include "os/os.h"
#include "project_settings.h"
#ifdef WINDOWS_ENABLED
#define ENV_PATH_SEP ";"
#else
#define ENV_PATH_SEP ":"
#include <limits.h>
#endif
#include <stdlib.h>
String path_which(const String &p_name) {
#ifdef WINDOWS_ENABLED
Vector<String> exts = OS::get_singleton()->get_environment("PATHEXT").split(ENV_PATH_SEP, false);
#endif
Vector<String> env_path = OS::get_singleton()->get_environment("PATH").split(ENV_PATH_SEP, false);
if (env_path.empty())
return String();
for (int i = 0; i < env_path.size(); i++) {
String p = path_join(env_path[i], p_name);
if (FileAccess::exists(p))
return p;
#ifdef WINDOWS_ENABLED
for (int j = 0; j < exts.size(); j++) {
String p2 = p + exts[j];
if (FileAccess::exists(p2))
return p2;
}
#endif
}
return String();
}
void fix_path(const String &p_path, String &r_out) {
r_out = p_path.replace("\\", "/");
while (true) { // in case of using 2 or more slash
String compare = r_out.replace("//", "/");
if (r_out == compare)
break;
else
r_out = compare;
}
}
bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path) {
#ifdef WINDOWS_ENABLED
CharType ret[_MAX_PATH];
if (_wfullpath(ret, p_existing_path.c_str(), _MAX_PATH)) {
String abspath = String(ret).replace("\\", "/");
int pos = abspath.find(":/");
if (pos != -1) {
r_abs_path = abspath.substr(pos - 1, abspath.length());
} else {
r_abs_path = abspath;
}
return true;
}
#else
char ret[PATH_MAX];
if (realpath(p_existing_path.utf8().get_data(), ret)) {
String retstr;
if (!retstr.parse_utf8(ret)) {
r_abs_path = retstr;
return true;
}
}
#endif
return false;
}

View file

@ -0,0 +1,53 @@
/*************************************************************************/
/* path_utils.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef PATH_UTILS_H
#define PATH_UTILS_H
#include "ustring.h"
_FORCE_INLINE_ String path_join(const String &e1, const String &e2) {
return e1.plus_file(e2);
}
_FORCE_INLINE_ String path_join(const String &e1, const String &e2, const String &e3) {
return e1.plus_file(e2).plus_file(e3);
}
_FORCE_INLINE_ String path_join(const String &e1, const String &e2, const String &e3, const String &e4) {
return e1.plus_file(e2).plus_file(e3).plus_file(e4);
}
String path_which(const String &p_name);
void fix_path(const String &p_path, String &r_out);
bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path);
#endif // PATH_UTILS_H

View file

@ -0,0 +1,128 @@
/*************************************************************************/
/* string_utils.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "string_utils.h"
namespace {
int sfind(const String &p_text, int p_from) {
if (p_from < 0)
return -1;
int src_len = 2;
int len = p_text.length();
if (src_len == 0 || len == 0)
return -1;
const CharType *src = p_text.c_str();
for (int i = p_from; i <= (len - src_len); i++) {
bool found = true;
for (int j = 0; j < src_len; j++) {
int read_pos = i + j;
if (read_pos >= len) {
ERR_PRINT("read_pos >= len");
return -1;
};
switch (j) {
case 0:
found = src[read_pos] == '%';
break;
case 1: {
CharType c = src[read_pos];
found = src[read_pos] == 's' || (c >= '0' || c <= '4');
break;
}
default:
found = false;
}
if (!found) {
break;
}
}
if (found)
return i;
}
return -1;
}
}
String sformat(const String &p_text, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4, const Variant &p5) {
if (p_text.length() < 2)
return p_text;
Array args;
if (p1.get_type() != Variant::NIL) {
args.push_back(p1);
if (p2.get_type() != Variant::NIL) {
args.push_back(p2);
if (p3.get_type() != Variant::NIL) {
args.push_back(p3);
if (p4.get_type() != Variant::NIL) {
args.push_back(p4);
if (p5.get_type() != Variant::NIL) {
args.push_back(p5);
}
}
}
}
}
String new_string;
int findex = 0;
int search_from = 0;
int result = 0;
while ((result = sfind(p_text, search_from)) >= 0) {
CharType c = p_text[result + 1];
int req_index = (c == 's' ? findex++ : c - '0');
new_string += p_text.substr(search_from, result - search_from);
new_string += args[req_index].operator String();
search_from = result + 2;
}
new_string += p_text.substr(search_from, p_text.length() - search_from);
return new_string;
}

View file

@ -0,0 +1,38 @@
/*************************************************************************/
/* string_utils.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef STRING_FORMAT_H
#define STRING_FORMAT_H
#include "ustring.h"
#include "variant.h"
String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant());
#endif // STRING_FORMAT_H