-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathmod_loader_store.gd
237 lines (185 loc) · 9.08 KB
/
mod_loader_store.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
extends Node
# ModLoaderStore
# Singleton (autoload) for storing data. Should be added before ModLoader,
# as an autoload called `ModLoaderStore`
# Constants
# =============================================================================
# Most of these settings should never need to change, aside from the DEBUG_*
# options (which should be `false` when distributing compiled PCKs)
const MODLOADER_VERSION = "6.2.0"
# If true, a complete array of filepaths is stored for each mod. This is
# disabled by default because the operation can be very expensive, but may
# be useful for debugging
const DEBUG_ENABLE_STORING_FILEPATHS := false
# This is where mod ZIPs are unpacked to
const UNPACKED_DIR := "res://mods-unpacked/"
# Set to true to require using "--enable-mods" to enable them
const REQUIRE_CMD_LINE := false
const LOG_NAME = "ModLoader:Store"
# Vars
# =============================================================================
# Order for mods to be loaded in, set by `get_load_order`
var mod_load_order := []
# Stores data for every found/loaded mod
var mod_data := {}
# Any mods that are missing their dependancies are added to this
# Example property: "mod_id": ["dep_mod_id_0", "dep_mod_id_2"]
var mod_missing_dependencies := {}
# Set to false after ModLoader._init()
# Helps to decide whether a script extension should go through the _ModLoaderScriptExtension.handle_script_extensions() process
var is_initializing := true
# Used when loading mod zips to determine which mod zip corresponds to which mod directory in the UNPACKED_DIR.
var previous_mod_dirs := []
# Store all extenders paths
var script_extensions := []
# True if ModLoader has displayed the warning about using zipped mods
var has_shown_editor_zips_warning := false
# Things to keep to ensure they are not garbage collected (used by `save_scene`)
var saved_objects := []
# Stores all the taken over scripts for restoration
var saved_scripts := {}
# Stores main scripts for mod disabling
var saved_mod_mains := {}
# Stores script extension paths with the key being the namespace of a mod
var saved_extension_paths := {}
# Keeps track of logged messages, to avoid flooding the log with duplicate notices
# Can also be used by mods, eg. to create an in-game developer console that
# shows messages
var logged_messages := {
"all": {},
"by_mod": {},
"by_type": {
"fatal-error": {},
"error": {},
"warning": {},
"info": {},
"success": {},
"debug": {},
}
}
# Active user profile
var current_user_profile: ModUserProfile
# List of user profiles loaded from user://mod_user_profiles.json
var user_profiles := {}
# ModLoader cache is stored in user://mod_loader_cache.json
var cache := {}
# These variables handle various options, which can be changed either via
# Godot's GUI (with the options.tres resource file), or via CLI args.
# Usage: `ModLoaderStore.ml_options.KEY`
# See: res://addons/mod_loader/options/options.tres
# See: res://addons/mod_loader/resources/options_profile.gd
var ml_options := {
enable_mods = true,
log_level = ModLoaderLog.VERBOSITY_LEVEL.DEBUG,
# Mods that can't be disabled or enabled in a user profile (contains mod IDs as strings)
locked_mods = [],
# Array of disabled mods (contains mod IDs as strings)
disabled_mods = [],
# If this flag is set to true, the ModLoaderStore and ModLoader Autoloads don't have to be the first Autoloads.
# The ModLoaderStore Autoload still needs to be placed before the ModLoader Autoload.
allow_modloader_autoloads_anywhere = false,
# If true, ModLoader will load mod ZIPs from the Steam workshop directory,
# instead of the default location (res://mods)
steam_workshop_enabled = false,
# Overrides for the path mods/configs/workshop folders are loaded from.
# Only applied if custom settings are provided, either via the options.tres
# resource, or via CLI args. Note that CLI args can be tested in the editor
# via: Project Settings > Display> Editor > Main Run Args
override_path_to_mods = "", # Default if unspecified: "res://mods" -- get with _ModLoaderPath.get_path_to_mods()
override_path_to_configs = "", # Default if unspecified: "res://configs" -- get with _ModLoaderPath.get_path_to_configs()
# Can be used in the editor to load mods from your Steam workshop directory
override_path_to_workshop = "",
# If true, using deprecated funcs will trigger a warning, instead of a fatal
# error. This can be helpful when developing mods that depend on a mod that
# hasn't been updated to fix the deprecated issues yet
ignore_deprecated_errors = false,
# Array of mods that should be ignored when logging messages (contains mod IDs as strings)
ignored_mod_names_in_log = [],
}
# Methods
# =============================================================================
func _init():
_update_ml_options_from_options_resource()
_update_ml_options_from_cli_args()
# ModLoaderStore is passed as argument so the cache data can be loaded on _init()
_ModLoaderCache.init_cache(self)
# Update ModLoader's options, via the custom options resource
func _update_ml_options_from_options_resource() -> void:
# Path to the options resource
# See: res://addons/mod_loader/resources/options_current.gd
var ml_options_path := "res://addons/mod_loader/options/options.tres"
# Get user options for ModLoader
if not _ModLoaderFile.file_exists(ml_options_path):
ModLoaderLog.fatal(str("A critical file is missing: ", ml_options_path), LOG_NAME)
var options_resource: ModLoaderCurrentOptions = load(ml_options_path)
if options_resource.current_options == null:
ModLoaderLog.warning(str(
"No current options are set. Falling back to defaults. ",
"Edit your options at %s. " % ml_options_path
), LOG_NAME)
else:
var current_options = options_resource.current_options
if not current_options is ModLoaderOptionsProfile:
ModLoaderLog.error(str(
"Current options is not a valid Resource of type ModLoaderOptionsProfile. ",
"Please edit your options at %s. " % ml_options_path
), LOG_NAME)
# Update from the options in the resource
for key in ml_options:
ml_options[key] = current_options[key]
# Get options overrides by feature tags
# An override is saved as Dictionary[String: ModLoaderOptionsProfile]
for feature_tag in options_resource.feature_override_options.keys():
if not feature_tag is String:
ModLoaderLog.error(str(
"Options override keys are required to be of type String. Failing key: \"%s.\" " % feature_tag,
"Please edit your options at %s. " % ml_options_path,
"Consult the documentation for all available feature tags: ",
"https://docs.godotengine.org/en/3.5/tutorials/export/feature_tags.html"
), LOG_NAME)
continue
if not OS.has_feature(feature_tag):
ModLoaderLog.info("Options override feature tag \"%s\". does not apply, skipping." % feature_tag, LOG_NAME)
continue
ModLoaderLog.info("Applying options override with feature tag \"%s\"." % feature_tag, LOG_NAME)
var override_options = options_resource.feature_override_options[feature_tag]
if not override_options is ModLoaderOptionsProfile:
ModLoaderLog.error(str(
"Options override is not a valid Resource of type ModLoaderOptionsProfile. ",
"Options override key with invalid resource: \"%s\". " % feature_tag,
"Please edit your options at %s. " % ml_options_path
), LOG_NAME)
continue
# Update from the options in the resource
for key in ml_options:
ml_options[key] = override_options[key]
# Update ModLoader's options, via CLI args
func _update_ml_options_from_cli_args() -> void:
# Disable mods
if _ModLoaderCLI.is_running_with_command_line_arg("--disable-mods"):
ml_options.enable_mods = false
# Override paths to mods
# Set via: --mods-path
# Example: --mods-path="C://path/mods"
var cmd_line_mod_path := _ModLoaderCLI.get_cmd_line_arg_value("--mods-path")
if cmd_line_mod_path:
ml_options.override_path_to_mods = cmd_line_mod_path
ModLoaderLog.info("The path mods are loaded from has been changed via the CLI arg `--mods-path`, to: " + cmd_line_mod_path, LOG_NAME)
# Override paths to configs
# Set via: --configs-path
# Example: --configs-path="C://path/configs"
var cmd_line_configs_path := _ModLoaderCLI.get_cmd_line_arg_value("--configs-path")
if cmd_line_configs_path:
ml_options.override_path_to_configs = cmd_line_configs_path
ModLoaderLog.info("The path configs are loaded from has been changed via the CLI arg `--configs-path`, to: " + cmd_line_configs_path, LOG_NAME)
# Log level verbosity
if _ModLoaderCLI.is_running_with_command_line_arg("-vvv") or _ModLoaderCLI.is_running_with_command_line_arg("--log-debug"):
ml_options.log_level = ModLoaderLog.VERBOSITY_LEVEL.DEBUG
elif _ModLoaderCLI.is_running_with_command_line_arg("-vv") or _ModLoaderCLI.is_running_with_command_line_arg("--log-info"):
ml_options.log_level = ModLoaderLog.VERBOSITY_LEVEL.INFO
elif _ModLoaderCLI.is_running_with_command_line_arg("-v") or _ModLoaderCLI.is_running_with_command_line_arg("--log-warning"):
ml_options.log_level = ModLoaderLog.VERBOSITY_LEVEL.WARNING
# Ignored mod_names in log
var ignore_mod_names := _ModLoaderCLI.get_cmd_line_arg_value("--log-ignore")
if not ignore_mod_names == "":
ml_options.ignored_mod_names_in_log = ignore_mod_names.split(",")