Skip to content
This repository has been archived by the owner on Feb 28, 2024. It is now read-only.

Cleanup and nullables enabled. #21

Merged
merged 9 commits into from
Apr 9, 2022
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.vscode

## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
Expand Down
3 changes: 2 additions & 1 deletion NeosModLoader/AssemblyFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ internal class AssemblyFile
{
internal string File { get; }
internal Assembly Assembly { get; set; }
internal AssemblyFile(string file)
internal AssemblyFile(string file, Assembly assembly)
{
File = file;
Assembly = assembly;
}
}
}
43 changes: 21 additions & 22 deletions NeosModLoader/AssemblyLoader.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace NeosModLoader
{
internal static class AssemblyLoader
{
private static AssemblyFile[] GetAssembliesFromDir(string dirName)
private static string[]? GetAssemblyPathsFromDir(string dirName)
{
string assembliesDirectory = Path.Combine(Directory.GetCurrentDirectory(), dirName);

Logger.MsgInternal($"loading assemblies from {dirName}");

AssemblyFile[] assembliesToLoad = null;
string[]? assembliesToLoad = null;
try
{
assembliesToLoad = Directory.GetFiles(assembliesDirectory, "*.dll")
.Select(file => new AssemblyFile(file))
.ToArray();

Array.Sort(assembliesToLoad, (a, b) => string.CompareOrdinal(a.File, b.File));
assembliesToLoad = Directory.GetFiles(assembliesDirectory, "*.dll");
Array.Sort(assembliesToLoad, (a, b) => string.CompareOrdinal(a, b));
}
catch (Exception e)
{
Expand All @@ -44,46 +41,48 @@ private static AssemblyFile[] GetAssembliesFromDir(string dirName)
return assembliesToLoad;
}

private static void LoadAssembly(AssemblyFile assemblyFile)
private static Assembly? LoadAssembly(string filepath)
{
string filename = Path.GetFileName(assemblyFile.File);
string filename = Path.GetFileName(filepath);
SplashChanger.SetCustom($"Loading file: {filename}");
Assembly assembly;
try
{
Logger.DebugInternal($"load assembly {filename}");
assembly = Assembly.LoadFile(assemblyFile.File);
assembly = Assembly.LoadFile(filepath);
}
catch (Exception e)
{
Logger.ErrorInternal($"error loading assembly from {assemblyFile.File}: {e}");
return;
Logger.ErrorInternal($"error loading assembly from {filepath}: {e}");
return null;
}
if (assembly == null)
{
Logger.ErrorInternal($"unexpected null loading assembly from {assemblyFile.File}");
return;
Logger.ErrorInternal($"unexpected null loading assembly from {filepath}");
return null;
}
assemblyFile.Assembly = assembly;
return assembly;
}

internal static AssemblyFile[] LoadAssembliesFromDir(string dirName) {
var assembliesOrNull = GetAssembliesFromDir(dirName);
if (assembliesOrNull is AssemblyFile[] assembliesToLoad) {
foreach (AssemblyFile assemblyFile in assembliesToLoad)
List<AssemblyFile> assemblyFiles = new();
if (GetAssemblyPathsFromDir(dirName) is string[] assemblyPaths) {
foreach (string assemblyFilepath in assemblyPaths)
{
try
{
LoadAssembly(assemblyFile);
if (LoadAssembly(assemblyFilepath) is Assembly assembly) {
assemblyFiles.Add(new AssemblyFile(assemblyFilepath, assembly));
}
}
catch (Exception e)
{
Logger.ErrorInternal($"Unexpected exception loading assembly from {assemblyFile.File}:\n{e}");
Logger.ErrorInternal($"Unexpected exception loading assembly from {assemblyFilepath}:\n{e}");
}
}
}

return assembliesOrNull;
return assemblyFiles.ToArray();
}
}
}
2 changes: 1 addition & 1 deletion NeosModLoader/DebugInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal static void Log()
Logger.MsgInternal($"Using Json.NET v{GetAssemblyVersion(typeof(Newtonsoft.Json.JsonSerializer))}");
}

private static string GetAssemblyVersion(Type typeFromAssembly)
private static string? GetAssemblyVersion(Type typeFromAssembly)
{
return typeFromAssembly.Assembly.GetName()?.Version?.ToString();
}
Expand Down
1 change: 1 addition & 0 deletions NeosModLoader/ExecutionHook.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using FrooxEngine;
using System;

#nullable disable
namespace NeosModLoader
{
[ImplementableClass(true)]
Expand Down
8 changes: 4 additions & 4 deletions NeosModLoader/JsonConverters/EnumConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace NeosModLoader.JsonConverters
{
// serializes and deserializes enums as strings
class EnumConverter : JsonConverter
internal class EnumConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
Expand All @@ -15,10 +15,10 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
{
// handle old behavior where enums were serialized as underlying type
Type underlyingType = Enum.GetUnderlyingType(objectType);
if (TryConvert(reader.Value, underlyingType, out object deserialized))
if (TryConvert(reader.Value, underlyingType, out object? deserialized))
{
Logger.DebugInternal($"Deserializing a BaseX type: {objectType} from a {reader.Value.GetType()}");
return deserialized;
return deserialized!;
}

// handle new behavior where enums are serialized as strings
Expand All @@ -36,7 +36,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s
writer.WriteValue(serialized);
}

private bool TryConvert(object value, Type newType, out object converted)
private bool TryConvert(object value, Type newType, out object? converted)
{
try
{
Expand Down
2 changes: 1 addition & 1 deletion NeosModLoader/JsonConverters/NeosPrimitiveConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace NeosModLoader.JsonConverters
{
class NeosPrimitiveConverter : JsonConverter
internal class NeosPrimitiveConverter : JsonConverter
{
private static readonly Assembly BASEX = typeof(color).Assembly;

Expand Down
2 changes: 1 addition & 1 deletion NeosModLoader/LoadedNeosMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ internal LoadedNeosMod(NeosMod neosMod, AssemblyFile modAssembly)

internal NeosMod NeosMod { get; private set; }
internal AssemblyFile ModAssembly { get; private set; }
internal ModConfiguration ModConfiguration { get; set; }
internal ModConfiguration? ModConfiguration { get; set; }
internal bool AllowSavingConfiguration = true;
internal bool FinishedLoading { get => NeosMod.FinishedLoading; set => NeosMod.FinishedLoading = value; }
internal string Name { get => NeosMod.Name; }
Expand Down
11 changes: 5 additions & 6 deletions NeosModLoader/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ internal static void DebugListExternal(object[] messages)
internal static void ErrorExternal(object message) => LogInternal(LogType.ERROR, message, SourceFromStackTrace());
internal static void ErrorListExternal(object[] messages) => LogListInternal(LogType.ERROR, messages, SourceFromStackTrace());

private static void LogInternal(string logTypePrefix, object message, string source = null)
private static void LogInternal(string logTypePrefix, object message, string? source = null)
{
if (message == null)
{
Expand All @@ -59,7 +59,7 @@ private static void LogInternal(string logTypePrefix, object message, string sou
}
}

private static void LogListInternal(string logTypePrefix, object[] messages, string source)
private static void LogListInternal(string logTypePrefix, object[] messages, string? source)
{
if (messages == null)
{
Expand All @@ -74,15 +74,14 @@ private static void LogListInternal(string logTypePrefix, object[] messages, str
}
}

private static string SourceFromStackTrace()
private static string? SourceFromStackTrace()
{
// skip three frames: SourceFromStackTrace(), MsgExternal(), Msg()
StackTrace stackTrace = new StackTrace(3);
StackTrace stackTrace = new(3);
for (int i = 0; i < stackTrace.FrameCount; i++)
{
Assembly assembly = stackTrace.GetFrame(i).GetMethod().DeclaringType.Assembly;
NeosMod mod;
if (ModLoader.AssemblyLookupMap.TryGetValue(assembly, out mod))
if (ModLoader.AssemblyLookupMap.TryGetValue(assembly, out NeosMod mod))
{
return mod.Name;
}
Expand Down
108 changes: 51 additions & 57 deletions NeosModLoader/ModConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Linq;
using System.Reflection;

#nullable disable
namespace NeosModLoader
{
public interface IModConfigurationDefinition
Expand Down Expand Up @@ -117,10 +118,10 @@ internal ModConfigurationDefinition(NeosModBase owner, Version version, HashSet<
/// </summary>
public class ModConfiguration : IModConfigurationDefinition
{
private ModConfigurationDefinition Definition;
private readonly ModConfigurationDefinition Definition;
internal LoadedNeosMod LoadedNeosMod { get; private set; }

private static string ConfigDirectory = Path.Combine(Directory.GetCurrentDirectory(), "nml_config");
private static readonly string ConfigDirectory = Path.Combine(Directory.GetCurrentDirectory(), "nml_config");
private static readonly string VERSION_JSON_KEY = "version";
private static readonly string VALUES_JSON_KEY = "values";

Expand Down Expand Up @@ -157,15 +158,17 @@ public class ModConfiguration : IModConfigurationDefinition
/// </summary>
public event ConfigurationChangedEventHandler OnThisConfigurationChanged;

private static JsonSerializer jsonSerializer = createJsonSerializer();
private static readonly JsonSerializer jsonSerializer = CreateJsonSerializer();

private static JsonSerializer createJsonSerializer()
private static JsonSerializer CreateJsonSerializer()
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MaxDepth = 32;
settings.ReferenceLoopHandling = ReferenceLoopHandling.Error;
settings.DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate;
List<JsonConverter> converters = new List<JsonConverter>();
JsonSerializerSettings settings = new()
{
MaxDepth = 32,
ReferenceLoopHandling = ReferenceLoopHandling.Error,
DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate
};
List<JsonConverter> converters = new();
IList<JsonConverter> defaultConverters = settings.Converters;
if (defaultConverters != null)
{
Expand Down Expand Up @@ -337,7 +340,7 @@ public bool TryGetValue<T>(ModConfigurationKey<T> key, out T value)
}
else
{
value = default(T);
value = default;
return false;
}
}
Expand Down Expand Up @@ -420,8 +423,7 @@ private bool AnyValuesSet()

internal static ModConfiguration LoadConfigForMod(LoadedNeosMod mod)
{
// intentional call to an obsolete method. This will need reorganizing in the next major version.
ModConfigurationDefinition definition = mod.NeosMod.GetConfigurationDefinition();
ModConfigurationDefinition definition = mod.NeosMod.BuildConfigurationDefinition();
if (definition == null)
{
// if there's no definition, then there's nothing for us to do here
Expand All @@ -432,38 +434,34 @@ internal static ModConfiguration LoadConfigForMod(LoadedNeosMod mod)

try
{
using (StreamReader file = File.OpenText(configFile))
using StreamReader file = File.OpenText(configFile);
using JsonTextReader reader = new(file);
JObject json = JObject.Load(reader);
Version version = new(json[VERSION_JSON_KEY].ToObject<string>(jsonSerializer));
if (!AreVersionsCompatible(version, definition.Version))
{
var handlingMode = mod.NeosMod.HandleIncompatibleConfigurationVersions(definition.Version, version);
switch (handlingMode)
{
case IncompatibleConfigurationHandlingOption.CLOBBER:
Logger.WarnInternal($"{mod.NeosMod.Name} saved config version is {version} which is incompatible with mod's definition version {definition.Version}. Clobbering old config and starting fresh.");
return new ModConfiguration(mod, definition);
case IncompatibleConfigurationHandlingOption.FORCE_LOAD:
// continue processing
break;
case IncompatibleConfigurationHandlingOption.ERROR: // fall through to default
default:
mod.AllowSavingConfiguration = false;
throw new ModConfigurationException($"{mod.NeosMod.Name} saved config version is {version} which is incompatible with mod's definition version {definition.Version}");
}
}
foreach (ModConfigurationKey key in definition.ConfigurationItemDefinitions)
{
using (JsonTextReader reader = new JsonTextReader(file))
JToken token = json[VALUES_JSON_KEY][key.Name];
if (token != null)
{
JObject json = JObject.Load(reader);
Version version = new Version(json[VERSION_JSON_KEY].ToObject<string>(jsonSerializer));
if (!AreVersionsCompatible(version, definition.Version))
{
var handlingMode = mod.NeosMod.HandleIncompatibleConfigurationVersions(definition.Version, version);
switch (handlingMode)
{
case IncompatibleConfigurationHandlingOption.CLOBBER:
Logger.WarnInternal($"{mod.NeosMod.Name} saved config version is {version} which is incompatible with mod's definition version {definition.Version}. Clobbering old config and starting fresh.");
return new ModConfiguration(mod, definition);
case IncompatibleConfigurationHandlingOption.FORCE_LOAD:
// continue processing
break;
case IncompatibleConfigurationHandlingOption.ERROR: // fall through to default
default:
mod.AllowSavingConfiguration = false;
throw new ModConfigurationException($"{mod.NeosMod.Name} saved config version is {version} which is incompatible with mod's definition version {definition.Version}");
}
}
foreach (ModConfigurationKey key in definition.ConfigurationItemDefinitions)
{
JToken token = json[VALUES_JSON_KEY][key.Name];
if (token != null)
{
object value = token.ToObject(key.ValueType(), jsonSerializer);
key.Set(value);
}
}
object value = token.ToObject(key.ValueType(), jsonSerializer);
key.Set(value);
}
}
}
Expand Down Expand Up @@ -503,10 +501,12 @@ public void Save(bool saveDefaultValues = false)
return;
}

JObject json = new JObject();
json[VERSION_JSON_KEY] = JToken.FromObject(Definition.Version.ToString(), jsonSerializer);
JObject json = new()
{
[VERSION_JSON_KEY] = JToken.FromObject(Definition.Version.ToString(), jsonSerializer)
};

JObject valueMap = new JObject();
JObject valueMap = new();
foreach (ModConfigurationKey key in ConfigurationItemDefinitions)
{
if (key.TryGetValue(out object value))
Expand All @@ -524,19 +524,13 @@ public void Save(bool saveDefaultValues = false)
json[VALUES_JSON_KEY] = valueMap;

string configFile = GetModConfigPath(LoadedNeosMod);
using (FileStream file = File.OpenWrite(configFile))
{
using (StreamWriter streamWriter = new StreamWriter(file))
{
using (JsonTextWriter jsonTextWriter = new JsonTextWriter(streamWriter))
{
json.WriteTo(jsonTextWriter);
using FileStream file = File.OpenWrite(configFile);
using StreamWriter streamWriter = new(file);
using JsonTextWriter jsonTextWriter = new(streamWriter);
json.WriteTo(jsonTextWriter);

// I actually cannot believe I have to truncate the file myself
file.SetLength(file.Position);
}
}
}
// I actually cannot believe I have to truncate the file myself
file.SetLength(file.Position);
}

private void FireConfigurationChangedEvent(ModConfigurationKey key, string label)
Expand Down
Loading