mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-21 10:01:27 -07:00
Merge branch 'main' into feature/taskbar-progressbar
This commit is contained in:
commit
89fef194a0
8
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
8
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -8,6 +8,7 @@ body:
|
||||
### **Preliminary instructions**
|
||||
- Before opening an issue, please [check the Wiki first](https://github.com/pizzaboxer/bloxstrap/wiki/) to see if your problem has been addressed there.
|
||||
- If it isn't, please confirm which pages that you read that were relevant to your issue.
|
||||
- Your issue ***will*** be closed without warning if there's a Wiki page addressing your problem.
|
||||
- If your problem is with Roblox itself (i.e. it crashes or doesn't launch), [check to see if it happens without Bloxstrap](https://github.com/pizzaboxer/bloxstrap/wiki/Roblox-crashes-or-does-not-launch).
|
||||
- Please only open an issue if your problem happens only with Bloxstrap, and state clearly that this is the case, as anything else is out of my control.
|
||||
- If you are getting a Bloxstrap Exception error, please attach a copy of the provided log file. There is a button on the dialog that locates it for you.
|
||||
@ -32,3 +33,10 @@ body:
|
||||
description: Provide a comprehensive description of the problem you're facing. Don't forget to attach any additional resources you may have, such as log files and screenshots.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
label: Bloxstrap Log
|
||||
description: If you're getting a Bloxstrap Exception error, upload your log file here. Otherwise, just leave it empty.
|
||||
value: "N/A"
|
||||
#render: text
|
||||
|
@ -15,7 +15,11 @@ namespace Bloxstrap
|
||||
public partial class App : Application
|
||||
{
|
||||
public const string ProjectName = "Bloxstrap";
|
||||
public const string ProjectOwner = "pizzaboxer";
|
||||
public const string ProjectRepository = "pizzaboxer/bloxstrap";
|
||||
public const string ProjectDownloadLink = "https://bloxstraplabs.com";
|
||||
public const string ProjectHelpLink = "https://github.com/pizzaboxer/bloxstrap/wiki";
|
||||
public const string ProjectSupportLink = "https://github.com/pizzaboxer/bloxstrap/issues/new";
|
||||
|
||||
public const string RobloxPlayerAppName = "RobloxPlayerBeta";
|
||||
public const string RobloxStudioAppName = "RobloxStudioBeta";
|
||||
@ -29,9 +33,11 @@ namespace Bloxstrap
|
||||
|
||||
public static string Version = Assembly.GetExecutingAssembly().GetName().Version!.ToString()[..^2];
|
||||
|
||||
public static readonly MD5 MD5Provider = MD5.Create();
|
||||
public static bool IsActionBuild => !String.IsNullOrEmpty(BuildMetadata.CommitRef);
|
||||
|
||||
public static NotifyIconWrapper? NotifyIcon { get; set; }
|
||||
public static bool IsProductionBuild => IsActionBuild && BuildMetadata.CommitRef.StartsWith("tag", StringComparison.Ordinal);
|
||||
|
||||
public static readonly MD5 MD5Provider = MD5.Create();
|
||||
|
||||
public static readonly Logger Logger = new();
|
||||
|
||||
@ -49,9 +55,7 @@ namespace Bloxstrap
|
||||
)
|
||||
);
|
||||
|
||||
#if RELEASE
|
||||
private static bool _showingExceptionDialog = false;
|
||||
#endif
|
||||
|
||||
public static void Terminate(ErrorCode exitCode = ErrorCode.ERROR_SUCCESS)
|
||||
{
|
||||
@ -59,8 +63,6 @@ namespace Bloxstrap
|
||||
|
||||
Logger.WriteLine("App::Terminate", $"Terminating with exit code {exitCodeNum} ({exitCode})");
|
||||
|
||||
NotifyIcon?.Dispose();
|
||||
|
||||
Environment.Exit(exitCodeNum);
|
||||
}
|
||||
|
||||
@ -73,24 +75,51 @@ namespace Bloxstrap
|
||||
FinalizeExceptionHandling(e.Exception);
|
||||
}
|
||||
|
||||
public static void FinalizeExceptionHandling(Exception exception, bool log = true)
|
||||
public static void FinalizeExceptionHandling(AggregateException ex)
|
||||
{
|
||||
foreach (var innerEx in ex.InnerExceptions)
|
||||
Logger.WriteException("App::FinalizeExceptionHandling", innerEx);
|
||||
|
||||
FinalizeExceptionHandling(ex.GetBaseException(), false);
|
||||
}
|
||||
|
||||
public static void FinalizeExceptionHandling(Exception ex, bool log = true)
|
||||
{
|
||||
if (log)
|
||||
Logger.WriteException("App::FinalizeExceptionHandling", exception);
|
||||
Logger.WriteException("App::FinalizeExceptionHandling", ex);
|
||||
|
||||
#if DEBUG
|
||||
throw exception;
|
||||
#else
|
||||
if (_showingExceptionDialog)
|
||||
return;
|
||||
|
||||
_showingExceptionDialog = true;
|
||||
|
||||
if (!LaunchSettings.QuietFlag.Active)
|
||||
Frontend.ShowExceptionDialog(exception);
|
||||
Frontend.ShowExceptionDialog(ex);
|
||||
|
||||
Terminate(ErrorCode.ERROR_INSTALL_FAILURE);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static async Task<GithubRelease?> GetLatestRelease()
|
||||
{
|
||||
const string LOG_IDENT = "App::GetLatestRelease";
|
||||
|
||||
try
|
||||
{
|
||||
var releaseInfo = await Http.GetJson<GithubRelease>($"https://api.github.com/repos/{ProjectRepository}/releases/latest");
|
||||
|
||||
if (releaseInfo is null || releaseInfo.Assets is null)
|
||||
{
|
||||
Logger.WriteLine(LOG_IDENT, "Encountered invalid data");
|
||||
return null;
|
||||
}
|
||||
|
||||
return releaseInfo;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.WriteException(LOG_IDENT, ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
@ -103,10 +132,10 @@ namespace Bloxstrap
|
||||
|
||||
Logger.WriteLine(LOG_IDENT, $"Starting {ProjectName} v{Version}");
|
||||
|
||||
if (String.IsNullOrEmpty(BuildMetadata.CommitHash))
|
||||
Logger.WriteLine(LOG_IDENT, $"Compiled {BuildMetadata.Timestamp.ToFriendlyString()} from {BuildMetadata.Machine}");
|
||||
else
|
||||
if (IsActionBuild)
|
||||
Logger.WriteLine(LOG_IDENT, $"Compiled {BuildMetadata.Timestamp.ToFriendlyString()} from commit {BuildMetadata.CommitHash} ({BuildMetadata.CommitRef})");
|
||||
else
|
||||
Logger.WriteLine(LOG_IDENT, $"Compiled {BuildMetadata.Timestamp.ToFriendlyString()} from {BuildMetadata.Machine}");
|
||||
|
||||
Logger.WriteLine(LOG_IDENT, $"Loaded from {Paths.Process}");
|
||||
|
||||
@ -162,6 +191,26 @@ namespace Bloxstrap
|
||||
}
|
||||
}
|
||||
|
||||
if (fixInstallLocation && installLocation is not null)
|
||||
{
|
||||
var installer = new Installer
|
||||
{
|
||||
InstallLocation = installLocation,
|
||||
IsImplicitInstall = true
|
||||
};
|
||||
|
||||
if (installer.CheckInstallLocation())
|
||||
{
|
||||
Logger.WriteLine(LOG_IDENT, $"Changing install location to '{installLocation}'");
|
||||
installer.DoInstall();
|
||||
}
|
||||
else
|
||||
{
|
||||
// force reinstall
|
||||
installLocation = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (installLocation is null)
|
||||
{
|
||||
Logger.Initialize(true);
|
||||
@ -169,21 +218,6 @@ namespace Bloxstrap
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fixInstallLocation)
|
||||
{
|
||||
var installer = new Installer
|
||||
{
|
||||
InstallLocation = installLocation,
|
||||
IsImplicitInstall = true
|
||||
};
|
||||
|
||||
if (installer.CheckInstallLocation())
|
||||
{
|
||||
Logger.WriteLine(LOG_IDENT, $"Changing install location to '{installLocation}'");
|
||||
installer.DoInstall();
|
||||
}
|
||||
}
|
||||
|
||||
Paths.Initialize(installLocation);
|
||||
|
||||
// ensure executable is in the install directory
|
||||
@ -202,10 +236,6 @@ namespace Bloxstrap
|
||||
State.Load();
|
||||
FastFlags.Load();
|
||||
|
||||
// we can only parse them now as settings need
|
||||
// to be loaded first to know what our channel is
|
||||
// LaunchSettings.ParseRoblox();
|
||||
|
||||
if (!Locale.SupportedLocales.ContainsKey(Settings.Prop.Locale))
|
||||
{
|
||||
Settings.Prop.Locale = "nil";
|
||||
@ -214,13 +244,13 @@ namespace Bloxstrap
|
||||
|
||||
Locale.Set(Settings.Prop.Locale);
|
||||
|
||||
if (!LaunchSettings.UninstallFlag.Active)
|
||||
if (!LaunchSettings.BypassUpdateCheck)
|
||||
Installer.HandleUpgrade();
|
||||
|
||||
LaunchHandler.ProcessLaunchArgs();
|
||||
}
|
||||
|
||||
Terminate();
|
||||
// you must *explicitly* call terminate when everything is done, it won't be called implicitly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,17 @@ namespace Bloxstrap.AppData
|
||||
{ "extracontent-places.zip", @"ExtraContent\places\" },
|
||||
};
|
||||
|
||||
public virtual string ExecutableName { get; } = null!;
|
||||
|
||||
public virtual string Directory { get; } = null!;
|
||||
|
||||
public string LockFilePath => Path.Combine(Directory, "Bloxstrap.lock");
|
||||
|
||||
public string ExecutablePath => Path.Combine(Directory, ExecutableName);
|
||||
|
||||
public virtual IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; }
|
||||
|
||||
|
||||
public CommonAppData()
|
||||
{
|
||||
if (PackageDirectoryMap is null)
|
||||
|
@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bloxstrap.AppData
|
||||
namespace Bloxstrap.AppData
|
||||
{
|
||||
internal interface IAppData
|
||||
{
|
||||
@ -18,6 +12,14 @@ namespace Bloxstrap.AppData
|
||||
|
||||
string StartEvent { get; }
|
||||
|
||||
string Directory { get; }
|
||||
|
||||
string LockFilePath { get; }
|
||||
|
||||
string ExecutablePath { get; }
|
||||
|
||||
AppState State { get; }
|
||||
|
||||
IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -8,15 +8,19 @@ namespace Bloxstrap.AppData
|
||||
{
|
||||
public class RobloxPlayerData : CommonAppData, IAppData
|
||||
{
|
||||
public string ProductName { get; } = "Roblox";
|
||||
public string ProductName => "Roblox";
|
||||
|
||||
public string BinaryType { get; } = "WindowsPlayer";
|
||||
public string BinaryType => "WindowsPlayer";
|
||||
|
||||
public string RegistryName { get; } = "RobloxPlayer";
|
||||
public string RegistryName => "RobloxPlayer";
|
||||
|
||||
public string ExecutableName { get; } = "RobloxPlayerBeta.exe";
|
||||
public override string ExecutableName => "RobloxPlayerBeta.exe";
|
||||
|
||||
public string StartEvent { get; } = "www.roblox.com/robloxStartedEvent";
|
||||
public string StartEvent => "www.roblox.com/robloxStartedEvent";
|
||||
|
||||
public override string Directory => Path.Combine(Paths.Roblox, "Player");
|
||||
|
||||
public AppState State => App.State.Prop.Player;
|
||||
|
||||
public override IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; } = new Dictionary<string, string>()
|
||||
{
|
||||
|
@ -1,22 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bloxstrap.AppData
|
||||
namespace Bloxstrap.AppData
|
||||
{
|
||||
public class RobloxStudioData : CommonAppData, IAppData
|
||||
{
|
||||
public string ProductName { get; } = "Roblox Studio";
|
||||
public string ProductName => "Roblox Studio";
|
||||
|
||||
public string BinaryType { get; } = "WindowsStudio64";
|
||||
public string BinaryType => "WindowsStudio64";
|
||||
|
||||
public string RegistryName { get; } = "RobloxStudio";
|
||||
public string RegistryName => "RobloxStudio";
|
||||
|
||||
public string ExecutableName { get; } = "RobloxStudioBeta.exe";
|
||||
public override string ExecutableName => "RobloxStudioBeta.exe";
|
||||
|
||||
public string StartEvent { get; } = "www.roblox.com/robloxStudioStartedEvent";
|
||||
public string StartEvent => "www.roblox.com/robloxStudioStartedEvent";
|
||||
|
||||
public override string Directory => Path.Combine(Paths.Roblox, "Studio");
|
||||
|
||||
public AppState State => App.State.Prop.Studio;
|
||||
|
||||
public override IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; } = new Dictionary<string, string>()
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,9 +4,7 @@
|
||||
{
|
||||
[EnumName(FromTranslation = "Common.Automatic")]
|
||||
Default,
|
||||
// Vulkan,
|
||||
D3D11,
|
||||
D3D10,
|
||||
// OpenGL
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bloxstrap.Exceptions
|
||||
{
|
||||
internal class HttpResponseException : Exception
|
||||
{
|
||||
public HttpResponseMessage ResponseMessage { get; }
|
||||
|
||||
public HttpResponseException(HttpResponseMessage responseMessage)
|
||||
: base($"Could not connect to {responseMessage.RequestMessage!.RequestUri} because it returned HTTP {(int)responseMessage.StatusCode} ({responseMessage.ReasonPhrase})")
|
||||
{
|
||||
ResponseMessage = responseMessage;
|
||||
}
|
||||
}
|
||||
}
|
13
Bloxstrap/Exceptions/InvalidHTTPResponseException.cs
Normal file
13
Bloxstrap/Exceptions/InvalidHTTPResponseException.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bloxstrap.Exceptions
|
||||
{
|
||||
internal class InvalidHTTPResponseException : Exception
|
||||
{
|
||||
public InvalidHTTPResponseException(string message) : base(message) { }
|
||||
}
|
||||
}
|
@ -8,11 +8,28 @@ namespace Bloxstrap.Extensions
|
||||
{
|
||||
public static Icon GetSized(this Icon icon, int width, int height) => new(icon, new Size(width, height));
|
||||
|
||||
public static ImageSource GetImageSource(this Icon icon)
|
||||
public static ImageSource GetImageSource(this Icon icon, bool handleException = true)
|
||||
{
|
||||
using MemoryStream stream = new();
|
||||
icon.Save(stream);
|
||||
return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
|
||||
if (handleException)
|
||||
{
|
||||
try
|
||||
{
|
||||
return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteException("IconEx::GetImageSource", ex);
|
||||
Frontend.ShowMessageBox(String.Format(Strings.Dialog_IconLoadFailed, ex.Message));
|
||||
return BootstrapperIcon.IconBloxstrap.GetIcon().GetImageSource(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
{
|
||||
public static string ToTranslatedString(this ServerType value) => value switch
|
||||
{
|
||||
ServerType.Public => Resources.Strings.Enums_ServerType_Public,
|
||||
ServerType.Private => Resources.Strings.Enums_ServerType_Private,
|
||||
ServerType.Reserved => Resources.Strings.Enums_ServerType_Reserved,
|
||||
ServerType.Public => Strings.Enums_ServerType_Public,
|
||||
ServerType.Private => Strings.Enums_ServerType_Private,
|
||||
ServerType.Reserved => Strings.Enums_ServerType_Reserved,
|
||||
_ => "?"
|
||||
};
|
||||
}
|
||||
|
@ -9,15 +9,10 @@ namespace Bloxstrap.Extensions
|
||||
if (dialogTheme != Theme.Default)
|
||||
return dialogTheme;
|
||||
|
||||
RegistryKey? key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
|
||||
using var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
|
||||
|
||||
if (key is not null)
|
||||
{
|
||||
var value = key.GetValue("AppsUseLightTheme");
|
||||
|
||||
if (value is not null && (int)value == 0)
|
||||
return Theme.Dark;
|
||||
}
|
||||
if (key?.GetValue("AppsUseLightTheme") is int value && value == 0)
|
||||
return Theme.Dark;
|
||||
|
||||
return Theme.Light;
|
||||
}
|
||||
|
@ -4,8 +4,14 @@ namespace Bloxstrap
|
||||
{
|
||||
public class FastFlagManager : JsonManager<Dictionary<string, object>>
|
||||
{
|
||||
public override string ClassName => nameof(FastFlagManager);
|
||||
|
||||
public override string LOG_IDENT_CLASS => ClassName;
|
||||
|
||||
public override string FileLocation => Path.Combine(Paths.Modifications, "ClientSettings\\ClientAppSettings.json");
|
||||
|
||||
public bool Changed => !OriginalProp.SequenceEqual(Prop);
|
||||
|
||||
public static IReadOnlyDictionary<string, string> PresetFlags = new Dictionary<string, string>
|
||||
{
|
||||
{ "Network.Log", "FLogNetwork" },
|
||||
@ -28,9 +34,6 @@ namespace Bloxstrap
|
||||
|
||||
{ "Rendering.Mode.D3D11", "FFlagDebugGraphicsPreferD3D11" },
|
||||
{ "Rendering.Mode.D3D10", "FFlagDebugGraphicsPreferD3D11FL10" },
|
||||
{ "Rendering.Mode.Vulkan", "FFlagDebugGraphicsPreferVulkan" },
|
||||
{ "Rendering.Mode.Vulkan.Fix", "FFlagRenderVulkanFixMinimizeWindow" },
|
||||
{ "Rendering.Mode.OpenGL", "FFlagDebugGraphicsPreferOpenGL" },
|
||||
|
||||
{ "Rendering.Lighting.Voxel", "DFFlagDebugRenderForceTechnologyVoxel" },
|
||||
{ "Rendering.Lighting.ShadowMap", "FFlagDebugForceFutureIsBrightPhase2" },
|
||||
@ -46,10 +49,9 @@ namespace Bloxstrap
|
||||
{ "UI.FlagState", "FStringDebugShowFlagState" },
|
||||
#endif
|
||||
|
||||
{ "UI.Menu.GraphicsSlider", "FFlagFixGraphicsQuality" },
|
||||
{ "UI.FullscreenTitlebarDelay", "FIntFullscreenTitleBarTriggerDelayMillis" },
|
||||
|
||||
{ "UI.Menu.Style.DisableV2", "FFlagDisableNewIGMinDUA" },
|
||||
{ "UI.Menu.Style.V2Rollout", "FIntNewInGameMenuPercentRollout3" },
|
||||
{ "UI.Menu.Style.EnableV4.1", "FFlagEnableInGameMenuControls" },
|
||||
{ "UI.Menu.Style.EnableV4.2", "FFlagEnableInGameMenuModernization" },
|
||||
{ "UI.Menu.Style.EnableV4Chrome", "FFlagEnableInGameMenuChrome" },
|
||||
@ -59,14 +61,11 @@ namespace Bloxstrap
|
||||
{ "UI.Menu.Style.ABTest.3", "FFlagEnableInGameMenuChromeABTest3" }
|
||||
};
|
||||
|
||||
// only one missing here is Metal because lol
|
||||
public static IReadOnlyDictionary<RenderingMode, string> RenderingModes => new Dictionary<RenderingMode, string>
|
||||
{
|
||||
{ RenderingMode.Default, "None" },
|
||||
// { RenderingMode.Vulkan, "Vulkan" },
|
||||
{ RenderingMode.D3D11, "D3D11" },
|
||||
{ RenderingMode.D3D10, "D3D10" },
|
||||
// { RenderingMode.OpenGL, "OpenGL" }
|
||||
};
|
||||
|
||||
public static IReadOnlyDictionary<LightingMode, string> LightingModes => new Dictionary<LightingMode, string>
|
||||
@ -102,7 +101,7 @@ namespace Bloxstrap
|
||||
InGameMenuVersion.Default,
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
{ "DisableV2", null },
|
||||
{ "V2Rollout", null },
|
||||
{ "EnableV4", null },
|
||||
{ "EnableV4Chrome", null },
|
||||
{ "ABTest", null }
|
||||
@ -113,7 +112,7 @@ namespace Bloxstrap
|
||||
InGameMenuVersion.V1,
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
{ "DisableV2", "True" },
|
||||
{ "V2Rollout", "0" },
|
||||
{ "EnableV4", "False" },
|
||||
{ "EnableV4Chrome", "False" },
|
||||
{ "ABTest", "False" }
|
||||
@ -124,7 +123,7 @@ namespace Bloxstrap
|
||||
InGameMenuVersion.V2,
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
{ "DisableV2", "False" },
|
||||
{ "V2Rollout", "100" },
|
||||
{ "EnableV4", "False" },
|
||||
{ "EnableV4Chrome", "False" },
|
||||
{ "ABTest", "False" }
|
||||
@ -135,7 +134,7 @@ namespace Bloxstrap
|
||||
InGameMenuVersion.V4,
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
{ "DisableV2", "True" },
|
||||
{ "V2Rollout", "0" },
|
||||
{ "EnableV4", "True" },
|
||||
{ "EnableV4Chrome", "False" },
|
||||
{ "ABTest", "False" }
|
||||
@ -146,7 +145,7 @@ namespace Bloxstrap
|
||||
InGameMenuVersion.V4Chrome,
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
{ "DisableV2", "True" },
|
||||
{ "V2Rollout", "0" },
|
||||
{ "EnableV4", "True" },
|
||||
{ "EnableV4Chrome", "True" },
|
||||
{ "ABTest", "False" }
|
||||
@ -228,14 +227,6 @@ namespace Bloxstrap
|
||||
return mapping.First().Key;
|
||||
}
|
||||
|
||||
public void CheckManualFullscreenPreset()
|
||||
{
|
||||
if (GetPreset("Rendering.Mode.Vulkan") == "True" || GetPreset("Rendering.Mode.OpenGL") == "True")
|
||||
SetPreset("Rendering.ManualFullscreen", null);
|
||||
else
|
||||
SetPreset("Rendering.ManualFullscreen", "False");
|
||||
}
|
||||
|
||||
public override void Save()
|
||||
{
|
||||
// convert all flag values to strings before saving
|
||||
@ -244,21 +235,21 @@ namespace Bloxstrap
|
||||
Prop[pair.Key] = pair.Value.ToString()!;
|
||||
|
||||
base.Save();
|
||||
|
||||
// clone the dictionary
|
||||
OriginalProp = new(Prop);
|
||||
}
|
||||
|
||||
public override void Load()
|
||||
public override void Load(bool alertFailure = true)
|
||||
{
|
||||
base.Load();
|
||||
base.Load(alertFailure);
|
||||
|
||||
CheckManualFullscreenPreset();
|
||||
// clone the dictionary
|
||||
OriginalProp = new(Prop);
|
||||
|
||||
// TODO - remove when activity tracking has been revamped
|
||||
if (GetPreset("Network.Log") != "7")
|
||||
SetPreset("Network.Log", "7");
|
||||
|
||||
string? val = GetPreset("UI.Menu.Style.EnableV4.1");
|
||||
if (GetPreset("UI.Menu.Style.EnableV4.2") != val)
|
||||
SetPreset("UI.Menu.Style.EnableV4.2", val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
7
Bloxstrap/GlobalCache.cs
Normal file
7
Bloxstrap/GlobalCache.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Bloxstrap
|
||||
{
|
||||
public static class GlobalCache
|
||||
{
|
||||
public static readonly Dictionary<string, string?> ServerLocation = new();
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ global using System.Collections.Generic;
|
||||
global using System.Diagnostics;
|
||||
global using System.Globalization;
|
||||
global using System.IO;
|
||||
global using System.IO.Compression;
|
||||
global using System.Text;
|
||||
global using System.Text.Json;
|
||||
global using System.Text.Json.Serialization;
|
||||
@ -18,10 +17,16 @@ global using Bloxstrap.Enums;
|
||||
global using Bloxstrap.Exceptions;
|
||||
global using Bloxstrap.Extensions;
|
||||
global using Bloxstrap.Models;
|
||||
global using Bloxstrap.Models.APIs.Config;
|
||||
global using Bloxstrap.Models.APIs.GitHub;
|
||||
global using Bloxstrap.Models.APIs.Roblox;
|
||||
global using Bloxstrap.Models.Attributes;
|
||||
global using Bloxstrap.Models.BloxstrapRPC;
|
||||
global using Bloxstrap.Models.RobloxApi;
|
||||
global using Bloxstrap.Models.Entities;
|
||||
global using Bloxstrap.Models.Manifest;
|
||||
global using Bloxstrap.Models.Persistable;
|
||||
global using Bloxstrap.Models.SettingTasks;
|
||||
global using Bloxstrap.Models.SettingTasks.Base;
|
||||
global using Bloxstrap.Resources;
|
||||
global using Bloxstrap.UI;
|
||||
global using Bloxstrap.Utility;
|
@ -1,9 +1,4 @@
|
||||
using System.DirectoryServices;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Animation;
|
||||
using Bloxstrap.Resources;
|
||||
using System.Windows;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Bloxstrap
|
||||
@ -16,6 +11,8 @@ namespace Bloxstrap
|
||||
|
||||
public string InstallLocation = Path.Combine(Paths.LocalAppData, "Bloxstrap");
|
||||
|
||||
public bool ExistingDataPresent => File.Exists(Path.Combine(InstallLocation, "Settings.json"));
|
||||
|
||||
public bool CreateDesktopShortcuts = true;
|
||||
|
||||
public bool CreateStartMenuShortcuts = true;
|
||||
@ -26,6 +23,10 @@ namespace Bloxstrap
|
||||
|
||||
public void DoInstall()
|
||||
{
|
||||
const string LOG_IDENT = "Installer::DoInstall";
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, "Beginning installation");
|
||||
|
||||
// should've been created earlier from the write test anyway
|
||||
Directory.CreateDirectory(InstallLocation);
|
||||
|
||||
@ -34,7 +35,19 @@ namespace Bloxstrap
|
||||
if (!IsImplicitInstall)
|
||||
{
|
||||
Filesystem.AssertReadOnly(Paths.Application);
|
||||
File.Copy(Paths.Process, Paths.Application, true);
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(Paths.Process, Paths.Application, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Could not overwrite executable");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
|
||||
Frontend.ShowMessageBox(Strings.Installer_Install_CannotOverwrite, MessageBoxImage.Error);
|
||||
App.Terminate(ErrorCode.ERROR_INSTALL_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: registry access checks, i'll need to look back on issues to see what the error looks like
|
||||
@ -50,21 +63,19 @@ namespace Bloxstrap
|
||||
|
||||
uninstallKey.SetValue("InstallLocation", Paths.Base);
|
||||
uninstallKey.SetValue("NoRepair", 1);
|
||||
uninstallKey.SetValue("Publisher", "pizzaboxer");
|
||||
uninstallKey.SetValue("Publisher", App.ProjectOwner);
|
||||
uninstallKey.SetValue("ModifyPath", $"\"{Paths.Application}\" -settings");
|
||||
uninstallKey.SetValue("QuietUninstallString", $"\"{Paths.Application}\" -uninstall -quiet");
|
||||
uninstallKey.SetValue("UninstallString", $"\"{Paths.Application}\" -uninstall");
|
||||
uninstallKey.SetValue("URLInfoAbout", $"https://github.com/{App.ProjectRepository}");
|
||||
uninstallKey.SetValue("URLUpdateInfo", $"https://github.com/{App.ProjectRepository}/releases/latest");
|
||||
uninstallKey.SetValue("HelpLink", App.ProjectHelpLink);
|
||||
uninstallKey.SetValue("URLInfoAbout", App.ProjectSupportLink);
|
||||
uninstallKey.SetValue("URLUpdateInfo", App.ProjectDownloadLink);
|
||||
}
|
||||
|
||||
// only register player, for the scenario where the user installs bloxstrap, closes it,
|
||||
// and then launches from the website expecting it to work
|
||||
// studio can be implicitly registered when it's first launched manually
|
||||
ProtocolHandler.Register("roblox", "Roblox", Paths.Application, "-player \"%1\"");
|
||||
ProtocolHandler.Register("roblox-player", "Roblox", Paths.Application, "-player \"%1\"");
|
||||
|
||||
// TODO: implicit installation needs to reregister studio
|
||||
WindowsRegistry.RegisterPlayer();
|
||||
|
||||
if (CreateDesktopShortcuts)
|
||||
Shortcut.Create(Paths.Application, "", DesktopShortcut);
|
||||
@ -73,9 +84,14 @@ namespace Bloxstrap
|
||||
Shortcut.Create(Paths.Application, "", StartMenuShortcut);
|
||||
|
||||
// existing configuration persisting from an earlier install
|
||||
App.Settings.Load();
|
||||
App.State.Load();
|
||||
App.FastFlags.Load();
|
||||
App.Settings.Load(false);
|
||||
App.State.Load(false);
|
||||
App.FastFlags.Load(false);
|
||||
|
||||
if (!String.IsNullOrEmpty(App.State.Prop.Studio.VersionGuid))
|
||||
WindowsRegistry.RegisterStudio();
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, "Installation finished");
|
||||
}
|
||||
|
||||
private bool ValidateLocation()
|
||||
@ -88,6 +104,10 @@ namespace Bloxstrap
|
||||
if (InstallLocation.StartsWith("\\\\"))
|
||||
return false;
|
||||
|
||||
if (InstallLocation.StartsWith(Path.GetTempPath(), StringComparison.InvariantCultureIgnoreCase)
|
||||
|| InstallLocation.Contains("\\Temp\\", StringComparison.InvariantCultureIgnoreCase))
|
||||
return false;
|
||||
|
||||
// prevent from installing to a onedrive folder
|
||||
if (InstallLocation.Contains("OneDrive", StringComparison.InvariantCultureIgnoreCase))
|
||||
return false;
|
||||
@ -158,11 +178,12 @@ namespace Bloxstrap
|
||||
const string LOG_IDENT = "Installer::DoUninstall";
|
||||
|
||||
var processes = new List<Process>();
|
||||
processes.AddRange(Process.GetProcessesByName(App.RobloxPlayerAppName));
|
||||
|
||||
#if STUDIO_FEATURES
|
||||
processes.AddRange(Process.GetProcessesByName(App.RobloxStudioAppName));
|
||||
#endif
|
||||
if (!String.IsNullOrEmpty(App.State.Prop.Player.VersionGuid))
|
||||
processes.AddRange(Process.GetProcessesByName(App.RobloxPlayerAppName));
|
||||
|
||||
if (!String.IsNullOrEmpty(App.State.Prop.Studio.VersionGuid))
|
||||
processes.AddRange(Process.GetProcessesByName(App.RobloxStudioAppName));
|
||||
|
||||
// prompt to shutdown roblox if its currently running
|
||||
if (processes.Any())
|
||||
@ -175,7 +196,10 @@ namespace Bloxstrap
|
||||
);
|
||||
|
||||
if (result != MessageBoxResult.OK)
|
||||
{
|
||||
App.Terminate(ErrorCode.ERROR_CANCELLED);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@ -203,44 +227,38 @@ namespace Bloxstrap
|
||||
{
|
||||
playerStillInstalled = false;
|
||||
|
||||
ProtocolHandler.Unregister("roblox");
|
||||
ProtocolHandler.Unregister("roblox-player");
|
||||
WindowsRegistry.Unregister("roblox");
|
||||
WindowsRegistry.Unregister("roblox-player");
|
||||
}
|
||||
else
|
||||
{
|
||||
// revert launch uri handler to stock bootstrapper
|
||||
string playerPath = Path.Combine((string)playerFolder, "RobloxPlayerBeta.exe");
|
||||
|
||||
ProtocolHandler.Register("roblox", "Roblox", playerPath);
|
||||
ProtocolHandler.Register("roblox-player", "Roblox", playerPath);
|
||||
WindowsRegistry.RegisterPlayer(playerPath, "%1");
|
||||
}
|
||||
|
||||
using RegistryKey? studioBootstrapperKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\roblox-studio");
|
||||
if (studioBootstrapperKey is null)
|
||||
using var studioKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\roblox-studio");
|
||||
var studioFolder = studioKey?.GetValue("InstallLocation");
|
||||
|
||||
if (studioKey is null || studioFolder is not string)
|
||||
{
|
||||
studioStillInstalled = false;
|
||||
|
||||
#if STUDIO_FEATURES
|
||||
ProtocolHandler.Unregister("roblox-studio");
|
||||
ProtocolHandler.Unregister("roblox-studio-auth");
|
||||
WindowsRegistry.Unregister("roblox-studio");
|
||||
WindowsRegistry.Unregister("roblox-studio-auth");
|
||||
|
||||
ProtocolHandler.Unregister("Roblox.Place");
|
||||
ProtocolHandler.Unregister(".rbxl");
|
||||
ProtocolHandler.Unregister(".rbxlx");
|
||||
#endif
|
||||
WindowsRegistry.Unregister("Roblox.Place");
|
||||
WindowsRegistry.Unregister(".rbxl");
|
||||
WindowsRegistry.Unregister(".rbxlx");
|
||||
}
|
||||
#if STUDIO_FEATURES
|
||||
else
|
||||
{
|
||||
string studioLocation = (string?)studioBootstrapperKey.GetValue("InstallLocation") + "RobloxStudioBeta.exe"; // points to studio exe instead of bootstrapper
|
||||
ProtocolHandler.Register("roblox-studio", "Roblox", studioLocation);
|
||||
ProtocolHandler.Register("roblox-studio-auth", "Roblox", studioLocation);
|
||||
string studioPath = Path.Combine((string)studioFolder, "RobloxStudioBeta.exe");
|
||||
string studioLauncherPath = Path.Combine((string)studioFolder, "RobloxStudioLauncherBeta.exe");
|
||||
|
||||
ProtocolHandler.RegisterRobloxPlace(studioLocation);
|
||||
WindowsRegistry.RegisterStudioProtocol(studioPath, "%1");
|
||||
WindowsRegistry.RegisterStudioFileClass(studioPath, "-ide \"%1\"");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
var cleanupSequence = new List<Action>
|
||||
{
|
||||
@ -257,8 +275,10 @@ namespace Bloxstrap
|
||||
|
||||
() => File.Delete(StartMenuShortcut),
|
||||
|
||||
() => Directory.Delete(Paths.Versions, true),
|
||||
() => Directory.Delete(Paths.Downloads, true),
|
||||
() => Directory.Delete(Paths.Roblox, true),
|
||||
|
||||
() => File.Delete(App.State.FileLocation)
|
||||
};
|
||||
|
||||
if (!keepData)
|
||||
@ -268,8 +288,7 @@ namespace Bloxstrap
|
||||
() => Directory.Delete(Paths.Modifications, true),
|
||||
() => Directory.Delete(Paths.Logs, true),
|
||||
|
||||
() => File.Delete(App.Settings.FileLocation),
|
||||
() => File.Delete(App.State.FileLocation), // TODO: maybe this should always be deleted? not sure yet
|
||||
() => File.Delete(App.Settings.FileLocation)
|
||||
});
|
||||
}
|
||||
|
||||
@ -331,8 +350,9 @@ namespace Bloxstrap
|
||||
return;
|
||||
|
||||
// 2.0.0 downloads updates to <BaseFolder>/Updates so lol
|
||||
// TODO: 2.8.0 will download them to <Temp>/Bloxstrap/Updates
|
||||
bool isAutoUpgrade = Paths.Process.StartsWith(Path.Combine(Paths.Base, "Updates")) || Paths.Process.StartsWith(Path.Combine(Paths.LocalAppData, "Temp"));
|
||||
bool isAutoUpgrade = App.LaunchSettings.UpgradeFlag.Active
|
||||
|| Paths.Process.StartsWith(Path.Combine(Paths.Base, "Updates"))
|
||||
|| Paths.Process.StartsWith(Paths.Temp);
|
||||
|
||||
var existingVer = FileVersionInfo.GetVersionInfo(Paths.Application).ProductVersion;
|
||||
var currentVer = FileVersionInfo.GetVersionInfo(Paths.Process).ProductVersion;
|
||||
@ -340,8 +360,20 @@ namespace Bloxstrap
|
||||
if (MD5Hash.FromFile(Paths.Process) == MD5Hash.FromFile(Paths.Application))
|
||||
return;
|
||||
|
||||
if (currentVer is not null && existingVer is not null && Utilities.CompareVersions(currentVer, existingVer) == VersionComparison.LessThan)
|
||||
{
|
||||
var result = Frontend.ShowMessageBox(
|
||||
Strings.InstallChecker_VersionLessThanInstalled,
|
||||
MessageBoxImage.Question,
|
||||
MessageBoxButton.YesNo
|
||||
);
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
}
|
||||
|
||||
// silently upgrade version if the command line flag is set or if we're launching from an auto update
|
||||
if (!App.LaunchSettings.UpgradeFlag.Active && !isAutoUpgrade)
|
||||
if (!isAutoUpgrade)
|
||||
{
|
||||
var result = Frontend.ShowMessageBox(
|
||||
Strings.InstallChecker_VersionDifferentThanInstalled,
|
||||
@ -353,47 +385,94 @@ namespace Bloxstrap
|
||||
return;
|
||||
}
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, "Doing upgrade");
|
||||
|
||||
Filesystem.AssertReadOnly(Paths.Application);
|
||||
|
||||
// TODO: make this use a mutex somehow
|
||||
// yes, this is EXTREMELY hacky, but the updater process that launched the
|
||||
// new version may still be open and so we have to wait for it to close
|
||||
int attempts = 0;
|
||||
while (attempts < 10)
|
||||
using (var ipl = new InterProcessLock("AutoUpdater", TimeSpan.FromSeconds(5)))
|
||||
{
|
||||
attempts++;
|
||||
if (!ipl.IsAcquired)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to update! (Could not obtain singleton mutex)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// prior to 2.8.0, auto-updating was handled with this... bruteforce method
|
||||
// now it's handled with the system mutex you see above, but we need to keep this logic for <2.8.0 versions
|
||||
for (int i = 1; i <= 10; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(Paths.Application);
|
||||
File.Copy(Paths.Process, Paths.Application, true);
|
||||
break;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (attempts == 1)
|
||||
if (i == 1)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Waiting for write permissions to update version");
|
||||
}
|
||||
else if (i == 10)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to update! (Could not get write permissions after 10 tries/5 seconds)");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
if (attempts == 10)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to update! (Could not get write permissions after 5 seconds)");
|
||||
return;
|
||||
}
|
||||
|
||||
File.Copy(Paths.Process, Paths.Application);
|
||||
|
||||
using (var uninstallKey = Registry.CurrentUser.CreateSubKey(App.UninstallKey))
|
||||
{
|
||||
uninstallKey.SetValue("DisplayVersion", App.Version);
|
||||
|
||||
uninstallKey.SetValue("Publisher", App.ProjectOwner);
|
||||
uninstallKey.SetValue("HelpLink", App.ProjectHelpLink);
|
||||
uninstallKey.SetValue("URLInfoAbout", App.ProjectSupportLink);
|
||||
uninstallKey.SetValue("URLUpdateInfo", App.ProjectDownloadLink);
|
||||
}
|
||||
|
||||
// update migrations
|
||||
|
||||
if (existingVer is not null)
|
||||
{
|
||||
if (Utilities.CompareVersions(existingVer, "2.2.0") == VersionComparison.LessThan)
|
||||
{
|
||||
string path = Path.Combine(Paths.Integrations, "rbxfpsunlocker");
|
||||
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
Directory.Delete(path, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (Utilities.CompareVersions(existingVer, "2.3.0") == VersionComparison.LessThan)
|
||||
{
|
||||
string injectorLocation = Path.Combine(Paths.Modifications, "dxgi.dll");
|
||||
string configLocation = Path.Combine(Paths.Modifications, "ReShade.ini");
|
||||
|
||||
if (File.Exists(injectorLocation))
|
||||
{
|
||||
Frontend.ShowMessageBox(
|
||||
Strings.Bootstrapper_HyperionUpdateInfo,
|
||||
MessageBoxImage.Warning
|
||||
);
|
||||
|
||||
File.Delete(injectorLocation);
|
||||
}
|
||||
|
||||
if (File.Exists(configLocation))
|
||||
File.Delete(configLocation);
|
||||
}
|
||||
|
||||
|
||||
if (Utilities.CompareVersions(existingVer, "2.5.0") == VersionComparison.LessThan)
|
||||
{
|
||||
App.FastFlags.SetValue("DFFlagDisableDPIScale", null);
|
||||
@ -408,6 +487,13 @@ namespace Bloxstrap
|
||||
App.FastFlags.SetPreset("UI.Menu.Style.ABTest", false);
|
||||
}
|
||||
|
||||
if (Utilities.CompareVersions(existingVer, "2.5.3") == VersionComparison.LessThan)
|
||||
{
|
||||
string? val = App.FastFlags.GetPreset("UI.Menu.Style.EnableV4.1");
|
||||
if (App.FastFlags.GetPreset("UI.Menu.Style.EnableV4.2") != val)
|
||||
App.FastFlags.SetPreset("UI.Menu.Style.EnableV4.2", val);
|
||||
}
|
||||
|
||||
if (Utilities.CompareVersions(existingVer, "2.6.0") == VersionComparison.LessThan)
|
||||
{
|
||||
if (App.Settings.Prop.UseDisableAppPatch)
|
||||
@ -429,9 +515,7 @@ namespace Bloxstrap
|
||||
|
||||
_ = int.TryParse(App.FastFlags.GetPreset("Rendering.Framerate"), out int x);
|
||||
if (x == 0)
|
||||
{
|
||||
App.FastFlags.SetPreset("Rendering.Framerate", null);
|
||||
}
|
||||
}
|
||||
|
||||
if (Utilities.CompareVersions(existingVer, "2.8.0") == VersionComparison.LessThan)
|
||||
@ -440,7 +524,7 @@ namespace Bloxstrap
|
||||
string oldStartPath = Path.Combine(Paths.WindowsStartMenu, "Bloxstrap");
|
||||
|
||||
if (File.Exists(oldDesktopPath))
|
||||
File.Move(oldDesktopPath, DesktopShortcut);
|
||||
File.Move(oldDesktopPath, DesktopShortcut, true);
|
||||
|
||||
if (Directory.Exists(oldStartPath))
|
||||
{
|
||||
@ -458,14 +542,32 @@ namespace Bloxstrap
|
||||
|
||||
Registry.CurrentUser.DeleteSubKeyTree("Software\\Bloxstrap", false);
|
||||
|
||||
ProtocolHandler.Register("roblox", "Roblox", Paths.Application, "-player \"%1\"");
|
||||
ProtocolHandler.Register("roblox-player", "Roblox", Paths.Application, "-player \"%1\"");
|
||||
WindowsRegistry.RegisterPlayer();
|
||||
|
||||
string? oldV2Val = App.FastFlags.GetValue("FFlagDisableNewIGMinDUA");
|
||||
|
||||
if (oldV2Val is not null)
|
||||
{
|
||||
if (oldV2Val == "True")
|
||||
App.FastFlags.SetPreset("UI.Menu.Style.V2Rollout", "0");
|
||||
else
|
||||
App.FastFlags.SetPreset("UI.Menu.Style.V2Rollout", "100");
|
||||
|
||||
App.FastFlags.SetValue("FFlagDisableNewIGMinDUA", null);
|
||||
}
|
||||
|
||||
App.FastFlags.SetValue("FFlagFixGraphicsQuality", null);
|
||||
|
||||
Directory.Delete(Path.Combine(Paths.Base, "Versions"));
|
||||
}
|
||||
|
||||
App.Settings.Save();
|
||||
App.FastFlags.Save();
|
||||
}
|
||||
|
||||
if (currentVer is null)
|
||||
return;
|
||||
|
||||
if (isAutoUpgrade)
|
||||
{
|
||||
Utilities.ShellExecute($"https://github.com/{App.ProjectRepository}/wiki/Release-notes-for-Bloxstrap-v{currentVer}");
|
||||
|
@ -2,24 +2,29 @@
|
||||
{
|
||||
public class ActivityWatcher : IDisposable
|
||||
{
|
||||
// i'm thinking the functionality for parsing roblox logs could be broadened for more features than just rich presence,
|
||||
// like checking the ping and region of the current connected server. maybe that's something to add?
|
||||
private const string GameJoiningEntry = "[FLog::Output] ! Joining game";
|
||||
private const string GameJoiningPrivateServerEntry = "[FLog::GameJoinUtil] GameJoinUtil::joinGamePostPrivateServer";
|
||||
private const string GameJoiningReservedServerEntry = "[FLog::GameJoinUtil] GameJoinUtil::initiateTeleportToReservedServer";
|
||||
private const string GameJoiningUDMUXEntry = "[FLog::Network] UDMUX Address = ";
|
||||
private const string GameJoinedEntry = "[FLog::Network] serverId:";
|
||||
private const string GameDisconnectedEntry = "[FLog::Network] Time to disconnect replication data:";
|
||||
private const string GameTeleportingEntry = "[FLog::SingleSurfaceApp] initiateTeleport";
|
||||
private const string GameMessageEntry = "[FLog::Output] [BloxstrapRPC]";
|
||||
private const string GameLeavingEntry = "[FLog::SingleSurfaceApp] leaveUGCGameInternal";
|
||||
private const string GameMessageEntry = "[FLog::Output] [BloxstrapRPC]";
|
||||
private const string GameJoiningEntry = "[FLog::Output] ! Joining game";
|
||||
|
||||
private const string GameJoiningEntryPattern = @"! Joining game '([0-9a-f\-]{36})' place ([0-9]+) at ([0-9\.]+)";
|
||||
private const string GameJoiningUDMUXPattern = @"UDMUX Address = ([0-9\.]+), Port = [0-9]+ \| RCC Server Address = ([0-9\.]+), Port = [0-9]+";
|
||||
private const string GameJoinedEntryPattern = @"serverId: ([0-9\.]+)\|[0-9]+";
|
||||
private const string GameMessageEntryPattern = @"\[BloxstrapRPC\] (.*)";
|
||||
// these entries are technically volatile!
|
||||
// they only get printed depending on their configured FLog level, which could change at any time
|
||||
// while levels being changed is fairly rare, please limit the number of varying number of FLog types you have to use, if possible
|
||||
|
||||
private const string GameJoiningPrivateServerEntry = "[FLog::GameJoinUtil] GameJoinUtil::joinGamePostPrivateServer";
|
||||
private const string GameJoiningReservedServerEntry = "[FLog::GameJoinUtil] GameJoinUtil::initiateTeleportToReservedServer";
|
||||
private const string GameJoiningUniverseEntry = "[FLog::GameJoinLoadTime] Report game_join_loadtime:";
|
||||
private const string GameJoiningUDMUXEntry = "[FLog::Network] UDMUX Address = ";
|
||||
private const string GameJoinedEntry = "[FLog::Network] serverId:";
|
||||
private const string GameDisconnectedEntry = "[FLog::Network] Time to disconnect replication data:";
|
||||
private const string GameTeleportingEntry = "[FLog::SingleSurfaceApp] initiateTeleport";
|
||||
private const string GameLeavingEntry = "[FLog::SingleSurfaceApp] leaveUGCGameInternal";
|
||||
|
||||
private const string GameJoiningEntryPattern = @"! Joining game '([0-9a-f\-]{36})' place ([0-9]+) at ([0-9\.]+)";
|
||||
private const string GameJoiningPrivateServerPattern = @"""accessCode"":""([0-9a-f\-]{36})""";
|
||||
private const string GameJoiningUniversePattern = @"universeid:([0-9]+)";
|
||||
private const string GameJoiningUDMUXPattern = @"UDMUX Address = ([0-9\.]+), Port = [0-9]+ \| RCC Server Address = ([0-9\.]+), Port = [0-9]+";
|
||||
private const string GameJoinedEntryPattern = @"serverId: ([0-9\.]+)\|[0-9]+";
|
||||
private const string GameMessageEntryPattern = @"\[BloxstrapRPC\] (.*)";
|
||||
|
||||
private int _gameClientPid;
|
||||
private int _logEntriesRead = 0;
|
||||
private bool _teleportMarker = false;
|
||||
private bool _reservedTeleportMarker = false;
|
||||
@ -27,34 +32,28 @@
|
||||
public event EventHandler<string>? OnLogEntry;
|
||||
public event EventHandler? OnGameJoin;
|
||||
public event EventHandler? OnGameLeave;
|
||||
public event EventHandler? OnLogOpen;
|
||||
public event EventHandler? OnAppClose;
|
||||
public event EventHandler<Message>? OnRPCMessage;
|
||||
|
||||
private readonly Dictionary<string, string> GeolocationCache = new();
|
||||
private DateTime LastRPCRequest;
|
||||
|
||||
public string LogLocation = null!;
|
||||
|
||||
// these are values to use assuming the player isn't currently in a game
|
||||
// hmm... do i move this to a model?
|
||||
public bool ActivityInGame = false;
|
||||
public long ActivityPlaceId = 0;
|
||||
public string ActivityJobId = "";
|
||||
public string ActivityMachineAddress = "";
|
||||
public bool ActivityMachineUDMUX = false;
|
||||
public bool ActivityIsTeleport = false;
|
||||
public ServerType ActivityServerType = ServerType.Public;
|
||||
public bool InGame = false;
|
||||
|
||||
public ActivityData Data { get; private set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Ordered by newest to oldest
|
||||
/// </summary>
|
||||
public List<ActivityData> History = new();
|
||||
|
||||
public bool IsDisposed = false;
|
||||
|
||||
public ActivityWatcher(int gameClientPid)
|
||||
public async void Start()
|
||||
{
|
||||
_gameClientPid = gameClientPid;
|
||||
}
|
||||
|
||||
public async void StartWatcher()
|
||||
{
|
||||
const string LOG_IDENT = "ActivityWatcher::StartWatcher";
|
||||
const string LOG_IDENT = "ActivityWatcher::Start";
|
||||
|
||||
// okay, here's the process:
|
||||
//
|
||||
@ -84,7 +83,7 @@
|
||||
{
|
||||
logFileInfo = new DirectoryInfo(logDirectory)
|
||||
.GetFiles()
|
||||
.Where(x => x.CreationTime <= DateTime.Now)
|
||||
.Where(x => x.Name.Contains("Player", StringComparison.OrdinalIgnoreCase) && x.CreationTime <= DateTime.Now)
|
||||
.OrderByDescending(x => x.CreationTime)
|
||||
.First();
|
||||
|
||||
@ -95,12 +94,14 @@
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
OnLogOpen?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
LogLocation = logFileInfo.FullName;
|
||||
FileStream logFileStream = logFileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Opened {LogLocation}");
|
||||
|
||||
AutoResetEvent logUpdatedEvent = new(false);
|
||||
FileSystemWatcher logWatcher = new()
|
||||
var logUpdatedEvent = new AutoResetEvent(false);
|
||||
var logWatcher = new FileSystemWatcher()
|
||||
{
|
||||
Path = logDirectory,
|
||||
Filter = Path.GetFileName(logFileInfo.FullName),
|
||||
@ -108,7 +109,7 @@
|
||||
};
|
||||
logWatcher.Changed += (s, e) => logUpdatedEvent.Set();
|
||||
|
||||
using StreamReader sr = new(logFileStream);
|
||||
using var sr = new StreamReader(logFileStream);
|
||||
|
||||
while (!IsDisposed)
|
||||
{
|
||||
@ -117,13 +118,13 @@
|
||||
if (log is null)
|
||||
logUpdatedEvent.WaitOne(250);
|
||||
else
|
||||
ExamineLogEntry(log);
|
||||
ReadLogEntry(log);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExamineLogEntry(string entry)
|
||||
private void ReadLogEntry(string entry)
|
||||
{
|
||||
const string LOG_IDENT = "ActivityWatcher::ExamineLogEntry";
|
||||
const string LOG_IDENT = "ActivityWatcher::ReadLogEntry";
|
||||
|
||||
OnLogEntry?.Invoke(this, entry);
|
||||
|
||||
@ -137,14 +138,38 @@
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Read {_logEntriesRead} log entries");
|
||||
|
||||
if (entry.Contains(GameLeavingEntry))
|
||||
OnAppClose?.Invoke(this, new EventArgs());
|
||||
|
||||
if (!ActivityInGame && ActivityPlaceId == 0)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "User is back into the desktop app");
|
||||
|
||||
OnAppClose?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
if (Data.PlaceId != 0 && !InGame)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "User appears to be leaving from a cancelled/errored join");
|
||||
Data = new();
|
||||
}
|
||||
}
|
||||
|
||||
if (!InGame && Data.PlaceId == 0)
|
||||
{
|
||||
// We are not in a game, nor are in the process of joining one
|
||||
|
||||
if (entry.Contains(GameJoiningPrivateServerEntry))
|
||||
{
|
||||
// we only expect to be joining a private server if we're not already in a game
|
||||
ActivityServerType = ServerType.Private;
|
||||
|
||||
Data.ServerType = ServerType.Private;
|
||||
|
||||
var match = Regex.Match(entry, GameJoiningPrivateServerPattern);
|
||||
|
||||
if (match.Groups.Count != 2)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to assert format for game join private server entry");
|
||||
App.Logger.WriteLine(LOG_IDENT, entry);
|
||||
return;
|
||||
}
|
||||
|
||||
Data.AccessCode = match.Groups[1].Value;
|
||||
}
|
||||
else if (entry.Contains(GameJoiningEntry))
|
||||
{
|
||||
@ -157,80 +182,111 @@
|
||||
return;
|
||||
}
|
||||
|
||||
ActivityInGame = false;
|
||||
ActivityPlaceId = long.Parse(match.Groups[2].Value);
|
||||
ActivityJobId = match.Groups[1].Value;
|
||||
ActivityMachineAddress = match.Groups[3].Value;
|
||||
InGame = false;
|
||||
Data.PlaceId = long.Parse(match.Groups[2].Value);
|
||||
Data.JobId = match.Groups[1].Value;
|
||||
Data.MachineAddress = match.Groups[3].Value;
|
||||
|
||||
if (App.Settings.Prop.ShowServerDetails && Data.MachineAddressValid)
|
||||
_ = Data.QueryServerLocation();
|
||||
|
||||
if (_teleportMarker)
|
||||
{
|
||||
ActivityIsTeleport = true;
|
||||
Data.IsTeleport = true;
|
||||
_teleportMarker = false;
|
||||
}
|
||||
|
||||
if (_reservedTeleportMarker)
|
||||
{
|
||||
ActivityServerType = ServerType.Reserved;
|
||||
Data.ServerType = ServerType.Reserved;
|
||||
_reservedTeleportMarker = false;
|
||||
}
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Joining Game ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})");
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Joining Game ({Data})");
|
||||
}
|
||||
}
|
||||
else if (!ActivityInGame && ActivityPlaceId != 0)
|
||||
else if (!InGame && Data.PlaceId != 0)
|
||||
{
|
||||
if (entry.Contains(GameJoiningUDMUXEntry))
|
||||
{
|
||||
Match match = Regex.Match(entry, GameJoiningUDMUXPattern);
|
||||
// We are not confirmed to be in a game, but we are in the process of joining one
|
||||
|
||||
if (match.Groups.Count != 3 || match.Groups[2].Value != ActivityMachineAddress)
|
||||
if (entry.Contains(GameJoiningUniverseEntry))
|
||||
{
|
||||
var match = Regex.Match(entry, GameJoiningUniversePattern);
|
||||
|
||||
if (match.Groups.Count != 2)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Failed to assert format for game join UDMUX entry");
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to assert format for game join universe entry");
|
||||
App.Logger.WriteLine(LOG_IDENT, entry);
|
||||
return;
|
||||
}
|
||||
|
||||
ActivityMachineAddress = match.Groups[1].Value;
|
||||
ActivityMachineUDMUX = true;
|
||||
Data.UniverseId = long.Parse(match.Groups[1].Value);
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Server is UDMUX protected ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})");
|
||||
if (History.Any())
|
||||
{
|
||||
var lastActivity = History.First();
|
||||
|
||||
if (Data.UniverseId == lastActivity.UniverseId && Data.IsTeleport)
|
||||
Data.RootActivity = lastActivity.RootActivity ?? lastActivity;
|
||||
}
|
||||
}
|
||||
else if (entry.Contains(GameJoiningUDMUXEntry))
|
||||
{
|
||||
var match = Regex.Match(entry, GameJoiningUDMUXPattern);
|
||||
|
||||
if (match.Groups.Count != 3 || match.Groups[2].Value != Data.MachineAddress)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to assert format for game join UDMUX entry");
|
||||
App.Logger.WriteLine(LOG_IDENT, entry);
|
||||
return;
|
||||
}
|
||||
|
||||
Data.MachineAddress = match.Groups[1].Value;
|
||||
|
||||
if (App.Settings.Prop.ShowServerDetails)
|
||||
_ = Data.QueryServerLocation();
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Server is UDMUX protected ({Data})");
|
||||
}
|
||||
else if (entry.Contains(GameJoinedEntry))
|
||||
{
|
||||
Match match = Regex.Match(entry, GameJoinedEntryPattern);
|
||||
|
||||
if (match.Groups.Count != 2 || match.Groups[1].Value != ActivityMachineAddress)
|
||||
if (match.Groups.Count != 2 || match.Groups[1].Value != Data.MachineAddress)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Failed to assert format for game joined entry");
|
||||
App.Logger.WriteLine(LOG_IDENT, entry);
|
||||
return;
|
||||
}
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Joined Game ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})");
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Joined Game ({Data})");
|
||||
|
||||
InGame = true;
|
||||
Data.TimeJoined = DateTime.Now;
|
||||
|
||||
ActivityInGame = true;
|
||||
OnGameJoin?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
else if (ActivityInGame && ActivityPlaceId != 0)
|
||||
else if (InGame && Data.PlaceId != 0)
|
||||
{
|
||||
// We are confirmed to be in a game
|
||||
|
||||
if (entry.Contains(GameDisconnectedEntry))
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Disconnected from Game ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})");
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Disconnected from Game ({Data})");
|
||||
|
||||
ActivityInGame = false;
|
||||
ActivityPlaceId = 0;
|
||||
ActivityJobId = "";
|
||||
ActivityMachineAddress = "";
|
||||
ActivityMachineUDMUX = false;
|
||||
ActivityIsTeleport = false;
|
||||
ActivityServerType = ServerType.Public;
|
||||
Data.TimeLeft = DateTime.Now;
|
||||
History.Insert(0, Data);
|
||||
|
||||
InGame = false;
|
||||
|
||||
Data = new();
|
||||
|
||||
OnGameLeave?.Invoke(this, new EventArgs());
|
||||
}
|
||||
else if (entry.Contains(GameTeleportingEntry))
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Initiating teleport to server ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})");
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Initiating teleport to server ({Data})");
|
||||
_teleportMarker = true;
|
||||
}
|
||||
else if (_teleportMarker && entry.Contains(GameJoiningReservedServerEntry))
|
||||
@ -282,6 +338,35 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.Command == "SetLaunchData")
|
||||
{
|
||||
string? data;
|
||||
|
||||
try
|
||||
{
|
||||
data = message.Data.Deserialize<string>();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization threw an exception)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data is null)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization returned null)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.Length > 200)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Data cannot be longer than 200 characters");
|
||||
return;
|
||||
}
|
||||
|
||||
Data.RPCLaunchData = data;
|
||||
}
|
||||
|
||||
OnRPCMessage?.Invoke(this, message);
|
||||
|
||||
LastRPCRequest = DateTime.Now;
|
||||
@ -289,44 +374,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> GetServerLocation()
|
||||
{
|
||||
const string LOG_IDENT = "ActivityWatcher::GetServerLocation";
|
||||
|
||||
if (GeolocationCache.ContainsKey(ActivityMachineAddress))
|
||||
return GeolocationCache[ActivityMachineAddress];
|
||||
|
||||
try
|
||||
{
|
||||
string location = "";
|
||||
var ipInfo = await Http.GetJson<IPInfoResponse>($"https://ipinfo.io/{ActivityMachineAddress}/json");
|
||||
|
||||
if (ipInfo is null)
|
||||
return $"? ({Resources.Strings.ActivityTracker_LookupFailed})";
|
||||
|
||||
if (string.IsNullOrEmpty(ipInfo.Country))
|
||||
location = "?";
|
||||
else if (ipInfo.City == ipInfo.Region)
|
||||
location = $"{ipInfo.Region}, {ipInfo.Country}";
|
||||
else
|
||||
location = $"{ipInfo.City}, {ipInfo.Region}, {ipInfo.Country}";
|
||||
|
||||
if (!ActivityInGame)
|
||||
return $"? ({Resources.Strings.ActivityTracker_LeftGame})";
|
||||
|
||||
GeolocationCache[ActivityMachineAddress] = location;
|
||||
|
||||
return location;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Failed to get server location for {ActivityMachineAddress}");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
|
||||
return $"? ({Resources.Strings.ActivityTracker_LookupFailed})";
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
IsDisposed = true;
|
||||
|
@ -1,4 +1,6 @@
|
||||
using DiscordRPC;
|
||||
using System.Windows;
|
||||
|
||||
using DiscordRPC;
|
||||
|
||||
namespace Bloxstrap.Integrations
|
||||
{
|
||||
@ -6,18 +8,16 @@ namespace Bloxstrap.Integrations
|
||||
{
|
||||
private readonly DiscordRpcClient _rpcClient = new("1005469189907173486");
|
||||
private readonly ActivityWatcher _activityWatcher;
|
||||
private readonly Queue<Message> _messageQueue = new();
|
||||
|
||||
private DiscordRPC.RichPresence? _currentPresence;
|
||||
private DiscordRPC.RichPresence? _currentPresenceCopy;
|
||||
private Message? _stashedRPCMessage;
|
||||
private DiscordRPC.RichPresence? _originalPresence;
|
||||
|
||||
private bool _visible = true;
|
||||
private long _currentUniverseId;
|
||||
private DateTime? _timeStartedUniverse;
|
||||
|
||||
public DiscordRichPresence(ActivityWatcher activityWatcher)
|
||||
{
|
||||
const string LOG_IDENT = "DiscordRichPresence::DiscordRichPresence";
|
||||
const string LOG_IDENT = "DiscordRichPresence";
|
||||
|
||||
_activityWatcher = activityWatcher;
|
||||
|
||||
@ -47,119 +47,121 @@ namespace Bloxstrap.Integrations
|
||||
_rpcClient.Initialize();
|
||||
}
|
||||
|
||||
public void ProcessRPCMessage(Message message)
|
||||
public void ProcessRPCMessage(Message message, bool implicitUpdate = true)
|
||||
{
|
||||
const string LOG_IDENT = "DiscordRichPresence::ProcessRPCMessage";
|
||||
|
||||
if (message.Command != "SetRichPresence")
|
||||
if (message.Command != "SetRichPresence" && message.Command != "SetLaunchData")
|
||||
return;
|
||||
|
||||
if (_currentPresence is null || _currentPresenceCopy is null)
|
||||
if (_currentPresence is null || _originalPresence is null)
|
||||
{
|
||||
if (_activityWatcher.ActivityInGame)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Presence is not yet set, but is currently in game, stashing presence set request");
|
||||
_stashedRPCMessage = message;
|
||||
return;
|
||||
}
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, "Presence is not set, aborting");
|
||||
App.Logger.WriteLine(LOG_IDENT, "Presence is not set, enqueuing message");
|
||||
_messageQueue.Enqueue(message);
|
||||
return;
|
||||
}
|
||||
|
||||
Models.BloxstrapRPC.RichPresence? presenceData;
|
||||
|
||||
// a lot of repeated code here, could this somehow be cleaned up?
|
||||
|
||||
try
|
||||
if (message.Command == "SetLaunchData")
|
||||
{
|
||||
presenceData = message.Data.Deserialize<Models.BloxstrapRPC.RichPresence>();
|
||||
_currentPresence.Buttons = GetButtons();
|
||||
}
|
||||
catch (Exception)
|
||||
else if (message.Command == "SetRichPresence")
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization threw an exception)");
|
||||
return;
|
||||
}
|
||||
Models.BloxstrapRPC.RichPresence? presenceData;
|
||||
|
||||
if (presenceData is null)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization returned null)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (presenceData.Details is not null)
|
||||
{
|
||||
if (presenceData.Details.Length > 128)
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Details cannot be longer than 128 characters");
|
||||
else if (presenceData.Details == "<reset>")
|
||||
_currentPresence.Details = _currentPresenceCopy.Details;
|
||||
else
|
||||
_currentPresence.Details = presenceData.Details;
|
||||
}
|
||||
|
||||
if (presenceData.State is not null)
|
||||
{
|
||||
if (presenceData.State.Length > 128)
|
||||
App.Logger.WriteLine(LOG_IDENT, $"State cannot be longer than 128 characters");
|
||||
else if (presenceData.State == "<reset>")
|
||||
_currentPresence.State = _currentPresenceCopy.State;
|
||||
else
|
||||
_currentPresence.State = presenceData.State;
|
||||
}
|
||||
|
||||
if (presenceData.TimestampStart == 0)
|
||||
_currentPresence.Timestamps.Start = null;
|
||||
else if (presenceData.TimestampStart is not null)
|
||||
_currentPresence.Timestamps.StartUnixMilliseconds = presenceData.TimestampStart * 1000;
|
||||
|
||||
if (presenceData.TimestampEnd == 0)
|
||||
_currentPresence.Timestamps.End = null;
|
||||
else if (presenceData.TimestampEnd is not null)
|
||||
_currentPresence.Timestamps.EndUnixMilliseconds = presenceData.TimestampEnd * 1000;
|
||||
|
||||
if (presenceData.SmallImage is not null)
|
||||
{
|
||||
if (presenceData.SmallImage.Clear)
|
||||
try
|
||||
{
|
||||
_currentPresence.Assets.SmallImageKey = "";
|
||||
presenceData = message.Data.Deserialize<Models.BloxstrapRPC.RichPresence>();
|
||||
}
|
||||
else if (presenceData.SmallImage.Reset)
|
||||
catch (Exception)
|
||||
{
|
||||
_currentPresence.Assets.SmallImageText = _currentPresenceCopy.Assets.SmallImageText;
|
||||
_currentPresence.Assets.SmallImageKey = _currentPresenceCopy.Assets.SmallImageKey;
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization threw an exception)");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (presenceData.SmallImage.AssetId is not null)
|
||||
_currentPresence.Assets.SmallImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.SmallImage.AssetId}";
|
||||
|
||||
if (presenceData.SmallImage.HoverText is not null)
|
||||
_currentPresence.Assets.SmallImageText = presenceData.SmallImage.HoverText;
|
||||
if (presenceData is null)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to parse message! (JSON deserialization returned null)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (presenceData.Details is not null)
|
||||
{
|
||||
if (presenceData.Details.Length > 128)
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Details cannot be longer than 128 characters");
|
||||
else if (presenceData.Details == "<reset>")
|
||||
_currentPresence.Details = _originalPresence.Details;
|
||||
else
|
||||
_currentPresence.Details = presenceData.Details;
|
||||
}
|
||||
|
||||
if (presenceData.State is not null)
|
||||
{
|
||||
if (presenceData.State.Length > 128)
|
||||
App.Logger.WriteLine(LOG_IDENT, $"State cannot be longer than 128 characters");
|
||||
else if (presenceData.State == "<reset>")
|
||||
_currentPresence.State = _originalPresence.State;
|
||||
else
|
||||
_currentPresence.State = presenceData.State;
|
||||
}
|
||||
|
||||
if (presenceData.TimestampStart == 0)
|
||||
_currentPresence.Timestamps.Start = null;
|
||||
else if (presenceData.TimestampStart is not null)
|
||||
_currentPresence.Timestamps.StartUnixMilliseconds = presenceData.TimestampStart * 1000;
|
||||
|
||||
if (presenceData.TimestampEnd == 0)
|
||||
_currentPresence.Timestamps.End = null;
|
||||
else if (presenceData.TimestampEnd is not null)
|
||||
_currentPresence.Timestamps.EndUnixMilliseconds = presenceData.TimestampEnd * 1000;
|
||||
|
||||
if (presenceData.SmallImage is not null)
|
||||
{
|
||||
if (presenceData.SmallImage.Clear)
|
||||
{
|
||||
_currentPresence.Assets.SmallImageKey = "";
|
||||
}
|
||||
else if (presenceData.SmallImage.Reset)
|
||||
{
|
||||
_currentPresence.Assets.SmallImageText = _originalPresence.Assets.SmallImageText;
|
||||
_currentPresence.Assets.SmallImageKey = _originalPresence.Assets.SmallImageKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (presenceData.SmallImage.AssetId is not null)
|
||||
_currentPresence.Assets.SmallImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.SmallImage.AssetId}";
|
||||
|
||||
if (presenceData.SmallImage.HoverText is not null)
|
||||
_currentPresence.Assets.SmallImageText = presenceData.SmallImage.HoverText;
|
||||
}
|
||||
}
|
||||
|
||||
if (presenceData.LargeImage is not null)
|
||||
{
|
||||
if (presenceData.LargeImage.Clear)
|
||||
{
|
||||
_currentPresence.Assets.LargeImageKey = "";
|
||||
}
|
||||
else if (presenceData.LargeImage.Reset)
|
||||
{
|
||||
_currentPresence.Assets.LargeImageText = _originalPresence.Assets.LargeImageText;
|
||||
_currentPresence.Assets.LargeImageKey = _originalPresence.Assets.LargeImageKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (presenceData.LargeImage.AssetId is not null)
|
||||
_currentPresence.Assets.LargeImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.LargeImage.AssetId}";
|
||||
|
||||
if (presenceData.LargeImage.HoverText is not null)
|
||||
_currentPresence.Assets.LargeImageText = presenceData.LargeImage.HoverText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (presenceData.LargeImage is not null)
|
||||
{
|
||||
if (presenceData.LargeImage.Clear)
|
||||
{
|
||||
_currentPresence.Assets.LargeImageKey = "";
|
||||
}
|
||||
else if (presenceData.LargeImage.Reset)
|
||||
{
|
||||
_currentPresence.Assets.LargeImageText = _currentPresenceCopy.Assets.LargeImageText;
|
||||
_currentPresence.Assets.LargeImageKey = _currentPresenceCopy.Assets.LargeImageKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (presenceData.LargeImage.AssetId is not null)
|
||||
_currentPresence.Assets.LargeImageKey = $"https://assetdelivery.roblox.com/v1/asset/?id={presenceData.LargeImage.AssetId}";
|
||||
|
||||
if (presenceData.LargeImage.HoverText is not null)
|
||||
_currentPresence.Assets.LargeImageText = presenceData.LargeImage.HoverText;
|
||||
}
|
||||
}
|
||||
|
||||
UpdatePresence();
|
||||
if (implicitUpdate)
|
||||
UpdatePresence();
|
||||
}
|
||||
|
||||
public void SetVisibility(bool visible)
|
||||
@ -178,124 +180,131 @@ namespace Bloxstrap.Integrations
|
||||
{
|
||||
const string LOG_IDENT = "DiscordRichPresence::SetCurrentGame";
|
||||
|
||||
if (!_activityWatcher.ActivityInGame)
|
||||
if (!_activityWatcher.InGame)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Not in game, clearing presence");
|
||||
|
||||
_currentPresence = _currentPresenceCopy = null;
|
||||
_stashedRPCMessage = null;
|
||||
_currentPresence = _originalPresence = null;
|
||||
_messageQueue.Clear();
|
||||
|
||||
UpdatePresence();
|
||||
return true;
|
||||
}
|
||||
|
||||
string icon = "roblox";
|
||||
long placeId = _activityWatcher.ActivityPlaceId;
|
||||
|
||||
var activity = _activityWatcher.Data;
|
||||
long placeId = activity.PlaceId;
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Setting presence for Place ID {placeId}");
|
||||
|
||||
var universeIdResponse = await Http.GetJson<UniverseIdResponse>($"https://apis.roblox.com/universes/v1/places/{placeId}/universe");
|
||||
if (universeIdResponse is null)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Could not get Universe ID!");
|
||||
return false;
|
||||
}
|
||||
|
||||
long universeId = universeIdResponse.UniverseId;
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Got Universe ID as {universeId}");
|
||||
|
||||
// preserve time spent playing if we're teleporting between places in the same universe
|
||||
if (_timeStartedUniverse is null || !_activityWatcher.ActivityIsTeleport || universeId != _currentUniverseId)
|
||||
_timeStartedUniverse = DateTime.UtcNow;
|
||||
var timeStarted = activity.TimeJoined;
|
||||
|
||||
_currentUniverseId = universeId;
|
||||
if (activity.RootActivity is not null)
|
||||
timeStarted = activity.RootActivity.TimeJoined;
|
||||
|
||||
var gameDetailResponse = await Http.GetJson<ApiArrayResponse<GameDetailResponse>>($"https://games.roblox.com/v1/games?universeIds={universeId}");
|
||||
if (gameDetailResponse is null || !gameDetailResponse.Data.Any())
|
||||
if (activity.UniverseDetails is null)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Could not get Universe info!");
|
||||
return false;
|
||||
}
|
||||
|
||||
GameDetailResponse universeDetails = gameDetailResponse.Data.ToArray()[0];
|
||||
App.Logger.WriteLine(LOG_IDENT, "Got Universe details");
|
||||
|
||||
var universeThumbnailResponse = await Http.GetJson<ApiArrayResponse<ThumbnailResponse>>($"https://thumbnails.roblox.com/v1/games/icons?universeIds={universeId}&returnPolicy=PlaceHolder&size=512x512&format=Png&isCircular=false");
|
||||
if (universeThumbnailResponse is null || !universeThumbnailResponse.Data.Any())
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Could not get Universe thumbnail info!");
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = universeThumbnailResponse.Data.ToArray()[0].ImageUrl;
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Got Universe thumbnail as {icon}");
|
||||
}
|
||||
|
||||
List<Button> buttons = new();
|
||||
|
||||
if (!App.Settings.Prop.HideRPCButtons && _activityWatcher.ActivityServerType == ServerType.Public)
|
||||
{
|
||||
buttons.Add(new Button
|
||||
try
|
||||
{
|
||||
Label = "Join server",
|
||||
Url = $"roblox://experiences/start?placeId={placeId}&gameInstanceId={_activityWatcher.ActivityJobId}"
|
||||
});
|
||||
await UniverseDetails.FetchSingle(activity.UniverseId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
Frontend.ShowMessageBox($"{Strings.ActivityWatcher_RichPresenceLoadFailed}\n\n{ex.Message}", MessageBoxImage.Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
activity.UniverseDetails = UniverseDetails.LoadFromCache(activity.UniverseId);
|
||||
}
|
||||
|
||||
buttons.Add(new Button
|
||||
{
|
||||
Label = "See game page",
|
||||
Url = $"https://www.roblox.com/games/{placeId}"
|
||||
});
|
||||
var universeDetails = activity.UniverseDetails!;
|
||||
|
||||
if (!_activityWatcher.ActivityInGame || placeId != _activityWatcher.ActivityPlaceId)
|
||||
icon = universeDetails.Thumbnail.ImageUrl;
|
||||
|
||||
if (!_activityWatcher.InGame || placeId != activity.PlaceId)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Aborting presence set because game activity has changed");
|
||||
return false;
|
||||
}
|
||||
|
||||
string status = _activityWatcher.ActivityServerType switch
|
||||
string status = _activityWatcher.Data.ServerType switch
|
||||
{
|
||||
ServerType.Private => "In a private server",
|
||||
ServerType.Reserved => "In a reserved server",
|
||||
_ => $"by {universeDetails.Creator.Name}" + (universeDetails.Creator.HasVerifiedBadge ? " ☑️" : ""),
|
||||
_ => $"by {universeDetails.Data.Creator.Name}" + (universeDetails.Data.Creator.HasVerifiedBadge ? " ☑️" : ""),
|
||||
};
|
||||
|
||||
if (universeDetails.Name.Length < 2)
|
||||
universeDetails.Name = $"{universeDetails.Name}\x2800\x2800\x2800";
|
||||
string universeName = universeDetails.Data.Name;
|
||||
|
||||
if (universeName.Length < 2)
|
||||
universeName = $"{universeName}\x2800\x2800\x2800";
|
||||
|
||||
_currentPresence = new DiscordRPC.RichPresence
|
||||
{
|
||||
Details = $"Playing {universeDetails.Name}",
|
||||
Details = universeName,
|
||||
State = status,
|
||||
Timestamps = new Timestamps { Start = _timeStartedUniverse },
|
||||
Buttons = buttons.ToArray(),
|
||||
Timestamps = new Timestamps { Start = timeStarted.ToUniversalTime() },
|
||||
Buttons = GetButtons(),
|
||||
Assets = new Assets
|
||||
{
|
||||
LargeImageKey = icon,
|
||||
LargeImageText = universeDetails.Name,
|
||||
LargeImageText = universeName,
|
||||
SmallImageKey = "roblox",
|
||||
SmallImageText = "Roblox"
|
||||
}
|
||||
};
|
||||
|
||||
// this is used for configuration from BloxstrapRPC
|
||||
_currentPresenceCopy = _currentPresence.Clone();
|
||||
_originalPresence = _currentPresence.Clone();
|
||||
|
||||
if (_stashedRPCMessage is not null)
|
||||
if (_messageQueue.Any())
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Found stashed RPC message, invoking presence set command now");
|
||||
ProcessRPCMessage(_stashedRPCMessage);
|
||||
_stashedRPCMessage = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdatePresence();
|
||||
App.Logger.WriteLine(LOG_IDENT, "Processing queued messages");
|
||||
ProcessRPCMessage(_messageQueue.Dequeue(), false);
|
||||
}
|
||||
|
||||
UpdatePresence();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Button[] GetButtons()
|
||||
{
|
||||
var buttons = new List<Button>();
|
||||
|
||||
var data = _activityWatcher.Data;
|
||||
|
||||
if (!App.Settings.Prop.HideRPCButtons)
|
||||
{
|
||||
bool show = false;
|
||||
|
||||
if (data.ServerType == ServerType.Public)
|
||||
show = true;
|
||||
else if (data.ServerType == ServerType.Reserved && !String.IsNullOrEmpty(data.RPCLaunchData))
|
||||
show = true;
|
||||
|
||||
if (show)
|
||||
{
|
||||
buttons.Add(new Button
|
||||
{
|
||||
Label = "Join server",
|
||||
Url = data.GetInviteDeeplink()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buttons.Add(new Button
|
||||
{
|
||||
Label = "See game page",
|
||||
Url = $"https://www.roblox.com/games/{data.PlaceId}"
|
||||
});
|
||||
|
||||
return buttons.ToArray();
|
||||
}
|
||||
|
||||
public void UpdatePresence()
|
||||
{
|
||||
const string LOG_IDENT = "DiscordRichPresence::UpdatePresence";
|
||||
|
@ -4,13 +4,17 @@ namespace Bloxstrap
|
||||
{
|
||||
public class JsonManager<T> where T : class, new()
|
||||
{
|
||||
public T OriginalProp { get; set; } = new();
|
||||
|
||||
public T Prop { get; set; } = new();
|
||||
|
||||
public virtual string FileLocation => Path.Combine(Paths.Base, $"{typeof(T).Name}.json");
|
||||
public virtual string ClassName => typeof(T).Name;
|
||||
|
||||
private string LOG_IDENT_CLASS => $"JsonManager<{typeof(T).Name}>";
|
||||
public virtual string FileLocation => Path.Combine(Paths.Base, $"{ClassName}.json");
|
||||
|
||||
public virtual void Load()
|
||||
public virtual string LOG_IDENT_CLASS => $"JsonManager<{ClassName}>";
|
||||
|
||||
public virtual void Load(bool alertFailure = true)
|
||||
{
|
||||
string LOG_IDENT = $"{LOG_IDENT_CLASS}::Load";
|
||||
|
||||
@ -30,7 +34,22 @@ namespace Bloxstrap
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to load!");
|
||||
App.Logger.WriteLine(LOG_IDENT, $"{ex.Message}");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
|
||||
if (alertFailure)
|
||||
{
|
||||
string message = "";
|
||||
|
||||
if (ClassName == nameof(Settings))
|
||||
message = Strings.JsonManager_SettingsLoadFailed;
|
||||
else if (ClassName == nameof(FastFlagManager))
|
||||
message = Strings.JsonManager_FastFlagsLoadFailed;
|
||||
|
||||
if (!String.IsNullOrEmpty(message))
|
||||
Frontend.ShowMessageBox($"{message}\n\n{ex.Message}", System.Windows.MessageBoxImage.Warning);
|
||||
}
|
||||
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +60,21 @@ namespace Bloxstrap
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Saving to {FileLocation}...");
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(FileLocation)!);
|
||||
File.WriteAllText(FileLocation, JsonSerializer.Serialize(Prop, new JsonSerializerOptions { WriteIndented = true }));
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(FileLocation, JsonSerializer.Serialize(Prop, new JsonSerializerOptions { WriteIndented = true }));
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to save");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
|
||||
string errorMessage = string.Format(Resources.Strings.Bootstrapper_JsonManagerSaveFailed, ClassName, ex.Message);
|
||||
Frontend.ShowMessageBox(errorMessage, System.Windows.MessageBoxImage.Warning);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, "Save complete!");
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
using Bloxstrap.UI.Elements.Dialogs;
|
||||
|
||||
using Microsoft.Win32;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
|
||||
using Bloxstrap.UI.Elements.Dialogs;
|
||||
|
||||
namespace Bloxstrap
|
||||
{
|
||||
public static class LaunchHandler
|
||||
@ -19,6 +18,7 @@ namespace Bloxstrap
|
||||
break;
|
||||
|
||||
case NextAction.LaunchRoblox:
|
||||
App.LaunchSettings.RobloxLaunchMode = LaunchMode.Player;
|
||||
LaunchRoblox();
|
||||
break;
|
||||
|
||||
@ -42,6 +42,8 @@ namespace Bloxstrap
|
||||
LaunchRoblox();
|
||||
else if (!App.LaunchSettings.QuietFlag.Active)
|
||||
LaunchMenu();
|
||||
else
|
||||
App.Terminate();
|
||||
}
|
||||
|
||||
public static void LaunchInstaller()
|
||||
@ -51,6 +53,7 @@ namespace Bloxstrap
|
||||
if (!interlock.IsAcquired)
|
||||
{
|
||||
Frontend.ShowMessageBox(Strings.Dialog_AlreadyRunning_Installer, MessageBoxImage.Stop);
|
||||
App.Terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -95,6 +98,7 @@ namespace Bloxstrap
|
||||
if (!interlock.IsAcquired)
|
||||
{
|
||||
Frontend.ShowMessageBox(Strings.Dialog_AlreadyRunning_Uninstaller, MessageBoxImage.Stop);
|
||||
App.Terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -115,11 +119,16 @@ namespace Bloxstrap
|
||||
}
|
||||
|
||||
if (!confirmed)
|
||||
{
|
||||
App.Terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
Installer.DoUninstall(keepData);
|
||||
|
||||
Frontend.ShowMessageBox(Strings.Bootstrapper_SuccessfullyUninstalled, MessageBoxImage.Information);
|
||||
|
||||
App.Terminate();
|
||||
}
|
||||
|
||||
public static void LaunchSettings()
|
||||
@ -131,7 +140,9 @@ namespace Bloxstrap
|
||||
if (interlock.IsAcquired)
|
||||
{
|
||||
bool showAlreadyRunningWarning = Process.GetProcessesByName(App.ProjectName).Length > 1;
|
||||
new UI.Elements.Settings.MainWindow(showAlreadyRunningWarning).ShowDialog();
|
||||
|
||||
var window = new UI.Elements.Settings.MainWindow(showAlreadyRunningWarning);
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -156,7 +167,6 @@ namespace Bloxstrap
|
||||
{
|
||||
const string LOG_IDENT = "LaunchHandler::LaunchRoblox";
|
||||
|
||||
|
||||
if (!File.Exists(Path.Combine(Paths.System, "mfplat.dll")))
|
||||
{
|
||||
Frontend.ShowMessageBox(Strings.Bootstrapper_WMFNotFound, MessageBoxImage.Error);
|
||||
@ -167,15 +177,6 @@ namespace Bloxstrap
|
||||
App.Terminate(ErrorCode.ERROR_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
bool installWebView2 = false;
|
||||
{
|
||||
using var hklmKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}");
|
||||
using var hkcuKey = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}");
|
||||
|
||||
if (hklmKey is null && hkcuKey is null)
|
||||
installWebView2 = Frontend.ShowMessageBox(Strings.Bootstrapper_WebView2NotFound, MessageBoxImage.Warning, MessageBoxButton.YesNo, MessageBoxResult.Yes) == MessageBoxResult.Yes;
|
||||
}
|
||||
|
||||
if (App.Settings.Prop.ConfirmLaunches && Mutex.TryOpenExisting("ROBLOX_singletonMutex", out var _))
|
||||
{
|
||||
// this currently doesn't work very well since it relies on checking the existence of the singleton mutex
|
||||
@ -191,11 +192,9 @@ namespace Bloxstrap
|
||||
}
|
||||
}
|
||||
|
||||
App.NotifyIcon = new();
|
||||
|
||||
// start bootstrapper and show the bootstrapper modal if we're not running silently
|
||||
App.Logger.WriteLine(LOG_IDENT, "Initializing bootstrapper");
|
||||
var bootstrapper = new Bootstrapper(installWebView2);
|
||||
var bootstrapper = new Bootstrapper();
|
||||
IBootstrapperDialog? dialog = null;
|
||||
|
||||
if (!App.LaunchSettings.QuietFlag.Active)
|
||||
@ -206,45 +205,53 @@ namespace Bloxstrap
|
||||
dialog.Bootstrapper = bootstrapper;
|
||||
}
|
||||
|
||||
Task bootstrapperTask = Task.Run(async () => await bootstrapper.Run()).ContinueWith(t =>
|
||||
Task.Run(bootstrapper.Run).ContinueWith(t =>
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Bootstrapper task has finished");
|
||||
|
||||
// notifyicon is blocking main thread, must be disposed here
|
||||
App.NotifyIcon?.Dispose();
|
||||
|
||||
if (t.IsFaulted)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "An exception occurred when running the bootstrapper");
|
||||
|
||||
if (t.Exception is null)
|
||||
return;
|
||||
if (t.Exception is not null)
|
||||
App.FinalizeExceptionHandling(t.Exception);
|
||||
}
|
||||
|
||||
App.Logger.WriteException(LOG_IDENT, t.Exception);
|
||||
|
||||
Exception exception = t.Exception;
|
||||
|
||||
#if !DEBUG
|
||||
if (t.Exception.GetType().ToString() == "System.AggregateException")
|
||||
exception = t.Exception.InnerException!;
|
||||
#endif
|
||||
|
||||
App.FinalizeExceptionHandling(exception, false);
|
||||
App.Terminate();
|
||||
});
|
||||
|
||||
// this ordering is very important as all wpf windows are shown as modal dialogs, mess it up and you'll end up blocking input to one of them
|
||||
dialog?.ShowBootstrapper();
|
||||
|
||||
if (!App.LaunchSettings.NoLaunchFlag.Active && App.Settings.Prop.EnableActivityTracking)
|
||||
App.NotifyIcon?.InitializeContextMenu();
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, "Waiting for bootstrapper task to finish");
|
||||
|
||||
bootstrapperTask.Wait();
|
||||
}
|
||||
|
||||
public static void LaunchWatcher()
|
||||
{
|
||||
const string LOG_IDENT = "LaunchHandler::LaunchWatcher";
|
||||
|
||||
// this whole topology is a bit confusing, bear with me:
|
||||
// main thread: strictly UI only, handles showing of the notification area icon, context menu, server details dialog
|
||||
// - server information task: queries server location, invoked if either the explorer notification is shown or the server details dialog is opened
|
||||
// - discord rpc thread: handles rpc connection with discord
|
||||
// - discord rich presence tasks: handles querying and displaying of game information, invoked on activity watcher events
|
||||
// - watcher task: runs activity watcher + waiting for roblox to close, terminates when it has
|
||||
|
||||
var watcher = new Watcher();
|
||||
|
||||
Task.Run(watcher.Run).ContinueWith(t =>
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Watcher task has finished");
|
||||
|
||||
watcher.Dispose();
|
||||
|
||||
if (t.IsFaulted)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "An exception occurred when running the watcher");
|
||||
|
||||
if (t.Exception is not null)
|
||||
App.FinalizeExceptionHandling(t.Exception);
|
||||
}
|
||||
|
||||
App.Terminate();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,13 @@ namespace Bloxstrap
|
||||
|
||||
public LaunchFlag StudioFlag { get; } = new("studio");
|
||||
|
||||
public LaunchMode RobloxLaunchMode { get; private set; } = LaunchMode.None;
|
||||
#if DEBUG
|
||||
public bool BypassUpdateCheck => true;
|
||||
#else
|
||||
public bool BypassUpdateCheck => UninstallFlag.Active || WatcherFlag.Active;
|
||||
#endif
|
||||
|
||||
public LaunchMode RobloxLaunchMode { get; set; } = LaunchMode.None;
|
||||
|
||||
public string RobloxLaunchArgs { get; private set; } = "";
|
||||
|
||||
@ -37,7 +43,7 @@ namespace Bloxstrap
|
||||
/// </summary>
|
||||
public string[] Args { get; private set; }
|
||||
|
||||
private Dictionary<string, LaunchFlag> _flagMap = new();
|
||||
private readonly Dictionary<string, LaunchFlag> _flagMap = new();
|
||||
|
||||
public LaunchSettings(string[] args)
|
||||
{
|
||||
@ -68,7 +74,7 @@ namespace Bloxstrap
|
||||
|
||||
string identifier = arg[1..];
|
||||
|
||||
if (_flagMap[identifier] is not LaunchFlag flag)
|
||||
if (!_flagMap.TryGetValue(identifier, out LaunchFlag? flag) || flag is null)
|
||||
continue;
|
||||
|
||||
flag.Active = true;
|
||||
|
@ -7,7 +7,7 @@
|
||||
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
||||
private FileStream? _filestream;
|
||||
|
||||
public readonly List<string> Backlog = new();
|
||||
public readonly List<string> History = new();
|
||||
public bool Initialized = false;
|
||||
public bool NoWriteMode = false;
|
||||
public string? FileLocation;
|
||||
@ -16,8 +16,7 @@
|
||||
{
|
||||
const string LOG_IDENT = "Logger::Initialize";
|
||||
|
||||
// TODO: <Temp>/Bloxstrap/Logs/
|
||||
string directory = useTempDir ? Path.Combine(Paths.LocalAppData, "Temp") : Path.Combine(Paths.Base, "Logs");
|
||||
string directory = useTempDir ? Path.Combine(Paths.TempLogs) : Path.Combine(Paths.Base, "Logs");
|
||||
string timestamp = DateTime.UtcNow.ToString("yyyyMMdd'T'HHmmss'Z'");
|
||||
string filename = $"{App.ProjectName}_{timestamp}.log";
|
||||
string location = Path.Combine(directory, filename);
|
||||
@ -55,7 +54,7 @@
|
||||
WriteLine(LOG_IDENT, $"Failed to initialize because Bloxstrap cannot write to {directory}");
|
||||
|
||||
Frontend.ShowMessageBox(
|
||||
String.Format(Resources.Strings.Logger_NoWriteMode, directory),
|
||||
String.Format(Strings.Logger_NoWriteMode, directory),
|
||||
System.Windows.MessageBoxImage.Warning,
|
||||
System.Windows.MessageBoxButton.OK
|
||||
);
|
||||
@ -68,8 +67,8 @@
|
||||
|
||||
Initialized = true;
|
||||
|
||||
if (Backlog.Count > 0)
|
||||
WriteToLog(string.Join("\r\n", Backlog));
|
||||
if (History.Count > 0)
|
||||
WriteToLog(string.Join("\r\n", History));
|
||||
|
||||
WriteLine(LOG_IDENT, "Finished initializing!");
|
||||
|
||||
@ -102,10 +101,12 @@
|
||||
{
|
||||
string timestamp = DateTime.UtcNow.ToString("s") + "Z";
|
||||
string outcon = $"{timestamp} {message}";
|
||||
string outlog = outcon.Replace(Paths.UserProfile, "%UserProfile%");
|
||||
string outlog = outcon.Replace(Paths.UserProfile, "%UserProfile%", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
Debug.WriteLine(outcon);
|
||||
WriteToLog(outlog);
|
||||
|
||||
History.Add(outlog);
|
||||
}
|
||||
|
||||
public void WriteLine(string identifier, string message) => WriteLine($"[{identifier}] {message}");
|
||||
@ -122,10 +123,7 @@
|
||||
private async void WriteToLog(string message)
|
||||
{
|
||||
if (!Initialized)
|
||||
{
|
||||
Backlog.Add(message);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
13
Bloxstrap/Models/APIs/Config/Supporter.cs
Normal file
13
Bloxstrap/Models/APIs/Config/Supporter.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Bloxstrap.Models.APIs.Config
|
||||
{
|
||||
public class Supporter
|
||||
{
|
||||
[JsonPropertyName("imageAsset")]
|
||||
public string ImageAsset { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
public string Image => $"https://raw.githubusercontent.com/bloxstraplabs/config/main/assets/{ImageAsset}";
|
||||
}
|
||||
}
|
11
Bloxstrap/Models/APIs/Config/SupporterData.cs
Normal file
11
Bloxstrap/Models/APIs/Config/SupporterData.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Bloxstrap.Models.APIs.Config
|
||||
{
|
||||
public class SupporterData
|
||||
{
|
||||
[JsonPropertyName("columns")]
|
||||
public int Columns { get; set; }
|
||||
|
||||
[JsonPropertyName("supporters")]
|
||||
public List<Supporter> Supporters { get; set; } = null!;
|
||||
}
|
||||
}
|
8
Bloxstrap/Models/APIs/GitHub/GitHubReleaseAsset.cs
Normal file
8
Bloxstrap/Models/APIs/GitHub/GitHubReleaseAsset.cs
Normal file
@ -0,0 +1,8 @@
|
||||
public class GithubReleaseAsset
|
||||
{
|
||||
[JsonPropertyName("browser_download_url")]
|
||||
public string BrowserDownloadUrl { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = null!;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace Bloxstrap.Models
|
||||
namespace Bloxstrap.Models.APIs.GitHub
|
||||
{
|
||||
public class GithubRelease
|
||||
{
|
||||
@ -17,13 +17,4 @@
|
||||
[JsonPropertyName("assets")]
|
||||
public List<GithubReleaseAsset>? Assets { get; set; }
|
||||
}
|
||||
|
||||
public class GithubReleaseAsset
|
||||
{
|
||||
[JsonPropertyName("browser_download_url")]
|
||||
public string BrowserDownloadUrl { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = null!;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace Bloxstrap.Models
|
||||
namespace Bloxstrap.Models.APIs
|
||||
{
|
||||
public class IPInfoResponse
|
||||
{
|
@ -1,4 +1,4 @@
|
||||
namespace Bloxstrap.Models.RobloxApi
|
||||
namespace Bloxstrap.Models.APIs.Roblox
|
||||
{
|
||||
/// <summary>
|
||||
/// Roblox.Web.WebAPI.Models.ApiArrayResponse
|
@ -1,4 +1,4 @@
|
||||
namespace Bloxstrap.Models
|
||||
namespace Bloxstrap.Models.APIs.Roblox
|
||||
{
|
||||
public class ClientFlagSettings
|
||||
{
|
@ -1,4 +1,4 @@
|
||||
namespace Bloxstrap.Models
|
||||
namespace Bloxstrap.Models.APIs.Roblox
|
||||
{
|
||||
public class ClientVersion
|
||||
{
|
@ -1,4 +1,4 @@
|
||||
namespace Bloxstrap.Models.RobloxApi
|
||||
namespace Bloxstrap.Models.APIs.Roblox
|
||||
{
|
||||
/// <summary>
|
||||
/// Roblox.Games.Api.Models.Response.GameCreator
|
@ -1,4 +1,4 @@
|
||||
namespace Bloxstrap.Models.RobloxApi
|
||||
namespace Bloxstrap.Models.APIs.Roblox
|
||||
{
|
||||
|
||||
/// <summary>
|
@ -1,4 +1,4 @@
|
||||
namespace Bloxstrap.Models.RobloxApi
|
||||
namespace Bloxstrap.Models.APIs.Roblox
|
||||
{
|
||||
/// <summary>
|
||||
/// Roblox.Web.Responses.Thumbnails.ThumbnailResponse
|
@ -1,4 +1,4 @@
|
||||
namespace Bloxstrap.Models.RobloxApi
|
||||
namespace Bloxstrap.Models.APIs.Roblox
|
||||
{
|
||||
// lmao its just one property
|
||||
public class UniverseIdResponse
|
149
Bloxstrap/Models/Entities/ActivityData.cs
Normal file
149
Bloxstrap/Models/Entities/ActivityData.cs
Normal file
@ -0,0 +1,149 @@
|
||||
using System.Web;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using Bloxstrap.Models.APIs;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Bloxstrap.Models.Entities
|
||||
{
|
||||
public class ActivityData
|
||||
{
|
||||
private long _universeId = 0;
|
||||
|
||||
/// <summary>
|
||||
/// If the current activity stems from an in-universe teleport, then this will be
|
||||
/// set to the activity that corresponds to the initial game join
|
||||
/// </summary>
|
||||
public ActivityData? RootActivity;
|
||||
|
||||
public long UniverseId
|
||||
{
|
||||
get => _universeId;
|
||||
set
|
||||
{
|
||||
_universeId = value;
|
||||
UniverseDetails.LoadFromCache(value);
|
||||
}
|
||||
}
|
||||
|
||||
public long PlaceId { get; set; } = 0;
|
||||
|
||||
public string JobId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// This will be empty unless the server joined is a private server
|
||||
/// </summary>
|
||||
public string AccessCode { get; set; } = string.Empty;
|
||||
|
||||
public string MachineAddress { get; set; } = string.Empty;
|
||||
|
||||
public bool MachineAddressValid => !string.IsNullOrEmpty(MachineAddress) && !MachineAddress.StartsWith("10.");
|
||||
|
||||
public bool IsTeleport { get; set; } = false;
|
||||
|
||||
public ServerType ServerType { get; set; } = ServerType.Public;
|
||||
|
||||
public DateTime TimeJoined { get; set; }
|
||||
|
||||
public DateTime? TimeLeft { get; set; }
|
||||
|
||||
// everything below here is optional strictly for bloxstraprpc, discord rich presence, or game history
|
||||
|
||||
/// <summary>
|
||||
/// This is intended only for other people to use, i.e. context menu invite link, rich presence joining
|
||||
/// </summary>
|
||||
public string RPCLaunchData { get; set; } = string.Empty;
|
||||
|
||||
public UniverseDetails? UniverseDetails { get; set; }
|
||||
|
||||
public string GameHistoryDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
string desc = string.Format("{0} • {1} - {2}", UniverseDetails?.Data.Creator.Name, TimeJoined.ToString("h:mm tt"), TimeLeft?.ToString("h:mm tt"));
|
||||
|
||||
if (ServerType != ServerType.Public)
|
||||
desc += " • " + ServerType.ToTranslatedString();
|
||||
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand RejoinServerCommand => new RelayCommand(RejoinServer);
|
||||
|
||||
private SemaphoreSlim serverQuerySemaphore = new(1, 1);
|
||||
|
||||
public string GetInviteDeeplink(bool launchData = true)
|
||||
{
|
||||
string deeplink = $"roblox://experiences/start?placeId={PlaceId}";
|
||||
|
||||
if (ServerType == ServerType.Private)
|
||||
deeplink += "&accessCode=" + AccessCode;
|
||||
else
|
||||
deeplink += "&gameInstanceId=" + JobId;
|
||||
|
||||
if (launchData && !string.IsNullOrEmpty(RPCLaunchData))
|
||||
deeplink += "&launchData=" + HttpUtility.UrlEncode(RPCLaunchData);
|
||||
|
||||
return deeplink;
|
||||
}
|
||||
|
||||
public async Task<string?> QueryServerLocation()
|
||||
{
|
||||
const string LOG_IDENT = "ActivityData::QueryServerLocation";
|
||||
|
||||
if (!MachineAddressValid)
|
||||
throw new InvalidOperationException($"Machine address is invalid ({MachineAddress})");
|
||||
|
||||
await serverQuerySemaphore.WaitAsync();
|
||||
|
||||
if (GlobalCache.ServerLocation.TryGetValue(MachineAddress, out string? location))
|
||||
{
|
||||
serverQuerySemaphore.Release();
|
||||
return location;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var ipInfo = await Http.GetJson<IPInfoResponse>($"https://ipinfo.io/{MachineAddress}/json");
|
||||
|
||||
if (string.IsNullOrEmpty(ipInfo.City))
|
||||
throw new InvalidHTTPResponseException("Reported city was blank");
|
||||
|
||||
if (ipInfo.City == ipInfo.Region)
|
||||
location = $"{ipInfo.Region}, {ipInfo.Country}";
|
||||
else
|
||||
location = $"{ipInfo.City}, {ipInfo.Region}, {ipInfo.Country}";
|
||||
|
||||
GlobalCache.ServerLocation[MachineAddress] = location;
|
||||
serverQuerySemaphore.Release();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Failed to get server location for {MachineAddress}");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
|
||||
GlobalCache.ServerLocation[MachineAddress] = location;
|
||||
serverQuerySemaphore.Release();
|
||||
|
||||
Frontend.ShowConnectivityDialog(
|
||||
string.Format(Strings.Dialog_Connectivity_UnableToConnect, "ipinfo.io"),
|
||||
Strings.ActivityWatcher_LocationQueryFailed,
|
||||
MessageBoxImage.Warning,
|
||||
ex
|
||||
);
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
public override string ToString() => $"{PlaceId}/{JobId}";
|
||||
|
||||
private void RejoinServer()
|
||||
{
|
||||
string playerPath = Path.Combine(Paths.Roblox, "Player", "RobloxPlayerBeta.exe");
|
||||
|
||||
Process.Start(playerPath, GetInviteDeeplink(false));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Windows.Markup;
|
||||
|
||||
namespace Bloxstrap.Models
|
||||
namespace Bloxstrap.Models.Entities
|
||||
{
|
||||
public class ModPresetFileData
|
||||
{
|
52
Bloxstrap/Models/Entities/UniverseDetails.cs
Normal file
52
Bloxstrap/Models/Entities/UniverseDetails.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using Bloxstrap.Models.APIs.Roblox;
|
||||
|
||||
namespace Bloxstrap.Models.Entities
|
||||
{
|
||||
public class UniverseDetails
|
||||
{
|
||||
private static List<UniverseDetails> _cache { get; set; } = new();
|
||||
|
||||
public GameDetailResponse Data { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Returns data for a 128x128 icon
|
||||
/// </summary>
|
||||
public ThumbnailResponse Thumbnail { get; set; } = null!;
|
||||
|
||||
public static UniverseDetails? LoadFromCache(long id)
|
||||
{
|
||||
var cacheQuery = _cache.Where(x => x.Data?.Id == id);
|
||||
|
||||
if (cacheQuery.Any())
|
||||
return cacheQuery.First();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Task FetchSingle(long id) => FetchBulk(id.ToString());
|
||||
|
||||
public static async Task FetchBulk(string ids)
|
||||
{
|
||||
var gameDetailResponse = await Http.GetJson<ApiArrayResponse<GameDetailResponse>>($"https://games.roblox.com/v1/games?universeIds={ids}");
|
||||
|
||||
if (!gameDetailResponse.Data.Any())
|
||||
throw new InvalidHTTPResponseException("Roblox API for Game Details returned invalid data");
|
||||
|
||||
var universeThumbnailResponse = await Http.GetJson<ApiArrayResponse<ThumbnailResponse>>($"https://thumbnails.roblox.com/v1/games/icons?universeIds={ids}&returnPolicy=PlaceHolder&size=128x128&format=Png&isCircular=false");
|
||||
|
||||
if (!universeThumbnailResponse.Data.Any())
|
||||
throw new InvalidHTTPResponseException("Roblox API for Game Thumbnails returned invalid data");
|
||||
|
||||
foreach (string strId in ids.Split(','))
|
||||
{
|
||||
long id = long.Parse(strId);
|
||||
|
||||
_cache.Add(new UniverseDetails
|
||||
{
|
||||
Data = gameDetailResponse.Data.Where(x => x.Id == id).First(),
|
||||
Thumbnail = universeThumbnailResponse.Data.Where(x => x.TargetId == id).First(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,10 +9,15 @@ namespace Bloxstrap.Models.Manifest
|
||||
public class Package
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
public string Signature { get; set; } = "";
|
||||
|
||||
public int PackedSize { get; set; }
|
||||
|
||||
public int Size { get; set; }
|
||||
|
||||
public string DownloadPath => Path.Combine(Paths.Downloads, Signature);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[{Signature}] {Name}";
|
||||
|
@ -8,9 +8,9 @@ namespace Bloxstrap.Models.Manifest
|
||||
{
|
||||
public class PackageManifest : List<Package>
|
||||
{
|
||||
private PackageManifest(string data)
|
||||
public PackageManifest(string data)
|
||||
{
|
||||
using StringReader reader = new StringReader(data);
|
||||
using var reader = new StringReader(data);
|
||||
string? version = reader.ReadLine();
|
||||
|
||||
if (version != "v0")
|
||||
@ -46,13 +46,5 @@ namespace Bloxstrap.Models.Manifest
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<PackageManifest> Get(string versionGuid)
|
||||
{
|
||||
string pkgManifestUrl = RobloxDeployment.GetLocation($"/{versionGuid}-rbxPkgManifest.txt");
|
||||
var pkgManifestData = await App.HttpClient.GetStringAsync(pkgManifestUrl);
|
||||
|
||||
return new PackageManifest(pkgManifestData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
Bloxstrap/Models/Persistable/AppState.cs
Normal file
11
Bloxstrap/Models/Persistable/AppState.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Bloxstrap.Models.Persistable
|
||||
{
|
||||
public class AppState
|
||||
{
|
||||
public string VersionGuid { get; set; } = string.Empty;
|
||||
|
||||
public Dictionary<string, string> PackageHashes { get; set; } = new();
|
||||
|
||||
public int Size { get; set; }
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Bloxstrap.Models
|
||||
namespace Bloxstrap.Models.Persistable
|
||||
{
|
||||
public class Settings
|
||||
{
|
||||
@ -25,6 +25,5 @@ namespace Bloxstrap.Models
|
||||
|
||||
// mod preset configuration
|
||||
public bool UseDisableAppPatch { get; set; } = false;
|
||||
public bool DisableFullscreenOptimizations { get; set; } = false;
|
||||
}
|
||||
}
|
17
Bloxstrap/Models/Persistable/State.cs
Normal file
17
Bloxstrap/Models/Persistable/State.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace Bloxstrap.Models.Persistable
|
||||
{
|
||||
public class State
|
||||
{
|
||||
public bool ShowFFlagEditorWarning { get; set; } = true;
|
||||
|
||||
public bool PromptWebView2Install { get; set; } = true;
|
||||
|
||||
public AppState Player { get; set; } = new();
|
||||
|
||||
public AppState Studio { get; set; } = new();
|
||||
|
||||
public WindowState SettingsWindow { get; set; } = new();
|
||||
|
||||
public List<string> ModManifest { get; set; } = new();
|
||||
}
|
||||
}
|
13
Bloxstrap/Models/Persistable/WindowState.cs
Normal file
13
Bloxstrap/Models/Persistable/WindowState.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Bloxstrap.Models.Persistable
|
||||
{
|
||||
public class WindowState
|
||||
{
|
||||
public double Width { get; set; }
|
||||
|
||||
public double Height { get; set; }
|
||||
|
||||
public double Left { get; set; }
|
||||
|
||||
public double Top { get; set; }
|
||||
}
|
||||
}
|
@ -29,12 +29,16 @@ namespace Bloxstrap.Models.SettingTasks.Base
|
||||
|
||||
set
|
||||
{
|
||||
App.PendingSettingTasks[Name] = this;
|
||||
_newState = value;
|
||||
|
||||
if (Changed)
|
||||
App.PendingSettingTasks[Name] = this;
|
||||
else
|
||||
App.PendingSettingTasks.Remove(Name);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Changed => NewState != OriginalState;
|
||||
public override bool Changed => _newState != OriginalState;
|
||||
|
||||
public BoolBaseTask(string prefix, string name) : base(prefix, name) { }
|
||||
}
|
||||
|
@ -23,12 +23,16 @@
|
||||
|
||||
set
|
||||
{
|
||||
App.PendingSettingTasks[Name] = this;
|
||||
_newState = value;
|
||||
|
||||
if (Changed)
|
||||
App.PendingSettingTasks[Name] = this;
|
||||
else
|
||||
App.PendingSettingTasks.Remove(Name);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Changed => !NewState.Equals(OriginalState);
|
||||
public override bool Changed => !_newState.Equals(OriginalState);
|
||||
|
||||
public IEnumerable<T> Selections { get; private set; }
|
||||
= Enum.GetValues(typeof(T)).Cast<T>().OrderBy(x =>
|
||||
|
@ -29,12 +29,16 @@ namespace Bloxstrap.Models.SettingTasks.Base
|
||||
|
||||
set
|
||||
{
|
||||
App.PendingSettingTasks[Name] = this;
|
||||
_newState = value;
|
||||
|
||||
if (Changed)
|
||||
App.PendingSettingTasks[Name] = this;
|
||||
else
|
||||
App.PendingSettingTasks.Remove(Name);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Changed => NewState != OriginalState;
|
||||
public override bool Changed => _newState != OriginalState;
|
||||
|
||||
public StringBaseTask(string prefix, string name) : base(prefix, name) { }
|
||||
}
|
||||
|
@ -52,9 +52,12 @@ namespace Bloxstrap.Models.SettingTasks
|
||||
{
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
|
||||
Frontend.ShowMessageBox(
|
||||
String.Format(Strings.Menu_Mods_Presets_EmojiType_Error, ex.Message),
|
||||
MessageBoxImage.Warning);
|
||||
Frontend.ShowConnectivityDialog(
|
||||
String.Format(Strings.Dialog_Connectivity_UnableToConnect, "GitHub"),
|
||||
$"{Strings.Menu_Mods_Presets_EmojiType_Error}\n\n{Strings.Dialog_Connectivity_TryAgainLater}",
|
||||
MessageBoxImage.Warning,
|
||||
ex
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (query is not null && query.Any())
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bloxstrap.Models.SettingTasks.Base;
|
||||
using Bloxstrap.Models.Entities;
|
||||
using Bloxstrap.Models.SettingTasks.Base;
|
||||
|
||||
namespace Bloxstrap.Models.SettingTasks
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bloxstrap.Models.SettingTasks.Base;
|
||||
using Bloxstrap.Models.Entities;
|
||||
using Bloxstrap.Models.SettingTasks.Base;
|
||||
|
||||
namespace Bloxstrap.Models.SettingTasks
|
||||
{
|
||||
|
@ -1,17 +0,0 @@
|
||||
namespace Bloxstrap.Models
|
||||
{
|
||||
public class State
|
||||
{
|
||||
public bool ShowFFlagEditorWarning { get; set; } = true;
|
||||
|
||||
[Obsolete("Use PlayerVersionGuid instead", true)]
|
||||
public string VersionGuid { set { PlayerVersionGuid = value; } }
|
||||
public string PlayerVersionGuid { get; set; } = "";
|
||||
public string StudioVersionGuid { get; set; } = "";
|
||||
|
||||
public int PlayerSize { get; set; } = 0;
|
||||
public int StudioSize { get; set; } = 0;
|
||||
|
||||
public List<string> ModManifest { get; set; } = new();
|
||||
}
|
||||
}
|
@ -2,4 +2,4 @@
|
||||
FlashWindow
|
||||
GetWindowLong
|
||||
SetWindowLong
|
||||
EnumDisplaySettings
|
||||
SHObjectProperties
|
@ -4,6 +4,7 @@
|
||||
{
|
||||
// note that these are directories that aren't tethered to the basedirectory
|
||||
// so these can safely be called before initialization
|
||||
public static string Temp => Path.Combine(Path.GetTempPath(), App.ProjectName);
|
||||
public static string UserProfile => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
public static string LocalAppData => Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
public static string Desktop => Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
|
||||
@ -12,12 +13,15 @@
|
||||
|
||||
public static string Process => Environment.ProcessPath!;
|
||||
|
||||
public static string TempUpdates => Path.Combine(Temp, "Updates");
|
||||
public static string TempLogs => Path.Combine(Temp, "Logs");
|
||||
|
||||
public static string Base { get; private set; } = "";
|
||||
public static string Downloads { get; private set; } = "";
|
||||
public static string Logs { get; private set; } = "";
|
||||
public static string Integrations { get; private set; } = "";
|
||||
public static string Versions { get; private set; } = "";
|
||||
public static string Modifications { get; private set; } = "";
|
||||
public static string Roblox { get; private set; } = "";
|
||||
|
||||
public static string Application { get; private set; } = "";
|
||||
|
||||
@ -31,8 +35,8 @@
|
||||
Downloads = Path.Combine(Base, "Downloads");
|
||||
Logs = Path.Combine(Base, "Logs");
|
||||
Integrations = Path.Combine(Base, "Integrations");
|
||||
Versions = Path.Combine(Base, "Versions");
|
||||
Modifications = Path.Combine(Base, "Modifications");
|
||||
Roblox = Path.Combine(Base, "Roblox");
|
||||
|
||||
Application = Path.Combine(Base, $"{App.ProjectName}.exe");
|
||||
}
|
||||
|
@ -26,6 +26,10 @@
|
||||
"Bloxstrap (Studio Launch)": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "-studio"
|
||||
},
|
||||
"Bloxstrap (Watcher)": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "-watcher"
|
||||
}
|
||||
}
|
||||
}
|
437
Bloxstrap/Resources/Strings.Designer.cs
generated
437
Bloxstrap/Resources/Strings.Designer.cs
generated
@ -61,25 +61,70 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to left game.
|
||||
/// Looks up a localized string similar to Licenses.
|
||||
/// </summary>
|
||||
public static string ActivityTracker_LeftGame {
|
||||
public static string About_Licenses_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTracker.LeftGame", resourceCulture);
|
||||
return ResourceManager.GetString("About.Licenses.Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to lookup failed.
|
||||
/// Looks up a localized string similar to These are the people currently supporting Bloxstrap through [Ko-fi]({0}). A massive thank you to everyone here!.
|
||||
/// </summary>
|
||||
public static string ActivityTracker_LookupFailed {
|
||||
public static string About_Supporters_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityTracker.LookupFailed", resourceCulture);
|
||||
return ResourceManager.GetString("About.Supporters.Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Bloxstrap was unable to auto-update to {0}. Please update it manually by downloading and running the latest release from the GitHub page..
|
||||
/// Looks up a localized string similar to Supporters.
|
||||
/// </summary>
|
||||
public static string About_Supporters_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("About.Supporters.Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to About Bloxstrap.
|
||||
/// </summary>
|
||||
public static string About_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("About.Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Translators.
|
||||
/// </summary>
|
||||
public static string About_Translators_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("About.Translators.Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The server location could not be queried. You may be joining games too quickly..
|
||||
/// </summary>
|
||||
public static string ActivityWatcher_LocationQueryFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityWatcher.LocationQueryFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Your current game will not show on your Discord presence because an error occurred when loading the game information..
|
||||
/// </summary>
|
||||
public static string ActivityWatcher_RichPresenceLoadFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("ActivityWatcher.RichPresenceLoadFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Bloxstrap was unable to automatically update to version {0}. Please update it manually by downloading and running it from the website..
|
||||
/// </summary>
|
||||
public static string Bootstrapper_AutoUpdateFailed {
|
||||
get {
|
||||
@ -96,33 +141,6 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to It's possible that something is preventing Bloxstrap from connecting to the internet. Please check and try again..
|
||||
/// </summary>
|
||||
public static string Bootstrapper_Connectivity_Preventing {
|
||||
get {
|
||||
return ResourceManager.GetString("Bootstrapper.Connectivity.Preventing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Roblox may be down right now. See status.roblox.com for more information. Please try again later..
|
||||
/// </summary>
|
||||
public static string Bootstrapper_Connectivity_RobloxDown {
|
||||
get {
|
||||
return ResourceManager.GetString("Bootstrapper.Connectivity.RobloxDown", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Bloxstrap timed out when trying to connect to three different Roblox deployment mirrors, indicating a poor internet connection. Please try again later..
|
||||
/// </summary>
|
||||
public static string Bootstrapper_Connectivity_TimedOut {
|
||||
get {
|
||||
return ResourceManager.GetString("Bootstrapper.Connectivity.TimedOut", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Could not apply the {0} emoji mod preset because of a network error. To try again, please reconfigure the option in the Bloxstrap Menu..
|
||||
/// </summary>
|
||||
@ -152,6 +170,15 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Failed to save {0}: {1}.
|
||||
/// </summary>
|
||||
public static string Bootstrapper_JsonManagerSaveFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("Bootstrapper.JsonManagerSaveFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Bloxstrap does not have enough disk space to download and install Roblox. Please free up some disk space and try again..
|
||||
/// </summary>
|
||||
@ -423,11 +450,11 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Locate log file.
|
||||
/// Looks up a localized string similar to Loading, please wait....
|
||||
/// </summary>
|
||||
public static string Common_LocateLogFile {
|
||||
public static string Common_Loading {
|
||||
get {
|
||||
return ResourceManager.GetString("Common.LocateLogFile", resourceCulture);
|
||||
return ResourceManager.GetString("Common.Loading", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@ -467,6 +494,15 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Could not load data because of a network error..
|
||||
/// </summary>
|
||||
public static string Common_NetworkError {
|
||||
get {
|
||||
return ResourceManager.GetString("Common.NetworkError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to New.
|
||||
/// </summary>
|
||||
@ -485,6 +521,15 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not available.
|
||||
/// </summary>
|
||||
public static string Common_NotAvailable {
|
||||
get {
|
||||
return ResourceManager.GetString("Common.NotAvailable", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OK.
|
||||
/// </summary>
|
||||
@ -494,6 +539,15 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open log file.
|
||||
/// </summary>
|
||||
public static string Common_OpenLogFile {
|
||||
get {
|
||||
return ResourceManager.GetString("Common.OpenLogFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Presets.
|
||||
/// </summary>
|
||||
@ -512,6 +566,15 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Roblox has not yet been installed. Please launch Roblox using Bloxstrap at least once before trying to use this option..
|
||||
/// </summary>
|
||||
public static string Common_RobloxNotInstalled {
|
||||
get {
|
||||
return ResourceManager.GetString("Common.RobloxNotInstalled", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Shortcuts.
|
||||
/// </summary>
|
||||
@ -603,11 +666,29 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open log file.
|
||||
/// Looks up a localized string similar to Game history is only recorded for your current Roblox session. Games will appear here as you leave them or teleport within them..
|
||||
/// </summary>
|
||||
public static string ContextMenu_OpenLogFile {
|
||||
public static string ContextMenu_GameHistory_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("ContextMenu.OpenLogFile", resourceCulture);
|
||||
return ResourceManager.GetString("ContextMenu.GameHistory.Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Rejoin.
|
||||
/// </summary>
|
||||
public static string ContextMenu_GameHistory_Rejoin {
|
||||
get {
|
||||
return ResourceManager.GetString("ContextMenu.GameHistory.Rejoin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Game history.
|
||||
/// </summary>
|
||||
public static string ContextMenu_GameHistory_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("ContextMenu.GameHistory.Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@ -620,15 +701,6 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to See server details.
|
||||
/// </summary>
|
||||
public static string ContextMenu_SeeServerDetails {
|
||||
get {
|
||||
return ResourceManager.GetString("ContextMenu.SeeServerDetails", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copy Instance ID.
|
||||
/// </summary>
|
||||
@ -647,15 +719,6 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Loading, please wait....
|
||||
/// </summary>
|
||||
public static string ContextMenu_ServerInformation_Loading {
|
||||
get {
|
||||
return ResourceManager.GetString("ContextMenu.ServerInformation.Loading", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Location.
|
||||
/// </summary>
|
||||
@ -765,6 +828,60 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Something is likely preventing Bloxstrap from connecting to the internet..
|
||||
/// </summary>
|
||||
public static string Dialog_Connectivity_Preventing {
|
||||
get {
|
||||
return ResourceManager.GetString("Dialog.Connectivity.Preventing", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Roblox may be down right now. See {0} for more information..
|
||||
/// </summary>
|
||||
public static string Dialog_Connectivity_RobloxDown {
|
||||
get {
|
||||
return ResourceManager.GetString("Dialog.Connectivity.RobloxDown", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Because Roblox needs to be installed or upgraded, Bloxstrap cannot continue..
|
||||
/// </summary>
|
||||
public static string Dialog_Connectivity_RobloxUpgradeNeeded {
|
||||
get {
|
||||
return ResourceManager.GetString("Dialog.Connectivity.RobloxUpgradeNeeded", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to For this launch, Roblox will not be checked for upgrades, and changes to mods will not be applied..
|
||||
/// </summary>
|
||||
public static string Dialog_Connectivity_RobloxUpgradeSkip {
|
||||
get {
|
||||
return ResourceManager.GetString("Dialog.Connectivity.RobloxUpgradeSkip", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} may be down right now..
|
||||
/// </summary>
|
||||
public static string Dialog_Connectivity_ServiceDown {
|
||||
get {
|
||||
return ResourceManager.GetString("Dialog.Connectivity.ServiceDown", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The connection timed out, which could indicate a poor internet connection or a firewall block..
|
||||
/// </summary>
|
||||
public static string Dialog_Connectivity_TimedOut {
|
||||
get {
|
||||
return ResourceManager.GetString("Dialog.Connectivity.TimedOut", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connectivity error.
|
||||
/// </summary>
|
||||
@ -775,7 +892,16 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Bloxstrap is unable to connect to Roblox.
|
||||
/// Looks up a localized string similar to Please try again later..
|
||||
/// </summary>
|
||||
public static string Dialog_Connectivity_TryAgainLater {
|
||||
get {
|
||||
return ResourceManager.GetString("Dialog.Connectivity.TryAgainLater", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Bloxstrap is unable to connect to {0}.
|
||||
/// </summary>
|
||||
public static string Dialog_Connectivity_UnableToConnect {
|
||||
get {
|
||||
@ -850,6 +976,17 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The chosen bootstrapper icon could not be loaded.
|
||||
///
|
||||
///{0}.
|
||||
/// </summary>
|
||||
public static string Dialog_IconLoadFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("Dialog.IconLoadFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Choose preferred language.
|
||||
/// </summary>
|
||||
@ -868,6 +1005,33 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Roblox has crashed..
|
||||
/// </summary>
|
||||
public static string Dialog_PlayerError_Crash {
|
||||
get {
|
||||
return ResourceManager.GetString("Dialog.PlayerError.Crash", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Roblox failed to launch..
|
||||
/// </summary>
|
||||
public static string Dialog_PlayerError_FailedLaunch {
|
||||
get {
|
||||
return ResourceManager.GetString("Dialog.PlayerError.FailedLaunch", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please read the following help information, which will open in your web browser when you close this dialog..
|
||||
/// </summary>
|
||||
public static string Dialog_PlayerError_HelpInformation {
|
||||
get {
|
||||
return ResourceManager.GetString("Dialog.PlayerError.HelpInformation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Early 2015.
|
||||
/// </summary>
|
||||
@ -1139,7 +1303,7 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Private.
|
||||
/// Looks up a localized string similar to Private server.
|
||||
/// </summary>
|
||||
public static string Enums_ServerType_Private {
|
||||
get {
|
||||
@ -1148,7 +1312,7 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Public.
|
||||
/// Looks up a localized string similar to Public server.
|
||||
/// </summary>
|
||||
public static string Enums_ServerType_Public {
|
||||
get {
|
||||
@ -1157,7 +1321,7 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reserved.
|
||||
/// Looks up a localized string similar to Reserved server.
|
||||
/// </summary>
|
||||
public static string Enums_ServerType_Reserved {
|
||||
get {
|
||||
@ -1249,6 +1413,17 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The version of Bloxstrap you've launched is older than the version you currently have installed.
|
||||
///Issues may occur and your settings may be altered. A reinstall is recommended.
|
||||
///Are you sure you want to continue?.
|
||||
/// </summary>
|
||||
public static string InstallChecker_VersionLessThanInstalled {
|
||||
get {
|
||||
return ResourceManager.GetString("InstallChecker.VersionLessThanInstalled", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Will drop you into the desktop app once everything's done.
|
||||
/// </summary>
|
||||
@ -1309,6 +1484,17 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Bloxstrap has been installed to this location before and is still present, however the installer cannot overwrite the old executable.
|
||||
///
|
||||
///Please manually delete Bloxstrap.exe from the install location or try restarting your system, and then retry installation afterwards..
|
||||
/// </summary>
|
||||
public static string Installer_Install_CannotOverwrite {
|
||||
get {
|
||||
return ResourceManager.GetString("Installer.Install.CannotOverwrite", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Existing data found. Your mods and settings will be restored..
|
||||
/// </summary>
|
||||
@ -1354,6 +1540,15 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Are you sure you want to cancel the installation?.
|
||||
/// </summary>
|
||||
public static string Installer_ShouldCancel {
|
||||
get {
|
||||
return ResourceManager.GetString("Installer.ShouldCancel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Bloxstrap Installer.
|
||||
/// </summary>
|
||||
@ -1366,7 +1561,7 @@ namespace Bloxstrap.Resources {
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Thank you for downloading Bloxstrap.
|
||||
///
|
||||
///You should have gotten it from either {0} or {1}. Those are the only official websites to get it from.
|
||||
///You should have downloaded it from either {0} or {1}. Those are the only official websites to get it from. It is your responsibility to ensure you download from an official source.
|
||||
///
|
||||
///This installation process will be quick and simple, and you will be able to configure any of Bloxstrap's settings after installation..
|
||||
/// </summary>
|
||||
@ -1403,6 +1598,24 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Your Fast Flags could not be loaded. They have been reset to the default configuration..
|
||||
/// </summary>
|
||||
public static string JsonManager_FastFlagsLoadFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("JsonManager.FastFlagsLoadFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Your Settings could not be loaded. They have been reset to the default configuration..
|
||||
/// </summary>
|
||||
public static string JsonManager_SettingsLoadFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("JsonManager.SettingsLoadFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Configure settings.
|
||||
/// </summary>
|
||||
@ -2151,24 +2364,6 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Allows you to configure 21 different quality levels instead of 10..
|
||||
/// </summary>
|
||||
public static string Menu_FastFlags_Presets_AltGraphicsSelector_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.FastFlags.Presets.AltGraphicsSelector.Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Use advanced graphics quality selector.
|
||||
/// </summary>
|
||||
public static string Menu_FastFlags_Presets_AltGraphicsSelector_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.FastFlags.Presets.AltGraphicsSelector.Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Rendering and Graphics.
|
||||
/// </summary>
|
||||
@ -2472,15 +2667,6 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to e.g. C:\Windows\System32\cmd.exe.
|
||||
/// </summary>
|
||||
public static string Menu_Integrations_Custom_AppLocation_Placeholder {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.Integrations.Custom.AppLocation.Placeholder", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Auto close when Roblox closes.
|
||||
/// </summary>
|
||||
@ -2509,7 +2695,7 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to e.g. /k echo Roblox is running!.
|
||||
/// Looks up a localized string similar to Roblox is running!.
|
||||
/// </summary>
|
||||
public static string Menu_Integrations_Custom_LaunchArgs_Placeholder {
|
||||
get {
|
||||
@ -2590,7 +2776,25 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running..
|
||||
/// Looks up a localized string similar to When in-game, you'll be able to see where your server is located via [ipinfo.io]({0})..
|
||||
/// </summary>
|
||||
public static string Menu_Integrations_QueryServerLocation_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.Integrations.QueryServerLocation.Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Query server location.
|
||||
/// </summary>
|
||||
public static string Menu_Integrations_QueryServerLocation_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.Integrations.QueryServerLocation.Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running. [Find out more]({0})..
|
||||
/// </summary>
|
||||
public static string Menu_Integrations_RequiresActivityTracking {
|
||||
get {
|
||||
@ -2616,24 +2820,6 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to When you join a game, you'll be notified of where your server's located. Won't show in fullscreen..
|
||||
/// </summary>
|
||||
public static string Menu_Integrations_ShowServerDetails_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.Integrations.ShowServerDetails.Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to See server location when joining a game.
|
||||
/// </summary>
|
||||
public static string Menu_Integrations_ShowServerDetails_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.Integrations.ShowServerDetails.Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Integrations.
|
||||
/// </summary>
|
||||
@ -2661,6 +2847,24 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Configure application parameters such as DPI scaling behaviour and [fullscreen optimizations]({0})..
|
||||
/// </summary>
|
||||
public static string Menu_Mods_Misc_CompatibilitySettings_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.Mods.Misc.CompatibilitySettings.Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Manage compatibility settings.
|
||||
/// </summary>
|
||||
public static string Menu_Mods_Misc_CompatibilitySettings_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.Mods.Misc.CompatibilitySettings.Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Choose font....
|
||||
/// </summary>
|
||||
@ -2752,9 +2956,7 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The emoji mod could not be applied because of a network error during download.
|
||||
///
|
||||
///{0}.
|
||||
/// Looks up a localized string similar to The emoji mod can not be applied at this time..
|
||||
/// </summary>
|
||||
public static string Menu_Mods_Presets_EmojiType_Error {
|
||||
get {
|
||||
@ -2942,6 +3144,15 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You have unsaved changes. Are you sure you want to close without saving?.
|
||||
/// </summary>
|
||||
public static string Menu_UnsavedChanges {
|
||||
get {
|
||||
return ResourceManager.GetString("Menu.UnsavedChanges", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to They'll be kept where Bloxstrap was installed, and will automatically be restored on a reinstall..
|
||||
/// </summary>
|
||||
|
@ -117,26 +117,20 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ActivityTracker.LeftGame" xml:space="preserve">
|
||||
<value>left game</value>
|
||||
</data>
|
||||
<data name="ActivityTracker.LookupFailed" xml:space="preserve">
|
||||
<value>lookup failed</value>
|
||||
</data>
|
||||
<data name="Bootstrapper.AutoUpdateFailed" xml:space="preserve">
|
||||
<value>Bloxstrap was unable to auto-update to {0}. Please update it manually by downloading and running the latest release from the GitHub page.</value>
|
||||
<value>Bloxstrap was unable to automatically update to version {0}. Please update it manually by downloading and running it from the website.</value>
|
||||
</data>
|
||||
<data name="Bootstrapper.ConfirmLaunch" xml:space="preserve">
|
||||
<value>Roblox is currently running, and launching another instance will close it. Are you sure you want to continue launching?</value>
|
||||
</data>
|
||||
<data name="Bootstrapper.Connectivity.Preventing" xml:space="preserve">
|
||||
<value>It's possible that something is preventing Bloxstrap from connecting to the internet. Please check and try again.</value>
|
||||
<data name="Dialog.Connectivity.Preventing" xml:space="preserve">
|
||||
<value>Something is likely preventing Bloxstrap from connecting to the internet.</value>
|
||||
</data>
|
||||
<data name="Bootstrapper.Connectivity.RobloxDown" xml:space="preserve">
|
||||
<value>Roblox may be down right now. See status.roblox.com for more information. Please try again later.</value>
|
||||
<data name="Dialog.Connectivity.RobloxDown" xml:space="preserve">
|
||||
<value>Roblox may be down right now. See {0} for more information.</value>
|
||||
</data>
|
||||
<data name="Bootstrapper.Connectivity.TimedOut" xml:space="preserve">
|
||||
<value>Bloxstrap timed out when trying to connect to three different Roblox deployment mirrors, indicating a poor internet connection. Please try again later.</value>
|
||||
<data name="Dialog.Connectivity.TimedOut" xml:space="preserve">
|
||||
<value>The connection timed out, which could indicate a poor internet connection or a firewall block.</value>
|
||||
</data>
|
||||
<data name="Bootstrapper.EmojiPresetFetchFailed" xml:space="preserve">
|
||||
<value>Could not apply the {0} emoji mod preset because of a network error. To try again, please reconfigure the option in the Bloxstrap Menu.</value>
|
||||
@ -226,8 +220,8 @@ Your ReShade configuration files will still be saved, and you can locate them by
|
||||
<data name="Common.ImportJson" xml:space="preserve">
|
||||
<value>Import JSON</value>
|
||||
</data>
|
||||
<data name="Common.LocateLogFile" xml:space="preserve">
|
||||
<value>Locate log file</value>
|
||||
<data name="Common.OpenLogFile" xml:space="preserve">
|
||||
<value>Open log file</value>
|
||||
</data>
|
||||
<data name="Common.Miscellaneous" xml:space="preserve">
|
||||
<value>Miscellaneous</value>
|
||||
@ -268,21 +262,12 @@ Your ReShade configuration files will still be saved, and you can locate them by
|
||||
<data name="ContextMenu.CopyDeeplinkInvite" xml:space="preserve">
|
||||
<value>Copy invite deeplink</value>
|
||||
</data>
|
||||
<data name="ContextMenu.OpenLogFile" xml:space="preserve">
|
||||
<value>Open log file</value>
|
||||
</data>
|
||||
<data name="ContextMenu.SeeServerDetails" xml:space="preserve">
|
||||
<value>See server details</value>
|
||||
</data>
|
||||
<data name="ContextMenu.ServerInformation.CopyInstanceId" xml:space="preserve">
|
||||
<value>Copy Instance ID</value>
|
||||
</data>
|
||||
<data name="ContextMenu.ServerInformation.InstanceId" xml:space="preserve">
|
||||
<value>Instance ID</value>
|
||||
</data>
|
||||
<data name="ContextMenu.ServerInformation.Loading" xml:space="preserve">
|
||||
<value>Loading, please wait...</value>
|
||||
</data>
|
||||
<data name="ContextMenu.ServerInformation.Location" xml:space="preserve">
|
||||
<value>Location</value>
|
||||
</data>
|
||||
@ -309,7 +294,7 @@ Click for more information</value>
|
||||
<value>Connectivity error</value>
|
||||
</data>
|
||||
<data name="Dialog.Connectivity.UnableToConnect" xml:space="preserve">
|
||||
<value>Bloxstrap is unable to connect to Roblox</value>
|
||||
<value>Bloxstrap is unable to connect to {0}</value>
|
||||
</data>
|
||||
<data name="Dialog.Exception.CopyLogContents" xml:space="preserve">
|
||||
<value>Copy log contents</value>
|
||||
@ -404,13 +389,13 @@ If not, then please report this exception through a [GitHub issue]({1}) along wi
|
||||
<value>Direct3D 11</value>
|
||||
</data>
|
||||
<data name="Enums.ServerType.Private" xml:space="preserve">
|
||||
<value>Private</value>
|
||||
<value>Private server</value>
|
||||
</data>
|
||||
<data name="Enums.ServerType.Public" xml:space="preserve">
|
||||
<value>Public</value>
|
||||
<value>Public server</value>
|
||||
</data>
|
||||
<data name="Enums.ServerType.Reserved" xml:space="preserve">
|
||||
<value>Reserved</value>
|
||||
<value>Reserved server</value>
|
||||
</data>
|
||||
<data name="Enums.Theme.Dark" xml:space="preserve">
|
||||
<value>Dark</value>
|
||||
@ -652,12 +637,6 @@ Do NOT use this to import large "flag lists" made by other people that promise t
|
||||
<value>Learn more about Fast Flags, what these presets do, and how to use them.</value>
|
||||
<comment>Title is Common.Help</comment>
|
||||
</data>
|
||||
<data name="Menu.FastFlags.Presets.AltGraphicsSelector.Description" xml:space="preserve">
|
||||
<value>Allows you to configure 21 different quality levels instead of 10.</value>
|
||||
</data>
|
||||
<data name="Menu.FastFlags.Presets.AltGraphicsSelector.Title" xml:space="preserve">
|
||||
<value>Use advanced graphics quality selector</value>
|
||||
</data>
|
||||
<data name="Menu.FastFlags.Presets.D3DExclusiveFullscreenInfo" xml:space="preserve">
|
||||
<value>Direct3D [exclusive fullscreen]({0}) using Alt+Enter is enabled by default.</value>
|
||||
</data>
|
||||
@ -730,9 +709,6 @@ Selecting 'No' will ignore this warning and continue installation.</value>
|
||||
<data name="Menu.Integrations.Custom.AppLocation" xml:space="preserve">
|
||||
<value>Application Location</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.Custom.AppLocation.Placeholder" xml:space="preserve">
|
||||
<value>e.g. C:\Windows\System32\cmd.exe</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.Custom.AutoClose" xml:space="preserve">
|
||||
<value>Auto close when Roblox closes</value>
|
||||
</data>
|
||||
@ -743,7 +719,7 @@ Selecting 'No' will ignore this warning and continue installation.</value>
|
||||
<value>Launch Arguments</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.Custom.LaunchArgs.Placeholder" xml:space="preserve">
|
||||
<value>e.g. /k echo Roblox is running!</value>
|
||||
<value>Roblox is running!</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.Custom.NewIntegration" xml:space="preserve">
|
||||
<value>New Integration</value>
|
||||
@ -770,7 +746,7 @@ Selecting 'No' will ignore this warning and continue installation.</value>
|
||||
<value>Enable activity tracking</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.RequiresActivityTracking" xml:space="preserve">
|
||||
<value>This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running.</value>
|
||||
<value>This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running. [Find out more]({0}).</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.ShowGameActivity.Description" xml:space="preserve">
|
||||
<value>The Roblox game you're playing will be shown on your Discord profile. [Not working?]({0})</value>
|
||||
@ -778,11 +754,11 @@ Selecting 'No' will ignore this warning and continue installation.</value>
|
||||
<data name="Menu.Integrations.ShowGameActivity.Title" xml:space="preserve">
|
||||
<value>Show game activity</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.ShowServerDetails.Description" xml:space="preserve">
|
||||
<value>When you join a game, you'll be notified of where your server's located. Won't show in fullscreen.</value>
|
||||
<data name="Menu.Integrations.QueryServerLocation.Description" xml:space="preserve">
|
||||
<value>When in-game, you'll be able to see where your server is located via [ipinfo.io]({0}).</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.ShowServerDetails.Title" xml:space="preserve">
|
||||
<value>See server location when joining a game</value>
|
||||
<data name="Menu.Integrations.QueryServerLocation.Title" xml:space="preserve">
|
||||
<value>Query server location</value>
|
||||
</data>
|
||||
<data name="Menu.Integrations.Title" xml:space="preserve">
|
||||
<value>Integrations</value>
|
||||
@ -999,7 +975,7 @@ Selecting 'No' will ignore this warning and continue installation.</value>
|
||||
<data name="Installer.Welcome.MainText" xml:space="preserve">
|
||||
<value>Thank you for downloading Bloxstrap.
|
||||
|
||||
You should have gotten it from either {0} or {1}. Those are the only official websites to get it from.
|
||||
You should have downloaded it from either {0} or {1}. Those are the only official websites to get it from. It is your responsibility to ensure you download from an official source.
|
||||
|
||||
This installation process will be quick and simple, and you will be able to configure any of Bloxstrap's settings after installation.</value>
|
||||
</data>
|
||||
@ -1120,9 +1096,7 @@ If not, then please report this exception to the maintainers of this fork. Do NO
|
||||
<value>Connected to reserved server</value>
|
||||
</data>
|
||||
<data name="Menu.Mods.Presets.EmojiType.Error" xml:space="preserve">
|
||||
<value>The emoji mod could not be applied because of a network error during download.
|
||||
|
||||
{0}</value>
|
||||
<value>The emoji mod can not be applied at this time.</value>
|
||||
</data>
|
||||
<data name="Dialog.AlreadyRunning.Installer" xml:space="preserve">
|
||||
<value>Please wait for installation to finish.</value>
|
||||
@ -1130,4 +1104,103 @@ If not, then please report this exception to the maintainers of this fork. Do NO
|
||||
<data name="Dialog.AlreadyRunning.Uninstaller" xml:space="preserve">
|
||||
<value>Please wait for uninstallation to finish.</value>
|
||||
</data>
|
||||
<data name="About.Title" xml:space="preserve">
|
||||
<value>About Bloxstrap</value>
|
||||
</data>
|
||||
<data name="About.Licenses.Title" xml:space="preserve">
|
||||
<value>Licenses</value>
|
||||
</data>
|
||||
<data name="About.Translators.Title" xml:space="preserve">
|
||||
<value>Translators</value>
|
||||
</data>
|
||||
<data name="Menu.UnsavedChanges" xml:space="preserve">
|
||||
<value>You have unsaved changes. Are you sure you want to close without saving?</value>
|
||||
</data>
|
||||
<data name="InstallChecker.VersionLessThanInstalled" xml:space="preserve">
|
||||
<value>The version of Bloxstrap you've launched is older than the version you currently have installed.
|
||||
Issues may occur and your settings may be altered. A reinstall is recommended.
|
||||
Are you sure you want to continue?</value>
|
||||
</data>
|
||||
<data name="Dialog.PlayerError.FailedLaunch" xml:space="preserve">
|
||||
<value>Roblox failed to launch.</value>
|
||||
</data>
|
||||
<data name="Dialog.PlayerError.Crash" xml:space="preserve">
|
||||
<value>Roblox has crashed.</value>
|
||||
</data>
|
||||
<data name="Dialog.PlayerError.HelpInformation" xml:space="preserve">
|
||||
<value>Please read the following help information, which will open in your web browser when you close this dialog.</value>
|
||||
</data>
|
||||
<data name="Common.NetworkError" xml:space="preserve">
|
||||
<value>Could not load data because of a network error.</value>
|
||||
</data>
|
||||
<data name="Common.Loading" xml:space="preserve">
|
||||
<value>Loading, please wait...</value>
|
||||
</data>
|
||||
<data name="About.Supporters.Title" xml:space="preserve">
|
||||
<value>Supporters</value>
|
||||
</data>
|
||||
<data name="About.Supporters.Description" xml:space="preserve">
|
||||
<value>These are the people currently supporting Bloxstrap through [Ko-fi]({0}). A massive thank you to everyone here!</value>
|
||||
</data>
|
||||
<data name="JsonManager.SettingsLoadFailed" xml:space="preserve">
|
||||
<value>Your Settings could not be loaded. They have been reset to the default configuration.</value>
|
||||
</data>
|
||||
<data name="JsonManager.FastFlagsLoadFailed" xml:space="preserve">
|
||||
<value>Your Fast Flags could not be loaded. They have been reset to the default configuration.</value>
|
||||
</data>
|
||||
<data name="ContextMenu.GameHistory.Title" xml:space="preserve">
|
||||
<value>Game history</value>
|
||||
</data>
|
||||
<data name="ContextMenu.GameHistory.Rejoin" xml:space="preserve">
|
||||
<value>Rejoin</value>
|
||||
</data>
|
||||
<data name="ActivityWatcher.RichPresenceLoadFailed" xml:space="preserve">
|
||||
<value>Your current game will not show on your Discord presence because an error occurred when loading the game information.</value>
|
||||
</data>
|
||||
<data name="ContextMenu.GameHistory.Description" xml:space="preserve">
|
||||
<value>Game history is only recorded for your current Roblox session. Games will appear here as you leave them or teleport within them.</value>
|
||||
</data>
|
||||
<data name="ActivityWatcher.LocationQueryFailed" xml:space="preserve">
|
||||
<value>The server location could not be queried. You may be joining games too quickly.</value>
|
||||
</data>
|
||||
<data name="Dialog.Connectivity.ServiceDown" xml:space="preserve">
|
||||
<value>{0} may be down right now.</value>
|
||||
</data>
|
||||
<data name="Dialog.Connectivity.TryAgainLater" xml:space="preserve">
|
||||
<value>Please try again later.</value>
|
||||
</data>
|
||||
<data name="Dialog.Connectivity.RobloxUpgradeSkip" xml:space="preserve">
|
||||
<value>For this launch, Roblox will not be checked for upgrades, and changes to mods will not be applied.</value>
|
||||
</data>
|
||||
<data name="Dialog.Connectivity.RobloxUpgradeNeeded" xml:space="preserve">
|
||||
<value>Because Roblox needs to be installed or upgraded, Bloxstrap cannot continue.</value>
|
||||
</data>
|
||||
<data name="Installer.Install.CannotOverwrite" xml:space="preserve">
|
||||
<value>Bloxstrap has been installed to this location before and is still present, however the installer cannot overwrite the old executable.
|
||||
|
||||
Please manually delete Bloxstrap.exe from the install location or try restarting your system, and then retry installation afterwards.</value>
|
||||
</data>
|
||||
<data name="Common.NotAvailable" xml:space="preserve">
|
||||
<value>Not available</value>
|
||||
</data>
|
||||
<data name="Menu.Mods.Misc.CompatibilitySettings.Title" xml:space="preserve">
|
||||
<value>Manage compatibility settings</value>
|
||||
</data>
|
||||
<data name="Menu.Mods.Misc.CompatibilitySettings.Description" xml:space="preserve">
|
||||
<value>Configure application parameters such as DPI scaling behaviour and [fullscreen optimizations]({0}).</value>
|
||||
</data>
|
||||
<data name="Common.RobloxNotInstalled" xml:space="preserve">
|
||||
<value>Roblox has not yet been installed. Please launch Roblox using Bloxstrap at least once before trying to use this option.</value>
|
||||
</data>
|
||||
<data name="Installer.ShouldCancel" xml:space="preserve">
|
||||
<value>Are you sure you want to cancel the installation?</value>
|
||||
</data>
|
||||
<data name="Bootstrapper.JsonManagerSaveFailed" xml:space="preserve">
|
||||
<value>Failed to save {0}: {1}</value>
|
||||
</data>
|
||||
<data name="Dialog.IconLoadFailed" xml:space="preserve">
|
||||
<value>The chosen bootstrapper icon could not be loaded.
|
||||
|
||||
{0}</value>
|
||||
</data>
|
||||
</root>
|
@ -15,6 +15,7 @@
|
||||
private static readonly Dictionary<string, int> BaseUrls = new()
|
||||
{
|
||||
{ "https://setup.rbxcdn.com", 0 },
|
||||
{ "https://setup-aws.rbxcdn.com", 2 },
|
||||
{ "https://setup-ak.rbxcdn.com", 2 },
|
||||
{ "https://roblox-setup.cachefly.net", 2 },
|
||||
{ "https://s3.amazonaws.com/setup.roblox.com", 4 }
|
||||
@ -22,7 +23,7 @@
|
||||
|
||||
private static async Task<string?> TestConnection(string url, int priority, CancellationToken token)
|
||||
{
|
||||
string LOG_IDENT = $"RobloxDeployment::TestConnection.{url}";
|
||||
string LOG_IDENT = $"RobloxDeployment::TestConnection<{url}>";
|
||||
|
||||
await Task.Delay(priority * 1000, token);
|
||||
|
||||
@ -32,14 +33,14 @@
|
||||
{
|
||||
var response = await App.HttpClient.GetAsync($"{url}/versionStudio", token);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new HttpResponseException(response);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// versionStudio is the version hash for the last MFC studio to be deployed.
|
||||
// the response body should always be "version-012732894899482c".
|
||||
string content = await response.Content.ReadAsStringAsync(token);
|
||||
|
||||
if (content != VersionStudioHash)
|
||||
throw new Exception($"versionStudio response does not match (expected \"{VersionStudioHash}\", got \"{content}\")");
|
||||
throw new InvalidHTTPResponseException($"versionStudio response does not match (expected \"{VersionStudioHash}\", got \"{content}\")");
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
@ -66,11 +67,10 @@
|
||||
|
||||
// returns null for success
|
||||
|
||||
CancellationTokenSource tokenSource = new CancellationTokenSource();
|
||||
CancellationToken token = tokenSource.Token;
|
||||
var tokenSource = new CancellationTokenSource();
|
||||
|
||||
var exceptions = new List<Exception>();
|
||||
var tasks = (from entry in BaseUrls select TestConnection(entry.Key, entry.Value, token)).ToList();
|
||||
var tasks = (from entry in BaseUrls select TestConnection(entry.Key, entry.Value, tokenSource.Token)).ToList();
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, "Testing connectivity...");
|
||||
|
||||
@ -127,7 +127,11 @@
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Getting deploy info for channel {channel}");
|
||||
|
||||
if (String.IsNullOrEmpty(channel))
|
||||
channel = DefaultChannel;
|
||||
|
||||
string cacheKey = $"{channel}-{binaryType}";
|
||||
|
||||
ClientVersion clientVersion;
|
||||
|
||||
if (ClientVersionCache.ContainsKey(cacheKey))
|
||||
@ -137,57 +141,37 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isDefaultChannel = String.Compare(channel, DefaultChannel, StringComparison.OrdinalIgnoreCase) == 0;
|
||||
|
||||
string path = $"/v2/client-version/{binaryType}";
|
||||
|
||||
if (String.Compare(channel, DefaultChannel, StringComparison.InvariantCultureIgnoreCase) != 0)
|
||||
if (!isDefaultChannel)
|
||||
path = $"/v2/client-version/{binaryType}/channel/{channel}";
|
||||
|
||||
HttpResponseMessage deployInfoResponse;
|
||||
|
||||
try
|
||||
{
|
||||
deployInfoResponse = await App.HttpClient.GetAsync("https://clientsettingscdn.roblox.com" + path);
|
||||
clientVersion = await Http.GetJson<ClientVersion>("https://clientsettingscdn.roblox.com" + path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to contact clientsettingscdn! Falling back to clientsettings...");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
|
||||
deployInfoResponse = await App.HttpClient.GetAsync("https://clientsettings.roblox.com" + path);
|
||||
clientVersion = await Http.GetJson<ClientVersion>("https://clientsettings.roblox.com" + path);
|
||||
}
|
||||
|
||||
string rawResponse = await deployInfoResponse.Content.ReadAsStringAsync();
|
||||
|
||||
if (!deployInfoResponse.IsSuccessStatusCode)
|
||||
// check if channel is behind LIVE
|
||||
if (!isDefaultChannel)
|
||||
{
|
||||
// 400 = Invalid binaryType.
|
||||
// 404 = Could not find version details for binaryType.
|
||||
// 500 = Error while fetching version information.
|
||||
// either way, we throw
|
||||
var defaultClientVersion = await GetInfo(DefaultChannel);
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT,
|
||||
"Failed to fetch deploy info!\r\n" +
|
||||
$"\tStatus code: {deployInfoResponse.StatusCode}\r\n" +
|
||||
$"\tResponse: {rawResponse}"
|
||||
);
|
||||
|
||||
throw new HttpResponseException(deployInfoResponse);
|
||||
if (Utilities.CompareVersions(clientVersion.Version, defaultClientVersion.Version) == VersionComparison.LessThan)
|
||||
clientVersion.IsBehindDefaultChannel = true;
|
||||
}
|
||||
|
||||
clientVersion = JsonSerializer.Deserialize<ClientVersion>(rawResponse)!;
|
||||
ClientVersionCache[cacheKey] = clientVersion;
|
||||
}
|
||||
|
||||
// check if channel is behind LIVE
|
||||
if (channel != DefaultChannel)
|
||||
{
|
||||
var defaultClientVersion = await GetInfo(DefaultChannel);
|
||||
|
||||
if (Utilities.CompareVersions(clientVersion.Version, defaultClientVersion.Version) == VersionComparison.LessThan)
|
||||
clientVersion.IsBehindDefaultChannel = true;
|
||||
}
|
||||
|
||||
ClientVersionCache[cacheKey] = clientVersion;
|
||||
|
||||
return clientVersion;
|
||||
}
|
||||
}
|
||||
|
@ -52,16 +52,7 @@ namespace Bloxstrap
|
||||
|
||||
string rawResponse = await response.Content.ReadAsStringAsync();
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
App.Logger.WriteLine(logIndent,
|
||||
"Failed to fetch client settings!\r\n" +
|
||||
$"\tStatus code: {response.StatusCode}\r\n" +
|
||||
$"\tResponse: {rawResponse}"
|
||||
);
|
||||
|
||||
throw new HttpResponseException(response);
|
||||
}
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var clientSettings = JsonSerializer.Deserialize<ClientFlagSettings>(rawResponse);
|
||||
|
||||
|
@ -32,11 +32,11 @@ namespace Bloxstrap.UI.Converters
|
||||
return attribute.StaticName;
|
||||
|
||||
if (attribute.FromTranslation is not null)
|
||||
return Resources.Strings.ResourceManager.GetStringSafe(attribute.FromTranslation);
|
||||
return Strings.ResourceManager.GetStringSafe(attribute.FromTranslation);
|
||||
}
|
||||
}
|
||||
|
||||
return Resources.Strings.ResourceManager.GetStringSafe(String.Format(
|
||||
return Strings.ResourceManager.GetStringSafe(String.Format(
|
||||
"{0}.{1}",
|
||||
typeName.Substring(typeName.IndexOf('.', StringComparison.Ordinal) + 1),
|
||||
stringVal
|
||||
|
48
Bloxstrap/UI/Elements/About/MainWindow.xaml
Normal file
48
Bloxstrap/UI/Elements/About/MainWindow.xaml
Normal file
@ -0,0 +1,48 @@
|
||||
<base:WpfUiWindow x:Class="Bloxstrap.UI.Elements.About.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:pages="clr-namespace:Bloxstrap.UI.Elements.About.Pages"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
xmlns:base="clr-namespace:Bloxstrap.UI.Elements.Base"
|
||||
xmlns:resources="clr-namespace:Bloxstrap.Resources"
|
||||
mc:Ignorable="d"
|
||||
Title="{x:Static resources:Strings.About_Title}"
|
||||
Background="{ui:ThemeResource ApplicationBackgroundBrush}"
|
||||
MinWidth="740"
|
||||
Width="740"
|
||||
Height="440"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
WindowBackdropType="Mica"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ui:TitleBar Padding="8" x:Name="RootTitleBar" Grid.Row="0" ForceShutdown="False" MinimizeToTray="False" UseSnapLayout="True" Title="{x:Static resources:Strings.About_Title}" Icon="pack://application:,,,/Bloxstrap.ico" />
|
||||
|
||||
<Grid x:Name="RootGrid" Grid.Row="1" Margin="12,12,0,0" Visibility="Visible">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ui:NavigationStore x:Name="RootNavigation" Grid.Row="1" Grid.Column="0" Margin="0,0,12,0" Frame="{Binding ElementName=RootFrame}" SelectedPageIndex="0">
|
||||
<ui:NavigationStore.Items>
|
||||
<ui:NavigationItem Content="{x:Static resources:Strings.Menu_About_Title}" PageType="{x:Type pages:AboutPage}" Icon="QuestionCircle48" Tag="about" Margin="0,0,0,12" />
|
||||
<ui:NavigationItem Content="{x:Static resources:Strings.About_Translators_Title}" PageType="{x:Type pages:TranslatorsPage}" Icon="Translate24" Tag="translators" Margin="0,0,0,12" />
|
||||
<ui:NavigationItem Content="{x:Static resources:Strings.About_Licenses_Title}" PageType="{x:Type pages:LicensesPage}" Icon="Code24" Tag="licenses" Margin="0,0,0,12" />
|
||||
</ui:NavigationStore.Items>
|
||||
</ui:NavigationStore>
|
||||
|
||||
<Frame x:Name="RootFrame" Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</base:WpfUiWindow>
|
35
Bloxstrap/UI/Elements/About/MainWindow.xaml.cs
Normal file
35
Bloxstrap/UI/Elements/About/MainWindow.xaml.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System.Windows.Controls;
|
||||
using Wpf.Ui.Controls.Interfaces;
|
||||
using Wpf.Ui.Mvvm.Contracts;
|
||||
|
||||
namespace Bloxstrap.UI.Elements.About
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : INavigationWindow
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
App.Logger.WriteLine("MainWindow::MainWindow", "Initializing menu");
|
||||
}
|
||||
|
||||
#region INavigationWindow methods
|
||||
|
||||
public Frame GetFrame() => RootFrame;
|
||||
|
||||
public INavigation GetNavigation() => RootNavigation;
|
||||
|
||||
public bool Navigate(Type pageType) => RootNavigation.Navigate(pageType);
|
||||
|
||||
public void SetPageService(IPageService pageService) => RootNavigation.PageService = pageService;
|
||||
|
||||
public void ShowWindow() => Show();
|
||||
|
||||
public void CloseWindow() => Close();
|
||||
|
||||
#endregion INavigationWindow methods
|
||||
}
|
||||
}
|
222
Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml
Normal file
222
Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml
Normal file
@ -0,0 +1,222 @@
|
||||
<ui:UiPage x:Class="Bloxstrap.UI.Elements.About.Pages.AboutPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:enums="clr-namespace:Bloxstrap.Enums"
|
||||
xmlns:models="clr-namespace:Bloxstrap.UI.ViewModels"
|
||||
xmlns:dmodels="clr-namespace:Bloxstrap.UI.ViewModels.About"
|
||||
xmlns:controls="clr-namespace:Bloxstrap.UI.Elements.Controls"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
xmlns:resources="clr-namespace:Bloxstrap.Resources"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="1500" d:DesignWidth="800"
|
||||
Title="AboutPage"
|
||||
Scrollable="True">
|
||||
<!--d:DataContext="{d:DesignInstance dmodels:AboutViewModel, IsDesignTimeCreatable=True}"-->
|
||||
|
||||
<StackPanel Margin="0,0,14,14">
|
||||
<StackPanel.Resources>
|
||||
<FrameworkElement x:Key="ProxyElement" DataContext="{Binding}"/>
|
||||
</StackPanel.Resources>
|
||||
|
||||
<Grid Margin="0,0,0,24" HorizontalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image Grid.Column="0" Width="72" Height="72" VerticalAlignment="Center" Source="pack://application:,,,/Bloxstrap.ico" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<StackPanel Grid.Column="1" Margin="12,0,0,0" VerticalAlignment="Center">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="Bloxstrap" Margin="0,0,4,0" FontSize="24" FontWeight="Medium" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding Version, Mode=OneTime}" Margin="4,0,0,2" VerticalAlignment="Bottom" FontSize="16" FontWeight="Medium" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</Grid>
|
||||
<TextBlock Text="{x:Static resources:Strings.Menu_About_Description}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<WrapPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<ui:Anchor Margin="0,0,8,8" Content="{x:Static resources:Strings.Menu_About_GithubRepository}" Icon="Code24" NavigateUri="https://github.com/pizzaboxer/bloxstrap" />
|
||||
<ui:Anchor Margin="0,0,8,8" Content="{x:Static resources:Strings.Menu_About_HelpInformation}" Icon="BookQuestionMark24" NavigateUri="https://github.com/pizzaboxer/bloxstrap/wiki" />
|
||||
<ui:Anchor Margin="0,0,8,8" Content="{x:Static resources:Strings.Menu_About_ReportIssue}" Icon="BookExclamationMark24" NavigateUri="https://github.com/pizzaboxer/bloxstrap/issues" />
|
||||
<ui:Anchor Margin="0,0,0,8" Content="{x:Static resources:Strings.Menu_About_DiscordServer}" Icon="Chat48" NavigateUri="https://discord.gg/nKjV3mGq6R" />
|
||||
</WrapPanel>
|
||||
|
||||
<StackPanel Visibility="{Binding BuildInformationVisibility, Mode=OneTime}">
|
||||
<TextBlock Text="Build Information" FontWeight="Medium" FontSize="20" Margin="0,16,0,0" />
|
||||
<TextBlock Text="hmmmm" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
|
||||
<Grid Column="0" Margin="0,8,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,4,16,4" FontSize="14" FontWeight="Medium" Text="Timestamp" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,0,4" VerticalAlignment="Bottom" Text="{Binding BuildTimestamp, Mode=OneTime}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,4,16,4" FontSize="14" FontWeight="Medium" Text="Machine" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,0,4" VerticalAlignment="Bottom" Text="{Binding BuildMetadata.Machine, Mode=OneTime}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,4,16,4" FontSize="14" FontWeight="Medium" Text="Commit Hash" Visibility="{Binding BuildCommitVisibility, Mode=OneTime}" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Margin="0,0,0,4" VerticalAlignment="Bottom" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Visibility="{Binding BuildCommitVisibility, Mode=OneTime}">
|
||||
<Hyperlink Foreground="{DynamicResource TextFillColorTertiaryBrush}" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="{Binding BuildCommitHashUrl, Mode=OneTime}">
|
||||
<TextBlock Text="{Binding BuildMetadata.CommitHash, Mode=OneTime}" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Margin="0,4,16,4" FontSize="14" FontWeight="Medium" Text="Commit Ref" Visibility="{Binding BuildCommitVisibility, Mode=OneTime}" />
|
||||
<TextBlock Grid.Row="3" Grid.Column="1" Margin="0,0,0,4" VerticalAlignment="Bottom" Text="{Binding BuildMetadata.CommitRef, Mode=OneTime}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Visibility="{Binding BuildCommitVisibility, Mode=OneTime}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Text="{x:Static resources:Strings.About_Supporters_Title}" FontWeight="Medium" FontSize="20" Margin="0,16,0,0" />
|
||||
<controls:MarkdownTextBlock MarkdownText="{Binding Source={x:Static resources:Strings.About_Supporters_Description}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://ko-fi.com/boxerpizza'}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.Style>
|
||||
<Style TargetType="Grid">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding SupportersLoadedState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Unknown}">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Style>
|
||||
</Grid.Style>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:ProgressRing Grid.Column="0" IsIndeterminate="True" />
|
||||
<TextBlock Grid.Column="1" Margin="16,0,0,0" Text="{x:Static resources:Strings.Common_Loading}" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.Style>
|
||||
<Style TargetType="Grid">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding SupportersLoadedState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Failed}">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Style>
|
||||
</Grid.Style>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Source="pack://application:,,,/Resources/MessageBox/Error.png" Width="60" Height="60" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<StackPanel Grid.Column="1" Margin="16,0,0,0" VerticalAlignment="Center">
|
||||
<TextBlock Text="{x:Static resources:Strings.Common_NetworkError}" />
|
||||
<TextBlock Text="{Binding SupportersLoadError, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<ListView ItemsSource="{Binding Supporters, Mode=OneWay}" Margin="0,8,0,0" ScrollViewer.CanContentScroll="False" IsEnabled="False">
|
||||
<ListView.Style>
|
||||
<Style TargetType="ListView" BasedOn="{StaticResource {x:Type ListView}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding SupportersLoadedState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Successful}">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Style>
|
||||
</ListView.Style>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ui:Card Padding="8">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Ellipse Grid.Column="0" Height="32" Width="32" VerticalAlignment="Center">
|
||||
<Ellipse.Fill>
|
||||
<ImageBrush ImageSource="{Binding Image, IsAsync=True}" />
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
|
||||
<TextBlock Grid.Column="1" Margin="8,0,2,0" VerticalAlignment="Center" Text="{Binding Name}" />
|
||||
</Grid>
|
||||
</ui:Card>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="{Binding SupporterColumns}" Margin="-4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
</ListView>
|
||||
|
||||
<TextBlock Text="{x:Static resources:Strings.Menu_About_Contributors}" FontWeight="Medium" FontSize="20" Margin="0,16,0,0" />
|
||||
<TextBlock Text="{x:Static resources:Strings.Menu_About_Contributors_Description}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:Expander Grid.Column="0" Margin="0,0,4,0" HeaderIcon="Code24" HeaderText="{x:Static resources:Strings.Menu_About_Contributors_Code}" IsExpanded="True">
|
||||
<StackPanel>
|
||||
<controls:MarkdownTextBlock MarkdownText="[Matt](https://github.com/bluepilledgreat)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[lolmanurfunny](https://github.com/lolmanurfunny)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[1011025m](https://github.com/1011025m)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[EasternBloxxer](https://github.com/EasternBloxxer)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[sitiom](https://github.com/sitiom)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[Extravi](https://github.com/Extravi)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[EpixScripts](https://github.com/EpixScripts)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[swatTurret](https://github.com/swatTurret)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[fxeP1](https://github.com/fxeP1)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[Redusofficial](https://github.com/Redusofficial)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[srthMD](https://github.com/srthMD)" />
|
||||
</StackPanel>
|
||||
</controls:Expander>
|
||||
|
||||
<controls:Expander Grid.Column="1" Margin="4,0,4,0" HeaderIcon="AppsAddIn28" HeaderText="{x:Static resources:Strings.Menu_About_Contributors_FeatureIdeas}" IsExpanded="True">
|
||||
<StackPanel>
|
||||
<controls:MarkdownTextBlock MarkdownText="[he3als](https://github.com/he3als)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[NikSavchenk0](https://github.com/NikSavchenk0)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[carter0nline](https://github.com/carter0nline)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[lolmanurfunny](https://github.com/lolmanurfunny)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[MehKako](https://github.com/MehKako)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[EpixScripts](https://github.com/EpixScripts)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[knivesofeylis](https://github.com/knivesofeylis)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[sha4owz](https://github.com/sha4owz)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[DaMlgNoodle](https://github.com/DaMlgNoodle)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[nakoyasha](https://github.com/nakoyasha)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[exurd](https://github.com/exurd)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[0xFE0F](https://github.com/0xFE0F)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[Tezos](https://github.com/GoingCrazyDude)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[CfwSky](https://www.roblox.com/users/129425241/profile)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[ruubloo](https://www.roblox.com/users/158082266/profile)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[toyoda165](https://www.roblox.com/users/923416649/profile)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[ShadowCodeX](https://github.com/ShadowCodeX-debug)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[cub-has-injected](https://github.com/cub-has-injected)" />
|
||||
</StackPanel>
|
||||
</controls:Expander>
|
||||
|
||||
<controls:Expander Grid.Column="2" Margin="4,0,0,0" HeaderIcon="Heart16" HeaderText="{x:Static resources:Strings.Menu_About_Contributors_SpecialThanks}" IsExpanded="True">
|
||||
<StackPanel>
|
||||
<controls:MarkdownTextBlock MarkdownText="[MaximumADHD](https://github.com/MaximumADHD)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[Multako](https://www.roblox.com/users/2485612194/profile)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[axstin](https://github.com/axstin)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[taskmanager](https://github.com/Mantaraix)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[apprehensions](https://github.com/apprehensions)" />
|
||||
</StackPanel>
|
||||
</controls:Expander>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:UiPage>
|
@ -1,6 +1,6 @@
|
||||
using Bloxstrap.UI.ViewModels.Settings;
|
||||
using Bloxstrap.UI.ViewModels.About;
|
||||
|
||||
namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
namespace Bloxstrap.UI.Elements.About.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for AboutPage.xaml
|
72
Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml
Normal file
72
Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml
Normal file
@ -0,0 +1,72 @@
|
||||
<ui:UiPage x:Class="Bloxstrap.UI.Elements.About.Pages.LicensesPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:models="clr-namespace:Bloxstrap.UI.ViewModels"
|
||||
xmlns:controls="clr-namespace:Bloxstrap.UI.Elements.Controls"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
xmlns:resources="clr-namespace:Bloxstrap.Resources"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="1500" d:DesignWidth="800"
|
||||
Title="AboutPage"
|
||||
Scrollable="True">
|
||||
<StackPanel Margin="0,0,14,14">
|
||||
<TextBlock Text="{x:Static resources:Strings.Menu_About_Licenses}" FontWeight="Medium" FontSize="24" Margin="0,0,0,16" />
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ui:CardAction Grid.Row="0" Grid.Column="0" Margin="0,8,8,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/pizzaboxer/bloxstrap/blob/main/LICENSE">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="Bloxstrap" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="0" Grid.Column="1" Margin="0,8,8,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/lepoco/wpfui/blob/main/LICENSE">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="WPF-UI" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="0" Grid.Column="2" Margin="0,8,0,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/securifybv/ShellLink/blob/master/LICENSE.txt">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="ShellLink" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="1" Grid.Column="0" Margin="0,8,8,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/Lachee/discord-rpc-csharp/blob/master/LICENSE">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="DiscordRPC" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,8,0,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/LICENSE">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="13" Text="Roblox Studio Mod Manager" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="2" Grid.Column="0" Margin="0,8,8,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/icsharpcode/SharpZipLib/blob/master/LICENSE.txt">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="13" Text="SharpZipLib" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,8,0,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/xoofx/markdig/blob/master/license.txt">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="Markdig" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_BSD2}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:UiPage>
|
13
Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml.cs
Normal file
13
Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Bloxstrap.UI.Elements.About.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for LicensesPage.xaml
|
||||
/// </summary>
|
||||
public partial class LicensesPage
|
||||
{
|
||||
public LicensesPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
484
Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml
Normal file
484
Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml
Normal file
@ -0,0 +1,484 @@
|
||||
<ui:UiPage x:Class="Bloxstrap.UI.Elements.About.Pages.TranslatorsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:models="clr-namespace:Bloxstrap.UI.ViewModels"
|
||||
xmlns:controls="clr-namespace:Bloxstrap.UI.Elements.Controls"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
xmlns:resources="clr-namespace:Bloxstrap.Resources"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="1500" d:DesignWidth="800"
|
||||
Title="AboutPage"
|
||||
Scrollable="True">
|
||||
<StackPanel Margin="0,0,14,14">
|
||||
<TextBlock Text="{x:Static resources:Strings.About_Translators_Title}" FontWeight="Medium" FontSize="24" Margin="0,0,0,16" />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Text="Bahasa Indonesia" FontSize="16" FontWeight="Medium">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="e7leopard" />
|
||||
<TextBlock Text="hfzrk" />
|
||||
<TextBlock Text="soudblox" />
|
||||
|
||||
<TextBlock Text="Bokmål" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="darkevilmage" />
|
||||
<TextBlock Text="endsouls" />
|
||||
<TextBlock Text="letoek" />
|
||||
|
||||
<!--<TextBlock Text="Čeština" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Chusaak" />
|
||||
<TextBlock Text="DanyCraftCZ" />
|
||||
<TextBlock Text="Franklin_Surten" />
|
||||
<TextBlock Text="Galaxy_Gangster6" />
|
||||
<TextBlock Text="jasperholo" />
|
||||
<TextBlock Text="letoek" />
|
||||
<TextBlock Text="noobkid4545" />
|
||||
<TextBlock Text="radim776" />-->
|
||||
|
||||
<!-- <TextBlock Text="Dansk" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Momslayer98" />
|
||||
<TextBlock Text="SirBlue" /> -->
|
||||
|
||||
<TextBlock Text="Deutsch" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="agent_phoenix" />
|
||||
<TextBlock Text="avoidr" />
|
||||
<TextBlock Text="hcjohnsgd" />
|
||||
<TextBlock Text="hxmbt" />
|
||||
<TextBlock Text="kale123" />
|
||||
<TextBlock Text="Marvin_Chu" />
|
||||
<TextBlock Text="nzxt_xll" />
|
||||
<TextBlock Text="Nlx095" />
|
||||
<TextBlock Text="Ph1lwtf" />
|
||||
<TextBlock Text="sxckqerz" />
|
||||
<TextBlock Text="TEAM_LILA" />
|
||||
<TextBlock Text="xDevoidx" />
|
||||
|
||||
<TextBlock Text="Español" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="4Xisty" />
|
||||
<TextBlock Text="Botkid" />
|
||||
<TextBlock Text="colocky" />
|
||||
<TextBlock Text="D0N-B0T" />
|
||||
<TextBlock Text="Dasp" />
|
||||
<TextBlock Text="devyyxn" />
|
||||
<TextBlock Text="ItzzExcel" />
|
||||
<TextBlock Text="Ilayhlinda" />
|
||||
<TextBlock Text="Ilushiouss" />
|
||||
<TextBlock Text="jayces." />
|
||||
<TextBlock Text="kroesufos" />
|
||||
<TextBlock Text="LaiyLiod" />
|
||||
<TextBlock Text="lyalekin" />
|
||||
<TextBlock Text="NezumiDS" />
|
||||
<TextBlock Text="NimuruDP" />
|
||||
<TextBlock Text="NescafeCL" />
|
||||
<TextBlock Text="Sw7gger" />
|
||||
<TextBlock Text="sxckqerz" />
|
||||
<TextBlock Text="Urzy" />
|
||||
|
||||
<TextBlock Text="Filipino" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="alphjectiom" />
|
||||
<TextBlock Text="FlaminDaPotato" />
|
||||
<TextBlock Text="RobiTheRobloxxer" />
|
||||
<TextBlock Text="shadow01148" />
|
||||
|
||||
<TextBlock Text="Français" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="At0zDx" />
|
||||
<TextBlock Text="built4aiming" />
|
||||
<TextBlock Text="hahaloserz360" />
|
||||
<TextBlock Text="K0ga" />
|
||||
<TextBlock Text="Marcssebaa" />
|
||||
<TextBlock Text="MommySernox" />
|
||||
<TextBlock Text="owentempest8" />
|
||||
<TextBlock Text="Subsical" />
|
||||
<TextBlock Text="thatsirwaffles" />
|
||||
<TextBlock Text="tyundrai" />
|
||||
<TextBlock Text="Waza80" />
|
||||
|
||||
<TextBlock Text="Hindi (Latin)" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="kunaljainop" />
|
||||
<TextBlock Text="marathedonroblox" />
|
||||
<TextBlock Text="Sur_" />
|
||||
<TextBlock Text="Tezos" />
|
||||
<TextBlock Text="TheTakuo" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="Hrvatski" FontSize="16" FontWeight="Medium">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Dzigos" />
|
||||
<TextBlock Text="Koyroii" />
|
||||
<TextBlock Text="Nemznja" />
|
||||
|
||||
<TextBlock Text="Italiano" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="crow_zxcr" />
|
||||
<TextBlock Text="devyyxn" />
|
||||
<TextBlock Text="domenicoiacono" />
|
||||
<TextBlock Text="hulabulaseop" />
|
||||
<TextBlock Text="lord_moth" />
|
||||
<TextBlock Text="loridori" />
|
||||
<TextBlock Text="Lupo01" />
|
||||
<TextBlock Text="Mogunars" />
|
||||
<TextBlock Text="pave08" />
|
||||
<TextBlock Text="spectrumbruh" />
|
||||
|
||||
<TextBlock Text="Lietuvių" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="cr0000142" />
|
||||
<TextBlock Text="Duexo" />
|
||||
<TextBlock Text="jessethedev" />
|
||||
<TextBlock Text="Vac31." />
|
||||
|
||||
<TextBlock Text="Magyar" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="DynoPlays" />
|
||||
<TextBlock Text="Elotomka" />
|
||||
<TextBlock Text="xM4rk1" />
|
||||
|
||||
<!--<TextBlock Text="Nederlands" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Cosmix" />
|
||||
<TextBlock Text="Miwwzy" />
|
||||
<TextBlock Text="Quickvision1" />
|
||||
<TextBlock Text="ydboss" />-->
|
||||
|
||||
<TextBlock Text="Polski" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Eztaru" />
|
||||
<TextBlock Text="lunar" />
|
||||
<TextBlock Text="markontm" />
|
||||
<TextBlock Text="my5q" />
|
||||
<TextBlock Text="nemzik2137" />
|
||||
<TextBlock Text="plexar" />
|
||||
<TextBlock Text="r.efil" />
|
||||
|
||||
<TextBlock Text="Português (Brasil)" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="anormalevis" />
|
||||
<TextBlock Text="ErisvaldoBalbino" />
|
||||
<TextBlock Text="G3xneric" />
|
||||
<TextBlock Text="hnter" />
|
||||
<TextBlock Text="issei_" />
|
||||
<TextBlock Text="iyto.lk" />
|
||||
<TextBlock Text="jhermesn" />
|
||||
<TextBlock Text="JorgeDaPelada" />
|
||||
<TextBlock Text="LwgoDev" />
|
||||
<TextBlock Text="nunk7" />
|
||||
<TextBlock Text="peke7374" />
|
||||
<TextBlock Text="SeeF" />
|
||||
<TextBlock Text="Snowzin1" />
|
||||
<TextBlock Text="storm930" />
|
||||
<TextBlock Text="toofastforboo" />
|
||||
<TextBlock Text="VMOICE" />
|
||||
<TextBlock Text="Ye4" />
|
||||
<TextBlock Text="ZaPeZaPe" />
|
||||
|
||||
<TextBlock Text="Română" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Externalkinetics" />
|
||||
<TextBlock Text="MonochromeAlex" />
|
||||
<TextBlock Text="PlayerValley" />
|
||||
<TextBlock Text="Smuki" />
|
||||
<TextBlock Text="theflopperguy" />
|
||||
|
||||
<TextBlock Text="Suomi" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="gelaxiz" />
|
||||
<TextBlock Text="jes5" />
|
||||
<TextBlock Text="retromaxwell" />
|
||||
<TextBlock Text="SomePinglord" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Text="Svenska" FontSize="16" FontWeight="Medium">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Axellse" />
|
||||
<TextBlock Text="CroppingFlea479" />
|
||||
<TextBlock Text="FishySpelar" />
|
||||
<TextBlock Text="PineappleSnackz" />
|
||||
<TextBlock Text="simonixen" />
|
||||
<TextBlock Text="thatgurkangurk" />
|
||||
|
||||
<TextBlock Text="Tiếng Việt" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="alexking2068" />
|
||||
<TextBlock Text="baterblx" />
|
||||
<TextBlock Text="Elytronn" />
|
||||
<TextBlock Text="fox810891" />
|
||||
<TextBlock Text="ItsPoofy" />
|
||||
<TextBlock Text="Limer1" />
|
||||
<TextBlock Text="makayu203332" />
|
||||
<TextBlock Text="MEx2/j7x6" />
|
||||
<TextBlock Text="NguyenDat208" />
|
||||
<TextBlock Text="quanmequankk" />
|
||||
<TextBlock Text="SomeRandomGuy175" />
|
||||
<TextBlock Text="SonThanhVN" />
|
||||
<TextBlock Text="teaanguyenn" />
|
||||
<TextBlock Text="Veiiorra" />
|
||||
|
||||
<TextBlock Text="Türkçe" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="canny19133" />
|
||||
<TextBlock Text="cfors55" />
|
||||
<TextBlock Text="drakfreddy" />
|
||||
<TextBlock Text="enisify" />
|
||||
<TextBlock Text="jayces." />
|
||||
<TextBlock Text="nyatie" />
|
||||
<TextBlock Text="PixelArmy" />
|
||||
<TextBlock Text="plants8332" />
|
||||
<TextBlock Text="r02" />
|
||||
<TextBlock Text="siyamicik" />
|
||||
<TextBlock Text="ydboss" />
|
||||
|
||||
<TextBlock Text="Українська" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="9zh" />
|
||||
<TextBlock Text="andrey3569s" />
|
||||
<TextBlock Text="DexterBloxxer" />
|
||||
<TextBlock Text="Externalkinetics" />
|
||||
<TextBlock Text="maksimvlad7" />
|
||||
|
||||
<TextBlock Text="Босански" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Cortex_1" />
|
||||
<TextBlock Text="Nemznja" />
|
||||
<TextBlock Text="Ren" />
|
||||
|
||||
<TextBlock Text="Български" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="GrafitNiki" />
|
||||
<TextBlock Text="sidefrappe" />
|
||||
|
||||
<TextBlock Text="Русский" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="3tcy" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="andBroz" />
|
||||
<TextBlock Text="alexneonwithglue" />
|
||||
<TextBlock Text="AnonymousDudeLOL123" />
|
||||
<TextBlock Text="aperna_of_the_ticks" />
|
||||
<TextBlock Text="arsenijveselov77" />
|
||||
<TextBlock Text="Art3mLapa" />
|
||||
<TextBlock Text="cherkash" />
|
||||
<TextBlock Text="cub-has-injected" />
|
||||
<TextBlock Text="dallyuser" />
|
||||
<TextBlock Text="Dr1mG" />
|
||||
<TextBlock Text="Externalkinetics" />
|
||||
<TextBlock Text="fxstyxx" />
|
||||
<TextBlock Text="Gustodd4202" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="harababura" />
|
||||
<TextBlock Text="ImperialRhyme" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="IStoleYourCheese" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="khat7" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="kostyan" />
|
||||
<TextBlock Text="Maks" />
|
||||
<TextBlock Text="niktoyou" />
|
||||
<TextBlock Text="nurgament2" />
|
||||
<TextBlock Text="poflexim" />
|
||||
<TextBlock Text="Prob1rka" />
|
||||
<TextBlock Text="Provo" />
|
||||
<TextBlock Text="Quenevelly" />
|
||||
<TextBlock Text="sally13249" />
|
||||
<TextBlock Text="simmon8800" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="Skylan031" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="Spuffio" />
|
||||
<TextBlock Text="StraiF" />
|
||||
<TextBlock Text="StrayCatSimb" />
|
||||
<TextBlock Text="Voxel" />
|
||||
<TextBlock Text="XonaShera" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="Ziio123" />
|
||||
<TextBlock Text="zor9na90000" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="3">
|
||||
<TextBlock Text="עברית" FontSize="16" FontWeight="Medium">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="1cur1" />
|
||||
<TextBlock Text="ilan0098" />
|
||||
<TextBlock Text="koerga" />
|
||||
<TextBlock Text="Sezei" />
|
||||
|
||||
<TextBlock Text="العربية" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="busguesjshbahsj" />
|
||||
<TextBlock Text="cq2i" />
|
||||
<TextBlock Text="mmiky" />
|
||||
<TextBlock Text="mostafagamingx1" />
|
||||
<TextBlock Text="RoRed" />
|
||||
<TextBlock Text="Sakupen" />
|
||||
<TextBlock Text="streoic" />
|
||||
<TextBlock Text="uvq18" />
|
||||
<TextBlock Text="wyfast" />
|
||||
<TextBlock Text="Zida" />
|
||||
|
||||
<TextBlock Text="বাংলা" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Arnian" />
|
||||
<TextBlock Text="Hydrated_panda" />
|
||||
<TextBlock Text="marathedonroblox" />
|
||||
<TextBlock Text="red_hi" />
|
||||
|
||||
<TextBlock Text="ภาษาไทย" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="._bonus_." />
|
||||
<TextBlock Text="marc15772" />
|
||||
<TextBlock Text="arthurwagon" />
|
||||
<TextBlock Text="Sem1z" />
|
||||
<TextBlock Text="xAom" />
|
||||
|
||||
<TextBlock Text="한국어" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="ADVI50R" />
|
||||
<TextBlock Text="asd123456fghqwerty" />
|
||||
<TextBlock Text="bacon1295" />
|
||||
<TextBlock Text="NightPlay" />
|
||||
|
||||
<TextBlock Text="中文 (简体)" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="14TQD" />
|
||||
<TextBlock Text="Aling00" />
|
||||
<TextBlock Text="Clock" />
|
||||
<TextBlock Text="ERSN_CERROR" />
|
||||
<TextBlock Text="Kirxvil" />
|
||||
<TextBlock Text="Typel" />
|
||||
<TextBlock Text="yuhaodatt" />
|
||||
|
||||
<TextBlock Text="中文 (廣東話)" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="henrychu1125" />
|
||||
<TextBlock Text="kitzure" />
|
||||
<TextBlock Text="Kimina898" />
|
||||
<TextBlock Text="shhh_op" />
|
||||
|
||||
<TextBlock Text="中文 (繁體)" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="14TQD" />
|
||||
<TextBlock Text="DXuwu" />
|
||||
<TextBlock Text="kitzure" />
|
||||
<TextBlock Text="Kimina898" />
|
||||
|
||||
<TextBlock Text="日本語" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="MintJapan" />
|
||||
<TextBlock Text="pimeja7" />
|
||||
<TextBlock Text="yixhuaa" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:UiPage>
|
13
Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml.cs
Normal file
13
Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Bloxstrap.UI.Elements.About.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for TranslatorsPage.xaml
|
||||
/// </summary>
|
||||
public partial class TranslatorsPage
|
||||
{
|
||||
public TranslatorsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -133,7 +133,7 @@ namespace Bloxstrap.UI.Elements.Bootstrapper.Base
|
||||
public void Dialog_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
if (!_isClosing)
|
||||
Bootstrapper?.CancelInstall();
|
||||
Bootstrapper?.Cancel();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
@ -126,7 +126,7 @@ namespace Bloxstrap.UI.Elements.Bootstrapper
|
||||
private void Window_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
if (!_isClosing)
|
||||
Bootstrapper?.CancelInstall();
|
||||
Bootstrapper?.Cancel();
|
||||
}
|
||||
|
||||
#region IBootstrapperDialog Methods
|
||||
|
@ -105,7 +105,7 @@ namespace Bloxstrap.UI.Elements.Bootstrapper
|
||||
private void UiWindow_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
if (!_isClosing)
|
||||
Bootstrapper?.CancelInstall();
|
||||
Bootstrapper?.Cancel();
|
||||
}
|
||||
|
||||
#region IBootstrapperDialog Methods
|
||||
|
@ -123,7 +123,7 @@ namespace Bloxstrap.UI.Elements.Bootstrapper
|
||||
private void UiWindow_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
if (!_isClosing)
|
||||
Bootstrapper?.CancelInstall();
|
||||
Bootstrapper?.Cancel();
|
||||
}
|
||||
|
||||
#region IBootstrapperDialog Methods
|
||||
|
@ -43,7 +43,7 @@ namespace Bloxstrap.UI.Elements.Bootstrapper
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.buttonCancel.Text = Resources.Strings.Common_Cancel;
|
||||
this.buttonCancel.Text = Strings.Common_Cancel;
|
||||
|
||||
ScaleWindow();
|
||||
SetupDialog();
|
||||
|
@ -43,7 +43,7 @@ namespace Bloxstrap.UI.Elements.Bootstrapper
|
||||
InitializeComponent();
|
||||
|
||||
this.IconBox.BackgroundImage = App.Settings.Prop.BootstrapperIcon.GetIcon().ToBitmap();
|
||||
this.buttonCancel.Text = Resources.Strings.Common_Cancel;
|
||||
this.buttonCancel.Text = Strings.Common_Cancel;
|
||||
|
||||
ScaleWindow();
|
||||
SetupDialog();
|
||||
|
@ -52,8 +52,8 @@ namespace Bloxstrap.UI.Elements.Bootstrapper
|
||||
this.BackColor = Color.FromArgb(25, 27, 29);
|
||||
}
|
||||
|
||||
this.labelMessage.Text = Resources.Strings.Bootstrapper_StylePreview_TextCancel;
|
||||
this.buttonCancel.Text = Resources.Strings.Common_Cancel;
|
||||
this.labelMessage.Text = Strings.Bootstrapper_StylePreview_TextCancel;
|
||||
this.buttonCancel.Text = Strings.Common_Cancel;
|
||||
this.IconBox.BackgroundImage = App.Settings.Prop.BootstrapperIcon.GetIcon().GetSized(128, 128).ToBitmap();
|
||||
|
||||
SetupDialog();
|
||||
|
@ -49,7 +49,7 @@
|
||||
</Grid>
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="ServerDetailsMenuItem" Visibility="Collapsed" Click="ServerDetailsMenuItem_Click">
|
||||
<MenuItem x:Name="ServerDetailsMenuItem" Visibility="Collapsed" Click="ServerDetailsMenuItem_Click">
|
||||
<MenuItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
@ -57,11 +57,23 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:SymbolIcon Grid.Column="0" Symbol="Info28"/>
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="4,0,0,0" Text="{x:Static resources:Strings.ContextMenu_SeeServerDetails}" />
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="4,0,0,0" Text="{x:Static resources:Strings.ContextMenu_ServerInformation_Title}" />
|
||||
</Grid>
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="CloseRobloxMenuItem" Visibility="Collapsed" Click="CloseRobloxMenuItem_Click">
|
||||
<MenuItem x:Name="GameHistoryMenuItem" Click="JoinLastServerMenuItem_Click" Visibility="Collapsed">
|
||||
<MenuItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="24" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:SymbolIcon Grid.Column="0" Symbol="History24"/>
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="4,0,0,0" Text="{x:Static resources:Strings.ContextMenu_GameHistory_Title}" />
|
||||
</Grid>
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
<MenuItem Click="CloseRobloxMenuItem_Click">
|
||||
<MenuItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
@ -73,7 +85,7 @@
|
||||
</Grid>
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
<MenuItem x:Name="LogTracerMenuItem" Click="LogTracerMenuItem_Click">
|
||||
<MenuItem x:Name="LogTracerMenuItem" Visibility="Collapsed" Click="LogTracerMenuItem_Click">
|
||||
<MenuItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
@ -81,7 +93,7 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:SymbolIcon Grid.Column="0" Symbol="DocumentCatchUp20"/>
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="4,0,0,0" Text="{x:Static resources:Strings.ContextMenu_OpenLogFile}" />
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="4,0,0,0" Text="{x:Static resources:Strings.Common_OpenLogFile}" />
|
||||
</Grid>
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
|
@ -2,16 +2,11 @@
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Interop;
|
||||
|
||||
using Wpf.Ui.Appearance;
|
||||
using Wpf.Ui.Mvvm.Contracts;
|
||||
using Wpf.Ui.Mvvm.Services;
|
||||
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
using Bloxstrap.Integrations;
|
||||
using Bloxstrap.Resources;
|
||||
|
||||
namespace Bloxstrap.UI.Elements.ContextMenu
|
||||
{
|
||||
@ -22,31 +17,32 @@ namespace Bloxstrap.UI.Elements.ContextMenu
|
||||
{
|
||||
// i wouldve gladly done this as mvvm but turns out that data binding just does not work with menuitems for some reason so idk this sucks
|
||||
|
||||
private readonly ActivityWatcher? _activityWatcher;
|
||||
private readonly DiscordRichPresence? _richPresenceHandler;
|
||||
private readonly Watcher _watcher;
|
||||
|
||||
private ActivityWatcher? _activityWatcher => _watcher.ActivityWatcher;
|
||||
|
||||
private ServerInformation? _serverInformationWindow;
|
||||
private int? _processId;
|
||||
|
||||
public MenuContainer(ActivityWatcher? activityWatcher, DiscordRichPresence? richPresenceHandler, int? processId)
|
||||
private ServerHistory? _gameHistoryWindow;
|
||||
|
||||
public MenuContainer(Watcher watcher)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_activityWatcher = activityWatcher;
|
||||
_richPresenceHandler = richPresenceHandler;
|
||||
_processId = processId;
|
||||
_watcher = watcher;
|
||||
|
||||
if (_activityWatcher is not null)
|
||||
{
|
||||
_activityWatcher.OnLogOpen += ActivityWatcher_OnLogOpen;
|
||||
_activityWatcher.OnGameJoin += ActivityWatcher_OnGameJoin;
|
||||
_activityWatcher.OnGameLeave += ActivityWatcher_OnGameLeave;
|
||||
}
|
||||
|
||||
if (_richPresenceHandler is not null)
|
||||
if (_watcher.RichPresence is not null)
|
||||
RichPresenceMenuItem.Visibility = Visibility.Visible;
|
||||
|
||||
if (_processId is not null)
|
||||
CloseRobloxMenuItem.Visibility = Visibility.Visible;
|
||||
if (!App.Settings.Prop.UseDisableAppPatch)
|
||||
GameHistoryMenuItem.Visibility = Visibility.Visible;
|
||||
|
||||
VersionTextBlock.Text = $"{App.ProjectName} v{App.Version}";
|
||||
}
|
||||
@ -55,27 +51,33 @@ namespace Bloxstrap.UI.Elements.ContextMenu
|
||||
{
|
||||
if (_serverInformationWindow is null)
|
||||
{
|
||||
_serverInformationWindow = new ServerInformation(_activityWatcher!);
|
||||
_serverInformationWindow = new(_watcher);
|
||||
_serverInformationWindow.Closed += (_, _) => _serverInformationWindow = null;
|
||||
}
|
||||
|
||||
if (!_serverInformationWindow.IsVisible)
|
||||
_serverInformationWindow.Show();
|
||||
|
||||
_serverInformationWindow.Activate();
|
||||
_serverInformationWindow.ShowDialog();
|
||||
else
|
||||
_serverInformationWindow.Activate();
|
||||
}
|
||||
|
||||
private void ActivityWatcher_OnGameJoin(object? sender, EventArgs e)
|
||||
public void ActivityWatcher_OnLogOpen(object? sender, EventArgs e) =>
|
||||
Dispatcher.Invoke(() => LogTracerMenuItem.Visibility = Visibility.Visible);
|
||||
|
||||
public void ActivityWatcher_OnGameJoin(object? sender, EventArgs e)
|
||||
{
|
||||
if (_activityWatcher is null)
|
||||
return;
|
||||
|
||||
Dispatcher.Invoke(() => {
|
||||
if (_activityWatcher?.ActivityServerType == ServerType.Public)
|
||||
if (_activityWatcher.Data.ServerType == ServerType.Public)
|
||||
InviteDeeplinkMenuItem.Visibility = Visibility.Visible;
|
||||
|
||||
ServerDetailsMenuItem.Visibility = Visibility.Visible;
|
||||
});
|
||||
}
|
||||
|
||||
private void ActivityWatcher_OnGameLeave(object? sender, EventArgs e)
|
||||
public void ActivityWatcher_OnGameLeave(object? sender, EventArgs e)
|
||||
{
|
||||
Dispatcher.Invoke(() => {
|
||||
InviteDeeplinkMenuItem.Visibility = Visibility.Collapsed;
|
||||
@ -100,9 +102,9 @@ namespace Bloxstrap.UI.Elements.ContextMenu
|
||||
|
||||
private void Window_Closed(object sender, EventArgs e) => App.Logger.WriteLine("MenuContainer::Window_Closed", "Context menu container closed");
|
||||
|
||||
private void RichPresenceMenuItem_Click(object sender, RoutedEventArgs e) => _richPresenceHandler?.SetVisibility(((MenuItem)sender).IsChecked);
|
||||
private void RichPresenceMenuItem_Click(object sender, RoutedEventArgs e) => _watcher.RichPresence?.SetVisibility(((MenuItem)sender).IsChecked);
|
||||
|
||||
private void InviteDeeplinkMenuItem_Click(object sender, RoutedEventArgs e) => Clipboard.SetDataObject($"roblox://experiences/start?placeId={_activityWatcher?.ActivityPlaceId}&gameInstanceId={_activityWatcher?.ActivityJobId}");
|
||||
private void InviteDeeplinkMenuItem_Click(object sender, RoutedEventArgs e) => Clipboard.SetDataObject(_activityWatcher?.Data.GetInviteDeeplink());
|
||||
|
||||
private void ServerDetailsMenuItem_Click(object sender, RoutedEventArgs e) => ShowServerInformationWindow();
|
||||
|
||||
@ -110,13 +112,8 @@ namespace Bloxstrap.UI.Elements.ContextMenu
|
||||
{
|
||||
string? location = _activityWatcher?.LogLocation;
|
||||
|
||||
if (location is null)
|
||||
{
|
||||
Frontend.ShowMessageBox(Strings.ContextMenu_RobloxNotRunning, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
Utilities.ShellExecute(location);
|
||||
if (location is not null)
|
||||
Utilities.ShellExecute(location);
|
||||
}
|
||||
|
||||
private void CloseRobloxMenuItem_Click(object sender, RoutedEventArgs e)
|
||||
@ -130,9 +127,24 @@ namespace Bloxstrap.UI.Elements.ContextMenu
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
|
||||
using Process process = Process.GetProcessById((int)_processId!);
|
||||
process.Kill();
|
||||
process.Close();
|
||||
_watcher.KillRobloxProcess();
|
||||
}
|
||||
|
||||
private void JoinLastServerMenuItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_activityWatcher is null)
|
||||
throw new ArgumentNullException(nameof(_activityWatcher));
|
||||
|
||||
if (_gameHistoryWindow is null)
|
||||
{
|
||||
_gameHistoryWindow = new(_activityWatcher);
|
||||
_gameHistoryWindow.Closed += (_, _) => _gameHistoryWindow = null;
|
||||
}
|
||||
|
||||
if (!_gameHistoryWindow.IsVisible)
|
||||
_gameHistoryWindow.ShowDialog();
|
||||
else
|
||||
_gameHistoryWindow.Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
107
Bloxstrap/UI/Elements/ContextMenu/ServerHistory.xaml
Normal file
107
Bloxstrap/UI/Elements/ContextMenu/ServerHistory.xaml
Normal file
@ -0,0 +1,107 @@
|
||||
<base:WpfUiWindow x:Class="Bloxstrap.UI.Elements.ContextMenu.ServerHistory"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Bloxstrap.UI.Elements.ContextMenu"
|
||||
xmlns:base="clr-namespace:Bloxstrap.UI.Elements.Base"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
xmlns:models="clr-namespace:Bloxstrap.UI.ViewModels.ContextMenu"
|
||||
xmlns:resources="clr-namespace:Bloxstrap.Resources"
|
||||
xmlns:enums="clr-namespace:Bloxstrap.Enums"
|
||||
d:DataContext="{d:DesignInstance Type=models:ServerHistoryViewModel}"
|
||||
mc:Ignorable="d"
|
||||
Title="{x:Static resources:Strings.ContextMenu_GameHistory_Title}"
|
||||
MinWidth="420"
|
||||
MinHeight="420"
|
||||
Width="580"
|
||||
Height="420"
|
||||
Background="{ui:ThemeResource ApplicationBackgroundBrush}"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ui:TitleBar Grid.Row="0" Grid.ColumnSpan="2" Padding="8" x:Name="RootTitleBar" Title="{x:Static resources:Strings.ContextMenu_GameHistory_Title}" ShowMinimize="False" ShowMaximize="False" CanMaximize="False" KeyboardNavigation.TabNavigation="None" Icon="pack://application:,,,/Bloxstrap.ico" />
|
||||
|
||||
<TextBlock Grid.Row="1" Margin="16,8,16,0" Text="{x:Static resources:Strings.ContextMenu_GameHistory_Description}" TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock Grid.Row="2" Margin="16,8,16,0" Text="{Binding Error, Mode=OneWay}" TextWrapping="Wrap">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding LoadState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Failed}">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
|
||||
<Border Grid.Row="2">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding LoadState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Unknown}">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Style>
|
||||
</Border.Style>
|
||||
|
||||
<ui:ProgressRing Grid.Row="1" IsIndeterminate="True" />
|
||||
</Border>
|
||||
|
||||
<ListView Grid.Row="2" ItemsSource="{Binding GameHistory, Mode=OneWay}" Margin="8">
|
||||
<ListView.Style>
|
||||
<Style TargetType="ListView" BasedOn="{StaticResource {x:Type ListView}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding LoadState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Successful}">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Style>
|
||||
</ListView.Style>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ui:Card Padding="12">
|
||||
<Grid VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Grid.Column="0" Width="84" Height="84" CornerRadius="4">
|
||||
<Border.Background>
|
||||
<ImageBrush ImageSource="{Binding UniverseDetails.Thumbnail.ImageUrl, IsAsync=True}" />
|
||||
</Border.Background>
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Column="1" Margin="16,0,0,0" VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding UniverseDetails.Data.Name}" FontSize="18" FontWeight="Medium" />
|
||||
<TextBlock Text="{Binding GameHistoryDescription}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<ui:Button Margin="0,8,0,0" Content="{x:Static resources:Strings.ContextMenu_GameHistory_Rejoin}" Command="{Binding RejoinServerCommand}"
|
||||
Appearance="Success" Foreground="White" IconForeground="White" Icon="Play28" IconFilled="True" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ui:Card>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<Border Grid.Row="3" Padding="15" Background="{ui:ThemeResource SolidBackgroundFillColorSecondaryBrush}">
|
||||
<StackPanel Orientation="Horizontal" FlowDirection="LeftToRight" HorizontalAlignment="Right">
|
||||
<Button Margin="12,0,0,0" MinWidth="100" Content="{x:Static resources:Strings.Common_Close}" IsCancel="True" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</base:WpfUiWindow>
|
21
Bloxstrap/UI/Elements/ContextMenu/ServerHistory.xaml.cs
Normal file
21
Bloxstrap/UI/Elements/ContextMenu/ServerHistory.xaml.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using Bloxstrap.Integrations;
|
||||
using Bloxstrap.UI.ViewModels.ContextMenu;
|
||||
|
||||
namespace Bloxstrap.UI.Elements.ContextMenu
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ServerInformation.xaml
|
||||
/// </summary>
|
||||
public partial class ServerHistory
|
||||
{
|
||||
public ServerHistory(ActivityWatcher watcher)
|
||||
{
|
||||
var viewModel = new ServerHistoryViewModel(watcher);
|
||||
|
||||
viewModel.RequestCloseEvent += (_, _) => Close();
|
||||
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -46,14 +46,14 @@
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,16,12" VerticalAlignment="Center" Text="{x:Static resources:Strings.ContextMenu_ServerInformation_InstanceId}" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="{Binding InstanceId, Mode=OneWay}" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,16,12" VerticalAlignment="Center" Text="{x:Static resources:Strings.ContextMenu_ServerInformation_Location}" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="{Binding ServerLocation, Mode=OneWay}" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,16,12" VerticalAlignment="Center" Text="{x:Static resources:Strings.ContextMenu_ServerInformation_Location}" Visibility="{Binding ServerLocationVisibility, Mode=OneTime}" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="{Binding ServerLocation, Mode=OneWay}" Visibility="{Binding ServerLocationVisibility, Mode=OneTime}" />
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Row="2" Padding="15" Background="{ui:ThemeResource SolidBackgroundFillColorSecondaryBrush}">
|
||||
<StackPanel Orientation="Horizontal" FlowDirection="LeftToRight" HorizontalAlignment="Right">
|
||||
<Button MinWidth="100" Content="{x:Static resources:Strings.ContextMenu_ServerInformation_CopyInstanceId}" Command="{Binding CopyInstanceIdCommand, Mode=OneTime}" />
|
||||
<Button Margin="12,0,0,0" MinWidth="100" Content="{x:Static resources:Strings.Common_Close}" Command="{Binding CloseWindowCommand, Mode=OneTime}" />
|
||||
<Button Margin="12,0,0,0" MinWidth="100" Content="{x:Static resources:Strings.Common_Close}" IsCancel="True" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
@ -22,9 +22,9 @@ namespace Bloxstrap.UI.Elements.ContextMenu
|
||||
/// </summary>
|
||||
public partial class ServerInformation
|
||||
{
|
||||
public ServerInformation(ActivityWatcher activityWatcher)
|
||||
public ServerInformation(Watcher watcher)
|
||||
{
|
||||
DataContext = new ServerInformationViewModel(this, activityWatcher);
|
||||
DataContext = new ServerInformationViewModel(watcher);
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
Width="480"
|
||||
MinHeight="0"
|
||||
SizeToContent="Height"
|
||||
Title="{x:Static resources:Strings.Dialog_Connectivity_Title}"
|
||||
Background="{ui:ThemeResource ApplicationBackgroundBrush}"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
@ -29,9 +30,9 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Grid.Column="0" Width="32" Height="32" Margin="0,0,15,0" VerticalAlignment="Top" RenderOptions.BitmapScalingMode="HighQuality" Source="pack://application:,,,/Resources/MessageBox/Error.png" />
|
||||
<Image x:Name="IconImage" Grid.Column="0" Width="32" Height="32" Margin="0,0,15,0" VerticalAlignment="Top" RenderOptions.BitmapScalingMode="HighQuality" Source="pack://application:,,,/Resources/MessageBox/Error.png" />
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock x:Name="TitleTextBlock" Text="? is unable to connect to ?" FontSize="18" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
<TextBlock x:Name="TitleTextBlock" Text="? is unable to connect to ?" FontSize="18" Foreground="{DynamicResource TextFillColorPrimaryBrush}" TextWrapping="Wrap" />
|
||||
<controls:MarkdownTextBlock x:Name="DescriptionTextBlock" MarkdownText="?" Margin="0,16,0,0" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
<TextBlock Text="{x:Static resources:Strings.Dialog_Connectivity_MoreInfo}" Margin="0,16,0,0" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
<RichTextBox x:Name="ErrorRichTextBox" Padding="8" Margin="0,8,0,0" Block.LineHeight="2" FontFamily="Courier New" IsReadOnly="True" />
|
||||
|
@ -1,5 +1,7 @@
|
||||
using System.Media;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
@ -14,10 +16,41 @@ namespace Bloxstrap.UI.Elements.Dialogs
|
||||
/// </summary>
|
||||
public partial class ConnectivityDialog
|
||||
{
|
||||
public ConnectivityDialog(string title, string description, Exception exception)
|
||||
public ConnectivityDialog(string title, string description, MessageBoxImage image, Exception exception)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
string? iconFilename = null;
|
||||
SystemSound? sound = null;
|
||||
|
||||
switch (image)
|
||||
{
|
||||
case MessageBoxImage.Error:
|
||||
iconFilename = "Error";
|
||||
sound = SystemSounds.Hand;
|
||||
break;
|
||||
|
||||
case MessageBoxImage.Question:
|
||||
iconFilename = "Question";
|
||||
sound = SystemSounds.Question;
|
||||
break;
|
||||
|
||||
case MessageBoxImage.Warning:
|
||||
iconFilename = "Warning";
|
||||
sound = SystemSounds.Exclamation;
|
||||
break;
|
||||
|
||||
case MessageBoxImage.Information:
|
||||
iconFilename = "Information";
|
||||
sound = SystemSounds.Asterisk;
|
||||
break;
|
||||
}
|
||||
|
||||
if (iconFilename is null)
|
||||
IconImage.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
IconImage.Source = new BitmapImage(new Uri($"pack://application:,,,/Resources/MessageBox/{iconFilename}.png"));
|
||||
|
||||
TitleTextBlock.Text = title;
|
||||
DescriptionTextBlock.MarkdownText = description;
|
||||
|
||||
@ -28,7 +61,7 @@ namespace Bloxstrap.UI.Elements.Dialogs
|
||||
Close();
|
||||
};
|
||||
|
||||
SystemSounds.Hand.Play();
|
||||
sound?.Play();
|
||||
|
||||
Loaded += delegate
|
||||
{
|
||||
|
@ -33,7 +33,7 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Grid.Column="0" Width="32" Height="32" Margin="0,0,15,0" VerticalAlignment="Top" RenderOptions.BitmapScalingMode="HighQuality" Source="pack://application:,,,/Resources/MessageBox/Error.png" />
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="{x:Static resources:Strings.Dialog_Exception_Info_1}" FontSize="18" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
<TextBlock Text="{x:Static resources:Strings.Dialog_Exception_Info_1}" FontSize="18" Foreground="{DynamicResource TextFillColorPrimaryBrush}" TextWrapping="Wrap" />
|
||||
<RichTextBox x:Name="ErrorRichTextBox" Padding="8" Margin="0,16,0,0" Block.LineHeight="2" FontFamily="Courier New" IsReadOnly="True" />
|
||||
<controls:MarkdownTextBlock x:Name="HelpMessageMDTextBlock" Margin="0,16,0,0" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
</StackPanel>
|
||||
@ -41,7 +41,7 @@
|
||||
|
||||
<Border Grid.Row="2" Padding="15" Background="{ui:ThemeResource SolidBackgroundFillColorSecondaryBrush}">
|
||||
<StackPanel Orientation="Horizontal" FlowDirection="LeftToRight" HorizontalAlignment="Right">
|
||||
<Button x:Name="LocateLogFileButton" Content="{x:Static resources:Strings.Common_LocateLogFile}" />
|
||||
<Button x:Name="LocateLogFileButton" Content="{x:Static resources:Strings.Common_OpenLogFile}" />
|
||||
<Button x:Name="CloseButton" MinWidth="100" Content="{x:Static resources:Strings.Common_Close}" Margin="12,0,0,0" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
@ -1,13 +1,11 @@
|
||||
using System.Media;
|
||||
using System.Web;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Interop;
|
||||
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
|
||||
using Bloxstrap.Resources;
|
||||
|
||||
namespace Bloxstrap.UI.Elements.Dialogs
|
||||
{
|
||||
// hmm... do i use MVVM for this?
|
||||
@ -26,19 +24,29 @@ namespace Bloxstrap.UI.Elements.Dialogs
|
||||
if (!App.Logger.Initialized)
|
||||
LocateLogFileButton.Content = Strings.Dialog_Exception_CopyLogContents;
|
||||
|
||||
string helpMessage = String.Format(Strings.Dialog_Exception_Info_2, "https://github.com/pizzaboxer/bloxstrap/wiki", "https://github.com/pizzaboxer/bloxstrap/issues/new?template=bug_report.yaml");
|
||||
string repoUrl = $"https://github.com/{App.ProjectRepository}";
|
||||
string wikiUrl = $"{repoUrl}/wiki";
|
||||
|
||||
if (String.IsNullOrEmpty(App.BuildMetadata.CommitHash))
|
||||
helpMessage = String.Format(Strings.Dialog_Exception_Info_2_Alt, "https://github.com/pizzaboxer/bloxstrap/wiki");
|
||||
string issueUrl = String.Format(
|
||||
"{0}/issues/new?template=bug_report.yaml&title={1}&log={2}",
|
||||
repoUrl,
|
||||
HttpUtility.UrlEncode($"[BUG] {exception.GetType()}: {exception.Message}"),
|
||||
HttpUtility.UrlEncode(String.Join('\n', App.Logger.History))
|
||||
);
|
||||
|
||||
string helpMessage = String.Format(Strings.Dialog_Exception_Info_2, wikiUrl, issueUrl);
|
||||
|
||||
if (!App.IsActionBuild && !App.BuildMetadata.Machine.Contains("pizzaboxer", StringComparison.Ordinal))
|
||||
helpMessage = String.Format(Strings.Dialog_Exception_Info_2_Alt, wikiUrl);
|
||||
|
||||
HelpMessageMDTextBlock.MarkdownText = helpMessage;
|
||||
|
||||
LocateLogFileButton.Click += delegate
|
||||
{
|
||||
if (App.Logger.Initialized)
|
||||
Process.Start("explorer.exe", $"/select,\"{App.Logger.FileLocation}\"");
|
||||
if (App.Logger.Initialized && !String.IsNullOrEmpty(App.Logger.FileLocation))
|
||||
Utilities.ShellExecute(App.Logger.FileLocation);
|
||||
else
|
||||
Clipboard.SetDataObject(String.Join("\r\n", App.Logger.Backlog));
|
||||
Clipboard.SetDataObject(String.Join("\r\n", App.Logger.History));
|
||||
};
|
||||
|
||||
CloseButton.Click += delegate
|
||||
|
@ -41,7 +41,7 @@ namespace Bloxstrap.UI.Elements.Dialogs
|
||||
|
||||
case MessageBoxImage.Warning:
|
||||
iconFilename = "Warning";
|
||||
sound = SystemSounds.Asterisk;
|
||||
sound = SystemSounds.Exclamation;
|
||||
break;
|
||||
|
||||
case MessageBoxImage.Information:
|
||||
@ -121,13 +121,13 @@ namespace Bloxstrap.UI.Elements.Dialogs
|
||||
switch (result)
|
||||
{
|
||||
case MessageBoxResult.OK:
|
||||
return Bloxstrap.Resources.Strings.Common_OK;
|
||||
return Strings.Common_OK;
|
||||
case MessageBoxResult.Cancel:
|
||||
return Bloxstrap.Resources.Strings.Common_Cancel;
|
||||
return Strings.Common_Cancel;
|
||||
case MessageBoxResult.Yes:
|
||||
return Bloxstrap.Resources.Strings.Common_Yes;
|
||||
return Strings.Common_Yes;
|
||||
case MessageBoxResult.No:
|
||||
return Bloxstrap.Resources.Strings.Common_No;
|
||||
return Strings.Common_No;
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
return result.ToString();
|
||||
|
@ -12,7 +12,7 @@
|
||||
Title="Bloxstrap"
|
||||
MinWidth="0"
|
||||
MinHeight="0"
|
||||
Width="320"
|
||||
Width="580"
|
||||
SizeToContent="Height"
|
||||
ResizeMode="NoResize"
|
||||
Background="{ui:ThemeResource ApplicationBackgroundBrush}"
|
||||
@ -24,31 +24,61 @@
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ui:TitleBar Grid.Row="0" Grid.ColumnSpan="2" Padding="8" Title="Bloxstrap" ShowMinimize="False" ShowMaximize="False" CanMaximize="False" KeyboardNavigation.TabNavigation="None" Icon="pack://application:,,,/Bloxstrap.ico" />
|
||||
<ui:TitleBar Grid.Row="0" Grid.ColumnSpan="2" Padding="8" ShowMinimize="False" ShowMaximize="False" Title="Bloxstrap" Icon="pack://application:,,,/Bloxstrap.ico" CanMaximize="False" KeyboardNavigation.TabNavigation="None" />
|
||||
|
||||
<StackPanel Grid.Row="1" Margin="12">
|
||||
<TextBlock FontSize="24" Text="{x:Static resources:Strings.LaunchMenu_Title}" HorizontalAlignment="Center" Margin="0,0,0,16" />
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ui:CardAction Icon="ArrowRight12" Command="{Binding LaunchRobloxCommand, Mode=OneTime}">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="{x:Static resources:Strings.LaunchMenu_LaunchRoblox}" />
|
||||
<Grid Grid.Column="0" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" HorizontalAlignment="Center" Margin="0,0,0,32">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image Grid.Column="0" Width="64" Height="64" Source="pack://application:,,,/Bloxstrap.ico" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<StackPanel Grid.Column="1" Margin="12,0,0,0" VerticalAlignment="Center">
|
||||
<TextBlock Text="Bloxstrap" FontSize="24" />
|
||||
<TextBlock Text="{Binding Version, Mode=OneTime}" VerticalAlignment="Bottom" FontSize="12" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="1" HorizontalAlignment="Center">
|
||||
<ui:Hyperlink Icon="QuestionCircle48" Content="About Bloxstrap" Margin="0,0,0,8" Command="{Binding LaunchAboutCommand, Mode=OneTime}" />
|
||||
<ui:Hyperlink Icon="Heart48" Content="Support us on Ko-fi!" NavigateUri="https://ko-fi.com/boxerpizza" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
</Grid>
|
||||
|
||||
<ui:CardAction Margin="0,8,0,0" Icon="Settings28" Command="{Binding LaunchSettingsCommand, Mode=OneTime}">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="{x:Static resources:Strings.LaunchMenu_ConfigureSettings}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<StackPanel Grid.Column="1" Margin="16">
|
||||
<ui:CardAction Icon="ArrowRight12" Command="{Binding LaunchRobloxCommand, Mode=OneTime}">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="{x:Static resources:Strings.LaunchMenu_LaunchRoblox}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
|
||||
<ui:CardAction Margin="0,8,0,0" Icon="BookQuestionMark24" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/pizzaboxer/bloxstrap/wiki/">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="{x:Static resources:Strings.LaunchMenu_Wiki_Title}" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.LaunchMenu_Wiki_Description}" Padding="0,0,16,0" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Margin="0,8,0,0" Icon="Settings28" Command="{Binding LaunchSettingsCommand, Mode=OneTime}">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="{x:Static resources:Strings.LaunchMenu_ConfigureSettings}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
|
||||
<TextBlock Margin="0,16,0,0" FontSize="12" Text="{Binding Version, Mode=OneTime}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
<Border Margin="16" />
|
||||
|
||||
<ui:CardAction Margin="0,8,0,0" Icon="BookQuestionMark24" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/pizzaboxer/bloxstrap/wiki/">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="{x:Static resources:Strings.LaunchMenu_Wiki_Title}" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.LaunchMenu_Wiki_Description}" Padding="0,0,16,0" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</base:WpfUiWindow>
|
||||
|
@ -9,6 +9,7 @@ using Bloxstrap.UI.Elements.Installer.Pages;
|
||||
using Bloxstrap.UI.Elements.Base;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using Bloxstrap.Resources;
|
||||
|
||||
namespace Bloxstrap.UI.Elements.Installer
|
||||
{
|
||||
@ -102,7 +103,7 @@ namespace Bloxstrap.UI.Elements.Installer
|
||||
if (Finished)
|
||||
return;
|
||||
|
||||
var result = Frontend.ShowMessageBox("Are you sure you want to cancel the installation?", MessageBoxImage.Warning, MessageBoxButton.YesNo);
|
||||
var result = Frontend.ShowMessageBox(Strings.Installer_ShouldCancel, MessageBoxImage.Warning, MessageBoxButton.YesNo);
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
e.Cancel = true;
|
||||
|
@ -15,7 +15,8 @@
|
||||
Background="{ui:ThemeResource ApplicationBackgroundBrush}"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
WindowBackdropType="Mica"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
WindowStartupLocation="CenterScreen"
|
||||
Closing="WpfUiWindow_Closing">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
@ -60,7 +61,7 @@
|
||||
<ui:NavigationItem Content="" PageType="{x:Type pages:FastFlagEditorWarningPage}" Tag="fastflageditorwarning" Visibility="Collapsed" x:Name="EditorWarningNavItem" />
|
||||
</ui:NavigationFluent.Items>
|
||||
<ui:NavigationFluent.Footer>
|
||||
<ui:NavigationItem Content="{x:Static resources:Strings.Menu_About_Title}" PageType="{x:Type pages:AboutPage}" Icon="QuestionCircle48" Tag="about" Margin="0,0,0,12" />
|
||||
<ui:NavigationItem Content="{x:Static resources:Strings.Menu_About_Title}" Icon="QuestionCircle48" Margin="0,0,0,12" Command="{Binding OpenAboutCommand, Mode=OneTime}" />
|
||||
</ui:NavigationFluent.Footer>
|
||||
</ui:NavigationFluent>
|
||||
|
||||
@ -93,7 +94,7 @@
|
||||
<ui:Button Content="{x:Static resources:Strings.Menu_Save}" Appearance="Primary" Command="{Binding SaveSettingsCommand, Mode=OneWay}" />
|
||||
</StatusBarItem>
|
||||
<StatusBarItem Grid.Column="2" Padding="4,0,0,0">
|
||||
<ui:Button Content="{x:Static resources:Strings.Common_Close}" IsCancel="True" />
|
||||
<ui:Button Content="{x:Static resources:Strings.Common_Close}" Command="{Binding CloseWindowCommand, Mode=OneWay}" />
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
|
@ -1,6 +1,10 @@
|
||||
using System.Windows.Controls;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
using Wpf.Ui.Controls.Interfaces;
|
||||
using Wpf.Ui.Mvvm.Contracts;
|
||||
|
||||
using Bloxstrap.UI.ViewModels.Settings;
|
||||
|
||||
namespace Bloxstrap.UI.Elements.Settings
|
||||
@ -10,10 +14,14 @@ namespace Bloxstrap.UI.Elements.Settings
|
||||
/// </summary>
|
||||
public partial class MainWindow : INavigationWindow
|
||||
{
|
||||
private Models.Persistable.WindowState _state => App.State.Prop.SettingsWindow;
|
||||
|
||||
public MainWindow(bool showAlreadyRunningWarning)
|
||||
{
|
||||
var viewModel = new MainWindowViewModel();
|
||||
|
||||
viewModel.RequestSaveNoticeEvent += (_, _) => SettingsSavedSnackbar.Show();
|
||||
viewModel.RequestCloseWindowEvent += (_, _) => Close();
|
||||
|
||||
DataContext = viewModel;
|
||||
|
||||
@ -22,11 +30,35 @@ namespace Bloxstrap.UI.Elements.Settings
|
||||
App.Logger.WriteLine("MainWindow::MainWindow", "Initializing menu");
|
||||
|
||||
#if DEBUG // easier access
|
||||
EditorWarningNavItem.Visibility = System.Windows.Visibility.Visible;
|
||||
EditorWarningNavItem.Visibility = Visibility.Visible;
|
||||
#endif
|
||||
|
||||
if (showAlreadyRunningWarning)
|
||||
ShowAlreadyRunningSnackbar();
|
||||
|
||||
LoadState();
|
||||
}
|
||||
|
||||
public void LoadState()
|
||||
{
|
||||
if (_state.Left > SystemParameters.VirtualScreenWidth)
|
||||
_state.Left = 0;
|
||||
|
||||
if (_state.Top > SystemParameters.VirtualScreenHeight)
|
||||
_state.Top = 0;
|
||||
|
||||
if (_state.Width > 0)
|
||||
this.Width = _state.Width;
|
||||
|
||||
if (_state.Height > 0)
|
||||
this.Height = _state.Height;
|
||||
|
||||
if (_state.Left > 0 && _state.Top > 0)
|
||||
{
|
||||
this.WindowStartupLocation = WindowStartupLocation.Manual;
|
||||
this.Left = _state.Left;
|
||||
this.Top = _state.Top;
|
||||
}
|
||||
}
|
||||
|
||||
private async void ShowAlreadyRunningSnackbar()
|
||||
@ -50,5 +82,27 @@ namespace Bloxstrap.UI.Elements.Settings
|
||||
public void CloseWindow() => Close();
|
||||
|
||||
#endregion INavigationWindow methods
|
||||
|
||||
private void WpfUiWindow_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
if (App.FastFlags.Changed || App.PendingSettingTasks.Any())
|
||||
{
|
||||
var result = Frontend.ShowMessageBox(Strings.Menu_UnsavedChanges, MessageBoxImage.Warning, MessageBoxButton.YesNo);
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
e.Cancel = true;
|
||||
}
|
||||
|
||||
_state.Width = this.Width;
|
||||
_state.Height = this.Height;
|
||||
|
||||
_state.Top = this.Top;
|
||||
_state.Left = this.Left;
|
||||
|
||||
App.State.Save();
|
||||
|
||||
if (!e.Cancel)
|
||||
App.Terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,665 +0,0 @@
|
||||
<ui:UiPage x:Class="Bloxstrap.UI.Elements.Settings.Pages.AboutPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:models="clr-namespace:Bloxstrap.UI.ViewModels"
|
||||
xmlns:controls="clr-namespace:Bloxstrap.UI.Elements.Controls"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
xmlns:resources="clr-namespace:Bloxstrap.Resources"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="1500" d:DesignWidth="800"
|
||||
Title="AboutPage"
|
||||
Scrollable="True">
|
||||
<StackPanel Margin="0,0,14,14">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image Grid.Column="0" Width="60" Height="60" VerticalAlignment="Center" Source="pack://application:,,,/Bloxstrap.ico" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<StackPanel Grid.Column="1" Margin="12,0,0,0" VerticalAlignment="Center">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="Bloxstrap" Margin="0,0,4,0" FontSize="24" FontWeight="Medium" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding Version, Mode=OneTime}" Margin="4,0,0,2" VerticalAlignment="Bottom" FontSize="16" FontWeight="Medium" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</Grid>
|
||||
<TextBlock Text="{x:Static resources:Strings.Menu_About_Description}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<WrapPanel Orientation="Horizontal">
|
||||
<ui:Anchor Margin="0,16,8,0" Content="{x:Static resources:Strings.Menu_About_GithubRepository}" Icon="Code24" NavigateUri="https://github.com/pizzaboxer/bloxstrap" />
|
||||
<ui:Anchor Margin="0,16,8,0" Content="{x:Static resources:Strings.Menu_About_ReportIssue}" Icon="Chat48" NavigateUri="https://github.com/pizzaboxer/bloxstrap/issues" />
|
||||
<ui:Anchor Margin="0,16,8,0" Content="{x:Static resources:Strings.Menu_About_HelpInformation}" Icon="BookQuestionMark24" NavigateUri="https://github.com/pizzaboxer/bloxstrap/wiki" />
|
||||
<ui:Anchor Margin="0,16,0,0" Content="{x:Static resources:Strings.Menu_About_DiscordServer}" Icon="Chat48" NavigateUri="https://discord.gg/nKjV3mGq6R" />
|
||||
</WrapPanel>
|
||||
|
||||
<StackPanel Visibility="{Binding BuildInformationVisibility, Mode=OneTime}">
|
||||
<TextBlock Text="Build Information" FontWeight="Medium" FontSize="20" Margin="0,16,0,0" />
|
||||
<TextBlock Text="hmmmm" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
|
||||
<Grid Column="0" Margin="0,8,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0,4,16,4" FontSize="14" FontWeight="Medium" Text="Timestamp" />
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,0,4" VerticalAlignment="Bottom" Text="{Binding BuildTimestamp, Mode=OneTime}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,4,16,4" FontSize="14" FontWeight="Medium" Text="Machine" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,0,4" VerticalAlignment="Bottom" Text="{Binding BuildMetadata.Machine, Mode=OneTime}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,4,16,4" FontSize="14" FontWeight="Medium" Text="Commit Hash" Visibility="{Binding BuildCommitVisibility, Mode=OneTime}" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Margin="0,0,0,4" VerticalAlignment="Bottom" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Visibility="{Binding BuildCommitVisibility, Mode=OneTime}">
|
||||
<Hyperlink Foreground="{DynamicResource TextFillColorTertiaryBrush}" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="{Binding BuildCommitHashUrl, Mode=OneTime}">
|
||||
<TextBlock Text="{Binding BuildMetadata.CommitHash, Mode=OneTime}" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Margin="0,4,16,4" FontSize="14" FontWeight="Medium" Text="Commit Ref" Visibility="{Binding BuildCommitVisibility, Mode=OneTime}" />
|
||||
<TextBlock Grid.Row="3" Grid.Column="1" Margin="0,0,0,4" VerticalAlignment="Bottom" Text="{Binding BuildMetadata.CommitRef, Mode=OneTime}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Visibility="{Binding BuildCommitVisibility, Mode=OneTime}" />
|
||||
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Text="{x:Static resources:Strings.Menu_About_Contributors}" FontWeight="Medium" FontSize="20" Margin="0,16,0,0" />
|
||||
<TextBlock Text="{x:Static resources:Strings.Menu_About_Contributors_Description}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<controls:Expander Grid.Row="0" Grid.ColumnSpan="3" Margin="0,0,0,8" HeaderIcon="Translate24" HeaderText="{x:Static resources:Strings.Menu_About_Contributors_Translations}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Text="Bahasa Indonesia" FontSize="16" FontWeight="Medium">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="e7leopard" />
|
||||
<TextBlock Text="hfzrk" />
|
||||
<TextBlock Text="soudblox" />
|
||||
|
||||
<TextBlock Text="Bokmål" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="darkevilmage" />
|
||||
<TextBlock Text="endsouls" />
|
||||
<TextBlock Text="letoek" />
|
||||
|
||||
<!--<TextBlock Text="Čeština" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Chusaak" />
|
||||
<TextBlock Text="DanyCraftCZ" />
|
||||
<TextBlock Text="Franklin_Surten" />
|
||||
<TextBlock Text="Galaxy_Gangster6" />
|
||||
<TextBlock Text="jasperholo" />
|
||||
<TextBlock Text="letoek" />
|
||||
<TextBlock Text="noobkid4545" />
|
||||
<TextBlock Text="radim776" />-->
|
||||
|
||||
<!-- <TextBlock Text="Dansk" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Momslayer98" />
|
||||
<TextBlock Text="SirBlue" /> -->
|
||||
|
||||
<TextBlock Text="Deutsch" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="agent_phoenix" />
|
||||
<TextBlock Text="avoidr" />
|
||||
<TextBlock Text="hcjohnsgd" />
|
||||
<TextBlock Text="hxmbt" />
|
||||
<TextBlock Text="kale123" />
|
||||
<TextBlock Text="Marvin_Chu" />
|
||||
<TextBlock Text="nzxt_xll" />
|
||||
<TextBlock Text="Nlx095" />
|
||||
<TextBlock Text="Ph1lwtf" />
|
||||
<TextBlock Text="sxckqerz" />
|
||||
<TextBlock Text="TEAM_LILA" />
|
||||
<TextBlock Text="xDevoidx" />
|
||||
|
||||
<TextBlock Text="Español" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="4Xisty" />
|
||||
<TextBlock Text="Botkid" />
|
||||
<TextBlock Text="colocky" />
|
||||
<TextBlock Text="D0N-B0T" />
|
||||
<TextBlock Text="Dasp" />
|
||||
<TextBlock Text="devyyxn" />
|
||||
<TextBlock Text="ItzzExcel" />
|
||||
<TextBlock Text="Ilayhlinda" />
|
||||
<TextBlock Text="Ilushiouss" />
|
||||
<TextBlock Text="jayces." />
|
||||
<TextBlock Text="kroesufos" />
|
||||
<TextBlock Text="LaiyLiod" />
|
||||
<TextBlock Text="lyalekin" />
|
||||
<TextBlock Text="NezumiDS" />
|
||||
<TextBlock Text="NimuruDP" />
|
||||
<TextBlock Text="NescafeCL" />
|
||||
<TextBlock Text="Sw7gger" />
|
||||
<TextBlock Text="sxckqerz" />
|
||||
<TextBlock Text="Urzy" />
|
||||
|
||||
<TextBlock Text="Filipino" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="alphjectiom" />
|
||||
<TextBlock Text="FlaminDaPotato" />
|
||||
<TextBlock Text="RobiTheRobloxxer" />
|
||||
<TextBlock Text="shadow01148" />
|
||||
|
||||
<TextBlock Text="Français" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="At0zDx" />
|
||||
<TextBlock Text="built4aiming" />
|
||||
<TextBlock Text="hahaloserz360" />
|
||||
<TextBlock Text="K0ga" />
|
||||
<TextBlock Text="Marcssebaa" />
|
||||
<TextBlock Text="MommySernox" />
|
||||
<TextBlock Text="owentempest8" />
|
||||
<TextBlock Text="Subsical" />
|
||||
<TextBlock Text="thatsirwaffles" />
|
||||
<TextBlock Text="tyundrai" />
|
||||
<TextBlock Text="Waza80" />
|
||||
|
||||
<TextBlock Text="Hindi (Latin)" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="kunaljainop" />
|
||||
<TextBlock Text="marathedonroblox" />
|
||||
<TextBlock Text="Sur_" />
|
||||
<TextBlock Text="Tezos" />
|
||||
<TextBlock Text="TheTakuo" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="Hrvatski" FontSize="16" FontWeight="Medium">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Dzigos" />
|
||||
<TextBlock Text="Koyroii" />
|
||||
<TextBlock Text="Nemznja" />
|
||||
|
||||
<TextBlock Text="Italiano" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="crow_zxcr" />
|
||||
<TextBlock Text="devyyxn" />
|
||||
<TextBlock Text="domenicoiacono" />
|
||||
<TextBlock Text="hulabulaseop" />
|
||||
<TextBlock Text="lord_moth" />
|
||||
<TextBlock Text="loridori" />
|
||||
<TextBlock Text="Lupo01" />
|
||||
<TextBlock Text="Mogunars" />
|
||||
<TextBlock Text="pave08" />
|
||||
<TextBlock Text="spectrumbruh" />
|
||||
|
||||
<TextBlock Text="Lietuvių" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="cr0000142" />
|
||||
<TextBlock Text="Duexo" />
|
||||
<TextBlock Text="jessethedev" />
|
||||
<TextBlock Text="Vac31." />
|
||||
|
||||
<TextBlock Text="Magyar" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="DynoPlays" />
|
||||
<TextBlock Text="Elotomka" />
|
||||
<TextBlock Text="xM4rk1" />
|
||||
|
||||
<!--<TextBlock Text="Nederlands" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Cosmix" />
|
||||
<TextBlock Text="Miwwzy" />
|
||||
<TextBlock Text="Quickvision1" />
|
||||
<TextBlock Text="ydboss" />-->
|
||||
|
||||
<TextBlock Text="Polski" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Eztaru" />
|
||||
<TextBlock Text="lunar" />
|
||||
<TextBlock Text="markontm" />
|
||||
<TextBlock Text="my5q" />
|
||||
<TextBlock Text="nemzik2137" />
|
||||
<TextBlock Text="plexar" />
|
||||
<TextBlock Text="r.efil" />
|
||||
|
||||
<TextBlock Text="Português (Brasil)" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="anormalevis" />
|
||||
<TextBlock Text="ErisvaldoBalbino" />
|
||||
<TextBlock Text="G3xneric" />
|
||||
<TextBlock Text="hnter" />
|
||||
<TextBlock Text="issei_" />
|
||||
<TextBlock Text="iyto.lk" />
|
||||
<TextBlock Text="jhermesn" />
|
||||
<TextBlock Text="JorgeDaPelada" />
|
||||
<TextBlock Text="LwgoDev" />
|
||||
<TextBlock Text="nunk7" />
|
||||
<TextBlock Text="peke7374" />
|
||||
<TextBlock Text="SeeF" />
|
||||
<TextBlock Text="Snowzin1" />
|
||||
<TextBlock Text="storm930" />
|
||||
<TextBlock Text="toofastforboo" />
|
||||
<TextBlock Text="VMOICE" />
|
||||
<TextBlock Text="Ye4" />
|
||||
<TextBlock Text="ZaPeZaPe" />
|
||||
|
||||
<TextBlock Text="Română" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Externalkinetics" />
|
||||
<TextBlock Text="MonochromeAlex" />
|
||||
<TextBlock Text="PlayerValley" />
|
||||
<TextBlock Text="Smuki" />
|
||||
<TextBlock Text="theflopperguy" />
|
||||
|
||||
<TextBlock Text="Suomi" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="gelaxiz" />
|
||||
<TextBlock Text="jes5" />
|
||||
<TextBlock Text="retromaxwell" />
|
||||
<TextBlock Text="SomePinglord" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Text="Svenska" FontSize="16" FontWeight="Medium">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Axellse" />
|
||||
<TextBlock Text="CroppingFlea479" />
|
||||
<TextBlock Text="FishySpelar" />
|
||||
<TextBlock Text="PineappleSnackz" />
|
||||
<TextBlock Text="simonixen" />
|
||||
<TextBlock Text="thatgurkangurk" />
|
||||
|
||||
<TextBlock Text="Tiếng Việt" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="alexking2068" />
|
||||
<TextBlock Text="baterblx" />
|
||||
<TextBlock Text="Elytronn" />
|
||||
<TextBlock Text="fox810891" />
|
||||
<TextBlock Text="ItsPoofy" />
|
||||
<TextBlock Text="Limer1" />
|
||||
<TextBlock Text="makayu203332" />
|
||||
<TextBlock Text="MEx2/j7x6" />
|
||||
<TextBlock Text="NguyenDat208" />
|
||||
<TextBlock Text="quanmequankk" />
|
||||
<TextBlock Text="SomeRandomGuy175" />
|
||||
<TextBlock Text="SonThanhVN" />
|
||||
<TextBlock Text="teaanguyenn" />
|
||||
<TextBlock Text="Veiiorra" />
|
||||
|
||||
<TextBlock Text="Türkçe" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="canny19133" />
|
||||
<TextBlock Text="cfors55" />
|
||||
<TextBlock Text="drakfreddy" />
|
||||
<TextBlock Text="enisify" />
|
||||
<TextBlock Text="jayces." />
|
||||
<TextBlock Text="nyatie" />
|
||||
<TextBlock Text="PixelArmy" />
|
||||
<TextBlock Text="plants8332" />
|
||||
<TextBlock Text="r02" />
|
||||
<TextBlock Text="siyamicik" />
|
||||
<TextBlock Text="ydboss" />
|
||||
|
||||
<TextBlock Text="Українська" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="9zh" />
|
||||
<TextBlock Text="andrey3569s" />
|
||||
<TextBlock Text="DexterBloxxer" />
|
||||
<TextBlock Text="Externalkinetics" />
|
||||
<TextBlock Text="maksimvlad7" />
|
||||
|
||||
<TextBlock Text="Босански" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Cortex_1" />
|
||||
<TextBlock Text="Nemznja" />
|
||||
<TextBlock Text="Ren" />
|
||||
|
||||
<TextBlock Text="Български" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="GrafitNiki" />
|
||||
<TextBlock Text="sidefrappe" />
|
||||
|
||||
<TextBlock Text="Русский" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="3tcy" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="andBroz" />
|
||||
<TextBlock Text="alexneonwithglue" />
|
||||
<TextBlock Text="AnonymousDudeLOL123" />
|
||||
<TextBlock Text="aperna_of_the_ticks" />
|
||||
<TextBlock Text="arsenijveselov77" />
|
||||
<TextBlock Text="Art3mLapa" />
|
||||
<TextBlock Text="cherkash" />
|
||||
<TextBlock Text="cub-has-injected" />
|
||||
<TextBlock Text="dallyuser" />
|
||||
<TextBlock Text="Dr1mG" />
|
||||
<TextBlock Text="Externalkinetics" />
|
||||
<TextBlock Text="fxstyxx" />
|
||||
<TextBlock Text="Gustodd4202" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="harababura" />
|
||||
<TextBlock Text="ImperialRhyme" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="IStoleYourCheese" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="khat7" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="kostyan" />
|
||||
<TextBlock Text="Maks" />
|
||||
<TextBlock Text="niktoyou" />
|
||||
<TextBlock Text="nurgament2" />
|
||||
<TextBlock Text="poflexim" />
|
||||
<TextBlock Text="Prob1rka" />
|
||||
<TextBlock Text="Provo" />
|
||||
<TextBlock Text="Quenevelly" />
|
||||
<TextBlock Text="sally13249" />
|
||||
<TextBlock Text="simmon8800" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="Skylan031" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="Spuffio" />
|
||||
<TextBlock Text="StraiF" />
|
||||
<TextBlock Text="StrayCatSimb" />
|
||||
<TextBlock Text="Voxel" />
|
||||
<TextBlock Text="XonaShera" Foreground="{DynamicResource SystemFillColorCriticalBrush}" />
|
||||
<TextBlock Text="Ziio123" />
|
||||
<TextBlock Text="zor9na90000" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="3">
|
||||
<TextBlock Text="עברית" FontSize="16" FontWeight="Medium">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="1cur1" />
|
||||
<TextBlock Text="ilan0098" />
|
||||
<TextBlock Text="koerga" />
|
||||
<TextBlock Text="Sezei" />
|
||||
|
||||
<TextBlock Text="العربية" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="busguesjshbahsj" />
|
||||
<TextBlock Text="cq2i" />
|
||||
<TextBlock Text="mmiky" />
|
||||
<TextBlock Text="mostafagamingx1" />
|
||||
<TextBlock Text="RoRed" />
|
||||
<TextBlock Text="Sakupen" />
|
||||
<TextBlock Text="streoic" />
|
||||
<TextBlock Text="uvq18" />
|
||||
<TextBlock Text="wyfast" />
|
||||
<TextBlock Text="Zida" />
|
||||
|
||||
<TextBlock Text="বাংলা" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Arnian" />
|
||||
<TextBlock Text="Hydrated_panda" />
|
||||
<TextBlock Text="marathedonroblox" />
|
||||
<TextBlock Text="red_hi" />
|
||||
|
||||
<TextBlock Text="ภาษาไทย" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="._bonus_." />
|
||||
<TextBlock Text="marc15772" />
|
||||
<TextBlock Text="arthurwagon" />
|
||||
<TextBlock Text="Sem1z" />
|
||||
<TextBlock Text="xAom" />
|
||||
|
||||
<TextBlock Text="한국어" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="ADVI50R" />
|
||||
<TextBlock Text="asd123456fghqwerty" />
|
||||
<TextBlock Text="bacon1295" />
|
||||
<TextBlock Text="NightPlay" />
|
||||
|
||||
<TextBlock Text="中文 (简体)" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="14TQD" />
|
||||
<TextBlock Text="Aling00" />
|
||||
<TextBlock Text="Clock" />
|
||||
<TextBlock Text="ERSN_CERROR" />
|
||||
<TextBlock Text="Kirxvil" />
|
||||
<TextBlock Text="Typel" />
|
||||
<TextBlock Text="yuhaodatt" />
|
||||
|
||||
<TextBlock Text="中文 (廣東話)" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="henrychu1125" />
|
||||
<TextBlock Text="kitzure" />
|
||||
<TextBlock Text="Kimina898" />
|
||||
<TextBlock Text="shhh_op" />
|
||||
|
||||
<TextBlock Text="中文 (繁體)" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="14TQD" />
|
||||
<TextBlock Text="DXuwu" />
|
||||
<TextBlock Text="kitzure" />
|
||||
<TextBlock Text="Kimina898" />
|
||||
|
||||
<TextBlock Text="日本語" FontSize="16" FontWeight="Medium" Margin="0,16,0,0">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{DynamicResource SystemAccentColorSecondary}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Text="MintJapan" />
|
||||
<TextBlock Text="pimeja7" />
|
||||
<TextBlock Text="yixhuaa" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</controls:Expander>
|
||||
|
||||
<controls:Expander Grid.Row="1" Grid.Column="0" Margin="0,0,4,0" HeaderIcon="Code24" HeaderText="{x:Static resources:Strings.Menu_About_Contributors_Code}" IsExpanded="True">
|
||||
<StackPanel>
|
||||
<controls:MarkdownTextBlock MarkdownText="[Matt](https://github.com/bluepilledgreat)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[1011025m](https://github.com/1011025m)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[EasternBloxxer](https://github.com/EasternBloxxer)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[sitiom](https://github.com/sitiom)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[Extravi](https://github.com/Extravi)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[EpixScripts](https://github.com/EpixScripts)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[swatTurret](https://github.com/swatTurret)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[fxeP1](https://github.com/fxeP1)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[Redusofficial](https://github.com/Redusofficial)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[srthMD](https://github.com/srthMD)" />
|
||||
</StackPanel>
|
||||
</controls:Expander>
|
||||
|
||||
<controls:Expander Grid.Row="1" Grid.Column="1" Margin="4,0,4,0" HeaderIcon="AppsAddIn28" HeaderText="{x:Static resources:Strings.Menu_About_Contributors_FeatureIdeas}" IsExpanded="True">
|
||||
<StackPanel>
|
||||
<controls:MarkdownTextBlock MarkdownText="[he3als](https://github.com/he3als)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[NikSavchenk0](https://github.com/NikSavchenk0)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[carter0nline](https://github.com/carter0nline)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[lolmanurfunny](https://github.com/lolmanurfunny)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[MehKako](https://github.com/MehKako)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[EpixScripts](https://github.com/EpixScripts)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[knivesofeylis](https://github.com/knivesofeylis)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[sha4owz](https://github.com/sha4owz)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[DaMlgNoodle](https://github.com/DaMlgNoodle)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[nakoyasha](https://github.com/nakoyasha)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[exurd](https://github.com/exurd)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[0xFE0F](https://github.com/0xFE0F)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[Tezos](https://github.com/GoingCrazyDude)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[CfwSky](https://www.roblox.com/users/129425241/profile)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[ruubloo](https://www.roblox.com/users/158082266/profile)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[toyoda165](https://www.roblox.com/users/923416649/profile)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[ShadowCodeX](https://github.com/ShadowCodeX-debug)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[cub-has-injected](https://github.com/cub-has-injected)" />
|
||||
</StackPanel>
|
||||
</controls:Expander>
|
||||
|
||||
<controls:Expander Grid.Row="1" Grid.Column="2" Margin="4,0,0,0" HeaderIcon="Heart16" HeaderText="{x:Static resources:Strings.Menu_About_Contributors_SpecialThanks}" IsExpanded="True">
|
||||
<StackPanel>
|
||||
<controls:MarkdownTextBlock MarkdownText="[MaximumADHD](https://github.com/MaximumADHD)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[Multako](https://www.roblox.com/users/2485612194/profile)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[axstin](https://github.com/axstin)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[taskmanager](https://github.com/Mantaraix)" />
|
||||
<controls:MarkdownTextBlock MarkdownText="[apprehensions](https://github.com/apprehensions)" />
|
||||
</StackPanel>
|
||||
</controls:Expander>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Text="{x:Static resources:Strings.Menu_About_Licenses}" FontWeight="Medium" FontSize="20" Margin="0,16,0,0" />
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ui:CardAction Grid.Row="0" Grid.Column="0" Margin="0,8,8,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/pizzaboxer/bloxstrap/blob/main/LICENSE">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="Bloxstrap" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="0" Grid.Column="1" Margin="0,8,8,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/lepoco/wpfui/blob/main/LICENSE">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="WPF-UI" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="0" Grid.Column="2" Margin="0,8,0,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/securifybv/ShellLink/blob/master/LICENSE.txt">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="ShellLink" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="1" Grid.Column="0" Margin="0,8,8,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/Lachee/discord-rpc-csharp/blob/master/LICENSE">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="DiscordRPC" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,8,0,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/LICENSE">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="13" Text="Roblox Studio Mod Manager" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="2" Grid.Column="0" Margin="0,8,8,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/icsharpcode/SharpZipLib/blob/master/LICENSE.txt">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="13" Text="SharpZipLib" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_MIT}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,8,0,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/xoofx/markdig/blob/master/license.txt">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="Markdig" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_About_Licenses_BSD2}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:UiPage>
|
@ -137,7 +137,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
}
|
||||
else
|
||||
{
|
||||
Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information);
|
||||
Frontend.ShowMessageBox(Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information);
|
||||
|
||||
bool refresh = false;
|
||||
|
||||
@ -175,7 +175,14 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
json = '{' + json;
|
||||
|
||||
if (!json.EndsWith('}'))
|
||||
json += '}';
|
||||
{
|
||||
int lastIndex = json.LastIndexOf('}');
|
||||
|
||||
if (lastIndex == -1)
|
||||
json += '}';
|
||||
else
|
||||
json = json.Substring(0, lastIndex+1);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@ -193,7 +200,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
catch (Exception ex)
|
||||
{
|
||||
Frontend.ShowMessageBox(
|
||||
String.Format(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidJSON, ex.Message),
|
||||
String.Format(Strings.Menu_FastFlagEditor_InvalidJSON, ex.Message),
|
||||
MessageBoxImage.Error
|
||||
);
|
||||
|
||||
@ -205,7 +212,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
if (list.Count > 16)
|
||||
{
|
||||
var result = Frontend.ShowMessageBox(
|
||||
Bloxstrap.Resources.Strings.Menu_FastFlagEditor_LargeConfig,
|
||||
Strings.Menu_FastFlagEditor_LargeConfig,
|
||||
MessageBoxImage.Warning,
|
||||
MessageBoxButton.YesNo
|
||||
);
|
||||
@ -222,7 +229,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
int count = conflictingFlags.Count();
|
||||
|
||||
string message = String.Format(
|
||||
Bloxstrap.Resources.Strings.Menu_FastFlagEditor_ConflictingImport,
|
||||
Strings.Menu_FastFlagEditor_ConflictingImport,
|
||||
count,
|
||||
String.Join(", ", conflictingFlags.Take(25))
|
||||
);
|
||||
@ -263,16 +270,16 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
string errorMessage = "";
|
||||
|
||||
if (!_validPrefixes.Any(name.StartsWith))
|
||||
errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidPrefix;
|
||||
errorMessage = Strings.Menu_FastFlagEditor_InvalidPrefix;
|
||||
else if (!name.All(x => char.IsLetterOrDigit(x) || x == '_'))
|
||||
errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidCharacter;
|
||||
errorMessage = Strings.Menu_FastFlagEditor_InvalidCharacter;
|
||||
|
||||
if (name.EndsWith("_PlaceFilter") || name.EndsWith("_DataCenterFilter"))
|
||||
errorMessage = !ValidateFilter(name, value) ? Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidPlaceFilter : "";
|
||||
errorMessage = !ValidateFilter(name, value) ? Strings.Menu_FastFlagEditor_InvalidPlaceFilter : "";
|
||||
else if ((name.StartsWith("FInt") || name.StartsWith("DFInt")) && !Int32.TryParse(value, out _))
|
||||
errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidNumberValue;
|
||||
errorMessage = Strings.Menu_FastFlagEditor_InvalidNumberValue;
|
||||
else if ((name.StartsWith("FFlag") || name.StartsWith("DFFlag")) && lowerValue != "true" && lowerValue != "false")
|
||||
errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidBoolValue;
|
||||
errorMessage = Strings.Menu_FastFlagEditor_InvalidBoolValue;
|
||||
|
||||
if (!String.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
@ -300,12 +307,10 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
|
||||
private void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
|
||||
{
|
||||
int index = e.Row.GetIndex();
|
||||
FastFlag entry = _fastFlagList[index];
|
||||
if (e.Row.DataContext is not FastFlag entry)
|
||||
return;
|
||||
|
||||
var textbox = e.EditingElement as TextBox;
|
||||
|
||||
if (textbox is null)
|
||||
if (e.EditingElement is not TextBox textbox)
|
||||
return;
|
||||
|
||||
switch (e.Column.Header)
|
||||
@ -319,7 +324,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
|
||||
if (App.FastFlags.GetValue(newName) is not null)
|
||||
{
|
||||
Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information);
|
||||
Frontend.ShowMessageBox(Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information);
|
||||
e.Cancel = true;
|
||||
textbox.Text = oldName;
|
||||
return;
|
||||
@ -387,7 +392,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
{
|
||||
string json = JsonSerializer.Serialize(App.FastFlags.Prop, new JsonSerializerOptions { WriteIndented = true });
|
||||
Clipboard.SetDataObject(json);
|
||||
Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_JsonCopiedToClipboard, MessageBoxImage.Information);
|
||||
Frontend.ShowMessageBox(Strings.Menu_FastFlagEditor_JsonCopiedToClipboard, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
|
@ -86,7 +86,7 @@
|
||||
Header="{x:Static resources:Strings.Menu_FastFlags_Presets_FPSLimit_Title}"
|
||||
Description="{x:Static resources:Strings.Menu_FastFlags_Presets_FPSLimit_Description}"
|
||||
HelpLink="https://github.com/pizzaboxer/bloxstrap/wiki/A-guide-to-FastFlags#framerate-limit">
|
||||
<ui:TextBox Margin="5,0,0,0" Padding="10,5,10,5" Width="200" Text="{Binding FramerateLimit, Mode=TwoWay}" PreviewTextInput="ValidateInt32" />
|
||||
<ui:TextBox Margin="5,0,0,0" Padding="10,5,10,5" Width="200" Text="{Binding FramerateLimit, Mode=TwoWay}" PreviewTextInput="ValidateUInt32" />
|
||||
</controls:OptionControl>
|
||||
|
||||
<controls:OptionControl
|
||||
@ -165,12 +165,6 @@
|
||||
</ComboBox>
|
||||
</controls:OptionControl>
|
||||
|
||||
<controls:OptionControl
|
||||
Header="{x:Static resources:Strings.Menu_FastFlags_Presets_AltGraphicsSelector_Title}"
|
||||
Description="{x:Static resources:Strings.Menu_FastFlags_Presets_AltGraphicsSelector_Description}">
|
||||
<ui:ToggleSwitch IsChecked="{Binding AlternateGraphicsSelectorEnabled, Mode=TwoWay}" />
|
||||
</controls:OptionControl>
|
||||
|
||||
<ui:CardAction Margin="0,24,0,0" Icon="EditSettings24" Command="{Binding OpenFastFlagEditorCommand}">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="{x:Static resources:Strings.Menu_FastFlagEditor_Title}" />
|
||||
|
@ -31,6 +31,8 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
DataContext = new FastFlagsViewModel(this);
|
||||
}
|
||||
|
||||
private void ValidateInt32(object sender, TextCompositionEventArgs e) => e.Handled = !Int32.TryParse(e.Text, out int _);
|
||||
private void ValidateInt32(object sender, TextCompositionEventArgs e) => e.Handled = e.Text != "-" && !Int32.TryParse(e.Text, out int _);
|
||||
|
||||
private void ValidateUInt32(object sender, TextCompositionEventArgs e) => e.Handled = !UInt32.TryParse(e.Text, out uint _);
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,9 @@
|
||||
</controls:OptionControl>
|
||||
|
||||
<controls:OptionControl
|
||||
Header="{x:Static resources:Strings.Menu_Integrations_ShowServerDetails_Title}"
|
||||
Description="{x:Static resources:Strings.Menu_Integrations_ShowServerDetails_Description}"
|
||||
Header="{x:Static resources:Strings.Menu_Integrations_QueryServerLocation_Title}"
|
||||
Description="{Binding Source={x:Static resources:Strings.Menu_Integrations_QueryServerLocation_Description}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://ipinfo.io'}"
|
||||
HelpLink="https://github.com/pizzaboxer/bloxstrap/wiki/What-is-activity-tracking%3F#server-location-querying"
|
||||
IsEnabled="{Binding InnerContent.IsChecked, ElementName=ActivityTrackingOption, Mode=OneWay}">
|
||||
<ui:ToggleSwitch IsChecked="{Binding ShowServerDetailsEnabled, Mode=TwoWay}" />
|
||||
</controls:OptionControl>
|
||||
@ -40,7 +41,7 @@
|
||||
</controls:OptionControl>
|
||||
|
||||
<TextBlock Text="{x:Static resources:Strings.Common_DiscordRichPresence}" FontSize="20" FontWeight="Medium" Margin="0,16,0,0" />
|
||||
<TextBlock Text="{x:Static resources:Strings.Menu_Integrations_RequiresActivityTracking}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<controls:MarkdownTextBlock MarkdownText="{Binding Source={x:Static resources:Strings.Menu_Integrations_RequiresActivityTracking}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://github.com/pizzaboxer/bloxstrap/wiki/What-is-activity-tracking%3F#discord-rich-presence'}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
|
||||
<controls:OptionControl
|
||||
Header="{x:Static resources:Strings.Menu_Integrations_ShowGameActivity_Title}"
|
||||
@ -88,18 +89,18 @@
|
||||
</Style>
|
||||
</StackPanel.Style>
|
||||
<TextBlock Text="{x:Static resources:Strings.Common_Name}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<ui:TextBox Margin="0,4,0,0" Text="{Binding SelectedCustomIntegration.Name}" />
|
||||
<ui:TextBox Margin="0,4,0,0" Text="{Binding SelectedCustomIntegration.Name, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<TextBlock Margin="0,8,0,0" Text="{x:Static resources:Strings.Menu_Integrations_Custom_AppLocation}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<Grid Margin="0,4,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBox Grid.Column="0" Margin="0,0,0,0" PlaceholderText="{x:Static resources:Strings.Menu_Integrations_Custom_AppLocation_Placeholder}" Text="{Binding SelectedCustomIntegration.Location}" />
|
||||
<ui:TextBox Grid.Column="0" Margin="0,0,0,0" PlaceholderText="C:\Windows\System32\cmd.exe" Text="{Binding SelectedCustomIntegration.Location}" />
|
||||
<ui:Button Grid.Column="1" Margin="8,0,0,0" Height="34" Icon="Folder24" Content="{x:Static resources:Strings.Common_Browse}" Command="{Binding BrowseIntegrationLocationCommand}" />
|
||||
</Grid>
|
||||
<TextBlock Margin="0,8,0,0" Text="{x:Static resources:Strings.Menu_Integrations_Custom_LaunchArgs}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<ui:TextBox Margin="0,4,0,0" PlaceholderText="{x:Static resources:Strings.Menu_Integrations_Custom_LaunchArgs_Placeholder}" Text="{Binding SelectedCustomIntegration.LaunchArgs}" TextWrapping="Wrap" AcceptsReturn="True" AcceptsTab="True" />
|
||||
<ui:TextBox Margin="0,4,0,0" PlaceholderText="{Binding Source='/k echo {0}', Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Static resources:Strings.Menu_Integrations_Custom_LaunchArgs_Placeholder}}" Text="{Binding SelectedCustomIntegration.LaunchArgs}" TextWrapping="Wrap" AcceptsReturn="True" AcceptsTab="True" />
|
||||
<CheckBox Margin="0,8,0,0" Content="{x:Static resources:Strings.Menu_Integrations_Custom_AutoClose}" IsChecked="{Binding SelectedCustomIntegration.AutoClose}" />
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Text="{x:Static resources:Strings.Menu_Integrations_Custom_NoneSelected}" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
|
@ -25,18 +25,27 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ui:CardAction Grid.Row="0" Grid.Column="0" x:Name="OpenModFolderCardAction" Margin="0,0,4,0" Icon="Folder24" Command="{Binding OpenModsFolderCommand}">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="{x:Static resources:Strings.Menu_Mods_OpenModsFolder_Title}" TextWrapping="Wrap" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_Mods_OpenModsFolder_Description}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
|
||||
<ui:CardAction Grid.Row="0" Grid.Column="1" Margin="4,0,0,0" Icon="BookQuestionMark24" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/pizzaboxer/bloxstrap/wiki/Adding-custom-mods">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="{x:Static resources:Strings.Common_Help}" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="{x:Static resources:Strings.Menu_Mods_Help_Description}" Padding="0,0,16,0" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
|
||||
<ui:CardAction Grid.Row="1" Grid.ColumnSpan="2" Margin="0,8,0,0" Icon="WindowWrench24" Command="{Binding OpenCompatSettingsCommand}">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="{x:Static resources:Strings.Menu_Mods_Misc_CompatibilitySettings_Title}" TextWrapping="Wrap" />
|
||||
<controls:MarkdownTextBlock Margin="0,2,0,0" FontSize="12" MarkdownText="{Binding Source={x:Static resources:Strings.Menu_Mods_Misc_CompatibilitySettings_Description}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://devblogs.microsoft.com/directx/demystifying-full-screen-optimizations/'}" Foreground="{DynamicResource TextFillColorTertiaryBrush}" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Text="{x:Static resources:Strings.Common_Presets}" FontSize="20" FontWeight="Medium" Margin="0,16,0,0" />
|
||||
@ -93,13 +102,5 @@
|
||||
<ui:Button Icon="Delete16" Content="{x:Static resources:Strings.Menu_Mods_Misc_CustomFont_Remove}" Appearance="Danger" Command="{Binding ManageCustomFontCommand}" Visibility="{Binding DeleteCustomFontVisibility, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</controls:OptionControl>
|
||||
|
||||
<controls:OptionControl
|
||||
Header="{x:Static resources:Strings.Menu_Mods_Misc_DisableFullscreenOptimisations_Title}"
|
||||
Description="{x:Static resources:Strings.Menu_Mods_Misc_DisableFullscreenOptimisations_Description}"
|
||||
HelpLink="https://devblogs.microsoft.com/directx/demystifying-full-screen-optimizations/"
|
||||
x:Name="FullscreenOptimizationsToggle">
|
||||
<ui:ToggleSwitch IsChecked="{Binding DisableFullscreenOptimizations, Mode=TwoWay}" />
|
||||
</controls:OptionControl>
|
||||
</StackPanel>
|
||||
</ui:UiPage>
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System.Windows;
|
||||
|
||||
using Bloxstrap.UI.ViewModels.Settings;
|
||||
using Bloxstrap.UI.ViewModels.Settings;
|
||||
|
||||
namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
{
|
||||
@ -13,10 +11,6 @@ namespace Bloxstrap.UI.Elements.Settings.Pages
|
||||
{
|
||||
DataContext = new ModsViewModel();
|
||||
InitializeComponent();
|
||||
|
||||
// fullscreen optimizations were only added in windows 10 build 17093
|
||||
if (Environment.OSVersion.Version.Build < 17093)
|
||||
this.FullscreenOptimizationsToggle.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,35 +17,42 @@ namespace Bloxstrap.UI
|
||||
if (App.LaunchSettings.QuietFlag.Active)
|
||||
return defaultResult;
|
||||
|
||||
if (App.LaunchSettings.RobloxLaunchMode != LaunchMode.None)
|
||||
return ShowFluentMessageBox(message, icon, buttons);
|
||||
return ShowFluentMessageBox(message, icon, buttons);
|
||||
}
|
||||
|
||||
switch (App.Settings.Prop.BootstrapperStyle)
|
||||
{
|
||||
case BootstrapperStyle.FluentDialog:
|
||||
case BootstrapperStyle.ClassicFluentDialog:
|
||||
case BootstrapperStyle.FluentAeroDialog:
|
||||
case BootstrapperStyle.ByfronDialog:
|
||||
return ShowFluentMessageBox(message, icon, buttons);
|
||||
public static void ShowPlayerErrorDialog(bool crash = false)
|
||||
{
|
||||
if (App.LaunchSettings.QuietFlag.Active)
|
||||
return;
|
||||
|
||||
default:
|
||||
return MessageBox.Show(message, App.ProjectName, buttons, icon);
|
||||
}
|
||||
string topLine = Strings.Dialog_PlayerError_FailedLaunch;
|
||||
|
||||
if (crash)
|
||||
topLine = Strings.Dialog_PlayerError_Crash;
|
||||
|
||||
ShowMessageBox($"{topLine}\n\n{Strings.Dialog_PlayerError_HelpInformation}", MessageBoxImage.Error);
|
||||
Utilities.ShellExecute($"https://github.com/{App.ProjectRepository}/wiki/Roblox-crashes-or-does-not-launch");
|
||||
}
|
||||
|
||||
public static void ShowExceptionDialog(Exception exception)
|
||||
{
|
||||
if (App.LaunchSettings.QuietFlag.Active)
|
||||
return;
|
||||
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
new ExceptionDialog(exception).ShowDialog();
|
||||
});
|
||||
}
|
||||
|
||||
public static void ShowConnectivityDialog(string title, string description, Exception exception)
|
||||
public static void ShowConnectivityDialog(string title, string description, MessageBoxImage image, Exception exception)
|
||||
{
|
||||
if (App.LaunchSettings.QuietFlag.Active)
|
||||
return;
|
||||
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
new ConnectivityDialog(title, description, exception).ShowDialog();
|
||||
new ConnectivityDialog(title, description, image, exception).ShowDialog();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bloxstrap.Integrations;
|
||||
using Bloxstrap.UI.Elements.About;
|
||||
using Bloxstrap.UI.Elements.ContextMenu;
|
||||
|
||||
namespace Bloxstrap.UI
|
||||
@ -10,19 +11,22 @@ namespace Bloxstrap.UI
|
||||
private bool _disposing = false;
|
||||
|
||||
private readonly System.Windows.Forms.NotifyIcon _notifyIcon;
|
||||
private MenuContainer? _menuContainer;
|
||||
|
||||
private ActivityWatcher? _activityWatcher;
|
||||
private DiscordRichPresence? _richPresenceHandler;
|
||||
private int? _processId;
|
||||
private readonly MenuContainer _menuContainer;
|
||||
|
||||
private readonly Watcher _watcher;
|
||||
|
||||
private ActivityWatcher? _activityWatcher => _watcher.ActivityWatcher;
|
||||
|
||||
EventHandler? _alertClickHandler;
|
||||
|
||||
public NotifyIconWrapper()
|
||||
public NotifyIconWrapper(Watcher watcher)
|
||||
{
|
||||
App.Logger.WriteLine("NotifyIconWrapper::NotifyIconWrapper", "Initializing notification area icon");
|
||||
|
||||
_notifyIcon = new()
|
||||
_watcher = watcher;
|
||||
|
||||
_notifyIcon = new(new System.ComponentModel.Container())
|
||||
{
|
||||
Icon = Properties.Resources.IconBloxstrap,
|
||||
Text = App.ProjectName,
|
||||
@ -30,52 +34,18 @@ namespace Bloxstrap.UI
|
||||
};
|
||||
|
||||
_notifyIcon.MouseClick += MouseClickEventHandler;
|
||||
|
||||
if (_activityWatcher is not null && App.Settings.Prop.ShowServerDetails)
|
||||
_activityWatcher.OnGameJoin += OnGameJoin;
|
||||
|
||||
_menuContainer = new(_watcher);
|
||||
_menuContainer.Show();
|
||||
}
|
||||
|
||||
#region Handler registers
|
||||
public void SetRichPresenceHandler(DiscordRichPresence richPresenceHandler)
|
||||
{
|
||||
if (_richPresenceHandler is not null)
|
||||
return;
|
||||
|
||||
_richPresenceHandler = richPresenceHandler;
|
||||
}
|
||||
|
||||
public void SetActivityWatcher(ActivityWatcher activityWatcher)
|
||||
{
|
||||
if (_activityWatcher is not null)
|
||||
return;
|
||||
|
||||
_activityWatcher = activityWatcher;
|
||||
|
||||
if (App.Settings.Prop.ShowServerDetails)
|
||||
_activityWatcher.OnGameJoin += (_, _) => Task.Run(OnGameJoin);
|
||||
}
|
||||
|
||||
public void SetProcessId(int processId)
|
||||
{
|
||||
if (_processId is not null)
|
||||
return;
|
||||
|
||||
_processId = processId;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Context menu
|
||||
public void InitializeContextMenu()
|
||||
{
|
||||
if (_menuContainer is not null || _disposing)
|
||||
return;
|
||||
|
||||
App.Logger.WriteLine("NotifyIconWrapper::InitializeContextMenu", "Initializing context menu");
|
||||
|
||||
_menuContainer = new(_activityWatcher, _richPresenceHandler, _processId);
|
||||
_menuContainer.ShowDialog();
|
||||
}
|
||||
|
||||
public void MouseClickEventHandler(object? sender, System.Windows.Forms.MouseEventArgs e)
|
||||
{
|
||||
if (e.Button != System.Windows.Forms.MouseButtons.Right || _menuContainer is null)
|
||||
if (e.Button != System.Windows.Forms.MouseButtons.Right)
|
||||
return;
|
||||
|
||||
_menuContainer.Activate();
|
||||
@ -84,10 +54,17 @@ namespace Bloxstrap.UI
|
||||
#endregion
|
||||
|
||||
#region Activity handlers
|
||||
public async void OnGameJoin()
|
||||
public async void OnGameJoin(object? sender, EventArgs e)
|
||||
{
|
||||
string serverLocation = await _activityWatcher!.GetServerLocation();
|
||||
string title = _activityWatcher.ActivityServerType switch
|
||||
if (_activityWatcher is null)
|
||||
return;
|
||||
|
||||
string? serverLocation = await _activityWatcher.Data.QueryServerLocation();
|
||||
|
||||
if (string.IsNullOrEmpty(serverLocation))
|
||||
return;
|
||||
|
||||
string title = _activityWatcher.Data.ServerType switch
|
||||
{
|
||||
ServerType.Public => Strings.ContextMenu_ServerInformation_Notification_Title_Public,
|
||||
ServerType.Private => Strings.ContextMenu_ServerInformation_Notification_Title_Private,
|
||||
@ -99,11 +76,12 @@ namespace Bloxstrap.UI
|
||||
title,
|
||||
String.Format(Strings.ContextMenu_ServerInformation_Notification_Text, serverLocation),
|
||||
10,
|
||||
(_, _) => _menuContainer?.ShowServerInformationWindow()
|
||||
(_, _) => _menuContainer.ShowServerInformationWindow()
|
||||
);
|
||||
}
|
||||
#endregion
|
||||
|
||||
// we may need to create our own handler for this, because this sorta sucks
|
||||
public void ShowAlert(string caption, string message, int duration, EventHandler? clickHandler)
|
||||
{
|
||||
string id = Guid.NewGuid().ToString()[..8];
|
||||
@ -151,9 +129,8 @@ namespace Bloxstrap.UI
|
||||
|
||||
App.Logger.WriteLine("NotifyIconWrapper::Dispose", "Disposing NotifyIcon");
|
||||
|
||||
_menuContainer?.Dispatcher.Invoke(_menuContainer.Close);
|
||||
_notifyIcon?.Dispose();
|
||||
|
||||
_menuContainer.Dispatcher.Invoke(_menuContainer.Close);
|
||||
_notifyIcon.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
63
Bloxstrap/UI/ViewModels/About/AboutViewModel.cs
Normal file
63
Bloxstrap/UI/ViewModels/About/AboutViewModel.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace Bloxstrap.UI.ViewModels.About
|
||||
{
|
||||
public class AboutViewModel : NotifyPropertyChangedViewModel
|
||||
{
|
||||
private SupporterData? _supporterData;
|
||||
|
||||
public string Version => string.Format(Strings.Menu_About_Version, App.Version);
|
||||
|
||||
public BuildMetadataAttribute BuildMetadata => App.BuildMetadata;
|
||||
|
||||
public string BuildTimestamp => BuildMetadata.Timestamp.ToFriendlyString();
|
||||
public string BuildCommitHashUrl => $"https://github.com/{App.ProjectRepository}/commit/{BuildMetadata.CommitHash}";
|
||||
|
||||
public Visibility BuildInformationVisibility => App.IsProductionBuild ? Visibility.Collapsed : Visibility.Visible;
|
||||
public Visibility BuildCommitVisibility => App.IsActionBuild ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
public List<Supporter> Supporters => _supporterData?.Supporters ?? Enumerable.Empty<Supporter>().ToList();
|
||||
|
||||
public int SupporterColumns => _supporterData?.Columns ?? 0;
|
||||
|
||||
public GenericTriState SupportersLoadedState { get; set; } = GenericTriState.Unknown;
|
||||
|
||||
public string SupportersLoadError { get; set; } = "";
|
||||
|
||||
public AboutViewModel()
|
||||
{
|
||||
// this will cause momentary freezes only when ran under the debugger
|
||||
LoadSupporterData();
|
||||
}
|
||||
|
||||
public async void LoadSupporterData()
|
||||
{
|
||||
const string LOG_IDENT = "AboutViewModel::LoadSupporterData";
|
||||
|
||||
try
|
||||
{
|
||||
_supporterData = await Http.GetJson<SupporterData>("https://raw.githubusercontent.com/bloxstraplabs/config/main/supporters.json");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Could not load supporter data");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
|
||||
SupportersLoadedState = GenericTriState.Failed;
|
||||
SupportersLoadError = ex.Message;
|
||||
|
||||
OnPropertyChanged(nameof(SupportersLoadError));
|
||||
}
|
||||
|
||||
if (_supporterData is not null)
|
||||
{
|
||||
SupportersLoadedState = GenericTriState.Successful;
|
||||
|
||||
OnPropertyChanged(nameof(Supporters));
|
||||
OnPropertyChanged(nameof(SupporterColumns));
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(SupportersLoadedState));
|
||||
}
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@ namespace Bloxstrap.UI.ViewModels.Bootstrapper
|
||||
|
||||
private void CancelInstall()
|
||||
{
|
||||
_dialog.Bootstrapper?.CancelInstall();
|
||||
_dialog.Bootstrapper?.Cancel();
|
||||
_dialog.CloseBootstrapper();
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user