Refactor ProjectSetting overrides
* Overrides no longer happen for set/get. * They must be checked with a new function: `ProjectSettings::get_setting_with_override()`. * GLOBAL_DEF/GLOBAL_GET updated to use this This change solves many problems: * General confusion about getting the actual or overriden setting. * Feature tags available after settings are loaded were being ignored, they are now considered. * Hacks required for the Project Settings editor to work. Fixes #64100. Fixes #64014. Fixes #61908.
This commit is contained in:
parent
3c9bf4bc21
commit
6f0e210093
9 changed files with 65 additions and 37 deletions
|
@ -291,31 +291,26 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!disable_feature_overrides) {
|
{ // Feature overrides.
|
||||||
int dot = p_name.operator String().find(".");
|
int dot = p_name.operator String().find(".");
|
||||||
if (dot != -1) {
|
if (dot != -1) {
|
||||||
Vector<String> s = p_name.operator String().split(".");
|
Vector<String> s = p_name.operator String().split(".");
|
||||||
|
|
||||||
bool override_valid = false;
|
|
||||||
for (int i = 1; i < s.size(); i++) {
|
for (int i = 1; i < s.size(); i++) {
|
||||||
String feature = s[i].strip_edges();
|
String feature = s[i].strip_edges();
|
||||||
if (OS::get_singleton()->has_feature(feature) || custom_features.has(feature)) {
|
Pair<StringName, StringName> fo(feature, p_name);
|
||||||
override_valid = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (override_valid) {
|
if (!feature_overrides.has(s[0])) {
|
||||||
feature_overrides[s[0]] = p_name;
|
feature_overrides[s[0]] = LocalVector<Pair<StringName, StringName>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
feature_overrides[s[0]].push_back(fo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.has(p_name)) {
|
if (props.has(p_name)) {
|
||||||
if (!props[p_name].overridden) {
|
props[p_name].variant = p_value;
|
||||||
props[p_name].variant = p_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
props[p_name] = VariantContainer(p_value, last_order++);
|
props[p_name] = VariantContainer(p_value, last_order++);
|
||||||
}
|
}
|
||||||
|
@ -340,18 +335,37 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const {
|
bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const {
|
||||||
_THREAD_SAFE_METHOD_
|
_THREAD_SAFE_METHOD_
|
||||||
|
|
||||||
StringName name = p_name;
|
if (!props.has(p_name)) {
|
||||||
if (!disable_feature_overrides && feature_overrides.has(name)) {
|
WARN_PRINT("Property not found: " + String(p_name));
|
||||||
name = feature_overrides[name];
|
|
||||||
}
|
|
||||||
if (!props.has(name)) {
|
|
||||||
WARN_PRINT("Property not found: " + String(name));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
r_ret = props[name].variant;
|
r_ret = props[p_name].variant;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Variant ProjectSettings::get_setting_with_override(const StringName &p_name) const {
|
||||||
|
_THREAD_SAFE_METHOD_
|
||||||
|
|
||||||
|
StringName name = p_name;
|
||||||
|
if (feature_overrides.has(name)) {
|
||||||
|
const LocalVector<Pair<StringName, StringName>> &overrides = feature_overrides[name];
|
||||||
|
for (uint32_t i = 0; i < overrides.size(); i++) {
|
||||||
|
if (OS::get_singleton()->has_feature(overrides[i].first)) { // Custom features are checked in OS.has_feature() already. No need to check twice.
|
||||||
|
if (props.has(overrides[i].second)) {
|
||||||
|
name = overrides[i].second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!props.has(name)) {
|
||||||
|
WARN_PRINT("Property not found: " + String(name));
|
||||||
|
return Variant();
|
||||||
|
}
|
||||||
|
return props[name].variant;
|
||||||
|
}
|
||||||
|
|
||||||
struct _VCSort {
|
struct _VCSort {
|
||||||
String name;
|
String name;
|
||||||
Variant::Type type = Variant::VARIANT_MAX;
|
Variant::Type type = Variant::VARIANT_MAX;
|
||||||
|
@ -1101,10 +1115,6 @@ const HashMap<StringName, PropertyInfo> &ProjectSettings::get_custom_property_in
|
||||||
return custom_prop_info;
|
return custom_prop_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectSettings::set_disable_feature_overrides(bool p_disable) {
|
|
||||||
disable_feature_overrides = p_disable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ProjectSettings::is_using_datapack() const {
|
bool ProjectSettings::is_using_datapack() const {
|
||||||
return using_datapack;
|
return using_datapack;
|
||||||
}
|
}
|
||||||
|
@ -1169,6 +1179,7 @@ void ProjectSettings::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting);
|
ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting);
|
||||||
ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting);
|
ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting);
|
||||||
ClassDB::bind_method(D_METHOD("get_setting", "name", "default_value"), &ProjectSettings::get_setting, DEFVAL(Variant()));
|
ClassDB::bind_method(D_METHOD("get_setting", "name", "default_value"), &ProjectSettings::get_setting, DEFVAL(Variant()));
|
||||||
|
ClassDB::bind_method(D_METHOD("get_setting_with_override", "name"), &ProjectSettings::get_setting_with_override);
|
||||||
ClassDB::bind_method(D_METHOD("set_order", "name", "position"), &ProjectSettings::set_order);
|
ClassDB::bind_method(D_METHOD("set_order", "name", "position"), &ProjectSettings::set_order);
|
||||||
ClassDB::bind_method(D_METHOD("get_order", "name"), &ProjectSettings::get_order);
|
ClassDB::bind_method(D_METHOD("get_order", "name"), &ProjectSettings::get_order);
|
||||||
ClassDB::bind_method(D_METHOD("set_initial_value", "name", "value"), &ProjectSettings::set_initial_value);
|
ClassDB::bind_method(D_METHOD("set_initial_value", "name", "value"), &ProjectSettings::set_initial_value);
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "core/object/class_db.h"
|
#include "core/object/class_db.h"
|
||||||
#include "core/os/thread_safe.h"
|
#include "core/os/thread_safe.h"
|
||||||
#include "core/templates/hash_map.h"
|
#include "core/templates/hash_map.h"
|
||||||
|
#include "core/templates/local_vector.h"
|
||||||
#include "core/templates/rb_set.h"
|
#include "core/templates/rb_set.h"
|
||||||
|
|
||||||
class ProjectSettings : public Object {
|
class ProjectSettings : public Object {
|
||||||
|
@ -69,7 +70,6 @@ protected:
|
||||||
Variant variant;
|
Variant variant;
|
||||||
Variant initial;
|
Variant initial;
|
||||||
bool hide_from_editor = false;
|
bool hide_from_editor = false;
|
||||||
bool overridden = false;
|
|
||||||
bool restart_if_changed = false;
|
bool restart_if_changed = false;
|
||||||
#ifdef DEBUG_METHODS_ENABLED
|
#ifdef DEBUG_METHODS_ENABLED
|
||||||
bool ignore_value_in_docs = false;
|
bool ignore_value_in_docs = false;
|
||||||
|
@ -91,12 +91,11 @@ protected:
|
||||||
RBMap<StringName, VariantContainer> props; // NOTE: Key order is used e.g. in the save_custom method.
|
RBMap<StringName, VariantContainer> props; // NOTE: Key order is used e.g. in the save_custom method.
|
||||||
String resource_path;
|
String resource_path;
|
||||||
HashMap<StringName, PropertyInfo> custom_prop_info;
|
HashMap<StringName, PropertyInfo> custom_prop_info;
|
||||||
bool disable_feature_overrides = false;
|
|
||||||
bool using_datapack = false;
|
bool using_datapack = false;
|
||||||
List<String> input_presets;
|
List<String> input_presets;
|
||||||
|
|
||||||
HashSet<String> custom_features;
|
HashSet<String> custom_features;
|
||||||
HashMap<StringName, StringName> feature_overrides;
|
HashMap<StringName, LocalVector<Pair<StringName, StringName>>> feature_overrides;
|
||||||
|
|
||||||
HashMap<StringName, AutoloadInfo> autoloads;
|
HashMap<StringName, AutoloadInfo> autoloads;
|
||||||
|
|
||||||
|
@ -181,7 +180,7 @@ public:
|
||||||
|
|
||||||
List<String> get_input_presets() const { return input_presets; }
|
List<String> get_input_presets() const { return input_presets; }
|
||||||
|
|
||||||
void set_disable_feature_overrides(bool p_disable);
|
Variant get_setting_with_override(const StringName &p_name) const;
|
||||||
|
|
||||||
bool is_using_datapack() const;
|
bool is_using_datapack() const;
|
||||||
|
|
||||||
|
@ -205,7 +204,7 @@ Variant _GLOBAL_DEF(const PropertyInfo &p_info, const Variant &p_default, bool p
|
||||||
#define GLOBAL_DEF_RST(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true)
|
#define GLOBAL_DEF_RST(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true)
|
||||||
#define GLOBAL_DEF_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, true)
|
#define GLOBAL_DEF_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, true)
|
||||||
#define GLOBAL_DEF_RST_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, true)
|
#define GLOBAL_DEF_RST_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, true)
|
||||||
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
|
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var)
|
||||||
|
|
||||||
#define GLOBAL_DEF_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, false, true)
|
#define GLOBAL_DEF_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, false, true)
|
||||||
#define GLOBAL_DEF_RST_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, false, true)
|
#define GLOBAL_DEF_RST_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, false, true)
|
||||||
|
|
|
@ -84,6 +84,25 @@
|
||||||
GD.Print(ProjectSettings.GetSetting("application/config/custom_description", "No description specified."));
|
GD.Print(ProjectSettings.GetSetting("application/config/custom_description", "No description specified."));
|
||||||
[/csharp]
|
[/csharp]
|
||||||
[/codeblocks]
|
[/codeblocks]
|
||||||
|
[b]Note:[/b] This method doesn't take potential feature overrides into account automatically. Use [method get_setting_with_override] to handle seamlessly.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="get_setting_with_override" qualifiers="const">
|
||||||
|
<return type="Variant" />
|
||||||
|
<param index="0" name="name" type="StringName" />
|
||||||
|
<description>
|
||||||
|
Similar to [method get_setting], but applies feature tag overrides if any exists and is valid.
|
||||||
|
[b]Example:[/b]
|
||||||
|
If the following setting override exists "application/config/name.windows", and the following code is executed:
|
||||||
|
[codeblocks]
|
||||||
|
[gdscript]
|
||||||
|
print(ProjectSettings.get_setting_with_override("application/config/name"))
|
||||||
|
[/gdscript]
|
||||||
|
[csharp]
|
||||||
|
GD.Print(ProjectSettings.GetSettingWithOverride("application/config/name"));
|
||||||
|
[/csharp]
|
||||||
|
[/codeblocks]
|
||||||
|
Then the overridden setting will be returned instead if the project is running on the [i]Windows[/i] operating system.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="globalize_path" qualifiers="const">
|
<method name="globalize_path" qualifiers="const">
|
||||||
|
|
|
@ -1385,7 +1385,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
if (editor) {
|
if (editor) {
|
||||||
packed_data->set_disabled(true);
|
packed_data->set_disabled(true);
|
||||||
globals->set_disable_feature_overrides(true);
|
|
||||||
Engine::get_singleton()->set_editor_hint(true);
|
Engine::get_singleton()->set_editor_hint(true);
|
||||||
main_args.push_back("--editor");
|
main_args.push_back("--editor");
|
||||||
if (!init_windowed) {
|
if (!init_windowed) {
|
||||||
|
|
|
@ -688,7 +688,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
|
||||||
return false; // Already up to date
|
return false; // Already up to date
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
|
String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
|
||||||
|
|
||||||
if (assembly_name.is_empty()) {
|
if (assembly_name.is_empty()) {
|
||||||
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
|
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
|
||||||
|
|
|
@ -289,7 +289,7 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static String get_assembly_name() {
|
static String get_assembly_name() {
|
||||||
String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
|
String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
|
||||||
|
|
||||||
if (assembly_name.is_empty()) {
|
if (assembly_name.is_empty()) {
|
||||||
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
|
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
|
||||||
|
@ -466,7 +466,7 @@ void GDMono::_init_godot_api_hashes() {
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
bool GDMono::_load_project_assembly() {
|
bool GDMono::_load_project_assembly() {
|
||||||
String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
|
String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
|
||||||
|
|
||||||
if (assembly_name.is_empty()) {
|
if (assembly_name.is_empty()) {
|
||||||
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
|
assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
|
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// Headers for building as built-in module.
|
// Headers for building as built-in module.
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var)
|
#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// Headers for building as built-in module.
|
// Headers for building as built-in module.
|
||||||
|
|
|
@ -153,7 +153,7 @@ void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<Edito
|
||||||
const String custom_head_include = p_preset->get("html/head_include");
|
const String custom_head_include = p_preset->get("html/head_include");
|
||||||
HashMap<String, String> replaces;
|
HashMap<String, String> replaces;
|
||||||
replaces["$GODOT_URL"] = p_name + ".js";
|
replaces["$GODOT_URL"] = p_name + ".js";
|
||||||
replaces["$GODOT_PROJECT_NAME"] = ProjectSettings::get_singleton()->get_setting("application/config/name");
|
replaces["$GODOT_PROJECT_NAME"] = GLOBAL_GET("application/config/name");
|
||||||
replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include;
|
replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include;
|
||||||
replaces["$GODOT_CONFIG"] = str_config;
|
replaces["$GODOT_CONFIG"] = str_config;
|
||||||
_replace_strings(replaces, p_html);
|
_replace_strings(replaces, p_html);
|
||||||
|
@ -193,7 +193,7 @@ Error EditorExportPlatformWeb::_add_manifest_icon(const String &p_path, const St
|
||||||
}
|
}
|
||||||
|
|
||||||
Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects) {
|
Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects) {
|
||||||
String proj_name = ProjectSettings::get_singleton()->get_setting("application/config/name");
|
String proj_name = GLOBAL_GET("application/config/name");
|
||||||
if (proj_name.is_empty()) {
|
if (proj_name.is_empty()) {
|
||||||
proj_name = "Godot Game";
|
proj_name = "Godot Game";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue