bunch of bug fixes with mods that get updated, prepping for Alpha

This commit is contained in:
Timothy Baldridge 2019-08-02 16:31:13 -06:00
parent dbd1adb719
commit 81a51265b0
8 changed files with 169 additions and 47 deletions

View File

@ -69,12 +69,41 @@ Wabbajack can currently install from many different file hosting sources. Curren
### FAQ ### FAQ
**Why does each modpack install another copy of Mod Organizer 2** **Why does each modpack install another copy of Mod Organizer 2?**
Self-contained folders are a cleaner abstraction than dumping tons of modlists into the same set of folders. It's easy to uninstall a modlist (simply delete the folder), Self-contained folders are a cleaner abstraction than dumping tons of modlists into the same set of folders. It's easy to uninstall a modlist (simply delete the folder),
and MO2 really isn't designed to support lots of disparate modlists. For example if two modlists both wanted a given texture mod, but different options they would and MO2 really isn't designed to support lots of disparate modlists. For example if two modlists both wanted a given texture mod, but different options they would
somehow have to keep the names of their mods separate. MO2 isn't that big of an app, so there's really no reason not to install a new copy for each modlist. somehow have to keep the names of their mods separate. MO2 isn't that big of an app, so there's really no reason not to install a new copy for each modlist.
**Why don't I see any mods when I open Mod Organizer 2 after install?**
Make sure you selected the "Portable" mode when starting MO2 for the first time. In addition, make sure you haven't installed MO2 in a non-portable way on the same box.
Really, always use "Portable Mode" it's cleaner and there really isn't a reason not too do so. Make the data self-contained. It's cleaner that way.
**Will Wabbajack ever support Vortex/other mod managers?**
I'll be honest, I don't use anything but MO2, so I probably won't write the code. If someone were to write a patch for the functionality
I wouldn't throw away the code, but it would have to be done in a way that was relatively seamless for users. Since Wabbajack treats all files in the same way
it doesn't know what mod manager a user is using. This means that if the modlist creator used Vortex all users of the modlist would have to use Vortex. This doesn't seem
optimal. It's possible perhaps, but it's at the bottom of the priority list.
**Where is the modlist? Why am I just given an .exe?**
When Wabbajack creates a modlist, as a final step it copies itself (the wabbajack.exe) and tacks onto the end of the file the modlist data, and a few bits
of magic text. When Wabbajack starts it looks at itself to see if it has this extra data tacked on to the end of the executable. If the data is found the app kicks
into installation mode. This means that Wabbajack acts a lot like a self-extracting installer.
**Do you know that some mod authors don't like their mods being automatically installed?**
Yes, I've heard this, but they chose to host their data on a public site. And no, they don't have the right to dictate what HTTP client is used to download a file.
We're using official Nexus APIs for nexus downloads, so any downloads Wabbajack performs are correctly tracked, and MO2 encourages users to endorse mods. It's 2019, we can
have better tools than manually clicking links.
**How does Wabbajack differ from Automaton?**
I (halgari) used to be a developer working on Automaton. Sadly development was moving a bit too slowly for my liking, and I realized that a complete rewrite would allow the
implementation of some really nice features (like BSA packing). As such I made the decision to strike out on my own and make an app that worked first, and then make it pretty.
The end result is an app with a ton of features, and a less than professional UI. But that's my motto when coding "make it work, then make it pretty".
### License & Copyright ### License & Copyright

View File

@ -26,6 +26,6 @@ namespace Wabbajack.Common
} }
} }
public static String AppName = "Vortex"; public static String AppName = "Wabbajack";
} }
} }

View File

@ -1,4 +1,5 @@
using System; using Newtonsoft.Json;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -153,6 +154,9 @@ namespace Wabbajack.Common
public class DirectURLArchive : Archive public class DirectURLArchive : Archive
{ {
public string URL; public string URL;
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public List<string> Headers;
} }
/// <summary> /// <summary>
@ -177,6 +181,13 @@ namespace Wabbajack.Common
{ {
} }
/// <summary>
/// Archive that comes from MediaFire
/// </summary>
public class MediaFireArchive : DirectURLArchive
{
}
/// <summary> /// <summary>
/// The indexed contents of an archive /// The indexed contents of an archive
/// </summary> /// </summary>

View File

@ -237,10 +237,14 @@ namespace Wabbajack
{ {
installer.Install(); installer.Install();
} }
catch (AggregateException ex)
{
LogMsg(ex.StackTrace);
LogMsg(ex.InnerException.ToString());
}
catch (Exception ex) catch (Exception ex)
{ {
LogMsg(ex.ToString()); LogMsg($"{ex.Message} - Can't continue");
LogMsg(ex.StackTrace);
} }
}); });
th.Priority = ThreadPriority.BelowNormal; th.Priority = ThreadPriority.BelowNormal;

View File

@ -179,7 +179,10 @@ namespace Wabbajack
foreach (var file in nomatch) foreach (var file in nomatch)
Info(" {0}", file.To); Info(" {0}", file.To);
if (nomatch.Count() > 0) if (nomatch.Count() > 0)
Error("Exiting due to no way to compile these files"); {
Info("Exiting due to no way to compile these files");
return;
}
InstallDirectives = results.Where(i => !(i is IgnoredDirectly)).ToList(); InstallDirectives = results.Where(i => !(i is IgnoredDirectly)).ToList();
@ -372,12 +375,28 @@ namespace Wabbajack
URL = general.directURL URL = general.directURL
}; };
} }
else if (general.directURL != null && general.directURL.StartsWith("http://www.mediafire.com/file/"))
{
Error("Mediafire links are not currently supported");
return null;
/*result = new MediaFireArchive()
{
URL = general.directURL
};*/
}
else if (general.directURL != null) else if (general.directURL != null)
{ {
result = new DirectURLArchive()
var tmp = new DirectURLArchive()
{ {
URL = general.directURL URL = general.directURL
}; };
if (general.directURLHeaders != null)
{
tmp.Headers = new List<string>();
tmp.Headers.AddRange(general.directURLHeaders.Split('|'));
}
result = tmp;
} }
else else
{ {
@ -686,6 +705,7 @@ namespace Wabbajack
return source => { return source => {
var result = source.EvolveTo<NoMatch>(); var result = source.EvolveTo<NoMatch>();
result.Reason = "No Match in Stack"; result.Reason = "No Match in Stack";
Info($"No match for: {source.Path}");
return result; return result;
}; };
} }
@ -698,14 +718,31 @@ namespace Wabbajack
.GroupBy(e => e.entry.Hash) .GroupBy(e => e.entry.Hash)
.ToDictionary(e => e.Key); .ToDictionary(e => e.Key);
var mod_inis = Directory.EnumerateDirectories(Path.Combine(MO2Folder, "mods"))
.Select(f =>
{
var mod_name = Path.GetFileName(f);
var meta_path = Path.Combine(f, "meta.ini");
if (File.Exists(meta_path))
return (mod_name, meta_path.LoadIniFile());
return (null, null);
})
.Where(f => f.Item2 != null)
.ToDictionary(f => f.Item1, f => f.Item2);
return source => return source =>
{ {
if (indexed.TryGetValue(source.Hash, out var found)) if (indexed.TryGetValue(source.Hash, out var found))
{ {
var result = source.EvolveTo<FromArchive>(); var result = source.EvolveTo<FromArchive>();
var match = found.FirstOrDefault(f => Path.GetFileName(f.entry.Path) == Path.GetFileName(source.Path));
var match = found.Where(f => Path.GetFileName(f.entry.Path) == Path.GetFileName(source.Path))
.OrderByDescending(f => new FileInfo(f.archive.AbsolutePath).LastWriteTime)
.FirstOrDefault();
if (match == null) if (match == null)
match = found.First(); match = found.OrderByDescending(f => new FileInfo(f.archive.AbsolutePath).LastWriteTime)
.FirstOrDefault();
result.ArchiveHash = match.archive.Hash; result.ArchiveHash = match.archive.Hash;
result.From = match.entry.Path; result.From = match.entry.Path;

View File

@ -241,7 +241,17 @@ namespace Wabbajack
{ {
switch (archive) { switch (archive) {
case NexusMod a: case NexusMod a:
var url = NexusAPI.GetNexusDownloadLink(a as NexusMod, NexusAPIKey); Info($"Downloading Nexus Archive - {archive.Name} - {a.GameName} - {a.ModID} - {a.FileID}");
string url;
try
{
url = NexusAPI.GetNexusDownloadLink(a as NexusMod, NexusAPIKey);
}
catch (Exception ex)
{
Info($"{a.Name} - Error Getting Nexus Download URL - {ex.Message}");
return;
}
DownloadURLDirect(archive, url); DownloadURLDirect(archive, url);
break; break;
case GoogleDriveMod a: case GoogleDriveMod a:
@ -250,8 +260,11 @@ namespace Wabbajack
case MODDBArchive a: case MODDBArchive a:
DownloadModDBArchive(archive, (archive as MODDBArchive).URL); DownloadModDBArchive(archive, (archive as MODDBArchive).URL);
break; break;
case MediaFireArchive a:
DownloadMediaFireArchive(archive, a.URL);
break;
case DirectURLArchive a: case DirectURLArchive a:
DownloadURLDirect(archive, (archive as DirectURLArchive).URL); DownloadURLDirect(archive, a.URL, headers:a.Headers);
break; break;
default: default:
break; break;
@ -260,6 +273,15 @@ namespace Wabbajack
}); });
} }
private void DownloadMediaFireArchive(Archive a, string url)
{
var client = new HttpClient();
var result = client.GetStringSync(url);
var regex = new Regex("(?<= href =\\\").*\\.mediafire\\.com.*(?=\\\")");
var confirm = regex.Match(result);
DownloadURLDirect(a, confirm.ToString(), client);
}
private void DownloadGoogleDriveArchive(GoogleDriveMod a) private void DownloadGoogleDriveArchive(GoogleDriveMod a)
{ {
var initial_url = $"https://drive.google.com/uc?id={a.Id}&export=download"; var initial_url = $"https://drive.google.com/uc?id={a.Id}&export=download";
@ -279,47 +301,66 @@ namespace Wabbajack
DownloadURLDirect(archive, match.Value); DownloadURLDirect(archive, match.Value);
} }
private void DownloadURLDirect(Archive archive, string url, HttpClient client = null) private void DownloadURLDirect(Archive archive, string url, HttpClient client = null, List<string> headers = null)
{ {
if (client == null) { try
client = new HttpClient();
client.DefaultRequestHeaders.Add("User-Agent", Consts.UserAgent);
}
long total_read = 0;
int buffer_size = 1024 * 32;
var response = client.GetSync(url);
var stream = response.Content.ReadAsStreamAsync();
stream.Wait();
string header = "1";
if (response.Content.Headers.Contains("Content-Length"))
header = response.Content.Headers.GetValues("Content-Length").FirstOrDefault();
long content_size = header != null ? long.Parse(header) : 1;
var output_path = Path.Combine(DownloadFolder, archive.Name);
if (output_path.FileExists())
File.Delete(output_path);
using (var webs = stream.Result)
using (var fs = File.OpenWrite(output_path))
{ {
var buffer = new byte[buffer_size]; if (client == null)
while (true)
{ {
var read = webs.Read(buffer, 0, buffer_size); client = new HttpClient();
if (read == 0) break; client.DefaultRequestHeaders.Add("User-Agent", Consts.UserAgent);
Status((int)(total_read * 100 / content_size), "Downloading {0}", archive.Name);
fs.Write(buffer, 0, read);
total_read += read;
} }
if (headers != null) {
foreach (var header in headers)
{
var idx = header.IndexOf(':');
var k = header.Substring(0, idx);
var v = header.Substring(idx + 1);
client.DefaultRequestHeaders.Add(k, v);
}
}
long total_read = 0;
int buffer_size = 1024 * 32;
var response = client.GetSync(url);
var stream = response.Content.ReadAsStreamAsync();
stream.Wait();
string header_var = "1";
if (response.Content.Headers.Contains("Content-Length"))
header_var = response.Content.Headers.GetValues("Content-Length").FirstOrDefault();
long content_size = header_var != null ? long.Parse(header_var) : 1;
var output_path = Path.Combine(DownloadFolder, archive.Name);
if (output_path.FileExists())
File.Delete(output_path);
using (var webs = stream.Result)
using (var fs = File.OpenWrite(output_path))
{
var buffer = new byte[buffer_size];
while (true)
{
var read = webs.Read(buffer, 0, buffer_size);
if (read == 0) break;
Status((int)(total_read * 100 / content_size), "Downloading {0}", archive.Name);
fs.Write(buffer, 0, read);
total_read += read;
}
}
Status("Hashing {0}", archive.Name);
HashArchive(output_path);
}
catch (Exception ex)
{
Info($"{archive.Name} - Error downloading from: {url}");
} }
Status("Hashing {0}", archive.Name);
HashArchive(output_path);
} }
private object GetNexusAPIKey() private object GetNexusAPIKey()

BIN
logos/logo_with_text.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB