This repository has been archived by the owner on Feb 28, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 22
Implement #75 - Command Line Argument parsing utility and config #96
Open
Banane9
wants to merge
5
commits into
neos-modding-group:master
Choose a base branch
from
Banane9:implement-#75-cmd-args
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
bf04824
Add LaunchArgument support class
Banane9 564339f
Add _all_ the possible Neos arguments
Banane9 3f19422
Overhaul Configuration loading: Assign with reflection, override path…
Banane9 e640f76
Add index to arguments and remove conflicting constructor
Banane9 e8f7011
Move unknown config file keys warning
Banane9 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
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}"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I imagine this'd be rather spammy too? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
{ | ||
|
@@ -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; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.