diff --git a/CHANGELOG.md b/CHANGELOG.md
index eff41aec16..1a029c5c61 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,7 @@ All notable changes to this project will be documented in this file.
- [GUI] Suppress filter updates for unchanged semantic search meaning (#3435 by: HebaruSan; reviewed: DasSkelett)
- [GUI] Use CRLF for resx files (#3471 by: HebaruSan; reviewed: DasSkelett)
- [Core] Case insensitive installed file lookup on Windows (#3479 by: HebaruSan; reviewed: DasSkelett)
+- [Core] Properly determine the game when cloning instances (#3478 by: DasSkelett; reviewed: HebaruSan)
### Internal
diff --git a/Cmdline/ConsoleUser.cs b/Cmdline/ConsoleUser.cs
index 1f0c511e8d..4e596a5a9f 100644
--- a/Cmdline/ConsoleUser.cs
+++ b/Cmdline/ConsoleUser.cs
@@ -78,7 +78,7 @@ public bool RaiseYesNoDialog(string question)
/// The output is index 0 based.
/// To supply a default option, make the first option an integer indicating the index of it.
///
- /// The selection dialog
+ /// The selected index or -1 if cancelled
/// Message
/// Array of available options
public int RaiseSelectionDialog(string message, params object[] args)
diff --git a/Core/GameInstanceManager.cs b/Core/GameInstanceManager.cs
index 1d0bdf715d..b93e63793f 100644
--- a/Core/GameInstanceManager.cs
+++ b/Core/GameInstanceManager.cs
@@ -22,6 +22,11 @@ public class GameInstanceManager : IDisposable
new KerbalSpaceProgram()
};
+ ///
+ /// An IUser object for user interaction.
+ /// It is initialized during the startup with a ConsoleUser,
+ /// do not use in functions that could be called by the GUI.
+ ///
public IUser User { get; set; }
public IConfiguration Configuration { get; set; }
public GameInstance CurrentInstance { get; set; }
@@ -69,7 +74,7 @@ public GameInstanceManager(IUser user, IConfiguration configuration = null)
}
///
- /// Returns the preferred KSP instance, or null if none can be found.
+ /// Returns the preferred game instance, or null if none can be found.
///
/// This works by checking to see if we're in a KSP dir first, then the
/// config for an autostart instance, then will try to auto-populate
@@ -170,45 +175,40 @@ public GameInstance FindAndRegisterDefaultInstance()
}
///
- /// Adds a KSP instance to config.
- /// Returns the resulting KSP object.
+ /// Adds a game instance to config.
///
- public GameInstance AddInstance(GameInstance ksp_instance)
+ /// The resulting GameInstance object
+ /// Thrown if the instance is not a valid game instance.
+ public GameInstance AddInstance(GameInstance instance)
{
- if (ksp_instance.Valid)
+ if (instance.Valid)
{
- string name = ksp_instance.Name;
- instances.Add(name, ksp_instance);
+ string name = instance.Name;
+ instances.Add(name, instance);
Configuration.SetRegistryToInstances(instances);
}
else
{
- throw new NotKSPDirKraken(ksp_instance.GameDir());
+ throw new NotKSPDirKraken(instance.GameDir());
}
- return ksp_instance;
+ return instance;
}
+ ///
+ /// Adds a game instance to config.
+ ///
+ /// The path of the instance
+ /// The name of the instance
+ /// IUser object for interaction
+ /// The resulting GameInstance object
+ /// Thrown if the instance is not a valid game instance.
public GameInstance AddInstance(string path, string name, IUser user)
{
- var matchingGames = knownGames
- .Where(g => g.GameInFolder(new DirectoryInfo(path)))
- .ToList();
- switch (matchingGames.Count)
- {
- case 0:
- throw new NotKSPDirKraken(path);
-
- case 1:
- return AddInstance(new GameInstance(
- matchingGames.First(),
- path, name, user
- ));
+ var game = DetermineGame(new DirectoryInfo(path), user);
+ if (game == null)
+ return null;
- default:
- // TODO: Prompt user to choose
- return null;
-
- }
+ return AddInstance(new GameInstance(game, path, name, user));
}
///
@@ -612,5 +612,32 @@ public static bool IsGameInstanceDir(DirectoryInfo path)
return knownGames.Any(g => g.GameInFolder(path));
}
+ ///
+ /// Tries to determine the game that is installed at the given path
+ ///
+ /// A DirectoryInfo of the path to check
+ /// IUser object for interaction
+ /// An instance of the matching game or null if the user cancelled
+ /// Thrown when no games found
+ public IGame DetermineGame(DirectoryInfo path, IUser user)
+ {
+ var matchingGames = knownGames.Where(g => g.GameInFolder(path)).ToList();
+ switch (matchingGames.Count)
+ {
+ case 0:
+ throw new NotKSPDirKraken(path.FullName);
+
+ case 1:
+ return matchingGames.First();
+
+ default:
+ // Prompt user to choose
+ int selection = user.RaiseSelectionDialog(
+ $"Please select the game that is installed at {path.FullName.Replace('/', Path.DirectorySeparatorChar)}",
+ matchingGames.Select(g => g.ShortName).ToArray());
+ return selection >= 0 ? matchingGames[selection] : null;
+ }
+ }
+
}
}
diff --git a/Core/User.cs b/Core/User.cs
index 8151f24ffd..e161fdcd3d 100644
--- a/Core/User.cs
+++ b/Core/User.cs
@@ -10,6 +10,13 @@ public interface IUser
bool Headless { get; }
bool RaiseYesNoDialog(string question);
+
+ ///
+ /// Ask the user to select one of the elements of the array.
+ /// The output is index 0 based.
+ /// To supply a default option, make the first option an integer indicating the index of it.
+ ///
+ /// The index of the item selected from the array or -1 if cancelled
int RaiseSelectionDialog(string message, params object[] args);
void RaiseError(string message, params object[] args);
diff --git a/GUI/Dialogs/CloneFakeGameDialog.cs b/GUI/Dialogs/CloneFakeGameDialog.cs
index 15d7da86a6..6ac1e00140 100644
--- a/GUI/Dialogs/CloneFakeGameDialog.cs
+++ b/GUI/Dialogs/CloneFakeGameDialog.cs
@@ -53,7 +53,7 @@ private void comboBoxKnownInstance_SelectedIndexChanged(object sender, EventArgs
string sel = comboBoxKnownInstance.SelectedItem as string;
textBoxClonePath.Text = string.IsNullOrEmpty(sel)
? ""
- : manager.Instances[sel].GameDir();
+ : manager.Instances[sel].GameDir().Replace('/', Path.DirectorySeparatorChar);
}
///
@@ -113,6 +113,7 @@ private void radioButton_CheckedChanged(object sender, EventArgs e)
///
private async void buttonOK_Click(object sender, EventArgs e)
{
+ string existingPath = textBoxClonePath.Text;
string newName = textBoxNewName.Text;
string newPath = textBoxNewPath.Text;
@@ -144,17 +145,26 @@ private async void buttonOK_Click(object sender, EventArgs e)
try
{
- await Task.Run(() =>
+ GameInstance instanceToClone = null;
+ if (!manager.Instances.TryGetValue(comboBoxKnownInstance.SelectedItem as string, out instanceToClone)
+ || existingPath != instanceToClone.GameDir().Replace('/', Path.DirectorySeparatorChar))
{
- GameInstance sourceInstance = manager.Instances.Values
- .FirstOrDefault(i => i.GameDir() == textBoxClonePath.Text);
- GameInstance instanceToClone = new GameInstance(
- sourceInstance.game,
- textBoxClonePath.Text,
+ IGame sourceGame = manager.DetermineGame(new DirectoryInfo(existingPath), user);
+ if (sourceGame == null)
+ {
+ // User cancelled, let them try again
+ reactivateDialog();
+ return;
+ }
+ instanceToClone = new GameInstance(
+ sourceGame,
+ existingPath,
"irrelevant",
user
);
-
+ }
+ await Task.Run(() =>
+ {
if (instanceToClone.Valid)
{
manager.CloneInstance(instanceToClone, newName, newPath);
@@ -173,13 +183,13 @@ await Task.Run(() =>
}
catch (NotKSPDirKraken kraken)
{
- user.RaiseError(string.Format(Properties.Resources.CloneFakeKspDialogInstanceNotValid, kraken.path));
+ user.RaiseError(string.Format(Properties.Resources.CloneFakeKspDialogInstanceNotValid, kraken.path.Replace('/', Path.DirectorySeparatorChar)));
reactivateDialog();
return;
}
catch (PathErrorKraken kraken)
{
- user.RaiseError(string.Format(Properties.Resources.CloneFakeKspDialogDestinationNotEmpty, kraken.path));
+ user.RaiseError(string.Format(Properties.Resources.CloneFakeKspDialogDestinationNotEmpty, kraken.path.Replace('/', Path.DirectorySeparatorChar)));
reactivateDialog();
return;
}
diff --git a/GUI/Dialogs/SelectionDialog.cs b/GUI/Dialogs/SelectionDialog.cs
index 2123aaabb6..1a8f04278b 100644
--- a/GUI/Dialogs/SelectionDialog.cs
+++ b/GUI/Dialogs/SelectionDialog.cs
@@ -80,7 +80,7 @@ public int ShowSelectionDialog (string message, params object[] args)
if (defaultSelection == i)
{
Util.Invoke(OptionsList, () => OptionsList.Items.Add(String.Concat(args[i].ToString(), " -- Default")));
-
+
}
else
{
diff --git a/GUI/Main/Main.cs b/GUI/Main/Main.cs
index ab00d401ca..5a5a7645ef 100644
--- a/GUI/Main/Main.cs
+++ b/GUI/Main/Main.cs
@@ -83,7 +83,16 @@ public Main(string[] cmdlineArgs, GameInstanceManager mgr, bool showConsole)
Instance = this;
currentUser = new GUIUser(this, this.Wait);
- manager = mgr ?? new GameInstanceManager(currentUser);
+ if (mgr != null)
+ {
+ // With a working GUI, assign a GUIUser to the GameInstanceManager to replace the ConsoleUser
+ mgr.User = currentUser;
+ manager = mgr;
+ }
+ else
+ {
+ manager = new GameInstanceManager(currentUser);
+ }
controlFactory = new ControlFactory();