Skip to content

Commit 9466fa2

Browse files
committed
v1.0.0
1 parent bef34ef commit 9466fa2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+4193
-1408
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Changelog
2+
=========================
3+
4+
## v1.0.0
5+
- Menu rework : hide option, change label/value dynamically.
6+
- I18n : change in-game translations or add new languages
7+
- TextInput keyboard support
8+
- Keyboard API + Toast API
9+
10+
## v2
11+
- Now loads mods not only at the root `Mods` folder but from any subfolder too.
12+
13+
## v1
14+
- First release

COSML/AssemblyUtils.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ public static Sprite LoadEmbeddedSprite(this Assembly asm, string path, float pi
2323
byte[] buffer = new byte[stream.Length];
2424
stream.Read(buffer, 0, buffer.Length);
2525

26-
Texture2D texture = new Texture2D(2, 2);
26+
Texture2D texture = new(2, 2);
2727
texture.LoadImage(buffer, true);
2828

2929
Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.one * 0.5f, pixelsPerUnit);
3030
string[] words = path.Split('.');
31-
sprite.name = words[words.Length - 2];
31+
sprite.name = words[^2];
3232
return sprite;
3333
}
3434
}

COSML/COSML.cs

Lines changed: 53 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,34 @@
66
using System.IO;
77
using System.Linq;
88
using System.Reflection;
9-
using System.Text;
109
using UnityEngine;
11-
using UObject = UnityEngine.Object;
1210

1311
namespace 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,

COSML/COSML.csproj

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,17 @@
22
<PropertyGroup>
33
<RootNamespace>COSML</RootNamespace>
44
<AssemblyName>Assembly-CSharp.mm</AssemblyName>
5-
<TargetFramework>net472</TargetFramework>
5+
<TargetFramework>net48</TargetFramework>
66
<AssemblyTitle>Chants Of Sennaar Mod Loader</AssemblyTitle>
77
<Product>COSML</Product>
8-
<Copyright>Copyright © 2023</Copyright>
8+
<Copyright>Copyright © 2024</Copyright>
9+
<Version>1.0.0</Version>
910
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
1011
<RestorePackagesPath>packages</RestorePackagesPath>
12+
<NoWarn>CS1591;CS0626</NoWarn>
1113
<LangVersion>latest</LangVersion>
12-
<NoWarn>$(NoWarn);1591;626</NoWarn>
1314
</PropertyGroup>
1415

15-
<Import Project="LocalBuildProperties.props" Condition="Exists('LocalBuildProperties.props')" />
16-
1716
<PropertyGroup>
1817
<Mono Condition="$(OS) == WINDOWS_NT" />
1918
<Mono Condition="$(OS) != WINDOWS_NT">mono</Mono>
@@ -31,7 +30,7 @@
3130
</ItemGroup>
3231

3332
<ItemGroup>
34-
<EmbeddedResource Include="Resources/InputButton_background.png" />
33+
<EmbeddedResource Include="Resources/InputText_background.png" />
3534
<EmbeddedResource Include="Resources/MainMenu_chevron_over_white.png" />
3635
</ItemGroup>
3736

@@ -92,7 +91,7 @@
9291
<HintPath>$(VanillaDir)\Assembly-CSharp.dll</HintPath>
9392
<SpecificVersion>False</SpecificVersion>
9493
</Reference>
95-
<Reference Include="mscorlib">
94+
<Reference Include="mscorlib">
9695
<HintPath>$(VanillaDir)\mscorlib.dll</HintPath>
9796
</Reference>
9897
<Reference Include="netstandard">
@@ -122,6 +121,9 @@
122121
<Reference Include="UnityEngine.ImageConversionModule">
123122
<HintPath>$(VanillaDir)\UnityEngine.ImageConversionModule.dll</HintPath>
124123
</Reference>
124+
<Reference Include="UnityEngine.PhysicsModule">
125+
<HintPath>$(VanillaDir)\UnityEngine.PhysicsModule.dll</HintPath>
126+
</Reference>
125127
<Reference Include="Rewired_Core">
126128
<HintPath>$(VanillaDir)\Rewired_Core.dll</HintPath>
127129
</Reference>

0 commit comments

Comments
 (0)