-
-
Notifications
You must be signed in to change notification settings - Fork 346
/
Install.cs
288 lines (262 loc) · 11.8 KB
/
Install.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
using System;
using System.IO;
using log4net;
namespace CKAN.CmdLine
{
public class Install : ICommand
{
private static readonly ILog log = LogManager.GetLogger(typeof(Install));
public IUser user { get; set; }
private KSPManager manager;
/// <summary>
/// Initialize the install command object
/// </summary>
/// <param name="mgr">KSPManager containing our instances</param>
/// <param name="user">IUser object for interaction</param>
public Install(KSPManager mgr, IUser user)
{
manager = mgr;
this.user = user;
}
/// <summary>
/// Installs a module, if available
/// </summary>
/// <param name="ksp">Game instance into which to install</param>
/// <param name="raw_options">Command line options object</param>
/// <returns>
/// Exit code for shell environment
/// </returns>
public int RunCommand(CKAN.KSP ksp, object raw_options)
{
InstallOptions options = (InstallOptions) raw_options;
if (options.ckan_files != null)
{
// Oooh! We're installing from a CKAN file.
foreach (string ckan_file in options.ckan_files)
{
Uri ckan_uri;
// Check if the argument if a wellformatted Uri.
if (!Uri.IsWellFormedUriString(ckan_file, UriKind.Absolute))
{
// Assume it is a local file, check if the file exists.
if (File.Exists(ckan_file))
{
// Get the full path of the file.
ckan_uri = new Uri(Path.GetFullPath(ckan_file));
}
else
{
// We have no further ideas as what we can do with this Uri, tell the user.
user.RaiseError("Can not find file \"{0}\".", ckan_file);
user.RaiseError("Exiting.");
return Exit.ERROR;
}
}
else
{
ckan_uri = new Uri(ckan_file);
}
string filename = String.Empty;
// If it is a local file, we already know the filename. If it is remote, create a temporary file and download the remote resource.
if (ckan_uri.IsFile)
{
filename = ckan_uri.LocalPath;
log.InfoFormat("Installing from local CKAN file \"{0}\"", filename);
}
else
{
log.InfoFormat("Installing from remote CKAN file \"{0}\"", ckan_uri);
filename = Net.Download(ckan_uri, null, user);
log.DebugFormat("Temporary file for \"{0}\" is at \"{1}\".", ckan_uri, filename);
}
// Parse the JSON file.
try
{
CkanModule m = MainClass.LoadCkanFromFile(ksp, filename);
options.modules.Add($"{m.identifier}={m.version}");
}
catch (Kraken kraken)
{
user.RaiseError(kraken.InnerException == null
? kraken.Message
: $"{kraken.Message}: {kraken.InnerException.Message}");
}
}
// At times RunCommand() calls itself recursively - in this case we do
// not want to be doing this again, so "consume" the option
options.ckan_files = null;
}
else
{
Search.AdjustModulesCase(ksp, options.modules);
}
if (options.modules.Count == 0)
{
// What? No files specified?
user.RaiseMessage(
"Usage: ckan install [--with-suggests] [--with-all-suggests] [--no-recommends] [--headless] Mod [Mod2, ...]");
return Exit.BADOPT;
}
// Prepare options. Can these all be done in the new() somehow?
var install_ops = new RelationshipResolverOptions
{
with_all_suggests = options.with_all_suggests,
with_suggests = options.with_suggests,
with_recommends = !options.no_recommends,
allow_incompatible = options.allow_incompatible
};
if (user.Headless)
{
install_ops.without_toomanyprovides_kraken = true;
install_ops.without_enforce_consistency = true;
}
// Install everything requested. :)
try
{
var installer = ModuleInstaller.GetInstance(ksp, manager.Cache, user);
installer.InstallList(options.modules, install_ops);
user.RaiseMessage("\r\n");
}
catch (DependencyNotSatisfiedKraken ex)
{
if (ex.version == null)
{
user.RaiseMessage("{0} requires {1} but it is not listed in the index, or not available for your version of KSP.", ex.parent, ex.module);
}
else
{
user.RaiseMessage("{0} requires {1} {2} but it is not listed in the index, or not available for your version of KSP.", ex.parent, ex.module, ex.version);
}
user.RaiseMessage("If you're lucky, you can do a `ckan update` and try again.");
user.RaiseMessage("Try `ckan install --no-recommends` to skip installation of recommended modules.");
user.RaiseMessage("Or `ckan install --allow-incompatible` to ignore module compatibility.");
return Exit.ERROR;
}
catch (ModuleNotFoundKraken ex)
{
if (ex.version == null)
{
user.RaiseMessage("Module {0} required but it is not listed in the index, or not available for your version of KSP.", ex.module);
}
else
{
user.RaiseMessage("Module {0} {1} required but it is not listed in the index, or not available for your version of KSP.", ex.module, ex.version);
}
user.RaiseMessage("If you're lucky, you can do a `ckan update` and try again.");
user.RaiseMessage("Try `ckan install --no-recommends` to skip installation of recommended modules.");
user.RaiseMessage("Or `ckan install --allow-incompatible` to ignore module compatibility.");
return Exit.ERROR;
}
catch (BadMetadataKraken ex)
{
user.RaiseMessage("Bad metadata detected for module {0}.", ex.module);
user.RaiseMessage(ex.Message);
return Exit.ERROR;
}
catch (TooManyModsProvideKraken ex)
{
// Request the user selects one of the mods.
string[] mods = new string[ex.modules.Count];
for (int i = 0; i < ex.modules.Count; i++)
{
mods[i] = String.Format("{0} ({1})", ex.modules[i].identifier, ex.modules[i].name);
}
string message = String.Format("Too many mods provide {0}. Please pick from the following:\r\n", ex.requested);
int result;
try
{
result = user.RaiseSelectionDialog(message, mods);
}
catch (Kraken e)
{
user.RaiseMessage(e.Message);
return Exit.ERROR;
}
if (result < 0)
{
user.RaiseMessage(String.Empty); // Looks tidier.
return Exit.ERROR;
}
// Add the module to the list.
options.modules.Add(ex.modules[result].identifier);
return (new Install(manager, user).RunCommand(ksp, options));
}
catch (FileExistsKraken ex)
{
if (ex.owningModule != null)
{
user.RaiseMessage(
"\r\nOh no! We tried to overwrite a file owned by another mod!\r\n"+
"Please try a `ckan update` and try again.\r\n\r\n"+
"If this problem re-occurs, then it maybe a packaging bug.\r\n"+
"Please report it at:\r\n\r\n" +
"https://github.com/KSP-CKAN/NetKAN/issues/new\r\n\r\n" +
"Please including the following information in your report:\r\n\r\n" +
"File : {0}\r\n" +
"Installing Mod : {1}\r\n" +
"Owning Mod : {2}\r\n" +
"CKAN Version : {3}\r\n",
ex.filename, ex.installingModule, ex.owningModule,
Meta.GetVersion(VersionFormat.Full)
);
}
else
{
user.RaiseMessage(
"\r\n\r\nOh no!\r\n\r\n"+
"It looks like you're trying to install a mod which is already installed,\r\n"+
"or which conflicts with another mod which is already installed.\r\n\r\n"+
"As a safety feature, the CKAN will *never* overwrite or alter a file\r\n"+
"that it did not install itself.\r\n\r\n"+
"If you wish to install {0} via the CKAN,\r\n"+
"then please manually uninstall the mod which owns:\r\n\r\n"+
"{1}\r\n\r\n"+"and try again.\r\n",
ex.installingModule, ex.filename
);
}
user.RaiseMessage("Your GameData has been returned to its original state.\r\n");
return Exit.ERROR;
}
catch (InconsistentKraken ex)
{
// The prettiest Kraken formats itself for us.
user.RaiseMessage(ex.InconsistenciesPretty);
user.RaiseMessage("Install canceled. Your files have been returned to their initial state.");
return Exit.ERROR;
}
catch (CancelledActionKraken)
{
user.RaiseMessage("Installation canceled at user request.");
return Exit.ERROR;
}
catch (MissingCertificateKraken kraken)
{
// Another very pretty kraken.
user.RaiseMessage(kraken.ToString());
return Exit.ERROR;
}
catch (DownloadThrottledKraken kraken)
{
user.RaiseMessage(kraken.ToString());
user.RaiseMessage($"Try the authtoken command. See {kraken.infoUrl} for details.");
return Exit.ERROR;
}
catch (DownloadErrorsKraken)
{
user.RaiseMessage("One or more files failed to download, stopped.");
return Exit.ERROR;
}
catch (ModuleDownloadErrorsKraken kraken)
{
user.RaiseMessage(kraken.ToString());
return Exit.ERROR;
}
catch (DirectoryNotFoundKraken kraken)
{
user.RaiseMessage("\r\n{0}", kraken.Message);
return Exit.ERROR;
}
return Exit.OK;
}
}
}