Skip to content

Commit

Permalink
Merge pull request #322 from UchuServer/fix/linux-dotnet-paths
Browse files Browse the repository at this point in the history
Fix Configuration Paths on Linux with .NET
  • Loading branch information
TheNexusAvenger authored Feb 10, 2022
2 parents ee7a824 + 322ceae commit 31c4870
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 25 deletions.
80 changes: 80 additions & 0 deletions Uchu.Core.Test/Config/ConfigurationPathAttributeTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Collections.Generic;
using NUnit.Framework;
using Uchu.Core.Config;

namespace Uchu.Core.Test.Config
{
public class TestConfiguration
{
public string TestString { get; set; } = "test";
[ConfigurationPath]
public string TestPath1 { get; set; } = "test1";
[ConfigurationPath]
public string TestPath2 { get; set; } = "test1/test2";
[ConfigurationPath]
public string TestPath3 { get; set; } = "/test";
[ConfigurationPath]
public string[] TestPaths1 { get; set; } = new string[]
{
"test1",
"test1/test2",
"/test",
};
[ConfigurationPath]
public List<string> TestPaths2 { get; set; } = new List<string>
{
"test1",
"test1/test2",
"/test",
};
}

public class ConfigurationPathAttributeTest
{
/// <summary>
/// Asserts 2 paths are the same.
/// </summary>
/// <param name="expected">Expected path.</param>
/// <param name="actual">Actual path.</param>
public static void AssertPath(string expected, string actual)
{
Assert.AreEqual(expected.Replace('\\', '/'), actual.Replace('\\', '/'));
}

/// <summary>
/// Tests replacing a null object to verify it doesn't fail.
/// </summary>
[Test]
public void TestNull()
{
ConfigurationPathAttribute.ReplaceFilePaths("", null);
}

/// <summary>
/// Tests replacing paths on Linux.
/// </summary>
[Test]
public void TestPaths()
{
// Replace the configuration paths.
var configuration = new TestConfiguration();
ConfigurationPathAttribute.ReplaceFilePaths("/test1/test2/config.xml", configuration);

// Assert the strings are correct.
AssertPath(configuration.TestString, "test");
AssertPath(configuration.TestPath1, "/test1/test2/test1");
AssertPath(configuration.TestPath2, "/test1/test2/test1/test2");
AssertPath(configuration.TestPath3, "/test");

// Assert the array of strings is correct.
AssertPath(configuration.TestPaths1[0], "/test1/test2/test1");
AssertPath(configuration.TestPaths1[1], "/test1/test2/test1/test2");
AssertPath(configuration.TestPaths1[2], "/test");

// Assert the list of strings is correct.
AssertPath(configuration.TestPaths2[0], "/test1/test2/test1");
AssertPath(configuration.TestPaths2[1], "/test1/test2/test1/test2");
AssertPath(configuration.TestPaths2[2], "/test");
}
}
}
79 changes: 79 additions & 0 deletions Uchu.Core/Config/ConfigurationPathAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace Uchu.Core.Config
{
[AttributeUsage(AttributeTargets.Property)]
public class ConfigurationPathAttribute : Attribute
{
/// <summary>
/// Returns an absolute path relative to a configuration path.
/// </summary>
/// <param name="configurationLocation">File location of the configuration.</param>
/// <param name="relativePath">Relative path to make absolute.</param>
/// <returns>Absolute path of the relative path.</returns>
public static string ReplaceRelativePath(string configurationLocation, string relativePath)
{
// Normalize the relative path.
configurationLocation ??= "";
relativePath ??= "";
relativePath = relativePath.Replace(configurationLocation.Contains('/', StringComparison.CurrentCulture) ? "\\" : "/", configurationLocation.Contains('/', StringComparison.CurrentCulture) ? "/" : "\\", StringComparison.CurrentCulture);

// Return the replaced path.
if (Path.IsPathRooted(relativePath)) return relativePath;
return Path.Join(Path.GetDirectoryName(configurationLocation), relativePath);
}

/// <summary>
/// Replaces local configuration paths relative to the configuration file.
/// </summary>
/// <param name="configurationLocation">File location of the configuration.</param>
/// <param name="configurationObject">Object to replace the paths in.</param>
public static void ReplaceFilePaths(string configurationLocation, object configurationObject)
{
// Return if there is no object to replace.
if (configurationObject == null)
{
return;
}

// Iterate over the properties.
foreach (var property in configurationObject.GetType().GetProperties())
{
if (property.GetIndexParameters().Length != 0) continue;
var value = property.GetValue(configurationObject);
if (value == null) continue;
if (property.GetCustomAttribute<ConfigurationPathAttribute>() != null)
{
// Replace the strings or list of strings.
if (property.PropertyType == typeof(string))
{
property.SetValue(configurationObject, ReplaceRelativePath(configurationLocation, (string) value));
}
else if (property.PropertyType == typeof(string[]))
{
var paths = (string[]) value;
for (var i = 0; i < paths.Length; i++)
{
paths[i] = ReplaceRelativePath(configurationLocation, paths[i]);
}
}
else if (property.PropertyType == typeof(List<string>))
{
var paths = (List<string>) value;
for (var i = 0; i < paths.Count; i++)
{
paths[i] = ReplaceRelativePath(configurationLocation, paths[i]);
}
}
}
else
{
ReplaceFilePaths(configurationLocation, value);
}
}
}
}
}
25 changes: 20 additions & 5 deletions Uchu.Core/Config/UchuConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,18 @@ public void Save(string path)
/// <returns>The <see cref="UchuConfiguration"/>, if the file exists, otherwise null.</returns>
public static UchuConfiguration Load(string path)
{
// Return null if the configuration path does not exist.
if (!File.Exists(path))
return null;

// Read the configuration.
using var file = File.OpenRead(path);
using var reader = XmlReader.Create(file);
return (UchuConfiguration) Serializer.Deserialize(reader);
var configuration = (UchuConfiguration)Serializer.Deserialize(reader);

// Replace the paths and return the configuration.
ConfigurationPathAttribute.ReplaceFilePaths(Path.GetFullPath(path), configuration);
return configuration;
}
}

Expand Down Expand Up @@ -192,12 +198,15 @@ public class ServerDllSource
/// <summary>
/// The path to the Uchu.Instance DLL
/// </summary>
[XmlElement] public string Instance { get; set; } = "../../../../Uchu.Instance/bin/Debug/net6.0/Uchu.Instance.dll";
[XmlElement]
[ConfigurationPath]
public string Instance { get; set; } = "../../../../Uchu.Instance/bin/Debug/net6.0/Uchu.Instance.dll";

/// <summary>
/// The path to the script source DLLs
/// </summary>
[XmlElement]
[ConfigurationPath]
public List<string> ScriptDllSource { get; } = new List<string>();
}

Expand All @@ -209,7 +218,9 @@ public class Networking
/// <summary>
/// Optional certificate file to use for connections
/// </summary>
[XmlElement] public string Certificate { get; set; } = "";
[XmlElement]
[ConfigurationPath]
public string Certificate { get; set; } = "";

/// <summary>
/// The hostname of the Uchu servers
Expand Down Expand Up @@ -303,7 +314,9 @@ public class ResourcesConfiguration
/// <summary>
/// The location of the local game resource folder
/// </summary>
[XmlElement] public string GameResourceFolder { get; set; }
[XmlElement]
[ConfigurationPath]
public string GameResourceFolder { get; set; }
}

/// <summary>
Expand All @@ -319,7 +332,9 @@ public class LoggingConfiguration
/// <summary>
/// The file to log to
/// </summary>
[XmlElement] public string File { get; set; }
[XmlElement]
[ConfigurationPath]
public string File { get; set; }

/// <summary>
/// Whether to log timestamps or not
Expand Down
18 changes: 4 additions & 14 deletions Uchu.Core/UchuServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.EntityFrameworkCore;
using RakDotNet;
using RakDotNet.IO;
Expand Down Expand Up @@ -191,24 +189,16 @@ public virtual async Task ConfigureAsync(string configFile)
ResourceStrings.Server_ConfigureAsync_ConfigFileNullException);

MasterPath = Path.GetDirectoryName(configFile);
var serializer = new XmlSerializer(typeof(UchuConfiguration));
Config = UchuConfiguration.Load(configFile);
if (!Config.DebugConfig.StartInstancesAsThreads)
Logger.SetConfiguration(Config);
UchuContextBase.Config = Config;

if (!File.Exists(configFile))
{
throw new ArgumentException($"{configFile} config file does not exist.");
}

await using (var fs = File.OpenRead(configFile))
{
using (var xmlReader = XmlReader.Create(fs))
{
Config = (UchuConfiguration) serializer.Deserialize(xmlReader);
if (!Config.DebugConfig.StartInstancesAsThreads)
Logger.SetConfiguration(Config);
UchuContextBase.Config = Config;
}
}

await SetupApiAsync().ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(Config.ResourcesConfiguration?.GameResourceFolder))
{
Expand Down
8 changes: 2 additions & 6 deletions Uchu.Instance/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,8 @@ private async Task ConfigureAsync(string config)
throw new ArgumentException($"{config} config file does not exist.");
}

UchuConfiguration uchuConfiguration;

await using (var fs = File.OpenRead(config))
{
UchuContextBase.Config = uchuConfiguration = (UchuConfiguration) serializer.Deserialize(fs);
}
var uchuConfiguration = UchuConfiguration.Load(config);
UchuContextBase.Config = uchuConfiguration;

var masterPath = Path.GetDirectoryName(config);

Expand Down

0 comments on commit 31c4870

Please sign in to comment.