66using System . IO ;
77using System . Linq ;
88using System . Reflection ;
9- using System . Text ;
109using UnityEngine ;
11- using UObject = UnityEngine . Object ;
1210
1311namespace COSML
1412{
1513 /// <summary>
1614 /// Handles loading of mods.
1715 /// </summary>
18- internal static class COSML
16+ public static class COSML
1917 {
2018 [ Flags ]
21- public enum ModLoadState
19+ internal enum ModLoadState
2220 {
2321 NotStarted = 0 ,
2422 Started = 1 ,
2523 Preloaded = 2 ,
2624 Loaded = 4 ,
2725 }
2826
29- public const int Version = 2 ;
27+ public const string Version = "1.0.0" ;
3028
31- public static ModLoadState LoadState = ModLoadState . NotStarted ;
29+ public static string ManagedPath { get ; private set ; }
30+ public static string ModsPath { get ; private set ; }
3231
33- public static Dictionary < Type , ModInstance > ModInstanceTypeMap { get ; private set ; } = new ( ) ;
34- public static Dictionary < string , ModInstance > ModInstanceNameMap { get ; private set ; } = new ( ) ;
35- public static HashSet < ModInstance > ModInstances { get ; private set ; } = new ( ) ;
32+ internal static ModLoadState LoadState = ModLoadState . NotStarted ;
33+ internal static Dictionary < Type , ModInstance > ModInstanceTypeMap { get ; private set ; } = [ ] ;
34+ internal static Dictionary < string , ModInstance > ModInstanceNameMap { get ; private set ; } = [ ] ;
35+ internal static HashSet < ModInstance > ModInstances { get ; private set ; } = [ ] ;
3636
37- /// <summary>
38- /// Try to add a ModInstance to the internal dictionaries.
39- /// </summary>
40- /// <param name="ty">The type of the mod.</param>
41- /// <param name="mod">The ModInstance.</param>
42- /// <returns>True if the ModInstance was successfully added; false otherwise.</returns>
4337 private static bool TryAddModInstance ( Type ty , ModInstance mod )
4438 {
4539 if ( ModInstanceNameMap . ContainsKey ( mod . Name ) )
@@ -58,29 +52,22 @@ private static bool TryAddModInstance(Type ty, ModInstance mod)
5852 return true ;
5953 }
6054
61- /// <summary>
62- /// Starts the main loading of all mods.
63- /// This loads assemblies, constructs and initializes mods, and creates the mod list menu.<br/>
64- /// This method should only be called once in the lifetime of the game.
65- /// </summary>
66- /// <param name="coroutineHolder"></param>
67- /// <returns></returns>
68- public static IEnumerator LoadModsInit ( GameObject coroutineHolder )
55+ internal static IEnumerator LoadModsInit ( GameObject coroutineHolder )
6956 {
7057 try
7158 {
7259 Logging . InitializeFileStream ( ) ;
7360 }
74- catch ( Exception e )
61+ catch ( Exception ex )
7562 {
7663 // We can still log to the console at least, if that's enabled.
77- Debug . Log ( $ "Error while initializing ModLog.txt: { e } ") ;
64+ Debug . Log ( $ "Error while initializing ModLog.txt\n { ex } ") ;
7865 }
7966
8067 Logging . API . Info ( $ "Mod loader: { Version } ") ;
8168 Logging . API . Info ( "Starting mod loading" ) ;
8269
83- string managed_path = SystemInfo . operatingSystemFamily switch
70+ ManagedPath = SystemInfo . operatingSystemFamily switch
8471 {
8572 OperatingSystemFamily . Windows => Path . Combine ( Application . dataPath , "Managed" ) ,
8673 OperatingSystemFamily . MacOSX => Path . Combine ( Application . dataPath , "Resources" , "Data" , "Managed" ) ,
@@ -89,28 +76,27 @@ public static IEnumerator LoadModsInit(GameObject coroutineHolder)
8976 _ => throw new ArgumentOutOfRangeException ( )
9077 } ;
9178
92- if ( managed_path is null )
79+ if ( ManagedPath is null )
9380 {
9481 LoadState |= ModLoadState . Loaded ;
95- UObject . Destroy ( coroutineHolder ) ;
82+ UnityEngine . Object . Destroy ( coroutineHolder ) ;
9683 yield break ;
9784 }
9885
9986 ModHooks . LoadGlobalSettings ( ) ;
10087
10188 Logging . API . Debug ( $ "Loading assemblies and constructing mods") ;
10289
103- Directory . CreateDirectory ( "Mods" ) ;
104- string mods = Path . Combine ( managed_path , "Mods" ) ;
105- DirectoryInfo modsDir = new DirectoryInfo ( mods ) ;
106- string [ ] files = modsDir . GetFiles ( "*" , SearchOption . AllDirectories ) . Where ( ( f ) => f . Extension == ".dll" ) . Select ( ( f ) => f . FullName ) . ToArray ( ) ;
90+ ModsPath = Path . Combine ( ManagedPath , "Mods" ) ;
91+ Directory . CreateDirectory ( ModsPath ) ;
92+ string [ ] filesPath = [ .. new DirectoryInfo ( ModsPath ) . GetFiles ( "*" , SearchOption . AllDirectories ) . Where ( f => f . Extension == ".dll" ) . Select ( f => f . FullName ) ] ;
10793
108- Logging . API . Debug ( $ "DLL files: { string . Join ( ",\n " , files ) } ") ;
94+ Logging . API . Debug ( $ "DLL files: { string . Join ( ",\n " , filesPath ) } ") ;
10995
11096 Assembly Resolve ( object sender , ResolveEventArgs args )
11197 {
11298 var asm_name = new AssemblyName ( args . Name ) ;
113- if ( files . FirstOrDefault ( x => x . EndsWith ( $ "{ asm_name . Name } .dll") ) is string path )
99+ if ( filesPath . FirstOrDefault ( x => x . EndsWith ( $ "{ asm_name . Name } .dll") ) is string path )
114100 {
115101 return Assembly . LoadFrom ( path ) ;
116102 }
@@ -120,25 +106,25 @@ Assembly Resolve(object sender, ResolveEventArgs args)
120106
121107 AppDomain . CurrentDomain . AssemblyResolve += Resolve ;
122108
123- List < Assembly > asms = new ( files . Length ) ;
109+ List < Assembly > asms = new ( filesPath . Length ) ;
124110
125111 // Load all the assemblies first to avoid dependency issues
126112 // Dependencies are lazy-loaded, so we won't have attempted loads until the mod initialization.
127- foreach ( string path in files )
113+ foreach ( string path in filesPath )
128114 {
129- Logging . API . Debug ( $ "Loading assembly ` { path } ` ") ;
115+ Logging . API . Debug ( $ "Loading assembly \" { path } \" ") ;
130116
131117 try
132118 {
133119 asms . Add ( Assembly . LoadFrom ( path ) ) ;
134120 }
135121 catch ( FileLoadException ex )
136122 {
137- Logging . API . Error ( $ "Unable to load assembly - { ex } ") ;
123+ Logging . API . Error ( $ "Unable to load assembly\n { ex } ") ;
138124 }
139125 catch ( BadImageFormatException ex )
140126 {
141- Logging . API . Error ( $ "Assembly is bad image. { ex } ") ;
127+ Logging . API . Error ( $ "Assembly is bad image\n { ex } ") ;
142128 }
143129 catch ( PathTooLongException )
144130 {
@@ -148,7 +134,7 @@ Assembly Resolve(object sender, ResolveEventArgs args)
148134
149135 foreach ( Assembly asm in asms )
150136 {
151- Logging . API . Debug ( $ "Loading mods in assembly ` { asm . FullName } ` ") ;
137+ Logging . API . Debug ( $ "Loading mods in assembly \" { asm . FullName } \" ") ;
152138
153139 bool foundMod = false ;
154140
@@ -173,11 +159,11 @@ Assembly Resolve(object sender, ResolveEventArgs args)
173159
174160 foundMod = true ;
175161
176- Logging . API . Debug ( $ "Constructing mod ` { ty . FullName } ` ") ;
162+ Logging . API . Debug ( $ "Constructing mod \" { ty . FullName } \" ") ;
177163
178164 try
179165 {
180- if ( ty . GetConstructor ( Type . EmptyTypes ) ? . Invoke ( Array . Empty < object > ( ) ) is Mod mod )
166+ if ( ty . GetConstructor ( Type . EmptyTypes ) ? . Invoke ( [ ] ) is Mod mod )
181167 {
182168 TryAddModInstance (
183169 ty ,
@@ -193,7 +179,7 @@ Assembly Resolve(object sender, ResolveEventArgs args)
193179 }
194180 catch ( Exception ex )
195181 {
196- Logging . API . Error ( ex ) ;
182+ Logging . API . Error ( $ "Failed to instantiate assembly mod \" { ty . FullName } \" \n { ex } " ) ;
197183
198184 TryAddModInstance (
199185 ty ,
@@ -220,9 +206,7 @@ Assembly Resolve(object sender, ResolveEventArgs args)
220206 }
221207 }
222208
223- ModInstance [ ] orderedMods = ModInstanceTypeMap . Values
224- . OrderBy ( x => x . Mod ? . LoadPriority ( ) ?? 0 )
225- . ToArray ( ) ;
209+ ModInstance [ ] orderedMods = [ .. ModInstanceTypeMap . Values . OrderBy ( x => x . Mod ? . LoadPriority ( ) ?? 0 ) ] ;
226210
227211 foreach ( ModInstance mod in orderedMods )
228212 {
@@ -251,47 +235,40 @@ Assembly Resolve(object sender, ResolveEventArgs args)
251235 }
252236 }
253237
254- Logging . API . Info ( "Finished loading mods:\n " + UpdateModText ( ) ) ;
238+ Logging . API . Info ( "Finished loading mods:" + UpdateModText ( ) ) ;
255239
256240 ModHooks . OnFinishedLoadingMods ( ) ;
257241 LoadState |= ModLoadState . Loaded ;
258242
259- UObject . Destroy ( coroutineHolder . gameObject ) ;
243+ I18n . LoadI18n ( ) ;
244+
245+ UnityEngine . Object . Destroy ( coroutineHolder . gameObject ) ;
260246 }
261247
262248 private static string UpdateModText ( )
263249 {
264- StringBuilder builder = new ( ) ;
250+ string text = "" ;
265251
266252 foreach ( ModInstance mod in ModInstances )
267253 {
268254 if ( mod . Error is not ModErrorState err )
269255 {
270- if ( mod . Enabled ) builder . AppendLine ( $ " { mod . Name } : { mod . Mod . GetVersionSafe ( "ERROR" ) } ") ;
256+ if ( mod . Enabled ) text += $ " \n { mod . Name } : { mod . Mod . GetVersionSafe ( "ERROR" ) } ";
271257 }
272258 else
273259 {
274- switch ( err )
260+ text += err switch
275261 {
276- case ModErrorState . Construct :
277- builder . AppendLine ( $ "{ mod . Name } : Failed to call constructor! Check ModLog.txt") ;
278- break ;
279- case ModErrorState . Duplicate :
280- builder . AppendLine ( $ "{ mod . Name } : Failed to load! Duplicate mod detected") ;
281- break ;
282- case ModErrorState . Init :
283- builder . AppendLine ( $ "{ mod . Name } : Failed to initialize! Check ModLog.txt") ;
284- break ;
285- case ModErrorState . Unload :
286- builder . AppendLine ( $ "{ mod . Name } : Failed to unload! Check ModLog.txt") ;
287- break ;
288- default :
289- throw new ArgumentOutOfRangeException ( ) ;
290- }
262+ ModErrorState . Construct => $ "\n { mod . Name } : Failed to call constructor! Check ModLog.txt",
263+ ModErrorState . Duplicate => $ "\n { mod . Name } : Failed to load! Duplicate mod detected",
264+ ModErrorState . Init => $ "\n { mod . Name } : Failed to initialize! Check ModLog.txt",
265+ ModErrorState . Unload => $ "\n { mod . Name } : Failed to unload! Check ModLog.txt",
266+ _ => throw new ArgumentOutOfRangeException ( ) ,
267+ } ;
291268 }
292269 }
293270
294- return builder . ToString ( ) ;
271+ return text ;
295272 }
296273
297274 internal static void LoadMod
@@ -311,7 +288,7 @@ internal static void LoadMod
311288 catch ( Exception ex )
312289 {
313290 mod . Error = ModErrorState . Init ;
314- Logging . API . Error ( $ "Failed to load mod ` { mod . Mod . GetName ( ) } ` \n { ex } ") ;
291+ Logging . API . Error ( $ "Failed to load mod \" { mod . Mod . GetName ( ) } \" \n { ex } ") ;
315292 }
316293
317294 if ( updateModText ) UpdateModText ( ) ;
@@ -330,28 +307,28 @@ internal static void UnloadMod(ModInstance mod, bool updateModText = true)
330307 catch ( Exception ex )
331308 {
332309 mod . Error = ModErrorState . Unload ;
333- Logging . API . Error ( $ "Failed to unload mod ` { mod . Name } ` \n { ex } ") ;
310+ Logging . API . Error ( $ "Failed to unload mod \" { mod . Name } \" \n { ex } ") ;
334311 }
335312
336313 if ( updateModText ) UpdateModText ( ) ;
337314 }
338315
339316 // Essentially the state of a loaded **mod**. The assembly has nothing to do directly with mods.
340- public class ModInstance
317+ internal class ModInstance
341318 {
342- // The constructed instance of the mod. If Error is ` Construct` this will be null.
319+ // The constructed instance of the mod. If Error is \" Construct\" this will be null.
343320 // Generally if Error is anything this value should not be referred to.
344- public IMod Mod ;
321+ internal IMod Mod ;
345322
346- public string Name ;
323+ internal string Name ;
347324
348- public ModErrorState ? Error ;
325+ internal ModErrorState ? Error ;
349326
350327 // If the mod is "Enabled" (in the context of IModTogglable)
351- public bool Enabled ;
328+ internal bool Enabled ;
352329 }
353330
354- public enum ModErrorState
331+ internal enum ModErrorState
355332 {
356333 Construct ,
357334 Duplicate ,
0 commit comments