Skip to content

Commit

Permalink
Alternate game command lines and Steam refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Jan 31, 2024
1 parent f16776f commit 3a21749
Show file tree
Hide file tree
Showing 46 changed files with 710 additions and 467 deletions.
2 changes: 1 addition & 1 deletion Cmdline/Action/GameInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ int badArgument()
string path = options.path;
GameVersion version;
bool setDefault = options.setDefault;
IGame game = GameInstanceManager.GameByShortName(options.gameId);
IGame game = KnownGames.GameByShortName(options.gameId);
if (game == null)
{
User.RaiseMessage(Properties.Resources.InstanceFakeBadGame, options.gameId);
Expand Down
16 changes: 11 additions & 5 deletions Cmdline/ConsoleUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,15 @@ public void RaiseError(string message, params object[] args)
/// <param name="percent">Progress in percent</param>
public void RaiseProgress(string message, int percent)
{
// The percent looks weird on non-download messages.
// The leading newline makes sure we don't end up with a mess from previous
// download messages.
GoToStartOfLine();
Console.Write("{0}", message);
if (message != lastProgressMessage)
{
// The percent looks weird on non-download messages.
// The leading newline makes sure we don't end up with a mess from previous
// download messages.
GoToStartOfLine();
Console.Write("{0}", message);
lastProgressMessage = message;
}

// This message leaves the cursor at the end of a line of text
atStartOfLine = false;
Expand Down Expand Up @@ -286,6 +290,8 @@ public void RaiseProgress(int percent, long bytesPerSecond, long bytesLeft)
/// </summary>
private int previousPercent = -1;

private string lastProgressMessage = null;

/// <summary>
/// Writes a message to the console
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions Core/CKAN-core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NJsonSchema" Version="10.9.0" />
<PackageReference Include="ValveKeyValue" Version="0.3.1.152" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net48' ">
<PackageReference Include="ChinhDo.Transactions.FileManager" Version="1.2.0" />
Expand Down
43 changes: 0 additions & 43 deletions Core/CKANPathUtils.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

using log4net;
Expand All @@ -20,48 +19,6 @@ public static class CKANPathUtils

private static readonly ILog log = LogManager.GetLogger(typeof(CKANPathUtils));

/// <summary>
/// Finds Steam on the current machine.
/// </summary>
/// <returns>The path to Steam, or null if not found</returns>
public static string SteamPath()
{
foreach (var steam in SteamPaths.Where(p => !string.IsNullOrEmpty(p)))
{
log.DebugFormat("Looking for Steam in {0}", steam);
if (Directory.Exists(steam))
{
log.InfoFormat("Found Steam at {0}", steam);
return steam;
}
}
log.Info("Steam not found on this system.");
return null;
}

private const string steamRegKey = @"HKEY_CURRENT_USER\Software\Valve\Steam";
private const string steamRegValue = @"SteamPath";

private static string[] SteamPaths
=> Platform.IsWindows ? new string[]
{
// First check the registry
(string)Microsoft.Win32.Registry.GetValue(steamRegKey, steamRegValue, null),
}
: Platform.IsUnix ? new string[]
{
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal),
".local", "share", "Steam"),
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal),
".steam", "steam"),
}
: Platform.IsMac ? new string[]
{
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal),
"Library", "Application Support", "Steam"),
}
: Array.Empty<string>();

/// <summary>
/// Normalizes the path by replacing all \ with / and removing any trailing slash.
/// </summary>
Expand Down
4 changes: 3 additions & 1 deletion Core/Converters/JsonToGamesDictionaryConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using CKAN.Games;

namespace CKAN
{
/// <summary>
Expand Down Expand Up @@ -59,7 +61,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
var obj = (IDictionary)Activator.CreateInstance(objectType);
if (!IsTokenEmpty(token))
{
foreach (var gameName in GameInstanceManager.AllGameShortNames())
foreach (var gameName in KnownGames.AllGameShortNames())
{
// Make a new copy of the value for each game
obj.Add(gameName, token.ToObject(valueType));
Expand Down
11 changes: 11 additions & 0 deletions Core/Extensions/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Text.RegularExpressions;

namespace CKAN.Extensions
{
Expand Down Expand Up @@ -216,6 +217,16 @@ public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> kvp, out T1 key
val = kvp.Value;
}

/// <summary>
/// Try matching a regex against a series of strings and return the Match objects
/// </summary>
/// <param name="source">Sequence of strings to scan</param>
/// <param name="pattern">Pattern to match</param>
/// <returns>Sequence of Match objects</returns>
public static IEnumerable<Match> WithMatches(this IEnumerable<string> source, Regex pattern)
=> source.Select(val => pattern.TryMatch(val, out Match match) ? match : null)
.Where(m => m != null);

}

/// <summary>
Expand Down
39 changes: 1 addition & 38 deletions Core/GameInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class GameInstance : IEquatable<GameInstance>
public TimeLog playTime;

public string Name { get; set; }

/// <summary>
/// Returns a file system safe version of the instance name that can be used within file names.
/// </summary>
Expand Down Expand Up @@ -240,44 +241,6 @@ public static string PortableDir(IGame game)
return null;
}

/// <summary>
/// Attempts to automatically find a KSP install on this system.
/// Returns the path to the install on success.
/// Throws a DirectoryNotFoundException on failure.
/// </summary>
public static string FindGameDir(IGame game)
{
// See if we can find KSP as part of a Steam install.
string gameSteamPath = game.SteamPath();
if (gameSteamPath != null)
{
if (game.GameInFolder(new DirectoryInfo(gameSteamPath)))
{
return gameSteamPath;
}

log.DebugFormat("Have Steam, but {0} is not at \"{1}\".",
game.ShortName, gameSteamPath);
}

// See if we can find a non-Steam Mac KSP install
string kspMacPath = game.MacPath();
if (kspMacPath != null)
{
if (game.GameInFolder(new DirectoryInfo(kspMacPath)))
{
log.InfoFormat("Found a {0} install at {1}",
game.ShortName, kspMacPath);
return kspMacPath;
}
log.DebugFormat("Default Mac {0} folder exists at \"{1}\", but {0} is not installed there.",
game.ShortName, kspMacPath);
}

// Oh noes! We can't find KSP!
throw new DirectoryNotFoundException();
}

/// <summary>
/// Detects the version of a game in a given directory.
/// </summary>
Expand Down
101 changes: 52 additions & 49 deletions Core/GameInstanceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ namespace CKAN
/// </summary>
public class GameInstanceManager : IDisposable
{
private static readonly IGame[] knownGames = new IGame[]
{
new KerbalSpaceProgram(),
new KerbalSpaceProgram2(),
};

/// <summary>
/// An IUser object for user interaction.
/// It is initialized during the startup with a ConsoleUser,
Expand All @@ -40,11 +34,13 @@ public class GameInstanceManager : IDisposable

public NetModuleCache Cache { get; private set; }

public readonly SteamLibrary SteamLibrary = new SteamLibrary();

private static readonly ILog log = LogManager.GetLogger(typeof (GameInstanceManager));

private readonly SortedList<string, GameInstance> instances = new SortedList<string, GameInstance>();

public string[] AllInstanceAnchorFiles => knownGames
public string[] AllInstanceAnchorFiles => KnownGames.knownGames
.SelectMany(g => g.InstanceAnchorFiles)
.Distinct()
.ToArray();
Expand Down Expand Up @@ -98,7 +94,7 @@ public GameInstance GetPreferredInstance()
// Actual worker for GetPreferredInstance()
internal GameInstance _GetPreferredInstance()
{
foreach (IGame game in knownGames)
foreach (IGame game in KnownGames.knownGames)
{
// TODO: Check which ones match, prompt user if >1

Expand Down Expand Up @@ -135,7 +131,7 @@ internal GameInstance _GetPreferredInstance()
// If we know of no instances, try to find one.
// Otherwise, we know of too many instances!
// We don't know which one to pick, so we return null.
return !instances.Any() ? FindAndRegisterDefaultInstance() : null;
return !instances.Any() ? FindAndRegisterDefaultInstances() : null;
}

/// <summary>
Expand All @@ -145,35 +141,58 @@ internal GameInstance _GetPreferredInstance()
///
/// Returns the resulting game instance if found.
/// </summary>
public GameInstance FindAndRegisterDefaultInstance()
public GameInstance FindAndRegisterDefaultInstances()
{
if (instances.Any())
{
throw new KSPManagerKraken("Attempted to scan for defaults with instances");
}
GameInstance val = null;
foreach (IGame game in knownGames)
var found = FindDefaultInstances();
foreach (var inst in found)
{
try
log.DebugFormat("Registering {0} at {1}...",
inst.Name, inst.GameDir());
AddInstance(inst);
}
return found.FirstOrDefault();
}

public GameInstance[] FindDefaultInstances()
{
var found = KnownGames.knownGames.SelectMany(g =>
SteamLibrary.Games
.Select(sg => new { name = sg.Name, dir = sg.GameDir })
.Append(new
{
name = string.Format(Properties.Resources.GameInstanceManagerAuto,
g.ShortName),
dir = g.MacPath(),
})
.Where(obj => obj.dir != null && g.GameInFolder(obj.dir))
.Select(obj => new GameInstance(g, obj.dir.FullName, obj.name, User)))
.Where(inst => inst.Valid)
.ToArray();
foreach (var group in found.GroupBy(inst => inst.Name))
{
if (group.Count() > 1)
{
string gamedir = GameInstance.FindGameDir(game);
GameInstance foundInst = new GameInstance(
game, gamedir, string.Format(Properties.Resources.GameInstanceManagerAuto, game.ShortName), User);
if (foundInst.Valid)
// Make sure the names are unique
int index = 0;
foreach (var inst in group)
{
var inst = AddInstance(foundInst);
val = val ?? inst;
// Find an unused name
string name;
do
{
++index;
name = $"{group.Key} ({++index})";
}
while (found.Any(other => other.Name == name));
inst.Name = name;
}
}
catch (DirectoryNotFoundException)
{
// Thrown if no folder found for a game
}
catch (NotKSPDirKraken)
{
}
}
return val;
return found;
}

/// <summary>
Expand Down Expand Up @@ -429,7 +448,7 @@ public void SetCurrentInstance(string name)

public void SetCurrentInstanceByPath(string path)
{
var matchingGames = knownGames
var matchingGames = KnownGames.knownGames
.Where(g => g.GameInFolder(new DirectoryInfo(path)))
.ToList();
switch (matchingGames.Count)
Expand Down Expand Up @@ -458,7 +477,7 @@ public void SetCurrentInstanceByPath(string path)

public GameInstance InstanceAt(string path)
{
var matchingGames = knownGames
var matchingGames = KnownGames.knownGames
.Where(g => g.GameInFolder(new DirectoryInfo(path)))
.ToList();
switch (matchingGames.Count)
Expand Down Expand Up @@ -514,8 +533,8 @@ private void LoadInstances()
var gameName = instance.Item3;
try
{
var game = knownGames.FirstOrDefault(g => g.ShortName == gameName)
?? knownGames[0];
var game = KnownGames.knownGames.FirstOrDefault(g => g.ShortName == gameName)
?? KnownGames.knownGames[0];
log.DebugFormat("Loading {0} from {1}", name, path);
// Add unconditionally, sort out invalid instances downstream
instances.Add(name, new GameInstance(game, path, name, User));
Expand Down Expand Up @@ -614,7 +633,7 @@ public void Dispose()
}

public static bool IsGameInstanceDir(DirectoryInfo path)
=> knownGames.Any(g => g.GameInFolder(path));
=> KnownGames.knownGames.Any(g => g.GameInFolder(path));

/// <summary>
/// Tries to determine the game that is installed at the given path
Expand All @@ -625,7 +644,7 @@ public static bool IsGameInstanceDir(DirectoryInfo path)
/// <exception cref="NotKSPDirKraken">Thrown when no games found</exception>
public IGame DetermineGame(DirectoryInfo path, IUser user)
{
var matchingGames = knownGames.Where(g => g.GameInFolder(path)).ToList();
var matchingGames = KnownGames.knownGames.Where(g => g.GameInFolder(path)).ToList();
switch (matchingGames.Count)
{
case 0:
Expand All @@ -642,21 +661,5 @@ public IGame DetermineGame(DirectoryInfo path, IUser user)
return selection >= 0 ? matchingGames[selection] : null;
}
}

/// <summary>
/// Return a game object based on its short name
/// </summary>
/// <param name="shortName">The short name to find</param>
/// <returns>A game object or null if none found</returns>
public static IGame GameByShortName(string shortName)
=> knownGames.FirstOrDefault(g => g.ShortName == shortName);

/// <summary>
/// Return the short names of all known games
/// </summary>
/// <returns>Sequence of short name strings</returns>
public static IEnumerable<string> AllGameShortNames()
=> knownGames.Select(g => g.ShortName);

}
}
Loading

0 comments on commit 3a21749

Please sign in to comment.