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

Add frontier url check to update check. Make update checks async. #98

Closed
wants to merge 6 commits into from
Closed
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
27 changes: 24 additions & 3 deletions src/XIVLauncher.Core/Components/MainPage/MainPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,34 @@ private void ProcessLogin(LoginAction action)
if (this.IsLoggingIn)
return;

this.App.StartLoading("Logging in...", canDisableAutoLogin: true);
this.App.StartLoading(!(App.IsLauncherSetup && App.IsUpdateCheckComplete) ? "Checking for updates..." : "Logging In...", canDisableAutoLogin: true);

// if (Program.UsesFallbackSteamAppId && this.loginFrame.IsSteam)
// throw new Exception("Doesn't own Steam AppId on this account.");

Task.Run(async () =>
{
var startTime = DateTime.UtcNow;
var updateCheckCompletePreLoop = App.IsUpdateCheckComplete;
while (!(App.IsLauncherSetup && App.IsUpdateCheckComplete))
{
if (DateTime.UtcNow - startTime > TimeSpan.FromSeconds(16))
{
App.ShowMessageBlocking(
"The update checks timed out and did not properly return fallback values.\nTry logging in again. If that doesn't work, restart XIVLauncher.",
"Timeout Error");

Reactivate();
return;
}
}

if (UpdateCheck.IsUpdateAvailable && !updateCheckCompletePreLoop)
{
Reactivate(LauncherApp.LauncherState.UpdateWarn);
return;
}

if (GameHelpers.CheckIsGameOpen() && action == LoginAction.Repair)
{
App.ShowMessageBlocking("The game and/or the official launcher are open. XIVLauncher cannot repair the game if this is the case.\nPlease close them and try again.", "XIVLauncher");
Expand Down Expand Up @@ -1241,10 +1262,10 @@ private void Hide()
Program.HideWindow();
}

private void Reactivate()
private void Reactivate(LauncherApp.LauncherState state = LauncherApp.LauncherState.Main)
{
IsLoggingIn = false;
this.App.State = LauncherApp.LauncherState.Main;
this.App.State = state;

Program.ShowWindow();
}
Expand Down
2 changes: 2 additions & 0 deletions src/XIVLauncher.Core/Components/MainPage/NewsFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public void ReloadNews()
{
this.newsLoaded = false;

if (!this.app.IsLauncherSetup) return;

await Headlines.GetWorlds(this.app.Launcher, this.app.Settings.ClientLanguage ?? ClientLanguage.English);

bannerList = await Headlines.GetBanners(this.app.Launcher, this.app.Settings.ClientLanguage ?? ClientLanguage.English).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class SettingsTabGame : SettingsTab
new SettingsEntry<bool>("Use XIVLauncher authenticator/OTP macros", "Check this if you want to use the XIVLauncher authenticator app or macros.", () => Program.Config.IsOtpServer ?? false, x => Program.Config.IsOtpServer = x),
new SettingsEntry<bool>("Ignore Steam", "Check this if you do not want XIVLauncher to communicate with Steam (Requires Restart).", () => Program.Config.IsIgnoringSteam ?? false, x => Program.Config.IsIgnoringSteam = x),
new SettingsEntry<bool>("Use Experimental UID Cache", "Tries to save your login token for the next start. Can result in launching with expired sessions.\nDisable if receiving FFXIV error 1012 or 500X.", () => Program.Config.IsUidCacheEnabled ?? false, x => Program.Config.IsUidCacheEnabled = x),
new SettingsEntry<bool>("Skip XIVLauncher version check", "Don't check for new versions of XIVLauncher. You won't be notified of new updates.\nResults in faster autologin, but may eventually fail to launch. (Requires restart)", () => !(Program.Config.DoVersionCheck ?? true), b => Program.Config.DoVersionCheck = !b),
};

public override string Title => "Game";
Expand Down
31 changes: 22 additions & 9 deletions src/XIVLauncher.Core/LauncherApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public class LauncherApp : Component
public static bool IsDebug { get; private set; } = Debugger.IsAttached;
private bool isDemoWindow = false;

public bool IsLauncherSetup { get; private set; } = false;

public bool IsUpdateCheckComplete { get; private set; } = false;

#region Modal State

private bool isModalDrawing = false;
Expand Down Expand Up @@ -107,7 +111,7 @@ public LauncherState State
};

public ILauncherConfig Settings => Program.Config;
public Launcher Launcher { get; private set; }
public Launcher? Launcher { get; private set; }
public ISteam? Steam => Program.Steam;
public Storage Storage { get; private set; }

Expand All @@ -125,13 +129,12 @@ public LauncherState State

private readonly Background background = new();

public LauncherApp(Storage storage, bool needsUpdateWarning, string frontierUrl)
public LauncherApp(Storage storage)
{
this.Storage = storage;

this.Accounts = new AccountManager(this.Storage.GetFile("accounts.json"));
this.UniqueIdCache = new CommonUniqueIdCache(this.Storage.GetFile("uidCache.json"));
this.Launcher = new Launcher(Program.Steam, UniqueIdCache, Program.CommonSettings, frontierUrl);

this.mainPage = new MainPage(this);
this.setPage = new SettingsPage(this);
Expand All @@ -141,14 +144,24 @@ public LauncherApp(Storage storage, bool needsUpdateWarning, string frontierUrl)
this.updateWarnPage = new UpdateWarnPage(this);
this.steamDeckPromptPage = new SteamDeckPromptPage(this);

if (needsUpdateWarning)
Task.Run(async () =>
{
this.State = LauncherState.UpdateWarn;
}
else
var versionCheckResult = await UpdateCheck.CheckForUpdate(Program.Config.DoVersionCheck ?? true);
if (versionCheckResult.Success)
if (versionCheckResult.NeedUpdate)
this.State = LauncherState.UpdateWarn;
this.IsUpdateCheckComplete = true;
});

Task.Run(async () =>
{
this.RunStartupTasks();
}
var frontierUrl = await UpdateCheck.GetFrontierUrl(Program.Config.DoVersionCheck ?? true);
this.Launcher = new Launcher(Program.Steam, UniqueIdCache, Program.CommonSettings, frontierUrl);
this.IsLauncherSetup = true;
this.mainPage.ReloadNews();
});

this.RunStartupTasks();

#if DEBUG
IsDebug = true;
Expand Down
18 changes: 1 addition & 17 deletions src/XIVLauncher.Core/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ class Program
private static uint invalidationFrames = 0;
private static Vector2 lastMousePosition;

private const string FRONTIER_FALLBACK = "https://launcher.finalfantasyxiv.com/v650/index.html?rc_lang={0}&time={1}";

public static string CType = CoreEnvironmentSettings.GetCType();

public static void Invalidate(uint frames = 100)
Expand Down Expand Up @@ -278,21 +276,7 @@ private static void Main(string[] args)
StyleModelV1.DalamudStandard.Apply();
ImGui.GetIO().FontGlobalScale = Config.GlobalScale ?? 1.0f;

var needUpdate = false;

#if FLATPAK
if (Config.DoVersionCheck ?? false)
{
var versionCheckResult = UpdateCheck.CheckForUpdate().GetAwaiter().GetResult();

if (versionCheckResult.Success)
needUpdate = versionCheckResult.NeedUpdate;
}
#endif

needUpdate = CoreEnvironmentSettings.IsUpgrade ? true : needUpdate;

launcherApp = new LauncherApp(storage, needUpdate, FRONTIER_FALLBACK);
launcherApp = new LauncherApp(storage);

Invalidate(20);

Expand Down
121 changes: 119 additions & 2 deletions src/XIVLauncher.Core/UpdateCheck.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,47 @@
using System.Net.Http.Headers;
using Newtonsoft.Json;
using Serilog;
using XIVLauncher.Common.Util;

namespace XIVLauncher.Core;

public static class UpdateCheck
{
private const string UPDATE_URL = "https://raw.githubusercontent.com/goatcorp/xlcore-distrib/main/version.txt";

public static async Task<VersionCheckResult> CheckForUpdate()
private const string LEASE_META_URL = "https://kamori.goats.dev/Launcher/GetLease";

private const string FRONTIER_FALLBACK_URL = "https://launcher.finalfantasyxiv.com/v650/index.html?rc_lang={0}&time={1}";

public static bool IsUpdateCheckComplete { get; private set; } = false;

public static bool IsUpdateAvailable { get; private set; } = false;

public static async Task<VersionCheckResult> CheckForUpdate(bool doVersionCheck = true)
{
if (!doVersionCheck)
{
Log.Information("DoVersionCheck set to false in launcher.ini. Skipping version check.");

return new VersionCheckResult
{
Success = false,
NeedUpdate = false,
};
}

try
{
using var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(5);
client.Timeout = TimeSpan.FromSeconds(15);

var response = await client.GetStringAsync(UPDATE_URL).ConfigureAwait(false);
var remoteVersion = Version.Parse(response);

var localVersion = Version.Parse(AppUtil.GetAssemblyVersion());

IsUpdateAvailable = remoteVersion > localVersion;

return new VersionCheckResult
{
Success = true,
Expand All @@ -42,4 +66,97 @@ public class VersionCheckResult
public bool NeedUpdate { get; init; }
public string? WantVersion { get; init; }
}

private enum LeaseFeatureFlags
{
None = 0,
GlobalDisableDalamud = 1,
GlobalDisableLogin = 1 << 1,
}

#pragma warning disable CS8618
private class Lease
{
public bool Success { get; set; }

public string? Message { get; set; }

public string? CutOffBootver { get; set; }

public string FrontierUrl { get; set; }

public LeaseFeatureFlags Flags { get; set; }

public string ReleasesList { get; set; }

public DateTime? ValidUntil { get; set; }
}
#pragma warning restore CS8618

public class LeaseAcquisitionException : Exception
{
public LeaseAcquisitionException(string message)
: base($"Couldn't acquire lease: {message}")
{
}
}

public static async Task<string> GetFrontierUrl(bool doVersionCheck)
{
if (!doVersionCheck)
return FRONTIER_FALLBACK_URL;

using var client = new HttpClient
{
DefaultRequestHeaders =
{
UserAgent = { new ProductInfoHeaderValue("XIVLauncher", AppUtil.GetGitHash()) }
}
};
client.Timeout = TimeSpan.FromSeconds(15);
client.DefaultRequestHeaders.AddWithoutValidation("X-XL-Track", "Release");
client.DefaultRequestHeaders.AddWithoutValidation("X-XL-LV", "0");
client.DefaultRequestHeaders.AddWithoutValidation("X-XL-HaveVersion", AppUtil.GetAssemblyVersion());
client.DefaultRequestHeaders.AddWithoutValidation("X-XL-HaveAddon", "no");
client.DefaultRequestHeaders.AddWithoutValidation("X-XL-FirstStart", "no");
client.DefaultRequestHeaders.AddWithoutValidation("X-XL-HaveWine", "no");

var result = FRONTIER_FALLBACK_URL;

HttpResponseMessage? response;
try
{
response = await client.GetAsync(LEASE_META_URL).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Headers.TryGetValues("X-XL-Canary", out var values) &&
values.FirstOrDefault() == "yes")
{
Log.Information("Updates: Received canary track lease!");
}

var leaseData = JsonConvert.DeserializeObject<Lease>(await response.Content.ReadAsStringAsync().ConfigureAwait(false));

if (!leaseData.Success)
throw new LeaseAcquisitionException(leaseData.Message!);
result = leaseData.FrontierUrl;
}
catch (TaskCanceledException tcex)
{
Log.Error(tcex, "Could not get frontier url from server. Request timed out.");
}
catch (LeaseAcquisitionException leex)
{
Log.Error(leex, leex.Message);
}
catch (HttpRequestException hrex)
{
Log.Error(hrex, "Could not get frontier url from server. Could not contact kamori.goats.dev.");
}
catch (JsonSerializationException jsex)
{
Log.Error(jsex, "Could not get frontier url from server. The server returned invalid data.");
}
return result;
}

}
Loading