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

Implement #75 - Command Line Argument parsing utility and config #96

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 120 additions & 64 deletions NeosModLoader/ModLoaderConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,74 +1,120 @@
using FrooxEngine;
using HarmonyLib;
using NeosModLoader.Utility;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;

namespace NeosModLoader
{
internal class ModLoaderConfiguration
{
private static readonly Lazy<ModLoaderConfiguration> _configuration = new(LoadConfig);
private static readonly string CONFIG_FILENAME = "NeosModLoader.config";

private static ModLoaderConfiguration? _configuration;
public bool AdvertiseVersion { get; private set; } = false;

public bool Debug { get; private set; } = false;

internal static ModLoaderConfiguration Get()
public bool ExposeLateTypes
{
if (_configuration == null)
{
// the config file can just sit next to the dll. Simple.
string path = Path.Combine(GetAssemblyDirectory(), CONFIG_FILENAME);
_configuration = new ModLoaderConfiguration();
get => !HideLateTypes;
set => HideLateTypes = !value;
}

public bool ExposeModTypes
{
get => !HideModTypes;
set => HideModTypes = !value;
}

public bool HideConflicts
{
get => !LogConflicts;
set => LogConflicts = !value;
}
Comment on lines +22 to +38
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...is there an actual reason to duplicate these...?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some inverted properties to allow setting them with flag arguments.


public bool HideLateTypes { get; private set; } = true;

public bool HideModTypes { get; private set; } = true;

public bool HideVisuals { get; private set; } = false;

public bool LogConflicts { get; private set; } = true;

public bool NoLibraries { get; private set; } = false;

public bool NoMods { get; private set; } = false;

public bool Unsafe { get; private set; } = false;

internal static ModLoaderConfiguration Get() => _configuration.Value;

private static string GetAssemblyDirectory()
{
var codeBase = Assembly.GetExecutingAssembly().CodeBase;
var uri = new UriBuilder(codeBase);
var path = Uri.UnescapeDataString(uri.Path);

return Path.GetDirectoryName(path);
}

private static string? GetConfigPath()
{
if (LaunchArguments.TryGetArgument("Config.Path", out var argument))
return argument.Value;

// the config file can just sit next to the dll. Simple.
return Path.Combine(GetAssemblyDirectory(), CONFIG_FILENAME);
}

private static ModLoaderConfiguration LoadConfig()
{
var path = GetConfigPath();
var config = new ModLoaderConfiguration();

var configOptions = typeof(ModLoaderConfiguration).GetProperties(AccessTools.all).ToArray();

if (!File.Exists(path))
{
Logger.MsgInternal($"Using default config - file doesn't exist: {path}");
}
else
{
// .NET's ConfigurationManager is some hot trash to the point where I'm just done with it.
// Time to reinvent the wheel. This parses simple key=value style properties from a text file.
try
{
var unknownKeys = new List<string>();
var lines = File.ReadAllLines(path);

foreach (var line in lines)
{
int splitIdx = line.IndexOf('=');
if (splitIdx != -1)
var splitIdx = line.IndexOf('=');
if (splitIdx == -1)
continue;

string key = line.Substring(0, splitIdx).Trim();
string value = line.Substring(splitIdx + 1).Trim();

var possibleProperty = configOptions.FirstOrDefault(property => property.Name.Equals(key, StringComparison.InvariantCultureIgnoreCase));
if (possibleProperty == null)
{
string key = line.Substring(0, splitIdx);
string value = line.Substring(splitIdx + 1);

if ("unsafe".Equals(key) && "true".Equals(value))
{
_configuration.Unsafe = true;
}
else if ("debug".Equals(key) && "true".Equals(value))
{
_configuration.Debug = true;
}
else if ("hidevisuals".Equals(key) && "true".Equals(value))
{
_configuration.HideVisuals = true;
}
else if ("nomods".Equals(key) && "true".Equals(value))
{
_configuration.NoMods = true;
}
else if ("nolibraries".Equals(key) && "true".Equals(value))
{
_configuration.NoLibraries = true;
}
else if ("advertiseversion".Equals(key) && "true".Equals(value))
{
_configuration.AdvertiseVersion = true;
}
else if ("logconflicts".Equals(key) && "false".Equals(value))
{
_configuration.LogConflicts = false;
}
else if ("hidemodtypes".Equals(key) && "false".Equals(value))
{
_configuration.HideModTypes = false;
}
else if ("hidelatetypes".Equals(key) && "false".Equals(value))
{
_configuration.HideLateTypes = false;
}
unknownKeys.Add(key);
continue;
}

var parsedValue = TypeDescriptor.GetConverter(possibleProperty.PropertyType).ConvertFromInvariantString(value);
possibleProperty.SetValue(config, parsedValue);

Logger.MsgInternal($"Loaded value for {possibleProperty.Name} from file: {parsedValue}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine this'd be rather spammy too?

Copy link
Contributor Author

@Banane9 Banane9 Mar 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It basically lists all the values that have been set - could make this Debug, but enabling debug may only happen after this...

}

Logger.WarnInternal($"Unknown key found in config file: {string.Join(", ", unknownKeys)}");
Logger.WarnInternal($"Supported keys: {string.Join(", ", configOptions.Select(property => $"{property.PropertyType} {property.Name}"))}");
}
catch (Exception e)
{
Expand All @@ -86,25 +132,35 @@ internal static ModLoaderConfiguration Get()
}
}
}
return _configuration;

var boolType = typeof(bool);
foreach (var option in configOptions)
{
if (LaunchArguments.TryGetArgument($"Config.{option.Name}", out var argument))
{
if (option.PropertyType == boolType)
{
option.SetValue(config, true);
Logger.MsgInternal($"Enabling [{option.Name}] from launch flag");

if (!argument.IsFlag)
Logger.WarnInternal("Found possible misplaced parameter value after this flag argument");
}
else if (!argument.IsFlag)
{
config.SetProperty(option, argument.Value!);
Logger.MsgInternal($"Setting [{option.Name}] from launch flag: {argument.Value}");
}
}
}

return config;
}

private static string GetAssemblyDirectory()
private void SetProperty(PropertyInfo property, string value)
{
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
var parsedValue = TypeDescriptor.GetConverter(property.PropertyType).ConvertFromInvariantString(value);
property.SetValue(this, parsedValue);
}

public bool Unsafe { get; private set; } = false;
public bool Debug { get; private set; } = false;
public bool HideVisuals { get; private set; } = false;
public bool NoMods { get; private set; } = false;
public bool NoLibraries { get; private set; } = false;
public bool AdvertiseVersion { get; private set; } = false;
public bool LogConflicts { get; private set; } = true;
public bool HideModTypes { get; private set; } = true;
public bool HideLateTypes { get; private set; } = true;
}
}
Loading