Skip to content

Commit 70a2836

Browse files
committed
Revert "refactor: ♻️ added GDRE download step"
This reverts commit 95ce66c.
1 parent c273061 commit 70a2836

File tree

3 files changed

+297
-384
lines changed

3 files changed

+297
-384
lines changed

addons/mod_loader/mod_loader_setup.gd

+297-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
extends SceneTree
22

3-
43
const LOG_NAME := "ModLoader:Setup"
54

5+
const settings := {
6+
"IS_LOADER_SETUP_APPLIED": "application/run/is_loader_setup_applied",
7+
"IS_LOADER_SET_UP": "application/run/is_loader_set_up",
8+
"MOD_LOADER_AUTOLOAD": "autoload/ModLoader",
9+
}
10+
611
# IMPORTANT: use the ModLoaderLog via this variable within this script!
712
# Otherwise, script compilation will break on first load since the class is not defined.
813
var ModLoaderSetupLog: Object = load("res://addons/mod_loader/setup/setup_log.gd")
914
var ModLoaderSetupUtils: Object = load("res://addons/mod_loader/setup/setup_utils.gd")
1015

16+
var path := {}
17+
var file_name := {}
18+
var is_only_setup: bool = ModLoaderSetupUtils.is_running_with_command_line_arg("--only-setup")
1119
var is_setup_create_override_cfg: bool = ModLoaderSetupUtils.is_running_with_command_line_arg(
1220
"--setup-create-override-cfg"
1321
)
@@ -31,7 +39,7 @@ func _init() -> void:
3139
modded_start()
3240
return
3341

34-
change_scene_to_file("res://addons/mod_loader/setup/setup.tscn")
42+
setup_modloader()
3543

3644

3745
# ModLoader already setup - switch to the main scene
@@ -41,3 +49,290 @@ func modded_start() -> void:
4149
root.set_title("%s (Modded)" % ProjectSettings.get_setting("application/config/name"))
4250

4351
change_scene_to_file.call_deferred(ProjectSettings.get_setting("application/run/main_scene"))
52+
53+
54+
# Set up the ModLoader as an autoload and register the other global classes.
55+
func setup_modloader() -> void:
56+
ModLoaderSetupLog.info("Setting up ModLoader", LOG_NAME)
57+
58+
# Setup path and file_name dict with all required paths and file names.
59+
setup_file_data()
60+
61+
# Add ModLoader autoload (the * marks the path as autoload)
62+
reorder_autoloads()
63+
ProjectSettings.set_setting(settings.IS_LOADER_SET_UP, true)
64+
65+
# The game needs to be restarted first, before the loader is truly set up
66+
# Set this here and check it elsewhere to prompt the user for a restart
67+
ProjectSettings.set_setting(settings.IS_LOADER_SETUP_APPLIED, false)
68+
69+
if is_setup_create_override_cfg:
70+
handle_override_cfg()
71+
else:
72+
handle_injection()
73+
74+
# ModLoader is set up. A game restart is required to apply the ProjectSettings.
75+
ModLoaderSetupLog.info("ModLoader is set up, a game restart is required.", LOG_NAME)
76+
77+
match true:
78+
# If the --only-setup cli argument is passed, quit with exit code 0
79+
is_only_setup:
80+
quit(0)
81+
# If no cli argument is passed, show message with OS.alert() and user has to restart the game
82+
_:
83+
OS.alert(
84+
"The Godot ModLoader has been set up. The game needs to be restarted to apply the changes. Confirm to restart."
85+
)
86+
restart()
87+
88+
89+
# Reorders the autoloads in the project settings, to get the ModLoader on top.
90+
func reorder_autoloads() -> void:
91+
# remove and re-add autoloads
92+
var original_autoloads := {}
93+
for prop in ProjectSettings.get_property_list():
94+
var name: String = prop.name
95+
if name.begins_with("autoload/"):
96+
var value: String = ProjectSettings.get_setting(name)
97+
original_autoloads[name] = value
98+
99+
ModLoaderSetupLog.info(
100+
"Start reorder autoloads current state: %s" % JSON.stringify(original_autoloads, "\t"),
101+
LOG_NAME
102+
)
103+
104+
for autoload in original_autoloads.keys():
105+
ProjectSettings.set_setting(autoload, null)
106+
107+
# Add ModLoaderStore autoload (the * marks the path as autoload)
108+
ProjectSettings.set_setting(
109+
"autoload/ModLoaderStore", "*" + "res://addons/mod_loader/mod_loader_store.gd"
110+
)
111+
112+
# Add ModLoader autoload (the * marks the path as autoload)
113+
ProjectSettings.set_setting("autoload/ModLoader", "*" + "res://addons/mod_loader/mod_loader.gd")
114+
115+
# add all previous autoloads back again
116+
for autoload in original_autoloads.keys():
117+
ProjectSettings.set_setting(autoload, original_autoloads[autoload])
118+
119+
var new_autoloads := {}
120+
for prop in ProjectSettings.get_property_list():
121+
var name: String = prop.name
122+
if name.begins_with("autoload/"):
123+
var value: String = ProjectSettings.get_setting(name)
124+
new_autoloads[name] = value
125+
126+
ModLoaderSetupLog.info(
127+
"Reorder autoloads completed - new state: %s" % JSON.stringify(new_autoloads, "\t"),
128+
LOG_NAME
129+
)
130+
131+
132+
# Saves the ProjectSettings to a override.cfg file in the base game directory.
133+
func handle_override_cfg() -> void:
134+
ModLoaderSetupLog.debug("using the override.cfg file", LOG_NAME)
135+
136+
# Make the '.godot' dir public as 'godot' and copy all files to the public dir.
137+
make_project_data_public()
138+
139+
# Combine mod_loader and game global classes
140+
var global_script_class_cache_combined := get_combined_global_script_class_cache()
141+
global_script_class_cache_combined.save("res://godot/global_script_class_cache.cfg")
142+
143+
var _save_custom_error: int = ProjectSettings.save_custom(
144+
ModLoaderSetupUtils.get_override_path()
145+
)
146+
147+
148+
# Creates the project.binary file, adds it to the pck and removes the no longer needed project.binary file.
149+
func handle_injection() -> void:
150+
var is_embedded: bool = not FileAccess.file_exists(path.pck)
151+
var injection_path: String = path.exe if is_embedded else path.pck
152+
var file_extension := injection_path.get_extension()
153+
154+
ModLoaderSetupLog.debug("Start injection", LOG_NAME)
155+
# Create temp dir
156+
ModLoaderSetupLog.debug('Creating temp dir at "%s"' % path.temp_dir_path, LOG_NAME)
157+
DirAccess.make_dir_recursive_absolute(path.temp_dir_path)
158+
159+
# Create project.binary
160+
ModLoaderSetupLog.debug(
161+
'Storing project.binary at "%s"' % path.temp_project_binary_path, LOG_NAME
162+
)
163+
var _error_save_custom_project_binary = ProjectSettings.save_custom(
164+
path.temp_project_binary_path
165+
)
166+
# Create combined global class cache cfg
167+
var combined_global_script_class_cache_file := get_combined_global_script_class_cache()
168+
ModLoaderSetupLog.debug(
169+
'Storing global_script_class_cache at "%s"' % path.temp_global_script_class_cache_path,
170+
LOG_NAME
171+
)
172+
# Create the .godot dir inside the temp dir
173+
DirAccess.make_dir_recursive_absolute(path.temp_dir_path.path_join(".godot"))
174+
# Save the global class cache config file
175+
combined_global_script_class_cache_file.save(path.temp_global_script_class_cache_path)
176+
177+
inject(injection_path, is_embedded)
178+
179+
# Rename vanilla
180+
var modded_path := "%s-modded.%s" % [injection_path.get_basename(), file_extension]
181+
var vanilla_path := "%s-vanilla.%s" % [injection_path.get_basename(), file_extension]
182+
183+
DirAccess.rename_absolute(injection_path, vanilla_path)
184+
ModLoaderSetupLog.debug('Renamed "%s" to "%s"' % [injection_path, vanilla_path], LOG_NAME)
185+
186+
# Rename modded
187+
DirAccess.rename_absolute(modded_path, injection_path)
188+
ModLoaderSetupLog.debug('Renamed "%s" to "%s"' % [modded_path, injection_path], LOG_NAME)
189+
190+
clean_up()
191+
192+
193+
# Add modified binary to the pck
194+
func inject(injection_path: String, is_embedded := false) -> void:
195+
var arguments := []
196+
arguments.push_back("--pck-patch=%s" % injection_path)
197+
if is_embedded:
198+
arguments.push_back("--embed=%s" % injection_path)
199+
arguments.push_back(
200+
"--patch-file=%s=%s" % [path.temp_project_binary_path, path.project_binary_path_internal]
201+
)
202+
arguments.push_back(
203+
(
204+
"--patch-file=%s=%s"
205+
% [
206+
path.temp_global_script_class_cache_path,
207+
path.global_script_class_cache_path_internal
208+
]
209+
)
210+
)
211+
arguments.push_back(
212+
(
213+
"--output=%s"
214+
% path.game_base_dir.path_join(
215+
(
216+
"%s-modded.%s"
217+
% [file_name[injection_path.get_extension()], injection_path.get_extension()]
218+
)
219+
)
220+
)
221+
)
222+
223+
# For unknown reasons the output only displays a single "[" - so only the executed arguments are logged.
224+
ModLoaderSetupLog.debug("Injection started: %s %s" % [path.gdre, arguments], LOG_NAME)
225+
var output := []
226+
var _exit_code_inject := OS.execute(path.gdre, arguments, output)
227+
ModLoaderSetupLog.debug("Injection completed: %s" % output, LOG_NAME)
228+
229+
230+
# Removes the temp files
231+
func clean_up() -> void:
232+
ModLoaderSetupLog.debug("Start clean up", LOG_NAME)
233+
DirAccess.remove_absolute(path.temp_project_binary_path)
234+
ModLoaderSetupLog.debug('Removed: "%s"' % path.temp_project_binary_path, LOG_NAME)
235+
DirAccess.remove_absolute(path.temp_global_script_class_cache_path)
236+
ModLoaderSetupLog.debug('Removed: "%s"' % path.temp_global_script_class_cache_path, LOG_NAME)
237+
DirAccess.remove_absolute(path.temp_dir_path.path_join(".godot"))
238+
ModLoaderSetupLog.debug('Removed: "%s"' % path.temp_dir_path.path_join(".godot"), LOG_NAME)
239+
DirAccess.remove_absolute(path.temp_dir_path)
240+
ModLoaderSetupLog.debug('Removed: "%s"' % path.temp_dir_path, LOG_NAME)
241+
ModLoaderSetupLog.debug("Clean up completed", LOG_NAME)
242+
243+
244+
# Initialize the path and file_name dictionary
245+
func setup_file_data() -> void:
246+
# C:/path/to/game/game.exe
247+
path.exe = OS.get_executable_path()
248+
# C:/path/to/game/
249+
path.game_base_dir = ModLoaderSetupUtils.get_local_folder_dir()
250+
# C:/path/to/game/addons/mod_loader
251+
path.mod_loader_dir = path.game_base_dir + "addons/mod_loader/"
252+
path.gdre = path.mod_loader_dir + get_gdre_path()
253+
path.temp_dir_path = path.mod_loader_dir + "setup/temp"
254+
path.temp_project_binary_path = path.temp_dir_path + "/project.binary"
255+
path.temp_global_script_class_cache_path = (
256+
path.temp_dir_path
257+
+ "/.godot/global_script_class_cache.cfg"
258+
)
259+
path.global_script_class_cache_path_internal = "res://.godot/global_script_class_cache.cfg"
260+
path.project_binary_path_internal = "res://project.binary"
261+
# can be supplied to override the exe_name
262+
file_name.cli_arg_exe = ModLoaderSetupUtils.get_cmd_line_arg_value("--exe-name")
263+
# can be supplied to override the pck_name
264+
file_name.cli_arg_pck = ModLoaderSetupUtils.get_cmd_line_arg_value("--pck-name")
265+
# game - or use the value of cli_arg_exe_name if there is one
266+
file_name.exe = (
267+
ModLoaderSetupUtils.get_file_name_from_path(path.exe, false, true)
268+
if file_name.cli_arg_exe == ""
269+
else file_name.cli_arg_exe
270+
)
271+
# game - or use the value of cli_arg_pck_name if there is one
272+
# using exe_path.get_file() instead of exe_name
273+
# so you don't override the pck_name with the --exe-name cli arg
274+
# the main pack name is the same as the .exe name
275+
# if --main-pack cli arg is not set
276+
file_name.pck = (
277+
ModLoaderSetupUtils.get_file_name_from_path(path.exe, false, true)
278+
if file_name.cli_arg_pck == ""
279+
else file_name.cli_arg_pck
280+
)
281+
# C:/path/to/game/game.pck
282+
path.pck = path.game_base_dir.path_join(file_name.pck + ".pck")
283+
284+
ModLoaderSetupLog.debug_json_print("path: ", path, LOG_NAME)
285+
ModLoaderSetupLog.debug_json_print("file_name: ", file_name, LOG_NAME)
286+
287+
288+
func make_project_data_public() -> void:
289+
ModLoaderSetupLog.info("Register Global Classes", LOG_NAME)
290+
ProjectSettings.set_setting("application/config/use_hidden_project_data_directory", false)
291+
292+
var godot_files = ModLoaderSetupUtils.get_flat_view_dict("res://.godot")
293+
294+
ModLoaderSetupLog.info('Copying all files from "res://.godot" to "res://godot".', LOG_NAME)
295+
296+
for file in godot_files:
297+
ModLoaderSetupUtils.copy_file(
298+
file, file.trim_prefix("res://.godot").insert(0, "res://godot")
299+
)
300+
301+
302+
func get_combined_global_script_class_cache() -> ConfigFile:
303+
ModLoaderSetupLog.info("Load mod loader class cache", LOG_NAME)
304+
var global_script_class_cache_mod_loader := ConfigFile.new()
305+
global_script_class_cache_mod_loader.load(
306+
"res://addons/mod_loader/setup/global_script_class_cache_mod_loader.cfg"
307+
)
308+
309+
ModLoaderSetupLog.info("Load game class cache", LOG_NAME)
310+
var global_script_class_cache_game := ConfigFile.new()
311+
global_script_class_cache_game.load("res://.godot/global_script_class_cache.cfg")
312+
313+
ModLoaderSetupLog.info("Create new class cache", LOG_NAME)
314+
var global_classes_mod_loader := global_script_class_cache_mod_loader.get_value("", "list")
315+
var global_classes_game := global_script_class_cache_game.get_value("", "list")
316+
317+
ModLoaderSetupLog.info("Combine class cache", LOG_NAME)
318+
var global_classes_combined := []
319+
global_classes_combined.append_array(global_classes_mod_loader)
320+
global_classes_combined.append_array(global_classes_game)
321+
322+
ModLoaderSetupLog.info("Save combined class cache", LOG_NAME)
323+
var global_script_class_cache_combined := ConfigFile.new()
324+
global_script_class_cache_combined.set_value("", "list", global_classes_combined)
325+
326+
return global_script_class_cache_combined
327+
328+
329+
func get_gdre_path() -> String:
330+
if OS.get_name() == "Windows":
331+
return "vendor/GDRE/gdre_tools.exe"
332+
333+
return ""
334+
335+
336+
func restart() -> void:
337+
OS.set_restart_on_exit(true)
338+
quit()

0 commit comments

Comments
 (0)