mirror of
https://github.com/JHDev2006/Super-Mario-Bros.-Remastered-Public.git
synced 2025-10-22 23:48:11 +00:00
added the game
This commit is contained in:
156
addons/mod_loader/internal/script_extension.gd
Normal file
156
addons/mod_loader/internal/script_extension.gd
Normal file
@@ -0,0 +1,156 @@
|
||||
class_name _ModLoaderScriptExtension
|
||||
extends RefCounted
|
||||
|
||||
# This Class provides methods for working with script extensions.
|
||||
# Currently all of the included methods are internal and should only be used by the mod loader itself.
|
||||
|
||||
const LOG_NAME := "ModLoader:ScriptExtension"
|
||||
|
||||
|
||||
# Sort script extensions by inheritance and apply them in order
|
||||
static func handle_script_extensions() -> void:
|
||||
var extension_paths := []
|
||||
for extension_path in ModLoaderStore.script_extensions:
|
||||
if FileAccess.file_exists(extension_path):
|
||||
extension_paths.push_back(extension_path)
|
||||
else:
|
||||
ModLoaderLog.error(
|
||||
"The child script path '%s' does not exist" % [extension_path], LOG_NAME
|
||||
)
|
||||
|
||||
# Sort by inheritance
|
||||
InheritanceSorting.new(extension_paths)
|
||||
|
||||
# Load and install all extensions
|
||||
for extension in extension_paths:
|
||||
var script: Script = apply_extension(extension)
|
||||
_reload_vanilla_child_classes_for(script)
|
||||
|
||||
|
||||
# Sorts script paths by their ancestors. Scripts are organized by their common
|
||||
# ancestors then sorted such that scripts extending script A will be before
|
||||
# a script extending script B if A is an ancestor of B.
|
||||
class InheritanceSorting:
|
||||
var stack_cache := {}
|
||||
# This dictionary's keys are mod_ids and it stores the corresponding position in the load_order
|
||||
var load_order := {}
|
||||
|
||||
func _init(inheritance_array_to_sort: Array) -> void:
|
||||
_populate_load_order_table()
|
||||
inheritance_array_to_sort.sort_custom(check_inheritances)
|
||||
|
||||
# Comparator function. return true if a should go before b. This may
|
||||
# enforce conditions beyond the stated inheritance relationship.
|
||||
func check_inheritances(extension_a: String, extension_b: String) -> bool:
|
||||
var a_stack := cached_inheritances_stack(extension_a)
|
||||
var b_stack := cached_inheritances_stack(extension_b)
|
||||
|
||||
var last_index: int
|
||||
for index in a_stack.size():
|
||||
if index >= b_stack.size():
|
||||
return false
|
||||
if a_stack[index] != b_stack[index]:
|
||||
return a_stack[index] < b_stack[index]
|
||||
last_index = index
|
||||
|
||||
if last_index < b_stack.size() - 1:
|
||||
return true
|
||||
|
||||
return compare_mods_order(extension_a, extension_b)
|
||||
|
||||
# Returns a list of scripts representing all the ancestors of the extension
|
||||
# script with the most recent ancestor last.
|
||||
#
|
||||
# Results are stored in a cache keyed by extension path
|
||||
func cached_inheritances_stack(extension_path: String) -> Array:
|
||||
if stack_cache.has(extension_path):
|
||||
return stack_cache[extension_path]
|
||||
|
||||
var stack := []
|
||||
|
||||
var parent_script: Script = load(extension_path)
|
||||
while parent_script:
|
||||
stack.push_front(parent_script.resource_path)
|
||||
parent_script = parent_script.get_base_script()
|
||||
stack.pop_back()
|
||||
|
||||
stack_cache[extension_path] = stack
|
||||
return stack
|
||||
|
||||
# Secondary comparator function for resolving scripts extending the same vanilla script
|
||||
# Will return whether a comes before b in the load order
|
||||
func compare_mods_order(extension_a: String, extension_b: String) -> bool:
|
||||
var mod_a_id: String = _ModLoaderPath.get_mod_dir(extension_a)
|
||||
var mod_b_id: String = _ModLoaderPath.get_mod_dir(extension_b)
|
||||
|
||||
return load_order[mod_a_id] < load_order[mod_b_id]
|
||||
|
||||
# Populate a load order dictionary for faster access and comparison between mod ids
|
||||
func _populate_load_order_table() -> void:
|
||||
var mod_index := 0
|
||||
for mod in ModLoaderStore.mod_load_order:
|
||||
load_order[mod.dir_name] = mod_index
|
||||
mod_index += 1
|
||||
|
||||
|
||||
static func apply_extension(extension_path: String) -> Script:
|
||||
# Check path to file exists
|
||||
if not FileAccess.file_exists(extension_path):
|
||||
ModLoaderLog.error("The child script path '%s' does not exist" % [extension_path], LOG_NAME)
|
||||
return null
|
||||
|
||||
var child_script: Script = load(extension_path)
|
||||
# Adding metadata that contains the extension script path
|
||||
# We cannot get that path in any other way
|
||||
# Passing the child_script as is would return the base script path
|
||||
# Passing the .duplicate() would return a '' path
|
||||
child_script.set_meta("extension_script_path", extension_path)
|
||||
|
||||
# Force Godot to compile the script now.
|
||||
# We need to do this here to ensure that the inheritance chain is
|
||||
# properly set up, and multiple mods can chain-extend the same
|
||||
# class multiple times.
|
||||
# This is also needed to make Godot instantiate the extended class
|
||||
# when creating singletons.
|
||||
child_script.reload()
|
||||
|
||||
var parent_script: Script = child_script.get_base_script()
|
||||
var parent_script_path: String = parent_script.resource_path
|
||||
|
||||
# We want to save scripts for resetting later
|
||||
# All the scripts are saved in order already
|
||||
if not ModLoaderStore.saved_scripts.has(parent_script_path):
|
||||
ModLoaderStore.saved_scripts[parent_script_path] = []
|
||||
# The first entry in the saved script array that has the path
|
||||
# used as a key will be the duplicate of the not modified script
|
||||
ModLoaderStore.saved_scripts[parent_script_path].append(parent_script.duplicate())
|
||||
|
||||
ModLoaderStore.saved_scripts[parent_script_path].append(child_script)
|
||||
|
||||
ModLoaderLog.info(
|
||||
"Installing script extension: %s <- %s" % [parent_script_path, extension_path], LOG_NAME
|
||||
)
|
||||
child_script.take_over_path(parent_script_path)
|
||||
|
||||
return child_script
|
||||
|
||||
|
||||
# Reload all children classes of the vanilla class we just extended
|
||||
# Calling reload() the children of an extended class seems to allow them to be extended
|
||||
# e.g if B is a child class of A, reloading B after apply an extender of A allows extenders of B to properly extend B, taking A's extender(s) into account
|
||||
static func _reload_vanilla_child_classes_for(script: Script) -> void:
|
||||
if script == null:
|
||||
return
|
||||
var current_child_classes := []
|
||||
var actual_path: String = script.get_base_script().resource_path
|
||||
var classes: Array = ProjectSettings.get_global_class_list()
|
||||
|
||||
for _class in classes:
|
||||
if _class.path == actual_path:
|
||||
current_child_classes.push_back(_class)
|
||||
break
|
||||
|
||||
for _class in current_child_classes:
|
||||
for child_class in classes:
|
||||
if child_class.base == _class.get_class():
|
||||
load(child_class.path).reload()
|
Reference in New Issue
Block a user