2014-02-10 02:10:30 +01:00
/*************************************************************************/
2017-09-01 16:07:55 +02:00
/* project_settings.cpp */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2021-01-01 20:13:46 +01:00
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 02:10:30 +01:00
/* */
/* 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"
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_network.h"
# include "core/io/file_access_pack.h"
# include "core/io/marshalls.h"
# include "core/os/keyboard.h"
# include "core/os/os.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"
# include "modules/modules_enabled.gen.h" // For mono.
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-11-25 08:45:33 +01:00
String ProjectSettings : : get_safe_project_name ( ) const {
String safe_name = OS : : get_singleton ( ) - > get_safe_dir_name ( get ( " application/config/name " ) ) ;
if ( safe_name . is_empty ( ) ) {
safe_name = " UnnamedProject " ;
}
return safe_name ;
}
2021-09-10 17:32:29 +02:00
String ProjectSettings : : get_imported_files_path ( ) const {
return get_project_data_path ( ) . plus_file ( " imported " ) ;
}
2020-07-06 02:55:43 +02:00
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 ( ) {
PackedStringArray features = PackedStringArray ( ) ;
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 ) ;
// For now, assume Vulkan is always supported.
// This should be removed if it's possible to build the editor without Vulkan.
features . append ( " Vulkan Clustered " ) ;
features . append ( " Vulkan Mobile " ) ;
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 ) {
PackedStringArray unsupported_features = PackedStringArray ( ) ;
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 ] ) ) {
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 ;
}
2017-07-19 22:00:46 +02:00
String ProjectSettings : : localize_path ( const String & p_path ) const {
2021-09-09 16:48:37 +02:00
if ( resource_path . is_empty ( ) | | p_path . begins_with ( " res:// " ) | | p_path . begins_with ( " user:// " ) | |
2021-06-03 15:41:22 +02:00
( p_path . is_absolute_path ( ) & & ! p_path . begins_with ( resource_path ) ) ) {
2014-02-10 02:10:30 +01:00
return p_path . simplify_path ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
DirAccess * dir = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2017-03-05 16:44:50 +01:00
String path = p_path . replace ( " \\ " , " / " ) . simplify_path ( ) ;
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
memdelete ( dir ) ;
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://.
// `plus_file("")` is an easy way to ensure we have a trailing '/'.
const String res_path = resource_path . plus_file ( " " ) ;
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'.
cwd = cwd . plus_file ( " " ) ;
2019-05-30 16:03:12 +02:00
if ( ! cwd . begins_with ( res_path ) ) {
2014-02-10 02:10:30 +01:00
return p_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 {
memdelete ( dir ) ;
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 ) ;
if ( plocal = = " " ) {
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 + " . " ) ;
2017-03-05 16:44:50 +01:00
props [ p_name ] . initial = p_value ;
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 ;
}
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
}
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:// " ) ) {
if ( resource_path ! = " " ) {
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 ( ) ;
2017-09-10 10:33:54 +02:00
if ( data_dir ! = " " ) {
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 ] ) ;
}
return true ;
}
if ( ! disable_feature_overrides ) {
int dot = p_name . operator String ( ) . find ( " . " ) ;
if ( dot ! = - 1 ) {
Vector < String > s = p_name . operator String ( ) . split ( " . " ) ;
bool override_valid = false ;
for ( int i = 1 ; i < s . size ( ) ; i + + ) {
String feature = s [ i ] . strip_edges ( ) ;
2017-10-02 21:38:39 +02:00
if ( OS : : get_singleton ( ) - > has_feature ( feature ) | | custom_features . has ( feature ) ) {
2017-07-19 22:00:46 +02:00
override_valid = true ;
break ;
}
}
if ( override_valid ) {
feature_overrides [ s [ 0 ] ] = p_name ;
}
}
}
2014-02-10 02:10:30 +01:00
if ( props . has ( p_name ) ) {
2020-05-14 16:41:43 +02:00
if ( ! props [ p_name ] . overridden ) {
2017-03-05 16:44:50 +01:00
props [ p_name ] . variant = p_value ;
2020-05-14 16:41:43 +02:00
}
2015-12-16 03:39:36 +01:00
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
}
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_
2017-07-19 22:00:46 +02:00
StringName name = p_name ;
if ( ! disable_feature_overrides & & feature_overrides . has ( name ) ) {
name = feature_overrides [ name ] ;
}
if ( ! props . has ( name ) ) {
2019-11-07 09:44:15 +01:00
WARN_PRINT ( " Property not found: " + String ( name ) ) ;
2014-02-10 02:10:30 +01:00
return false ;
2017-01-15 01:56:22 +01:00
}
2017-07-19 22:00:46 +02:00
r_ret = props [ name ] . variant ;
2014-02-10 02:10:30 +01:00
return true ;
}
struct _VCSort {
String name ;
Variant : : Type type ;
int order ;
2021-07-01 03:24:34 +02:00
uint32_t flags ;
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_
Set < _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 ( ) ;
2020-05-14 16:41:43 +02:00
if ( vc . name . begins_with ( " input/ " ) | | vc . name . begins_with ( " import/ " ) | | vc . name . begins_with ( " export/ " ) | | vc . name . begins_with ( " /remap " ) | | vc . name . begins_with ( " /locale " ) | | vc . name . begins_with ( " /autoload " ) ) {
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
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 ) ;
}
2017-03-05 16:44:50 +01:00
for ( Set < _VCSort > : : Element * E = vclist . front ( ) ; E ; E = E - > next ( ) ) {
2017-07-19 22:00:46 +02:00
String prop_info_name = E - > get ( ) . name ;
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 ] ;
2017-03-05 16:44:50 +01:00
pi . name = E - > get ( ) . name ;
pi . usage = E - > get ( ) . flags ;
p_list - > push_back ( pi ) ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
p_list - > push_back ( PropertyInfo ( E - > get ( ) . type , E - > get ( ) . name , PROPERTY_HINT_NONE , " " , E - > get ( ) . flags ) ) ;
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 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 ) {
2018-10-24 20:04:36 +02:00
// If looking for files in a network client, use it directly
2017-04-29 17:56:51 +02:00
2017-02-21 04:05:15 +01:00
if ( FileAccessNetworkClient : : get_singleton ( ) ) {
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
// Optional, we don't mind if it fails
_load_settings_text ( " res://override.cfg " ) ;
2017-02-21 04:05:15 +01:00
}
2018-02-19 14:53:59 +01:00
return err ;
2017-02-21 04:05:15 +01:00
}
2018-10-24 20:04:36 +02:00
// Attempt with a user-defined main pack first
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( p_main_pack ! = " " ) {
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
_load_settings_text ( p_main_pack . get_base_dir ( ) . plus_file ( " 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 ( ) ;
2017-03-05 16:44:50 +01:00
if ( exec_path ! = " " ) {
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
2020-01-10 17:02:29 +01:00
# ifdef OSX_ENABLED
if ( ! found ) {
2020-05-25 17:50:16 +02:00
// Attempt to load PCK from macOS .app bundle resources.
2020-12-02 10:04:44 +01:00
found = _load_resource_pack ( OS : : get_singleton ( ) - > get_bundle_resource_dir ( ) . plus_file ( exec_basename + " .pck " ) ) | | _load_resource_pack ( OS : : get_singleton ( ) - > get_bundle_resource_dir ( ) . plus_file ( 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.
2020-04-16 18:41:59 +02:00
found = _load_resource_pack ( exec_dir . plus_file ( exec_basename + " .pck " ) ) | | _load_resource_pack ( exec_dir . plus_file ( 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 ) {
2020-05-25 17:50:16 +02:00
// Load override from location of the executable.
// Optional, we don't mind if it fails.
2018-02-19 14:53:59 +01:00
_load_settings_text ( exec_path . get_base_dir ( ) . plus_file ( " 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
2018-10-24 20:04:36 +02:00
if ( OS : : get_singleton ( ) - > get_resource_dir ( ) ! = " " ) {
// 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.
2017-03-05 16:44:50 +01:00
resource_path = OS : : get_singleton ( ) - > get_resource_dir ( ) . replace ( " \\ " , " / " ) ;
2018-10-24 20:04:36 +02:00
if ( resource_path ! = " " & & 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.
2018-10-24 20:04:36 +02:00
}
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 ) {
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
2017-02-21 04:05:15 +01:00
DirAccess * d = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( ! d , 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.
2018-02-19 14:53:59 +01:00
err = _load_settings_text_or_binary ( current_dir . plus_file ( " project.godot " ) , current_dir . plus_file ( " 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 ( current_dir . plus_file ( " 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
2017-02-21 04:05:15 +01:00
memdelete ( d ) ;
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 ) ;
2018-11-18 14:56:21 +01:00
if ( err = = OK ) {
String custom_settings = GLOBAL_DEF ( " application/config/project_settings_override " , " " ) ;
if ( custom_settings ! = " " ) {
_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
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_
return props . has ( p_var ) ;
}
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 ;
2017-03-05 16:44:50 +01:00
FileAccess * f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
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 ) ;
if ( hdr [ 0 ] ! = ' E ' | | hdr [ 1 ] ! = ' C ' | | hdr [ 2 ] ! = ' F ' | | hdr [ 3 ] ! = ' G ' ) {
2014-02-10 02:10:30 +01:00
memdelete ( f ) ;
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( 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
}
2019-11-20 16:22:16 +01:00
f - > close ( ) ;
memdelete ( f ) ;
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 ;
2017-03-05 16:44:50 +01:00
FileAccess * f = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
2014-02-10 02:10:30 +01:00
2018-02-21 09:07:47 +01:00
if ( ! f ) {
// 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 ) {
2017-01-11 20:15:40 +01:00
memdelete ( f ) ;
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 ) ;
2021-01-17 01:09:17 +01:00
last_save_time = FileAccess : : get_modified_time ( get_resource_path ( ) . plus_file ( " project.godot " ) ) ;
2017-01-11 20:15:40 +01:00
return OK ;
2017-03-05 16:44:50 +01:00
} else if ( err ! = OK ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Error parsing " + p_path + " at line " + itos ( lines ) + " : " + error_text + " File might be corrupted. " ) ;
2017-01-11 20:15:40 +01:00
memdelete ( f ) ;
return err ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
if ( assign ! = String ( ) ) {
if ( section = = String ( ) & & assign = = " config_version " ) {
2018-12-21 12:20:48 +01:00
config_version = value ;
if ( config_version > CONFIG_VERSION ) {
2017-02-05 01:02:52 +01:00
memdelete ( f ) ;
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( 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-02-05 01:02:52 +01:00
}
2017-07-26 15:28:54 +02:00
} else {
2018-07-16 00:29:00 +02:00
if ( section = = String ( ) ) {
set ( assign , value ) ;
} else {
set ( section + " / " + assign , value ) ;
}
2017-02-05 01:02:52 +01:00
}
2017-03-05 16:44:50 +01:00
} else if ( next_tag . name ! = String ( ) ) {
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 ( ) {
2021-01-17 01:09:17 +01:00
Error error = save_custom ( get_resource_path ( ) . plus_file ( " project.godot " ) ) ;
if ( error = = OK ) {
last_save_time = FileAccess : : get_modified_time ( get_resource_path ( ) . plus_file ( " project.godot " ) ) ;
}
return error ;
2014-02-10 02:10:30 +01:00
}
2020-03-17 07:33:00 +01:00
Error ProjectSettings : : _save_settings_binary ( const String & p_file , const Map < String , List < String > > & props , const CustomMap & p_custom , const String & p_custom_features ) {
2014-02-10 02:10:30 +01:00
Error err ;
2017-03-05 16:44:50 +01:00
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
2021-08-09 22:13:42 +02:00
for ( const KeyValue < String , List < String > > & E : props ) {
count + = E . value . size ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-07-19 22:00:46 +02:00
if ( p_custom_features ! = String ( ) ) {
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 ) ;
2017-07-19 22:00:46 +02:00
if ( err ! = OK ) {
memdelete ( file ) ;
ERR_FAIL_V ( err ) ;
}
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 ) ;
2017-07-19 22:00:46 +02:00
if ( err ! = OK ) {
memdelete ( file ) ;
ERR_FAIL_V ( err ) ;
}
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
2020-03-17 07:33:00 +01:00
for ( Map < String , List < String > > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2021-07-16 05:45:57 +02:00
for ( String & key : E - > get ( ) ) {
2020-05-14 16:41:43 +02:00
if ( E - > key ( ) ! = " " ) {
2017-03-05 16:44:50 +01:00
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
2021-06-16 10:32:03 +02:00
file - > store_pascal_string ( key ) ;
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 ) ;
2020-05-14 16:41:43 +02:00
if ( err ! = OK ) {
2014-02-10 02:10:30 +01:00
memdelete ( file ) ;
2020-05-14 16:41:43 +02:00
}
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 ) ;
2020-05-14 16:41:43 +02:00
if ( err ! = OK ) {
2014-02-10 02:10:30 +01:00
memdelete ( file ) ;
2020-05-14 16:41:43 +02:00
}
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
}
}
file - > close ( ) ;
memdelete ( file ) ;
return OK ;
}
2020-03-17 07:33:00 +01:00
Error ProjectSettings : : _save_settings_text ( const String & p_file , const Map < String , List < String > > & props , const CustomMap & p_custom , const String & p_custom_features ) {
2014-02-10 02:10:30 +01:00
Error err ;
2017-03-05 16:44:50 +01:00
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 " ) ;
2020-05-14 16:41:43 +02:00
if ( p_custom_features ! = String ( ) ) {
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
2021-08-09 22:13:42 +02:00
for ( const Map < String , List < String > > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2020-05-14 16:41:43 +02:00
if ( E ! = props . front ( ) ) {
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
2020-05-14 16:41:43 +02:00
if ( E - > key ( ) ! = " " ) {
2017-03-05 16:44:50 +01:00
file - > store_string ( " [ " + E - > key ( ) + " ] \n \n " ) ;
2020-05-14 16:41:43 +02:00
}
2021-07-24 15:46:25 +02:00
for ( const String & F : E - > get ( ) ) {
2021-07-16 05:45:57 +02:00
String key = F ;
2020-05-14 16:41:43 +02:00
if ( E - > key ( ) ! = " " ) {
2017-03-05 16:44:50 +01:00
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
}
}
file - > close ( ) ;
memdelete ( file ) ;
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
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 ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( p_path = = " " , ERR_INVALID_PARAMETER , " Project settings save path cannot be empty. " ) ;
2014-02-10 02:10:30 +01:00
2021-11-25 15:23:42 +01:00
PackedStringArray project_features = has_setting ( " application/config/features " ) ? ( PackedStringArray ) get_setting ( " application/config/features " ) : PackedStringArray ( ) ;
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.
2021-11-25 15:23:42 +01:00
const String rendering_api = has_setting ( " rendering/quality/driver/driver_name " ) ? ( String ) get_setting ( " rendering/quality/driver/driver_name " ) : String ( ) ;
2021-11-25 08:45:33 +01:00
if ( rendering_api ! = " " ) {
// 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.
if ( FileAccess : : exists ( get_resource_path ( ) . plus_file ( get_safe_project_name ( ) + " .csproj " ) ) ) {
// 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 ) ;
2021-11-25 08:45:33 +01:00
2014-02-10 02:10:30 +01:00
Set < _VCSort > vclist ;
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
2021-08-09 22:13:42 +02:00
Map < StringName , VariantContainer > : : Element * 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 ;
2017-07-26 15:28:54 +02:00
vc . order = global_prop ? global_prop - > get ( ) . 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 ) ;
}
2020-03-17 07:33:00 +01:00
Map < String , List < String > > props ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( Set < _VCSort > : : Element * E = vclist . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
String category = E - > get ( ) . name ;
String name = E - > get ( ) . name ;
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
}
props [ category ] . push_back ( name ) ;
}
2017-07-19 22:00:46 +02:00
String custom_features ;
for ( int i = 0 ; i < p_custom_features . size ( ) ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( i > 0 ) {
2017-07-19 22:00:46 +02:00
custom_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 ( " \" " , " " ) ;
custom_features + = f ;
}
2021-02-24 16:37:29 +01:00
if ( p_path . ends_with ( " .godot " ) | | p_path . ends_with ( " override.cfg " ) ) {
2017-07-19 22:00:46 +02:00
return _save_settings_text ( p_path , props , p_custom , custom_features ) ;
2020-05-14 16:41:43 +02:00
} else if ( p_path . ends_with ( " .binary " ) ) {
2017-07-19 22:00:46 +02:00
return _save_settings_binary ( p_path , props , p_custom , custom_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
}
}
2021-02-17 17:44:49 +01: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 ) {
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
}
2018-02-25 21:05:40 +01:00
ret = ProjectSettings : : get_singleton ( ) - > get ( p_var ) ;
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 ) ;
2017-07-18 02:05:38 +02:00
return ret ;
2014-02-10 02:10:30 +01:00
}
2017-07-19 22:00:46 +02:00
Vector < String > ProjectSettings : : get_optimizer_presets ( ) const {
2014-02-10 02:10:30 +01:00
List < PropertyInfo > pi ;
2017-07-19 22:00:46 +02:00
ProjectSettings : : get_singleton ( ) - > get_property_list ( & pi ) ;
2014-02-10 02:10:30 +01:00
Vector < String > names ;
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & E : pi ) {
2021-07-16 05:45:57 +02:00
if ( ! E . name . begins_with ( " optimizer_presets/ " ) ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2021-07-16 05:45:57 +02:00
names . push_back ( E . name . get_slicec ( ' / ' , 1 ) ) ;
2014-02-10 02:10:30 +01:00
}
names . sort ( ) ;
return names ;
}
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
set_custom_property_info ( pinfo . name , pinfo ) ;
}
2017-07-19 22:00:46 +02:00
void ProjectSettings : : set_custom_property_info ( const String & p_prop , const PropertyInfo & p_info ) {
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND ( ! props . has ( p_prop ) ) ;
2017-03-05 16:44:50 +01:00
custom_prop_info [ p_prop ] = p_info ;
custom_prop_info [ p_prop ] . name = p_prop ;
2014-02-10 02:10:30 +01:00
}
2018-05-26 20:19:38 +02:00
const Map < StringName , PropertyInfo > & ProjectSettings : : get_custom_property_info ( ) const {
return custom_prop_info ;
}
2017-07-19 22:00:46 +02:00
void ProjectSettings : : set_disable_feature_overrides ( bool p_disable ) {
disable_feature_overrides = p_disable ;
2014-02-10 02:10:30 +01:00
}
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
2017-07-19 22:00:46 +02:00
bool ProjectSettings : : property_can_revert ( const String & p_name ) {
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
}
2017-07-19 22:00:46 +02:00
Variant ProjectSettings : : property_get_revert ( const String & p_name ) {
2020-05-14 16:41:43 +02:00
if ( ! props . has ( p_name ) ) {
2017-01-05 13:16:00 +01:00
return Variant ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-01-05 13:16:00 +01:00
return props [ p_name ] . initial ;
}
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 ) ;
}
Variant ProjectSettings : : get_setting ( const String & p_setting ) const {
return get ( p_setting ) ;
}
2018-07-18 10:22:59 +02:00
bool ProjectSettings : : has_custom_feature ( const String & p_feature ) const {
return custom_features . has ( p_feature ) ;
}
2021-08-27 05:46:51 +02:00
OrderedHashMap < 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 ) ;
ClassDB : : bind_method ( D_METHOD ( " get_setting " , " name " ) , & ProjectSettings : : get_setting ) ;
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 ) ;
ClassDB : : bind_method ( D_METHOD ( " add_property_info " , " hint " ) , & ProjectSettings : : _add_property_info_bind ) ;
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 ( " property_can_revert " , " name " ) , & ProjectSettings : : property_can_revert ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " property_get_revert " , " name " ) , & ProjectSettings : : property_get_revert ) ;
2017-07-19 22:00:46 +02:00
ClassDB : : bind_method ( D_METHOD ( " save_custom " , " file " ) , & ProjectSettings : : _save_custom_bnd ) ;
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 ( ) ) {
OrderedHashMap < String , List < Ref < InputEvent > > > builtins = InputMap : : get_singleton ( ) - > get_builtins ( ) ;
for ( OrderedHashMap < String , List < Ref < InputEvent > > > : : Element E = builtins . front ( ) ; E ; E = E . next ( ) ) {
Array events ;
// Convert list of input events into array
for ( List < Ref < InputEvent > > : : Element * I = E . get ( ) . front ( ) ; I ; I = I - > next ( ) ) {
events . push_back ( I - > get ( ) ) ;
}
Dictionary action ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
action [ " events " ] = events ;
String action_name = " input/ " + E . key ( ) ;
GLOBAL_DEF ( action_name , action ) ;
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.
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 " , " " ) ;
GLOBAL_DEF_BASIC ( " application/config/description " , " " ) ;
2019-08-17 18:20:17 +02:00
custom_prop_info [ " application/config/description " ] = PropertyInfo ( Variant : : STRING , " application/config/description " , PROPERTY_HINT_MULTILINE_TEXT ) ;
2021-02-17 17:44:49 +01:00
GLOBAL_DEF_BASIC ( " application/run/main_scene " , " " ) ;
2018-07-26 11:48:12 +02:00
custom_prop_info [ " application/run/main_scene " ] = 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 " , " " ) ;
2021-02-17 17:44:49 +01:00
GLOBAL_DEF_BASIC ( " audio/buses/default_bus_layout " , " res://default_bus_layout.tres " ) ;
custom_prop_info [ " audio/buses/default_bus_layout " ] = PropertyInfo ( Variant : : STRING , " audio/buses/default_bus_layout " , PROPERTY_HINT_FILE , " *.tres " ) ;
2014-02-10 02:10:30 +01:00
2020-02-17 22:06:54 +01:00
PackedStringArray extensions = PackedStringArray ( ) ;
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
2020-02-07 19:51:50 +01:00
GLOBAL_DEF ( " editor/run/main_run_args " , " " ) ;
2021-02-17 17:44:49 +01:00
GLOBAL_DEF ( " editor/script/search_in_file_extensions " , extensions ) ;
custom_prop_info [ " editor/script/search_in_file_extensions " ] = PropertyInfo ( Variant : : PACKED_STRING_ARRAY , " editor/script/search_in_file_extensions " ) ;
2019-02-04 22:59:51 +01:00
2021-02-17 17:44:49 +01:00
GLOBAL_DEF ( " editor/script/templates_search_path " , " res://script_templates " ) ;
custom_prop_info [ " editor/script/templates_search_path " ] = PropertyInfo ( Variant : : STRING , " editor/script/templates_search_path " , PROPERTY_HINT_DIR ) ;
2019-08-22 17:59:43 +02: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 " ) ;
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 " ) ;
2021-02-09 17:19:03 +01:00
GLOBAL_DEF ( " physics/2d/run_on_thread " , false ) ;
GLOBAL_DEF ( " physics/3d/run_on_thread " , false ) ;
2014-02-10 02:10:30 +01:00
2017-07-18 02:05:38 +02:00
GLOBAL_DEF ( " debug/settings/profiler/max_functions " , 16384 ) ;
2018-10-05 18:43:53 +02:00
custom_prop_info [ " debug/settings/profiler/max_functions " ] = PropertyInfo ( Variant : : INT , " debug/settings/profiler/max_functions " , PROPERTY_HINT_RANGE , " 128,65535,1 " ) ;
2017-06-12 03:27:07 +02:00
2020-07-13 13:38:35 +02:00
GLOBAL_DEF ( " compression/formats/zstd/long_distance_matching " , Compression : : zstd_long_distance_matching ) ;
2017-10-26 22:42:02 +02:00
custom_prop_info [ " compression/formats/zstd/long_distance_matching " ] = PropertyInfo ( Variant : : BOOL , " compression/formats/zstd/long_distance_matching " ) ;
2020-07-13 13:38:35 +02:00
GLOBAL_DEF ( " compression/formats/zstd/compression_level " , Compression : : zstd_level ) ;
2017-07-18 02:05:38 +02:00
custom_prop_info [ " compression/formats/zstd/compression_level " ] = PropertyInfo ( Variant : : INT , " compression/formats/zstd/compression_level " , PROPERTY_HINT_RANGE , " 1,22,1 " ) ;
2020-07-13 13:38:35 +02:00
GLOBAL_DEF ( " compression/formats/zstd/window_log_size " , Compression : : zstd_window_log_size ) ;
2017-10-26 22:42:02 +02:00
custom_prop_info [ " compression/formats/zstd/window_log_size " ] = PropertyInfo ( Variant : : INT , " compression/formats/zstd/window_log_size " , PROPERTY_HINT_RANGE , " 10,30,1 " ) ;
2020-07-13 13:38:35 +02:00
GLOBAL_DEF ( " compression/formats/zlib/compression_level " , Compression : : zlib_level ) ;
2017-07-18 02:05:38 +02:00
custom_prop_info [ " compression/formats/zlib/compression_level " ] = PropertyInfo ( Variant : : INT , " compression/formats/zlib/compression_level " , PROPERTY_HINT_RANGE , " -1,9,1 " ) ;
2017-10-26 22:42:02 +02:00
2020-07-13 13:38:35 +02:00
GLOBAL_DEF ( " compression/formats/gzip/compression_level " , Compression : : gzip_level ) ;
2017-07-18 02:05:38 +02:00
custom_prop_info [ " compression/formats/gzip/compression_level " ] = PropertyInfo ( Variant : : INT , " compression/formats/gzip/compression_level " , PROPERTY_HINT_RANGE , " -1,9,1 " ) ;
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
}