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
2018-09-11 18:13:45 +02:00
# include "core/bind/core_bind.h"
# include "core/core_string_names.h"
# include "core/io/file_access_network.h"
# include "core/io/file_access_pack.h"
# include "core/io/marshalls.h"
# include "core/os/dir_access.h"
# include "core/os/file_access.h"
# include "core/os/keyboard.h"
# include "core/os/os.h"
# include "core/variant_parser.h"
2017-06-12 03:27:07 +02:00
# include <zlib.h>
2017-07-19 22:00:46 +02:00
ProjectSettings * ProjectSettings : : singleton = NULL ;
2014-02-10 02:10:30 +01:00
2017-07-19 22:00:46 +02:00
ProjectSettings * ProjectSettings : : get_singleton ( ) {
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
return singleton ;
}
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 ;
} ;
2017-07-19 22:00:46 +02:00
String ProjectSettings : : localize_path ( const String & p_path ) const {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( resource_path = = " " )
2018-09-13 03:38:39 +02:00
return p_path ; //not initialized yet
2014-02-10 02:10:30 +01:00
2016-10-30 18:22:05 +01:00
if ( p_path . begins_with ( " res:// " ) | | p_path . begins_with ( " user:// " ) | |
2017-03-05 16:44:50 +01:00
( p_path . is_abs_path ( ) & & ! p_path . begins_with ( resource_path ) ) )
2014-02-10 02:10:30 +01:00
return p_path . simplify_path ( ) ;
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 ;
} ;
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 ) ;
int sep = path . find_last ( " / " ) ;
if ( sep = = - 1 ) {
2017-03-05 16:44:50 +01:00
return " res:// " + path ;
2014-02-10 02:10:30 +01: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 " " ;
} ;
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 ) ;
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 ) {
2014-02-10 02:10:30 +01:00
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
}
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
2017-07-19 22:00:46 +02:00
String ProjectSettings : : globalize_path ( const String & p_path ) const {
2016-03-09 00:00:52 +01:00
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 ) ;
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 ) ;
} ;
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
2017-03-05 16:44:50 +01:00
if ( p_value . get_type ( ) = = Variant : : NIL )
2014-02-10 02:10:30 +01:00
props . erase ( p_name ) ;
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 ) ) {
2018-01-18 21:37:17 +01:00
if ( ! props [ p_name ] . overridden )
2017-03-05 16:44:50 +01:00
props [ p_name ] . variant = p_value ;
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
}
}
return true ;
}
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 ) ) {
2018-08-24 09:35:07 +02:00
WARN_PRINTS ( " 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 ;
int flags ;
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 {
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
_THREAD_SAFE_METHOD_
Set < _VCSort > vclist ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( Map < StringName , VariantContainer > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
const VariantContainer * v = & E - > get ( ) ;
2014-02-10 02:10:30 +01:00
if ( v - > hide_from_editor )
continue ;
_VCSort vc ;
2017-03-05 16:44:50 +01:00
vc . name = E - > key ( ) ;
vc . order = v - > order ;
vc . type = v - > variant . get_type ( ) ;
2014-02-10 02:10:30 +01: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 ;
2014-02-10 02:10:30 +01:00
else
2017-03-05 16:44:50 +01:00
vc . flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE ;
2014-02-10 02:10:30 +01:00
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 ( ) ) {
2014-02-10 02:10:30 +01:00
2017-07-19 22:00:46 +02:00
String prop_info_name = E - > get ( ) . name ;
int dot = prop_info_name . find ( " . " ) ;
if ( dot ! = - 1 )
prop_info_name = prop_info_name . substr ( 0 , dot ) ;
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 ) ;
2014-02-10 02:10:30 +01: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 ) ) ;
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 ) {
2014-02-10 02:10:30 +01:00
if ( PackedData : : get_singleton ( ) - > is_disabled ( ) )
return false ;
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
if ( ! ok )
return false ;
//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 ) {
2018-02-21 22:06:34 +01:00
2018-12-21 12:20:48 +01:00
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)
for ( Map < StringName , ProjectSettings : : VariantContainer > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
Variant value = E - > get ( ) . variant ;
if ( String ( E - > key ( ) ) . begins_with ( " input/ " ) & & value . get_type ( ) = = Variant : : ARRAY ) {
Array array = value ;
Dictionary action ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
action [ " events " ] = array ;
E - > get ( ) . variant = action ;
}
}
}
}
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 .
*/
2018-11-18 14:56:21 +01:00
Error ProjectSettings : : _setup ( const String & p_path , const String & p_main_pack , bool p_upwards ) {
2014-02-10 02:10:30 +01:00
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 ( ) ) {
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 " ) ;
if ( err = = OK ) {
// 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 " ) ;
if ( err = = OK ) {
// 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,
// 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 " ) ;
if ( err = = OK ) {
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 " ) ;
if ( err = = OK ) {
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 ( ) ;
2018-10-24 20:04:36 +02:00
String candidate = current_dir ;
2017-02-21 04:05:15 +01:00
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 ) {
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 " ) ) ;
if ( err = = OK ) {
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
candidate = current_dir ;
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 ( " .. " ) ;
if ( d - > get_current_dir ( ) = = current_dir )
2018-10-24 20:04:36 +02:00
break ; // not doing anything useful
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-03-05 16:44:50 +01:00
resource_path = candidate ;
2020-05-25 17:50:16 +02:00
resource_path = resource_path . replace ( " \\ " , " / " ) ; // Windows path to Unix path just in case.
2017-02-21 04:05:15 +01:00
memdelete ( d ) ;
2014-02-10 02:10:30 +01:00
2017-02-21 04:05:15 +01:00
if ( ! found )
2018-02-19 14:53:59 +01:00
return err ;
2014-02-10 02:10:30 +01:00
2020-05-25 17:50:16 +02:00
if ( resource_path . length ( ) & & resource_path [ resource_path . length ( ) - 1 ] = = ' / ' ) {
resource_path = resource_path . substr ( 0 , resource_path . length ( ) - 1 ) ; // Chop end.
}
2014-02-10 02:10:30 +01:00
return OK ;
}
2018-11-18 14:56:21 +01:00
Error ProjectSettings : : setup ( const String & p_path , const String & p_main_pack , bool p_upwards ) {
Error err = _setup ( p_path , p_main_pack , p_upwards ) ;
if ( err = = OK ) {
String custom_settings = GLOBAL_DEF ( " application/config/project_settings_override " , " " ) ;
if ( custom_settings ! = " " ) {
_load_settings_text ( custom_settings ) ;
}
}
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 {
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
_THREAD_SAFE_METHOD_
return props . has ( p_var ) ;
}
2017-07-19 22:00:46 +02:00
void ProjectSettings : : set_registering_order ( bool p_enable ) {
2015-12-16 03:39:36 +01:00
2017-03-05 16:44:50 +01:00
registering_order = p_enable ;
2015-12-16 03:39:36 +01:00
}
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 ;
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 + + ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
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 ;
2019-04-15 15:30:54 +02:00
err = decode_variant ( value , d . ptr ( ) , d . size ( ) , NULL , 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 ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
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
2017-03-05 16:44:50 +01:00
err = VariantParser : : parse_tag_assign_eof ( & stream , lines , error_text , next_tag , assign , value , NULL , true ) ;
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-02-11 01:08:49 +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 ) {
2018-02-19 14:53:59 +01:00
ERR_PRINTS ( " 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 ) + " . " ) ;
return err ;
}
2018-02-19 14:53:59 +01:00
2020-10-22 00:12:18 +02:00
// 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 ) + " . " ) ;
return 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
}
2017-07-19 22:00:46 +02:00
int ProjectSettings : : get_order ( const String & p_name ) const {
2014-02-10 02:10:30 +01:00
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 ) {
2014-02-10 02:10:30 +01:00
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 ) {
2017-07-18 02:05:38 +02:00
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 + + ;
}
}
2017-07-19 22:00:46 +02:00
void ProjectSettings : : clear ( const String & p_name ) {
2014-02-10 02:10:30 +01:00
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-02-11 01:08:49 +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
}
2017-07-19 22:00:46 +02: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
2017-03-05 16:44:50 +01:00
for ( Map < String , List < String > > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( List < String > : : Element * F = E - > get ( ) . front ( ) ; F ; F = F - > next ( ) ) {
2014-02-10 02:10:30 +01:00
count + + ;
}
}
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 ;
file - > store_32 ( key . length ( ) ) ;
file - > store_string ( key ) ;
int len ;
2019-03-26 16:52:42 +01:00
err = encode_variant ( p_custom_features , NULL , 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
2017-03-05 16:44:50 +01:00
for ( Map < String , List < String > > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( List < String > : : Element * F = E - > get ( ) . front ( ) ; F ; F = F - > next ( ) ) {
2014-02-10 02:10:30 +01:00
String key = F - > get ( ) ;
2017-03-05 16:44:50 +01:00
if ( E - > key ( ) ! = " " )
key = E - > key ( ) + " / " + key ;
2014-02-10 02:10:30 +01:00
Variant value ;
if ( p_custom . has ( key ) )
2017-03-05 16:44:50 +01:00
value = p_custom [ key ] ;
2014-02-10 02:10:30 +01:00
else
value = get ( key ) ;
file - > store_32 ( key . length ( ) ) ;
file - > store_string ( key ) ;
int len ;
2019-04-15 15:30:54 +02:00
err = encode_variant ( value , NULL , len , true ) ;
2017-03-05 16:44:50 +01:00
if ( err ! = OK )
2014-02-10 02:10:30 +01:00
memdelete ( file ) ;
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 ) ;
2017-03-05 16:44:50 +01:00
if ( err ! = OK )
2014-02-10 02:10:30 +01:00
memdelete ( file ) ;
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 ;
}
2017-07-19 22:00:46 +02: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 " ) ;
2017-07-19 22:00:46 +02:00
if ( p_custom_features ! = String ( ) )
2017-07-25 21:02:38 +02:00
file - > store_string ( " custom_features= \" " + p_custom_features + " \" \n " ) ;
file - > store_string ( " \n " ) ;
2017-02-05 01:02:52 +01:00
2017-03-05 16:44:50 +01:00
for ( Map < String , List < String > > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2017-02-05 01:02:52 +01:00
2017-03-05 16:44:50 +01:00
if ( E ! = props . front ( ) )
2014-02-10 02:10:30 +01:00
file - > store_string ( " \n " ) ;
2017-03-05 16:44:50 +01:00
if ( E - > key ( ) ! = " " )
file - > store_string ( " [ " + E - > key ( ) + " ] \n \n " ) ;
for ( List < String > : : Element * F = E - > get ( ) . front ( ) ; F ; F = F - > next ( ) ) {
2014-02-10 02:10:30 +01:00
String key = F - > get ( ) ;
2017-03-05 16:44:50 +01:00
if ( E - > key ( ) ! = " " )
key = E - > key ( ) + " / " + key ;
2014-02-10 02:10:30 +01:00
Variant value ;
if ( p_custom . has ( key ) )
2017-03-05 16:44:50 +01:00
value = p_custom [ key ] ;
2014-02-10 02:10:30 +01:00
else
value = get ( key ) ;
2017-01-11 20:15:40 +01:00
String vstr ;
2017-03-05 16:44:50 +01:00
VariantWriter : : write_to_string ( value , vstr ) ;
2019-12-20 03:03:15 +01:00
file - > store_string ( F - > get ( ) . 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 ) ;
} ;
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 ) {
2014-02-10 02:10:30 +01:00
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
Set < _VCSort > vclist ;
2017-07-26 15:28:54 +02:00
if ( p_merge_with_current ) {
for ( Map < StringName , VariantContainer > : : Element * G = props . front ( ) ; G ; G = G - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-07-26 15:28:54 +02:00
const VariantContainer * v = & G - > get ( ) ;
2014-02-10 02:10:30 +01:00
2017-07-26 15:28:54 +02:00
if ( v - > hide_from_editor )
continue ;
2014-02-10 02:10:30 +01:00
2017-07-26 15:28:54 +02:00
if ( p_custom . has ( G - > key ( ) ) )
continue ;
2014-02-10 02:10:30 +01:00
2017-07-26 15:28:54 +02:00
_VCSort vc ;
vc . name = G - > key ( ) ; //*k;
vc . order = v - > order ;
vc . type = v - > variant . get_type ( ) ;
vc . flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE ;
if ( v - > variant = = v - > initial )
continue ;
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
}
2017-03-05 16:44:50 +01:00
for ( const Map < String , Variant > : : Element * E = p_custom . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-07-26 15:28:54 +02:00
// Lookup global prop to store in the same order
Map < StringName , VariantContainer > : : Element * global_prop = props . find ( E - > key ( ) ) ;
2014-02-10 02:10:30 +01:00
_VCSort vc ;
2017-03-05 16:44:50 +01:00
vc . name = E - > key ( ) ;
2017-07-26 15:28:54 +02:00
vc . order = global_prop ? global_prop - > get ( ) . order : 0xFFFFFFF ;
2017-03-05 16:44:50 +01:00
vc . type = E - > get ( ) . get_type ( ) ;
vc . flags = PROPERTY_USAGE_STORAGE ;
2014-02-10 02:10:30 +01:00
vclist . insert ( vc ) ;
}
2017-03-05 16:44:50 +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 ( " / " ) ;
2017-03-05 16:44:50 +01:00
if ( div < 0 )
category = " " ;
2014-02-10 02:10:30 +01: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 + + ) {
if ( i > 0 )
custom_features + = " , " ;
String f = p_custom_features [ i ] . strip_edges ( ) . replace ( " \" " , " " ) ;
custom_features + = f ;
}
2017-04-18 15:46:37 +02:00
if ( p_path . ends_with ( " .godot " ) )
2017-07-19 22:00:46 +02:00
return _save_settings_text ( p_path , props , p_custom , custom_features ) ;
else if ( p_path . ends_with ( " .binary " ) )
return _save_settings_binary ( p_path , props , p_custom , custom_features ) ;
2014-02-10 02:10:30 +01: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
}
}
2018-07-19 23:58:15 +02:00
Variant _GLOBAL_DEF ( const String & p_var , const Variant & p_default , bool p_restart_if_changed ) {
2014-02-10 02:10:30 +01:00
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 ) ;
2018-07-19 23:58:15 +02:00
ProjectSettings : : get_singleton ( ) - > set_restart_if_changed ( p_var , p_restart_if_changed ) ;
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 ;
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = pi . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
if ( ! E - > get ( ) . name . begins_with ( " optimizer_presets/ " ) )
continue ;
2017-03-05 16:44:50 +01:00
names . push_back ( E - > get ( ) . 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 ) ;
if ( p_info . has ( " hint " ) )
pinfo . hint = PropertyHint ( p_info [ " hint " ] . operator int ( ) ) ;
if ( p_info . has ( " hint_string " ) )
pinfo . hint_string = p_info [ " hint_string " ] ;
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 ) {
2014-02-10 02:10:30 +01:00
2017-07-19 22:00:46 +02:00
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 ) {
2014-02-10 02:10:30 +01:00
2017-01-05 13:16:00 +01:00
if ( ! props . has ( p_name ) )
return false ;
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 ) {
2017-01-05 13:16:00 +01:00
if ( ! props . has ( p_name ) )
return Variant ( ) ;
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 ) ;
}
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
}
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.
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
singleton = this ;
2017-07-18 02:05:38 +02:00
last_order = NO_BUILTIN_ORDER_BASE ;
last_builtin_order = 0 ;
2017-07-19 22:00:46 +02:00
disable_feature_overrides = false ;
2017-03-05 16:44:50 +01:00
registering_order = true ;
2014-02-10 02:10:30 +01:00
2018-08-14 18:44:22 +02:00
Array events ;
Dictionary action ;
2017-05-20 17:38:03 +02:00
Ref < InputEventKey > key ;
Ref < InputEventJoypadButton > joyb ;
2014-02-10 02:10:30 +01:00
2017-07-18 02:05:38 +02:00
GLOBAL_DEF ( " application/config/name " , " " ) ;
2019-08-17 18:20:17 +02:00
GLOBAL_DEF ( " application/config/description " , " " ) ;
custom_prop_info [ " application/config/description " ] = PropertyInfo ( Variant : : STRING , " application/config/description " , PROPERTY_HINT_MULTILINE_TEXT ) ;
2017-07-18 02:05:38 +02:00
GLOBAL_DEF ( " 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 ) ;
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 " , " " ) ;
2019-04-05 17:19:25 +02:00
GLOBAL_DEF ( " audio/default_bus_layout " , " res://default_bus_layout.tres " ) ;
custom_prop_info [ " audio/default_bus_layout " ] = PropertyInfo ( Variant : : STRING , " audio/default_bus_layout " , PROPERTY_HINT_FILE , " *.tres " ) ;
2014-02-10 02:10:30 +01:00
2019-02-04 22:59:51 +01:00
PoolStringArray extensions = PoolStringArray ( ) ;
extensions . push_back ( " gd " ) ;
if ( Engine : : get_singleton ( ) - > has_singleton ( " GodotSharp " ) )
extensions . push_back ( " cs " ) ;
extensions . push_back ( " shader " ) ;
GLOBAL_DEF ( " editor/search_in_file_extensions " , extensions ) ;
custom_prop_info [ " editor/search_in_file_extensions " ] = PropertyInfo ( Variant : : POOL_STRING_ARRAY , " editor/search_in_file_extensions " ) ;
2019-08-22 17:59:43 +02: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 ) ;
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
2017-08-06 15:26:07 +02:00
key - > set_scancode ( KEY_ENTER ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
2017-08-06 15:26:07 +02:00
key - > set_scancode ( KEY_KP_ENTER ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
key - > set_scancode ( KEY_SPACE ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
2017-05-20 17:38:03 +02:00
joyb . instance ( ) ;
joyb - > set_button_index ( JOY_BUTTON_0 ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( joyb ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_accept " , action ) ;
2015-12-14 22:36:53 +01:00
input_presets . push_back ( " input/ui_accept " ) ;
2014-02-10 02:10:30 +01:00
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
key - > set_scancode ( KEY_SPACE ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
2017-05-20 17:38:03 +02:00
joyb . instance ( ) ;
joyb - > set_button_index ( JOY_BUTTON_3 ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( joyb ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_select " , action ) ;
2015-12-14 22:36:53 +01:00
input_presets . push_back ( " input/ui_select " ) ;
2015-06-06 14:44:38 +02:00
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
key - > set_scancode ( KEY_ESCAPE ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
2017-05-20 17:38:03 +02:00
joyb . instance ( ) ;
joyb - > set_button_index ( JOY_BUTTON_1 ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( joyb ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_cancel " , action ) ;
2015-12-14 22:36:53 +01:00
input_presets . push_back ( " input/ui_cancel " ) ;
2014-02-10 02:10:30 +01:00
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
key - > set_scancode ( KEY_TAB ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_focus_next " , action ) ;
2015-12-14 22:36:53 +01:00
input_presets . push_back ( " input/ui_focus_next " ) ;
2014-02-10 02:10:30 +01:00
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
key - > set_scancode ( KEY_TAB ) ;
key - > set_shift ( true ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_focus_prev " , action ) ;
2015-12-14 22:36:53 +01:00
input_presets . push_back ( " input/ui_focus_prev " ) ;
2014-02-10 02:10:30 +01:00
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
key - > set_scancode ( KEY_LEFT ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
2017-05-20 17:38:03 +02:00
joyb . instance ( ) ;
joyb - > set_button_index ( JOY_DPAD_LEFT ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( joyb ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_left " , action ) ;
2015-12-14 22:36:53 +01:00
input_presets . push_back ( " input/ui_left " ) ;
2014-02-10 02:10:30 +01:00
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
key - > set_scancode ( KEY_RIGHT ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
2017-05-20 17:38:03 +02:00
joyb . instance ( ) ;
joyb - > set_button_index ( JOY_DPAD_RIGHT ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( joyb ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_right " , action ) ;
2015-12-14 22:36:53 +01:00
input_presets . push_back ( " input/ui_right " ) ;
2014-02-10 02:10:30 +01:00
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
key - > set_scancode ( KEY_UP ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
2017-05-20 17:38:03 +02:00
joyb . instance ( ) ;
joyb - > set_button_index ( JOY_DPAD_UP ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( joyb ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_up " , action ) ;
2015-12-14 22:36:53 +01:00
input_presets . push_back ( " input/ui_up " ) ;
2014-02-10 02:10:30 +01:00
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
key - > set_scancode ( KEY_DOWN ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
2017-05-20 17:38:03 +02:00
joyb . instance ( ) ;
joyb - > set_button_index ( JOY_DPAD_DOWN ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( joyb ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_down " , action ) ;
2015-12-14 22:36:53 +01:00
input_presets . push_back ( " input/ui_down " ) ;
2014-02-10 02:10:30 +01:00
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
key - > set_scancode ( KEY_PAGEUP ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_page_up " , action ) ;
2015-12-14 22:36:53 +01:00
input_presets . push_back ( " input/ui_page_up " ) ;
2014-02-10 02:10:30 +01:00
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2017-05-20 17:38:03 +02:00
key . instance ( ) ;
key - > set_scancode ( KEY_PAGEDOWN ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_page_down " , action ) ;
2015-12-14 22:36:53 +01:00
input_presets . push_back ( " input/ui_page_down " ) ;
2014-02-10 02:10:30 +01:00
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2018-02-23 12:17:15 +01:00
key . instance ( ) ;
key - > set_scancode ( KEY_HOME ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_home " , action ) ;
2018-02-23 12:17:15 +01:00
input_presets . push_back ( " input/ui_home " ) ;
2018-08-14 18:44:22 +02:00
action = Dictionary ( ) ;
action [ " deadzone " ] = Variant ( 0.5f ) ;
events = Array ( ) ;
2018-02-23 12:17:15 +01:00
key . instance ( ) ;
key - > set_scancode ( KEY_END ) ;
2018-08-14 18:44:22 +02:00
events . push_back ( key ) ;
action [ " events " ] = events ;
GLOBAL_DEF ( " input/ui_end " , action ) ;
2018-02-23 12:17:15 +01:00
input_presets . push_back ( " input/ui_end " ) ;
2017-07-18 02:05:38 +02:00
custom_prop_info [ " display/window/handheld/orientation " ] = PropertyInfo ( Variant : : STRING , " display/window/handheld/orientation " , PROPERTY_HINT_ENUM , " landscape,portrait,reverse_landscape,reverse_portrait,sensor_landscape,sensor_portrait,sensor " ) ;
2017-03-05 16:44:50 +01:00
custom_prop_info [ " rendering/threads/thread_model " ] = PropertyInfo ( Variant : : INT , " rendering/threads/thread_model " , PROPERTY_HINT_ENUM , " Single-Unsafe,Single-Safe,Multi-Threaded " ) ;
custom_prop_info [ " physics/2d/thread_model " ] = PropertyInfo ( Variant : : INT , " physics/2d/thread_model " , PROPERTY_HINT_ENUM , " Single-Unsafe,Single-Safe,Multi-Threaded " ) ;
2017-07-19 22:00:46 +02:00
custom_prop_info [ " rendering/quality/intended_usage/framebuffer_allocation " ] = PropertyInfo ( Variant : : INT , " rendering/quality/intended_usage/framebuffer_allocation " , PROPERTY_HINT_ENUM , " 2D,2D Without Sampling,3D,3D Without Effects " ) ;
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 " ) ;
2017-06-12 03:27:07 +02:00
2019-03-07 10:40:44 +01:00
// Would ideally be defined in an Android-specific file, but then it doesn't appear in the docs
GLOBAL_DEF ( " android/modules " , " " ) ;
2017-03-05 16:44:50 +01:00
using_datapack = false ;
2014-02-10 02:10:30 +01:00
}
2017-07-19 22:00:46 +02:00
ProjectSettings : : ~ ProjectSettings ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
singleton = NULL ;
2014-02-10 02:10:30 +01:00
}