2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* project_settings.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2017-07-19 22:00:46 +02:00
# include "project_settings.h"
2017-01-16 08:04:19 +01:00
2021-07-15 00:46:35 +02:00
# include "core/core_bind.h" // For Compression enum.
2018-09-11 18:13:45 +02:00
# include "core/core_string_names.h"
2020-12-07 12:31:51 +01:00
# include "core/input/input_map.h"
2022-12-25 15:08:32 +01:00
# include "core/io/config_file.h"
2021-06-11 14:51:48 +02:00
# include "core/io/dir_access.h"
# include "core/io/file_access.h"
2018-09-11 18:13:45 +02:00
# include "core/io/file_access_pack.h"
# include "core/io/marshalls.h"
# include "core/os/keyboard.h"
2023-02-03 19:37:52 +01:00
# include "core/variant/typed_array.h"
2020-11-07 23:33:38 +01:00
# include "core/variant/variant_parser.h"
2021-11-24 17:12:56 +01:00
# include "core/version.h"
2022-08-09 21:36:02 +02:00
# ifdef TOOLS_ENABLED
2021-11-24 17:12:56 +01:00
# include "modules/modules_enabled.gen.h" // For mono.
2022-08-09 21:36:02 +02:00
# endif // TOOLS_ENABLED
2018-09-11 18:13:45 +02:00
2021-10-13 22:56:18 +02:00
const String ProjectSettings : : PROJECT_DATA_DIR_NAME_SUFFIX = " godot " ;
2020-04-02 01:20:12 +02:00
ProjectSettings * ProjectSettings : : singleton = nullptr ;
2014-02-10 02:10:30 +01:00
2017-07-19 22:00:46 +02:00
ProjectSettings * ProjectSettings : : get_singleton ( ) {
2014-02-10 02:10:30 +01:00
return singleton ;
}
2021-09-10 17:32:29 +02:00
String ProjectSettings : : get_project_data_dir_name ( ) const {
2021-09-10 17:32:29 +02:00
return project_data_dir_name ;
2021-09-10 17:32:29 +02:00
}
String ProjectSettings : : get_project_data_path ( ) const {
2021-09-10 17:32:29 +02:00
return " res:// " + get_project_data_dir_name ( ) ;
2021-09-10 17:32:29 +02:00
}
2017-07-19 22:00:46 +02:00
String ProjectSettings : : get_resource_path ( ) const {
2014-02-10 02:10:30 +01:00
return resource_path ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
2021-09-10 17:32:29 +02:00
String ProjectSettings : : get_imported_files_path ( ) const {
2022-08-30 02:34:01 +02:00
return get_project_data_path ( ) . path_join ( " imported " ) ;
2021-09-10 17:32:29 +02:00
}
2020-07-06 02:55:43 +02:00
2022-08-09 21:36:02 +02:00
# ifdef TOOLS_ENABLED
2021-11-24 17:12:56 +01:00
// Returns the features that a project must have when opened with this build of Godot.
// This is used by the project manager to provide the initial_settings for config/features.
const PackedStringArray ProjectSettings : : get_required_features ( ) {
2022-11-14 18:21:06 +01:00
PackedStringArray features ;
2021-11-24 17:12:56 +01:00
features . append ( VERSION_BRANCH ) ;
# ifdef REAL_T_IS_DOUBLE
features . append ( " Double Precision " ) ;
# endif
return features ;
}
// Returns the features supported by this build of Godot. Includes all required features.
const PackedStringArray ProjectSettings : : _get_supported_features ( ) {
PackedStringArray features = get_required_features ( ) ;
# ifdef MODULE_MONO_ENABLED
features . append ( " C# " ) ;
# endif
// Allow pinning to a specific patch number or build type by marking
// them as supported. They're only used if the user adds them manually.
features . append ( VERSION_BRANCH " . " _MKSTR ( VERSION_PATCH ) ) ;
features . append ( VERSION_FULL_CONFIG ) ;
features . append ( VERSION_FULL_BUILD ) ;
2022-09-08 02:44:36 +02:00
# ifdef VULKAN_ENABLED
features . append ( " Forward Plus " ) ;
features . append ( " Mobile " ) ;
# endif
# ifdef GLES3_ENABLED
features . append ( " GL Compatibility " ) ;
# endif
2021-11-24 17:12:56 +01:00
return features ;
}
// Returns the features that this project needs but this build of Godot lacks.
const PackedStringArray ProjectSettings : : get_unsupported_features ( const PackedStringArray & p_project_features ) {
2022-11-14 18:21:06 +01:00
PackedStringArray unsupported_features ;
2021-11-24 17:12:56 +01:00
PackedStringArray supported_features = singleton - > _get_supported_features ( ) ;
for ( int i = 0 ; i < p_project_features . size ( ) ; i + + ) {
if ( ! supported_features . has ( p_project_features [ i ] ) ) {
2022-09-28 23:27:07 +02:00
// Temporary compatibility code to ease upgrade to 4.0 beta 2+.
if ( p_project_features [ i ] . begins_with ( " Vulkan " ) ) {
continue ;
}
2021-11-24 17:12:56 +01:00
unsupported_features . append ( p_project_features [ i ] ) ;
}
}
unsupported_features . sort ( ) ;
return unsupported_features ;
}
// Returns the features that both this project has and this build of Godot has, ensuring required features exist.
const PackedStringArray ProjectSettings : : _trim_to_supported_features ( const PackedStringArray & p_project_features ) {
// Remove unsupported features if present.
PackedStringArray features = PackedStringArray ( p_project_features ) ;
PackedStringArray supported_features = _get_supported_features ( ) ;
for ( int i = p_project_features . size ( ) - 1 ; i > - 1 ; i - - ) {
if ( ! supported_features . has ( p_project_features [ i ] ) ) {
features . remove_at ( i ) ;
}
}
// Add required features if not present.
PackedStringArray required_features = get_required_features ( ) ;
for ( int i = 0 ; i < required_features . size ( ) ; i + + ) {
if ( ! features . has ( required_features [ i ] ) ) {
features . append ( required_features [ i ] ) ;
}
}
features . sort ( ) ;
return features ;
}
2022-08-09 21:36:02 +02:00
# endif // TOOLS_ENABLED
2021-11-24 17:12:56 +01:00
2017-07-19 22:00:46 +02:00
String ProjectSettings : : localize_path ( const String & p_path ) const {
2023-07-11 16:07:35 +02:00
String path = p_path . simplify_path ( ) ;
if ( resource_path . is_empty ( ) | | ( path . is_absolute_path ( ) & & ! path . begins_with ( resource_path ) ) ) {
return path ;
2022-10-19 18:23:20 +02:00
}
// Check if we have a special path (like res://) or a protocol identifier.
2023-07-11 16:07:35 +02:00
int p = path . find ( " :// " ) ;
2022-10-19 18:23:20 +02:00
bool found = false ;
if ( p > 0 ) {
found = true ;
for ( int i = 0 ; i < p ; i + + ) {
2023-07-11 16:07:35 +02:00
if ( ! is_ascii_alphanumeric_char ( path [ i ] ) ) {
2022-10-19 18:23:20 +02:00
found = false ;
break ;
}
}
}
if ( found ) {
2023-07-11 16:07:35 +02:00
return path ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-03-23 10:08:58 +01:00
Ref < DirAccess > dir = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( dir - > change_dir ( path ) = = OK ) {
2014-02-10 02:10:30 +01:00
String cwd = dir - > get_current_dir ( ) ;
2017-03-05 16:44:50 +01:00
cwd = cwd . replace ( " \\ " , " / " ) ;
2014-02-10 02:10:30 +01:00
2019-05-30 16:03:12 +02:00
// Ensure that we end with a '/'.
// This is important to ensure that we do not wrongly localize the resource path
// in an absolute path that just happens to contain this string but points to a
// different folder (e.g. "/my/project" as resource_path would be contained in
// "/my/project_data", even though the latter is not part of res://.
2022-08-30 02:34:01 +02:00
// `path_join("")` is an easy way to ensure we have a trailing '/'.
const String res_path = resource_path . path_join ( " " ) ;
2019-05-30 16:03:12 +02:00
2019-07-03 09:53:52 +02:00
// DirAccess::get_current_dir() is not guaranteed to return a path that with a trailing '/',
// so we must make sure we have it as well in order to compare with 'res_path'.
2022-08-30 02:34:01 +02:00
cwd = cwd . path_join ( " " ) ;
2019-07-03 09:53:52 +02:00
2019-05-30 16:03:12 +02:00
if ( ! cwd . begins_with ( res_path ) ) {
2023-07-11 16:07:35 +02:00
return path ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
2019-05-30 16:03:12 +02:00
return cwd . replace_first ( res_path , " res:// " ) ;
2014-02-10 02:10:30 +01:00
} else {
2020-07-03 15:26:22 +02:00
int sep = path . rfind ( " / " ) ;
2014-02-10 02:10:30 +01:00
if ( sep = = - 1 ) {
2017-03-05 16:44:50 +01:00
return " res:// " + path ;
2020-05-19 15:46:49 +02:00
}
2016-05-02 04:12:30 +02:00
2014-02-10 02:10:30 +01:00
String parent = path . substr ( 0 , sep ) ;
String plocal = localize_path ( parent ) ;
2021-12-09 10:42:46 +01:00
if ( plocal . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
return " " ;
2020-05-19 15:46:49 +02:00
}
2019-12-20 06:49:01 +01:00
// Only strip the starting '/' from 'path' if its parent ('plocal') ends with '/'
if ( plocal [ plocal . length ( ) - 1 ] = = ' / ' ) {
sep + = 1 ;
}
2019-11-26 13:42:18 +01:00
return plocal + path . substr ( sep , path . size ( ) - sep ) ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-07-19 22:00:46 +02:00
void ProjectSettings : : set_initial_value ( const String & p_name , const Variant & p_value ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( ! props . has ( p_name ) , " Request for nonexistent project setting: " + p_name + " . " ) ;
2023-01-13 12:36:44 +01:00
// Duplicate so that if value is array or dictionary, changing the setting will not change the stored initial value.
props [ p_name ] . initial = p_value . duplicate ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2018-07-19 23:58:15 +02:00
void ProjectSettings : : set_restart_if_changed ( const String & p_name , bool p_restart ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( ! props . has ( p_name ) , " Request for nonexistent project setting: " + p_name + " . " ) ;
2018-07-19 23:58:15 +02:00
props [ p_name ] . restart_if_changed = p_restart ;
}
2014-02-10 02:10:30 +01:00
2021-02-17 17:44:49 +01:00
void ProjectSettings : : set_as_basic ( const String & p_name , bool p_basic ) {
ERR_FAIL_COND_MSG ( ! props . has ( p_name ) , " Request for nonexistent project setting: " + p_name + " . " ) ;
props [ p_name ] . basic = p_basic ;
}
2022-06-06 15:22:57 +02:00
void ProjectSettings : : set_as_internal ( const String & p_name , bool p_internal ) {
ERR_FAIL_COND_MSG ( ! props . has ( p_name ) , " Request for nonexistent project setting: " + p_name + " . " ) ;
props [ p_name ] . internal = p_internal ;
}
2020-05-20 21:49:39 +02:00
void ProjectSettings : : set_ignore_value_in_docs ( const String & p_name , bool p_ignore ) {
ERR_FAIL_COND_MSG ( ! props . has ( p_name ) , " Request for nonexistent project setting: " + p_name + " . " ) ;
# ifdef DEBUG_METHODS_ENABLED
props [ p_name ] . ignore_value_in_docs = p_ignore ;
# endif
}
bool ProjectSettings : : get_ignore_value_in_docs ( const String & p_name ) const {
ERR_FAIL_COND_V_MSG ( ! props . has ( p_name ) , false , " Request for nonexistent project setting: " + p_name + " . " ) ;
# ifdef DEBUG_METHODS_ENABLED
return props [ p_name ] . ignore_value_in_docs ;
# else
return false ;
# endif
}
2022-06-08 17:52:19 +02:00
void ProjectSettings : : add_hidden_prefix ( const String & p_prefix ) {
ERR_FAIL_COND_MSG ( hidden_prefixes . find ( p_prefix ) > - 1 , vformat ( " Hidden prefix '%s' already exists. " , p_prefix ) ) ;
hidden_prefixes . push_back ( p_prefix ) ;
}
2017-07-19 22:00:46 +02:00
String ProjectSettings : : globalize_path ( const String & p_path ) const {
2014-02-10 02:10:30 +01:00
if ( p_path . begins_with ( " res:// " ) ) {
2021-12-09 10:42:46 +01:00
if ( ! resource_path . is_empty ( ) ) {
2017-03-05 16:44:50 +01:00
return p_path . replace ( " res:/ " , resource_path ) ;
2020-05-19 15:46:49 +02:00
}
2014-02-10 02:10:30 +01:00
return p_path . replace ( " res:// " , " " ) ;
2017-09-10 10:33:54 +02:00
} else if ( p_path . begins_with ( " user:// " ) ) {
2017-11-17 15:25:22 +01:00
String data_dir = OS : : get_singleton ( ) - > get_user_data_dir ( ) ;
2021-12-09 10:42:46 +01:00
if ( ! data_dir . is_empty ( ) ) {
2017-09-10 10:33:54 +02:00
return p_path . replace ( " user:/ " , data_dir ) ;
2020-05-19 15:46:49 +02:00
}
2017-09-10 10:33:54 +02:00
return p_path . replace ( " user:// " , " " ) ;
}
2014-02-10 02:10:30 +01:00
return p_path ;
}
2017-07-19 22:00:46 +02:00
bool ProjectSettings : : _set ( const StringName & p_name , const Variant & p_value ) {
2014-02-10 02:10:30 +01:00
_THREAD_SAFE_METHOD_
2016-03-09 00:00:52 +01:00
2020-05-14 16:41:43 +02:00
if ( p_value . get_type ( ) = = Variant : : NIL ) {
2014-02-10 02:10:30 +01:00
props . erase ( p_name ) ;
2020-06-18 01:45:08 +02:00
if ( p_name . operator String ( ) . begins_with ( " autoload/ " ) ) {
String node_name = p_name . operator String ( ) . split ( " / " ) [ 1 ] ;
if ( autoloads . has ( node_name ) ) {
remove_autoload ( node_name ) ;
}
}
2020-05-14 16:41:43 +02:00
} else {
2017-07-19 22:00:46 +02:00
if ( p_name = = CoreStringNames : : get_singleton ( ) - > _custom_features ) {
2018-06-27 13:37:55 +02:00
Vector < String > custom_feature_array = String ( p_value ) . split ( " , " ) ;
2017-07-19 22:00:46 +02:00
for ( int i = 0 ; i < custom_feature_array . size ( ) ; i + + ) {
custom_features . insert ( custom_feature_array [ i ] ) ;
}
2022-06-14 18:33:57 +02:00
_queue_changed ( ) ;
2017-07-19 22:00:46 +02:00
return true ;
}
2023-01-13 13:16:49 +01:00
{ // Feature overrides.
2017-07-19 22:00:46 +02:00
int dot = p_name . operator String ( ) . find ( " . " ) ;
if ( dot ! = - 1 ) {
Vector < String > s = p_name . operator String ( ) . split ( " . " ) ;
for ( int i = 1 ; i < s . size ( ) ; i + + ) {
String feature = s [ i ] . strip_edges ( ) ;
2023-02-01 10:49:05 +01:00
Pair < StringName , StringName > feature_override ( feature , p_name ) ;
2023-01-13 13:16:49 +01:00
if ( ! feature_overrides . has ( s [ 0 ] ) ) {
feature_overrides [ s [ 0 ] ] = LocalVector < Pair < StringName , StringName > > ( ) ;
2017-07-19 22:00:46 +02:00
}
2023-02-01 10:49:05 +01:00
feature_overrides [ s [ 0 ] ] . push_back ( feature_override ) ;
2017-07-19 22:00:46 +02:00
}
}
}
2014-02-10 02:10:30 +01:00
if ( props . has ( p_name ) ) {
2023-01-13 13:16:49 +01:00
props [ p_name ] . variant = p_value ;
2014-02-10 02:10:30 +01:00
} else {
2017-07-18 02:05:38 +02:00
props [ p_name ] = VariantContainer ( p_value , last_order + + ) ;
2014-02-10 02:10:30 +01:00
}
2020-06-18 01:45:08 +02:00
if ( p_name . operator String ( ) . begins_with ( " autoload/ " ) ) {
String node_name = p_name . operator String ( ) . split ( " / " ) [ 1 ] ;
AutoloadInfo autoload ;
autoload . name = node_name ;
String path = p_value ;
if ( path . begins_with ( " * " ) ) {
autoload . is_singleton = true ;
autoload . path = path . substr ( 1 ) ;
} else {
autoload . path = path ;
}
add_autoload ( autoload ) ;
}
2014-02-10 02:10:30 +01:00
}
2022-06-14 18:33:57 +02:00
_queue_changed ( ) ;
2014-02-10 02:10:30 +01:00
return true ;
}
2020-05-14 14:29:06 +02:00
2017-07-19 22:00:46 +02:00
bool ProjectSettings : : _get ( const StringName & p_name , Variant & r_ret ) const {
2014-02-10 02:10:30 +01:00
_THREAD_SAFE_METHOD_
2023-01-13 13:16:49 +01:00
if ( ! props . has ( p_name ) ) {
WARN_PRINT ( " Property not found: " + String ( p_name ) ) ;
return false ;
}
r_ret = props [ p_name ] . variant ;
return true ;
}
Variant ProjectSettings : : get_setting_with_override ( const StringName & p_name ) const {
_THREAD_SAFE_METHOD_
2017-07-19 22:00:46 +02:00
StringName name = p_name ;
2023-01-13 13:16:49 +01:00
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 ;
}
}
}
2017-07-19 22:00:46 +02:00
}
2023-01-13 13:16:49 +01:00
2017-07-19 22:00:46 +02:00
if ( ! props . has ( name ) ) {
2019-11-07 09:44:15 +01:00
WARN_PRINT ( " Property not found: " + String ( name ) ) ;
2023-01-13 13:16:49 +01:00
return Variant ( ) ;
2017-01-15 01:56:22 +01:00
}
2023-01-13 13:16:49 +01:00
return props [ name ] . variant ;
2014-02-10 02:10:30 +01:00
}
struct _VCSort {
String name ;
2022-05-02 16:28:25 +02:00
Variant : : Type type = Variant : : VARIANT_MAX ;
int order = 0 ;
uint32_t flags = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
bool operator < ( const _VCSort & p_vcs ) const { return order = = p_vcs . order ? name < p_vcs . name : order < p_vcs . order ; }
2014-02-10 02:10:30 +01:00
} ;
2017-07-19 22:00:46 +02:00
void ProjectSettings : : _get_property_list ( List < PropertyInfo > * p_list ) const {
2014-02-10 02:10:30 +01:00
_THREAD_SAFE_METHOD_
2022-05-13 15:04:37 +02:00
RBSet < _VCSort > vclist ;
2016-03-09 00:00:52 +01:00
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , VariantContainer > & E : props ) {
const VariantContainer * v = & E . value ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( v - > hide_from_editor ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
_VCSort vc ;
2021-08-09 22:13:42 +02:00
vc . name = E . key ;
2017-03-05 16:44:50 +01:00
vc . order = v - > order ;
vc . type = v - > variant . get_type ( ) ;
2022-06-08 17:52:19 +02:00
bool internal = v - > internal ;
if ( ! internal ) {
for ( const String & F : hidden_prefixes ) {
if ( vc . name . begins_with ( F ) ) {
internal = true ;
break ;
}
}
}
if ( internal ) {
2017-03-05 16:44:50 +01:00
vc . flags = PROPERTY_USAGE_STORAGE ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
vc . flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2023-03-02 09:54:15 +01:00
if ( v - > internal ) {
vc . flags | = PROPERTY_USAGE_INTERNAL ;
}
2021-02-17 17:44:49 +01:00
if ( v - > basic ) {
vc . flags | = PROPERTY_USAGE_EDITOR_BASIC_SETTING ;
}
2018-07-19 23:58:15 +02:00
if ( v - > restart_if_changed ) {
vc . flags | = PROPERTY_USAGE_RESTART_IF_CHANGED ;
}
2014-02-10 02:10:30 +01:00
vclist . insert ( vc ) ;
}
2022-05-19 01:43:40 +02:00
for ( const _VCSort & E : vclist ) {
String prop_info_name = E . name ;
2017-07-19 22:00:46 +02:00
int dot = prop_info_name . find ( " . " ) ;
2021-02-17 17:44:49 +01:00
if ( dot ! = - 1 & & ! custom_prop_info . has ( prop_info_name ) ) {
2017-07-19 22:00:46 +02:00
prop_info_name = prop_info_name . substr ( 0 , dot ) ;
2020-05-14 16:41:43 +02:00
}
2017-07-19 22:00:46 +02:00
if ( custom_prop_info . has ( prop_info_name ) ) {
PropertyInfo pi = custom_prop_info [ prop_info_name ] ;
2022-05-19 01:43:40 +02:00
pi . name = E . name ;
pi . usage = E . flags ;
2017-03-05 16:44:50 +01:00
p_list - > push_back ( pi ) ;
2020-05-14 16:41:43 +02:00
} else {
2022-05-19 01:43:40 +02:00
p_list - > push_back ( PropertyInfo ( E . type , E . name , PROPERTY_HINT_NONE , " " , E . flags ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
}
2022-06-14 18:33:57 +02:00
void ProjectSettings : : _queue_changed ( ) {
if ( is_changed | | ! MessageQueue : : get_singleton ( ) | | MessageQueue : : get_singleton ( ) - > get_max_buffer_usage ( ) = = 0 ) {
return ;
}
is_changed = true ;
callable_mp ( this , & ProjectSettings : : _emit_changed ) . call_deferred ( ) ;
}
void ProjectSettings : : _emit_changed ( ) {
if ( ! is_changed ) {
return ;
}
is_changed = false ;
emit_signal ( " settings_changed " ) ;
}
2020-07-13 18:22:06 +02:00
bool ProjectSettings : : _load_resource_pack ( const String & p_pack , bool p_replace_files , int p_offset ) {
2020-05-14 16:41:43 +02:00
if ( PackedData : : get_singleton ( ) - > is_disabled ( ) ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-07-13 18:22:06 +02:00
bool ok = PackedData : : get_singleton ( ) - > add_pack ( p_pack , p_replace_files , p_offset ) = = OK ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( ! ok ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
//if data.pck is found, all directory access will be from here
DirAccess : : make_default < DirAccessPack > ( DirAccess : : ACCESS_RESOURCES ) ;
2017-03-05 16:44:50 +01:00
using_datapack = true ;
2014-02-10 02:10:30 +01:00
return true ;
}
2018-12-21 12:20:48 +01:00
void ProjectSettings : : _convert_to_last_version ( int p_from_version ) {
if ( p_from_version < = 3 ) {
2018-02-21 22:06:34 +01:00
// Converts the actions from array to dictionary (array of events to dictionary with deadzone + events)
2021-08-09 22:13:42 +02:00
for ( KeyValue < StringName , ProjectSettings : : VariantContainer > & E : props ) {
Variant value = E . value . variant ;
if ( String ( E . key ) . begins_with ( " input/ " ) & & value . get_type ( ) = = Variant : : ARRAY ) {
2018-02-21 22:06:34 +01:00
Array array = value ;
Dictionary action ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
action [ " events " ] = array ;
2021-08-09 22:13:42 +02:00
E . value . variant = action ;
2018-02-21 22:06:34 +01:00
}
}
}
}
2018-10-24 20:04:36 +02:00
/*
* This method is responsible for loading a project . godot file and / or data file
* using the following merit order :
* - If using NetworkClient , try to lookup project file or fail .
* - If - - main - pack was passed by the user ( ` p_main_pack ` ) , load it or fail .
2020-05-25 17:50:16 +02:00
* - Search for project PCKs automatically . For each step we try loading a potential
* PCK , and if it doesn ' t work , we proceed to the next step . If any step succeeds ,
* we try loading the project settings , and abort if it fails . Steps :
* o Bundled PCK in the executable .
* o [ macOS only ] PCK with same basename as the binary in the . app resource dir .
* o PCK with same basename as the binary in the binary ' s directory . We handle both
* changing the extension to ' . pck ' ( e . g . ' win_game . exe ' - > ' win_game . pck ' ) and
* appending ' . pck ' to the binary name ( e . g . ' linux_game ' - > ' linux_game . pck ' ) .
* o PCK with the same basename as the binary in the current working directory .
* Same as above for the two possible PCK file names .
2018-10-24 20:04:36 +02:00
* - On relevant platforms ( Android / iOS ) , lookup project file in OS resource path .
* If found , load it or fail .
* - Lookup project file in passed ` p_path ` ( - - path passed by the user ) , i . e . we
* are running from source code .
* If not found and ` p_upwards ` is true ( - - upwards passed by the user ) , look for
* project files in parent folders up to the system root ( used to run a game
* from command line while in a subfolder ) .
* If a project file is found , load it or fail .
* If nothing was found , error out .
*/
2021-11-22 16:10:31 +01:00
Error ProjectSettings : : _setup ( const String & p_path , const String & p_main_pack , bool p_upwards , bool p_ignore_override ) {
2022-08-26 15:01:22 +02:00
if ( ! OS : : get_singleton ( ) - > get_resource_dir ( ) . is_empty ( ) ) {
// OS will call ProjectSettings->get_resource_path which will be empty if not overridden!
// If the OS would rather use a specific location, then it will not be empty.
resource_path = OS : : get_singleton ( ) - > get_resource_dir ( ) . replace ( " \\ " , " / " ) ;
if ( ! resource_path . is_empty ( ) & & resource_path [ resource_path . length ( ) - 1 ] = = ' / ' ) {
resource_path = resource_path . substr ( 0 , resource_path . length ( ) - 1 ) ; // Chop end.
}
}
2018-10-24 20:04:36 +02:00
// Attempt with a user-defined main pack first
2014-02-10 02:10:30 +01:00
2021-12-09 10:42:46 +01:00
if ( ! p_main_pack . is_empty ( ) ) {
2014-06-28 04:21:45 +02:00
bool ok = _load_resource_pack ( p_main_pack ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( ! ok , ERR_CANT_OPEN , " Cannot open resource pack ' " + p_main_pack + " '. " ) ;
2017-04-29 17:56:51 +02:00
2018-02-19 14:53:59 +01:00
Error err = _load_settings_text_or_binary ( " res://project.godot " , " res://project.binary " ) ;
2021-11-22 16:10:31 +01:00
if ( err = = OK & & ! p_ignore_override ) {
2018-02-19 14:53:59 +01:00
// Load override from location of the main pack
// Optional, we don't mind if it fails
2022-08-30 02:34:01 +02:00
_load_settings_text ( p_main_pack . get_base_dir ( ) . path_join ( " override.cfg " ) ) ;
2014-06-28 04:21:45 +02:00
}
2018-02-19 14:53:59 +01:00
return err ;
2014-06-28 04:21:45 +02:00
}
2018-10-24 20:04:36 +02:00
String exec_path = OS : : get_singleton ( ) - > get_executable_path ( ) ;
2021-12-09 10:42:46 +01:00
if ( ! exec_path . is_empty ( ) ) {
2020-05-25 17:50:16 +02:00
// We do several tests sequentially until one succeeds to find a PCK,
2021-03-12 14:35:16 +01:00
// and if so, we attempt loading it at the end.
2019-03-25 20:56:33 +01:00
2020-05-25 17:50:16 +02:00
// Attempt with PCK bundled into executable.
bool found = _load_resource_pack ( exec_path ) ;
2019-03-25 20:56:33 +01:00
2020-05-25 17:50:16 +02:00
// Attempt with exec_name.pck.
// (This is the usual case when distributing a Godot game.)
2018-10-24 20:04:36 +02:00
String exec_dir = exec_path . get_base_dir ( ) ;
String exec_filename = exec_path . get_file ( ) ;
String exec_basename = exec_filename . get_basename ( ) ;
2020-05-25 17:50:16 +02:00
// Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle)
// or the exec path's basename + '.pck' (Windows).
// We need to test both possibilities as extensions for Linux binaries are optional
// (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck').
2014-02-10 02:10:30 +01:00
2022-07-20 08:28:22 +02:00
# ifdef MACOS_ENABLED
2020-01-10 17:02:29 +01:00
if ( ! found ) {
2020-05-25 17:50:16 +02:00
// Attempt to load PCK from macOS .app bundle resources.
2022-08-30 02:34:01 +02:00
found = _load_resource_pack ( OS : : get_singleton ( ) - > get_bundle_resource_dir ( ) . path_join ( exec_basename + " .pck " ) ) | | _load_resource_pack ( OS : : get_singleton ( ) - > get_bundle_resource_dir ( ) . path_join ( exec_filename + " .pck " ) ) ;
2020-01-10 17:02:29 +01:00
}
# endif
2019-03-25 20:56:33 +01:00
if ( ! found ) {
2020-05-25 17:50:16 +02:00
// Try to load data pack at the location of the executable.
// As mentioned above, we have two potential names to attempt.
2022-08-30 02:34:01 +02:00
found = _load_resource_pack ( exec_dir . path_join ( exec_basename + " .pck " ) ) | | _load_resource_pack ( exec_dir . path_join ( exec_filename + " .pck " ) ) ;
2020-05-25 17:50:16 +02:00
}
2020-04-16 18:41:59 +02:00
2020-05-25 17:50:16 +02:00
if ( ! found ) {
// If we couldn't find them next to the executable, we attempt
// the current working directory. Same story, two tests.
found = _load_resource_pack ( exec_basename + " .pck " ) | | _load_resource_pack ( exec_filename + " .pck " ) ;
2019-03-25 20:56:33 +01:00
}
2020-05-25 17:50:16 +02:00
// If we opened our package, try and load our project.
2017-09-08 03:39:32 +02:00
if ( found ) {
2018-02-19 14:53:59 +01:00
Error err = _load_settings_text_or_binary ( " res://project.godot " , " res://project.binary " ) ;
2021-11-22 16:10:31 +01:00
if ( err = = OK & & ! p_ignore_override ) {
2022-07-11 23:58:27 +02:00
// Load overrides from the PCK and the executable location.
// Optional, we don't mind if either fails.
_load_settings_text ( " res://override.cfg " ) ;
2022-08-30 02:34:01 +02:00
_load_settings_text ( exec_path . get_base_dir ( ) . path_join ( " override.cfg " ) ) ;
2014-02-10 02:10:30 +01:00
}
2018-02-19 14:53:59 +01:00
return err ;
2014-02-10 02:10:30 +01:00
}
}
2016-03-09 00:00:52 +01:00
2020-05-25 17:50:16 +02:00
// Try to use the filesystem for files, according to OS.
// (Only Android -when reading from pck- and iOS use this.)
2017-02-21 04:05:15 +01:00
2021-12-09 10:42:46 +01:00
if ( ! OS : : get_singleton ( ) - > get_resource_dir ( ) . is_empty ( ) ) {
2018-02-19 14:53:59 +01:00
Error err = _load_settings_text_or_binary ( " res://project.godot " , " res://project.binary " ) ;
2021-11-22 16:10:31 +01:00
if ( err = = OK & & ! p_ignore_override ) {
2020-05-25 17:50:16 +02:00
// Optional, we don't mind if it fails.
2018-02-19 14:53:59 +01:00
_load_settings_text ( " res://override.cfg " ) ;
2014-02-10 02:10:30 +01:00
}
2018-02-19 14:53:59 +01:00
return err ;
2014-02-10 02:10:30 +01:00
}
2018-10-24 20:04:36 +02:00
// Nothing was found, try to find a project file in provided path (`p_path`)
// or, if requested (`p_upwards`) in parent directories.
2014-02-10 02:10:30 +01:00
2022-03-23 10:08:58 +01:00
Ref < DirAccess > d = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
ERR_FAIL_COND_V_MSG ( d . is_null ( ) , ERR_CANT_CREATE , " Cannot create DirAccess for path ' " + p_path + " '. " ) ;
2017-02-21 04:05:15 +01:00
d - > change_dir ( p_path ) ;
2015-11-14 22:43:14 +01:00
2017-02-21 04:05:15 +01:00
String current_dir = d - > get_current_dir ( ) ;
bool found = false ;
2018-02-19 14:53:59 +01:00
Error err ;
2015-11-14 22:43:14 +01:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2021-04-07 19:32:26 +02:00
// Set the resource path early so things can be resolved when loading.
resource_path = current_dir ;
resource_path = resource_path . replace ( " \\ " , " / " ) ; // Windows path to Unix path just in case.
2022-08-30 02:34:01 +02:00
err = _load_settings_text_or_binary ( current_dir . path_join ( " project.godot " ) , current_dir . path_join ( " project.binary " ) ) ;
2021-11-22 16:10:31 +01:00
if ( err = = OK & & ! p_ignore_override ) {
2020-05-25 17:50:16 +02:00
// Optional, we don't mind if it fails.
2022-08-30 02:34:01 +02:00
_load_settings_text ( current_dir . path_join ( " override.cfg " ) ) ;
2017-03-05 16:44:50 +01:00
found = true ;
2017-02-21 04:05:15 +01:00
break ;
2014-02-10 02:10:30 +01:00
}
2017-10-10 14:45:54 +02:00
if ( p_upwards ) {
2018-10-24 20:04:36 +02:00
// Try to load settings ascending through parent directories
2017-10-10 14:45:54 +02:00
d - > change_dir ( " .. " ) ;
2020-05-14 16:41:43 +02:00
if ( d - > get_current_dir ( ) = = current_dir ) {
2018-10-24 20:04:36 +02:00
break ; // not doing anything useful
2020-05-14 16:41:43 +02:00
}
2017-10-10 14:45:54 +02:00
current_dir = d - > get_current_dir ( ) ;
} else {
break ;
}
2017-02-21 04:05:15 +01:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( ! found ) {
2018-02-19 14:53:59 +01:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( resource_path . length ( ) & & resource_path [ resource_path . length ( ) - 1 ] = = ' / ' ) {
2020-05-25 17:50:16 +02:00
resource_path = resource_path . substr ( 0 , resource_path . length ( ) - 1 ) ; // Chop end.
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
return OK ;
}
2021-11-22 16:10:31 +01:00
Error ProjectSettings : : setup ( const String & p_path , const String & p_main_pack , bool p_upwards , bool p_ignore_override ) {
Error err = _setup ( p_path , p_main_pack , p_upwards , p_ignore_override ) ;
2023-05-25 06:30:37 +02:00
if ( err = = OK & & ! p_ignore_override ) {
2022-10-20 15:43:17 +02:00
String custom_settings = GLOBAL_GET ( " application/config/project_settings_override " ) ;
2021-12-09 10:42:46 +01:00
if ( ! custom_settings . is_empty ( ) ) {
2018-11-18 14:56:21 +01:00
_load_settings_text ( custom_settings ) ;
}
}
2021-09-10 17:32:29 +02:00
// Updating the default value after the project settings have loaded.
2021-10-13 22:56:18 +02:00
bool use_hidden_directory = GLOBAL_GET ( " application/config/use_hidden_project_data_directory " ) ;
project_data_dir_name = ( use_hidden_directory ? " . " : " " ) + PROJECT_DATA_DIR_NAME_SUFFIX ;
2021-09-10 17:32:29 +02:00
2020-07-13 13:38:35 +02:00
// Using GLOBAL_GET on every block for compressing can be slow, so assigning here.
Compression : : zstd_long_distance_matching = GLOBAL_GET ( " compression/formats/zstd/long_distance_matching " ) ;
Compression : : zstd_level = GLOBAL_GET ( " compression/formats/zstd/compression_level " ) ;
Compression : : zstd_window_log_size = GLOBAL_GET ( " compression/formats/zstd/window_log_size " ) ;
Compression : : zlib_level = GLOBAL_GET ( " compression/formats/zlib/compression_level " ) ;
Compression : : gzip_level = GLOBAL_GET ( " compression/formats/gzip/compression_level " ) ;
2018-11-18 14:56:21 +01:00
2023-02-19 21:05:16 +01:00
project_loaded = err = = OK ;
2018-11-18 14:56:21 +01:00
return err ;
}
2017-10-05 20:34:34 +02:00
bool ProjectSettings : : has_setting ( String p_var ) const {
2014-02-10 02:10:30 +01:00
_THREAD_SAFE_METHOD_
2022-03-07 12:33:07 +01:00
return props . has ( p_var ) ;
2014-02-10 02:10:30 +01:00
}
2019-07-10 11:54:12 +02:00
Error ProjectSettings : : _load_settings_binary ( const String & p_path ) {
2014-02-10 02:10:30 +01:00
Error err ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
2017-03-05 16:44:50 +01:00
if ( err ! = OK ) {
2014-02-10 02:10:30 +01:00
return err ;
}
uint8_t hdr [ 4 ] ;
2017-03-05 16:44:50 +01:00
f - > get_buffer ( hdr , 4 ) ;
2022-03-23 10:08:58 +01:00
ERR_FAIL_COND_V_MSG ( ( hdr [ 0 ] ! = ' E ' | | hdr [ 1 ] ! = ' C ' | | hdr [ 2 ] ! = ' F ' | | hdr [ 3 ] ! = ' G ' ) , ERR_FILE_CORRUPT , " Corrupted header in binary project.binary (not ECFG). " ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
uint32_t count = f - > get_32 ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( uint32_t i = 0 ; i < count ; i + + ) {
uint32_t slen = f - > get_32 ( ) ;
2014-02-10 02:10:30 +01:00
CharString cs ;
2017-03-05 16:44:50 +01:00
cs . resize ( slen + 1 ) ;
cs [ slen ] = 0 ;
f - > get_buffer ( ( uint8_t * ) cs . ptr ( ) , slen ) ;
2014-02-10 02:10:30 +01:00
String key ;
key . parse_utf8 ( cs . ptr ( ) ) ;
2017-03-05 16:44:50 +01:00
uint32_t vlen = f - > get_32 ( ) ;
2014-02-10 02:10:30 +01:00
Vector < uint8_t > d ;
d . resize ( vlen ) ;
2017-11-25 04:07:54 +01:00
f - > get_buffer ( d . ptrw ( ) , vlen ) ;
2014-02-10 02:10:30 +01:00
Variant value ;
2020-04-02 01:20:12 +02:00
err = decode_variant ( value , d . ptr ( ) , d . size ( ) , nullptr , true ) ;
2019-08-15 04:57:49 +02:00
ERR_CONTINUE_MSG ( err ! = OK , " Error decoding property: " + key + " . " ) ;
2017-03-05 16:44:50 +01:00
set ( key , value ) ;
2014-02-10 02:10:30 +01:00
}
return OK ;
}
2018-02-19 14:53:59 +01:00
2019-07-10 11:54:12 +02:00
Error ProjectSettings : : _load_settings_text ( const String & p_path ) {
2014-02-10 02:10:30 +01:00
Error err ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
2014-02-10 02:10:30 +01:00
2022-03-23 10:08:58 +01:00
if ( f . is_null ( ) ) {
2018-02-21 09:07:47 +01:00
// FIXME: Above 'err' error code is ERR_FILE_CANT_OPEN if the file is missing
// This needs to be streamlined if we want decent error reporting
return ERR_FILE_NOT_FOUND ;
}
2014-02-10 02:10:30 +01:00
2017-01-11 20:15:40 +01:00
VariantParser : : StreamFile stream ;
2017-03-05 16:44:50 +01:00
stream . f = f ;
2014-02-10 02:10:30 +01:00
2017-01-11 20:15:40 +01:00
String assign ;
Variant value ;
VariantParser : : Tag next_tag ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int lines = 0 ;
2017-01-11 20:15:40 +01:00
String error_text ;
String section ;
2018-12-21 12:20:48 +01:00
int config_version = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( true ) {
assign = Variant ( ) ;
2017-01-11 20:15:40 +01:00
next_tag . fields . clear ( ) ;
2017-03-05 16:44:50 +01:00
next_tag . name = String ( ) ;
2014-02-10 02:10:30 +01:00
2020-04-02 01:20:12 +02:00
err = VariantParser : : parse_tag_assign_eof ( & stream , lines , error_text , next_tag , assign , value , nullptr , true ) ;
2017-03-05 16:44:50 +01:00
if ( err = = ERR_FILE_EOF ) {
2018-12-21 12:20:48 +01:00
// If we're loading a project.godot from source code, we can operate some
// ProjectSettings conversions if need be.
_convert_to_last_version ( config_version ) ;
2022-08-30 02:34:01 +02:00
last_save_time = FileAccess : : get_modified_time ( get_resource_path ( ) . path_join ( " project.godot " ) ) ;
2017-01-11 20:15:40 +01:00
return OK ;
2014-02-10 02:10:30 +01:00
}
2022-03-23 10:08:58 +01:00
ERR_FAIL_COND_V_MSG ( err ! = OK , err , " Error parsing " + p_path + " at line " + itos ( lines ) + " : " + error_text + " File might be corrupted. " ) ;
2014-02-10 02:10:30 +01:00
2021-12-09 10:42:46 +01:00
if ( ! assign . is_empty ( ) ) {
if ( section . is_empty ( ) & & assign = = " config_version " ) {
2018-12-21 12:20:48 +01:00
config_version = value ;
2022-03-23 10:08:58 +01:00
ERR_FAIL_COND_V_MSG ( config_version > CONFIG_VERSION , ERR_FILE_CANT_OPEN , vformat ( " Can't open project at '%s', its `config_version` (%d) is from a more recent and incompatible version of the engine. Expected config version: %d. " , p_path , config_version , CONFIG_VERSION ) ) ;
2017-07-26 15:28:54 +02:00
} else {
2021-12-09 10:42:46 +01:00
if ( section . is_empty ( ) ) {
2018-07-16 00:29:00 +02:00
set ( assign , value ) ;
} else {
set ( section + " / " + assign , value ) ;
}
2017-02-05 01:02:52 +01:00
}
2021-12-09 10:42:46 +01:00
} else if ( ! next_tag . name . is_empty ( ) ) {
2017-03-05 16:44:50 +01:00
section = next_tag . name ;
2017-01-11 20:15:40 +01:00
}
2014-02-10 02:10:30 +01:00
}
}
2019-07-10 11:54:12 +02:00
Error ProjectSettings : : _load_settings_text_or_binary ( const String & p_text_path , const String & p_bin_path ) {
2020-10-22 00:12:18 +02:00
// Attempt first to load the binary project.godot file.
Error err = _load_settings_binary ( p_bin_path ) ;
if ( err = = OK ) {
return OK ;
} else if ( err ! = ERR_FILE_NOT_FOUND ) {
// If the file exists but can't be loaded, we want to know it.
ERR_PRINT ( " Couldn't load file ' " + p_bin_path + " ', error code " + itos ( err ) + " . " ) ;
}
// Fallback to text-based project.godot file if binary was not found.
err = _load_settings_text ( p_text_path ) ;
if ( err = = OK ) {
2018-02-19 14:53:59 +01:00
return OK ;
2020-10-22 00:12:18 +02:00
} else if ( err ! = ERR_FILE_NOT_FOUND ) {
ERR_PRINT ( " Couldn't load file ' " + p_text_path + " ', error code " + itos ( err ) + " . " ) ;
2018-02-19 14:53:59 +01:00
}
2020-10-22 00:12:18 +02:00
return err ;
2018-02-19 14:53:59 +01:00
}
2021-11-24 17:12:56 +01:00
Error ProjectSettings : : load_custom ( const String & p_path ) {
if ( p_path . ends_with ( " .binary " ) ) {
return _load_settings_binary ( p_path ) ;
}
return _load_settings_text ( p_path ) ;
}
2017-07-19 22:00:46 +02:00
int ProjectSettings : : get_order ( const String & p_name ) const {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( ! props . has ( p_name ) , - 1 , " Request for nonexistent project setting: " + p_name + " . " ) ;
2014-02-10 02:10:30 +01:00
return props [ p_name ] . order ;
}
2017-07-19 22:00:46 +02:00
void ProjectSettings : : set_order ( const String & p_name , int p_order ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( ! props . has ( p_name ) , " Request for nonexistent project setting: " + p_name + " . " ) ;
2017-03-05 16:44:50 +01:00
props [ p_name ] . order = p_order ;
2014-02-10 02:10:30 +01:00
}
2017-07-19 22:00:46 +02:00
void ProjectSettings : : set_builtin_order ( const String & p_name ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( ! props . has ( p_name ) , " Request for nonexistent project setting: " + p_name + " . " ) ;
2017-07-18 02:05:38 +02:00
if ( props [ p_name ] . order > = NO_BUILTIN_ORDER_BASE ) {
props [ p_name ] . order = last_builtin_order + + ;
}
}
2020-07-15 18:33:34 +02:00
bool ProjectSettings : : is_builtin_setting ( const String & p_name ) const {
// Return true because a false negative is worse than a false positive.
ERR_FAIL_COND_V_MSG ( ! props . has ( p_name ) , true , " Request for nonexistent project setting: " + p_name + " . " ) ;
return props [ p_name ] . order < NO_BUILTIN_ORDER_BASE ;
}
2017-07-19 22:00:46 +02:00
void ProjectSettings : : clear ( const String & p_name ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( ! props . has ( p_name ) , " Request for nonexistent project setting: " + p_name + " . " ) ;
2014-02-10 02:10:30 +01:00
props . erase ( p_name ) ;
}
2017-07-19 22:00:46 +02:00
Error ProjectSettings : : save ( ) {
2022-08-30 02:34:01 +02:00
Error error = save_custom ( get_resource_path ( ) . path_join ( " project.godot " ) ) ;
2021-01-17 01:09:17 +01:00
if ( error = = OK ) {
2022-08-30 02:34:01 +02:00
last_save_time = FileAccess : : get_modified_time ( get_resource_path ( ) . path_join ( " project.godot " ) ) ;
2021-01-17 01:09:17 +01:00
}
return error ;
2014-02-10 02:10:30 +01:00
}
2022-09-29 11:53:28 +02:00
Error ProjectSettings : : _save_settings_binary ( const String & p_file , const RBMap < String , List < String > > & p_props , const CustomMap & p_custom , const String & p_custom_features ) {
2014-02-10 02:10:30 +01:00
Error err ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > file = FileAccess : : open ( p_file , FileAccess : : WRITE , & err ) ;
2019-08-15 04:57:49 +02:00
ERR_FAIL_COND_V_MSG ( err ! = OK , err , " Couldn't save project.binary at " + p_file + " . " ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
uint8_t hdr [ 4 ] = { ' E ' , ' C ' , ' F ' , ' G ' } ;
file - > store_buffer ( hdr , 4 ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int count = 0 ;
2014-02-10 02:10:30 +01:00
2022-09-29 11:53:28 +02:00
for ( const KeyValue < String , List < String > > & E : p_props ) {
2021-08-09 22:13:42 +02:00
count + = E . value . size ( ) ;
2014-02-10 02:10:30 +01:00
}
2021-12-09 10:42:46 +01:00
if ( ! p_custom_features . is_empty ( ) ) {
2017-07-19 22:00:46 +02:00
file - > store_32 ( count + 1 ) ;
//store how many properties are saved, add one for custom featuers, which must always go first
String key = CoreStringNames : : get_singleton ( ) - > _custom_features ;
2021-06-16 10:32:03 +02:00
file - > store_pascal_string ( key ) ;
2017-07-19 22:00:46 +02:00
int len ;
2020-04-02 01:20:12 +02:00
err = encode_variant ( p_custom_features , nullptr , len , false ) ;
2022-03-23 10:08:58 +01:00
ERR_FAIL_COND_V ( err ! = OK , err ) ;
2017-07-19 22:00:46 +02:00
Vector < uint8_t > buff ;
buff . resize ( len ) ;
2019-03-26 16:52:42 +01:00
err = encode_variant ( p_custom_features , buff . ptrw ( ) , len , false ) ;
2022-03-23 10:08:58 +01:00
ERR_FAIL_COND_V ( err ! = OK , err ) ;
2017-07-19 22:00:46 +02:00
file - > store_32 ( len ) ;
file - > store_buffer ( buff . ptr ( ) , buff . size ( ) ) ;
} else {
file - > store_32 ( count ) ; //store how many properties are saved
}
2014-02-10 02:10:30 +01:00
2022-09-29 11:53:28 +02:00
for ( const KeyValue < String , List < String > > & E : p_props ) {
2022-05-13 15:04:37 +02:00
for ( const String & key : E . value ) {
String k = key ;
if ( ! E . key . is_empty ( ) ) {
k = E . key + " / " + k ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
Variant value ;
2022-05-13 15:04:37 +02:00
if ( p_custom . has ( k ) ) {
value = p_custom [ k ] ;
2020-05-14 16:41:43 +02:00
} else {
2022-05-13 15:04:37 +02:00
value = get ( k ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-05-13 15:04:37 +02:00
file - > store_pascal_string ( k ) ;
2014-02-10 02:10:30 +01:00
int len ;
2020-04-02 01:20:12 +02:00
err = encode_variant ( value , nullptr , len , true ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( err ! = OK , ERR_INVALID_DATA , " Error when trying to encode Variant. " ) ;
2014-02-10 02:10:30 +01:00
Vector < uint8_t > buff ;
buff . resize ( len ) ;
2019-04-15 15:30:54 +02:00
err = encode_variant ( value , buff . ptrw ( ) , len , true ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( err ! = OK , ERR_INVALID_DATA , " Error when trying to encode Variant. " ) ;
2014-02-10 02:10:30 +01:00
file - > store_32 ( len ) ;
2017-03-05 16:44:50 +01:00
file - > store_buffer ( buff . ptr ( ) , buff . size ( ) ) ;
2014-02-10 02:10:30 +01:00
}
}
return OK ;
}
2022-09-29 11:53:28 +02:00
Error ProjectSettings : : _save_settings_text ( const String & p_file , const RBMap < String , List < String > > & p_props , const CustomMap & p_custom , const String & p_custom_features ) {
2014-02-10 02:10:30 +01:00
Error err ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > file = FileAccess : : open ( p_file , FileAccess : : WRITE , & err ) ;
2014-02-10 02:10:30 +01:00
2019-08-15 04:57:49 +02:00
ERR_FAIL_COND_V_MSG ( err ! = OK , err , " Couldn't save project.godot - " + p_file + " . " ) ;
2014-02-10 02:10:30 +01:00
2017-07-26 15:28:54 +02:00
file - > store_line ( " ; Engine configuration file. " ) ;
file - > store_line ( " ; It's best edited using the editor UI and not directly, " ) ;
file - > store_line ( " ; since the parameters that go here are not all obvious. " ) ;
2017-10-09 08:34:32 +02:00
file - > store_line ( " ; " ) ;
file - > store_line ( " ; Format: " ) ;
2017-07-26 15:28:54 +02:00
file - > store_line ( " ; [section] ; section goes between [] " ) ;
file - > store_line ( " ; param=value ; assign values to parameters " ) ;
file - > store_line ( " " ) ;
2018-12-21 12:20:48 +01:00
file - > store_string ( " config_version= " + itos ( CONFIG_VERSION ) + " \n " ) ;
2021-12-09 10:42:46 +01:00
if ( ! p_custom_features . is_empty ( ) ) {
2017-07-25 21:02:38 +02:00
file - > store_string ( " custom_features= \" " + p_custom_features + " \" \n " ) ;
2020-05-14 16:41:43 +02:00
}
2017-07-25 21:02:38 +02:00
file - > store_string ( " \n " ) ;
2017-02-05 01:02:52 +01:00
2022-09-29 11:53:28 +02:00
for ( const KeyValue < String , List < String > > & E : p_props ) {
if ( E . key ! = p_props . begin ( ) - > key ) {
2014-02-10 02:10:30 +01:00
file - > store_string ( " \n " ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2022-05-13 15:04:37 +02:00
if ( ! E . key . is_empty ( ) ) {
file - > store_string ( " [ " + E . key + " ] \n \n " ) ;
2020-05-14 16:41:43 +02:00
}
2022-05-13 15:04:37 +02:00
for ( const String & F : E . value ) {
2021-07-16 05:45:57 +02:00
String key = F ;
2022-05-13 15:04:37 +02:00
if ( ! E . key . is_empty ( ) ) {
key = E . key + " / " + key ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
Variant value ;
2020-05-14 16:41:43 +02:00
if ( p_custom . has ( key ) ) {
2017-03-05 16:44:50 +01:00
value = p_custom [ key ] ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
value = get ( key ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-01-11 20:15:40 +01:00
String vstr ;
2017-03-05 16:44:50 +01:00
VariantWriter : : write_to_string ( value , vstr ) ;
2021-07-16 05:45:57 +02:00
file - > store_string ( F . property_name_encode ( ) + " = " + vstr + " \n " ) ;
2014-02-10 02:10:30 +01:00
}
}
return OK ;
}
2015-06-30 16:28:43 +02:00
2017-07-19 22:00:46 +02:00
Error ProjectSettings : : _save_custom_bnd ( const String & p_file ) { // add other params as dictionary and array?
2015-06-30 16:28:43 +02:00
return save_custom ( p_file ) ;
2020-05-19 15:46:49 +02:00
}
2015-06-30 16:28:43 +02:00
2023-03-06 21:39:24 +01:00
# ifdef TOOLS_ENABLED
bool _csproj_exists ( String p_root_dir ) {
Ref < DirAccess > dir = DirAccess : : open ( p_root_dir ) ;
2023-09-26 04:07:43 +02:00
ERR_FAIL_COND_V ( dir . is_null ( ) , false ) ;
2023-03-06 21:39:24 +01:00
dir - > list_dir_begin ( ) ;
String file_name = dir - > _get_next ( ) ;
while ( file_name ! = " " ) {
if ( ! dir - > current_is_dir ( ) & & file_name . get_extension ( ) = = " csproj " ) {
return true ;
}
file_name = dir - > _get_next ( ) ;
}
return false ;
}
# endif // TOOLS_ENABLED
2017-07-26 15:28:54 +02:00
Error ProjectSettings : : save_custom ( const String & p_path , const CustomMap & p_custom , const Vector < String > & p_custom_features , bool p_merge_with_current ) {
2021-12-09 10:42:46 +01:00
ERR_FAIL_COND_V_MSG ( p_path . is_empty ( ) , ERR_INVALID_PARAMETER , " Project settings save path cannot be empty. " ) ;
2014-02-10 02:10:30 +01:00
2022-08-09 21:36:02 +02:00
# ifdef TOOLS_ENABLED
2022-06-06 15:22:57 +02:00
PackedStringArray project_features = get_setting ( " application/config/features " ) ;
2021-11-25 08:45:33 +01:00
// If there is no feature list currently present, force one to generate.
if ( project_features . is_empty ( ) ) {
project_features = ProjectSettings : : get_required_features ( ) ;
}
// Check the rendering API.
2022-09-08 02:44:36 +02:00
const String rendering_api = has_setting ( " rendering/renderer/rendering_method " ) ? ( String ) get_setting ( " rendering/renderer/rendering_method " ) : String ( ) ;
2021-12-09 10:42:46 +01:00
if ( ! rendering_api . is_empty ( ) ) {
2021-11-25 08:45:33 +01:00
// Add the rendering API as a project feature if it doesn't already exist.
if ( ! project_features . has ( rendering_api ) ) {
project_features . append ( rendering_api ) ;
}
}
// Check for the existence of a csproj file.
2023-03-06 21:39:24 +01:00
if ( _csproj_exists ( get_resource_path ( ) ) ) {
2021-11-25 08:45:33 +01:00
// If there is a csproj file, add the C# feature if it doesn't already exist.
if ( ! project_features . has ( " C# " ) ) {
project_features . append ( " C# " ) ;
}
} else {
// If there isn't a csproj file, remove the C# feature if it exists.
if ( project_features . has ( " C# " ) ) {
project_features . remove_at ( project_features . find ( " C# " ) ) ;
}
}
project_features = _trim_to_supported_features ( project_features ) ;
2021-11-25 15:23:42 +01:00
set_setting ( " application/config/features " , project_features ) ;
2022-08-09 21:36:02 +02:00
# endif // TOOLS_ENABLED
2021-11-25 08:45:33 +01:00
2022-05-13 15:04:37 +02:00
RBSet < _VCSort > vclist ;
2014-02-10 02:10:30 +01:00
2017-07-26 15:28:54 +02:00
if ( p_merge_with_current ) {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , VariantContainer > & G : props ) {
const VariantContainer * v = & G . value ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( v - > hide_from_editor ) {
2017-07-26 15:28:54 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2021-08-09 22:13:42 +02:00
if ( p_custom . has ( G . key ) ) {
2017-07-26 15:28:54 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-07-26 15:28:54 +02:00
_VCSort vc ;
2021-08-09 22:13:42 +02:00
vc . name = G . key ; //*k;
2017-07-26 15:28:54 +02:00
vc . order = v - > order ;
vc . type = v - > variant . get_type ( ) ;
vc . flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE ;
2020-05-14 16:41:43 +02:00
if ( v - > variant = = v - > initial ) {
2017-07-26 15:28:54 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-07-26 15:28:54 +02:00
vclist . insert ( vc ) ;
}
2014-02-10 02:10:30 +01:00
}
2021-08-09 22:13:42 +02:00
for ( const KeyValue < String , Variant > & E : p_custom ) {
2017-07-26 15:28:54 +02:00
// Lookup global prop to store in the same order
2022-05-17 10:53:59 +02:00
RBMap < StringName , VariantContainer > : : Iterator global_prop = props . find ( E . key ) ;
2017-07-26 15:28:54 +02:00
2014-02-10 02:10:30 +01:00
_VCSort vc ;
2021-08-09 22:13:42 +02:00
vc . name = E . key ;
2022-05-13 15:04:37 +02:00
vc . order = global_prop ? global_prop - > value . order : 0xFFFFFFF ;
2021-08-09 22:13:42 +02:00
vc . type = E . value . get_type ( ) ;
2017-03-05 16:44:50 +01:00
vc . flags = PROPERTY_USAGE_STORAGE ;
2014-02-10 02:10:30 +01:00
vclist . insert ( vc ) ;
}
2022-09-29 11:53:28 +02:00
RBMap < String , List < String > > save_props ;
2014-02-10 02:10:30 +01:00
2022-05-19 01:43:40 +02:00
for ( const _VCSort & E : vclist ) {
String category = E . name ;
String name = E . name ;
2014-02-10 02:10:30 +01:00
int div = category . find ( " / " ) ;
2020-05-14 16:41:43 +02:00
if ( div < 0 ) {
2017-03-05 16:44:50 +01:00
category = " " ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
category = category . substr ( 0 , div ) ;
name = name . substr ( div + 1 , name . size ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2022-09-29 11:53:28 +02:00
save_props [ category ] . push_back ( name ) ;
2014-02-10 02:10:30 +01:00
}
2022-09-29 11:53:28 +02:00
String save_features ;
2017-07-19 22:00:46 +02:00
for ( int i = 0 ; i < p_custom_features . size ( ) ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( i > 0 ) {
2022-09-29 11:53:28 +02:00
save_features + = " , " ;
2020-05-14 16:41:43 +02:00
}
2017-07-19 22:00:46 +02:00
String f = p_custom_features [ i ] . strip_edges ( ) . replace ( " \" " , " " ) ;
2022-09-29 11:53:28 +02:00
save_features + = f ;
2017-07-19 22:00:46 +02:00
}
2021-02-24 16:37:29 +01:00
if ( p_path . ends_with ( " .godot " ) | | p_path . ends_with ( " override.cfg " ) ) {
2022-09-29 11:53:28 +02:00
return _save_settings_text ( p_path , save_props , p_custom , save_features ) ;
2020-05-14 16:41:43 +02:00
} else if ( p_path . ends_with ( " .binary " ) ) {
2022-09-29 11:53:28 +02:00
return _save_settings_binary ( p_path , save_props , p_custom , save_features ) ;
2020-05-14 16:41:43 +02:00
} else {
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( ERR_FILE_UNRECOGNIZED , " Unknown config file format: " + p_path + " . " ) ;
2014-02-10 02:10:30 +01:00
}
}
2022-06-06 15:22:57 +02:00
Variant _GLOBAL_DEF ( const String & p_var , const Variant & p_default , bool p_restart_if_changed , bool p_ignore_value_in_docs , bool p_basic , bool p_internal ) {
2017-07-18 02:05:38 +02:00
Variant ret ;
2018-02-25 21:05:40 +01:00
if ( ! ProjectSettings : : get_singleton ( ) - > has_setting ( p_var ) ) {
2017-07-19 22:00:46 +02:00
ProjectSettings : : get_singleton ( ) - > set ( p_var , p_default ) ;
2017-01-05 13:16:00 +01:00
}
2022-10-18 16:43:37 +02:00
ret = GLOBAL_GET ( p_var ) ;
2018-02-25 21:05:40 +01:00
2017-07-19 22:00:46 +02:00
ProjectSettings : : get_singleton ( ) - > set_initial_value ( p_var , p_default ) ;
ProjectSettings : : get_singleton ( ) - > set_builtin_order ( p_var ) ;
2021-02-17 17:44:49 +01:00
ProjectSettings : : get_singleton ( ) - > set_as_basic ( p_var , p_basic ) ;
2018-07-19 23:58:15 +02:00
ProjectSettings : : get_singleton ( ) - > set_restart_if_changed ( p_var , p_restart_if_changed ) ;
2020-05-20 21:49:39 +02:00
ProjectSettings : : get_singleton ( ) - > set_ignore_value_in_docs ( p_var , p_ignore_value_in_docs ) ;
2022-06-06 15:22:57 +02:00
ProjectSettings : : get_singleton ( ) - > set_as_internal ( p_var , p_internal ) ;
2017-07-18 02:05:38 +02:00
return ret ;
2014-02-10 02:10:30 +01:00
}
2022-11-08 19:53:22 +01:00
Variant _GLOBAL_DEF ( const PropertyInfo & p_info , const Variant & p_default , bool p_restart_if_changed , bool p_ignore_value_in_docs , bool p_basic , bool p_internal ) {
Variant ret = _GLOBAL_DEF ( p_info . name , p_default , p_restart_if_changed , p_ignore_value_in_docs , p_basic , p_internal ) ;
ProjectSettings : : get_singleton ( ) - > set_custom_property_info ( p_info ) ;
return ret ;
}
2017-07-19 22:00:46 +02:00
void ProjectSettings : : _add_property_info_bind ( const Dictionary & p_info ) {
2016-08-16 22:10:53 +02:00
ERR_FAIL_COND ( ! p_info . has ( " name " ) ) ;
ERR_FAIL_COND ( ! p_info . has ( " type " ) ) ;
PropertyInfo pinfo ;
pinfo . name = p_info [ " name " ] ;
ERR_FAIL_COND ( ! props . has ( pinfo . name ) ) ;
pinfo . type = Variant : : Type ( p_info [ " type " ] . operator int ( ) ) ;
ERR_FAIL_INDEX ( pinfo . type , Variant : : VARIANT_MAX ) ;
2020-05-14 16:41:43 +02:00
if ( p_info . has ( " hint " ) ) {
2016-08-16 22:10:53 +02:00
pinfo . hint = PropertyHint ( p_info [ " hint " ] . operator int ( ) ) ;
2020-05-14 16:41:43 +02:00
}
if ( p_info . has ( " hint_string " ) ) {
2016-08-16 22:10:53 +02:00
pinfo . hint_string = p_info [ " hint_string " ] ;
2020-05-14 16:41:43 +02:00
}
2016-08-16 22:10:53 +02:00
2022-11-08 19:53:22 +01:00
set_custom_property_info ( pinfo ) ;
2016-08-16 22:10:53 +02:00
}
2022-11-08 19:53:22 +01:00
void ProjectSettings : : set_custom_property_info ( const PropertyInfo & p_info ) {
const String & prop_name = p_info . name ;
ERR_FAIL_COND ( ! props . has ( prop_name ) ) ;
custom_prop_info [ prop_name ] = p_info ;
2014-02-10 02:10:30 +01:00
}
2022-05-13 15:04:37 +02:00
const HashMap < StringName , PropertyInfo > & ProjectSettings : : get_custom_property_info ( ) const {
2018-05-26 20:19:38 +02:00
return custom_prop_info ;
}
2017-07-19 22:00:46 +02:00
bool ProjectSettings : : is_using_datapack ( ) const {
2014-02-19 15:57:14 +01:00
return using_datapack ;
}
2014-02-10 02:10:30 +01:00
2023-02-19 21:05:16 +01:00
bool ProjectSettings : : is_project_loaded ( ) const {
return project_loaded ;
}
2022-08-12 20:43:14 +02:00
bool ProjectSettings : : _property_can_revert ( const StringName & p_name ) const {
2020-05-14 16:41:43 +02:00
if ( ! props . has ( p_name ) ) {
2017-01-05 13:16:00 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2017-01-05 13:16:00 +01:00
2017-03-05 16:44:50 +01:00
return props [ p_name ] . initial ! = props [ p_name ] . variant ;
2017-01-05 13:16:00 +01:00
}
2022-08-12 20:43:14 +02:00
bool ProjectSettings : : _property_get_revert ( const StringName & p_name , Variant & r_property ) const {
2020-05-14 16:41:43 +02:00
if ( ! props . has ( p_name ) ) {
2022-08-12 20:43:14 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2017-01-05 13:16:00 +01:00
2023-01-13 12:36:44 +01:00
// Duplicate so that if value is array or dictionary, changing the setting will not change the stored initial value.
r_property = props [ p_name ] . initial . duplicate ( ) ;
2022-08-12 20:43:14 +02:00
return true ;
2017-01-05 13:16:00 +01:00
}
2017-01-03 03:03:46 +01:00
2017-10-05 20:34:34 +02:00
void ProjectSettings : : set_setting ( const String & p_setting , const Variant & p_value ) {
set ( p_setting , p_value ) ;
}
2022-06-13 23:35:02 +02:00
Variant ProjectSettings : : get_setting ( const String & p_setting , const Variant & p_default_value ) const {
if ( has_setting ( p_setting ) ) {
return get ( p_setting ) ;
} else {
return p_default_value ;
}
2017-10-05 20:34:34 +02:00
}
2023-02-03 19:37:52 +01:00
TypedArray < Dictionary > ProjectSettings : : get_global_class_list ( ) {
if ( is_global_class_list_loaded ) {
return global_class_list ;
}
2022-12-25 15:08:32 +01:00
Ref < ConfigFile > cf ;
cf . instantiate ( ) ;
2023-01-31 10:52:43 +01:00
if ( cf - > load ( get_global_class_list_path ( ) ) = = OK ) {
2023-02-03 19:37:52 +01:00
global_class_list = cf - > get_value ( " " , " list " , Array ( ) ) ;
2022-12-25 15:08:32 +01:00
} else {
# ifndef TOOLS_ENABLED
// Script classes can't be recreated in exported project, so print an error.
ERR_PRINT ( " Could not load global script cache. " ) ;
# endif
}
2023-02-03 19:37:52 +01:00
// File read succeeded or failed. If it failed, assume everything is still okay.
// We will later receive updated class data in store_global_class_list().
is_global_class_list_loaded = true ;
return global_class_list ;
2022-12-25 15:08:32 +01:00
}
2023-01-31 10:52:43 +01:00
String ProjectSettings : : get_global_class_list_path ( ) const {
return get_project_data_path ( ) . path_join ( " global_script_class_cache.cfg " ) ;
}
2022-12-25 15:08:32 +01:00
void ProjectSettings : : store_global_class_list ( const Array & p_classes ) {
Ref < ConfigFile > cf ;
cf . instantiate ( ) ;
cf - > set_value ( " " , " list " , p_classes ) ;
2023-01-31 10:52:43 +01:00
cf - > save ( get_global_class_list_path ( ) ) ;
2023-02-03 19:37:52 +01:00
global_class_list = p_classes ;
2022-12-25 15:08:32 +01:00
}
2018-07-18 10:22:59 +02:00
bool ProjectSettings : : has_custom_feature ( const String & p_feature ) const {
return custom_features . has ( p_feature ) ;
}
2022-05-08 10:09:19 +02:00
const HashMap < StringName , ProjectSettings : : AutoloadInfo > & ProjectSettings : : get_autoload_list ( ) const {
2020-06-18 01:45:08 +02:00
return autoloads ;
}
void ProjectSettings : : add_autoload ( const AutoloadInfo & p_autoload ) {
ERR_FAIL_COND_MSG ( p_autoload . name = = StringName ( ) , " Trying to add autoload with no name. " ) ;
autoloads [ p_autoload . name ] = p_autoload ;
}
void ProjectSettings : : remove_autoload ( const StringName & p_autoload ) {
ERR_FAIL_COND_MSG ( ! autoloads . has ( p_autoload ) , " Trying to remove non-existent autoload. " ) ;
autoloads . erase ( p_autoload ) ;
}
bool ProjectSettings : : has_autoload ( const StringName & p_autoload ) const {
return autoloads . has ( p_autoload ) ;
}
ProjectSettings : : AutoloadInfo ProjectSettings : : get_autoload ( const StringName & p_name ) const {
ERR_FAIL_COND_V_MSG ( ! autoloads . has ( p_name ) , AutoloadInfo ( ) , " Trying to get non-existent autoload. " ) ;
return autoloads [ p_name ] ;
}
2017-07-19 22:00:46 +02:00
void ProjectSettings : : _bind_methods ( ) {
2017-10-05 20:34:34 +02:00
ClassDB : : bind_method ( D_METHOD ( " has_setting " , " name " ) , & ProjectSettings : : has_setting ) ;
ClassDB : : bind_method ( D_METHOD ( " set_setting " , " name " , " value " ) , & ProjectSettings : : set_setting ) ;
2022-06-13 23:35:02 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_setting " , " name " , " default_value " ) , & ProjectSettings : : get_setting , DEFVAL ( Variant ( ) ) ) ;
2023-01-13 13:16:49 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_setting_with_override " , " name " ) , & ProjectSettings : : get_setting_with_override ) ;
2023-02-03 19:37:52 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_global_class_list " ) , & ProjectSettings : : get_global_class_list ) ;
2017-09-10 15:37:49 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_order " , " name " , " position " ) , & ProjectSettings : : set_order ) ;
2017-07-19 22:00:46 +02:00
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 ) ;
2023-05-24 04:33:35 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_as_basic " , " name " , " basic " ) , & ProjectSettings : : set_as_basic ) ;
2023-05-30 21:16:29 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_as_internal " , " name " , " internal " ) , & ProjectSettings : : set_as_internal ) ;
2017-07-19 22:00:46 +02:00
ClassDB : : bind_method ( D_METHOD ( " add_property_info " , " hint " ) , & ProjectSettings : : _add_property_info_bind ) ;
2022-09-19 00:31:48 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_restart_if_changed " , " name " , " restart " ) , & ProjectSettings : : set_restart_if_changed ) ;
2017-07-19 22:00:46 +02:00
ClassDB : : bind_method ( D_METHOD ( " clear " , " name " ) , & ProjectSettings : : clear ) ;
ClassDB : : bind_method ( D_METHOD ( " localize_path " , " path " ) , & ProjectSettings : : localize_path ) ;
ClassDB : : bind_method ( D_METHOD ( " globalize_path " , " path " ) , & ProjectSettings : : globalize_path ) ;
ClassDB : : bind_method ( D_METHOD ( " save " ) , & ProjectSettings : : save ) ;
2020-07-13 18:22:06 +02:00
ClassDB : : bind_method ( D_METHOD ( " load_resource_pack " , " pack " , " replace_files " , " offset " ) , & ProjectSettings : : _load_resource_pack , DEFVAL ( true ) , DEFVAL ( 0 ) ) ;
2017-07-19 22:00:46 +02:00
ClassDB : : bind_method ( D_METHOD ( " save_custom " , " file " ) , & ProjectSettings : : _save_custom_bnd ) ;
2022-06-14 18:33:57 +02:00
ADD_SIGNAL ( MethodInfo ( " settings_changed " ) ) ;
2014-02-10 02:10:30 +01:00
}
2020-12-07 12:31:51 +01:00
void ProjectSettings : : _add_builtin_input_map ( ) {
if ( InputMap : : get_singleton ( ) ) {
2022-05-08 10:09:19 +02:00
HashMap < String , List < Ref < InputEvent > > > builtins = InputMap : : get_singleton ( ) - > get_builtins ( ) ;
2020-12-07 12:31:51 +01:00
2022-05-08 10:09:19 +02:00
for ( KeyValue < String , List < Ref < InputEvent > > > & E : builtins ) {
2020-12-07 12:31:51 +01:00
Array events ;
// Convert list of input events into array
2022-05-08 10:09:19 +02:00
for ( List < Ref < InputEvent > > : : Element * I = E . value . front ( ) ; I ; I = I - > next ( ) ) {
2020-12-07 12:31:51 +01:00
events . push_back ( I - > get ( ) ) ;
}
Dictionary action ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
action [ " events " ] = events ;
2022-05-08 10:09:19 +02:00
String action_name = " input/ " + E . key ;
2023-03-02 09:54:15 +01:00
GLOBAL_DEF ( action_name , action ) ;
2020-12-07 12:31:51 +01:00
input_presets . push_back ( action_name ) ;
}
}
}
2017-07-19 22:00:46 +02:00
ProjectSettings : : ProjectSettings ( ) {
2020-07-13 13:38:35 +02:00
// Initialization of engine variables should be done in the setup() method,
// so that the values can be overridden from project.godot or project.binary.
2022-11-29 15:20:15 +01:00
CRASH_COND_MSG ( singleton ! = nullptr , " Instantiating a new ProjectSettings singleton is not supported. " ) ;
2017-03-05 16:44:50 +01:00
singleton = this ;
2014-02-10 02:10:30 +01:00
2021-02-17 17:44:49 +01:00
GLOBAL_DEF_BASIC ( " application/config/name " , " " ) ;
2023-01-19 09:28:53 +01:00
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : DICTIONARY , " application/config/name_localized " , PROPERTY_HINT_LOCALIZABLE_STRING ) , Dictionary ( ) ) ;
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : STRING , " application/config/description " , PROPERTY_HINT_MULTILINE_TEXT ) , " " ) ;
2023-07-17 09:53:45 +02:00
GLOBAL_DEF_BASIC ( " application/config/version " , " " ) ;
2023-03-17 23:30:21 +01:00
GLOBAL_DEF_INTERNAL ( PropertyInfo ( Variant : : STRING , " application/config/tags " ) , PackedStringArray ( ) ) ;
2023-01-19 09:28:53 +01:00
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : STRING , " application/run/main_scene " , PROPERTY_HINT_FILE , " *.tscn,*.scn,*.res " ) , " " ) ;
2017-07-18 02:05:38 +02:00
GLOBAL_DEF ( " application/run/disable_stdout " , false ) ;
GLOBAL_DEF ( " application/run/disable_stderr " , false ) ;
2021-10-13 22:56:18 +02:00
GLOBAL_DEF_RST ( " application/config/use_hidden_project_data_directory " , true ) ;
2017-11-26 19:00:53 +01:00
GLOBAL_DEF ( " application/config/use_custom_user_dir " , false ) ;
GLOBAL_DEF ( " application/config/custom_user_dir_name " , " " ) ;
2018-11-18 14:56:21 +01:00
GLOBAL_DEF ( " application/config/project_settings_override " , " " ) ;
2022-05-12 16:38:59 +02:00
2023-03-10 15:09:56 +01:00
GLOBAL_DEF ( " application/run/main_loop_type " , " SceneTree " ) ;
GLOBAL_DEF ( " application/config/auto_accept_quit " , true ) ;
GLOBAL_DEF ( " application/config/quit_on_go_back " , true ) ;
2021-11-16 19:53:27 +01:00
// The default window size is tuned to:
// - Have a 16:9 aspect ratio,
// - Have both dimensions divisible by 8 to better play along with video recording,
// - Be displayable correctly in windowed mode on a 1366× 768 display (tested on Windows 10 with default settings).
2023-01-19 09:28:53 +01:00
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : INT , " display/window/size/viewport_width " , PROPERTY_HINT_RANGE , " 0,7680,1,or_greater " ) , 1152 ) ; // 8K resolution
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : INT , " display/window/size/viewport_height " , PROPERTY_HINT_RANGE , " 0,4320,1,or_greater " ) , 648 ) ; // 8K resolution
2022-05-12 16:38:59 +02:00
2023-01-19 09:28:53 +01:00
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : INT , " display/window/size/mode " , PROPERTY_HINT_ENUM , " Windowed,Minimized,Maximized,Fullscreen,Exclusive Fullscreen " ) , 0 ) ;
2022-09-15 09:05:41 +02:00
2023-01-04 23:00:02 +01:00
// Keep the enum values in sync with the `DisplayServer::SCREEN_` enum.
2023-03-21 12:08:46 +01:00
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : INT , " display/window/size/initial_position_type " , PROPERTY_HINT_ENUM , " Absolute,Center of Primary Screen,Center of Other Screen,Center of Screen With Mouse Pointer,Center of Screen With Keyboard Focus " ) , 1 ) ;
2023-01-19 09:28:53 +01:00
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : VECTOR2I , " display/window/size/initial_position " ) , Vector2i ( ) ) ;
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : INT , " display/window/size/initial_screen " , PROPERTY_HINT_RANGE , " 0,64,1,or_greater " ) , 0 ) ;
2023-01-04 23:00:02 +01:00
2022-05-12 16:38:59 +02:00
GLOBAL_DEF_BASIC ( " display/window/size/resizable " , true ) ;
GLOBAL_DEF_BASIC ( " display/window/size/borderless " , false ) ;
GLOBAL_DEF ( " display/window/size/always_on_top " , false ) ;
2022-09-15 09:05:41 +02:00
GLOBAL_DEF ( " display/window/size/transparent " , false ) ;
GLOBAL_DEF ( " display/window/size/extend_to_title " , false ) ;
GLOBAL_DEF ( " display/window/size/no_focus " , false ) ;
2023-01-19 09:28:53 +01:00
GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " display/window/size/window_width_override " , PROPERTY_HINT_RANGE , " 0,7680,1,or_greater " ) , 0 ) ; // 8K resolution
GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " display/window/size/window_height_override " , PROPERTY_HINT_RANGE , " 0,4320,1,or_greater " ) , 0 ) ; // 8K resolution
2022-05-12 16:38:59 +02:00
2022-08-06 22:10:24 +02:00
GLOBAL_DEF ( " display/window/energy_saving/keep_screen_on " , true ) ;
GLOBAL_DEF ( " display/window/energy_saving/keep_screen_on.editor " , false ) ;
2023-01-19 09:28:53 +01:00
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : STRING , " audio/buses/default_bus_layout " , PROPERTY_HINT_FILE , " *.tres " ) , " res://default_bus_layout.tres " ) ;
2023-05-16 13:18:12 +02:00
GLOBAL_DEF_RST ( " audio/general/text_to_speech " , false ) ;
2023-01-19 09:28:53 +01:00
GLOBAL_DEF_RST ( PropertyInfo ( Variant : : FLOAT , " audio/general/2d_panning_strength " , PROPERTY_HINT_RANGE , " 0,2,0.01 " ) , 0.5f ) ;
GLOBAL_DEF_RST ( PropertyInfo ( Variant : : FLOAT , " audio/general/3d_panning_strength " , PROPERTY_HINT_RANGE , " 0,2,0.01 " ) , 0.5f ) ;
2014-02-10 02:10:30 +01:00
2022-11-14 18:21:06 +01:00
PackedStringArray extensions ;
2019-02-04 22:59:51 +01:00
extensions . push_back ( " gd " ) ;
2020-05-14 16:41:43 +02:00
if ( Engine : : get_singleton ( ) - > has_singleton ( " GodotSharp " ) ) {
2019-02-04 22:59:51 +01:00
extensions . push_back ( " cs " ) ;
2020-05-14 16:41:43 +02:00
}
2021-03-24 19:50:41 +01:00
extensions . push_back ( " gdshader " ) ;
2019-02-04 22:59:51 +01:00
2023-01-19 09:28:53 +01:00
GLOBAL_DEF ( PropertyInfo ( Variant : : PACKED_STRING_ARRAY , " editor/script/search_in_file_extensions " ) , extensions ) ;
2019-02-04 22:59:51 +01:00
2020-12-07 12:31:51 +01:00
_add_builtin_input_map ( ) ;
2018-02-23 12:17:15 +01:00
2021-05-21 21:29:24 +02:00
// Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum.
custom_prop_info [ " display/window/handheld/orientation " ] = PropertyInfo ( Variant : : INT , " display/window/handheld/orientation " , PROPERTY_HINT_ENUM , " Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor " ) ;
2023-03-10 15:09:56 +01:00
GLOBAL_DEF ( " display/window/subwindows/embed_subwindows " , true ) ;
2021-07-12 03:35:51 +02:00
// Keep the enum values in sync with the `DisplayServer::VSyncMode` enum.
custom_prop_info [ " display/window/vsync/vsync_mode " ] = PropertyInfo ( Variant : : INT , " display/window/vsync/vsync_mode " , PROPERTY_HINT_ENUM , " Disabled,Enabled,Adaptive,Mailbox " ) ;
2021-02-17 17:44:49 +01:00
custom_prop_info [ " rendering/driver/threads/thread_model " ] = PropertyInfo ( Variant : : INT , " rendering/driver/threads/thread_model " , PROPERTY_HINT_ENUM , " Single-Unsafe,Single-Safe,Multi-Threaded " ) ;
2022-01-29 17:35:50 +01:00
GLOBAL_DEF ( " physics/2d/run_on_separate_thread " , false ) ;
GLOBAL_DEF ( " physics/3d/run_on_separate_thread " , false ) ;
2014-02-10 02:10:30 +01:00
2023-03-10 15:09:56 +01:00
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : STRING , " display/window/stretch/mode " , PROPERTY_HINT_ENUM , " disabled,canvas_items,viewport " ) , " disabled " ) ;
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : STRING , " display/window/stretch/aspect " , PROPERTY_HINT_ENUM , " ignore,keep,keep_width,keep_height,expand " ) , " keep " ) ;
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : FLOAT , " display/window/stretch/scale " , PROPERTY_HINT_RANGE , " 0.5,8.0,0.01 " ) , 1.0 ) ;
2023-04-06 23:41:27 +02:00
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : STRING , " display/window/stretch/scale_mode " , PROPERTY_HINT_ENUM , " fractional,integer " ) , " fractional " ) ;
2023-03-10 15:09:56 +01:00
2023-01-19 09:28:53 +01:00
GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " debug/settings/profiler/max_functions " , PROPERTY_HINT_RANGE , " 128,65535,1 " ) , 16384 ) ;
2017-10-26 22:42:02 +02:00
2023-01-19 09:28:53 +01:00
GLOBAL_DEF ( PropertyInfo ( Variant : : BOOL , " compression/formats/zstd/long_distance_matching " ) , Compression : : zstd_long_distance_matching ) ;
GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " compression/formats/zstd/compression_level " , PROPERTY_HINT_RANGE , " 1,22,1 " ) , Compression : : zstd_level ) ;
GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " compression/formats/zstd/window_log_size " , PROPERTY_HINT_RANGE , " 10,30,1 " ) , Compression : : zstd_window_log_size ) ;
GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " compression/formats/zlib/compression_level " , PROPERTY_HINT_RANGE , " -1,9,1 " ) , Compression : : zlib_level ) ;
GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " compression/formats/gzip/compression_level " , PROPERTY_HINT_RANGE , " -1,9,1 " ) , Compression : : gzip_level ) ;
2022-06-06 15:22:57 +02:00
2022-10-20 15:43:17 +02:00
GLOBAL_DEF ( " debug/settings/crash_handler/message " ,
String ( " Please include this when reporting the bug to the project developer. " ) ) ;
GLOBAL_DEF ( " debug/settings/crash_handler/message.editor " ,
String ( " Please include this when reporting the bug on: https://github.com/godotengine/godot/issues " ) ) ;
2022-11-08 19:53:22 +01:00
GLOBAL_DEF_RST ( PropertyInfo ( Variant : : INT , " rendering/occlusion_culling/bvh_build_quality " , PROPERTY_HINT_ENUM , " Low,Medium,High " ) , 2 ) ;
GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " memory/limits/multithreaded_server/rid_pool_prealloc " , PROPERTY_HINT_RANGE , " 0,500,1 " ) , 60 ) ; // No negative and limit to 500 due to crashes.
2022-10-20 15:43:17 +02:00
GLOBAL_DEF_RST ( " internationalization/rendering/force_right_to_left_layout_direction " , false ) ;
2023-07-18 14:33:13 +02:00
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : INT , " internationalization/rendering/root_node_layout_direction " , PROPERTY_HINT_ENUM , " Based on Locale,Left-to-Right,Right-to-Left " ) , 0 ) ;
2022-10-20 15:43:17 +02:00
2022-11-08 19:53:22 +01:00
GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " gui/timers/incremental_search_max_interval_msec " , PROPERTY_HINT_RANGE , " 0,10000,1,or_greater " ) , 2000 ) ;
2022-10-20 15:43:17 +02:00
2023-03-10 15:09:56 +01:00
GLOBAL_DEF_BASIC ( " gui/common/snap_controls_to_pixels " , true ) ;
GLOBAL_DEF_BASIC ( " gui/fonts/dynamic_fonts/use_oversampling " , true ) ;
2022-10-20 15:43:17 +02:00
GLOBAL_DEF ( " rendering/rendering_device/staging_buffer/block_size_kb " , 256 ) ;
GLOBAL_DEF ( " rendering/rendering_device/staging_buffer/max_size_mb " , 128 ) ;
GLOBAL_DEF ( " rendering/rendering_device/staging_buffer/texture_upload_region_size_px " , 64 ) ;
2023-04-22 18:05:05 +02:00
GLOBAL_DEF ( " rendering/rendering_device/pipeline_cache/save_chunk_size_mb " , 3.0 ) ;
2022-10-20 15:43:17 +02:00
GLOBAL_DEF ( " rendering/rendering_device/vulkan/max_descriptors_per_pool " , 64 ) ;
2023-03-10 15:09:56 +01:00
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : INT , " rendering/textures/canvas_textures/default_texture_filter " , PROPERTY_HINT_ENUM , " Nearest,Linear,Linear Mipmap,Nearest Mipmap " ) , 1 ) ;
GLOBAL_DEF_BASIC ( PropertyInfo ( Variant : : INT , " rendering/textures/canvas_textures/default_texture_repeat " , PROPERTY_HINT_ENUM , " Disable,Enable,Mirror " ) , 0 ) ;
2023-08-24 22:39:40 +02:00
GLOBAL_DEF ( " collada/use_ambient " , false ) ;
2022-06-08 17:52:19 +02:00
// These properties will not show up in the dialog. If you want to exclude whole groups, use add_hidden_prefix().
2022-06-06 15:22:57 +02:00
GLOBAL_DEF_INTERNAL ( " application/config/features " , PackedStringArray ( ) ) ;
GLOBAL_DEF_INTERNAL ( " internationalization/locale/translation_remaps " , PackedStringArray ( ) ) ;
GLOBAL_DEF_INTERNAL ( " internationalization/locale/translations " , PackedStringArray ( ) ) ;
2023-03-02 07:38:59 +01:00
GLOBAL_DEF_INTERNAL ( " internationalization/locale/translations_pot_files " , PackedStringArray ( ) ) ;
2022-06-08 17:52:19 +02:00
ProjectSettings : : get_singleton ( ) - > add_hidden_prefix ( " input/ " ) ;
2014-02-10 02:10:30 +01:00
}
2017-07-19 22:00:46 +02:00
ProjectSettings : : ~ ProjectSettings ( ) {
2020-04-02 01:20:12 +02:00
singleton = nullptr ;
2014-02-10 02:10:30 +01:00
}