mirror of
				https://github.com/JHDev2006/Super-Mario-Bros.-Remastered-Public.git
				synced 2025-11-04 08:35:41 +00:00 
			
		
		
		
	* Update Mod Loader and Tool addons * Mod tool checks if script exists before reloading * Change script export mode to Text for hook export * Change return type of load_image_from_path to Texture2D
		
			
				
	
	
		
			133 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			GDScript
		
	
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			GDScript
		
	
	
	
	
	
class_name _ModLoaderDependency
 | 
						|
extends RefCounted
 | 
						|
 | 
						|
 | 
						|
# This Class provides methods for working with dependencies.
 | 
						|
# Currently all of the included methods are internal and should only be used by the mod loader itself.
 | 
						|
 | 
						|
const LOG_NAME := "ModLoader:Dependency"
 | 
						|
 | 
						|
 | 
						|
# Run dependency checks on a mod, checking any dependencies it lists in its
 | 
						|
# mod_manifest (ie. its manifest.json file). If a mod depends on another mod that
 | 
						|
# hasn't been loaded, the dependent mod won't be loaded, if it is a required dependency.
 | 
						|
#
 | 
						|
# Parameters:
 | 
						|
# - mod: A ModData object representing the mod being checked.
 | 
						|
# - dependency_chain: An array that stores the IDs of the mods that have already
 | 
						|
#   been checked to avoid circular dependencies.
 | 
						|
# - is_required: A boolean indicating whether the mod is a required or optional
 | 
						|
#   dependency. Optional dependencies will not prevent the dependent mod from
 | 
						|
#   loading if they are missing.
 | 
						|
#
 | 
						|
# Returns: A boolean indicating whether a circular dependency was detected.
 | 
						|
static func check_dependencies(mod: ModData, is_required := true, dependency_chain := []) -> bool:
 | 
						|
	var dependency_type := "required" if is_required else "optional"
 | 
						|
	# Get the dependency array based on the is_required flag
 | 
						|
	var dependencies := mod.manifest.dependencies if is_required else mod.manifest.optional_dependencies
 | 
						|
	# Get the ID of the mod being checked
 | 
						|
	var mod_id := mod.dir_name
 | 
						|
 | 
						|
	ModLoaderLog.debug("Checking dependencies - mod_id: %s %s dependencies: %s" % [mod_id, dependency_type, dependencies], LOG_NAME)
 | 
						|
 | 
						|
	# Check for circular dependency
 | 
						|
	if mod_id in dependency_chain:
 | 
						|
		ModLoaderLog.debug("%s dependency check - circular dependency detected for mod with ID %s." % [dependency_type.capitalize(), mod_id], LOG_NAME)
 | 
						|
		return true
 | 
						|
 | 
						|
	# Add mod_id to dependency_chain to avoid circular dependencies
 | 
						|
	dependency_chain.append(mod_id)
 | 
						|
 | 
						|
	# Loop through each dependency listed in the mod's manifest
 | 
						|
	for dependency_id in dependencies:
 | 
						|
		# Check if dependency is missing
 | 
						|
		if not ModLoaderStore.mod_data.has(dependency_id) or not ModLoaderStore.mod_data[dependency_id].is_loadable or not ModLoaderStore.mod_data[dependency_id].is_active:
 | 
						|
			# Skip to the next dependency if it's optional
 | 
						|
			if not is_required:
 | 
						|
				ModLoaderLog.info("Missing optional dependency - mod: -> %s dependency -> %s" % [mod_id, dependency_id], LOG_NAME)
 | 
						|
				continue
 | 
						|
			_handle_missing_dependency(mod_id, dependency_id)
 | 
						|
			# Flag the mod so it's not loaded later
 | 
						|
			mod.is_loadable = false
 | 
						|
			mod.is_active = false
 | 
						|
		else:
 | 
						|
			var dependency: ModData = ModLoaderStore.mod_data[dependency_id]
 | 
						|
 | 
						|
			# Increase the importance score of the dependency by 1
 | 
						|
			dependency.importance += 1
 | 
						|
			ModLoaderLog.debug("%s dependency -> %s importance -> %s" % [dependency_type.capitalize(), dependency_id, dependency.importance], LOG_NAME)
 | 
						|
 | 
						|
			# Check if the dependency has any dependencies of its own
 | 
						|
			if dependency.manifest.dependencies.size() > 0:
 | 
						|
				if check_dependencies(dependency, is_required, dependency_chain):
 | 
						|
					return true
 | 
						|
 | 
						|
	# Return false if all dependencies have been resolved
 | 
						|
	return false
 | 
						|
 | 
						|
 | 
						|
# Run load before check on a mod, checking any load_before entries it lists in its
 | 
						|
# mod_manifest (ie. its manifest.json file). Add the mod to the dependency of the
 | 
						|
# mods inside the load_before array.
 | 
						|
static func check_load_before(mod: ModData) -> void:
 | 
						|
	# Skip if no entries in load_before
 | 
						|
	if mod.manifest.load_before.size() == 0:
 | 
						|
		return
 | 
						|
 | 
						|
	ModLoaderLog.debug("Load before - In mod %s detected." % mod.dir_name, LOG_NAME)
 | 
						|
 | 
						|
	# For each mod id in load_before
 | 
						|
	for load_before_id in mod.manifest.load_before:
 | 
						|
		# Check if the load_before mod exists
 | 
						|
		if not ModLoaderStore.mod_data.has(load_before_id):
 | 
						|
			ModLoaderLog.debug("Load before - Skipping %s because it's missing" % load_before_id, LOG_NAME)
 | 
						|
			continue
 | 
						|
 | 
						|
		var load_before_mod_dependencies := ModLoaderStore.mod_data[load_before_id].manifest.dependencies as PackedStringArray
 | 
						|
 | 
						|
		# Check if it's already a dependency
 | 
						|
		if mod.dir_name in load_before_mod_dependencies:
 | 
						|
			ModLoaderLog.debug("Load before - Skipping because it's already a dependency for %s" % load_before_id, LOG_NAME)
 | 
						|
			continue
 | 
						|
 | 
						|
		# Add the mod to the dependency array
 | 
						|
		load_before_mod_dependencies.append(mod.dir_name)
 | 
						|
		ModLoaderStore.mod_data[load_before_id].manifest.dependencies = load_before_mod_dependencies
 | 
						|
 | 
						|
		ModLoaderLog.debug("Load before - Added %s as dependency for %s" % [mod.dir_name, load_before_id], LOG_NAME)
 | 
						|
 | 
						|
 | 
						|
# Get the load order of mods, using a custom sorter
 | 
						|
static func get_load_order(mod_data_array: Array) -> Array:
 | 
						|
	# Add loadable mods to the mod load order array
 | 
						|
	for mod in mod_data_array:
 | 
						|
		mod = mod as ModData
 | 
						|
		if mod.is_loadable:
 | 
						|
			ModLoaderStore.mod_load_order.append(mod)
 | 
						|
 | 
						|
	# Sort mods by the importance value
 | 
						|
	ModLoaderStore.mod_load_order.sort_custom(Callable(CompareImportance, "_compare_importance"))
 | 
						|
	return  ModLoaderStore.mod_load_order
 | 
						|
 | 
						|
 | 
						|
# Handles a missing dependency for a given mod ID. Logs an error message indicating the missing dependency and adds
 | 
						|
# the dependency ID to the mod_missing_dependencies dictionary for the specified mod.
 | 
						|
static func _handle_missing_dependency(mod_id: String, dependency_id: String) -> void:
 | 
						|
	ModLoaderLog.error("Missing dependency - mod: -> %s dependency -> %s" % [mod_id, dependency_id], LOG_NAME)
 | 
						|
	# if mod is not present in the missing dependencies array
 | 
						|
	if not ModLoaderStore.mod_missing_dependencies.has(mod_id):
 | 
						|
		# add it
 | 
						|
		ModLoaderStore.mod_missing_dependencies[mod_id] = []
 | 
						|
 | 
						|
	ModLoaderStore.mod_missing_dependencies[mod_id].append(dependency_id)
 | 
						|
 | 
						|
 | 
						|
# Inner class so the sort function can be called by get_load_order()
 | 
						|
class CompareImportance:
 | 
						|
	# Custom sorter that orders mods by important
 | 
						|
	static func _compare_importance(a: ModData, b: ModData) -> bool:
 | 
						|
		if a.importance > b.importance:
 | 
						|
			return true # a -> b
 | 
						|
		else:
 | 
						|
			return false # b -> a
 |