mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-21 10:01:27 -07:00
Merge branch 'main' into user-pfp-discord-rpc
This commit is contained in:
commit
77262dc6e4
@ -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";
|
||||
|
||||
|
@ -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);
|
||||
int gameClientPid;
|
||||
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))
|
||||
{
|
||||
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
|
||||
int gameClientPid;
|
||||
using (var gameClient = Process.Start(startInfo)!)
|
||||
using (var process = Process.Start(startInfo)!)
|
||||
{
|
||||
gameClientPid = gameClient.Id;
|
||||
gameClientPid = process.Id;
|
||||
}
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {gameClientPid}), waiting for start event");
|
||||
|
||||
if (!startEvent.WaitOne(TimeSpan.FromSeconds(10)))
|
||||
startEventSignalled = 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";
|
||||
|
@ -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,
|
||||
_ => "?"
|
||||
};
|
||||
}
|
||||
|
@ -4,6 +4,10 @@ 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);
|
||||
@ -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<string, string?>
|
||||
{
|
||||
{ "DisableV2", null },
|
||||
{ "V2Rollout", null },
|
||||
{ "EnableV4", null },
|
||||
{ "EnableV4Chrome", null },
|
||||
{ "ABTest", null }
|
||||
@ -110,7 +114,7 @@ namespace Bloxstrap
|
||||
InGameMenuVersion.V1,
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
{ "DisableV2", "True" },
|
||||
{ "V2Rollout", "0" },
|
||||
{ "EnableV4", "False" },
|
||||
{ "EnableV4Chrome", "False" },
|
||||
{ "ABTest", "False" }
|
||||
@ -121,7 +125,7 @@ namespace Bloxstrap
|
||||
InGameMenuVersion.V2,
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
{ "DisableV2", "False" },
|
||||
{ "V2Rollout", "100" },
|
||||
{ "EnableV4", "False" },
|
||||
{ "EnableV4Chrome", "False" },
|
||||
{ "ABTest", "False" }
|
||||
@ -132,7 +136,7 @@ namespace Bloxstrap
|
||||
InGameMenuVersion.V4,
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
{ "DisableV2", "True" },
|
||||
{ "V2Rollout", "0" },
|
||||
{ "EnableV4", "True" },
|
||||
{ "EnableV4Chrome", "False" },
|
||||
{ "ABTest", "False" }
|
||||
@ -143,7 +147,7 @@ namespace Bloxstrap
|
||||
InGameMenuVersion.V4Chrome,
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
{ "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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,10 +485,8 @@ 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();
|
||||
|
@ -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<ActivityHistoryEntry> 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;
|
||||
|
@ -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<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}");
|
||||
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()
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
38
Bloxstrap/Models/ActivityHistoryEntry.cs
Normal file
38
Bloxstrap/Models/ActivityHistoryEntry.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
13
Bloxstrap/Models/Supporter.cs
Normal file
13
Bloxstrap/Models/Supporter.cs
Normal file
@ -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}";
|
||||
}
|
||||
}
|
11
Bloxstrap/Models/SupporterData.cs
Normal file
11
Bloxstrap/Models/SupporterData.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Bloxstrap.Models
|
||||
{
|
||||
public class SupporterData
|
||||
{
|
||||
[JsonPropertyName("columns")]
|
||||
public int Columns { get; set; }
|
||||
|
||||
[JsonPropertyName("supporters")]
|
||||
public List<Supporter> Supporters { get; set; } = null!;
|
||||
}
|
||||
}
|
83
Bloxstrap/Resources/Strings.Designer.cs
generated
83
Bloxstrap/Resources/Strings.Designer.cs
generated
@ -69,6 +69,24 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 About_Supporters_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("About.Supporters.Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
@ -449,6 +467,15 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Loading, please wait....
|
||||
/// </summary>
|
||||
public static string Common_Loading {
|
||||
get {
|
||||
return ResourceManager.GetString("Common.Loading", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Miscellaneous.
|
||||
/// </summary>
|
||||
@ -485,6 +512,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>
|
||||
@ -629,6 +665,24 @@ namespace Bloxstrap.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Roblox is still launching. A log file will only be available once Roblox launches..
|
||||
/// </summary>
|
||||
@ -638,15 +692,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>
|
||||
@ -1422,7 +1467,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>
|
||||
@ -1459,6 +1504,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>
|
||||
|
@ -268,9 +268,6 @@ 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.SeeServerDetails" xml:space="preserve">
|
||||
<value>See server details</value>
|
||||
</data>
|
||||
<data name="ContextMenu.ServerInformation.CopyInstanceId" xml:space="preserve">
|
||||
<value>Copy Instance ID</value>
|
||||
</data>
|
||||
@ -999,7 +996,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>
|
||||
@ -1156,4 +1153,28 @@ Are you sure you want to continue?</value>
|
||||
<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>
|
||||
</root>
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
<!--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" />
|
||||
@ -73,6 +81,85 @@
|
||||
</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">
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Bloxstrap.UI.ViewModels.Settings;
|
||||
using Bloxstrap.UI.ViewModels.About;
|
||||
|
||||
namespace Bloxstrap.UI.Elements.About.Pages
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -57,7 +57,19 @@
|
||||
<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="JoinLastServerMenuItem" Visibility="Collapsed" Click="JoinLastServerMenuItem_Click">
|
||||
<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>
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
89
Bloxstrap/UI/Elements/ContextMenu/ServerHistory.xaml
Normal file
89
Bloxstrap/UI/Elements/ContextMenu/ServerHistory.xaml
Normal file
@ -0,0 +1,89 @@
|
||||
<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"
|
||||
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="*" />
|
||||
<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" />
|
||||
|
||||
<Border Grid.Row="1">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ActivityHistory, Mode=OneWay}" Value="{x:Null}">
|
||||
<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="1" ItemsSource="{Binding ActivityHistory, Mode=OneWay}" Margin="8">
|
||||
<ListView.Style>
|
||||
<Style TargetType="ListView" BasedOn="{StaticResource {x:Type ListView}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ActivityHistory, Mode=OneWay}" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</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 GameThumbnail, IsAsync=True}" />
|
||||
</Border.Background>
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Column="1" Margin="16,0,0,0" VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding GameName}" FontSize="18" FontWeight="Medium" />
|
||||
<TextBlock Text="{Binding TimeJoinedFriendly}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<ui:Button Margin="0,8,0,0" Content="{x:Static resources:Strings.ContextMenu_GameHistory_Rejoin}" Command="{Binding RejoinServerCommand}" Appearance="Success" Icon="Play28" IconFilled="True" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ui:Card>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<Border Grid.Row="2" 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -24,7 +24,7 @@
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ui:TitleBar Grid.Row="0" Grid.ColumnSpan="2" Padding="8" ShowMinimize="False" ShowMaximize="False" CanMaximize="False" KeyboardNavigation.TabNavigation="None" />
|
||||
<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" />
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
@ -53,7 +53,7 @@
|
||||
|
||||
<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="WindowNew16" Content="Support us on Ko-fi!" NavigateUri="https://ko-fi.com/boxerpizza" />
|
||||
<ui:Hyperlink Icon="Heart48" Content="Support us on Ko-fi!" NavigateUri="https://ko-fi.com/boxerpizza" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
|
@ -307,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)
|
||||
|
@ -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
|
||||
|
@ -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 _);
|
||||
}
|
||||
}
|
||||
|
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));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using Bloxstrap.Integrations;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace Bloxstrap.UI.ViewModels.ContextMenu
|
||||
{
|
||||
internal class ServerHistoryViewModel : NotifyPropertyChangedViewModel
|
||||
{
|
||||
private readonly ActivityWatcher _activityWatcher;
|
||||
|
||||
public List<ActivityHistoryEntry>? ActivityHistory { get; private set; }
|
||||
|
||||
public ICommand CloseWindowCommand => new RelayCommand(RequestClose);
|
||||
|
||||
public EventHandler? RequestCloseEvent;
|
||||
|
||||
public ServerHistoryViewModel(ActivityWatcher activityWatcher)
|
||||
{
|
||||
_activityWatcher = activityWatcher;
|
||||
|
||||
_activityWatcher.OnGameLeave += (_, _) => LoadData();
|
||||
|
||||
LoadData();
|
||||
}
|
||||
|
||||
private async void LoadData()
|
||||
{
|
||||
var entries = _activityWatcher.ActivityHistory.Where(x => !x.DetailsLoaded);
|
||||
|
||||
if (entries.Any())
|
||||
{
|
||||
string universeIds = String.Join(',', entries.Select(x => x.UniverseId));
|
||||
|
||||
var gameDetailResponse = await Http.GetJson<ApiArrayResponse<GameDetailResponse>>($"https://games.roblox.com/v1/games?universeIds={universeIds}");
|
||||
|
||||
if (gameDetailResponse is null || !gameDetailResponse.Data.Any())
|
||||
return;
|
||||
|
||||
var universeThumbnailResponse = await Http.GetJson<ApiArrayResponse<ThumbnailResponse>>($"https://thumbnails.roblox.com/v1/games/icons?universeIds={universeIds}&returnPolicy=PlaceHolder&size=128x128&format=Png&isCircular=false");
|
||||
|
||||
if (universeThumbnailResponse is null || !universeThumbnailResponse.Data.Any())
|
||||
return;
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
entry.GameName = gameDetailResponse.Data.Where(x => x.Id == entry.UniverseId).Select(x => x.Name).First();
|
||||
entry.GameThumbnail = universeThumbnailResponse.Data.Where(x => x.TargetId == entry.UniverseId).Select(x => x.ImageUrl).First();
|
||||
entry.DetailsLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
ActivityHistory = new(_activityWatcher.ActivityHistory);
|
||||
OnPropertyChanged(nameof(ActivityHistory));
|
||||
}
|
||||
|
||||
private void RequestClose() => RequestCloseEvent?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
@ -32,11 +32,11 @@ namespace Bloxstrap.UI.ViewModels.Installer
|
||||
}
|
||||
|
||||
installer.InstallLocation = value;
|
||||
CheckExistingData();
|
||||
OnPropertyChanged(nameof(DataFoundMessageVisibility));
|
||||
}
|
||||
}
|
||||
|
||||
public Visibility DataFoundMessageVisibility { get; set; } = Visibility.Collapsed;
|
||||
public Visibility DataFoundMessageVisibility => installer.ExistingDataPresent ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
public string ErrorMessage => installer.InstallLocationError;
|
||||
|
||||
@ -61,7 +61,6 @@ namespace Bloxstrap.UI.ViewModels.Installer
|
||||
public InstallViewModel()
|
||||
{
|
||||
_originalInstallLocation = installer.InstallLocation;
|
||||
CheckExistingData();
|
||||
}
|
||||
|
||||
public bool DoInstall()
|
||||
@ -79,16 +78,6 @@ namespace Bloxstrap.UI.ViewModels.Installer
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CheckExistingData()
|
||||
{
|
||||
if (File.Exists(Path.Combine(InstallLocation, "Settings.json")))
|
||||
DataFoundMessageVisibility = Visibility.Visible;
|
||||
else
|
||||
DataFoundMessageVisibility = Visibility.Collapsed;
|
||||
|
||||
OnPropertyChanged(nameof(DataFoundMessageVisibility));
|
||||
}
|
||||
|
||||
private void BrowseInstallLocation()
|
||||
{
|
||||
using var dialog = new System.Windows.Forms.FolderBrowserDialog();
|
||||
|
@ -5,9 +5,9 @@
|
||||
{
|
||||
// formatting is done here instead of in xaml, it's just a bit easier
|
||||
public string MainText => String.Format(
|
||||
Resources.Strings.Installer_Welcome_MainText,
|
||||
Strings.Installer_Welcome_MainText,
|
||||
"[github.com/pizzaboxer/bloxstrap](https://github.com/pizzaboxer/bloxstrap)",
|
||||
"[bloxstrap.pizzaboxer.xyz](https://bloxstrap.pizzaboxer.xyz)"
|
||||
"[bloxstraplabs.com](https://bloxstraplabs.com)"
|
||||
);
|
||||
|
||||
public string VersionNotice { get; private set; } = "";
|
||||
|
@ -1,17 +0,0 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace Bloxstrap.UI.ViewModels.Settings
|
||||
{
|
||||
public class AboutViewModel
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -23,9 +23,9 @@ namespace Bloxstrap.UI.ViewModels.Settings
|
||||
IBootstrapperDialog dialog = App.Settings.Prop.BootstrapperStyle.GetNew();
|
||||
|
||||
if (App.Settings.Prop.BootstrapperStyle == BootstrapperStyle.ByfronDialog)
|
||||
dialog.Message = Resources.Strings.Bootstrapper_StylePreview_ImageCancel;
|
||||
dialog.Message = Strings.Bootstrapper_StylePreview_ImageCancel;
|
||||
else
|
||||
dialog.Message = Resources.Strings.Bootstrapper_StylePreview_TextCancel;
|
||||
dialog.Message = Strings.Bootstrapper_StylePreview_TextCancel;
|
||||
|
||||
dialog.CancelEnabled = true;
|
||||
dialog.ShowBootstrapper();
|
||||
@ -35,7 +35,7 @@ namespace Bloxstrap.UI.ViewModels.Settings
|
||||
{
|
||||
var dialog = new OpenFileDialog
|
||||
{
|
||||
Filter = $"{Resources.Strings.Menu_IconFiles}|*.ico"
|
||||
Filter = $"{Strings.Menu_IconFiles}|*.ico"
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() != true)
|
||||
|
@ -31,13 +31,13 @@ namespace Bloxstrap.UI.ViewModels.Settings
|
||||
{
|
||||
for (int i = 10; i > 0; i--)
|
||||
{
|
||||
ContinueButtonText = $"({i}) {Resources.Strings.Menu_FastFlagEditor_Warning_Continue}";
|
||||
ContinueButtonText = $"({i}) {Strings.Menu_FastFlagEditor_Warning_Continue}";
|
||||
OnPropertyChanged(nameof(ContinueButtonText));
|
||||
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
ContinueButtonText = Resources.Strings.Menu_FastFlagEditor_Warning_Continue;
|
||||
ContinueButtonText = Strings.Menu_FastFlagEditor_Warning_Continue;
|
||||
OnPropertyChanged(nameof(ContinueButtonText));
|
||||
|
||||
CanContinue = true;
|
||||
|
@ -19,7 +19,7 @@ namespace Bloxstrap.UI.ViewModels.Settings
|
||||
{
|
||||
CustomIntegrations.Add(new CustomIntegration()
|
||||
{
|
||||
Name = Resources.Strings.Menu_Integrations_Custom_NewIntegration
|
||||
Name = Strings.Menu_Integrations_Custom_NewIntegration
|
||||
});
|
||||
|
||||
SelectedCustomIntegrationIndex = CustomIntegrations.Count - 1;
|
||||
|
@ -17,8 +17,16 @@ namespace Bloxstrap.Utility
|
||||
public InterProcessLock(string name, TimeSpan timeout)
|
||||
{
|
||||
Mutex = new Mutex(false, "Bloxstrap-" + name);
|
||||
|
||||
try
|
||||
{
|
||||
IsAcquired = Mutex.WaitOne(timeout);
|
||||
}
|
||||
catch (AbandonedMutexException)
|
||||
{
|
||||
IsAcquired = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
> [!CAUTION]
|
||||
> The only official places to download Bloxstrap are this GitHub repository and [bloxstrap.pizzaboxer.xyz](https://bloxstrap.pizzaboxer.xyz). Any other websites offering downloads or claiming to be us are not controlled by us.
|
||||
> The only official places to download Bloxstrap are this GitHub repository and [bloxstraplabs.com](https://bloxstraplabs.com). Any other websites offering downloads or claiming to be us are not controlled by us.
|
||||
|
||||
# <img src="https://github.com/pizzaboxer/bloxstrap/raw/main/Images/Bloxstrap.png" width="48"/> Bloxstrap
|
||||
|
||||
@ -20,7 +20,7 @@ Bloxstrap is only supported for PCs running Windows.
|
||||
|
||||
**Q: Is this malware?**
|
||||
|
||||
**A:** No. The source code here is viewable to all, and it'd be impossible for us to slip anything malicious into the downloads without anyone noticing. Just be sure you're downloading it from an official source. The only two official sources are this GitHub repository and [bloxstrap.pizzaboxer.xyz](https://bloxstrap.pizzaboxer.xyz).
|
||||
**A:** No. The source code here is viewable to all, and it'd be impossible for us to slip anything malicious into the downloads without anyone noticing. Just be sure you're downloading it from an official source. The only two official sources are this GitHub repository and [bloxstraplabs.com](https://bloxstraplabs.com).
|
||||
|
||||
**Q: Can using this get me banned?**
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user