diff options
Diffstat (limited to 'modules')
41 files changed, 595 insertions, 408 deletions
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index 283e9b3bc8..f07fdef488 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -243,12 +243,12 @@ void GDNativeLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix); ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable); - ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton"); - ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "reloadable"), "set_reloadable", "is_reloadable"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reloadable"), "set_reloadable", "is_reloadable"); } GDNative::GDNative() { @@ -268,7 +268,7 @@ void GDNative::_bind_methods() { ClassDB::bind_method(D_METHOD("call_native", "calling_type", "procedure_name", "arguments"), &GDNative::call_native); - ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library"); } void GDNative::set_library(Ref<GDNativeLibrary> p_library) { diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 3e56275396..eb133502c4 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -72,11 +72,11 @@ void NativeScript::_bind_methods() { ClassDB::bind_method(D_METHOD("get_signal_documentation", "signal_name"), &NativeScript::get_signal_documentation); ClassDB::bind_method(D_METHOD("get_property_documentation", "path"), &NativeScript::get_property_documentation); - ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "class_name"), "set_class_name", "get_class_name"); - ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "class_name"), "set_class_name", "get_class_name"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library"); ADD_GROUP("Script Class", "script_class_"); - ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "script_class_name"), "set_script_class_name", "get_script_class_name"); - ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "script_class_icon_path", PROPERTY_HINT_FILE), "set_script_class_icon_path", "get_script_class_icon_path"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "script_class_name"), "set_script_class_name", "get_script_class_name"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "script_class_icon_path", PROPERTY_HINT_FILE), "set_script_class_icon_path", "get_script_class_icon_path"); ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &NativeScript::_new, MethodInfo(Variant::OBJECT, "new")); } diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index ef86ccae14..159085df34 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -469,8 +469,15 @@ bool GDScript::_update_exports() { for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { E->get()->set_build_failed(true); } + return false; } } else { + if (!valid) { + for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) { + E->get()->set_build_failed(true); + } + return false; + } } if (base_cache.is_valid()) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 1d26700fa0..09b3a5631f 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -4840,6 +4840,21 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { tokenizer->advance(); if (tokenizer->is_token_literal(0, true)) { enum_name = tokenizer->get_token_literal(); + + if (current_class->constant_expressions.has(enum_name)) { + _set_error("A constant named '" + String(enum_name) + "' already exists in this class (at line: " + + itos(current_class->constant_expressions[enum_name].expression->line) + ")."); + return; + } + + for (int i = 0; i < current_class->variables.size(); i++) { + if (current_class->variables[i].identifier == enum_name) { + _set_error("A variable named '" + String(enum_name) + "' already exists in this class (at line: " + + itos(current_class->variables[i].line) + ")."); + return; + } + } + tokenizer->advance(); } if (tokenizer->get_token() != GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) { @@ -4866,26 +4881,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } else { // tokenizer->is_token_literal(0, true) - ClassNode::Constant constant; - StringName const_id = tokenizer->get_token_literal(); - if (current_class->constant_expressions.has(const_id)) { - _set_error("A constant named '" + String(const_id) + "' already exists in this class (at line: " + - itos(current_class->constant_expressions[const_id].expression->line) + ")."); - return; - } - - for (int i = 0; i < current_class->variables.size(); i++) { - if (current_class->variables[i].identifier == const_id) { - _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " + - itos(current_class->variables[i].line) + ")."); - return; - } - } - tokenizer->advance(); + ConstantNode *enum_value_expr; + if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) { tokenizer->advance(); @@ -4902,23 +4903,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - ConstantNode *subexpr_const = static_cast<ConstantNode *>(subexpr); + enum_value_expr = static_cast<ConstantNode *>(subexpr); - if (subexpr_const->value.get_type() != Variant::INT) { + if (enum_value_expr->value.get_type() != Variant::INT) { _set_error("Expected an int value for enum"); return; } - last_assign = subexpr_const->value; - - constant.expression = subexpr_const; + last_assign = enum_value_expr->value; } else { last_assign = last_assign + 1; - ConstantNode *cn = alloc_node<ConstantNode>(); - cn->value = last_assign; - cn->datatype = _type_from_variant(cn->value); - constant.expression = cn; + enum_value_expr = alloc_node<ConstantNode>(); + enum_value_expr->value = last_assign; + enum_value_expr->datatype = _type_from_variant(enum_value_expr->value); } if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { @@ -4926,14 +4924,29 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } if (enum_name != "") { - const ConstantNode *cn = static_cast<const ConstantNode *>(constant.expression); - enum_dict[const_id] = cn->value; - } + enum_dict[const_id] = enum_value_expr->value; + } else { + if (current_class->constant_expressions.has(const_id)) { + _set_error("A constant named '" + String(const_id) + "' already exists in this class (at line: " + + itos(current_class->constant_expressions[const_id].expression->line) + ")."); + return; + } - constant.type.has_type = true; - constant.type.kind = DataType::BUILTIN; - constant.type.builtin_type = Variant::INT; - p_class->constant_expressions.insert(const_id, constant); + for (int i = 0; i < current_class->variables.size(); i++) { + if (current_class->variables[i].identifier == const_id) { + _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " + + itos(current_class->variables[i].line) + ")."); + return; + } + } + + ClassNode::Constant constant; + constant.type.has_type = true; + constant.type.kind = DataType::BUILTIN; + constant.type.builtin_type = Variant::INT; + constant.expression = enum_value_expr; + p_class->constant_expressions.insert(const_id, constant); + } } } diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index a8fdf8cf1f..274a2f0249 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -892,7 +892,7 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("make_baked_meshes", "gen_lightmap_uv", "lightmap_uv_texel_size"), &GridMap::make_baked_meshes, DEFVAL(false), DEFVAL(0.1)); #ifndef DISABLE_DEPRECATED - ADD_PROPERTYNO(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary", 0), "set_theme", "get_theme"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary", 0), "set_theme", "get_theme"); #endif // DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_library", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary"), "set_mesh_library", "get_mesh_library"); diff --git a/modules/mono/config.py b/modules/mono/config.py index 8427103ee7..189699cca8 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -265,11 +265,7 @@ def make_template_dir(env, mono_root): template_dir_name = '' - if platform == 'windows': - template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) - elif platform == 'osx': - template_dir_name = 'data.mono.%s.%s' % (platform, target) - elif platform == 'x11': + if platform in ['windows', 'osx', 'x11']: template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) else: assert False diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index a048baf5d7..700e518cfc 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -2770,7 +2770,8 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r "Compile", ProjectSettings::get_singleton()->globalize_path(p_path)); } else { - ERR_PRINTS("Cannot add " + p_path + " to the C# project because it could not be created."); + ERR_PRINTS("Failed to create C# project"); + ERR_PRINTS("Cannot add " + p_path + " to the C# project"); } } #endif diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs index 574b711b29..2ce7837a27 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs @@ -6,22 +6,24 @@ namespace GodotSharpTools.Project { public static class ProjectGenerator { + public const string CoreApiProjectName = "GodotSharp"; + public const string EditorApiProjectName = "GodotSharpEditor"; const string CoreApiProjectGuid = "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}"; const string EditorApiProjectGuid = "{8FBEC238-D944-4074-8548-B3B524305905}"; public static string GenCoreApiProject(string dir, string[] compileItems) { - string path = Path.Combine(dir, CoreApiProject + ".csproj"); + string path = Path.Combine(dir, CoreApiProjectName + ".csproj"); ProjectPropertyGroupElement mainGroup; - var root = CreateLibraryProject(CoreApiProject, out mainGroup); + var root = CreateLibraryProject(CoreApiProjectName, out mainGroup); mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); mainGroup.SetProperty("ProjectGuid", CoreApiProjectGuid); - GenAssemblyInfoFile(root, dir, CoreApiProject, - new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProject + "\")]" }, + GenAssemblyInfoFile(root, dir, CoreApiProjectName, + new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProjectName + "\")]" }, new string[] { "System.Runtime.CompilerServices" }); foreach (var item in compileItems) @@ -31,34 +33,33 @@ namespace GodotSharpTools.Project root.Save(path); - return root.GetGuid().ToString().ToUpper(); + return CoreApiProjectGuid; } - public static string GenEditorApiProject(string dir, string coreApiHintPath, string[] compileItems) + public static string GenEditorApiProject(string dir, string coreApiProjPath, string[] compileItems) { - string path = Path.Combine(dir, EditorApiProject + ".csproj"); + string path = Path.Combine(dir, EditorApiProjectName + ".csproj"); ProjectPropertyGroupElement mainGroup; - var root = CreateLibraryProject(EditorApiProject, out mainGroup); + var root = CreateLibraryProject(EditorApiProjectName, out mainGroup); mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); mainGroup.SetProperty("ProjectGuid", EditorApiProjectGuid); - GenAssemblyInfoFile(root, dir, EditorApiProject); + GenAssemblyInfoFile(root, dir, EditorApiProjectName); foreach (var item in compileItems) { root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\")); } - var coreApiRef = root.AddItem("Reference", CoreApiProject); - coreApiRef.AddMetadata("HintPath", coreApiHintPath); + var coreApiRef = root.AddItem("ProjectReference", coreApiProjPath.Replace("/", "\\")); coreApiRef.AddMetadata("Private", "False"); root.Save(path); - return root.GetGuid().ToString().ToUpper(); + return EditorApiProjectGuid; } public static string GenGameProject(string dir, string name, string[] compileItems) @@ -82,13 +83,13 @@ namespace GodotSharpTools.Project toolsGroup.AddProperty("WarningLevel", "4"); toolsGroup.AddProperty("ConsolePause", "false"); - var coreApiRef = root.AddItem("Reference", CoreApiProject); - coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProject + ".dll")); + var coreApiRef = root.AddItem("Reference", CoreApiProjectName); + coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProjectName + ".dll")); coreApiRef.AddMetadata("Private", "False"); - var editorApiRef = root.AddItem("Reference", EditorApiProject); + var editorApiRef = root.AddItem("Reference", EditorApiProjectName); editorApiRef.Condition = " '$(Configuration)' == 'Tools' "; - editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProject + ".dll")); + editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProjectName + ".dll")); editorApiRef.AddMetadata("Private", "False"); GenAssemblyInfoFile(root, dir, name); @@ -187,9 +188,6 @@ namespace GodotSharpTools.Project } } - public const string CoreApiProject = "GodotSharp"; - public const string EditorApiProject = "GodotSharpEditor"; - private const string assemblyInfoTemplate = @"using System.Reflection;{0} diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 710682d3aa..166b3e1324 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -47,7 +47,6 @@ #include "../utils/path_utils.h" #include "../utils/string_utils.h" #include "csharp_project.h" -#include "net_solution.h" #define CS_INDENT " " // 4 whitespaces @@ -401,32 +400,29 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) { p_output.push_back(CLOSE_BLOCK); // end of namespace } -Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bool p_verbose_output) { +Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) { verbose_output = p_verbose_output; + String proj_dir = p_solution_dir.plus_file(CORE_API_ASSEMBLY_NAME); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); - if (!DirAccess::exists(p_output_dir)) { - Error err = da->make_dir_recursive(p_output_dir); + if (!DirAccess::exists(proj_dir)) { + Error err = da->make_dir_recursive(proj_dir); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); } - da->change_dir(p_output_dir); + da->change_dir(proj_dir); da->make_dir("Core"); da->make_dir("ObjectType"); - String core_dir = path_join(p_output_dir, "Core"); - String obj_type_dir = path_join(p_output_dir, "ObjectType"); + String core_dir = path_join(proj_dir, "Core"); + String obj_type_dir = path_join(proj_dir, "ObjectType"); Vector<String> compile_items; - NETSolution solution(API_ASSEMBLY_NAME); - - if (!solution.set_path(p_output_dir)) - return ERR_FILE_NOT_FOUND; - // Generate source file for global scope constants and enums { List<String> constants_source; @@ -530,15 +526,15 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo compile_items.push_back(internal_methods_file); - String guid = CSharpProject::generate_core_api_project(p_output_dir, compile_items); + String guid = CSharpProject::generate_core_api_project(proj_dir, compile_items); - solution.add_new_project(API_ASSEMBLY_NAME, guid); + DotNetSolution::ProjectInfo proj_info; + proj_info.guid = guid; + proj_info.relpath = String(CORE_API_ASSEMBLY_NAME).plus_file(CORE_API_ASSEMBLY_NAME ".csproj"); + proj_info.configs.push_back("Debug"); + proj_info.configs.push_back("Release"); - Error sln_error = solution.save(); - if (sln_error != OK) { - ERR_PRINT("Could not to save .NET solution."); - return sln_error; - } + r_solution.add_new_project(CORE_API_ASSEMBLY_NAME, proj_info); if (verbose_output) OS::get_singleton()->print("The solution and C# project for the Core API was generated successfully\n"); @@ -546,32 +542,29 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo return OK; } -Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output) { +Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) { verbose_output = p_verbose_output; + String proj_dir = p_solution_dir.plus_file(EDITOR_API_ASSEMBLY_NAME); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); - if (!DirAccess::exists(p_output_dir)) { - Error err = da->make_dir_recursive(p_output_dir); + if (!DirAccess::exists(proj_dir)) { + Error err = da->make_dir_recursive(proj_dir); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); } - da->change_dir(p_output_dir); + da->change_dir(proj_dir); da->make_dir("Core"); da->make_dir("ObjectType"); - String core_dir = path_join(p_output_dir, "Core"); - String obj_type_dir = path_join(p_output_dir, "ObjectType"); + String core_dir = path_join(proj_dir, "Core"); + String obj_type_dir = path_join(proj_dir, "ObjectType"); Vector<String> compile_items; - NETSolution solution(EDITOR_API_ASSEMBLY_NAME); - - if (!solution.set_path(p_output_dir)) - return ERR_FILE_NOT_FOUND; - for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) { const TypeInterface &itype = E.get(); @@ -632,19 +625,57 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, compile_items.push_back(internal_methods_file); - String guid = CSharpProject::generate_editor_api_project(p_output_dir, p_core_dll_path, compile_items); + String guid = CSharpProject::generate_editor_api_project(proj_dir, "../" CORE_API_ASSEMBLY_NAME "/" CORE_API_ASSEMBLY_NAME ".csproj", compile_items); + + DotNetSolution::ProjectInfo proj_info; + proj_info.guid = guid; + proj_info.relpath = String(EDITOR_API_ASSEMBLY_NAME).plus_file(EDITOR_API_ASSEMBLY_NAME ".csproj"); + proj_info.configs.push_back("Debug"); + proj_info.configs.push_back("Release"); + + r_solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, proj_info); + + if (verbose_output) + OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n"); + + return OK; +} - solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, guid); +Error BindingsGenerator::generate_cs_api(const String &p_output_dir, bool p_verbose_output) { + + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); + + if (!DirAccess::exists(p_output_dir)) { + Error err = da->make_dir_recursive(p_output_dir); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + } + + DotNetSolution solution(API_SOLUTION_NAME); + + if (!solution.set_path(p_output_dir)) + return ERR_FILE_NOT_FOUND; + + Error proj_err; + + proj_err = generate_cs_core_project(p_output_dir, solution, p_verbose_output); + if (proj_err != OK) { + ERR_PRINT("Generation of the Core API C# project failed"); + return proj_err; + } + + proj_err = generate_cs_editor_project(p_output_dir, solution, p_verbose_output); + if (proj_err != OK) { + ERR_PRINT("Generation of the Editor API C# project failed"); + return proj_err; + } Error sln_error = solution.save(); if (sln_error != OK) { - ERR_PRINT("Could not to save .NET solution."); + ERR_PRINT("Failed to save API solution"); return sln_error; } - if (verbose_output) - OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n"); - return OK; } @@ -2368,12 +2399,11 @@ void BindingsGenerator::initialize() { void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) { - const int NUM_OPTIONS = 3; + const int NUM_OPTIONS = 2; int options_left = NUM_OPTIONS; String mono_glue_option = "--generate-mono-glue"; - String cs_core_api_option = "--generate-cs-core-api"; - String cs_editor_api_option = "--generate-cs-editor-api"; + String cs_api_option = "--generate-cs-api"; verbose_output = true; @@ -2387,42 +2417,24 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) if (path_elem) { if (get_singleton()->generate_glue(path_elem->get()) != OK) - ERR_PRINT("Mono glue generation failed"); + ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue"); elem = elem->next(); } else { - ERR_PRINTS("--generate-mono-glue: No output directory specified"); + ERR_PRINTS(mono_glue_option + ": No output directory specified"); } --options_left; - } else if (elem->get() == cs_core_api_option) { + } else if (elem->get() == cs_api_option) { const List<String>::Element *path_elem = elem->next(); if (path_elem) { - if (get_singleton()->generate_cs_core_project(path_elem->get()) != OK) - ERR_PRINT("Generation of solution and C# project for the Core API failed"); + if (get_singleton()->generate_cs_api(path_elem->get()) != OK) + ERR_PRINTS(cs_api_option + ": Failed to generate the C# API"); elem = elem->next(); } else { - ERR_PRINTS(cs_core_api_option + ": No output directory specified"); - } - - --options_left; - - } else if (elem->get() == cs_editor_api_option) { - - const List<String>::Element *path_elem = elem->next(); - - if (path_elem) { - if (path_elem->next()) { - if (get_singleton()->generate_cs_editor_project(path_elem->get(), path_elem->next()->get()) != OK) - ERR_PRINT("Generation of solution and C# project for the Editor API failed"); - elem = path_elem->next(); - } else { - ERR_PRINTS(cs_editor_api_option + ": No hint path for the Core API dll specified"); - } - } else { - ERR_PRINTS(cs_editor_api_option + ": No output directory specified"); + ERR_PRINTS(cs_api_option + ": No output directory specified"); } --options_left; @@ -2434,7 +2446,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) verbose_output = false; if (options_left != NUM_OPTIONS) - exit(0); + ::exit(0); } #endif diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 38cf99c294..91c474c4f0 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -32,6 +32,7 @@ #define BINDINGS_GENERATOR_H #include "core/class_db.h" +#include "dotnet_solution.h" #include "editor/doc/doc_data.h" #include "editor/editor_help.h" @@ -556,8 +557,9 @@ class BindingsGenerator { static BindingsGenerator *singleton; public: - Error generate_cs_core_project(const String &p_output_dir, bool p_verbose_output = true); - Error generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output = true); + Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true); + Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true); + Error generate_cs_api(const String &p_output_dir, bool p_verbose_output = true); Error generate_glue(const String &p_output_dir); static uint32_t get_version(); diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index ab96356d6d..416108603b 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -31,6 +31,8 @@ #include "csharp_project.h" #include "core/io/json.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" #include "core/os/os.h" #include "core/project_settings.h" @@ -62,16 +64,16 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String(); } -String generate_editor_api_project(const String &p_dir, const String &p_core_dll_path, const Vector<String> &p_files) { +String generate_editor_api_project(const String &p_dir, const String &p_core_proj_path, const Vector<String> &p_files) { _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator"); Variant dir = p_dir; - Variant core_dll_path = p_core_dll_path; + Variant core_proj_path = p_core_proj_path; Variant compile_items = p_files; - const Variant *args[3] = { &dir, &core_dll_path, &compile_items }; + const Variant *args[3] = { &dir, &core_proj_path, &compile_items }; MonoException *exc = NULL; MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc); @@ -106,6 +108,9 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) { + if (!GLOBAL_DEF("mono/project/auto_update_project", true)) + return; + _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils"); @@ -127,6 +132,14 @@ Error generate_scripts_metadata(const String &p_project_path, const String &p_ou _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + if (FileAccess::exists(p_output_path)) { + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Error rm_err = da->remove(p_output_path); + + ERR_EXPLAIN("Failed to remove old scripts metadata file"); + ERR_FAIL_COND_V(rm_err != OK, rm_err); + } + GDMonoClass *project_utils = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils"); void *args[2] = { diff --git a/modules/mono/editor/net_solution.cpp b/modules/mono/editor/dotnet_solution.cpp index a000debe52..ab92e2e378 100644 --- a/modules/mono/editor/net_solution.cpp +++ b/modules/mono/editor/dotnet_solution.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* net_solution.cpp */ +/* dotnet_solution.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "net_solution.h" +#include "dotnet_solution.h" #include "core/os/dir_access.h" #include "core/os/file_access.h" @@ -58,27 +58,26 @@ "\t\t{%0}.%1|Any CPU.ActiveCfg = %1|Any CPU\n" \ "\t\t{%0}.%1|Any CPU.Build.0 = %1|Any CPU" -void NETSolution::add_new_project(const String &p_name, const String &p_guid, const Vector<String> &p_extra_configs) { - if (projects.has(p_name)) - WARN_PRINT("Overriding existing project."); - - ProjectInfo procinfo; - procinfo.guid = p_guid; +void DotNetSolution::add_new_project(const String &p_name, const ProjectInfo &p_project_info) { + projects[p_name] = p_project_info; +} - procinfo.configs.push_back("Debug"); - procinfo.configs.push_back("Release"); +bool DotNetSolution::has_project(const String &p_name) const { + return projects.find(p_name) != NULL; +} - for (int i = 0; i < p_extra_configs.size(); i++) { - procinfo.configs.push_back(p_extra_configs[i]); - } +const DotNetSolution::ProjectInfo &DotNetSolution::get_project_info(const String &p_name) const { + return projects[p_name]; +} - projects[p_name] = procinfo; +bool DotNetSolution::remove_project(const String &p_name) { + return projects.erase(p_name); } -Error NETSolution::save() { +Error DotNetSolution::save() { bool dir_exists = DirAccess::exists(path); ERR_EXPLAIN("The directory does not exist."); - ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH); + ERR_FAIL_COND_V(!dir_exists, ERR_FILE_NOT_FOUND); String projs_decl; String sln_platform_cfg; @@ -86,34 +85,40 @@ Error NETSolution::save() { for (Map<String, ProjectInfo>::Element *E = projects.front(); E; E = E->next()) { const String &name = E->key(); - const ProjectInfo &procinfo = E->value(); + const ProjectInfo &proj_info = E->value(); - projs_decl += sformat(PROJECT_DECLARATION, name, name + ".csproj", procinfo.guid); + bool is_front = E == projects.front(); - for (int i = 0; i < procinfo.configs.size(); i++) { - const String &config = procinfo.configs[i]; + if (!is_front) + projs_decl += "\n"; - if (i != 0) { + projs_decl += sformat(PROJECT_DECLARATION, name, proj_info.relpath.replace("/", "\\"), proj_info.guid); + + for (int i = 0; i < proj_info.configs.size(); i++) { + const String &config = proj_info.configs[i]; + + if (i != 0 || !is_front) { sln_platform_cfg += "\n"; proj_platform_cfg += "\n"; } sln_platform_cfg += sformat(SOLUTION_PLATFORMS_CONFIG, config); - proj_platform_cfg += sformat(PROJECT_PLATFORMS_CONFIG, procinfo.guid, config); + proj_platform_cfg += sformat(PROJECT_PLATFORMS_CONFIG, proj_info.guid, config); } } String content = sformat(SOLUTION_TEMPLATE, projs_decl, sln_platform_cfg, proj_platform_cfg); - FileAccessRef file = FileAccess::open(path_join(path, name + ".sln"), FileAccess::WRITE); - ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE); + FileAccess *file = FileAccess::open(path_join(path, name + ".sln"), FileAccess::WRITE); + ERR_FAIL_NULL_V(file, ERR_FILE_CANT_WRITE); file->store_string(content); file->close(); + memdelete(file); return OK; } -bool NETSolution::set_path(const String &p_existing_path) { +bool DotNetSolution::set_path(const String &p_existing_path) { if (p_existing_path.is_abs_path()) { path = p_existing_path; } else { @@ -126,6 +131,10 @@ bool NETSolution::set_path(const String &p_existing_path) { return true; } -NETSolution::NETSolution(const String &p_name) { +String DotNetSolution::get_path() { + return path; +} + +DotNetSolution::DotNetSolution(const String &p_name) { name = p_name; } diff --git a/modules/mono/editor/net_solution.h b/modules/mono/editor/dotnet_solution.h index bdff24af0b..629605ad18 100644 --- a/modules/mono/editor/net_solution.h +++ b/modules/mono/editor/dotnet_solution.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* net_solution.h */ +/* dotnet_solution.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -34,23 +34,28 @@ #include "core/map.h" #include "core/ustring.h" -struct NETSolution { +struct DotNetSolution { String name; - void add_new_project(const String &p_name, const String &p_guid, const Vector<String> &p_extra_configs = Vector<String>()); + struct ProjectInfo { + String guid; + String relpath; // Must be relative to the solution directory + Vector<String> configs; + }; + + void add_new_project(const String &p_name, const ProjectInfo &p_project_info); + bool has_project(const String &p_name) const; + const ProjectInfo &get_project_info(const String &p_name) const; + bool remove_project(const String &p_name); Error save(); bool set_path(const String &p_existing_path); + String get_path(); - NETSolution(const String &p_name); + DotNetSolution(const String &p_name); private: - struct ProjectInfo { - String guid; - Vector<String> configs; - }; - String path; Map<String, ProjectInfo> projects; }; diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 6216213716..0f12fc5243 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -227,20 +227,24 @@ void GodotSharpBuilds::show_build_error_dialog(const String &p_message) { MonoBottomPanel::get_singleton()->show_build_tab(); } -bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config) { +bool GodotSharpBuilds::build_api_sln(const String &p_api_sln_dir, const String &p_config) { - String api_sln_file = p_api_sln_dir.plus_file(p_name + ".sln"); - String api_assembly_dir = p_api_sln_dir.plus_file("bin").plus_file(p_config); - String api_assembly_file = api_assembly_dir.plus_file(p_name + ".dll"); + String api_sln_file = p_api_sln_dir.plus_file(API_SOLUTION_NAME ".sln"); - if (!FileAccess::exists(api_assembly_file)) { + String core_api_assembly_dir = p_api_sln_dir.plus_file(CORE_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config); + String core_api_assembly_file = core_api_assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); + + String editor_api_assembly_dir = p_api_sln_dir.plus_file(EDITOR_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config); + String editor_api_assembly_file = editor_api_assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); + + if (!FileAccess::exists(core_api_assembly_file) || !FileAccess::exists(editor_api_assembly_file)) { MonoBuildInfo api_build_info(api_sln_file, p_config); // TODO Replace this global NoWarn with '#pragma warning' directives on generated files, // once we start to actively document manually maintained C# classes api_build_info.custom_props.push_back("NoWarn=1591"); // Ignore missing documentation warnings if (!GodotSharpBuilds::get_singleton()->build(api_build_info)) { - show_build_error_dialog("Failed to build " + p_name + " solution."); + show_build_error_dialog("Failed to build " API_SOLUTION_NAME " solution."); return false; } } @@ -302,9 +306,9 @@ String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) { "_" + String::num_uint64(CS_GLUE_VERSION); } -bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) { +bool GodotSharpBuilds::make_api_assembly(APIAssembly::Type p_api_type) { - String api_name = p_api_type == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; + String api_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; String editor_prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir(); String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir(); @@ -317,55 +321,35 @@ bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) { String api_build_config = "Release"; - EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 3); + EditorProgress pr("mono_build_release_" API_SOLUTION_NAME, "Building " API_SOLUTION_NAME " solution...", 3); - pr.step("Generating " + api_name + " solution", 0); + pr.step("Generating " API_SOLUTION_NAME " solution", 0); - String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir() - .plus_file(_api_folder_name(APIAssembly::API_CORE)) - .plus_file(API_ASSEMBLY_NAME); - String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir() - .plus_file(_api_folder_name(APIAssembly::API_EDITOR)) - .plus_file(EDITOR_API_ASSEMBLY_NAME); + String api_sln_dir = GodotSharpDirs::get_mono_solutions_dir() + .plus_file(_api_folder_name(APIAssembly::API_CORE)); - String api_sln_dir = p_api_type == APIAssembly::API_CORE ? core_api_sln_dir : editor_api_sln_dir; - String api_sln_file = api_sln_dir.plus_file(api_name + ".sln"); + String api_sln_file = api_sln_dir.plus_file(API_SOLUTION_NAME ".sln"); if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) { - String core_api_assembly; - - if (p_api_type == APIAssembly::API_EDITOR) { - core_api_assembly = core_api_sln_dir.plus_file("bin") - .plus_file(api_build_config) - .plus_file(API_ASSEMBLY_NAME ".dll"); - } - -#ifndef DEBUG_METHODS_ENABLED -#error "How am I supposed to generate the bindings?" -#endif - BindingsGenerator *gen = BindingsGenerator::get_singleton(); bool gen_verbose = OS::get_singleton()->is_stdout_verbose(); - Error err = p_api_type == APIAssembly::API_CORE ? - gen->generate_cs_core_project(api_sln_dir, gen_verbose) : - gen->generate_cs_editor_project(api_sln_dir, core_api_assembly, gen_verbose); - + Error err = gen->generate_cs_api(api_sln_dir, gen_verbose); if (err != OK) { - show_build_error_dialog("Failed to generate " + api_name + " solution. Error: " + itos(err)); + show_build_error_dialog("Failed to generate " API_SOLUTION_NAME " solution. Error: " + itos(err)); return false; } } - pr.step("Building " + api_name + " solution", 1); + pr.step("Building " API_SOLUTION_NAME " solution", 1); - if (!GodotSharpBuilds::build_api_sln(api_name, api_sln_dir, api_build_config)) + if (!GodotSharpBuilds::build_api_sln(api_sln_dir, api_build_config)) return false; pr.step("Copying " + api_name + " assembly", 2); // Copy the built assembly to the assemblies directory - String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config); + String api_assembly_dir = api_sln_dir.plus_file(api_name).plus_file("bin").plus_file(api_build_config); if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type)) return false; @@ -377,10 +361,10 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) { if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path())) return true; // No solution to build - if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE)) + if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE)) return false; - if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR)) + if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR)) return false; EditorProgress pr("mono_project_debug_build", "Building project solution...", 1); @@ -403,11 +387,13 @@ bool GodotSharpBuilds::editor_build_callback() { Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor); ERR_FAIL_COND_V(metadata_err != OK, false); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player); + if (FileAccess::exists(scripts_metadata_path_editor)) { + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player); - ERR_EXPLAIN("Failed to copy scripts metadata file"); - ERR_FAIL_COND_V(copy_err != OK, false); + ERR_EXPLAIN("Failed to copy scripts metadata file"); + ERR_FAIL_COND_V(copy_err != OK, false); + } return build_project_blocking("Tools"); } diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h index c6dc6b6236..7f38b0aa49 100644 --- a/modules/mono/editor/godotsharp_builds.h +++ b/modules/mono/editor/godotsharp_builds.h @@ -84,10 +84,10 @@ public: bool build(const MonoBuildInfo &p_build_info); bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL); - static bool build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config); + static bool build_api_sln(const String &p_api_sln_dir, const String &p_config); static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type); - static bool make_api_sln(APIAssembly::Type p_api_type); + static bool make_api_assembly(APIAssembly::Type p_api_type); static bool build_project_blocking(const String &p_config); diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index 9df4e10266..f27511ad5e 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -42,8 +42,8 @@ #include "../utils/path_utils.h" #include "bindings_generator.h" #include "csharp_project.h" +#include "dotnet_solution.h" #include "godotsharp_export.h" -#include "net_solution.h" #ifdef OSX_ENABLED #include "../utils/osx_utils.h" @@ -71,17 +71,21 @@ bool GodotSharpEditor::_create_project_solution() { if (guid.length()) { - NETSolution solution(name); + DotNetSolution solution(name); if (!solution.set_path(path)) { show_error_dialog(TTR("Failed to create solution.")); return false; } - Vector<String> extra_configs; - extra_configs.push_back("Tools"); + DotNetSolution::ProjectInfo proj_info; + proj_info.guid = guid; + proj_info.relpath = name + ".csproj"; + proj_info.configs.push_back("Debug"); + proj_info.configs.push_back("Release"); + proj_info.configs.push_back("Tools"); - solution.add_new_project(name, guid, extra_configs); + solution.add_new_project(name, proj_info); Error sln_error = solution.save(); @@ -90,10 +94,10 @@ bool GodotSharpEditor::_create_project_solution() { return false; } - if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE)) + if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE)) return false; - if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR)) + if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR)) return false; pr.step(TTR("Done")); @@ -122,15 +126,15 @@ void GodotSharpEditor::_make_api_solutions_if_needed_impl() { // If the project has a solution and C# project make sure the API assemblies are present and up to date String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir(); - if (!FileAccess::exists(res_assemblies_dir.plus_file(API_ASSEMBLY_NAME ".dll")) || + if (!FileAccess::exists(res_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll")) || GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) { - if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE)) + if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE)) return; } if (!FileAccess::exists(res_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll")) || GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) { - if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR)) + if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR)) return; // Redundant? I don't think so } } diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index 509ec961fb..34c710320a 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -117,7 +117,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug GDMonoAssembly *scripts_assembly = NULL; bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name, - project_dll_src_dir, &scripts_assembly, /* refonly: */ true); + project_dll_src_path, &scripts_assembly, /* refonly: */ true); ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name); ERR_FAIL_COND(!load_success); diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h index 39d608de9f..5a6a2d1742 100644 --- a/modules/mono/godotsharp_defs.h +++ b/modules/mono/godotsharp_defs.h @@ -36,7 +36,8 @@ #define BINDINGS_GLOBAL_SCOPE_CLASS "GD" #define BINDINGS_PTR_FIELD "ptr" #define BINDINGS_NATIVE_NAME_FIELD "nativeName" -#define API_ASSEMBLY_NAME "GodotSharp" +#define API_SOLUTION_NAME "GodotSharp" +#define CORE_API_ASSEMBLY_NAME "GodotSharp" #define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor" #define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools" diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index c3feafee28..a80155bd89 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -496,12 +496,12 @@ bool GDMono::_load_core_api_assembly() { } #endif - String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll"); + String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll"); if (!FileAccess::exists(assembly_path)) return false; - bool success = load_assembly_from(API_ASSEMBLY_NAME, + bool success = load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly); @@ -635,7 +635,7 @@ void GDMono::metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, String assembly_path = GodotSharpDirs::get_res_assemblies_dir() .plus_file(p_api_type == APIAssembly::API_CORE ? - API_ASSEMBLY_NAME ".dll" : + CORE_API_ASSEMBLY_NAME ".dll" : EDITOR_API_ASSEMBLY_NAME ".dll"); ERR_FAIL_COND(!FileAccess::exists(assembly_path)); @@ -666,7 +666,7 @@ bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type) String assembly_path = GodotSharpDirs::get_res_assemblies_dir() .plus_file(p_api_type == APIAssembly::API_CORE ? - API_ASSEMBLY_NAME ".dll" : + CORE_API_ASSEMBLY_NAME ".dll" : EDITOR_API_ASSEMBLY_NAME ".dll"); if (!FileAccess::exists(assembly_path)) diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index b97a24b09c..fe2c09799c 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -631,36 +631,36 @@ void set_pending_exception(MonoException *p_exc) { _THREAD_LOCAL_(int) current_invoke_count = 0; -MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc) { +MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) { GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)p_exc); + MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)r_exc); GD_MONO_END_RUNTIME_INVOKE; return ret; } -MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc) { +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc) { GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)p_exc); + MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)r_exc); GD_MONO_END_RUNTIME_INVOKE; return ret; } -MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc) { +MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) { GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)p_exc); + MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc); GD_MONO_END_RUNTIME_INVOKE; return ret; } -void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) { +void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) { GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)p_exc); + mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)r_exc); GD_MONO_END_RUNTIME_INVOKE; } -MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) { +MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) { GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)p_exc); + MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)r_exc); GD_MONO_END_RUNTIME_INVOKE; return ret; } diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index ec3a57eb46..f00680ff03 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -233,13 +233,13 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() { return current_invoke_count; } -MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc); -MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc); +MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc); +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc); -MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc); +MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc); -void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc); -MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc); +void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc); +MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc); uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error); diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp index ea942a9a8e..e663ee3c4a 100644 --- a/modules/mono/utils/path_utils.cpp +++ b/modules/mono/utils/path_utils.cpp @@ -88,7 +88,7 @@ void fix_path(const String &p_path, String &r_out) { bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path) { #ifdef WINDOWS_ENABLED CharType ret[_MAX_PATH]; - if (_wfullpath(ret, p_existing_path.c_str(), _MAX_PATH)) { + if (::_wfullpath(ret, p_existing_path.c_str(), _MAX_PATH)) { String abspath = String(ret).replace("\\", "/"); int pos = abspath.find(":/"); if (pos != -1) { @@ -99,10 +99,12 @@ bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path) { return true; } #else - char ret[PATH_MAX]; - if (realpath(p_existing_path.utf8().get_data(), ret)) { + char *resolved_path = ::realpath(p_existing_path.utf8().get_data(), NULL); + if (resolved_path) { String retstr; - if (!retstr.parse_utf8(ret)) { + bool success = !retstr.parse_utf8(resolved_path); + ::free(resolved_path); + if (success) { r_abs_path = retstr; return true; } diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 8255ed7116..82a577790e 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -31,6 +31,7 @@ #include "emws_client.h" #include "core/io/ip.h" +#include "core/project_settings.h" #include "emscripten.h" extern "C" { @@ -43,8 +44,9 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_connect(void *obj, char *proto) { EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_data_size, int p_is_string) { EMWSClient *client = static_cast<EMWSClient *>(obj); - static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1); - client->_on_peer_packet(); + Error err = static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1); + if (err == OK) + client->_on_peer_packet(); } EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) { @@ -159,7 +161,7 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, }, _js_id, str.utf8().get_data(), proto_string.utf8().get_data()); /* clang-format on */ - static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock); + static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock, _in_buf_size, _in_pkt_size); return OK; }; @@ -198,7 +200,13 @@ uint16_t EMWSClient::get_connected_port() const { return 1025; }; +int EMWSClient::get_max_packet_size() const { + return (1 << _in_buf_size) - PROTO_SIZE; +} + EMWSClient::EMWSClient() { + _in_buf_size = GLOBAL_GET(WSC_IN_BUF); + _in_pkt_size = GLOBAL_GET(WSC_IN_PKT); _is_connecting = false; _peer = Ref<EMWSPeer>(memnew(EMWSPeer)); /* clang-format off */ diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index b20633baff..a21090a1a3 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -41,6 +41,8 @@ class EMWSClient : public WebSocketClient { GDCIIMPL(EMWSClient, WebSocketClient); private: + int _in_buf_size; + int _in_pkt_size; int _js_id; public: @@ -52,6 +54,7 @@ public: IP_Address get_connected_host() const; uint16_t get_connected_port() const; virtual ConnectionStatus get_connection_status() const; + int get_max_packet_size() const; virtual void poll(); EMWSClient(); ~EMWSClient(); diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index 68f41165eb..bb97934824 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -32,11 +32,11 @@ #include "emws_peer.h" #include "core/io/ip.h" -void EMWSPeer::set_sock(int p_sock) { +void EMWSPeer::set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size) { peer_sock = p_sock; - in_buffer.clear(); - queue_count = 0; + _in_buffer.resize(p_in_pkt_size, p_in_buf_size); + _packet_buffer.resize((1 << p_in_buf_size)); } void EMWSPeer::set_write_mode(WriteMode p_mode) { @@ -47,18 +47,10 @@ EMWSPeer::WriteMode EMWSPeer::get_write_mode() const { return write_mode; } -void EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) { - - if (in_buffer.space_left() < p_size + 5) { - ERR_EXPLAIN("Buffer full! Dropping data"); - ERR_FAIL(); - } +Error EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) { uint8_t is_string = p_is_string ? 1 : 0; - in_buffer.write((uint8_t *)&p_size, 4); - in_buffer.write((uint8_t *)&is_string, 1); - in_buffer.write(p_data, p_size); - queue_count++; + return _in_buffer.write_packet(p_data, p_size, &is_string); } Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { @@ -89,40 +81,28 @@ Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { Error EMWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - if (queue_count == 0) + if (_in_buffer.packets_left() == 0) return ERR_UNAVAILABLE; - uint32_t to_read = 0; - uint32_t left = 0; - uint8_t is_string = 0; - r_buffer_size = 0; - - in_buffer.read((uint8_t *)&to_read, 4); - --queue_count; - left = in_buffer.data_left(); + PoolVector<uint8_t>::Write rw = _packet_buffer.write(); + int read = 0; + Error err = _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read); + ERR_FAIL_COND_V(err != OK, err); - if (left < to_read + 1) { - in_buffer.advance_read(left); - return FAILED; - } - - in_buffer.read(&is_string, 1); - _was_string = is_string == 1; - in_buffer.read(packet_buffer, to_read); - *r_buffer = packet_buffer; - r_buffer_size = to_read; + *r_buffer = rw.ptr(); + r_buffer_size = read; return OK; }; int EMWSPeer::get_available_packet_count() const { - return queue_count; + return _in_buffer.packets_left(); }; bool EMWSPeer::was_string_packet() const { - return _was_string; + return _is_string; }; bool EMWSPeer::is_connected_to_host() const { @@ -143,9 +123,9 @@ void EMWSPeer::close(int p_code, String p_reason) { }, peer_sock, p_code, p_reason.utf8().get_data()); /* clang-format on */ } + _is_string = 0; + _in_buffer.clear(); peer_sock = -1; - queue_count = 0; - in_buffer.clear(); }; IP_Address EMWSPeer::get_connected_host() const { @@ -162,15 +142,12 @@ uint16_t EMWSPeer::get_connected_port() const { EMWSPeer::EMWSPeer() { peer_sock = -1; - queue_count = 0; - _was_string = false; - in_buffer.resize(16); write_mode = WRITE_MODE_BINARY; + close(); }; EMWSPeer::~EMWSPeer() { - in_buffer.resize(0); close(); }; diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h index a4b2c8f50b..4beb86d45b 100644 --- a/modules/websocket/emws_peer.h +++ b/modules/websocket/emws_peer.h @@ -36,6 +36,7 @@ #include "core/io/packet_peer.h" #include "core/ring_buffer.h" #include "emscripten.h" +#include "packet_buffer.h" #include "websocket_peer.h" class EMWSPeer : public WebSocketPeer { @@ -43,25 +44,20 @@ class EMWSPeer : public WebSocketPeer { GDCIIMPL(EMWSPeer, WebSocketPeer); private: - enum { - PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type - }; - int peer_sock; WriteMode write_mode; - uint8_t packet_buffer[PACKET_BUFFER_SIZE]; - RingBuffer<uint8_t> in_buffer; - int queue_count; - bool _was_string; + PoolVector<uint8_t> _packet_buffer; + PacketBuffer<uint8_t> _in_buffer; + uint8_t _is_string; public: - void read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string); - void set_sock(int sock); + Error read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string); + void set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size); virtual int get_available_packet_count() const; virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); - virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; }; + virtual int get_max_packet_size() const { return _packet_buffer.size(); }; virtual void close(int p_code = 1000, String p_reason = ""); virtual bool is_connected_to_host() const; @@ -72,10 +68,6 @@ public: virtual void set_write_mode(WriteMode p_mode); virtual bool was_string_packet() const; - void set_wsi(struct lws *wsi); - Error read_wsi(void *in, size_t len); - Error write_wsi(); - EMWSPeer(); ~EMWSPeer(); }; diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp index db02162699..09f9c1ceec 100644 --- a/modules/websocket/emws_server.cpp +++ b/modules/websocket/emws_server.cpp @@ -74,6 +74,10 @@ void EMWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) { void EMWSServer::poll() { } +int EMWSServer::get_max_packet_size() const { + return 0; +} + EMWSServer::EMWSServer() { } diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h index 74b689a29b..2dc455c389 100644 --- a/modules/websocket/emws_server.h +++ b/modules/websocket/emws_server.h @@ -49,6 +49,7 @@ public: IP_Address get_peer_address(int p_peer_id) const; int get_peer_port(int p_peer_id) const; void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = ""); + int get_max_packet_size() const; virtual void poll(); virtual PoolVector<String> get_protocols() const; diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp index d71d091720..fa0bb4cfb2 100644 --- a/modules/websocket/lws_client.cpp +++ b/modules/websocket/lws_client.cpp @@ -32,6 +32,7 @@ #include "lws_client.h" #include "core/io/ip.h" #include "core/io/stream_peer_ssl.h" +#include "core/project_settings.h" #include "tls/mbedtls/wrapper/include/openssl/ssl.h" Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { @@ -90,12 +91,13 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, i.ssl_connection = 0; } - // This String needs to survive till we call lws_client_connect_via_info - String addr_str = (String)addr; - - i.address = addr_str.ascii().get_data(); - i.host = p_host.utf8().get_data(); - i.path = p_path.utf8().get_data(); + // These CharStrings needs to survive till we call lws_client_connect_via_info + CharString addr_ch = ((String)addr).ascii(); + CharString host_ch = p_host.utf8(); + CharString path_ch = p_path.utf8(); + i.address = addr_ch.get_data(); + i.host = host_ch.get_data(); + i.path = path_ch.get_data(); i.port = p_port; lws_client_connect_via_info(&i); @@ -103,6 +105,10 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, return OK; }; +int LWSClient::get_max_packet_size() const { + return (1 << _out_buf_size) - PROTO_SIZE; +} + void LWSClient::poll() { _lws_poll(); @@ -123,7 +129,7 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi } break; case LWS_CALLBACK_CLIENT_ESTABLISHED: - peer->set_wsi(wsi); + peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); peer_data->peer_id = 0; peer_data->force_close = false; peer_data->clean_close = false; @@ -206,6 +212,11 @@ uint16_t LWSClient::get_connected_port() const { }; LWSClient::LWSClient() { + _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10; + _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1); + _out_buf_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_BUF) - 1) + 10; + _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_PKT) - 1); + context = NULL; _lws_ref = NULL; _peer = Ref<LWSPeer>(memnew(LWSPeer)); diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h index 1bbc19f352..fdecb99925 100644 --- a/modules/websocket/lws_client.h +++ b/modules/websocket/lws_client.h @@ -43,8 +43,15 @@ class LWSClient : public WebSocketClient { LWS_HELPER(LWSClient); +private: + int _in_buf_size; + int _in_pkt_size; + int _out_buf_size; + int _out_pkt_size; + public: Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); + int get_max_packet_size() const; Ref<WebSocketPeer> get_peer(int p_peer_id) const; void disconnect_from_host(int p_code = 1000, String p_reason = ""); IP_Address get_connected_host() const; diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp index b5c130b308..04e6e7c951 100644 --- a/modules/websocket/lws_peer.cpp +++ b/modules/websocket/lws_peer.cpp @@ -41,11 +41,12 @@ #include "drivers/unix/net_socket_posix.h" -void LWSPeer::set_wsi(struct lws *p_wsi) { +void LWSPeer::set_wsi(struct lws *p_wsi, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size) { ERR_FAIL_COND(wsi != NULL); - rbw.resize(16); - rbr.resize(16); + _in_buffer.resize(p_in_pkt_size, p_in_buf_size); + _out_buffer.resize(p_out_pkt_size, p_out_buf_size); + _packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size)) + LWS_PRE); wsi = p_wsi; }; @@ -61,24 +62,29 @@ Error LWSPeer::read_wsi(void *in, size_t len) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - uint32_t size = in_size; - uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1; + if (lws_is_first_fragment(wsi)) + _in_size = 0; + else if (_in_size == -1) // Trash this frame + return ERR_FILE_CORRUPT; - if (rbr.space_left() < len + 5) { - ERR_EXPLAIN("Buffer full! Dropping data"); - ERR_FAIL_V(FAILED); + Error err = _in_buffer.write_packet((const uint8_t *)in, len, NULL); + + if (err != OK) { + _in_buffer.discard_payload(_in_size); + _in_size = -1; + ERR_FAIL_V(err); } - copymem(&(input_buffer[size]), in, len); - size += len; + _in_size += len; - in_size = size; if (lws_is_final_fragment(wsi)) { - rbr.write((uint8_t *)&size, 4); - rbr.write((uint8_t *)&is_string, 1); - rbr.write(input_buffer, size); - in_count++; - in_size = 0; + uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1; + err = _in_buffer.write_packet(NULL, _in_size, &is_string); + if (err != OK) { + _in_buffer.discard_payload(_in_size); + _in_size = -1; + ERR_FAIL_V(err); + } } return OK; @@ -89,26 +95,20 @@ Error LWSPeer::write_wsi() { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); PoolVector<uint8_t> tmp; - int left = rbw.data_left(); - uint32_t to_write = 0; + int count = _out_buffer.packets_left(); - if (left == 0 || out_count == 0) + if (count == 0) return OK; - rbw.read((uint8_t *)&to_write, 4); - out_count--; - - if (left < to_write) { - rbw.advance_read(left); - return FAILED; - } + int read = 0; + uint8_t is_string; + PoolVector<uint8_t>::Write rw = _packet_buffer.write(); + _out_buffer.read_packet(&(rw[LWS_PRE]), _packet_buffer.size() - LWS_PRE, &is_string, read); - tmp.resize(LWS_PRE + to_write); - rbw.read(&(tmp.write()[LWS_PRE]), to_write); - lws_write(wsi, &(tmp.write()[LWS_PRE]), to_write, (enum lws_write_protocol)write_mode); - tmp.resize(0); + enum lws_write_protocol mode = is_string ? LWS_WRITE_TEXT : LWS_WRITE_BINARY; + lws_write(wsi, &(rw[LWS_PRE]), read, mode); - if (out_count > 0) + if (count > 1) lws_callback_on_writable(wsi); // we want to write more! return OK; @@ -118,40 +118,27 @@ Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - rbw.write((uint8_t *)&p_buffer_size, 4); - rbw.write(p_buffer, MIN(p_buffer_size, rbw.space_left())); - out_count++; - + uint8_t is_string = write_mode == WRITE_MODE_TEXT; + _out_buffer.write_packet(p_buffer, p_buffer_size, &is_string); lws_callback_on_writable(wsi); // notify that we want to write return OK; }; Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + r_buffer_size = 0; + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - if (in_count == 0) + if (_in_buffer.packets_left() == 0) return ERR_UNAVAILABLE; - uint32_t to_read = 0; - uint32_t left = 0; - uint8_t is_string = 0; - r_buffer_size = 0; - - rbr.read((uint8_t *)&to_read, 4); - in_count--; - left = rbr.data_left(); - - if (left < to_read + 1) { - rbr.advance_read(left); - return FAILED; - } + int read = 0; + PoolVector<uint8_t>::Write rw = _packet_buffer.write(); + _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read); - rbr.read(&is_string, 1); - rbr.read(packet_buffer, to_read); - *r_buffer = packet_buffer; - r_buffer_size = to_read; - _was_string = is_string; + *r_buffer = rw.ptr(); + r_buffer_size = read; return OK; }; @@ -161,12 +148,12 @@ int LWSPeer::get_available_packet_count() const { if (!is_connected_to_host()) return 0; - return in_count; + return _in_buffer.packets_left(); }; bool LWSPeer::was_string_packet() const { - return _was_string; + return _is_string; }; bool LWSPeer::is_connected_to_host() const { @@ -219,12 +206,11 @@ void LWSPeer::close(int p_code, String p_reason) { close_reason = ""; } wsi = NULL; - rbw.resize(0); - rbr.resize(0); - in_count = 0; - in_size = 0; - out_count = 0; - _was_string = false; + _in_buffer.clear(); + _out_buffer.clear(); + _in_size = 0; + _is_string = 0; + _packet_buffer.resize(0); }; IP_Address LWSPeer::get_connected_host() const { diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h index 571445db01..3ded3810d1 100644 --- a/modules/websocket/lws_peer.h +++ b/modules/websocket/lws_peer.h @@ -37,6 +37,7 @@ #include "core/ring_buffer.h" #include "libwebsockets.h" #include "lws_config.h" +#include "packet_buffer.h" #include "websocket_peer.h" class LWSPeer : public WebSocketPeer { @@ -44,14 +45,16 @@ class LWSPeer : public WebSocketPeer { GDCIIMPL(LWSPeer, WebSocketPeer); private: - enum { - PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for the type - }; + int _in_size; + uint8_t _is_string; + // Our packet info is just a boolean (is_string), using uint8_t for it. + PacketBuffer<uint8_t> _in_buffer; + PacketBuffer<uint8_t> _out_buffer; + + PoolVector<uint8_t> _packet_buffer; - uint8_t packet_buffer[PACKET_BUFFER_SIZE]; struct lws *wsi; WriteMode write_mode; - bool _was_string; int close_code; String close_reason; @@ -63,17 +66,10 @@ public: bool clean_close; }; - RingBuffer<uint8_t> rbw; - RingBuffer<uint8_t> rbr; - uint8_t input_buffer[PACKET_BUFFER_SIZE]; - uint32_t in_size; - int in_count; - int out_count; - virtual int get_available_packet_count() const; virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); - virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; }; + virtual int get_max_packet_size() const { return _packet_buffer.size(); }; virtual void close(int p_code = 1000, String p_reason = ""); virtual bool is_connected_to_host() const; @@ -84,7 +80,7 @@ public: virtual void set_write_mode(WriteMode p_mode); virtual bool was_string_packet() const; - void set_wsi(struct lws *wsi); + void set_wsi(struct lws *wsi, unsigned int _in_buf_size, unsigned int _in_pkt_size, unsigned int _out_buf_size, unsigned int _out_pkt_size); Error read_wsi(void *in, size_t len); Error write_wsi(); void send_close_status(struct lws *wsi); diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp index 58fa043346..0e551eb318 100644 --- a/modules/websocket/lws_server.cpp +++ b/modules/websocket/lws_server.cpp @@ -31,6 +31,7 @@ #include "lws_server.h" #include "core/os/os.h" +#include "core/project_settings.h" Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { @@ -67,6 +68,10 @@ bool LWSServer::is_listening() const { return context != NULL; } +int LWSServer::get_max_packet_size() const { + return (1 << _out_buf_size) - PROTO_SIZE; +} + int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user; @@ -85,7 +90,7 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi int32_t id = _gen_unique_id(); Ref<LWSPeer> peer = Ref<LWSPeer>(memnew(LWSPeer)); - peer->set_wsi(wsi); + peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); _peer_map[id] = peer; peer_data->peer_id = id; @@ -192,6 +197,10 @@ void LWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) { } LWSServer::LWSServer() { + _in_buf_size = nearest_shift((int)GLOBAL_GET(WSS_IN_BUF) - 1) + 10; + _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_IN_PKT) - 1); + _out_buf_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_BUF) - 1) + 10; + _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_PKT) - 1); context = NULL; _lws_ref = NULL; } diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h index 346773ebc4..c43044f194 100644 --- a/modules/websocket/lws_server.h +++ b/modules/websocket/lws_server.h @@ -45,11 +45,16 @@ class LWSServer : public WebSocketServer { private: Map<int, Ref<LWSPeer> > peer_map; + int _in_buf_size; + int _in_pkt_size; + int _out_buf_size; + int _out_pkt_size; public: Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); void stop(); bool is_listening() const; + int get_max_packet_size() const; bool has_peer(int p_id) const; Ref<WebSocketPeer> get_peer(int p_id) const; IP_Address get_peer_address(int p_peer_id) const; diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h new file mode 100644 index 0000000000..a3af7f728a --- /dev/null +++ b/modules/websocket/packet_buffer.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* packet_buffer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PACKET_BUFFER_H +#define PACKET_BUFFER_H + +#include "core/os/copymem.h" +#include "core/ring_buffer.h" + +template <class T> +class PacketBuffer { + +private: + typedef struct { + uint32_t size; + T info; + } _Packet; + + RingBuffer<_Packet> _packets; + RingBuffer<uint8_t> _payload; + +public: + Error write_packet(const uint8_t *p_payload, uint32_t p_size, const T *p_info) { +#ifdef TOOLS_ENABLED + // Verbose buffer warnings + if (p_payload && _payload.space_left() < p_size) { + ERR_PRINT("Buffer payload full! Dropping data."); + ERR_FAIL_V(ERR_OUT_OF_MEMORY); + } + if (p_info && _packets.space_left() < 1) { + ERR_PRINT("Too many packets in queue! Dropping data."); + ERR_FAIL_V(ERR_OUT_OF_MEMORY); + } +#else + ERR_FAIL_COND_V(p_payload && _payload.space_left() < p_size, ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V(p_info && _packets.space_left() < 1, ERR_OUT_OF_MEMORY); +#endif + + // If p_info is NULL, only the payload is written + if (p_info) { + _Packet p; + p.size = p_size; + copymem(&p.info, p_info, sizeof(T)); + _packets.write(p); + } + + // If p_payload is NULL, only the packet information is written. + if (p_payload) { + _payload.write((const uint8_t *)p_payload, p_size); + } + + return OK; + } + + Error read_packet(uint8_t *r_payload, int p_bytes, T *r_info, int &r_read) { + ERR_FAIL_COND_V(_packets.data_left() < 1, ERR_UNAVAILABLE); + _Packet p; + _packets.read(&p, 1); + ERR_FAIL_COND_V(_payload.data_left() < p.size, ERR_BUG); + ERR_FAIL_COND_V(p_bytes < p.size, ERR_OUT_OF_MEMORY); + + r_read = p.size; + copymem(r_info, &p.info, sizeof(T)); + _payload.read(r_payload, p.size); + return OK; + } + + void discard_payload(int p_size) { + _packets.decrease_write(p_size); + } + + void resize(int p_pkt_shift, int p_buf_shift) { + _packets.resize(p_pkt_shift); + _payload.resize(p_buf_shift); + } + + int packets_left() const { + return _packets.data_left(); + } + + void clear() { + _payload.resize(0); + _packets.resize(0); + } + + PacketBuffer() { + clear(); + } + + ~PacketBuffer() { + clear(); + } +}; + +#endif // PACKET_BUFFER_H diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp index 538cd40454..8946faffa9 100644 --- a/modules/websocket/register_types.cpp +++ b/modules/websocket/register_types.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "register_types.h" #include "core/error_macros.h" +#include "core/project_settings.h" #ifdef JAVASCRIPT_ENABLED #include "emscripten.h" #include "emws_client.h" @@ -41,6 +42,22 @@ #endif void register_websocket_types() { +#define _SET_HINT(NAME, _VAL_, _MAX_) \ + GLOBAL_DEF(NAME, _VAL_); \ + ProjectSettings::get_singleton()->set_custom_property_info(NAME, PropertyInfo(Variant::INT, NAME, PROPERTY_HINT_RANGE, "2," #_MAX_ ",1,or_greater")); + + // Client buffers project settings + _SET_HINT(WSC_IN_BUF, 64, 4096); + _SET_HINT(WSC_IN_PKT, 1024, 16384); + _SET_HINT(WSC_OUT_BUF, 64, 4096); + _SET_HINT(WSC_OUT_PKT, 1024, 16384); + + // Server buffers project settings + _SET_HINT(WSS_IN_BUF, 64, 4096); + _SET_HINT(WSS_IN_PKT, 1024, 16384); + _SET_HINT(WSS_OUT_BUF, 64, 4096); + _SET_HINT(WSS_OUT_PKT, 1024, 16384); + #ifdef JAVASCRIPT_ENABLED EM_ASM({ var IDHandler = {}; diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index f9b94dc519..6c5018bb79 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -136,7 +136,7 @@ void WebSocketClient::_bind_methods() { ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled); ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled"); ADD_SIGNAL(MethodInfo("data_received")); ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol"))); diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h index d27fb4d778..45dd30d0ce 100644 --- a/modules/websocket/websocket_macros.h +++ b/modules/websocket/websocket_macros.h @@ -30,6 +30,16 @@ #ifndef WEBSOCKETMACTOS_H #define WEBSOCKETMACTOS_H +#define WSC_IN_BUF "network/limits/websocket_client/max_in_buffer_kb" +#define WSC_IN_PKT "network/limits/websocket_client/max_in_packets" +#define WSC_OUT_BUF "network/limits/websocket_client/max_out_buffer_kb" +#define WSC_OUT_PKT "network/limits/websocket_client/max_out_packets" + +#define WSS_IN_BUF "network/limits/websocket_server/max_in_buffer_kb" +#define WSS_IN_PKT "network/limits/websocket_server/max_in_packets" +#define WSS_OUT_BUF "network/limits/websocket_server/max_out_buffer_kb" +#define WSS_OUT_PKT "network/limits/websocket_server/max_out_packets" + /* clang-format off */ #define GDCICLASS(CNAME) \ public:\ diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer.cpp index 873658559a..9a95c17e47 100644 --- a/modules/websocket/websocket_multiplayer.cpp +++ b/modules/websocket/websocket_multiplayer.cpp @@ -100,13 +100,6 @@ int WebSocketMultiplayerPeer::get_available_packet_count() const { return _incoming_packets.size(); } -int WebSocketMultiplayerPeer::get_max_packet_size() const { - - ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); - - return MAX_PACKET_SIZE; -} - Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { r_buffer_size = 0; diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer.h index 8edfc5296e..3cba0011fc 100644 --- a/modules/websocket/websocket_multiplayer.h +++ b/modules/websocket/websocket_multiplayer.h @@ -51,9 +51,7 @@ protected: SYS_DEL = 2, SYS_ID = 3, - PROTO_SIZE = 9, - SYS_PACKET_SIZE = 13, - MAX_PACKET_SIZE = 65536 - 14 // 5 websocket, 9 multiplayer + PROTO_SIZE = 9 }; struct Packet { @@ -93,7 +91,7 @@ public: /* PacketPeer */ virtual int get_available_packet_count() const; - virtual int get_max_packet_size() const; + virtual int get_max_packet_size() const = 0; virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h index 5918fda3c2..4966cdfc72 100644 --- a/modules/websocket/websocket_peer.h +++ b/modules/websocket/websocket_peer.h @@ -32,7 +32,6 @@ #include "core/error_list.h" #include "core/io/packet_peer.h" -#include "core/ring_buffer.h" #include "websocket_macros.h" class WebSocketPeer : public PacketPeer { |