fix deadlock with singleton NexusAPI client. We can look into this in the future, but it's still causing problems so removing the singleton for now

This commit is contained in:
Timothy Baldridge 2019-09-28 18:18:42 -06:00
parent 43b227a865
commit c9d12c7904
6 changed files with 72 additions and 79 deletions

View File

@ -131,6 +131,7 @@ namespace VFS
{ {
} }
_isDirty = false;
CleanDB(); CleanDB();
} }
@ -257,6 +258,7 @@ namespace VFS
_isDirty = true; _isDirty = true;
_files.Remove(f.FullPath); _files.Remove(f.FullPath);
}); });
SyncToDisk();
} }
} }
@ -335,8 +337,12 @@ namespace VFS
lv.Analyze(); lv.Analyze();
Add(lv); Add(lv);
if (lv.IsArchive) UpdateArchive(lv); if (lv.IsArchive)
// Upsert after extraction incase extraction fails {
UpdateArchive(lv);
// Upsert after extraction incase extraction fails
lv.FinishedIndexing = true;
}
} }
if (lv.IsOutdated) if (lv.IsOutdated)

View File

@ -91,6 +91,7 @@
<Compile Include="Consts.cs" /> <Compile Include="Consts.cs" />
<Compile Include="DynamicIniData.cs" /> <Compile Include="DynamicIniData.cs" />
<Compile Include="FileExtractor.cs" /> <Compile Include="FileExtractor.cs" />
<Compile Include="GameMetaData.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SplittingStream.cs" /> <Compile Include="SplittingStream.cs" />
<Compile Include="Utils.cs" /> <Compile Include="Utils.cs" />

View File

@ -74,17 +74,6 @@ namespace Wabbajack
public ConcurrentBag<Directive> ExtraFiles { get; private set; } public ConcurrentBag<Directive> ExtraFiles { get; private set; }
public Dictionary<string, dynamic> ModInis { get; private set; } public Dictionary<string, dynamic> ModInis { get; private set; }
private NexusApiClient _nexusApiClient;
private NexusApiClient NexusApiClient {
get
{
if (_nexusApiClient == null)
_nexusApiClient = new NexusApiClient();
return _nexusApiClient;
}
}
public VirtualFileSystem VFS => VirtualFileSystem.VFS; public VirtualFileSystem VFS => VirtualFileSystem.VFS;
public List<IndexedArchive> IndexedArchives { get; private set; } public List<IndexedArchive> IndexedArchives { get; private set; }
@ -238,7 +227,8 @@ namespace Wabbajack
if (IndexedArchives.Any(a => a.IniData?.General?.gameName != null)) if (IndexedArchives.Any(a => a.IniData?.General?.gameName != null))
{ {
var nexusClient = new NexusApiClient(); var nexusClient = new NexusApiClient();
if (!nexusClient.IsPremium) Info($"User {nexusClient.Username} is not a premium Nexus user, cannot continue"); var status = nexusClient.GetUserStatus();
if (!status.is_premium) Info($"User {status.user_id} is not a premium Nexus user, cannot continue");
} }
@ -493,7 +483,7 @@ namespace Wabbajack
ModID = general.modID, ModID = general.modID,
Version = general.version ?? "0.0.0.0" Version = general.version ?? "0.0.0.0"
}; };
var info = NexusApiClient.GetModInfo(nm); var info = new NexusApiClient().GetModInfo(nm);
nm.Author = info.author; nm.Author = info.author;
nm.UploadedBy = info.uploaded_by; nm.UploadedBy = info.uploaded_by;
nm.UploaderProfile = info.uploaded_users_profile_url; nm.UploaderProfile = info.uploaded_users_profile_url;
@ -528,7 +518,6 @@ namespace Wabbajack
Info($"Checking link for {found.Name}"); Info($"Checking link for {found.Name}");
var installer = new Installer(null, ""); var installer = new Installer(null, "");
installer.NexusClient = NexusApiClient;
if (!installer.DownloadArchive(result, false)) if (!installer.DownloadArchive(result, false))
Error( Error(

View File

@ -25,20 +25,6 @@ namespace Wabbajack
public class Installer public class Installer
{ {
private string _downloadsFolder; private string _downloadsFolder;
private NexusApiClient _nexusClient;
public NexusApiClient NexusClient
{
get
{
if (_nexusClient == null)
_nexusClient = new NexusApiClient();
return _nexusClient;
}
set => _nexusClient = value;
}
public string NexusAPIKey { get; set; }
public Installer(ModList mod_list, string output_folder) public Installer(ModList mod_list, string output_folder)
{ {
@ -173,7 +159,7 @@ namespace Wabbajack
mods.PMap(mod => mods.PMap(mod =>
{ {
var er = NexusClient.EndorseMod(mod); var er = new NexusApiClient().EndorseMod(mod);
Utils.Log($"Endorsed {mod.GameName} - {mod.ModID} - Result: {er.message}"); Utils.Log($"Endorsed {mod.GameName} - {mod.ModID} - Result: {er.message}");
}); });
Info("Done! You may now exit the application!"); Info("Done! You may now exit the application!");
@ -416,19 +402,19 @@ namespace Wabbajack
Info("Getting Nexus API Key, if a browser appears, please accept"); Info("Getting Nexus API Key, if a browser appears, please accept");
if (ModList.Archives.OfType<NexusMod>().Any()) if (ModList.Archives.OfType<NexusMod>().Any())
{ {
NexusClient = new NexusApiClient(); var client = new NexusApiClient();
var status = client.GetUserStatus();
if (!NexusClient.IsAuthenticated) if (!client.IsAuthenticated)
{ {
Error( Error(
$"Authenticating for the Nexus failed. A nexus account is required to automatically download mods."); $"Authenticating for the Nexus failed. A nexus account is required to automatically download mods.");
return; return;
} }
if (!NexusClient.IsPremium) if (!status.is_premium)
{ {
Error( Error(
$"Automated installs with Wabbajack requires a premium nexus account. {NexusClient.Username} is not a premium account."); $"Automated installs with Wabbajack requires a premium nexus account. {client.Username} is not a premium account.");
return; return;
} }
} }
@ -461,7 +447,7 @@ namespace Wabbajack
string url; string url;
try try
{ {
url = NexusClient.GetNexusDownloadLink(a, !download); url = new NexusApiClient().GetNexusDownloadLink(a, !download);
if (!download) return true; if (!download) return true;
} }
catch (Exception ex) catch (Exception ex)

View File

@ -34,48 +34,62 @@ namespace Wabbajack.NexusApi
private UserStatus _userStatus; private UserStatus _userStatus;
public bool IsPremium => IsAuthenticated && _userStatus.is_premium; public UserStatus UserStatus
public string Username => _userStatus?.name;
private static string GetApiKey()
{ {
// check if there exists a cached api key get
var fi = new FileInfo(API_KEY_CACHE_FILE);
if (fi.Exists && fi.LastWriteTime > DateTime.Now.AddHours(-72))
{ {
return File.ReadAllText(API_KEY_CACHE_FILE); if (_userStatus == null)
_userStatus = GetUserStatus();
return _userStatus;
} }
// open a web socket to receive the api key
var guid = Guid.NewGuid();
var _websocket = new WebSocket("wss://sso.nexusmods.com")
{
SslConfiguration =
{
EnabledSslProtocols = SslProtocols.Tls12
}
};
var api_key = new TaskCompletionSource<string>();
_websocket.OnMessage += (sender, msg) => { api_key.SetResult(msg.Data); };
_websocket.Connect();
_websocket.Send("{\"id\": \"" + guid + "\", \"appid\": \"" + Consts.AppName + "\"}");
// open a web browser to get user permission
Process.Start($"https://www.nexusmods.com/sso?id={guid}&application=" + Consts.AppName);
// get the api key from the socket and cache it
api_key.Task.Wait();
var result = api_key.Task.Result;
File.WriteAllText(API_KEY_CACHE_FILE, result);
return result;
} }
private UserStatus GetUserStatus() public bool IsPremium => IsAuthenticated && UserStatus.is_premium;
public string Username => UserStatus?.name;
private static object _getAPIKeyLock = new object();
private static string GetApiKey()
{
lock (_getAPIKeyLock)
{
// check if there exists a cached api key
var fi = new FileInfo(API_KEY_CACHE_FILE);
if (fi.Exists && fi.LastWriteTime > DateTime.Now.AddHours(-72))
{
return File.ReadAllText(API_KEY_CACHE_FILE);
}
// open a web socket to receive the api key
var guid = Guid.NewGuid();
var _websocket = new WebSocket("wss://sso.nexusmods.com")
{
SslConfiguration =
{
EnabledSslProtocols = SslProtocols.Tls12
}
};
var api_key = new TaskCompletionSource<string>();
_websocket.OnMessage += (sender, msg) => { api_key.SetResult(msg.Data); };
_websocket.Connect();
_websocket.Send("{\"id\": \"" + guid + "\", \"appid\": \"" + Consts.AppName + "\"}");
// open a web browser to get user permission
Process.Start($"https://www.nexusmods.com/sso?id={guid}&application=" + Consts.AppName);
// get the api key from the socket and cache it
api_key.Task.Wait();
var result = api_key.Task.Result;
File.WriteAllText(API_KEY_CACHE_FILE, result);
return result;
}
}
public UserStatus GetUserStatus()
{ {
var url = "https://api.nexusmods.com/v1/users/validate.json"; var url = "https://api.nexusmods.com/v1/users/validate.json";
return Get<UserStatus>(url); return Get<UserStatus>(url);
@ -142,8 +156,6 @@ namespace Wabbajack.NexusApi
headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
headers.Add("Application-Name", Consts.AppName); headers.Add("Application-Name", Consts.AppName);
headers.Add("Application-Version", $"{Assembly.GetEntryAssembly().GetName().Version}"); headers.Add("Application-Version", $"{Assembly.GetEntryAssembly().GetName().Version}");
_userStatus = GetUserStatus();
} }
private T Get<T>(string url) private T Get<T>(string url)

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Wabbajack.Common;
namespace Wabbajack.NexusApi namespace Wabbajack.NexusApi
{ {
@ -10,9 +11,7 @@ namespace Wabbajack.NexusApi
{ {
public static string ConvertGameName(string gameName) public static string ConvertGameName(string gameName)
{ {
if (gameName == "SkyrimSE") return "skyrimspecialedition"; return GameRegistry.GetByMO2ArchiveName(gameName)?.NexusName ?? gameName.ToLower();
if (gameName == "FalloutNV") return "newvegas";
return gameName.ToLower();
} }
public static string GetModURL(string argGameName, string argModId) public static string GetModURL(string argGameName, string argModId)