diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs index 3a02b2e..7bfb8df 100644 --- a/Bloxstrap/App.xaml.cs +++ b/Bloxstrap/App.xaml.cs @@ -17,7 +17,7 @@ namespace Bloxstrap public const string ProjectName = "Bloxstrap"; public const string ProjectOwner = "pizzaboxer"; public const string ProjectRepository = "pizzaboxer/bloxstrap"; - public const string ProjectDownloadLink = "https://bloxstrap.pizzaboxer.xyz"; + 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"; diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index 16c7c84..4ac7217 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -209,12 +209,9 @@ namespace Bloxstrap if (_latestVersionGuid != _versionGuid || !File.Exists(_playerLocation)) await InstallLatestVersion(); - MigrateIntegrations(); - if (_installWebView2) await InstallWebView2(); - App.FastFlags.Save(); await ApplyModifications(); // TODO: move this to install/upgrade flow @@ -224,7 +221,6 @@ namespace Bloxstrap CheckInstall(); // at this point we've finished updating our configs - App.Settings.Save(); App.State.Save(); await mutex.ReleaseAsync(); @@ -328,18 +324,26 @@ namespace Bloxstrap return; } - using var startEvent = new EventWaitHandle(false, EventResetMode.ManualReset, AppData.StartEvent); - - // v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now int gameClientPid; - using (var gameClient = Process.Start(startInfo)!) + bool startEventSignalled; + + // TODO: figure out why this is causing roblox to block for some users + using (var startEvent = new EventWaitHandle(false, EventResetMode.ManualReset, AppData.StartEvent)) { - gameClientPid = gameClient.Id; + startEvent.Reset(); + + // v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now + using (var process = Process.Start(startInfo)!) + { + gameClientPid = process.Id; + } + + App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {gameClientPid}), waiting for start event"); + + startEventSignalled = startEvent.WaitOne(TimeSpan.FromSeconds(10)); } - App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {gameClientPid}), waiting for start event"); - - if (!startEvent.WaitOne(TimeSpan.FromSeconds(10))) + if (!startEventSignalled) { Frontend.ShowPlayerErrorDialog(); return; @@ -382,8 +386,10 @@ namespace Bloxstrap if (autoclosePids.Any()) args += $";{String.Join(',', autoclosePids)}"; - using (var ipl = new InterProcessLock("Watcher")) + if (App.Settings.Prop.EnableActivityTracking || autoclosePids.Any()) { + using var ipl = new InterProcessLock("Watcher", TimeSpan.FromSeconds(5)); + if (ipl.IsAcquired) Process.Start(Paths.Process, $"-watcher \"{args}\""); } @@ -766,32 +772,6 @@ namespace Bloxstrap App.Logger.WriteLine(LOG_IDENT, "Finished installing runtime"); } - public static void MigrateIntegrations() - { - // v2.2.0 - remove rbxfpsunlocker - string rbxfpsunlocker = Path.Combine(Paths.Integrations, "rbxfpsunlocker"); - - if (Directory.Exists(rbxfpsunlocker)) - Directory.Delete(rbxfpsunlocker, true); - - // v2.3.0 - remove reshade - 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); - } - private async Task ApplyModifications() { const string LOG_IDENT = "Bootstrapper::ApplyModifications"; diff --git a/Bloxstrap/Extensions/ServerTypeEx.cs b/Bloxstrap/Extensions/ServerTypeEx.cs index 88956d0..2b65d8b 100644 --- a/Bloxstrap/Extensions/ServerTypeEx.cs +++ b/Bloxstrap/Extensions/ServerTypeEx.cs @@ -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, _ => "?" }; } diff --git a/Bloxstrap/FastFlagManager.cs b/Bloxstrap/FastFlagManager.cs index f211752..5c92751 100644 --- a/Bloxstrap/FastFlagManager.cs +++ b/Bloxstrap/FastFlagManager.cs @@ -4,6 +4,10 @@ namespace Bloxstrap { public class FastFlagManager : JsonManager> { + 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); @@ -48,7 +52,7 @@ namespace Bloxstrap { "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" }, @@ -99,7 +103,7 @@ namespace Bloxstrap InGameMenuVersion.Default, new Dictionary { - { "DisableV2", null }, + { "V2Rollout", null }, { "EnableV4", null }, { "EnableV4Chrome", null }, { "ABTest", null } @@ -110,7 +114,7 @@ namespace Bloxstrap InGameMenuVersion.V1, new Dictionary { - { "DisableV2", "True" }, + { "V2Rollout", "0" }, { "EnableV4", "False" }, { "EnableV4Chrome", "False" }, { "ABTest", "False" } @@ -121,7 +125,7 @@ namespace Bloxstrap InGameMenuVersion.V2, new Dictionary { - { "DisableV2", "False" }, + { "V2Rollout", "100" }, { "EnableV4", "False" }, { "EnableV4Chrome", "False" }, { "ABTest", "False" } @@ -132,7 +136,7 @@ namespace Bloxstrap InGameMenuVersion.V4, new Dictionary { - { "DisableV2", "True" }, + { "V2Rollout", "0" }, { "EnableV4", "True" }, { "EnableV4Chrome", "False" }, { "ABTest", "False" } @@ -143,7 +147,7 @@ namespace Bloxstrap InGameMenuVersion.V4Chrome, new Dictionary { - { "DisableV2", "True" }, + { "V2Rollout", "0" }, { "EnableV4", "True" }, { "EnableV4Chrome", "True" }, { "ABTest", "False" } @@ -238,9 +242,9 @@ namespace Bloxstrap OriginalProp = new(Prop); } - public override void Load() + public override void Load(bool alertFailure = true) { - base.Load(); + base.Load(alertFailure); // clone the dictionary OriginalProp = new(Prop); @@ -248,10 +252,6 @@ namespace Bloxstrap // 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); } } } diff --git a/Bloxstrap/Installer.cs b/Bloxstrap/Installer.cs index 2e841fd..c08c45f 100644 --- a/Bloxstrap/Installer.cs +++ b/Bloxstrap/Installer.cs @@ -11,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; @@ -21,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); @@ -69,9 +75,11 @@ 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); + + App.Logger.WriteLine(LOG_IDENT, "Installation finished"); } private bool ValidateLocation() @@ -400,6 +408,41 @@ namespace Bloxstrap 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); @@ -414,6 +457,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) @@ -435,9 +485,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) @@ -466,6 +514,18 @@ namespace Bloxstrap ProtocolHandler.Register("roblox", "Roblox", Paths.Application, "-player \"%1\""); ProtocolHandler.Register("roblox-player", "Roblox", Paths.Application, "-player \"%1\""); + + 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.Settings.Save(); diff --git a/Bloxstrap/Integrations/ActivityWatcher.cs b/Bloxstrap/Integrations/ActivityWatcher.cs index e75173e..f11464c 100644 --- a/Bloxstrap/Integrations/ActivityWatcher.cs +++ b/Bloxstrap/Integrations/ActivityWatcher.cs @@ -9,6 +9,7 @@ namespace Bloxstrap.Integrations 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 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:"; @@ -19,6 +20,7 @@ namespace Bloxstrap.Integrations private const string GameJoinLoadTimeEntryPattern = ", userid:([0-9]+)"; private const string GameJoiningEntryPattern = @"! Joining game '([0-9a-f\-]{36})' place ([0-9]+) at ([0-9\.]+)"; + 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\] (.*)"; @@ -41,8 +43,10 @@ namespace Bloxstrap.Integrations // these are values to use assuming the player isn't currently in a game // hmm... do i move this to a model? + public DateTime ActivityTimeJoined; public bool ActivityInGame = false; public long ActivityPlaceId = 0; + public long ActivityUniverseId = 0; public string ActivityJobId = ""; public string ActivityUserId = ""; public string ActivityMachineAddress = ""; @@ -51,6 +55,8 @@ namespace Bloxstrap.Integrations public string ActivityLaunchData = ""; public ServerType ActivityServerType = ServerType.Public; + public List ActivityHistory = new(); + public bool IsDisposed = false; public async void Start() @@ -92,7 +98,6 @@ namespace Bloxstrap.Integrations if (logFileInfo.CreationTime.AddSeconds(15) > DateTime.Now) break; - // TODO: report failure after 10 seconds of no log file App.Logger.WriteLine(LOG_IDENT, $"Could not find recent enough log file, waiting... (newest is {logFileInfo.Name})"); await Task.Delay(1000); } @@ -135,6 +140,7 @@ namespace Bloxstrap.Integrations return deeplink; } + // TODO: i need to double check how this handles failed game joins (connection error, invalid permissions, etc) private void ReadLogEntry(string entry) { const string LOG_IDENT = "ActivityWatcher::ReadLogEntry"; @@ -168,6 +174,8 @@ namespace Bloxstrap.Integrations } if (!ActivityInGame && ActivityPlaceId == 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 @@ -206,13 +214,28 @@ namespace Bloxstrap.Integrations } else if (!ActivityInGame && ActivityPlaceId != 0) { - if (entry.Contains(GameJoiningUDMUXEntry)) + // We are not confirmed to be in a game, but we are in the process of joining one + + 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 universe entry"); + App.Logger.WriteLine(LOG_IDENT, entry); + return; + } + + ActivityUniverseId = long.Parse(match.Groups[1].Value); + } + else if (entry.Contains(GameJoiningUDMUXEntry)) { Match match = Regex.Match(entry, GameJoiningUDMUXPattern); if (match.Groups.Count != 3 || match.Groups[2].Value != ActivityMachineAddress) { - 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 UDMUX entry"); App.Logger.WriteLine(LOG_IDENT, entry); return; } @@ -236,17 +259,35 @@ namespace Bloxstrap.Integrations App.Logger.WriteLine(LOG_IDENT, $"Joined Game ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})"); ActivityInGame = true; + ActivityTimeJoined = DateTime.Now; + OnGameJoin?.Invoke(this, new EventArgs()); } } else if (ActivityInGame && ActivityPlaceId != 0) { + // We are confirmed to be in a game + if (entry.Contains(GameDisconnectedEntry)) { App.Logger.WriteLine(LOG_IDENT, $"Disconnected from Game ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})"); + // TODO: should this be including launchdata? + if (ActivityServerType != ServerType.Reserved) + { + ActivityHistory.Insert(0, new ActivityHistoryEntry + { + PlaceId = ActivityPlaceId, + UniverseId = ActivityUniverseId, + JobId = ActivityJobId, + TimeJoined = ActivityTimeJoined, + TimeLeft = DateTime.Now + }); + } + ActivityInGame = false; ActivityPlaceId = 0; + ActivityUniverseId = 0; ActivityJobId = ""; ActivityMachineAddress = ""; ActivityMachineUDMUX = false; diff --git a/Bloxstrap/Integrations/DiscordRichPresence.cs b/Bloxstrap/Integrations/DiscordRichPresence.cs index 9febb82..d66b958 100644 --- a/Bloxstrap/Integrations/DiscordRichPresence.cs +++ b/Bloxstrap/Integrations/DiscordRichPresence.cs @@ -207,15 +207,8 @@ namespace Bloxstrap.Integrations // TODO: move this to its own function under the activity watcher? // TODO: show error if information cannot be queried instead of silently failing - var universeIdResponse = await Http.GetJson($"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}"); + long universeId = _activityWatcher.ActivityUniverseId; // preserve time spent playing if we're teleporting between places in the same universe if (_timeStartedUniverse is null || !_activityWatcher.ActivityIsTeleport || universeId != _currentUniverseId) @@ -277,7 +270,7 @@ namespace Bloxstrap.Integrations buttons.Add(new Button { Label = "Join server", - Url = $"roblox://experiences/start?placeId={placeId}&gameInstanceId={_activityWatcher.ActivityJobId}" + Url = _activityWatcher.GetActivityDeeplink() }); } diff --git a/Bloxstrap/JsonManager.cs b/Bloxstrap/JsonManager.cs index a8e8c3e..cf4d66a 100644 --- a/Bloxstrap/JsonManager.cs +++ b/Bloxstrap/JsonManager.cs @@ -8,11 +8,13 @@ namespace Bloxstrap 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"; @@ -32,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.GetType()}: {ex.Message}", System.Windows.MessageBoxImage.Warning); + } + + Save(); } } diff --git a/Bloxstrap/LaunchHandler.cs b/Bloxstrap/LaunchHandler.cs index a7f5406..0c00a4c 100644 --- a/Bloxstrap/LaunchHandler.cs +++ b/Bloxstrap/LaunchHandler.cs @@ -215,7 +215,7 @@ namespace Bloxstrap App.Logger.WriteLine(LOG_IDENT, "An exception occurred when running the bootstrapper"); if (t.Exception is not null) - App.FinalizeExceptionHandling(t.Exception, false); + App.FinalizeExceptionHandling(t.Exception); } App.Terminate(); diff --git a/Bloxstrap/Logger.cs b/Bloxstrap/Logger.cs index bbaa40a..4d4ee69 100644 --- a/Bloxstrap/Logger.cs +++ b/Bloxstrap/Logger.cs @@ -16,8 +16,7 @@ { const string LOG_IDENT = "Logger::Initialize"; - // TODO: /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); diff --git a/Bloxstrap/Models/ActivityHistoryEntry.cs b/Bloxstrap/Models/ActivityHistoryEntry.cs new file mode 100644 index 0000000..1e05640 --- /dev/null +++ b/Bloxstrap/Models/ActivityHistoryEntry.cs @@ -0,0 +1,38 @@ +using CommunityToolkit.Mvvm.Input; +using System.Windows.Input; + +namespace Bloxstrap.Models +{ + public class ActivityHistoryEntry + { + public long UniverseId { get; set; } + + public long PlaceId { get; set; } + + public string JobId { get; set; } = String.Empty; + + public DateTime TimeJoined { get; set; } + + public DateTime TimeLeft { get; set; } + + public string TimeJoinedFriendly => String.Format("{0} - {1}", TimeJoined.ToString("h:mm tt"), TimeLeft.ToString("h:mm tt")); + + public bool DetailsLoaded = false; + + public string GameName { get; set; } = String.Empty; + + public string GameThumbnail { get; set; } = String.Empty; + + public ICommand RejoinServerCommand => new RelayCommand(RejoinServer); + + private void RejoinServer() + { + string playerPath = Path.Combine(Paths.Versions, App.State.Prop.PlayerVersionGuid, "RobloxPlayerBeta.exe"); + string deeplink = $"roblox://experiences/start?placeId={PlaceId}&gameInstanceId={JobId}"; + + // start RobloxPlayerBeta.exe directly since Roblox can reuse the existing window + // ideally, i'd like to find out how roblox is doing it + Process.Start(playerPath, deeplink); + } + } +} diff --git a/Bloxstrap/Models/Supporter.cs b/Bloxstrap/Models/Supporter.cs new file mode 100644 index 0000000..6732a26 --- /dev/null +++ b/Bloxstrap/Models/Supporter.cs @@ -0,0 +1,13 @@ +namespace Bloxstrap.Models +{ + 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}"; + } +} diff --git a/Bloxstrap/Models/SupporterData.cs b/Bloxstrap/Models/SupporterData.cs new file mode 100644 index 0000000..f9ef2fe --- /dev/null +++ b/Bloxstrap/Models/SupporterData.cs @@ -0,0 +1,11 @@ +namespace Bloxstrap.Models +{ + public class SupporterData + { + [JsonPropertyName("columns")] + public int Columns { get; set; } + + [JsonPropertyName("supporters")] + public List Supporters { get; set; } = null!; + } +} diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index f9b32fe..d97661e 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -69,6 +69,24 @@ namespace Bloxstrap.Resources { } } + /// + /// 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!. + /// + public static string About_Supporters_Description { + get { + return ResourceManager.GetString("About.Supporters.Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supporters. + /// + public static string About_Supporters_Title { + get { + return ResourceManager.GetString("About.Supporters.Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to About Bloxstrap. /// @@ -449,6 +467,15 @@ namespace Bloxstrap.Resources { } } + /// + /// Looks up a localized string similar to Loading, please wait.... + /// + public static string Common_Loading { + get { + return ResourceManager.GetString("Common.Loading", resourceCulture); + } + } + /// /// Looks up a localized string similar to Miscellaneous. /// @@ -485,6 +512,15 @@ namespace Bloxstrap.Resources { } } + /// + /// Looks up a localized string similar to Could not load data because of a network error.. + /// + public static string Common_NetworkError { + get { + return ResourceManager.GetString("Common.NetworkError", resourceCulture); + } + } + /// /// Looks up a localized string similar to New. /// @@ -629,6 +665,24 @@ namespace Bloxstrap.Resources { } } + /// + /// Looks up a localized string similar to Rejoin. + /// + public static string ContextMenu_GameHistory_Rejoin { + get { + return ResourceManager.GetString("ContextMenu.GameHistory.Rejoin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Game history. + /// + public static string ContextMenu_GameHistory_Title { + get { + return ResourceManager.GetString("ContextMenu.GameHistory.Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Roblox is still launching. A log file will only be available once Roblox launches.. /// @@ -638,15 +692,6 @@ namespace Bloxstrap.Resources { } } - /// - /// Looks up a localized string similar to See server details. - /// - public static string ContextMenu_SeeServerDetails { - get { - return ResourceManager.GetString("ContextMenu.SeeServerDetails", resourceCulture); - } - } - /// /// Looks up a localized string similar to Copy Instance ID. /// @@ -1422,7 +1467,7 @@ namespace Bloxstrap.Resources { /// /// 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.. /// @@ -1459,6 +1504,24 @@ namespace Bloxstrap.Resources { } } + /// + /// Looks up a localized string similar to Your Fast Flags could not be loaded. They have been reset to the default configuration.. + /// + public static string JsonManager_FastFlagsLoadFailed { + get { + return ResourceManager.GetString("JsonManager.FastFlagsLoadFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your Settings could not be loaded. They have been reset to the default configuration.. + /// + public static string JsonManager_SettingsLoadFailed { + get { + return ResourceManager.GetString("JsonManager.SettingsLoadFailed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Configure settings. /// diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 4f3f06b..bb2fec8 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -268,9 +268,6 @@ Your ReShade configuration files will still be saved, and you can locate them by Copy invite deeplink - - See server details - Copy Instance ID @@ -999,7 +996,7 @@ Selecting 'No' will ignore this warning and continue installation. 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. @@ -1156,4 +1153,28 @@ Are you sure you want to continue? Please read the following help information, which will open in your web browser when you close this dialog. + + Could not load data because of a network error. + + + Loading, please wait... + + + Supporters + + + These are the people currently supporting Bloxstrap through [Ko-fi]({0}). A massive thank you to everyone here! + + + Your Settings could not be loaded. They have been reset to the default configuration. + + + Your Fast Flags could not be loaded. They have been reset to the default configuration. + + + Game history + + + Rejoin + diff --git a/Bloxstrap/UI/Converters/EnumNameConverter.cs b/Bloxstrap/UI/Converters/EnumNameConverter.cs index 52e1cf0..e0b9638 100644 --- a/Bloxstrap/UI/Converters/EnumNameConverter.cs +++ b/Bloxstrap/UI/Converters/EnumNameConverter.cs @@ -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 diff --git a/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml b/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml index 8e16999..88c4aa9 100644 --- a/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml +++ b/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml @@ -3,7 +3,9 @@ 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" @@ -11,7 +13,13 @@ d:DesignHeight="1500" d:DesignWidth="800" Title="AboutPage" Scrollable="True"> + + + + + + @@ -73,6 +81,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml.cs b/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml.cs index 3294258..d6a1bdc 100644 --- a/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml.cs +++ b/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml.cs @@ -1,4 +1,4 @@ -using Bloxstrap.UI.ViewModels.Settings; +using Bloxstrap.UI.ViewModels.About; namespace Bloxstrap.UI.Elements.About.Pages { diff --git a/Bloxstrap/UI/Elements/Bootstrapper/LegacyDialog2008.cs b/Bloxstrap/UI/Elements/Bootstrapper/LegacyDialog2008.cs index 5543902..2842a94 100644 --- a/Bloxstrap/UI/Elements/Bootstrapper/LegacyDialog2008.cs +++ b/Bloxstrap/UI/Elements/Bootstrapper/LegacyDialog2008.cs @@ -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(); diff --git a/Bloxstrap/UI/Elements/Bootstrapper/LegacyDialog2011.cs b/Bloxstrap/UI/Elements/Bootstrapper/LegacyDialog2011.cs index 2349eda..93192c1 100644 --- a/Bloxstrap/UI/Elements/Bootstrapper/LegacyDialog2011.cs +++ b/Bloxstrap/UI/Elements/Bootstrapper/LegacyDialog2011.cs @@ -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(); diff --git a/Bloxstrap/UI/Elements/Bootstrapper/ProgressDialog.cs b/Bloxstrap/UI/Elements/Bootstrapper/ProgressDialog.cs index fc2974a..da4bd57 100644 --- a/Bloxstrap/UI/Elements/Bootstrapper/ProgressDialog.cs +++ b/Bloxstrap/UI/Elements/Bootstrapper/ProgressDialog.cs @@ -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(); diff --git a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml index 88a7380..d62976a 100644 --- a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml +++ b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml @@ -57,7 +57,19 @@ - + + + + + + + + + + + + + diff --git a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs index bd90844..615067a 100644 --- a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs +++ b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs @@ -80,6 +80,7 @@ namespace Bloxstrap.UI.Elements.ContextMenu public void ActivityWatcher_OnGameLeave(object? sender, EventArgs e) { Dispatcher.Invoke(() => { + JoinLastServerMenuItem.Visibility = Visibility.Visible; InviteDeeplinkMenuItem.Visibility = Visibility.Collapsed; ServerDetailsMenuItem.Visibility = Visibility.Collapsed; @@ -129,5 +130,13 @@ namespace Bloxstrap.UI.Elements.ContextMenu _watcher.KillRobloxProcess(); } + + private void JoinLastServerMenuItem_Click(object sender, RoutedEventArgs e) + { + if (_activityWatcher is null) + throw new ArgumentNullException(nameof(_activityWatcher)); + + new ServerHistory(_activityWatcher).ShowDialog(); + } } } diff --git a/Bloxstrap/UI/Elements/ContextMenu/ServerHistory.xaml b/Bloxstrap/UI/Elements/ContextMenu/ServerHistory.xaml new file mode 100644 index 0000000..c1b5751 --- /dev/null +++ b/Bloxstrap/UI/Elements/ContextMenu/ServerHistory.xaml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +