Skip to content

Commit

Permalink
implement and test modfinder
Browse files Browse the repository at this point in the history
  • Loading branch information
AnakinRaW committed Dec 14, 2024
1 parent 0929dac commit 6643fe7
Show file tree
Hide file tree
Showing 15 changed files with 324 additions and 587 deletions.
2 changes: 1 addition & 1 deletion sample/SampleApplication/SampleApplication.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.71" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="AnakinRaW.CommonUtilities.Registry" Version="12.0.28-beta" />
<PackageReference Include="AnakinRaW.CommonUtilities.Registry" Version="12.0.39-beta" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AnakinRaW.CommonUtilities" Version="12.0.28-beta" />
<PackageReference Include="AnakinRaW.CommonUtilities.FileSystem" Version="12.0.28-beta" />
<PackageReference Include="AnakinRaW.CommonUtilities.Registry" Version="12.0.28-beta" />
<PackageReference Include="eaw.modinfo" Version="6.2.4" />
<PackageReference Include="AnakinRaW.CommonUtilities" Version="12.0.39-beta" />
<PackageReference Include="AnakinRaW.CommonUtilities.FileSystem" Version="12.0.39-beta" />
<PackageReference Include="AnakinRaW.CommonUtilities.Registry" Version="12.0.39-beta" />
<PackageReference Include="eaw.modinfo" Version="6.2.7" />
<PackageReference Include="IsExternalInit" Version="1.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public static void InitializeServices(IServiceCollection serviceCollection)
serviceCollection.AddSingleton<ISteamGameHelpers>(sp => new SteamGameHelpers(sp));
serviceCollection.AddSingleton<IGameFactory>(sp => new GameFactory(sp));
//serviceCollection.AddSingleton<IModFactory>(sp => new ModFactory(sp));
serviceCollection.AddSingleton<IModIdentifierBuilder>(sp => new ModIdentifierBuilder(sp));
serviceCollection.AddSingleton<IModFinder>(sp => new ModFinder(sp));
serviceCollection.AddSingleton<IModReferenceLocationResolver>(sp => new ModReferenceLocationResolver(sp));

Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using EawModinfo.Spec;
using EawModinfo.Utilities;
using PG.StarWarsGame.Infrastructure.Games;

namespace PG.StarWarsGame.Infrastructure.Services.Detection;
Expand All @@ -10,9 +12,11 @@ namespace PG.StarWarsGame.Infrastructure.Services.Detection;
public interface IModFinder
{
/// <summary>
/// Searches for <see cref="IModReference"/> for a given game.
/// Searches for physically installed mods for the specified game.
/// </summary>
/// <param name="game">The game to search mods for.</param>
/// <returns>A set of detected mod references.</returns>
ISet<DetectedModReference> FindMods(IGame game);
/// <returns>A collection of installed mods of <paramref name="game"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="game"/> is <see langword="null"/>.</exception>
/// <exception cref="GameException"><paramref name="game"/> does not exist.</exception>
ICollection<DetectedModReference> FindMods(IGame game);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
using System.IO.Abstractions;
using System.Linq;
using EawModinfo.File;
using EawModinfo.Model;
using EawModinfo.Spec;
using EawModinfo.Utilities;
using Microsoft.Extensions.DependencyInjection;
using PG.StarWarsGame.Infrastructure.Games;
using PG.StarWarsGame.Infrastructure.Services.Steam;
Expand All @@ -14,77 +14,52 @@ namespace PG.StarWarsGame.Infrastructure.Services.Detection;
internal class ModFinder : IModFinder
{
private readonly ISteamGameHelpers _steamHelper;
private readonly IModIdentifierBuilder _idBuilder;
private readonly IModGameTypeResolver _gameTypeResolver;

public ModFinder(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
throw new ArgumentNullException(nameof(serviceProvider));
_idBuilder = serviceProvider.GetRequiredService<IModIdentifierBuilder>();
_steamHelper = serviceProvider.GetRequiredService<ISteamGameHelpers>();
_gameTypeResolver = serviceProvider.GetRequiredService<IModGameTypeResolver>();
}

public ISet<DetectedModReference> FindMods(IGame game)
public ICollection<DetectedModReference> FindMods(IGame game)
{
if (game == null)
throw new ArgumentNullException(nameof(game));

if (!game.Exists())
throw new GameException("The game does not exist");

var mods = new HashSet<DetectedModReference>();
foreach (var modReference in GetNormalMods(game).Union(GetWorkshopsMods(game)))
mods.Add(modReference);
return mods;
return GetNormalMods(game).Union(GetWorkshopsMods(game)).ToList();
}

private IEnumerable<DetectedModReference> GetNormalMods(IGame game)
{
return GetAllModsFromPath(game.ModsLocation, false, game.Type);
return GetAllModsFromPath(game.ModsLocation, ModReferenceBuilder.ModLocationKind.GameModsDirectory, game.Type);
}

private IEnumerable<DetectedModReference> GetWorkshopsMods(IGame game)
{
return game.Platform != GamePlatform.SteamGold
? []
: GetAllModsFromPath(_steamHelper.GetWorkshopsLocation(game), true, game.Type);
: GetAllModsFromPath(_steamHelper.GetWorkshopsLocation(game), ModReferenceBuilder.ModLocationKind.SteamWorkshops, game.Type);
}

private IEnumerable<DetectedModReference> GetAllModsFromPath(IDirectoryInfo lookupDirectory, bool isWorkshopsPath, GameType requestedGameType)
private IEnumerable<DetectedModReference> GetAllModsFromPath(IDirectoryInfo lookupDirectory, ModReferenceBuilder.ModLocationKind locationKind, GameType requestedGameType)
{
if (!lookupDirectory.Exists)
yield break;

var type = isWorkshopsPath ? ModType.Workshops : ModType.Default;

foreach (var modDirectory in lookupDirectory.EnumerateDirectories())
{
var modinfoFiles = ModinfoFileFinder.FindModinfoFiles(modDirectory);

IModinfo? mainModinfo = null;

// Since the concept of modinfo files is completely optional,
// a malformed modinfo file should not cause a crash. The mod in question gets fond without a modinfo.
modinfoFiles.MainModinfo?.TryGetModinfo(out mainModinfo);

if (IsDefinitelyNotGameType(requestedGameType, modDirectory, type, mainModinfo))
continue;
ModinfoFinderCollection modinfoFiles;
modinfoFiles = ModinfoFileFinder.FindModinfoFiles(modDirectory);

var id = _idBuilder.Build(modDirectory, isWorkshopsPath);
yield return new DetectedModReference(new ModReference(id, type), modDirectory, mainModinfo);

foreach (var variantModinfoFile in modinfoFiles.Variants)
foreach (var modRef in ModReferenceBuilder.CreateIdentifiers(modinfoFiles, locationKind))
{
if (!variantModinfoFile.TryGetModinfo(out var variantModinfo))
continue;

if (IsDefinitelyNotGameType(requestedGameType, modDirectory, type, variantModinfo))
if (IsDefinitelyNotGameType(requestedGameType, modDirectory, modRef.ModReference.Type, modRef.Modinfo))
continue;

id = _idBuilder.Build(modDirectory, isWorkshopsPath);
yield return new DetectedModReference(new ModReference(id, type), modDirectory, variantModinfo);
yield return modRef;
}
}
}
Expand All @@ -94,6 +69,6 @@ private bool IsDefinitelyNotGameType(GameType expected, IDirectoryInfo modDirect
// If the type resolver was unable to find the type, we have to assume that the current mod matches to the game.
// Otherwise, we'd produce false negatives. Only if the resolver was able to determine a result, we use that finding.
return _gameTypeResolver.TryGetGameType(modDirectory, modType, modinfo, out var variantGameType) &&
variantGameType.Contains(expected);
!variantGameType.Contains(expected);
}
}

This file was deleted.

2 changes: 1 addition & 1 deletion src/PetroGlyph.Games.EawFoc/src/Services/IModFactory.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System.Globalization;
using System.IO;
using EawModinfo.Spec;
using EawModinfo.Utilities;
using PG.StarWarsGame.Infrastructure.Games;
using PG.StarWarsGame.Infrastructure.Mods;
using PG.StarWarsGame.Infrastructure.Services.Detection;

namespace PG.StarWarsGame.Infrastructure.Services;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.IO.Abstractions;
using Moq;
using PG.StarWarsGame.Infrastructure.Games;
using PG.StarWarsGame.Infrastructure.Services.Detection;
using PG.StarWarsGame.Infrastructure.Testing.Game.Installation;
Expand Down Expand Up @@ -28,10 +27,9 @@ protected override GameDetectorTestInfo<EmptyStruct> SetupGame(GameIdentity game
[Fact]
public void InvalidArgs_Throws()
{
var sp = new Mock<IServiceProvider>();
Assert.Throws<ArgumentNullException>(() => new DirectoryGameDetector(null!, null!));
Assert.Throws<ArgumentNullException>(() => new DirectoryGameDetector(FileSystem.DirectoryInfo.New("Game"), null!));
Assert.Throws<ArgumentNullException>(() => new DirectoryGameDetector(null!, sp.Object));
Assert.Throws<ArgumentNullException>(() => new DirectoryGameDetector(null!, ServiceProvider));
}

[Theory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public void Dispose_ShallDisposeRegistries(GameIdentity identity)
false, ServiceProvider);
detector.Dispose();

Assert.Throws<ObjectDisposedException>(() => info.DetectorSetupInfo.EawRegistry.CdKey);
Assert.Throws<ObjectDisposedException>(() => info.DetectorSetupInfo.FocRegistry.CdKey);
Assert.Throws<ObjectDisposedException>(() => info.DetectorSetupInfo.EawRegistry!.CdKey);
Assert.Throws<ObjectDisposedException>(() => info.DetectorSetupInfo.FocRegistry!.CdKey);
}
}
Loading

0 comments on commit 6643fe7

Please sign in to comment.