Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api update #133

Merged
merged 11 commits into from
Aug 3, 2024
Merged
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
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Condition="!Exists('packages.config')">
<PrivateAssets>all</PrivateAssets>
<Version>3.6.133</Version>
<Version>3.6.139</Version>
</PackageReference>
<PackageReference Include="SauceControl.InheritDoc" Version="2.0.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
Expand Down
57 changes: 40 additions & 17 deletions sample/SampleApplication/Program.cs
Original file line number Diff line number Diff line change
@@ -1,65 +1,79 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO.Abstractions;
using System.Linq;
using AET.SteamAbstraction;
using AnakinRaW.CommonUtilities.Registry.Windows;
using Microsoft.Extensions.DependencyInjection;
using PG.StarWarsGame.Infrastructure;
using PG.StarWarsGame.Infrastructure.Clients;
using PG.StarWarsGame.Infrastructure.Clients.Arguments;
using PG.StarWarsGame.Infrastructure.Clients.Arguments.GameArguments;
using PG.StarWarsGame.Infrastructure.Clients.Steam;
using PG.StarWarsGame.Infrastructure.Games;
using PG.StarWarsGame.Infrastructure.Mods;
using PG.StarWarsGame.Infrastructure.Services;
using PG.StarWarsGame.Infrastructure.Services.Dependencies;
using PG.StarWarsGame.Infrastructure.Services.Detection;
using PG.StarWarsGame.Infrastructure.Services.Name;
using PG.StarWarsGame.Infrastructure.Services.Steam;


var sp = SetupApplication();
var services = SetupApplication();

var game = FindGame();
var mods = FindMods();

var client = sp.GetRequiredService<IGameClientFactory>().CreateClient(game.Platform, sp);
var client = services.GetRequiredService<IGameClientFactory>().CreateClient(game.Platform, services);

client.Play((IPlayableObject)mods.FirstOrDefault() ?? game);

var firstMod = mods.FirstOrDefault();

IEnumerable<IMod> FindMods()
Console.WriteLine($"Playing {firstMod}");

client.Play((IPlayableObject)firstMod ?? game, new ArgumentCollection(new List<IGameArgument>

Check warning on line 35 in sample/SampleApplication/Program.cs

View workflow job for this annotation

GitHub Actions / Analyze

Converting null literal or possible null value to non-nullable type.
{
new WindowedArgument()
}));
return;


IList<IMod> FindMods()
{
var mods = new List<IMod>();
var modList = new List<IMod>();

var modFinder = sp.GetRequiredService<IModReferenceFinder>();
var modFinder = services.GetRequiredService<IModReferenceFinder>();
var modRefs = modFinder.FindMods(game);
var factory = sp.GetRequiredService<IModFactory>();
var factory = services.GetRequiredService<IModFactory>();

foreach (var modReference in modRefs)
{
var mod = factory.FromReference(game, modReference);
mods.AddRange(mod);
var mod = factory.FromReference(game, modReference, true, CultureInfo.CurrentCulture);
modList.Add(mod);
}

foreach (var mod in mods)
foreach (var mod in modList)
game.AddMod(mod);

// Mods need to be added to the game first, before resolving their dependencies.
foreach(var mod in mods)
foreach(var mod in modList)
{
var resolver = sp.GetRequiredService<IDependencyResolver>();
var resolver = services.GetRequiredService<IDependencyResolver>();
mod.ResolveDependencies(resolver,
new DependencyResolverOptions { CheckForCycle = true, ResolveCompleteChain = true });
}

return mods;
return modList;

}

IGame FindGame()
{
var detector = sp.GetRequiredService<IGameDetector>();
var detector = services.GetRequiredService<IGameDetector>();
var detectionResult = detector.Detect(new GameDetectorOptions(GameType.Foc));
var gameFactory = sp.GetRequiredService<IGameFactory>();
return gameFactory.CreateGame(detectionResult);
var gameFactory = services.GetRequiredService<IGameFactory>();
return gameFactory.CreateGame(detectionResult, CultureInfo.CurrentCulture);
}


Expand All @@ -73,9 +87,18 @@
PetroglyphGameInfrastructure.InitializeServices(sc);
SteamAbstractionLayer.InitializeServices(sc);
PetroglyphGameClients.InitializeServices(sc);

sc.AddSingleton<IModNameResolver>(sp => new CompositeModNameResolver(sp, s => new List<IModNameResolver>
{
new OfflineWorkshopNameResolver(sp),
new OnlineWorkshopNameResolver(sp),
new DirectoryModNameResolver(sp)
}));

sc.AddSingleton<IModGameTypeResolver>(sp => new OnlineModGameTypeResolver(sp));

// The game detector to use for this application.
sc.AddTransient<IGameDetector>(sp => new SteamPetroglyphStarWarsGameDetector(sp));
sc.AddSingleton<IGameDetector>(sp => new SteamPetroglyphStarWarsGameDetector(sp));

return sc.BuildServiceProvider();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="Testably.Abstractions.Testing" Version="3.2.1" />
<PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<PackageReference Include="Testably.Abstractions.Testing" Version="3.2.2" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Gameloop.Vdf" Version="0.6.2" PrivateAssets="compile" />
<PackageReference Include="System.Text.Json" Version="8.0.3" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static void InitializeServices(IServiceCollection serviceCollection)

serviceCollection.AddSingleton<ISteamVdfReader>(sp => new SteamVdfReader(sp));

serviceCollection.AddTransient<ISteamLibraryFinder>(sp => new SteamLibraryFinder(sp));
serviceCollection.AddTransient<ISteamGameFinder>(sp => new SteamGameFinder(sp));
serviceCollection.AddSingleton<ISteamLibraryFinder>(sp => new SteamLibraryFinder(sp));
serviceCollection.AddSingleton<ISteamGameFinder>(sp => new SteamGameFinder(sp));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using PG.StarWarsGame.Infrastructure.Clients.Arguments.GameArguments;

namespace PG.StarWarsGame.Infrastructure.Clients.Arguments;

Expand Down Expand Up @@ -55,9 +56,9 @@ internal string ToCommandLine(IGameArgument argument, string name, string value)
case ArgumentKind.KeyValue:
return BuildKeyValueArgument(name, value);
case ArgumentKind.ModList:
if (argument is not IGameArgument<IReadOnlyList<IGameArgument<string>>> modList)
if (argument is not IGameArgument<IReadOnlyList<ModArgument>> modList)
throw new GameArgumentException(argument,
"Mod List argument is expected to be of type 'IGameArgument<IReadOnlyList<IGameArgument<string>>>'");
"Mod List argument is expected to be of type 'IGameArgument<IReadOnlyList<ModArgument>>'");
return BuildModListString(modList);
default:
throw new ArgumentOutOfRangeException();
Expand All @@ -78,7 +79,7 @@ private static string BuildKeyValueArgument(string key, string value)
return $"{key}={value}";
}

private string BuildModListString(IGameArgument<IReadOnlyList<IGameArgument<string>>> modList)
private string BuildModListString(IGameArgument<IReadOnlyList<ModArgument>> modList)
{
var sb = new StringBuilder();
foreach (var modArg in modList.Value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace PG.StarWarsGame.Infrastructure.Clients.Arguments;
/// <summary>
/// Collections of supported Argument Names
/// </summary>
public static class ArgumentNameCatalog
internal static class ArgumentNameCatalog
{
internal const string ModListArg = "MODLIST";
internal const string WindowedArg = "WINDOWED";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,27 @@ namespace PG.StarWarsGame.Infrastructure.Clients.Arguments;

internal sealed class ArgumentValidator : IArgumentValidator
{
private static readonly char[] InvalidArgumentChars = { '\"', '<', '>', '|', ':', '*', '?'};

// Based on: https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/12-Testing_for_Command_Injection
// Additionally, the game does not like quotes and spaces, so we filter these out too.
private static readonly char[] InvalidArgumentChars = ['\"', '\'', '#', '+', ',', '`', '<', '>', '|', ':', ';', '*', '?', '&', ' ', '!', '$', '=', '@', '%', '~'];

public ArgumentValidityStatus CheckArgument(IGameArgument argument, out string name, out string value)
{
name = argument.Name;
name = argument.Name.ToUpperInvariant();
value = argument.ValueToCommandLine();
var kind = argument.Kind;

// We do not support custom arguments
if (!ArgumentNameCatalog.AllSupportedArgumentNames.Contains(name))
{
name = name.ToUpperInvariant();
if (!ArgumentNameCatalog.AllSupportedArgumentNames.Contains(name))
return ArgumentValidityStatus.InvalidName;
}
return ArgumentValidityStatus.InvalidName;

var isNullOrEmpty = string.IsNullOrEmpty(value);

if (isNullOrEmpty && kind == ArgumentKind.KeyValue)
return ArgumentValidityStatus.EmptyData;

// ModList kind is expected to return an empty string.
// ModList must always return an empty string.
if (!isNullOrEmpty && kind == ArgumentKind.ModList)
return ArgumentValidityStatus.InvalidData;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public enum ArgumentValidityStatus
/// </summary>
IllegalCharacter,
/// <summary>
/// The argument contains a space character. This not legal e.g. for filesystem paths.
/// The argument contains a space character. This is not legal for e.g, filesystem paths.
/// </summary>
PathContainsSpaces,
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ namespace PG.StarWarsGame.Infrastructure.Clients.Arguments;

internal class ArgumentValueSerializer
{
private static readonly PathNormalizeOptions NormalizeOptions = new()
{
TrailingDirectorySeparatorBehavior = TrailingDirectorySeparatorBehavior.Trim,
UnifyCase = UnifyCasingKind.UpperCase,
UnifyDirectorySeparators = true
};

private static readonly Dictionary<string, (Type, TypeConverter?)> SpecialTypes;

static ArgumentValueSerializer()
Expand Down Expand Up @@ -50,19 +57,10 @@ public string ShortenPath(IFileSystemInfo target, IDirectoryInfo gameDir)
{
var fileSystem = target.FileSystem;

var gamePath = PathNormalizer.Normalize(fileSystem.Path.GetFullPath(gameDir.FullName),
PathNormalizeOptions.EnsureTrailingSeparator);
if (!fileSystem.Path.IsChildOf(gameDir.FullName, target.FullName))
return PathNormalizer.Normalize(target.FullName, NormalizeOptions);

var targetPath = PathNormalizer.Normalize(fileSystem.Path.GetFullPath(target.FullName),
PathNormalizeOptions.EnsureTrailingSeparator);

// It's important not to resolve, as that would give us an absolute path again.
return PathNormalizer.Normalize(fileSystem.Path.GetRelativePathEx(gamePath, targetPath), new PathNormalizeOptions
{
TrailingDirectorySeparatorBehavior = TrailingDirectorySeparatorBehavior.Trim,
UnifyCase = UnifyCasingKind.UpperCase,
UnifyDirectorySeparators = true
});
return PathNormalizer.Normalize(fileSystem.Path.GetRelativePathEx(gameDir.FullName, target.FullName), NormalizeOptions);
}

private static TypeConverter? GetConverter(string typeName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ namespace PG.StarWarsGame.Infrastructure.Clients.Arguments;
/// <summary>
/// Argument which enables a game behavior because it exists.
/// </summary>
/// <remarks>This argument can be explicitly unset by setting the value to <see langword="false"/>.
/// On the command line it will just omitted.</remarks>
/// <remarks>This argument can be explicitly unset by setting the value to <see langword="false"/>. In this case, on the command line it will be omitted.</remarks>
public abstract class FlagArgument : GameArgument<bool>
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected GameArgument(T value, bool isDebug = false)
public abstract string ValueToCommandLine();

/// <summary>
/// This method shall only perform semantical checks on the <see cref="Value"/> property.
/// This method shall only perform semantic checks on the <see cref="Value"/> property.
/// <para>If this method returns <see langword="false"/>, <see cref="IsValid(out ArgumentValidityStatus)"/>
/// with return <see langword="false"/> with reason <see cref="ArgumentValidityStatus.InvalidData"/>.</para>
/// <para>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// <summary>
/// The exception that is thrown when a <see cref="IGameArgument"/> or handling it caused an error.
/// </summary>
public class GameArgumentException : PetroglyphException
public sealed class GameArgumentException : PetroglyphException
{
/// <summary>
/// The argument which caused the error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ internal sealed class LowRamArgument() : FlagArgument(ArgumentNameCatalog.LowRam
/// <summary>
/// UNKNOWN BEHAVIOR!
/// </summary>
internal sealed class MCEArgument() : FlagArgument(ArgumentNameCatalog.LowRamArg, true, true);
internal sealed class MCEArgument() : FlagArgument(ArgumentNameCatalog.MCEArg, true, true);

#endregion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ namespace PG.StarWarsGame.Infrastructure.Clients.Arguments.GameArguments;
/// <summary>
/// Special argument which holds an ordered list of arguments to represent a mod chain.
/// </summary>
public sealed class ModArgumentList : GameArgument<IReadOnlyList<IGameArgument<string>>>
public sealed class ModArgumentList : GameArgument<IReadOnlyList<ModArgument>>
{
/// <summary>
/// Empty <see cref="ModArgumentList"/>.
/// </summary>
public static ModArgumentList Empty = new(new List<NamedArgument<string>>(0));
public static ModArgumentList Empty = new(new List<ModArgument>(0));

/// <summary>
/// This is always <see cref="ArgumentKind.ModList"/>.
Expand All @@ -26,7 +26,7 @@ public sealed class ModArgumentList : GameArgument<IReadOnlyList<IGameArgument<s
/// Creates a new argument from a given list of
/// </summary>
/// <param name="mods">The mod arguments of this list.</param>
public ModArgumentList(IReadOnlyList<IGameArgument<string>> mods) : base(mods)
public ModArgumentList(IReadOnlyList<ModArgument> mods) : base(mods)
{
}

Expand All @@ -52,7 +52,7 @@ public override bool Equals(IGameArgument? other)
return false;
if (ReferenceEquals(this, other))
return true;
if (other is not IGameArgument<IReadOnlyList<IGameArgument<string>>> otherModList)
if (other is not IGameArgument<IReadOnlyList<ModArgument>> otherModList)
return false;
return Kind == other.Kind && Value.SequenceEqual(otherModList.Value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,4 @@ namespace PG.StarWarsGame.Infrastructure.Clients.Arguments;
/// <summary>
/// Represents a collection of <see cref="IGameArgument"/>s.
/// </summary>
public interface IArgumentCollection : IReadOnlyCollection<IGameArgument>
{
}
public interface IArgumentCollection : IReadOnlyCollection<IGameArgument>;

This file was deleted.

Loading
Loading