mirror of
https://github.com/godotengine/godot.git
synced 2024-11-10 06:03:09 +00:00
Merge pull request #62996 from reduz/feature-build-profiles
This commit is contained in:
commit
653f95282c
24
SConstruct
24
SConstruct
@ -195,7 +195,7 @@ opts.Add("extra_suffix", "Custom extra suffix added to the base filename of all
|
||||
opts.Add(BoolVariable("vsproj", "Generate a Visual Studio solution", False))
|
||||
opts.Add(BoolVariable("disable_3d", "Disable 3D nodes for a smaller executable", False))
|
||||
opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and behaviors", False))
|
||||
opts.Add("disable_classes", "Disable given classes (comma separated)", "")
|
||||
opts.Add("build_feature_profile", "Path to a file containing a feature build profile", "")
|
||||
opts.Add(BoolVariable("modules_enabled_by_default", "If no, disable all modules except ones explicitly enabled", True))
|
||||
opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", True))
|
||||
opts.Add("system_certs_path", "Use this path as SSL certificates default for editor (for package maintainers)", "")
|
||||
@ -752,7 +752,27 @@ if selected_platform in platform_list:
|
||||
|
||||
if env["tools"]:
|
||||
env.Append(CPPDEFINES=["TOOLS_ENABLED"])
|
||||
methods.write_disabled_classes(env["disable_classes"].split(","))
|
||||
|
||||
disabled_classes = []
|
||||
|
||||
if env["build_feature_profile"] != "":
|
||||
print("Using build feature profile: " + env["build_feature_profile"])
|
||||
import json
|
||||
|
||||
try:
|
||||
ft = json.load(open(env["build_feature_profile"]))
|
||||
if "disabled_classes" in ft:
|
||||
disabled_classes = ft["disabled_classes"]
|
||||
if "disabled_build_options" in ft:
|
||||
dbo = ft["disabled_build_options"]
|
||||
for c in dbo:
|
||||
env[c] = dbo[c]
|
||||
except:
|
||||
print("Error opening feature build profile: " + env["build_feature_profile"])
|
||||
Exit(255)
|
||||
|
||||
methods.write_disabled_classes(disabled_classes)
|
||||
|
||||
if env["disable_3d"]:
|
||||
if env["tools"]:
|
||||
print(
|
||||
|
@ -865,6 +865,22 @@ String ResourceLoaderBinary::get_unicode_string() {
|
||||
return s;
|
||||
}
|
||||
|
||||
void ResourceLoaderBinary::get_classes_used(Ref<FileAccess> p_f, HashSet<StringName> *p_classes) {
|
||||
open(p_f, false, true);
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < internal_resources.size(); i++) {
|
||||
p_f->seek(internal_resources[i].offset);
|
||||
String t = get_unicode_string();
|
||||
ERR_FAIL_COND(p_f->get_error() != OK);
|
||||
if (t != String()) {
|
||||
p_classes->insert(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceLoaderBinary::get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types) {
|
||||
open(p_f, false, true);
|
||||
if (error) {
|
||||
@ -1337,6 +1353,16 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
|
||||
return OK;
|
||||
}
|
||||
|
||||
void ResourceFormatLoaderBinary::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file '" + p_path + "'.");
|
||||
|
||||
ResourceLoaderBinary loader;
|
||||
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
|
||||
loader.res_path = loader.local_path;
|
||||
loader.get_classes_used(f, r_classes);
|
||||
}
|
||||
|
||||
String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const {
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
if (f.is_null()) {
|
||||
|
@ -101,6 +101,7 @@ public:
|
||||
void open(Ref<FileAccess> p_f, bool p_no_resources = false, bool p_keep_uuid_paths = false);
|
||||
String recognize(Ref<FileAccess> p_f);
|
||||
void get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types);
|
||||
void get_classes_used(Ref<FileAccess> p_f, HashSet<StringName> *p_classes);
|
||||
|
||||
ResourceLoaderBinary() {}
|
||||
};
|
||||
@ -112,6 +113,7 @@ public:
|
||||
virtual void get_recognized_extensions(List<String> *p_extensions) const;
|
||||
virtual bool handles_type(const String &p_type) const;
|
||||
virtual String get_resource_type(const String &p_path) const;
|
||||
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
|
||||
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
|
||||
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
|
||||
virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map);
|
||||
|
@ -352,6 +352,16 @@ Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) cons
|
||||
|
||||
return pat.metadata;
|
||||
}
|
||||
void ResourceFormatImporter::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
|
||||
PathAndType pat;
|
||||
Error err = _get_path_and_type(p_path, pat);
|
||||
|
||||
if (err != OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceLoader::get_classes_used(pat.path, r_classes);
|
||||
}
|
||||
|
||||
void ResourceFormatImporter::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
|
||||
PathAndType pat;
|
||||
|
@ -65,12 +65,12 @@ public:
|
||||
virtual bool handles_type(const String &p_type) const;
|
||||
virtual String get_resource_type(const String &p_path) const;
|
||||
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
|
||||
|
||||
virtual Variant get_resource_metadata(const String &p_path) const;
|
||||
virtual bool is_import_valid(const String &p_path) const;
|
||||
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
|
||||
virtual bool is_imported(const String &p_path) const { return recognize_path(p_path); }
|
||||
virtual String get_import_group_file(const String &p_path) const;
|
||||
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
|
||||
virtual bool exists(const String &p_path) const;
|
||||
|
||||
virtual int get_import_order(const String &p_path) const;
|
||||
|
@ -76,6 +76,21 @@ bool ResourceFormatLoader::handles_type(const String &p_type) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResourceFormatLoader::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
|
||||
Vector<String> ret;
|
||||
if (GDVIRTUAL_CALL(_get_classes_used, p_path, ret)) {
|
||||
for (int i = 0; i < ret.size(); i++) {
|
||||
r_classes->insert(ret[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
String res = get_resource_type(p_path);
|
||||
if (!res.is_empty()) {
|
||||
r_classes->insert(res);
|
||||
}
|
||||
}
|
||||
|
||||
String ResourceFormatLoader::get_resource_type(const String &p_path) const {
|
||||
String ret;
|
||||
|
||||
@ -180,6 +195,7 @@ void ResourceFormatLoader::_bind_methods() {
|
||||
GDVIRTUAL_BIND(_get_dependencies, "path", "add_types");
|
||||
GDVIRTUAL_BIND(_rename_dependencies, "path", "renames");
|
||||
GDVIRTUAL_BIND(_exists, "path");
|
||||
GDVIRTUAL_BIND(_get_classes_used, "path");
|
||||
GDVIRTUAL_BIND(_load, "path", "original_path", "use_sub_threads", "cache_mode");
|
||||
}
|
||||
|
||||
@ -730,6 +746,18 @@ Error ResourceLoader::rename_dependencies(const String &p_path, const HashMap<St
|
||||
return OK; // ??
|
||||
}
|
||||
|
||||
void ResourceLoader::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
|
||||
String local_path = _validate_local_path(p_path);
|
||||
|
||||
for (int i = 0; i < loader_count; i++) {
|
||||
if (!loader[i]->recognize_path(local_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return loader[i]->get_classes_used(p_path, r_classes);
|
||||
}
|
||||
}
|
||||
|
||||
String ResourceLoader::get_resource_type(const String &p_path) {
|
||||
String local_path = _validate_local_path(p_path);
|
||||
|
||||
|
@ -55,6 +55,7 @@ protected:
|
||||
GDVIRTUAL1RC(String, _get_resource_type, String)
|
||||
GDVIRTUAL1RC(ResourceUID::ID, _get_resource_uid, String)
|
||||
GDVIRTUAL2RC(Vector<String>, _get_dependencies, String, bool)
|
||||
GDVIRTUAL1RC(Vector<String>, _get_classes_used, String)
|
||||
GDVIRTUAL2RC(int64_t, _rename_dependencies, String, Dictionary)
|
||||
GDVIRTUAL1RC(bool, _exists, String)
|
||||
|
||||
@ -67,6 +68,7 @@ public:
|
||||
virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
|
||||
virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const;
|
||||
virtual bool handles_type(const String &p_type) const;
|
||||
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
|
||||
virtual String get_resource_type(const String &p_path) const;
|
||||
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
|
||||
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
|
||||
@ -170,6 +172,7 @@ public:
|
||||
static void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions);
|
||||
static void add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front = false);
|
||||
static void remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader);
|
||||
static void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
|
||||
static String get_resource_type(const String &p_path);
|
||||
static ResourceUID::ID get_resource_uid(const String &p_path);
|
||||
static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
|
||||
|
@ -305,6 +305,13 @@ void ClassDB::add_compatibility_class(const StringName &p_class, const StringNam
|
||||
compat_classes[p_class] = p_fallback;
|
||||
}
|
||||
|
||||
StringName ClassDB::get_compatibility_class(const StringName &p_class) {
|
||||
if (compat_classes.has(p_class)) {
|
||||
return compat_classes[p_class];
|
||||
}
|
||||
return StringName();
|
||||
}
|
||||
|
||||
Object *ClassDB::instantiate(const StringName &p_class) {
|
||||
ClassInfo *ti;
|
||||
{
|
||||
|
@ -357,6 +357,7 @@ public:
|
||||
static bool is_resource_extension(const StringName &p_extension);
|
||||
|
||||
static void add_compatibility_class(const StringName &p_class, const StringName &p_fallback);
|
||||
static StringName get_compatibility_class(const StringName &p_class);
|
||||
|
||||
static void set_current_api(APIType p_api);
|
||||
static APIType get_current_api();
|
||||
@ -418,16 +419,16 @@ _FORCE_INLINE_ Vector<Error> errarray(P... p_args) {
|
||||
|
||||
#endif
|
||||
|
||||
#define GDREGISTER_CLASS(m_class) \
|
||||
if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
|
||||
::ClassDB::register_class<m_class>(); \
|
||||
#define GDREGISTER_CLASS(m_class) \
|
||||
if (m_class::_class_is_enabled) { \
|
||||
::ClassDB::register_class<m_class>(); \
|
||||
}
|
||||
#define GDREGISTER_VIRTUAL_CLASS(m_class) \
|
||||
if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
|
||||
::ClassDB::register_class<m_class>(true); \
|
||||
#define GDREGISTER_VIRTUAL_CLASS(m_class) \
|
||||
if (m_class::_class_is_enabled) { \
|
||||
::ClassDB::register_class<m_class>(true); \
|
||||
}
|
||||
#define GDREGISTER_ABSTRACT_CLASS(m_class) \
|
||||
if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
|
||||
if (m_class::_class_is_enabled) { \
|
||||
::ClassDB::register_abstract_class<m_class>(); \
|
||||
}
|
||||
|
||||
|
@ -358,6 +358,7 @@ private:
|
||||
friend class ::ClassDB; \
|
||||
\
|
||||
public: \
|
||||
static constexpr bool _class_is_enabled = !bool(GD_IS_DEFINED(ClassDB_Disable_##m_class)) && m_inherits::_class_is_enabled; \
|
||||
virtual String get_class() const override { \
|
||||
if (_get_extension()) { \
|
||||
return _get_extension()->class_name.operator String(); \
|
||||
@ -667,6 +668,8 @@ public: // Should be protected, but bug in clang++.
|
||||
_FORCE_INLINE_ static void register_custom_data_to_otdb() {}
|
||||
|
||||
public:
|
||||
static constexpr bool _class_is_enabled = true;
|
||||
|
||||
void notify_property_list_changed();
|
||||
|
||||
static void *get_class_ptr_static() {
|
||||
|
@ -17,6 +17,12 @@
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="_get_classes_used" qualifiers="virtual const">
|
||||
<return type="PackedStringArray" />
|
||||
<argument index="0" name="path" type="String" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="_get_dependencies" qualifiers="virtual const">
|
||||
<return type="PackedStringArray" />
|
||||
<argument index="0" name="path" type="String" />
|
||||
|
797
editor/editor_build_profile.cpp
Normal file
797
editor/editor_build_profile.cpp
Normal file
@ -0,0 +1,797 @@
|
||||
/*************************************************************************/
|
||||
/* editor_build_profile.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "editor_build_profile.h"
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/json.h"
|
||||
#include "editor/editor_file_dialog.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_property_name_processor.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
|
||||
const char *EditorBuildProfile::build_option_identifiers[BUILD_OPTION_MAX] = {
|
||||
// This maps to SCons build options.
|
||||
"disable_3d",
|
||||
"disable_2d_physics",
|
||||
"disable_3d_physics",
|
||||
"disable_navigation",
|
||||
"openxr",
|
||||
"opengl3",
|
||||
"vulkan",
|
||||
};
|
||||
|
||||
const bool EditorBuildProfile::build_option_disable_values[BUILD_OPTION_MAX] = {
|
||||
// This maps to SCons build options.
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
};
|
||||
|
||||
void EditorBuildProfile::set_disable_class(const StringName &p_class, bool p_disabled) {
|
||||
if (p_disabled) {
|
||||
disabled_classes.insert(p_class);
|
||||
} else {
|
||||
disabled_classes.erase(p_class);
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorBuildProfile::is_class_disabled(const StringName &p_class) const {
|
||||
if (p_class == StringName()) {
|
||||
return false;
|
||||
}
|
||||
return disabled_classes.has(p_class) || is_class_disabled(ClassDB::get_parent_class_nocheck(p_class));
|
||||
}
|
||||
|
||||
void EditorBuildProfile::set_item_collapsed(const StringName &p_class, bool p_collapsed) {
|
||||
if (p_collapsed) {
|
||||
collapsed_classes.insert(p_class);
|
||||
} else {
|
||||
collapsed_classes.erase(p_class);
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorBuildProfile::is_item_collapsed(const StringName &p_class) const {
|
||||
return collapsed_classes.has(p_class);
|
||||
}
|
||||
|
||||
void EditorBuildProfile::set_disable_build_option(BuildOption p_build_option, bool p_disable) {
|
||||
ERR_FAIL_INDEX(p_build_option, BUILD_OPTION_MAX);
|
||||
build_options_disabled[p_build_option] = p_disable;
|
||||
}
|
||||
|
||||
void EditorBuildProfile::clear_disabled_classes() {
|
||||
disabled_classes.clear();
|
||||
collapsed_classes.clear();
|
||||
}
|
||||
|
||||
bool EditorBuildProfile::is_build_option_disabled(BuildOption p_build_option) const {
|
||||
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, false);
|
||||
return build_options_disabled[p_build_option];
|
||||
}
|
||||
|
||||
bool EditorBuildProfile::get_build_option_disable_value(BuildOption p_build_option) {
|
||||
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, false);
|
||||
return build_option_disable_values[p_build_option];
|
||||
}
|
||||
|
||||
void EditorBuildProfile::set_force_detect_classes(const String &p_classes) {
|
||||
force_detect_classes = p_classes;
|
||||
}
|
||||
|
||||
String EditorBuildProfile::get_force_detect_classes() const {
|
||||
return force_detect_classes;
|
||||
}
|
||||
|
||||
String EditorBuildProfile::get_build_option_name(BuildOption p_build_option) {
|
||||
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, String());
|
||||
const char *build_option_names[BUILD_OPTION_MAX] = {
|
||||
TTRC("3D Engine"),
|
||||
TTRC("2D Physics"),
|
||||
TTRC("3D Physics"),
|
||||
TTRC("Navigation"),
|
||||
TTRC("XR"),
|
||||
TTRC("RenderingDevice"),
|
||||
TTRC("OpenGL"),
|
||||
TTRC("Vulkan"),
|
||||
};
|
||||
return TTRGET(build_option_names[p_build_option]);
|
||||
}
|
||||
|
||||
String EditorBuildProfile::get_build_option_description(BuildOption p_build_option) {
|
||||
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, String());
|
||||
|
||||
const char *build_option_descriptions[BUILD_OPTION_MAX] = {
|
||||
TTRC("3D Nodes as well as RenderingServer access to 3D features."),
|
||||
TTRC("2D Physics nodes and PhysicsServer2D."),
|
||||
TTRC("3D Physics nodes and PhysicsServer3D."),
|
||||
TTRC("Navigation, both 2D and 3D."),
|
||||
TTRC("XR (AR and VR)."),
|
||||
TTRC("RenderingDevice based rendering (if disabled, the OpenGL back-end is required)."),
|
||||
TTRC("OpenGL back-end (if disabled, the RenderingDevice back-end is required)."),
|
||||
TTRC("Vulkan back-end of RenderingDevice."),
|
||||
};
|
||||
|
||||
return TTRGET(build_option_descriptions[p_build_option]);
|
||||
}
|
||||
|
||||
Error EditorBuildProfile::save_to_file(const String &p_path) {
|
||||
Dictionary data;
|
||||
data["type"] = "build_profile";
|
||||
Array dis_classes;
|
||||
for (const StringName &E : disabled_classes) {
|
||||
dis_classes.push_back(String(E));
|
||||
}
|
||||
dis_classes.sort();
|
||||
data["disabled_classes"] = dis_classes;
|
||||
|
||||
Dictionary dis_build_options;
|
||||
for (int i = 0; i < BUILD_OPTION_MAX; i++) {
|
||||
if (build_options_disabled[i]) {
|
||||
dis_build_options[build_option_identifiers[i]] = build_option_disable_values[i];
|
||||
}
|
||||
}
|
||||
|
||||
data["disabled_build_options"] = dis_build_options;
|
||||
|
||||
if (!force_detect_classes.is_empty()) {
|
||||
data["force_detect_classes"] = force_detect_classes;
|
||||
}
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
|
||||
|
||||
JSON json;
|
||||
String text = json.stringify(data, "\t");
|
||||
f->store_string(text);
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EditorBuildProfile::load_from_file(const String &p_path) {
|
||||
Error err;
|
||||
String text = FileAccess::get_file_as_string(p_path, &err);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
JSON json;
|
||||
err = json.parse(text);
|
||||
if (err != OK) {
|
||||
ERR_PRINT("Error parsing '" + p_path + "' on line " + itos(json.get_error_line()) + ": " + json.get_error_message());
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
||||
Dictionary data = json.get_data();
|
||||
|
||||
if (!data.has("type") || String(data["type"]) != "build_profile") {
|
||||
ERR_PRINT("Error parsing '" + p_path + "', it's not a build profile.");
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
||||
disabled_classes.clear();
|
||||
|
||||
if (data.has("disabled_classes")) {
|
||||
Array disabled_classes_arr = data["disabled_classes"];
|
||||
for (int i = 0; i < disabled_classes_arr.size(); i++) {
|
||||
disabled_classes.insert(disabled_classes_arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < BUILD_OPTION_MAX; i++) {
|
||||
build_options_disabled[i] = false;
|
||||
}
|
||||
|
||||
if (data.has("disabled_build_options")) {
|
||||
Dictionary disabled_build_options_arr = data["disabled_build_options"];
|
||||
List<Variant> keys;
|
||||
disabled_build_options_arr.get_key_list(&keys);
|
||||
|
||||
for (const Variant &K : keys) {
|
||||
String key = K;
|
||||
|
||||
for (int i = 0; i < BUILD_OPTION_MAX; i++) {
|
||||
String f = build_option_identifiers[i];
|
||||
if (f == key) {
|
||||
build_options_disabled[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.has("force_detect_classes")) {
|
||||
force_detect_classes = data["force_detect_classes"];
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void EditorBuildProfile::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_disable_class", "class_name", "disable"), &EditorBuildProfile::set_disable_class);
|
||||
ClassDB::bind_method(D_METHOD("is_class_disabled", "class_name"), &EditorBuildProfile::is_class_disabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_disable_build_option", "build_option", "disable"), &EditorBuildProfile::set_disable_build_option);
|
||||
ClassDB::bind_method(D_METHOD("is_build_option_disabled", "build_option"), &EditorBuildProfile::is_build_option_disabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_build_option_name", "build_option"), &EditorBuildProfile::_get_build_option_name);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("save_to_file", "path"), &EditorBuildProfile::save_to_file);
|
||||
ClassDB::bind_method(D_METHOD("load_from_file", "path"), &EditorBuildProfile::load_from_file);
|
||||
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_3D);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_2D);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_3D);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_NAVIGATION);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_XR);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_RENDERING_DEVICE);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_OPENGL);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_VULKAN);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_MAX);
|
||||
}
|
||||
|
||||
EditorBuildProfile::EditorBuildProfile() {}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void EditorBuildProfileManager::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
String last_file = EditorSettings::get_singleton()->get_project_metadata("build_profile", "last_file_path", "");
|
||||
if (!last_file.is_empty()) {
|
||||
_import_profile(last_file);
|
||||
}
|
||||
if (edited.is_null()) {
|
||||
edited.instantiate();
|
||||
_update_edited_profile();
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_profile_action(int p_action) {
|
||||
last_action = Action(p_action);
|
||||
|
||||
switch (p_action) {
|
||||
case ACTION_RESET: {
|
||||
confirm_dialog->set_text("Reset the edited profile?");
|
||||
confirm_dialog->popup_centered();
|
||||
} break;
|
||||
case ACTION_LOAD: {
|
||||
import_profile->popup_file_dialog();
|
||||
} break;
|
||||
case ACTION_SAVE: {
|
||||
if (!profile_path->get_text().is_empty()) {
|
||||
Error err = edited->save_to_file(profile_path->get_text());
|
||||
if (err != OK) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("File saving failed."));
|
||||
}
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
case ACTION_SAVE_AS: {
|
||||
export_profile->popup_file_dialog();
|
||||
export_profile->set_current_file(profile_path->get_text());
|
||||
} break;
|
||||
case ACTION_NEW: {
|
||||
confirm_dialog->set_text("Create a new profile?");
|
||||
confirm_dialog->popup_centered();
|
||||
} break;
|
||||
case ACTION_DETECT: {
|
||||
confirm_dialog->set_text("This will scan all files in the current project to detect used classes.");
|
||||
confirm_dialog->popup_centered();
|
||||
} break;
|
||||
case ACTION_MAX: {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_find_files(EditorFileSystemDirectory *p_dir, const HashMap<String, DetectedFile> &p_cache, HashMap<String, DetectedFile> &r_detected) {
|
||||
if (p_dir == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < p_dir->get_file_count(); i++) {
|
||||
String p = p_dir->get_file_path(i);
|
||||
|
||||
uint64_t timestamp = 0;
|
||||
String md5;
|
||||
|
||||
if (p_cache.has(p)) {
|
||||
const DetectedFile &cache = p_cache[p];
|
||||
// Check if timestamp and MD5 match.
|
||||
timestamp = FileAccess::get_modified_time(p);
|
||||
bool cache_valid = true;
|
||||
if (cache.timestamp != timestamp) {
|
||||
md5 = FileAccess::get_md5(p);
|
||||
if (md5 != cache.md5) {
|
||||
cache_valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cache_valid) {
|
||||
r_detected.insert(p, cache);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Not cached, or cache invalid.
|
||||
|
||||
DetectedFile cache;
|
||||
|
||||
HashSet<StringName> classes;
|
||||
ResourceLoader::get_classes_used(p, &classes);
|
||||
|
||||
for (const StringName &E : classes) {
|
||||
cache.classes.push_back(E);
|
||||
}
|
||||
|
||||
if (md5.is_empty()) {
|
||||
cache.timestamp = FileAccess::get_modified_time(p);
|
||||
cache.md5 = FileAccess::get_md5(p);
|
||||
} else {
|
||||
cache.timestamp = timestamp;
|
||||
cache.md5 = md5;
|
||||
}
|
||||
|
||||
r_detected.insert(p, cache);
|
||||
}
|
||||
|
||||
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
|
||||
_find_files(p_dir->get_subdir(i), p_cache, r_detected);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_detect_classes() {
|
||||
HashMap<String, DetectedFile> previous_file_cache;
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open("res://.godot/editor/used_class_cache", FileAccess::READ);
|
||||
if (f.is_valid()) {
|
||||
while (!f->eof_reached()) {
|
||||
String l = f->get_line();
|
||||
Vector<String> fields = l.split("::");
|
||||
if (fields.size() == 4) {
|
||||
String path = fields[0];
|
||||
DetectedFile df;
|
||||
df.timestamp = fields[1].to_int();
|
||||
df.md5 = fields[2];
|
||||
df.classes = fields[3].split(",");
|
||||
previous_file_cache.insert(path, df);
|
||||
}
|
||||
}
|
||||
f.unref();
|
||||
}
|
||||
|
||||
HashMap<String, DetectedFile> updated_file_cache;
|
||||
|
||||
_find_files(EditorFileSystem::get_singleton()->get_filesystem(), previous_file_cache, updated_file_cache);
|
||||
|
||||
HashSet<StringName> used_classes;
|
||||
|
||||
// Find classes and update the disk cache in the process.
|
||||
f = FileAccess::open("res://.godot/editor/used_class_cache", FileAccess::WRITE);
|
||||
|
||||
for (const KeyValue<String, DetectedFile> &E : updated_file_cache) {
|
||||
String l = E.key + "::" + itos(E.value.timestamp) + "::" + E.value.md5 + "::";
|
||||
for (int i = 0; i < E.value.classes.size(); i++) {
|
||||
String c = E.value.classes[i];
|
||||
if (i > 0) {
|
||||
l += ",";
|
||||
}
|
||||
l += c;
|
||||
used_classes.insert(c);
|
||||
}
|
||||
f->store_line(l);
|
||||
}
|
||||
|
||||
f.unref();
|
||||
|
||||
// Add forced ones.
|
||||
|
||||
Vector<String> force_detect = edited->get_force_detect_classes().split(",");
|
||||
for (int i = 0; i < force_detect.size(); i++) {
|
||||
String c = force_detect[i].strip_edges();
|
||||
if (c.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
used_classes.insert(c);
|
||||
}
|
||||
|
||||
// Filter all classes to discard inherited ones.
|
||||
|
||||
HashSet<StringName> all_used_classes;
|
||||
|
||||
for (const StringName &E : used_classes) {
|
||||
StringName c = E;
|
||||
if (!ClassDB::class_exists(c)) {
|
||||
// Maybe this is an old class that got replaced? try getting compat class.
|
||||
c = ClassDB::get_compatibility_class(c);
|
||||
if (!c) {
|
||||
// No luck, skip.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
while (c) {
|
||||
all_used_classes.insert(c);
|
||||
c = ClassDB::get_parent_class(c);
|
||||
}
|
||||
}
|
||||
|
||||
edited->clear_disabled_classes();
|
||||
|
||||
List<StringName> all_classes;
|
||||
ClassDB::get_class_list(&all_classes);
|
||||
|
||||
for (const StringName &E : all_classes) {
|
||||
if (all_used_classes.has(E)) {
|
||||
// This class is valid, do nothing.
|
||||
continue;
|
||||
}
|
||||
|
||||
StringName p = ClassDB::get_parent_class(E);
|
||||
if (!p || all_used_classes.has(p)) {
|
||||
// If no parent, or if the parent is enabled, then add to disabled classes.
|
||||
// This way we avoid disabling redundant classes.
|
||||
edited->set_disable_class(E, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_action_confirm() {
|
||||
switch (last_action) {
|
||||
case ACTION_RESET: {
|
||||
edited.instantiate();
|
||||
_update_edited_profile();
|
||||
} break;
|
||||
case ACTION_LOAD: {
|
||||
} break;
|
||||
case ACTION_SAVE: {
|
||||
} break;
|
||||
case ACTION_SAVE_AS: {
|
||||
} break;
|
||||
case ACTION_NEW: {
|
||||
profile_path->set_text("");
|
||||
edited.instantiate();
|
||||
_update_edited_profile();
|
||||
} break;
|
||||
case ACTION_DETECT: {
|
||||
_detect_classes();
|
||||
_update_edited_profile();
|
||||
} break;
|
||||
case ACTION_MAX: {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected) {
|
||||
TreeItem *class_item = class_list->create_item(p_parent);
|
||||
class_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
class_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_class, "Node"));
|
||||
String text = p_class;
|
||||
|
||||
bool disabled = edited->is_class_disabled(p_class);
|
||||
if (disabled) {
|
||||
class_item->set_custom_color(0, class_list->get_theme_color(SNAME("disabled_font_color"), SNAME("Editor")));
|
||||
}
|
||||
|
||||
class_item->set_text(0, text);
|
||||
class_item->set_editable(0, true);
|
||||
class_item->set_selectable(0, true);
|
||||
class_item->set_metadata(0, p_class);
|
||||
|
||||
bool collapsed = edited->is_item_collapsed(p_class);
|
||||
class_item->set_collapsed(collapsed);
|
||||
|
||||
if (p_class == p_selected) {
|
||||
class_item->select(0);
|
||||
}
|
||||
if (disabled) {
|
||||
// Class disabled, do nothing else (do not show further).
|
||||
return;
|
||||
}
|
||||
|
||||
class_item->set_checked(0, true); // If it's not disabled, its checked.
|
||||
|
||||
List<StringName> child_classes;
|
||||
ClassDB::get_direct_inheriters_from_class(p_class, &child_classes);
|
||||
child_classes.sort_custom<StringName::AlphCompare>();
|
||||
|
||||
for (const StringName &name : child_classes) {
|
||||
if (String(name).begins_with("Editor") || ClassDB::get_api_type(name) != ClassDB::API_CORE) {
|
||||
continue;
|
||||
}
|
||||
_fill_classes_from(class_item, name, p_selected);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_class_list_item_selected() {
|
||||
if (updating_build_options) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem *item = class_list->get_selected();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
Variant md = item->get_metadata(0);
|
||||
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
|
||||
String class_name = md;
|
||||
String class_description;
|
||||
|
||||
DocTools *dd = EditorHelp::get_doc_data();
|
||||
HashMap<String, DocData::ClassDoc>::Iterator E = dd->class_list.find(class_name);
|
||||
if (E) {
|
||||
class_description = DTR(E->value.brief_description);
|
||||
}
|
||||
|
||||
description_bit->set_text(class_description);
|
||||
} else if (md.get_type() == Variant::INT) {
|
||||
int build_option_id = md;
|
||||
String build_option_description = EditorBuildProfile::get_build_option_description(EditorBuildProfile::BuildOption(build_option_id));
|
||||
|
||||
description_bit->set_text(TTRGET(build_option_description));
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_class_list_item_edited() {
|
||||
if (updating_build_options) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem *item = class_list->get_edited();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool checked = item->is_checked(0);
|
||||
|
||||
Variant md = item->get_metadata(0);
|
||||
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
|
||||
String class_selected = md;
|
||||
edited->set_disable_class(class_selected, !checked);
|
||||
_update_edited_profile();
|
||||
} else if (md.get_type() == Variant::INT) {
|
||||
int build_option_selected = md;
|
||||
edited->set_disable_build_option(EditorBuildProfile::BuildOption(build_option_selected), !checked);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_class_list_item_collapsed(Object *p_item) {
|
||||
if (updating_build_options) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem *item = Object::cast_to<TreeItem>(p_item);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
Variant md = item->get_metadata(0);
|
||||
if (md.get_type() != Variant::STRING && md.get_type() != Variant::STRING_NAME) {
|
||||
return;
|
||||
}
|
||||
|
||||
String class_name = md;
|
||||
bool collapsed = item->is_collapsed();
|
||||
edited->set_item_collapsed(class_name, collapsed);
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_update_edited_profile() {
|
||||
String class_selected;
|
||||
int build_option_selected = -1;
|
||||
|
||||
if (class_list->get_selected()) {
|
||||
Variant md = class_list->get_selected()->get_metadata(0);
|
||||
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
|
||||
class_selected = md;
|
||||
} else if (md.get_type() == Variant::INT) {
|
||||
build_option_selected = md;
|
||||
}
|
||||
}
|
||||
|
||||
class_list->clear();
|
||||
|
||||
updating_build_options = true;
|
||||
|
||||
TreeItem *root = class_list->create_item();
|
||||
|
||||
TreeItem *build_options = class_list->create_item(root);
|
||||
build_options->set_text(0, TTR("General Features:"));
|
||||
for (int i = 0; i < EditorBuildProfile::BUILD_OPTION_MAX; i++) {
|
||||
TreeItem *build_option;
|
||||
build_option = class_list->create_item(build_options);
|
||||
|
||||
build_option->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
build_option->set_text(0, EditorBuildProfile::get_build_option_name(EditorBuildProfile::BuildOption(i)));
|
||||
build_option->set_selectable(0, true);
|
||||
build_option->set_editable(0, true);
|
||||
build_option->set_metadata(0, i);
|
||||
if (!edited->is_build_option_disabled(EditorBuildProfile::BuildOption(i))) {
|
||||
build_option->set_checked(0, true);
|
||||
}
|
||||
|
||||
if (i == build_option_selected) {
|
||||
build_option->select(0);
|
||||
}
|
||||
}
|
||||
|
||||
TreeItem *classes = class_list->create_item(root);
|
||||
classes->set_text(0, TTR("Nodes and Classes:"));
|
||||
|
||||
_fill_classes_from(classes, "Node", class_selected);
|
||||
_fill_classes_from(classes, "Resource", class_selected);
|
||||
|
||||
force_detect_classes->set_text(edited->get_force_detect_classes());
|
||||
|
||||
updating_build_options = false;
|
||||
|
||||
_class_list_item_selected();
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_force_detect_classes_changed(const String &p_text) {
|
||||
if (updating_build_options) {
|
||||
return;
|
||||
}
|
||||
edited->set_force_detect_classes(force_detect_classes->get_text());
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_import_profile(const String &p_path) {
|
||||
Ref<EditorBuildProfile> profile;
|
||||
profile.instantiate();
|
||||
Error err = profile->load_from_file(p_path);
|
||||
String basefile = p_path.get_file();
|
||||
if (err != OK) {
|
||||
EditorNode::get_singleton()->show_warning(vformat(TTR("File '%s' format is invalid, import aborted."), basefile));
|
||||
return;
|
||||
}
|
||||
|
||||
profile_path->set_text(p_path);
|
||||
EditorSettings::get_singleton()->set_project_metadata("build_profile", "last_file_path", p_path);
|
||||
|
||||
edited = profile;
|
||||
_update_edited_profile();
|
||||
}
|
||||
|
||||
void EditorBuildProfileManager::_export_profile(const String &p_path) {
|
||||
ERR_FAIL_COND(edited.is_null());
|
||||
Error err = edited->save_to_file(p_path);
|
||||
if (err != OK) {
|
||||
EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving profile to path: '%s'."), p_path));
|
||||
} else {
|
||||
profile_path->set_text(p_path);
|
||||
EditorSettings::get_singleton()->set_project_metadata("build_profile", "last_file_path", p_path);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<EditorBuildProfile> EditorBuildProfileManager::get_current_profile() {
|
||||
return edited;
|
||||
}
|
||||
|
||||
EditorBuildProfileManager *EditorBuildProfileManager::singleton = nullptr;
|
||||
|
||||
void EditorBuildProfileManager::_bind_methods() {
|
||||
ClassDB::bind_method("_update_selected_profile", &EditorBuildProfileManager::_update_edited_profile);
|
||||
}
|
||||
|
||||
EditorBuildProfileManager::EditorBuildProfileManager() {
|
||||
VBoxContainer *main_vbc = memnew(VBoxContainer);
|
||||
add_child(main_vbc);
|
||||
|
||||
HBoxContainer *path_hbc = memnew(HBoxContainer);
|
||||
profile_path = memnew(LineEdit);
|
||||
path_hbc->add_child(profile_path);
|
||||
profile_path->set_editable(true);
|
||||
profile_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
profile_actions[ACTION_NEW] = memnew(Button(TTR("New")));
|
||||
path_hbc->add_child(profile_actions[ACTION_NEW]);
|
||||
profile_actions[ACTION_NEW]->connect("pressed", callable_mp(this, &EditorBuildProfileManager::_profile_action), varray(ACTION_NEW));
|
||||
|
||||
profile_actions[ACTION_LOAD] = memnew(Button(TTR("Load")));
|
||||
path_hbc->add_child(profile_actions[ACTION_LOAD]);
|
||||
profile_actions[ACTION_LOAD]->connect("pressed", callable_mp(this, &EditorBuildProfileManager::_profile_action), varray(ACTION_LOAD));
|
||||
|
||||
profile_actions[ACTION_SAVE] = memnew(Button(TTR("Save")));
|
||||
path_hbc->add_child(profile_actions[ACTION_SAVE]);
|
||||
profile_actions[ACTION_SAVE]->connect("pressed", callable_mp(this, &EditorBuildProfileManager::_profile_action), varray(ACTION_SAVE));
|
||||
|
||||
profile_actions[ACTION_SAVE_AS] = memnew(Button(TTR("Save As")));
|
||||
path_hbc->add_child(profile_actions[ACTION_SAVE_AS]);
|
||||
profile_actions[ACTION_SAVE_AS]->connect("pressed", callable_mp(this, &EditorBuildProfileManager::_profile_action), varray(ACTION_SAVE_AS));
|
||||
|
||||
main_vbc->add_margin_child(TTR("Profile:"), path_hbc);
|
||||
|
||||
main_vbc->add_child(memnew(HSeparator));
|
||||
|
||||
HBoxContainer *profiles_hbc = memnew(HBoxContainer);
|
||||
|
||||
profile_actions[ACTION_RESET] = memnew(Button(TTR("Reset to Defaults")));
|
||||
profiles_hbc->add_child(profile_actions[ACTION_RESET]);
|
||||
profile_actions[ACTION_RESET]->connect("pressed", callable_mp(this, &EditorBuildProfileManager::_profile_action), varray(ACTION_RESET));
|
||||
|
||||
profile_actions[ACTION_DETECT] = memnew(Button(TTR("Detect from Project")));
|
||||
profiles_hbc->add_child(profile_actions[ACTION_DETECT]);
|
||||
profile_actions[ACTION_DETECT]->connect("pressed", callable_mp(this, &EditorBuildProfileManager::_profile_action), varray(ACTION_DETECT));
|
||||
|
||||
main_vbc->add_margin_child(TTR("Actions:"), profiles_hbc);
|
||||
|
||||
class_list = memnew(Tree);
|
||||
class_list->set_hide_root(true);
|
||||
class_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
|
||||
class_list->connect("cell_selected", callable_mp(this, &EditorBuildProfileManager::_class_list_item_selected));
|
||||
class_list->connect("item_edited", callable_mp(this, &EditorBuildProfileManager::_class_list_item_edited), varray(), CONNECT_DEFERRED);
|
||||
class_list->connect("item_collapsed", callable_mp(this, &EditorBuildProfileManager::_class_list_item_collapsed));
|
||||
// It will be displayed once the user creates or chooses a profile.
|
||||
main_vbc->add_margin_child(TTR("Configure Engine Build Profile:"), class_list, true);
|
||||
|
||||
description_bit = memnew(EditorHelpBit);
|
||||
description_bit->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
|
||||
main_vbc->add_margin_child(TTR("Description:"), description_bit, false);
|
||||
|
||||
confirm_dialog = memnew(ConfirmationDialog);
|
||||
add_child(confirm_dialog);
|
||||
confirm_dialog->set_title(TTR("Please Confirm:"));
|
||||
confirm_dialog->connect("confirmed", callable_mp(this, &EditorBuildProfileManager::_action_confirm));
|
||||
|
||||
import_profile = memnew(EditorFileDialog);
|
||||
add_child(import_profile);
|
||||
import_profile->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
|
||||
import_profile->add_filter("*.build", TTR("Egine Build Profile"));
|
||||
import_profile->connect("files_selected", callable_mp(this, &EditorBuildProfileManager::_import_profile));
|
||||
import_profile->set_title(TTR("Load Profile"));
|
||||
import_profile->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
|
||||
|
||||
export_profile = memnew(EditorFileDialog);
|
||||
add_child(export_profile);
|
||||
export_profile->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
|
||||
export_profile->add_filter("*.build", TTR("Egine Build Profile"));
|
||||
export_profile->connect("file_selected", callable_mp(this, &EditorBuildProfileManager::_export_profile));
|
||||
export_profile->set_title(TTR("Export Profile"));
|
||||
export_profile->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
|
||||
|
||||
force_detect_classes = memnew(LineEdit);
|
||||
main_vbc->add_margin_child(TTR("Forced classes on detect:"), force_detect_classes);
|
||||
force_detect_classes->connect("text_changed", callable_mp(this, &EditorBuildProfileManager::_force_detect_classes_changed));
|
||||
|
||||
set_title(TTR("Edit Build Configuration Profile"));
|
||||
|
||||
singleton = this;
|
||||
}
|
173
editor/editor_build_profile.h
Normal file
173
editor/editor_build_profile.h
Normal file
@ -0,0 +1,173 @@
|
||||
/*************************************************************************/
|
||||
/* editor_build_profile.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 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 EDITOR_BUILD_PROFILE_H
|
||||
#define EDITOR_BUILD_PROFILE_H
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "editor/editor_help.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class EditorBuildProfile : public RefCounted {
|
||||
GDCLASS(EditorBuildProfile, RefCounted);
|
||||
|
||||
public:
|
||||
enum BuildOption {
|
||||
BUILD_OPTION_3D,
|
||||
BUILD_OPTION_PHYSICS_2D,
|
||||
BUILD_OPTION_PHYSICS_3D,
|
||||
BUILD_OPTION_NAVIGATION,
|
||||
BUILD_OPTION_XR,
|
||||
BUILD_OPTION_RENDERING_DEVICE,
|
||||
BUILD_OPTION_OPENGL,
|
||||
BUILD_OPTION_VULKAN,
|
||||
BUILD_OPTION_MAX
|
||||
};
|
||||
|
||||
private:
|
||||
HashSet<StringName> disabled_classes;
|
||||
|
||||
HashSet<StringName> collapsed_classes;
|
||||
|
||||
String force_detect_classes;
|
||||
|
||||
bool build_options_disabled[BUILD_OPTION_MAX] = {};
|
||||
static const char *build_option_identifiers[BUILD_OPTION_MAX];
|
||||
static const bool build_option_disable_values[BUILD_OPTION_MAX];
|
||||
|
||||
String _get_build_option_name(BuildOption p_build_option) { return get_build_option_name(p_build_option); }
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_disable_class(const StringName &p_class, bool p_disabled);
|
||||
bool is_class_disabled(const StringName &p_class) const;
|
||||
|
||||
void set_item_collapsed(const StringName &p_class, bool p_collapsed);
|
||||
bool is_item_collapsed(const StringName &p_class) const;
|
||||
|
||||
void set_disable_build_option(BuildOption p_build_option, bool p_disable);
|
||||
bool is_build_option_disabled(BuildOption p_build_option) const;
|
||||
|
||||
void set_force_detect_classes(const String &p_classes);
|
||||
String get_force_detect_classes() const;
|
||||
|
||||
void clear_disabled_classes();
|
||||
|
||||
Error save_to_file(const String &p_path);
|
||||
Error load_from_file(const String &p_path);
|
||||
|
||||
static String get_build_option_name(BuildOption p_build_option);
|
||||
static String get_build_option_description(BuildOption p_build_option);
|
||||
static bool get_build_option_disable_value(BuildOption p_build_option);
|
||||
|
||||
EditorBuildProfile();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(EditorBuildProfile::BuildOption)
|
||||
|
||||
class EditorFileSystemDirectory;
|
||||
|
||||
class EditorBuildProfileManager : public AcceptDialog {
|
||||
GDCLASS(EditorBuildProfileManager, AcceptDialog);
|
||||
|
||||
enum Action {
|
||||
ACTION_NEW,
|
||||
ACTION_RESET,
|
||||
ACTION_LOAD,
|
||||
ACTION_SAVE,
|
||||
ACTION_SAVE_AS,
|
||||
ACTION_DETECT,
|
||||
ACTION_MAX
|
||||
};
|
||||
|
||||
Action last_action = ACTION_NEW;
|
||||
|
||||
ConfirmationDialog *confirm_dialog = nullptr;
|
||||
Button *profile_actions[ACTION_MAX];
|
||||
|
||||
Tree *class_list = nullptr;
|
||||
EditorHelpBit *description_bit = nullptr;
|
||||
|
||||
EditorFileDialog *import_profile = nullptr;
|
||||
EditorFileDialog *export_profile = nullptr;
|
||||
|
||||
LineEdit *profile_path = nullptr;
|
||||
|
||||
LineEdit *force_detect_classes = nullptr;
|
||||
|
||||
void _profile_action(int p_action);
|
||||
void _action_confirm();
|
||||
|
||||
void _update_edited_profile();
|
||||
void _fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected);
|
||||
|
||||
Ref<EditorBuildProfile> edited;
|
||||
|
||||
void _import_profile(const String &p_path);
|
||||
void _export_profile(const String &p_path);
|
||||
|
||||
bool updating_build_options = false;
|
||||
|
||||
void _class_list_item_selected();
|
||||
void _class_list_item_edited();
|
||||
void _class_list_item_collapsed(Object *p_item);
|
||||
void _detect_classes();
|
||||
|
||||
void _force_detect_classes_changed(const String &p_text);
|
||||
|
||||
struct DetectedFile {
|
||||
uint32_t timestamp = 0;
|
||||
String md5;
|
||||
Vector<String> classes;
|
||||
};
|
||||
|
||||
void _find_files(EditorFileSystemDirectory *p_dir, const HashMap<String, DetectedFile> &p_cache, HashMap<String, DetectedFile> &r_detected);
|
||||
|
||||
static EditorBuildProfileManager *singleton;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
Ref<EditorBuildProfile> get_current_profile();
|
||||
|
||||
static EditorBuildProfileManager *get_singleton() { return singleton; }
|
||||
EditorBuildProfileManager();
|
||||
};
|
||||
|
||||
#endif // EDITOR_BUILD_PROFILE_H
|
@ -75,6 +75,7 @@
|
||||
#include "editor/dependency_editor.h"
|
||||
#include "editor/editor_about.h"
|
||||
#include "editor/editor_audio_buses.h"
|
||||
#include "editor/editor_build_profile.h"
|
||||
#include "editor/editor_command_palette.h"
|
||||
#include "editor/editor_data.h"
|
||||
#include "editor/editor_export.h"
|
||||
@ -2805,6 +2806,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case TOOLS_BUILD_PROFILE_MANAGER: {
|
||||
build_profile_manager->popup_centered_clamped(Size2(700, 800) * EDSCALE, 0.8);
|
||||
} break;
|
||||
case RUN_USER_DATA_FOLDER: {
|
||||
// Ensure_user_data_dir() to prevent the edge case: "Open User Data Folder" won't work after the project was renamed in ProjectSettingsEditor unless the project is saved.
|
||||
OS::get_singleton()->ensure_user_data_dir();
|
||||
@ -6451,6 +6455,10 @@ EditorNode::EditorNode() {
|
||||
|
||||
feature_profile_manager = memnew(EditorFeatureProfileManager);
|
||||
gui_base->add_child(feature_profile_manager);
|
||||
|
||||
build_profile_manager = memnew(EditorBuildProfileManager);
|
||||
gui_base->add_child(build_profile_manager);
|
||||
|
||||
about = memnew(EditorAbout);
|
||||
gui_base->add_child(about);
|
||||
feature_profile_manager->connect("current_feature_profile_changed", callable_mp(this, &EditorNode::_feature_profile_changed));
|
||||
@ -6543,6 +6551,10 @@ EditorNode::EditorNode() {
|
||||
p->add_item(TTR("Install Android Build Template..."), FILE_INSTALL_ANDROID_SOURCE);
|
||||
p->add_item(TTR("Open User Data Folder"), RUN_USER_DATA_FOLDER);
|
||||
|
||||
p->add_separator();
|
||||
p->add_item(TTR("Customize Engine Build Configuration..."), TOOLS_BUILD_PROFILE_MANAGER);
|
||||
p->add_separator();
|
||||
|
||||
plugin_config_dialog = memnew(PluginConfigDialog);
|
||||
plugin_config_dialog->connect("plugin_ready", callable_mp(this, &EditorNode::_on_plugin_ready));
|
||||
gui_base->add_child(plugin_config_dialog);
|
||||
@ -7190,6 +7202,7 @@ EditorNode::EditorNode() {
|
||||
vshader_convert.instantiate();
|
||||
resource_conversion_plugins.push_back(vshader_convert);
|
||||
}
|
||||
|
||||
update_spinner_step_msec = OS::get_singleton()->get_ticks_msec();
|
||||
update_spinner_step_frame = Engine::get_singleton()->get_frames_drawn();
|
||||
|
||||
|
@ -95,6 +95,7 @@ class TabContainer;
|
||||
class TextureProgressBar;
|
||||
class VSplitContainer;
|
||||
class Window;
|
||||
class EditorBuildProfileManager;
|
||||
|
||||
class EditorNode : public Node {
|
||||
GDCLASS(EditorNode, Node);
|
||||
@ -163,6 +164,7 @@ private:
|
||||
EDIT_REDO,
|
||||
EDIT_RELOAD_SAVED_SCENE,
|
||||
TOOLS_ORPHAN_RESOURCES,
|
||||
TOOLS_BUILD_PROFILE_MANAGER,
|
||||
TOOLS_CUSTOM,
|
||||
RESOURCE_SAVE,
|
||||
RESOURCE_SAVE_AS,
|
||||
@ -377,6 +379,7 @@ private:
|
||||
EditorFileDialog *file = nullptr;
|
||||
ExportTemplateManager *export_template_manager = nullptr;
|
||||
EditorFeatureProfileManager *feature_profile_manager = nullptr;
|
||||
EditorBuildProfileManager *build_profile_manager = nullptr;
|
||||
EditorFileDialog *file_templates = nullptr;
|
||||
EditorFileDialog *file_export_lib = nullptr;
|
||||
EditorFileDialog *file_script = nullptr;
|
||||
|
@ -65,15 +65,19 @@ Error ResourceLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, Varia
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
||||
String unique_id = token.value;
|
||||
if (p_data->no_placeholders) {
|
||||
r_res.unref();
|
||||
} else {
|
||||
String unique_id = token.value;
|
||||
|
||||
if (!p_data->resource_map.has(unique_id)) {
|
||||
r_err_str = "Found unique_id reference before mapping, sub-resources stored out of order in resource file";
|
||||
return ERR_PARSE_ERROR;
|
||||
if (!p_data->resource_map.has(unique_id)) {
|
||||
r_err_str = "Found unique_id reference before mapping, sub-resources stored out of order in resource file";
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
||||
r_res = p_data->resource_map[unique_id];
|
||||
}
|
||||
|
||||
r_res = p_data->resource_map[unique_id];
|
||||
|
||||
VariantParser::get_token(p_stream, token, line, r_err_str);
|
||||
if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
|
||||
r_err_str = "Expected ')'";
|
||||
@ -91,11 +95,15 @@ Error ResourceLoaderText::_parse_ext_resource_dummy(DummyReadData *p_data, Varia
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
||||
String id = token.value;
|
||||
if (p_data->no_placeholders) {
|
||||
r_res.unref();
|
||||
} else {
|
||||
String id = token.value;
|
||||
|
||||
ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR);
|
||||
ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR);
|
||||
|
||||
r_res = p_data->rev_external_resources[id];
|
||||
r_res = p_data->rev_external_resources[id];
|
||||
}
|
||||
|
||||
VariantParser::get_token(p_stream, token, line, r_err_str);
|
||||
if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
|
||||
@ -1066,7 +1074,7 @@ static void bs_save_unicode_string(Ref<FileAccess> p_f, const String &p_string,
|
||||
p_f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
|
||||
}
|
||||
|
||||
Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_path) {
|
||||
Error ResourceLoaderText::save_as_binary(const String &p_path) {
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
@ -1271,7 +1279,7 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa
|
||||
}
|
||||
|
||||
if (next_tag.name == "node") {
|
||||
//this is a node, must save one more!
|
||||
// This is a node, must save one more!
|
||||
|
||||
if (!is_scene) {
|
||||
error_text += "found the 'node' tag on a resource file!";
|
||||
@ -1346,6 +1354,126 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error ResourceLoaderText::get_classes_used(HashSet<StringName> *r_classes) {
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
ignore_resource_parsing = true;
|
||||
|
||||
DummyReadData dummy_read;
|
||||
dummy_read.no_placeholders = true;
|
||||
VariantParser::ResourceParser rp;
|
||||
rp.ext_func = _parse_ext_resource_dummys;
|
||||
rp.sub_func = _parse_sub_resource_dummys;
|
||||
rp.userdata = &dummy_read;
|
||||
|
||||
while (next_tag.name == "ext_resource") {
|
||||
error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
|
||||
|
||||
if (error) {
|
||||
_printerr();
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
while (next_tag.name == "sub_resource" || next_tag.name == "resource") {
|
||||
if (next_tag.name == "sub_resource") {
|
||||
if (!next_tag.fields.has("type")) {
|
||||
error = ERR_FILE_CORRUPT;
|
||||
error_text = "Missing 'type' in external resource tag";
|
||||
_printerr();
|
||||
return error;
|
||||
}
|
||||
|
||||
r_classes->insert(next_tag.fields["type"]);
|
||||
|
||||
} else {
|
||||
r_classes->insert(next_tag.fields["res_type"]);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
String assign;
|
||||
Variant value;
|
||||
|
||||
error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp);
|
||||
|
||||
if (error) {
|
||||
if (error == ERR_FILE_EOF) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
_printerr();
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!assign.is_empty()) {
|
||||
continue;
|
||||
} else if (!next_tag.name.is_empty()) {
|
||||
error = OK;
|
||||
break;
|
||||
} else {
|
||||
error = ERR_FILE_CORRUPT;
|
||||
error_text = "Premature end of file while parsing [sub_resource]";
|
||||
_printerr();
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (next_tag.name == "node") {
|
||||
// This is a node, must save one more!
|
||||
|
||||
if (!is_scene) {
|
||||
error_text += "found the 'node' tag on a resource file!";
|
||||
_printerr();
|
||||
error = ERR_FILE_CORRUPT;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!next_tag.fields.has("type")) {
|
||||
error = ERR_FILE_CORRUPT;
|
||||
error_text = "Missing 'type' in external resource tag";
|
||||
_printerr();
|
||||
return error;
|
||||
}
|
||||
|
||||
r_classes->insert(next_tag.fields["type"]);
|
||||
|
||||
while (true) {
|
||||
String assign;
|
||||
Variant value;
|
||||
|
||||
error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp);
|
||||
|
||||
if (error) {
|
||||
if (error == ERR_FILE_MISSING_DEPENDENCIES) {
|
||||
// Resource loading error, just skip it.
|
||||
} else if (error != ERR_FILE_EOF) {
|
||||
_printerr();
|
||||
return error;
|
||||
} else {
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!assign.is_empty()) {
|
||||
continue;
|
||||
} else if (!next_tag.name.is_empty()) {
|
||||
error = OK;
|
||||
break;
|
||||
} else {
|
||||
error = ERR_FILE_CORRUPT;
|
||||
error_text = "Premature end of file while parsing [sub_resource]";
|
||||
_printerr();
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
String ResourceLoaderText::recognize(Ref<FileAccess> p_f) {
|
||||
error = OK;
|
||||
|
||||
@ -1473,6 +1601,26 @@ bool ResourceFormatLoaderText::handles_type(const String &p_type) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResourceFormatLoaderText::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
|
||||
String ext = p_path.get_extension().to_lower();
|
||||
if (ext == "tscn") {
|
||||
r_classes->insert("PackedScene");
|
||||
}
|
||||
|
||||
// ...for anything else must test...
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
if (f.is_null()) {
|
||||
return; // Could not read.
|
||||
}
|
||||
|
||||
ResourceLoaderText loader;
|
||||
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
|
||||
loader.res_path = loader.local_path;
|
||||
loader.open(f);
|
||||
loader.get_classes_used(r_classes);
|
||||
}
|
||||
|
||||
String ResourceFormatLoaderText::get_resource_type(const String &p_path) const {
|
||||
String ext = p_path.get_extension().to_lower();
|
||||
if (ext == "tscn") {
|
||||
@ -1561,7 +1709,7 @@ Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path,
|
||||
loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
|
||||
loader.res_path = loader.local_path;
|
||||
loader.open(f);
|
||||
return loader.save_as_binary(f, p_dst_path);
|
||||
return loader.save_as_binary(p_dst_path);
|
||||
}
|
||||
|
||||
/*****************************************************************************************************/
|
||||
|
@ -90,6 +90,7 @@ class ResourceLoaderText {
|
||||
};
|
||||
|
||||
struct DummyReadData {
|
||||
bool no_placeholders = false;
|
||||
HashMap<Ref<Resource>, int> external_resources;
|
||||
HashMap<String, Ref<Resource>> rev_external_resources;
|
||||
HashMap<Ref<Resource>, int> resource_index_map;
|
||||
@ -125,8 +126,9 @@ public:
|
||||
ResourceUID::ID get_uid(Ref<FileAccess> p_f);
|
||||
void get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types);
|
||||
Error rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map);
|
||||
Error get_classes_used(HashSet<StringName> *r_classes);
|
||||
|
||||
Error save_as_binary(Ref<FileAccess> p_f, const String &p_path);
|
||||
Error save_as_binary(const String &p_path);
|
||||
ResourceLoaderText();
|
||||
};
|
||||
|
||||
@ -137,6 +139,8 @@ public:
|
||||
virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const;
|
||||
virtual void get_recognized_extensions(List<String> *p_extensions) const;
|
||||
virtual bool handles_type(const String &p_type) const;
|
||||
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes);
|
||||
|
||||
virtual String get_resource_type(const String &p_path) const;
|
||||
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
|
||||
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
|
||||
|
Loading…
Reference in New Issue
Block a user