diff options
Diffstat (limited to 'modules')
38 files changed, 738 insertions, 612 deletions
diff --git a/modules/arkit/arkit_interface.mm b/modules/arkit/arkit_interface.mm index 79f09e2a7e..7de824815a 100644 --- a/modules/arkit/arkit_interface.mm +++ b/modules/arkit/arkit_interface.mm @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/input/input_filter.h" +#include "core/input/input.h" #include "core/os/os.h" #include "scene/resources/surface_tool.h" #include "servers/rendering/rendering_server_globals.h" diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub index 692c749886..6f64edce3d 100644 --- a/modules/bullet/SCsub +++ b/modules/bullet/SCsub @@ -175,6 +175,7 @@ if env["builtin_bullet"]: "BulletSoftBody/btDeformableContactProjection.cpp", "BulletSoftBody/btDeformableMultiBodyDynamicsWorld.cpp", "BulletSoftBody/btDeformableContactConstraint.cpp", + "BulletSoftBody/poly34.cpp", # clew "clew/clew.c", # LinearMath diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 5557da3014..d38ddf3f90 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -296,20 +296,18 @@ void CSGShape3D::_update_shape() { int mat = n->faces[i].material; ERR_CONTINUE(mat < -1 || mat >= face_count.size()); int idx = mat == -1 ? face_count.size() - 1 : mat; - if (n->faces[i].smooth) { - Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); + Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); - for (int j = 0; j < 3; j++) { - Vector3 v = n->faces[i].vertices[j]; - Vector3 add; - if (vec_map.lookup(v, add)) { - add += p.normal; - } else { - add = p.normal; - } - vec_map.set(v, add); + for (int j = 0; j < 3; j++) { + Vector3 v = n->faces[i].vertices[j]; + Vector3 add; + if (vec_map.lookup(v, add)) { + add += p.normal; + } else { + add = p.normal; } + vec_map.set(v, add); } face_count.write[idx]++; diff --git a/modules/gdnative/xr/xr_interface_gdnative.cpp b/modules/gdnative/xr/xr_interface_gdnative.cpp index 0451945139..d65089a123 100644 --- a/modules/gdnative/xr/xr_interface_gdnative.cpp +++ b/modules/gdnative/xr/xr_interface_gdnative.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "xr_interface_gdnative.h" -#include "core/input/input_filter.h" +#include "core/input/input.h" #include "servers/rendering/rendering_server_globals.h" #include "servers/xr/xr_positional_tracker.h" @@ -306,7 +306,7 @@ godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, g XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, 0); - InputFilter *input = InputFilter::get_singleton(); + Input *input = Input::get_singleton(); ERR_FAIL_NULL_V(input, 0); XRPositionalTracker *new_tracker = memnew(XRPositionalTracker); @@ -345,7 +345,7 @@ void GDAPI godot_xr_remove_controller(godot_int p_controller_id) { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - InputFilter *input = InputFilter::get_singleton(); + Input *input = Input::get_singleton(); ERR_FAIL_NULL(input); XRPositionalTracker *remove_tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); @@ -383,7 +383,7 @@ void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - InputFilter *input = InputFilter::get_singleton(); + Input *input = Input::get_singleton(); ERR_FAIL_NULL(input); XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); @@ -399,14 +399,14 @@ void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_a XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - InputFilter *input = InputFilter::get_singleton(); + Input *input = Input::get_singleton(); ERR_FAIL_NULL(input); XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); if (tracker != nullptr) { int joyid = tracker->get_joy_id(); if (joyid != -1) { - InputFilter::JoyAxis jx; + Input::JoyAxis jx; jx.min = p_can_be_negative ? -1 : 0; jx.value = p_value; input->joy_axis(joyid, p_axis, jx); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 221cce205a..7ad0682637 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2150,7 +2150,7 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p } const GDScriptParser::ClassNode *clss = p_context._class; - bool _static = !p_context.function || p_context.function->_static; + bool _static = p_context.function && p_context.function->_static; while (clss) { GDScriptCompletionContext c = p_context; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index ca4d6f6de9..4e0891921e 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -337,15 +337,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } if (!argument_types[i].is_type(*p_args[i], true)) { - if (argument_types[i].is_type(Variant(), true)) { - memnew_placement(&stack[i], Variant); - continue; - } else { - r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_err.argument = i; - r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; - return Variant(); - } + r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_err.argument = i; + r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; + return Variant(); } if (argument_types[i].kind == GDScriptDataType::BUILTIN) { Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 7e8d2b0436..411512d631 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -746,6 +746,13 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) { _make_completable_call(0); completion_node = op; + + if (op->arguments[0]->type == GDScriptParser::Node::Type::TYPE_BUILT_IN_FUNCTION) { + BuiltInFunctionNode *bn = static_cast<BuiltInFunctionNode *>(op->arguments[0]); + if (bn->function == GDScriptFunctions::Function::RESOURCE_LOAD) { + completion_type = COMPLETION_RESOURCE_PATH; + } + } } if (!replaced) { if (!_parse_arguments(op, op->arguments, p_static, true, p_parsing_constant)) @@ -827,6 +834,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s //check from singletons ConstantNode *constant = alloc_node<ConstantNode>(); constant->value = GDScriptLanguage::get_singleton()->get_named_globals_map()[identifier]; + constant->datatype = _type_from_variant(constant->value); expr = constant; bfn = true; } @@ -837,6 +845,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (scr.is_valid() && scr->is_valid()) { ConstantNode *constant = alloc_node<ConstantNode>(); constant->value = scr; + constant->datatype = _type_from_variant(constant->value); expr = constant; bfn = true; } @@ -852,6 +861,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (parent_constants.has(identifier)) { ConstantNode *constant = alloc_node<ConstantNode>(); constant->value = parent_constants[identifier]; + constant->datatype = _type_from_variant(constant->value); expr = constant; bfn = true; } @@ -2022,6 +2032,38 @@ GDScriptParser::Node *GDScriptParser::_parse_and_reduce_expression(Node *p_paren return expr; } +bool GDScriptParser::_reduce_export_var_type(Variant &p_value, int p_line) { + + if (p_value.get_type() == Variant::ARRAY) { + Array arr = p_value; + for (int i = 0; i < arr.size(); i++) { + if (!_reduce_export_var_type(arr[i], p_line)) return false; + } + return true; + } + + if (p_value.get_type() == Variant::DICTIONARY) { + Dictionary dict = p_value; + for (int i = 0; i < dict.size(); i++) { + Variant value = dict.get_value_at_index(i); + if (!_reduce_export_var_type(value, p_line)) return false; + } + return true; + } + + // validate type + DataType type = _type_from_variant(p_value); + if (type.kind == DataType::BUILTIN) { + return true; + } else if (type.kind == DataType::NATIVE) { + if (ClassDB::is_parent_class(type.native_type, "Resource")) { + return true; + } + } + _set_error("Invalid export type. Only built-in and native resource types can be exported.", p_line); + return false; +} + bool GDScriptParser::_recover_from_completion() { if (!completion_found) { @@ -2386,6 +2428,7 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m // a bind always matches ConstantNode *true_value = alloc_node<ConstantNode>(); true_value->value = Variant(true); + true_value->datatype = _type_from_variant(true_value->value); p_resulting_node = true_value; } break; case PatternNode::PT_ARRAY: { @@ -2432,6 +2475,7 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m // size ConstantNode *length = alloc_node<ConstantNode>(); length->value = Variant(open_ended ? p_pattern->array.size() - 1 : p_pattern->array.size()); + length->datatype = _type_from_variant(length->value); OperatorNode *call = alloc_node<OperatorNode>(); call->op = OperatorNode::OP_CALL; @@ -2465,6 +2509,7 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m ConstantNode *index = alloc_node<ConstantNode>(); index->value = Variant(i); + index->datatype = _type_from_variant(index->value); OperatorNode *indexed_value = alloc_node<OperatorNode>(); indexed_value->op = OperatorNode::OP_INDEX; @@ -2525,6 +2570,7 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m // size ConstantNode *length = alloc_node<ConstantNode>(); length->value = Variant(open_ended ? p_pattern->dictionary.size() - 1 : p_pattern->dictionary.size()); + length->datatype = _type_from_variant(length->value); OperatorNode *call = alloc_node<OperatorNode>(); call->op = OperatorNode::OP_CALL; @@ -2601,6 +2647,7 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m // simply generate a `true` ConstantNode *true_value = alloc_node<ConstantNode>(); true_value->value = Variant(true); + true_value->datatype = _type_from_variant(true_value->value); p_resulting_node = true_value; } break; default: { @@ -2683,6 +2730,7 @@ void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) { LocalVarNode *local_var = branch->body->variables[e->key()]; local_var->assign = e->value(); local_var->set_datatype(local_var->assign->get_datatype()); + local_var->assignments++; IdentifierNode *id2 = alloc_node<IdentifierNode>(); id2->name = local_var->name; @@ -2785,6 +2833,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { return; } + _mark_line_as_safe(line); NewLineNode *nl2 = alloc_node<NewLineNode>(); nl2->line = line; p_block->statements.push_back(nl2); @@ -3298,6 +3347,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { return; } + int assert_line = tokenizer->get_token_line(); + tokenizer->advance(); Vector<Node *> args; @@ -3311,14 +3362,21 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { return; } +#ifdef DEBUG_ENABLED + // Mark as safe, let type check mark as unsafe if needed + _mark_line_as_safe(assert_line); + _reduce_node_type(args[0]); +#endif AssertNode *an = alloc_node<AssertNode>(); an->condition = _reduce_expression(args[0], p_static); + an->line = assert_line; if (args.size() == 2) { an->message = _reduce_expression(args[1], p_static); } else { ConstantNode *message_node = alloc_node<ConstantNode>(); message_node->value = String(); + message_node->datatype = _type_from_variant(message_node->value); an->message = message_node; } @@ -4869,6 +4927,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("Can't accept a null constant expression for inferring export type."); return; } + + if (!_reduce_export_var_type(cn->value, member.line)) return; + member._export.type = cn->value.get_type(); member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; if (cn->value.get_type() == Variant::OBJECT) { @@ -6235,11 +6296,13 @@ GDScriptParser::Node *GDScriptParser::_get_default_value_for_type(const DataType ConstantNode *c = alloc_node<ConstantNode>(); Callable::CallError err; c->value = Variant::construct(p_type.builtin_type, nullptr, 0, err); + c->datatype = _type_from_variant(c->value); result = c; } } else { ConstantNode *c = alloc_node<ConstantNode>(); c->value = Variant(); + c->datatype = _type_from_variant(c->value); result = c; } @@ -8117,6 +8180,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { ConstantNode *tgt_type = alloc_node<ConstantNode>(); tgt_type->line = lv->line; tgt_type->value = (int)lv->datatype.builtin_type; + tgt_type->datatype = _type_from_variant(tgt_type->value); OperatorNode *convert_call = alloc_node<OperatorNode>(); convert_call->line = lv->line; @@ -8252,6 +8316,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { ConstantNode *tgt_type = alloc_node<ConstantNode>(); tgt_type->line = op->line; tgt_type->value = (int)lh_type.builtin_type; + tgt_type->datatype = _type_from_variant(tgt_type->value); OperatorNode *convert_call = alloc_node<OperatorNode>(); convert_call->line = op->line; diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index eca5f83f7a..f254352423 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -613,6 +613,7 @@ private: Node *_parse_expression(Node *p_parent, bool p_static, bool p_allow_assign = false, bool p_parsing_constant = false); Node *_reduce_expression(Node *p_node, bool p_to_const = false); Node *_parse_and_reduce_expression(Node *p_parent, bool p_static, bool p_reduce_const = false, bool p_allow_assign = false); + bool _reduce_export_var_type(Variant &p_value, int p_line = 0); PatternNode *_parse_pattern(bool p_static); void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static); diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index eb8feb5bc7..9c3101945a 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "grid_map_editor_plugin.h" -#include "core/input/input_filter.h" +#include "core/input/input.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/node_3d_editor_plugin.h" diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index 2f0a15f20b..48276c0d3d 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "mobile_vr_interface.h" -#include "core/input/input_filter.h" + +#include "core/input/input.h" #include "core/os/os.h" #include "servers/display_server.h" #include "servers/rendering/rendering_server_globals.h" @@ -118,7 +119,7 @@ void MobileVRInterface::set_position_from_sensors() { float delta_time = (double)ticks_elapsed / 1000000.0; // few things we need - InputFilter *input = InputFilter::get_singleton(); + Input *input = Input::get_singleton(); Vector3 down(0.0, -1.0, 0.0); // Down is Y negative Vector3 north(0.0, 0.0, 1.0); // North is Z positive diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs b/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs new file mode 100644 index 0000000000..85760a3705 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs @@ -0,0 +1,27 @@ +using System.IO; + +namespace GodotTools.Core +{ + public static class FileUtils + { + public static void SaveBackupCopy(string filePath) + { + string backupPathBase = filePath + ".old"; + string backupPath = backupPathBase; + + const int maxAttempts = 5; + int attempt = 1; + + while (File.Exists(backupPath) && attempt <= maxAttempts) + { + backupPath = backupPathBase + "." + (attempt); + attempt++; + } + + if (attempt > maxAttempts + 1) + return; + + File.Copy(filePath, backupPath, overwrite: true); + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj index 2c35ef540a..c9ea7d3a2c 100644 --- a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj @@ -31,6 +31,7 @@ <Reference Include="System" /> </ItemGroup> <ItemGroup> + <Compile Include="FileUtils.cs" /> <Compile Include="ProcessExtensions.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="StringExtensions.cs" /> diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs index 9afd9adeb1..6f318aab4a 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs @@ -153,7 +153,12 @@ EndProject"; var result = regex.Replace(input,m => dict[m.Value]); if (result != input) + { + // Save a copy of the solution before replacing it + FileUtils.SaveBackupCopy(slnPath); + File.WriteAllText(slnPath, result); + } } } } diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs index 1776b46e6a..f2ebef1a7d 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs @@ -9,8 +9,28 @@ using Microsoft.Build.Construction; namespace GodotTools.ProjectEditor { + public sealed class MSBuildProject + { + public ProjectRootElement Root { get; } + + public bool HasUnsavedChanges { get; set; } + + public void Save() => Root.Save(); + + public MSBuildProject(ProjectRootElement root) + { + Root = root; + } + } + public static class ProjectUtils { + public static MSBuildProject Open(string path) + { + var root = ProjectRootElement.Open(path); + return root != null ? new MSBuildProject(root) : null; + } + public static void AddItemToProjectChecked(string projectPath, string itemType, string include) { var dir = Directory.GetParent(projectPath).FullName; @@ -43,7 +63,6 @@ namespace GodotTools.ProjectEditor public static void RemoveItemFromProjectChecked(string projectPath, string itemType, string include) { - var dir = Directory.GetParent(projectPath).FullName; var root = ProjectRootElement.Open(projectPath); Debug.Assert(root != null); @@ -150,12 +169,9 @@ namespace GodotTools.ProjectEditor } /// Simple function to make sure the Api assembly references are configured correctly - public static void FixApiHintPath(string projectPath) + public static void FixApiHintPath(MSBuildProject project) { - var root = ProjectRootElement.Open(projectPath); - Debug.Assert(root != null); - - bool dirty = false; + var root = project.Root; void AddPropertyIfNotPresent(string name, string condition, string value) { @@ -170,7 +186,7 @@ namespace GodotTools.ProjectEditor } root.AddProperty(name, value).Condition = " " + condition + " "; - dirty = true; + project.HasUnsavedChanges = true; } AddPropertyIfNotPresent(name: "ApiConfiguration", @@ -212,7 +228,7 @@ namespace GodotTools.ProjectEditor } referenceWithHintPath.AddMetadata("HintPath", hintPath); - dirty = true; + project.HasUnsavedChanges = true; return; } @@ -221,14 +237,14 @@ namespace GodotTools.ProjectEditor { // Found a Reference item without a HintPath referenceWithoutHintPath.AddMetadata("HintPath", hintPath); - dirty = true; + project.HasUnsavedChanges = true; return; } } // Found no Reference item at all. Add it. root.AddItem("Reference", referenceName).Condition = " " + condition + " "; - dirty = true; + project.HasUnsavedChanges = true; } const string coreProjectName = "GodotSharp"; @@ -242,17 +258,11 @@ namespace GodotTools.ProjectEditor SetReferenceHintPath(coreProjectName, coreCondition, coreHintPath); SetReferenceHintPath(editorProjectName, editorCondition, editorHintPath); - - if (dirty) - root.Save(); } - public static void MigrateFromOldConfigNames(string projectPath) + public static void MigrateFromOldConfigNames(MSBuildProject project) { - var root = ProjectRootElement.Open(projectPath); - Debug.Assert(root != null); - - bool dirty = false; + var root = project.Root; bool hasGodotProjectGeneratorVersion = false; bool foundOldConfiguration = false; @@ -267,7 +277,7 @@ namespace GodotTools.ProjectEditor { configItem.Value = "Debug"; foundOldConfiguration = true; - dirty = true; + project.HasUnsavedChanges = true; } } @@ -275,7 +285,7 @@ namespace GodotTools.ProjectEditor { root.PropertyGroups.First(g => g.Condition == string.Empty)? .AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString()); - dirty = true; + project.HasUnsavedChanges = true; } if (!foundOldConfiguration) @@ -301,7 +311,7 @@ namespace GodotTools.ProjectEditor foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition.Trim() == oldCondition)) { propertyGroup.Condition = " " + newCondition + " "; - dirty = true; + project.HasUnsavedChanges = true; } foreach (var propertyGroup in root.PropertyGroups) @@ -309,14 +319,14 @@ namespace GodotTools.ProjectEditor foreach (var prop in propertyGroup.Properties.Where(p => p.Condition.Trim() == oldCondition)) { prop.Condition = " " + newCondition + " "; - dirty = true; + project.HasUnsavedChanges = true; } } foreach (var itemGroup in root.ItemGroups.Where(g => g.Condition.Trim() == oldCondition)) { itemGroup.Condition = " " + newCondition + " "; - dirty = true; + project.HasUnsavedChanges = true; } foreach (var itemGroup in root.ItemGroups) @@ -324,7 +334,7 @@ namespace GodotTools.ProjectEditor foreach (var item in itemGroup.Items.Where(item => item.Condition.Trim() == oldCondition)) { item.Condition = " " + newCondition + " "; - dirty = true; + project.HasUnsavedChanges = true; } } } @@ -340,10 +350,6 @@ namespace GodotTools.ProjectEditor MigrateConfigurationConditions("Release", "ExportRelease"); MigrateConfigurationConditions("Tools", "Debug"); // Must be last } - - - if (dirty) - root.Save(); } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index d782d4e61b..2ceb4888a2 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -168,13 +168,13 @@ namespace GodotTools.Export // Add dependency assemblies - var dependencies = new Godot.Collections.Dictionary<string, string>(); + var assemblies = new Godot.Collections.Dictionary<string, string>(); string projectDllName = GodotSharpEditor.ProjectAssemblyName; string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig); string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll"); - dependencies[projectDllName] = projectDllSrcPath; + assemblies[projectDllName] = projectDllSrcPath; if (platform == OS.Platforms.Android) { @@ -184,15 +184,15 @@ namespace GodotTools.Export if (!File.Exists(monoAndroidAssemblyPath)) throw new FileNotFoundException("Assembly not found: 'Mono.Android'", monoAndroidAssemblyPath); - dependencies["Mono.Android"] = monoAndroidAssemblyPath; + assemblies["Mono.Android"] = monoAndroidAssemblyPath; } string bclDir = DeterminePlatformBclDir(platform); - var initialDependencies = dependencies.Duplicate(); - internal_GetExportedAssemblyDependencies(initialDependencies, buildConfig, bclDir, dependencies); + var initialAssemblies = assemblies.Duplicate(); + internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies); - AddI18NAssemblies(dependencies, bclDir); + AddI18NAssemblies(assemblies, bclDir); string outputDataDir = null; @@ -211,20 +211,32 @@ namespace GodotTools.Export Directory.CreateDirectory(outputDataGameAssembliesDir); } - foreach (var dependency in dependencies) + foreach (var assembly in assemblies) { - string dependSrcPath = dependency.Value; - - if (assembliesInsidePck) - { - string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile()); - AddFile(dependSrcPath, dependDstPath); - } - else + void AddToAssembliesDir(string fileSrcPath) { - string dependDstPath = Path.Combine(outputDataDir, "Assemblies", dependSrcPath.GetFile()); - File.Copy(dependSrcPath, dependDstPath); + if (assembliesInsidePck) + { + string fileDstPath = Path.Combine(resAssembliesDir, fileSrcPath.GetFile()); + AddFile(fileSrcPath, fileDstPath); + } + else + { + Debug.Assert(outputDataDir != null); + string fileDstPath = Path.Combine(outputDataDir, "Assemblies", fileSrcPath.GetFile()); + File.Copy(fileSrcPath, fileDstPath); + } } + + string assemblySrcPath = assembly.Value; + + string assemblyPathWithoutExtension = Path.ChangeExtension(assemblySrcPath, null); + string pdbSrcPath = assemblyPathWithoutExtension + ".pdb"; + + AddToAssembliesDir(assemblySrcPath); + + if (File.Exists(pdbSrcPath)) + AddToAssembliesDir(pdbSrcPath); } // AOT compilation @@ -254,7 +266,7 @@ namespace GodotTools.Export ToolchainPath = aotToolchainPath }; - AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, dependencies); + AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, assemblies); } } @@ -366,7 +378,7 @@ namespace GodotTools.Export if (PlatformRequiresCustomBcl(platform)) throw new FileNotFoundException($"Missing BCL (Base Class Library) for platform: {platform}"); - platformBclDir = typeof(object).Assembly.Location; // Use the one we're running on + platformBclDir = typeof(object).Assembly.Location.GetBaseDir(); // Use the one we're running on } } @@ -425,7 +437,7 @@ namespace GodotTools.Export } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialDependencies, - string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencies); + private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialAssemblies, + string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencyAssemblies); } } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index c9d7dd26f8..c070cb16d9 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -1,4 +1,5 @@ using Godot; +using GodotTools.Core; using GodotTools.Export; using GodotTools.Utils; using System; @@ -442,13 +443,27 @@ namespace GodotTools { // Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath); + + var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath) + ?? throw new Exception("Cannot open C# project"); + + // NOTE: The order in which changes are made to the project is important + // Migrate csproj from old configuration names to: Debug, ExportDebug and ExportRelease - ProjectUtils.MigrateFromOldConfigNames(GodotSharpDirs.ProjectCsProjPath); + ProjectUtils.MigrateFromOldConfigNames(msbuildProject); - // Apply the other fixes after configurations are migrated + // Apply the other fixes only after configurations have been migrated // Make sure the existing project has Api assembly references configured correctly - ProjectUtils.FixApiHintPath(GodotSharpDirs.ProjectCsProjPath); + ProjectUtils.FixApiHintPath(msbuildProject); + + if (msbuildProject.HasUnsavedChanges) + { + // Save a copy of the project before replacing it + FileUtils.SaveBackupCopy(GodotSharpDirs.ProjectCsProjPath); + + msbuildProject.Save(); + } } catch (Exception e) { diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 283d4beb8e..c3e7e67ae9 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -231,14 +231,14 @@ int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObje return err; } -uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_dependencies, - MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_dependencies) { - Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_dependencies); +uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_assemblies, + MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_assembly_dependencies) { + Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_assemblies); String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config); String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir); - Dictionary dependencies = GDMonoMarshal::mono_object_to_variant(r_dependencies); + Dictionary assembly_dependencies = GDMonoMarshal::mono_object_to_variant(r_assembly_dependencies); - return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, dependencies); + return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, assembly_dependencies); } MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) { diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index 324013e5e2..4126da16be 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -50,13 +50,13 @@ String get_assemblyref_name(MonoImage *p_image, int index) { return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME])); } -Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) { +Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) { MonoImage *image = p_assembly->get_image(); for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) { String ref_name = get_assemblyref_name(image, i); - if (r_dependencies.has(ref_name)) + if (r_assembly_dependencies.has(ref_name)) continue; GDMonoAssembly *ref_assembly = nullptr; @@ -93,17 +93,17 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> ERR_FAIL_COND_V_MSG(!ref_assembly, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'."); // Use the path we got from the search. Don't try to get the path from the loaded assembly as we can't trust it will be from the selected BCL dir. - r_dependencies[ref_name] = path; + r_assembly_dependencies[ref_name] = path; - Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies); + Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_assembly_dependencies); ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'."); } return OK; } -Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencies, - const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_dependencies) { +Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies, + const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_assembly_dependencies) { MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport"); ERR_FAIL_NULL_V(export_domain, FAILED); _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain); @@ -113,16 +113,16 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencie Vector<String> search_dirs; GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir); - for (const Variant *key = p_initial_dependencies.next(); key; key = p_initial_dependencies.next(key)) { + for (const Variant *key = p_initial_assemblies.next(); key; key = p_initial_assemblies.next(key)) { String assembly_name = *key; - String assembly_path = p_initial_dependencies[*key]; + String assembly_path = p_initial_assemblies[*key]; GDMonoAssembly *assembly = nullptr; bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly, /* refonly: */ true); ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'."); - Error err = get_assembly_dependencies(assembly, search_dirs, r_dependencies); + Error err = get_assembly_dependencies(assembly, search_dirs, r_assembly_dependencies); if (err != OK) return err; } diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h index 36138f81b7..9ab57755de 100644 --- a/modules/mono/editor/godotsharp_export.h +++ b/modules/mono/editor/godotsharp_export.h @@ -41,8 +41,8 @@ namespace GodotSharpExport { Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies); -Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencies, - const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_dependencies); +Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies, + const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_assembly_dependencies); } // namespace GodotSharpExport diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index aba1065498..a963810d63 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -15,10 +15,7 @@ namespace Godot.Collections public override bool IsInvalid { - get - { - return handle == IntPtr.Zero; - } + get { return handle == IntPtr.Zero; } } protected override bool ReleaseHandle() @@ -43,7 +40,8 @@ namespace Godot.Collections if (collection == null) throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); - MarshalUtils.EnumerableToArray(collection, GetPtr()); + foreach (object element in collection) + Add(element); } internal Array(ArraySafeHandle handle) @@ -272,14 +270,8 @@ namespace Godot.Collections public T this[int index] { - get - { - return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); - } - set - { - objectArray[index] = value; - } + get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); } + set { objectArray[index] = value; } } public int IndexOf(T item) @@ -301,18 +293,12 @@ namespace Godot.Collections public int Count { - get - { - return objectArray.Count; - } + get { return objectArray.Count; } } public bool IsReadOnly { - get - { - return objectArray.IsReadOnly; - } + get { return objectArray.IsReadOnly; } } public void Add(T item) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index d72109de92..213fc181c1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -15,10 +15,7 @@ namespace Godot.Collections public override bool IsInvalid { - get - { - return handle == IntPtr.Zero; - } + get { return handle == IntPtr.Zero; } } protected override bool ReleaseHandle() @@ -45,7 +42,8 @@ namespace Godot.Collections if (dictionary == null) throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); - MarshalUtils.IDictionaryToDictionary(dictionary, GetPtr()); + foreach (DictionaryEntry entry in dictionary) + Add(entry.Key, entry.Value); } internal Dictionary(DictionarySafeHandle handle) @@ -330,14 +328,8 @@ namespace Godot.Collections public TValue this[TKey key] { - get - { - return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); - } - set - { - objectDict[key] = value; - } + get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); } + set { objectDict[key] = value; } } public ICollection<TKey> Keys @@ -385,18 +377,12 @@ namespace Godot.Collections public int Count { - get - { - return objectDict.Count; - } + get { return objectDict.Count; } } public bool IsReadOnly { - get - { - return objectDict.IsReadOnly; - } + get { return objectDict.IsReadOnly; } } public void Add(KeyValuePair<TKey, TValue> item) @@ -440,7 +426,8 @@ namespace Godot.Collections public bool Remove(KeyValuePair<TKey, TValue> item) { - return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); ; + return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); + ; } // IEnumerable<KeyValuePair<TKey, TValue>> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs index a1d63a62ef..c59d083080 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs @@ -16,10 +16,8 @@ namespace Godot /// <exception cref="System.InvalidOperationException"> /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. /// </exception> - static bool TypeIsGenericArray(Type type) - { - return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); - } + static bool TypeIsGenericArray(Type type) => + type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); /// <summary> /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> @@ -28,10 +26,20 @@ namespace Godot /// <exception cref="System.InvalidOperationException"> /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. /// </exception> - static bool TypeIsGenericDictionary(Type type) - { - return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); - } + static bool TypeIsGenericDictionary(Type type) => + type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); + + static bool TypeIsSystemGenericList(Type type) => + type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.List<>); + + static bool TypeIsSystemGenericDictionary(Type type) => + type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.Dictionary<,>); + + static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>); + + static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>); + + static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>); static void ArrayGetElementType(Type arrayType, out Type elementType) { @@ -45,105 +53,6 @@ namespace Godot valueType = genericArgs[1]; } - static bool GenericIEnumerableIsAssignableFromType(Type type) - { - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - return true; - - foreach (var interfaceType in type.GetInterfaces()) - { - if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - return true; - } - - Type baseType = type.BaseType; - - if (baseType == null) - return false; - - return GenericIEnumerableIsAssignableFromType(baseType); - } - - static bool GenericIDictionaryIsAssignableFromType(Type type) - { - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) - return true; - - foreach (var interfaceType in type.GetInterfaces()) - { - if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) - return true; - } - - Type baseType = type.BaseType; - - if (baseType == null) - return false; - - return GenericIDictionaryIsAssignableFromType(baseType); - } - - static bool GenericIEnumerableIsAssignableFromType(Type type, out Type elementType) - { - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - { - elementType = type.GetGenericArguments()[0]; - return true; - } - - foreach (var interfaceType in type.GetInterfaces()) - { - if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - { - elementType = interfaceType.GetGenericArguments()[0]; - return true; - } - } - - Type baseType = type.BaseType; - - if (baseType == null) - { - elementType = null; - return false; - } - - return GenericIEnumerableIsAssignableFromType(baseType, out elementType); - } - - static bool GenericIDictionaryIsAssignableFromType(Type type, out Type keyType, out Type valueType) - { - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) - { - var genericArgs = type.GetGenericArguments(); - keyType = genericArgs[0]; - valueType = genericArgs[1]; - return true; - } - - foreach (var interfaceType in type.GetInterfaces()) - { - if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) - { - var genericArgs = interfaceType.GetGenericArguments(); - keyType = genericArgs[0]; - valueType = genericArgs[1]; - return true; - } - } - - Type baseType = type.BaseType; - - if (baseType == null) - { - keyType = null; - valueType = null; - return false; - } - - return GenericIDictionaryIsAssignableFromType(baseType, out keyType, out valueType); - } - static Type MakeGenericArrayType(Type elemType) { return typeof(Godot.Collections.Array<>).MakeGenericType(elemType); @@ -153,60 +62,5 @@ namespace Godot { return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType); } - - // TODO Add support for IEnumerable<T> and IDictionary<TKey, TValue> - // TODO: EnumerableToArray and IDictionaryToDictionary can be optimized - - internal static void EnumerableToArray(IEnumerable enumerable, IntPtr godotArrayPtr) - { - if (enumerable is ICollection collection) - { - int count = collection.Count; - - object[] tempArray = new object[count]; - collection.CopyTo(tempArray, 0); - - for (int i = 0; i < count; i++) - { - Array.godot_icall_Array_Add(godotArrayPtr, tempArray[i]); - } - } - else - { - foreach (object element in enumerable) - { - Array.godot_icall_Array_Add(godotArrayPtr, element); - } - } - } - - internal static void IDictionaryToDictionary(IDictionary dictionary, IntPtr godotDictionaryPtr) - { - foreach (DictionaryEntry entry in dictionary) - { - Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); - } - } - - internal static void GenericIDictionaryToDictionary(object dictionary, IntPtr godotDictionaryPtr) - { -#if DEBUG - if (!GenericIDictionaryIsAssignableFromType(dictionary.GetType())) - throw new InvalidOperationException("The type does not implement IDictionary<,>"); -#endif - - // TODO: Can we optimize this? - - var keys = ((IEnumerable)dictionary.GetType().GetProperty("Keys").GetValue(dictionary)).GetEnumerator(); - var values = ((IEnumerable)dictionary.GetType().GetProperty("Values").GetValue(dictionary)).GetEnumerator(); - - while (keys.MoveNext() && values.MoveNext()) - { - object key = keys.Current; - object value = values.Current; - - Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, key, value); - } - } } } diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 306a1997ab..3298c5da4c 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -129,12 +129,8 @@ void gd_mono_profiler_init() { } } -#if defined(DEBUG_ENABLED) - void gd_mono_debug_init() { - mono_debug_init(MONO_DEBUG_FORMAT_MONO); - CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8(); #ifdef TOOLS_ENABLED @@ -159,6 +155,10 @@ void gd_mono_debug_init() { return; // Exported games don't use the project settings to setup the debugger agent #endif + // Debugging enabled + + mono_debug_init(MONO_DEBUG_FORMAT_MONO); + // --debugger-agent=help const char *options[] = { "--soft-breakpoints", @@ -167,7 +167,6 @@ void gd_mono_debug_init() { mono_jit_parse_options(2, (char **)options); } -#endif // defined(DEBUG_ENABLED) #endif // !defined(JAVASCRIPT_ENABLED) #if defined(JAVASCRIPT_ENABLED) @@ -175,6 +174,7 @@ MonoDomain *gd_initialize_mono_runtime() { const char *vfs_prefix = "managed"; int enable_debugging = 0; + // TODO: Provide a way to enable debugging on WASM release builds. #ifdef DEBUG_ENABLED enable_debugging = 1; #endif @@ -185,9 +185,7 @@ MonoDomain *gd_initialize_mono_runtime() { } #else MonoDomain *gd_initialize_mono_runtime() { -#ifdef DEBUG_ENABLED gd_mono_debug_init(); -#endif #if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED) // I don't know whether this actually matters or not @@ -1389,7 +1387,10 @@ bool _GodotSharp::is_runtime_initialized() { void _GodotSharp::_reload_assemblies(bool p_soft_reload) { #ifdef GD_MONO_HOT_RELOAD - CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload); + // This method may be called more than once with `call_deferred`, so we need to check + // again if reloading is needed to avoid reloading multiple times unnecessarily. + if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) + CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload); #endif } diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 8439769d84..0f211eebc6 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -277,12 +277,25 @@ no_pdb: #endif + bool need_manual_load_hook = mono_image_get_assembly(image) != nullptr; // Re-using an existing image with an assembly loaded + status = MONO_IMAGE_OK; MonoAssembly *assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, p_refonly); ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !assembly, nullptr, "Failed to load assembly for image"); + if (need_manual_load_hook) { + // For some reason if an assembly survived domain reloading (maybe because it's referenced somewhere else), + // the mono internal search hook don't detect it, yet mono_image_open_from_data_with_name re-uses the image + // and assembly, and mono_assembly_load_from_full doesn't call the load hook. We need to call it manually. + String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly))); + bool has_extension = name.ends_with(".dll") || name.ends_with(".exe"); + GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name); + if (!loaded_asm) + assembly_load_hook(assembly, nullptr); + } + // Decrement refcount which was previously incremented by mono_image_open_from_data_with_name mono_image_close(image); diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index facc0da780..5ddf18d544 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -84,6 +84,7 @@ void CachedData::clear_corlib_cache() { class_IntPtr = nullptr; class_System_Collections_IEnumerable = nullptr; + class_System_Collections_ICollection = nullptr; class_System_Collections_IDictionary = nullptr; #ifdef DEBUG_ENABLED @@ -171,22 +172,18 @@ void CachedData::clear_godot_api_cache() { methodthunk_MarshalUtils_TypeIsGenericArray.nullify(); methodthunk_MarshalUtils_TypeIsGenericDictionary.nullify(); + methodthunk_MarshalUtils_TypeIsSystemGenericList.nullify(); + methodthunk_MarshalUtils_TypeIsSystemGenericDictionary.nullify(); + methodthunk_MarshalUtils_TypeIsGenericIEnumerable.nullify(); + methodthunk_MarshalUtils_TypeIsGenericICollection.nullify(); + methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify(); methodthunk_MarshalUtils_ArrayGetElementType.nullify(); methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify(); - methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType.nullify(); - methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType.nullify(); - methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info.nullify(); - methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info.nullify(); - methodthunk_MarshalUtils_MakeGenericArrayType.nullify(); methodthunk_MarshalUtils_MakeGenericDictionaryType.nullify(); - methodthunk_MarshalUtils_EnumerableToArray.nullify(); - methodthunk_MarshalUtils_IDictionaryToDictionary.nullify(); - methodthunk_MarshalUtils_GenericIDictionaryToDictionary.nullify(); - // End of MarshalUtils methods task_scheduler_handle = Ref<MonoGCHandleRef>(); @@ -213,6 +210,7 @@ void update_corlib_cache() { CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class())); CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable")); + CACHE_CLASS_AND_CHECK(System_Collections_ICollection, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "ICollection")); CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary")); #ifdef DEBUG_ENABLED @@ -297,22 +295,18 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericDictionary", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericList, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericList", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericDictionary", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIEnumerable, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIEnumerable", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericICollection, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericICollection", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIDictionary", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, GODOT_API_CLASS(MarshalUtils)->get_method("ArrayGetElementType", 2)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, GODOT_API_CLASS(MarshalUtils)->get_method("DictionaryGetKeyValueTypes", 3)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIEnumerableIsAssignableFromType", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryIsAssignableFromType", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIEnumerableIsAssignableFromType", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryIsAssignableFromType", 3)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericArrayType", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericDictionaryType", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, GODOT_API_CLASS(MarshalUtils)->get_method("EnumerableToArray", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("IDictionaryToDictionary", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryToDictionary", 2)); - // End of MarshalUtils methods #ifdef DEBUG_ENABLED diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 21c8ed4efe..3cf2bd6ce5 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -58,6 +58,7 @@ struct CachedData { GDMonoClass *class_IntPtr; // System.IntPtr GDMonoClass *class_System_Collections_IEnumerable; + GDMonoClass *class_System_Collections_ICollection; GDMonoClass *class_System_Collections_IDictionary; #ifdef DEBUG_ENABLED @@ -141,22 +142,18 @@ struct CachedData { GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray; GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericDictionary; + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsSystemGenericList; + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsSystemGenericDictionary; + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIEnumerable; + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericICollection; + GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIDictionary; GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_ArrayGetElementType; GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info; - GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericArrayType; GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericDictionaryType; - GDMonoMethodThunk<MonoObject *, Array *> methodthunk_MarshalUtils_EnumerableToArray; - GDMonoMethodThunk<MonoObject *, Dictionary *> methodthunk_MarshalUtils_IDictionaryToDictionary; - GDMonoMethodThunk<MonoObject *, Dictionary *> methodthunk_MarshalUtils_GenericIDictionaryToDictionary; - // End of MarshalUtils methods Ref<MonoGCHandleRef> task_scheduler_handle; @@ -186,14 +183,6 @@ inline void clear_godot_api_cache() { cached_data.clear_godot_api_cache(); } -_FORCE_INLINE_ bool tools_godot_api_check() { -#ifdef TOOLS_ENABLED - return cached_data.godot_api_cache_updated; -#else - return true; // Assume it's updated if this was called, otherwise it's a bug -#endif -} - } // namespace GDMonoCache #define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class) diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index ede4203e35..2c65f7e3a0 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -31,6 +31,7 @@ #include "gd_mono_class.h" #include <mono/metadata/attrdefs.h> +#include <mono/metadata/debug-helpers.h> #include "gd_mono_assembly.h" #include "gd_mono_cache.h" @@ -55,7 +56,11 @@ String GDMonoClass::get_full_name() const { return get_full_name(mono_class); } -MonoType *GDMonoClass::get_mono_type() { +String GDMonoClass::get_type_desc() const { + return GDMonoUtils::get_type_desc(get_mono_type()); +} + +MonoType *GDMonoClass::get_mono_type() const { // Careful, you cannot compare two MonoType*. // There is mono_metadata_type_equal, how is this different from comparing two MonoClass*? return get_mono_type(mono_class); @@ -264,6 +269,12 @@ bool GDMonoClass::implements_interface(GDMonoClass *p_interface) { return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr()); } +bool GDMonoClass::has_public_parameterless_ctor() { + + GDMonoMethod *ctor = get_method(".ctor", 0); + return ctor && ctor->get_visibility() == IMonoClassMember::PUBLIC; +} + GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) { MethodKey key = MethodKey(p_name, p_params_count); @@ -328,6 +339,9 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class); mono_method_desc_free(desc); + if (!method) + return nullptr; + ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, nullptr); return get_method(method); diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index 0c9a8cdafe..9237aae057 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -31,8 +31,6 @@ #ifndef GD_MONO_CLASS_H #define GD_MONO_CLASS_H -#include <mono/metadata/debug-helpers.h> - #include "core/map.h" #include "core/ustring.h" @@ -107,7 +105,8 @@ public: static MonoType *get_mono_type(MonoClass *p_mono_class); String get_full_name() const; - MonoType *get_mono_type(); + String get_type_desc() const; + MonoType *get_mono_type() const; uint32_t get_flags() const; bool is_static() const; @@ -137,6 +136,7 @@ public: void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base); bool implements_interface(GDMonoClass *p_interface); + bool has_public_parameterless_ctor(); GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0); GDMonoMethod *get_method(MonoMethod *p_raw_method); diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 3f4e5fe5ac..e76cb84d43 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -322,6 +322,13 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); + if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) { + MonoArray *managed = GDMonoMarshal::Array_to_mono_array(p_value.operator ::Array(), array_type_class); + mono_field_set_value(p_object, mono_field, managed); + break; + } + ERR_FAIL_MSG("Attempted to convert Variant to a managed array of unmarshallable element type."); } break; @@ -353,56 +360,22 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } - if (CACHED_CLASS(Dictionary) == type_class) { + // Godot.Collections.Dictionary or IDictionary + if (CACHED_CLASS(Dictionary) == type_class || type_class == CACHED_CLASS(System_Collections_IDictionary)) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); mono_field_set_value(p_object, mono_field, managed); break; } - if (CACHED_CLASS(Array) == type_class) { + // Godot.Collections.Array or ICollection or IEnumerable + if (CACHED_CLASS(Array) == type_class || + type_class == CACHED_CLASS(System_Collections_ICollection) || + type_class == CACHED_CLASS(System_Collections_IEnumerable)) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); mono_field_set_value(p_object, mono_field, managed); break; } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); - - MonoReflectionType *key_reftype, *value_reftype; - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), - GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - MonoReflectionType *elem_reftype; - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), - GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoCache::tools_godot_api_check()) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); - mono_field_set_value(p_object, mono_field, managed); - break; - } else { - MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array()); - mono_field_set_value(p_object, mono_field, managed); - break; - } - } - ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + type_class->get_name() + "'."); } break; @@ -535,52 +508,62 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type()); + // Godot.Collections.Dictionary<TKey, TValue> if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class); mono_field_set_value(p_object, mono_field, managed); break; } + // Godot.Collections.Array<T> if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class); mono_field_set_value(p_object, mono_field, managed); break; } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *key_reftype, *value_reftype; - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), - GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); + // System.Collections.Generic.Dictionary<TKey, TValue> + if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { + MonoReflectionType *key_reftype = nullptr; + MonoReflectionType *value_reftype = nullptr; + GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); + MonoObject *managed = GDMonoMarshal::Dictionary_to_system_generic_dict(p_value.operator Dictionary(), + type.type_class, key_reftype, value_reftype); mono_field_set_value(p_object, mono_field, managed); break; } - if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + // System.Collections.Generic.List<T> + if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { + MonoReflectionType *elem_reftype = nullptr; + GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); + MonoObject *managed = GDMonoMarshal::Array_to_system_generic_list(p_value.operator Array(), + type.type_class, elem_reftype); mono_field_set_value(p_object, mono_field, managed); break; } - MonoReflectionType *elem_reftype; - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), - GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); + // IDictionary<TKey, TValue> + if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) { + MonoReflectionType *key_reftype; + MonoReflectionType *value_reftype; + GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); + GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype); + + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), godot_dict_class); mono_field_set_value(p_object, mono_field, managed); break; } - if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoCache::tools_godot_api_check()) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); - mono_field_set_value(p_object, mono_field, managed); - break; - } else { - MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array()); - mono_field_set_value(p_object, mono_field, managed); - break; - } + // ICollection<T> or IEnumerable<T> + if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) { + MonoReflectionType *elem_reftype; + GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); + GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype); + + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), godot_array_class); + mono_field_set_value(p_object, mono_field, managed); + break; } } break; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 1878038f44..91ee07388b 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -154,6 +154,10 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_ if (array_type->eklass == CACHED_CLASS_RAW(Color)) return Variant::PACKED_COLOR_ARRAY; + + GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); + if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) + return Variant::ARRAY; } break; case MONO_TYPE_CLASS: { @@ -184,23 +188,14 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_ return Variant::ARRAY; } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); - - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { - return Variant::DICTIONARY; - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + // IDictionary + if (p_type.type_class == CACHED_CLASS(System_Collections_IDictionary)) { return Variant::DICTIONARY; } - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) { - return Variant::ARRAY; - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + // ICollection or IEnumerable + if (p_type.type_class == CACHED_CLASS(System_Collections_ICollection) || + p_type.type_class == CACHED_CLASS(System_Collections_IEnumerable)) { return Variant::ARRAY; } } break; @@ -214,27 +209,33 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_ case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); + // Godot.Collections.Dictionary<TKey, TValue> if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { return Variant::DICTIONARY; } + // Godot.Collections.Array<T> if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { return Variant::ARRAY; } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) - return Variant::DICTIONARY; - - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + // System.Collections.Generic.Dictionary<TKey, TValue> + if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { return Variant::DICTIONARY; } - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) + // System.Collections.Generic.List<T> + if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { return Variant::ARRAY; + } - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + // IDictionary<TKey, TValue> + if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) { + return Variant::DICTIONARY; + } + + // ICollection<T> or IEnumerable<T> + if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) { return Variant::ARRAY; } } break; @@ -252,10 +253,20 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) { switch (p_array_type.type_encoding) { + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: { + MonoArrayType *array_type = mono_type_get_array_type(p_array_type.type_class->get_mono_type()); + GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); + r_elem_type = ManagedType::from_class(array_type_class); + return true; + } break; case MONO_TYPE_GENERICINST: { MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type()); - if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) { + if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype) || + GDMonoUtils::Marshal::type_is_system_generic_list(array_reftype) || + GDMonoUtils::Marshal::type_is_generic_icollection(array_reftype) || + GDMonoUtils::Marshal::type_is_generic_ienumerable(array_reftype)) { MonoReflectionType *elem_reftype; GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype); @@ -263,12 +274,6 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_ r_elem_type = ManagedType::from_reftype(elem_reftype); return true; } - - MonoReflectionType *elem_reftype; - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(array_reftype, &elem_reftype)) { - r_elem_type = ManagedType::from_reftype(elem_reftype); - return true; - } } break; default: { } break; @@ -282,7 +287,9 @@ bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, Ma case MONO_TYPE_GENERICINST: { MonoReflectionType *dict_reftype = mono_type_get_object(mono_domain_get(), p_dictionary_type.type_class->get_mono_type()); - if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) { + if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype) || + GDMonoUtils::Marshal::type_is_system_generic_dictionary(dict_reftype) || + GDMonoUtils::Marshal::type_is_generic_idictionary(dict_reftype)) { MonoReflectionType *key_reftype; MonoReflectionType *value_reftype; @@ -292,13 +299,6 @@ bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, Ma r_value_type = ManagedType::from_reftype(value_reftype); return true; } - - MonoReflectionType *key_reftype, *value_reftype; - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(dict_reftype, &key_reftype, &value_reftype)) { - r_key_type = ManagedType::from_reftype(key_reftype); - r_value_type = ManagedType::from_reftype(value_reftype); - return true; - } } break; default: { } break; @@ -577,6 +577,10 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty if (array_type->eklass == CACHED_CLASS_RAW(Color)) return (MonoObject *)PackedColorArray_to_mono_array(p_var->operator PackedColorArray()); + GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); + if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) + return (MonoObject *)Array_to_mono_array(p_var->operator Array(), array_type_class); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to a managed array of unmarshallable element type."); } break; @@ -600,41 +604,17 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return GDMonoUtils::create_managed_from(p_var->operator RID()); } - if (CACHED_CLASS(Dictionary) == type_class) { + // Godot.Collections.Dictionary or IDictionary + if (CACHED_CLASS(Dictionary) == type_class || CACHED_CLASS(System_Collections_IDictionary) == type_class) { return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); } - if (CACHED_CLASS(Array) == type_class) { + // Godot.Collections.Array or ICollection or IEnumerable + if (CACHED_CLASS(Array) == type_class || + CACHED_CLASS(System_Collections_ICollection) == type_class || + CACHED_CLASS(System_Collections_IEnumerable) == type_class) { return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); } - - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); - - MonoReflectionType *key_reftype, *value_reftype; - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), - GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); - } - - MonoReflectionType *elem_reftype; - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), - GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoCache::tools_godot_api_check()) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); - } else { - return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array()); - } - } } break; case MONO_TYPE_OBJECT: { // Variant @@ -755,38 +735,48 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); + // Godot.Collections.Dictionary<TKey, TValue> if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class); } + // Godot.Collections.Array<T> if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class); } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *key_reftype, *value_reftype; - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), - GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); + // System.Collections.Generic.Dictionary<TKey, TValue> + if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { + MonoReflectionType *key_reftype = nullptr; + MonoReflectionType *value_reftype = nullptr; + GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); + return Dictionary_to_system_generic_dict(p_var->operator Dictionary(), p_type.type_class, key_reftype, value_reftype); } - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); + // System.Collections.Generic.List<T> + if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { + MonoReflectionType *elem_reftype = nullptr; + GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); + return Array_to_system_generic_list(p_var->operator Array(), p_type.type_class, elem_reftype); } - MonoReflectionType *elem_reftype; - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), - GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); + // IDictionary<TKey, TValue> + if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) { + MonoReflectionType *key_reftype; + MonoReflectionType *value_reftype; + GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); + GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype); + + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), godot_dict_class); } - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoCache::tools_godot_api_check()) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); - } else { - return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array()); - } + // ICollection<T> or IEnumerable<T> + if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) { + MonoReflectionType *elem_reftype; + GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); + GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype); + + return GDMonoUtils::create_managed_from(p_var->operator Array(), godot_array_class); } } break; } break; @@ -922,6 +912,10 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type if (array_type->eklass == CACHED_CLASS_RAW(Color)) return mono_array_to_PackedColorArray((MonoArray *)p_obj); + GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); + if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) + return mono_array_to_Array((MonoArray *)p_obj); + if (p_fail_with_err) { ERR_FAIL_V_MSG(Variant(), "Attempted to convert a managed array of unmarshallable element type to Variant."); } else { @@ -957,44 +951,27 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return ptr ? Variant(*ptr) : Variant(); } - if (CACHED_CLASS(Array) == type_class) { + // Godot.Collections.Dictionary + if (CACHED_CLASS(Dictionary) == type_class) { MonoException *exc = nullptr; - Array *ptr = CACHED_METHOD_THUNK(Array, GetPtr).invoke(p_obj, &exc); + Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc); UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } - if (CACHED_CLASS(Dictionary) == type_class) { + // Godot.Collections.Array + if (CACHED_CLASS(Array) == type_class) { MonoException *exc = nullptr; - Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc); + Array *ptr = CACHED_METHOD_THUNK(Array, GetPtr).invoke(p_obj, &exc); UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } - - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); - - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { - return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj); - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj); - } - - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) { - return GDMonoUtils::Marshal::enumerable_to_array(p_obj); - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - return GDMonoUtils::Marshal::enumerable_to_array(p_obj); - } } break; case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); + // Godot.Collections.Dictionary<TKey, TValue> if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { MonoException *exc = nullptr; MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); @@ -1002,6 +979,7 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return *unbox<Dictionary *>(ret); } + // Godot.Collections.Array<T> if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { MonoException *exc = nullptr; MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); @@ -1009,22 +987,19 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return *unbox<Array *>(ret); } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { - return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj); - } - - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj); + // System.Collections.Generic.Dictionary<TKey, TValue> + if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { + MonoReflectionType *key_reftype = nullptr; + MonoReflectionType *value_reftype = nullptr; + GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype); + return system_generic_dict_to_Dictionary(p_obj, p_type.type_class, key_reftype, value_reftype); } - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) { - return GDMonoUtils::Marshal::enumerable_to_array(p_obj); - } - - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - return GDMonoUtils::Marshal::enumerable_to_array(p_obj); + // System.Collections.Generic.List<T> + if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { + MonoReflectionType *elem_reftype = nullptr; + GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype); + return system_generic_list_to_Array(p_obj, p_type.type_class, elem_reftype); } } break; } @@ -1081,6 +1056,80 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) { } } +MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { + String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) + + ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)"; + GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true); + CRASH_COND(ctor == nullptr); + + MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); + ERR_FAIL_NULL_V(mono_object, nullptr); + + GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype); + MonoObject *godot_dict = GDMonoUtils::create_managed_from(p_dict, godot_dict_class); + + void *ctor_args[1] = { godot_dict }; + + MonoException *exc = nullptr; + ctor->invoke_raw(mono_object, ctor_args, &exc); + UNHANDLED_EXCEPTION(exc); + + return mono_object; +} + +Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]] GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { + GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype); + String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) + + ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)"; + GDMonoMethod *godot_dict_ctor = godot_dict_class->get_method_with_desc(ctor_desc, true); + CRASH_COND(godot_dict_ctor == nullptr); + + MonoObject *godot_dict = mono_object_new(mono_domain_get(), godot_dict_class->get_mono_ptr()); + ERR_FAIL_NULL_V(godot_dict, Dictionary()); + + void *ctor_args[1] = { p_obj }; + + MonoException *exc = nullptr; + godot_dict_ctor->invoke_raw(godot_dict, ctor_args, &exc); + UNHANDLED_EXCEPTION(exc); + + exc = nullptr; + MonoObject *ret = godot_dict_class->get_method("GetPtr")->invoke(godot_dict, &exc); + UNHANDLED_EXCEPTION(exc); + + return *unbox<Dictionary *>(ret); +} + +MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype) { + GDMonoClass *elem_class = ManagedType::from_reftype(p_elem_reftype).type_class; + + String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + elem_class->get_type_desc() + ">)"; + GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true); + CRASH_COND(ctor == nullptr); + + MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); + ERR_FAIL_NULL_V(mono_object, nullptr); + + void *ctor_args[1] = { Array_to_mono_array(p_array, elem_class) }; + + MonoException *exc = nullptr; + ctor->invoke_raw(mono_object, ctor_args, &exc); + UNHANDLED_EXCEPTION(exc); + + return mono_object; +} + +Array system_generic_list_to_Array(MonoObject *p_obj, GDMonoClass *p_class, [[maybe_unused]] MonoReflectionType *p_elem_reftype) { + GDMonoMethod *to_array = p_class->get_method("ToArray", 0); + CRASH_COND(to_array == nullptr); + + MonoException *exc = nullptr; + MonoArray *mono_array = (MonoArray *)to_array->invoke_raw(p_obj, nullptr, &exc); + UNHANDLED_EXCEPTION(exc); + + return mono_array_to_Array(mono_array); +} + MonoArray *Array_to_mono_array(const Array &p_array) { int length = p_array.size(); MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length); @@ -1093,6 +1142,18 @@ MonoArray *Array_to_mono_array(const Array &p_array) { return ret; } +MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_class) { + int length = p_array.size(); + MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class->get_mono_ptr(), length); + + for (int i = 0; i < length; i++) { + MonoObject *boxed = variant_to_mono_object(p_array[i]); + mono_array_setref(ret, i, boxed); + } + + return ret; +} + Array mono_array_to_Array(MonoArray *p_array) { Array ret; if (!p_array) diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 8b405291a7..f2d887e6d6 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -123,9 +123,18 @@ Variant mono_object_to_variant_no_err(MonoObject *p_obj, const ManagedType &p_ty /// If the MonoObject* cannot be converted to Variant, then 'ToString()' is called instead. String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc); +// System.Collections.Generic + +MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); +Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); + +MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype); +Array system_generic_list_to_Array(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype); + // Array MonoArray *Array_to_mono_array(const Array &p_array); +MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_class); Array mono_array_to_Array(MonoArray *p_array); // PackedInt32Array diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index c8cc5247c6..432aa74a6f 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -30,13 +30,14 @@ #include "gd_mono_method.h" +#include <mono/metadata/attrdefs.h> +#include <mono/metadata/debug-helpers.h> + #include "gd_mono_cache.h" #include "gd_mono_class.h" #include "gd_mono_marshal.h" #include "gd_mono_utils.h" -#include <mono/metadata/attrdefs.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. diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 00119ced88..f9d492dabb 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -30,6 +30,7 @@ #include "gd_mono_utils.h" +#include <mono/metadata/debug-helpers.h> #include <mono/metadata/exception.h> #include "core/debugger/engine_debugger.h" @@ -358,6 +359,14 @@ MonoDomain *create_domain(const String &p_friendly_name) { return domain; } +String get_type_desc(MonoType *p_type) { + return mono_type_full_name(p_type); +} + +String get_type_desc(MonoReflectionType *p_reftype) { + return get_type_desc(mono_reflection_type_get_type(p_reftype)); +} + String get_exception_name_and_message(MonoException *p_exc) { String res; @@ -584,75 +593,56 @@ bool type_is_generic_dictionary(MonoReflectionType *p_reftype) { return (bool)res; } -void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) { - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc); - UNHANDLED_EXCEPTION(exc); -} - -void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { - MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes).invoke(p_dict_reftype, r_key_reftype, r_value_reftype, &exc); - UNHANDLED_EXCEPTION(exc); -} - -bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) { +bool type_is_system_generic_list(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType).invoke(p_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericList).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } -bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) { +bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType).invoke(p_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericDictionary).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } -bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) { +bool type_is_generic_ienumerable(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info).invoke(p_reftype, r_elem_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIEnumerable).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } -bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { +bool type_is_generic_icollection(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info).invoke(p_reftype, r_key_reftype, r_value_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericICollection).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } -Array enumerable_to_array(MonoObject *p_enumerable) { - NO_GLUE_RET(Array()); - Array result; +bool type_is_generic_idictionary(MonoReflectionType *p_reftype) { + NO_GLUE_RET(false); MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray).invoke(p_enumerable, &result, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIDictionary).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); - return result; + return (bool)res; } -Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) { - NO_GLUE_RET(Dictionary()); - Dictionary result; +void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) { MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary).invoke(p_idictionary, &result, &exc); + CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc); UNHANDLED_EXCEPTION(exc); - return result; } -Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) { - NO_GLUE_RET(Dictionary()); - Dictionary result; +void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary).invoke(p_generic_idictionary, &result, &exc); + CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes).invoke(p_dict_reftype, r_key_reftype, r_value_reftype, &exc); UNHANDLED_EXCEPTION(exc); - return result; } GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) { diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index b850e1be9b..e3011ade5d 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -52,22 +52,18 @@ namespace Marshal { bool type_is_generic_array(MonoReflectionType *p_reftype); bool type_is_generic_dictionary(MonoReflectionType *p_reftype); +bool type_is_system_generic_list(MonoReflectionType *p_reftype); +bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype); +bool type_is_generic_ienumerable(MonoReflectionType *p_reftype); +bool type_is_generic_icollection(MonoReflectionType *p_reftype); +bool type_is_generic_idictionary(MonoReflectionType *p_reftype); void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype); void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype); -bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype); -bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype); -bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype); -bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype); - GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype); GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); -Array enumerable_to_array(MonoObject *p_enumerable); -Dictionary idictionary_to_dictionary(MonoObject *p_idictionary); -Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary); - } // namespace Marshal _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) { @@ -115,6 +111,9 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class); MonoDomain *create_domain(const String &p_friendly_name); +String get_type_desc(MonoType *p_type); +String get_type_desc(MonoReflectionType *p_reftype); + String get_exception_name_and_message(MonoException *p_exc); void set_exception_message(MonoException *p_exc, String message); diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 59b1bcdd96..9a4076bec4 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -30,7 +30,7 @@ #include "visual_script_editor.h" -#include "core/input/input_filter.h" +#include "core/input/input.h" #include "core/object.h" #include "core/os/keyboard.h" #include "core/script_language.h" @@ -1073,9 +1073,9 @@ void VisualScriptEditor::_member_selected() { if (ti->get_parent() == members->get_root()->get_children()) { #ifdef OSX_ENABLED - bool held_ctrl = InputFilter::get_singleton()->is_key_pressed(KEY_META); + bool held_ctrl = Input::get_singleton()->is_key_pressed(KEY_META); #else - bool held_ctrl = InputFilter::get_singleton()->is_key_pressed(KEY_CONTROL); + bool held_ctrl = Input::get_singleton()->is_key_pressed(KEY_CONTROL); #endif if (held_ctrl) { ERR_FAIL_COND(!script->has_function(selected)); @@ -1378,7 +1378,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt } } else if (ti->get_parent() == root->get_children()) { selected = ti->get_text(0); - function_name_edit->set_position(InputFilter::get_singleton()->get_mouse_position() - Vector2(60, -10)); + function_name_edit->set_position(Input::get_singleton()->get_mouse_position() - Vector2(60, -10)); function_name_edit->popup(); function_name_box->set_text(selected); function_name_box->select_all(); @@ -1740,7 +1740,7 @@ void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> key = p_event; if (key.is_valid() && !key->is_pressed()) { - mouse_up_position = InputFilter::get_singleton()->get_mouse_position(); + mouse_up_position = Input::get_singleton()->get_mouse_position(); } } @@ -1750,7 +1750,7 @@ void VisualScriptEditor::_graph_gui_input(const Ref<InputEvent> &p_event) { if (key.is_valid() && key->is_pressed() && key->get_button_mask() == BUTTON_RIGHT) { saved_position = graph->get_local_mouse_position(); - Point2 gpos = InputFilter::get_singleton()->get_mouse_position(); + Point2 gpos = Input::get_singleton()->get_mouse_position(); _generic_search(script->get_instance_base_type(), gpos); } } @@ -2004,9 +2004,9 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da if (String(d["type"]) == "visual_script_variable_drag") { #ifdef OSX_ENABLED - bool use_set = InputFilter::get_singleton()->is_key_pressed(KEY_META); + bool use_set = Input::get_singleton()->is_key_pressed(KEY_META); #else - bool use_set = InputFilter::get_singleton()->is_key_pressed(KEY_CONTROL); + bool use_set = Input::get_singleton()->is_key_pressed(KEY_CONTROL); #endif Vector2 ofs = graph->get_scroll_ofs() + p_point; if (graph->is_using_snap()) { @@ -2199,9 +2199,9 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } #ifdef OSX_ENABLED - bool use_node = InputFilter::get_singleton()->is_key_pressed(KEY_META); + bool use_node = Input::get_singleton()->is_key_pressed(KEY_META); #else - bool use_node = InputFilter::get_singleton()->is_key_pressed(KEY_CONTROL); + bool use_node = Input::get_singleton()->is_key_pressed(KEY_CONTROL); #endif Array nodes = d["nodes"]; @@ -2263,7 +2263,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script); - if (!sn && !InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT)) { + if (!sn && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { EditorNode::get_singleton()->show_warning(vformat(TTR("Can't drop properties because script '%s' is not used in this scene.\nDrop holding 'Shift' to just copy the signature."), get_name())); return; } @@ -2283,12 +2283,12 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da ofs /= EDSCALE; #ifdef OSX_ENABLED - bool use_get = InputFilter::get_singleton()->is_key_pressed(KEY_META); + bool use_get = Input::get_singleton()->is_key_pressed(KEY_META); #else - bool use_get = InputFilter::get_singleton()->is_key_pressed(KEY_CONTROL); + bool use_get = Input::get_singleton()->is_key_pressed(KEY_CONTROL); #endif - if (!node || InputFilter::get_singleton()->is_key_pressed(KEY_SHIFT)) { + if (!node || Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { if (use_get) undo_redo->create_action(TTR("Add Getter Property")); diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp index 475893e86d..8a644c6860 100644 --- a/modules/visual_script/visual_script_flow_control.cpp +++ b/modules/visual_script/visual_script_flow_control.cpp @@ -934,6 +934,6 @@ void register_visual_script_flow_control_nodes() { VisualScriptLanguage::singleton->add_register_func("flow_control/iterator", create_node_generic<VisualScriptIterator>); VisualScriptLanguage::singleton->add_register_func("flow_control/sequence", create_node_generic<VisualScriptSequence>); VisualScriptLanguage::singleton->add_register_func("flow_control/switch", create_node_generic<VisualScriptSwitch>); - //VisualScriptLanguage::singleton->add_register_func("flow_control/input_filter", create_node_generic<VisualScriptInputFilter>); + //VisualScriptLanguage::singleton->add_register_func("flow_control/input", create_node_generic<VisualScriptInputFilter>); VisualScriptLanguage::singleton->add_register_func("flow_control/type_cast", create_node_generic<VisualScriptTypeCast>); } diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index c860e08943..52399d29d0 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -32,7 +32,7 @@ #include "core/engine.h" #include "core/global_constants.h" -#include "core/input/input_filter.h" +#include "core/input/input.h" #include "core/os/os.h" #include "core/project_settings.h" #include "scene/main/node.h" @@ -3870,16 +3870,16 @@ public: switch (mode) { case VisualScriptInputAction::MODE_PRESSED: { - *p_outputs[0] = InputFilter::get_singleton()->is_action_pressed(action); + *p_outputs[0] = Input::get_singleton()->is_action_pressed(action); } break; case VisualScriptInputAction::MODE_RELEASED: { - *p_outputs[0] = !InputFilter::get_singleton()->is_action_pressed(action); + *p_outputs[0] = !Input::get_singleton()->is_action_pressed(action); } break; case VisualScriptInputAction::MODE_JUST_PRESSED: { - *p_outputs[0] = InputFilter::get_singleton()->is_action_just_pressed(action); + *p_outputs[0] = Input::get_singleton()->is_action_just_pressed(action); } break; case VisualScriptInputAction::MODE_JUST_RELEASED: { - *p_outputs[0] = InputFilter::get_singleton()->is_action_just_released(action); + *p_outputs[0] = Input::get_singleton()->is_action_just_released(action); } break; } diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp index e293dfd50c..8c5525bed3 100644 --- a/modules/xatlas_unwrap/register_types.cpp +++ b/modules/xatlas_unwrap/register_types.cpp @@ -32,14 +32,91 @@ #include "core/error_macros.h" +#include "core/crypto/crypto_core.h" + #include "thirdparty/xatlas/xatlas.h" #include <stdio.h> #include <stdlib.h> -extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y); +extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache); + +bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uvs, int **r_vertices, int *r_vertex_count, int **r_indices, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache) { + + CryptoCore::MD5Context ctx; + ctx.start(); + + ctx.update((unsigned char *)&p_texel_size, sizeof(float)); + ctx.update((unsigned char *)p_indices, sizeof(int) * p_index_count); + ctx.update((unsigned char *)p_vertices, sizeof(float) * p_vertex_count); + ctx.update((unsigned char *)p_normals, sizeof(float) * p_vertex_count); + + unsigned char hash[16]; + ctx.finish(hash); + + bool cached = false; + unsigned int cache_idx = 0; + + if (r_used_cache && r_cache_size) { + //Check if hash is in cache data + + int *cache_data = r_cache_data; + int n_entries = cache_data[0]; + unsigned int r_idx = 1; + for (int i = 0; i < n_entries; ++i) { + if (memcmp(&cache_data[r_idx], hash, 16) == 0) { + cached = true; + cache_idx = r_idx; + break; + } + + r_idx += 4; // hash + r_idx += 2; // size hint + + int vertex_count = cache_data[r_idx]; + r_idx += 1; // vertex count + r_idx += vertex_count; // vertex + r_idx += vertex_count * 2; // uvs + + int index_count = cache_data[r_idx]; + r_idx += 1; // index count + r_idx += index_count; // indices + } + } -bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) { + if (r_used_cache && cached) { + int *cache_data = r_cache_data; + + // Return cache data pointer to the caller + r_cache_data = &cache_data[cache_idx]; + + cache_idx += 4; + + // Load size + *r_size_hint_x = cache_data[cache_idx]; + *r_size_hint_y = cache_data[cache_idx + 1]; + cache_idx += 2; + + // Load vertices + *r_vertex_count = cache_data[cache_idx]; + cache_idx++; + *r_vertices = &cache_data[cache_idx]; + cache_idx += *r_vertex_count; + + // Load UVs + *r_uvs = (float *)&cache_data[cache_idx]; + cache_idx += *r_vertex_count * 2; + + // Load indices + *r_index_count = cache_data[cache_idx]; + cache_idx++; + *r_indices = &cache_data[cache_idx]; + + // Return cache data size to the caller + r_cache_size = sizeof(int) * (4 + 2 + 1 + *r_vertex_count + (*r_vertex_count * 2) + 1 + *r_index_count); // hash + size hint + vertex_count + vertices + uvs + index_count + indices + r_used_cache = true; + return true; + } //set up input mesh xatlas::MeshDecl input_mesh; @@ -82,16 +159,16 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver const xatlas::Mesh &output = atlas->meshes[0]; - *r_vertex = (int *)malloc(sizeof(int) * output.vertexCount); - *r_uv = (float *)malloc(sizeof(float) * output.vertexCount * 2); - *r_index = (int *)malloc(sizeof(int) * output.indexCount); + *r_vertices = (int *)malloc(sizeof(int) * output.vertexCount); + *r_uvs = (float *)malloc(sizeof(float) * output.vertexCount * 2); + *r_indices = (int *)malloc(sizeof(int) * output.indexCount); float max_x = 0; float max_y = 0; for (uint32_t i = 0; i < output.vertexCount; i++) { - (*r_vertex)[i] = output.vertexArray[i].xref; - (*r_uv)[i * 2 + 0] = output.vertexArray[i].uv[0] / w; - (*r_uv)[i * 2 + 1] = output.vertexArray[i].uv[1] / h; + (*r_vertices)[i] = output.vertexArray[i].xref; + (*r_uvs)[i * 2 + 0] = output.vertexArray[i].uv[0] / w; + (*r_uvs)[i * 2 + 1] = output.vertexArray[i].uv[1] / h; max_x = MAX(max_x, output.vertexArray[i].uv[0]); max_y = MAX(max_y, output.vertexArray[i].uv[1]); } @@ -100,13 +177,54 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver *r_vertex_count = output.vertexCount; for (uint32_t i = 0; i < output.indexCount; i++) { - (*r_index)[i] = output.indexArray[i]; + (*r_indices)[i] = output.indexArray[i]; } *r_index_count = output.indexCount; xatlas::Destroy(atlas); - printf("Done\n"); + + if (r_used_cache) { + unsigned int new_cache_size = 4 + 2 + 1 + *r_vertex_count + (*r_vertex_count * 2) + 1 + *r_index_count; // hash + size hint + vertex_count + vertices + uvs + index_count + indices + new_cache_size *= sizeof(int); + int *new_cache_data = (int *)memalloc(new_cache_size); + unsigned int new_cache_idx = 0; + + // hash + memcpy(&new_cache_data[new_cache_idx], hash, 16); + new_cache_idx += 4; + + // size hint + new_cache_data[new_cache_idx] = *r_size_hint_x; + new_cache_data[new_cache_idx + 1] = *r_size_hint_y; + new_cache_idx += 2; + + // vertex count + new_cache_data[new_cache_idx] = *r_vertex_count; + new_cache_idx++; + + // vertices + memcpy(&new_cache_data[new_cache_idx], *r_vertices, sizeof(int) * *r_vertex_count); + new_cache_idx += *r_vertex_count; + + // uvs + memcpy(&new_cache_data[new_cache_idx], *r_uvs, sizeof(float) * *r_vertex_count * 2); + new_cache_idx += *r_vertex_count * 2; + + // index count + new_cache_data[new_cache_idx] = *r_index_count; + new_cache_idx++; + + // indices + memcpy(&new_cache_data[new_cache_idx], *r_indices, sizeof(int) * *r_index_count); + new_cache_idx += *r_index_count; + + // Return cache data to the caller + r_cache_data = new_cache_data; + r_cache_size = new_cache_size; + r_used_cache = false; + } + return true; } |