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

Alternate game command lines and Steam refactor #4010

Merged
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 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
102 changes: 52 additions & 50 deletions Core/GameInstanceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using CKAN.Configuration;
using CKAN.Games;
using CKAN.Games.KerbalSpaceProgram;
using CKAN.Games.KerbalSpaceProgram2;
using CKAN.Extensions;
using CKAN.Games.KerbalSpaceProgram.GameVersionProviders;

Expand All @@ -23,12 +22,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 +33,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 +93,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 +130,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 +140,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 +447,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 +476,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 +532,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 +632,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 +643,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 +660,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
Loading