2025-10-23 00:49:44 -04:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Reflection ;
using System.Text.RegularExpressions ;
2025-11-07 01:46:56 -06:00
using System.Threading.Tasks ;
2025-10-23 00:49:44 -04:00
using SLSFramework.General ;
using UMod ;
using UMod.Scripting ;
using UnityEngine ;
using Object = UnityEngine . Object ;
namespace SLSFramework.UModAssistance
{
public static partial class ModManager
{
public static ModHost LoadMod ( IModInfo modInfo )
{
string modName = modInfo . NameInfo . ModName ;
ModHost host = Mod . Load ( Mod . DefaultDirectory . GetModPath ( modName ) ) ;
LoadedMods . Add ( modName , host ) ;
Debug . Log ( $"Mod '{modName}' loaded successfully." ) ;
return host ;
}
2025-11-07 01:46:56 -06:00
public static async Task < ModHost > LoadAsync ( IModInfo modInfo )
{
string modName = modInfo . NameInfo . ModName ;
var host = Mod . LoadAsync ( Mod . DefaultDirectory . GetModPath ( modName ) ) ;
while ( ! host . IsDone )
{
await Task . Yield ( ) ;
}
2025-11-16 09:56:20 -05:00
2025-11-07 01:46:56 -06:00
if ( ! host . IsSuccessful )
{
2025-11-16 09:56:20 -05:00
Debug . LogError ( $"[ModLoader] Mod load operation for '{modName}' failed (hostTask.IsSuccessful == false)." ) ;
throw new Exception ( $"Failed to load mod '{modName}' asynchronously (Operation Failed)" ) ;
2025-11-07 01:46:56 -06:00
}
2025-11-16 09:56:20 -05:00
ModHost modHost = host . Result ;
if ( modHost = = null | | ! modHost . IsModLoaded )
{
if ( modHost ! = null )
{
var errorMessage = modHost . LoadResult . Message ;
Debug . LogError ( $"[ModLoader] Mod load operation for '{modName}' failed: {errorMessage}" ) ;
}
throw new Exception ( $"Failed to load mod '{modName}' asynchronously (ModHost is null or not loaded)" ) ;
}
2025-11-07 01:46:56 -06:00
LoadedMods . Add ( modName , host . Result ) ;
Debug . Log ( $"Mod '{modName}' async loaded successfully." ) ;
2025-11-16 09:56:20 -05:00
return modHost ;
2025-11-07 01:46:56 -06:00
}
2025-10-23 00:49:44 -04:00
}
public static partial class ModManager
{
public static readonly SerializableDictionary < string , ModHost > LoadedMods = new SerializableDictionary < string , ModHost > ( ) ;
public static readonly Dictionary < Type , Dictionary < string , ScriptableObject > > Database = new Dictionary < Type , Dictionary < string , ScriptableObject > > ( ) ;
2025-10-24 09:11:22 -04:00
/// <summary>
/// Get the standardized class name for a mod class, combining its class name and mod name.
/// Format: "ModName_ClassName"
/// </summary>
public static string GetModClassName ( Type type )
{
string modName = type . Namespace ! . Replace ( "Continentis.Mods." , "" ) . Split ( '.' ) [ 0 ] ;
string className = type . Name ;
return $"{modName}_{className}" ;
}
2025-10-23 00:49:44 -04:00
public static bool IsValidAssetName ( string assetName ) = > Regex . IsMatch ( assetName , @"^\w+_\w+_.+$" ) ;
/// <summary>
/// Get asset by its name, automatically determining which mod it belongs to.
/// </summary>
/// <param name="assetName">Name of the asset <b>MUST</b> in the format "Type_ModName_AssetName"</param>
public static T GetAsset < T > ( string assetName ) where T : Object
{
//命名符合“Type_ModName_AssetName”格式规范
if ( IsValidAssetName ( assetName ) )
{
string assumeModName = assetName . Split ( '_' ) [ 1 ] ;
if ( LoadedMods . TryGetValue ( assumeModName , out ModHost host ) )
{
T asset = host . Assets . Load < T > ( assetName ) ;
if ( asset ! = null )
{
return asset ;
}
}
Debug . LogWarning ( $"Mod '{assumeModName}' is not loaded, or cannot get asset '{assetName}'." ) ;
}
else
{
Debug . LogError ( $"Please check the name format (Type_ModName_AssetName) of this asset, '{assetName}'." ) ;
}
return null ;
}
/// <summary>
/// Get asset from specified mod.
/// </summary>
/// <param name="modName">Name of the mod</param>
/// <param name="assetName">Name of the asset, recommend name format "Type_ModName_AssetName"</param>
public static T GetAsset < T > ( string modName , string assetName ) where T : Object
{
if ( ! IsValidAssetName ( assetName ) )
{
Debug . LogWarning ( $"Asset name '{assetName}' does not follow the 'Type_ModName_AssetName' format." ) ;
}
if ( LoadedMods . TryGetValue ( modName , out ModHost host ) )
{
return host . Assets . Load < T > ( assetName ) ;
}
Debug . LogWarning ( $"Mod '{modName}' is not loaded, or cannot get asset '{assetName}'." ) ;
return null ;
}
public static bool TryGetData < T > ( string assetName , out T data ) where T : ScriptableObject
{
return ( data = GetData < T > ( assetName ) ) ! = null ;
}
/// <summary>
/// Get data (Scriptable Objects, loaded by data manifest) from the database by its name and type.
/// </summary>
/// <param name="assetName">Name of the asset</param>
public static T GetData < T > ( string assetName ) where T : ScriptableObject
{
if ( ! IsValidAssetName ( assetName ) )
{
Debug . LogWarning ( $"Asset name '{assetName}' does not follow the 'Type_ModName_AssetName' format." ) ;
}
Type assetType = typeof ( T ) ;
if ( Database . TryGetValue ( assetType , out Dictionary < string , ScriptableObject > assets ) )
{
if ( assets . TryGetValue ( assetName , out ScriptableObject asset ) )
{
return asset as T ;
}
else
{
Debug . LogWarning ( $"Data Asset '{assetName}' of type '{assetType}' not found in database." ) ;
return null ;
}
}
else
{
Debug . LogWarning ( $"No assets of type '{assetType}' found in database." ) ;
return null ;
}
}
}
public partial class ModManager
{
public static readonly Dictionary < string , Type > TypeRegistry = new Dictionary < string , Type > ( ) ;
2025-10-24 09:11:22 -04:00
public static string GetTypeID ( Type type )
{
return type . Namespace ! . Replace ( "Continentis.Mods." , "" ) + "." + type . Name ;
}
2025-11-10 12:57:04 -05:00
public static string GetTypeID ( string modName , string classification , string category , string className )
2025-10-24 09:11:22 -04:00
{
2025-11-10 12:57:04 -05:00
if ( string . IsNullOrEmpty ( category ) )
{
return $"{modName}.{classification}.{className}" ;
}
return $"{modName}.{classification}.{category}.{className}" ;
2025-10-24 09:11:22 -04:00
}
2025-10-23 00:49:44 -04:00
/// <summary>
/// 从一个已加载的Mod中, 查找所有指定基类的子类, 并将其注册到全局字典中。
/// </summary>
/// <param name="host">已加载的ModHost对象</param>
/// <param name="baseType">要查找的基类,例如 typeof(CardLogicBase)</param>
public static void RegisterTypesFromMod ( ModHost host , Type baseType )
{
if ( host ? . ScriptDomain = = null )
{
return ;
}
int countBefore = TypeRegistry . Count ;
// 遍历Mod包含的所有程序集(DLLs)
foreach ( ScriptAssembly assembly in host . ScriptDomain . Assemblies )
{
// assembly.RawAssembly 是 uMod 封装的真实 System.Reflection.Assembly 对象
var typesInAssembly = assembly . RawAssembly . GetTypes ( )
. Where ( t = > baseType . IsAssignableFrom ( t ) & & ! t . IsAbstract & & ! t . IsInterface ) ;
foreach ( var type in typesInAssembly )
{
2025-10-24 09:11:22 -04:00
string typeID = GetTypeID ( type ) ;
if ( TypeRegistry . TryAdd ( typeID , type ) )
2025-10-23 00:49:44 -04:00
{
2025-10-24 09:11:22 -04:00
Debug . Log ( $"Registered script type '{typeID}' from mod '{host.CurrentMod.NameInfo.ModName}'." ) ;
2025-10-23 00:49:44 -04:00
}
else
{
// 处理命名冲突: 如果不同Mod中存在同名的类, 后加载的会被忽略
2025-10-24 09:11:22 -04:00
Debug . LogWarning ( $"Duplicate script type name found: '{typeID}'. The existing type from assembly '{TypeRegistry[type.Name].Assembly.FullName}' will be kept." ) ;
2025-10-23 00:49:44 -04:00
}
}
}
int countAfter = TypeRegistry . Count ;
if ( countAfter > countBefore )
{
Debug . Log ( $"Registered {countAfter - countBefore} new script types deriving from '{baseType.Name}' from mod '{host.CurrentMod.NameInfo.ModName}'." ) ;
}
}
2025-10-24 09:11:22 -04:00
public static Type GetType ( string typeID )
2025-10-23 00:49:44 -04:00
{
2025-10-24 09:11:22 -04:00
if ( TypeRegistry . TryGetValue ( typeID , out Type type ) )
2025-10-23 00:49:44 -04:00
{
return type ;
}
2025-10-24 09:11:22 -04:00
Debug . LogWarning ( $"Type '{typeID}' not found in TypeRegistry." ) ;
2025-10-23 00:49:44 -04:00
return null ;
}
public static T CreateInstance < T > ( string typeName ) where T : class
{
Type type = GetType ( typeName ) ;
if ( type ! = null & & typeof ( T ) . IsAssignableFrom ( type ) )
{
return Activator . CreateInstance ( type ) as T ;
}
Debug . LogWarning ( $"Cannot create instance of type '{typeName}' as it is not found or not assignable to '{typeof(T).Name}'." ) ;
return null ;
}
public static T CreateInstance < T > ( string typeName , params object [ ] parameters ) where T : class
{
Type type = GetType ( typeName ) ;
if ( type ! = null & & typeof ( T ) . IsAssignableFrom ( type ) )
{
return Activator . CreateInstance ( type , parameters ) as T ;
}
Debug . LogWarning ( $"Cannot create instance of type '{typeName}' as it is not found or not assignable to '{typeof(T).Name}'." ) ;
return null ;
}
}
}