Fix replay load error screens (#32115)

If an error occurs while loading a replay, it's *supposed* to show a popup "hey do you wanna continue with error tolerance", and it does do this. But because of spaghetti in the replay state code, it also immediately tries to reload the replay without input, as a consequence of trying to reset to the game's default state.

Now the replay load system has a proper game state for "replay load failed", with nice presentation and explicit formatting and 10% less italian cuisine.
This commit is contained in:
Pieter-Jan Briers
2024-09-13 15:58:02 +02:00
committed by GitHub
parent 1d997d6e46
commit b6ca604a66
5 changed files with 115 additions and 30 deletions

View File

@@ -1,10 +1,8 @@
using System.IO.Compression;
using Content.Client.Administration.Managers; using Content.Client.Administration.Managers;
using Content.Client.Launcher; using Content.Client.Launcher;
using Content.Client.MainMenu; using Content.Client.MainMenu;
using Content.Client.Replay.Spectator; using Content.Client.Replay.Spectator;
using Content.Client.Replay.UI.Loading; using Content.Client.Replay.UI.Loading;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Systems.Chat; using Content.Client.UserInterface.Systems.Chat;
using Content.Shared.Chat; using Content.Shared.Chat;
using Content.Shared.Effects; using Content.Shared.Effects;
@@ -26,8 +24,6 @@ using Robust.Client.Replays.Playback;
using Robust.Client.State; using Robust.Client.State;
using Robust.Client.Timing; using Robust.Client.Timing;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared; using Robust.Shared;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
@@ -96,32 +92,17 @@ public sealed class ContentReplayPlaybackManager
return; return;
} }
ReturnToDefaultState(); if (_client.RunLevel == ClientRunLevel.SinglePlayerGame)
_client.StopSinglePlayer();
// Show a popup window with the error message Action? retryAction = null;
var text = Loc.GetString("replay-loading-failed", ("reason", exception)); Action? cancelAction = null;
var box = new BoxContainer
if (!_cfg.GetCVar(CVars.ReplayIgnoreErrors) && LastLoad is { } last)
{ {
Orientation = BoxContainer.LayoutOrientation.Vertical, retryAction = () =>
Children = {new Label {Text = text}}
};
var popup = new DefaultWindow { Title = "Error!" };
popup.Contents.AddChild(box);
// Add button for attempting to re-load the replay while ignoring some errors.
if (!_cfg.GetCVar(CVars.ReplayIgnoreErrors) && LastLoad is {} last)
{
var button = new Button
{
Text = Loc.GetString("replay-loading-retry"),
StyleClasses = { StyleBase.ButtonCaution }
};
button.OnPressed += _ =>
{ {
_cfg.SetCVar(CVars.ReplayIgnoreErrors, true); _cfg.SetCVar(CVars.ReplayIgnoreErrors, true);
popup.Dispose();
IReplayFileReader reader = last.Zip == null IReplayFileReader reader = last.Zip == null
? new ReplayFileReaderResources(_resMan, last.Folder) ? new ReplayFileReaderResources(_resMan, last.Folder)
@@ -129,11 +110,20 @@ public sealed class ContentReplayPlaybackManager
_loadMan.LoadAndStartReplay(reader); _loadMan.LoadAndStartReplay(reader);
}; };
box.AddChild(button);
} }
popup.OpenCentered(); // If we have an explicit menu to get back to (e.g. replay browser UI), show a cancel button.
if (DefaultState != null)
{
cancelAction = () =>
{
_stateMan.RequestStateChange(DefaultState);
};
}
// Switch to a new game state to present the error and cancel/retry options.
var state = _stateMan.RequestStateChange<ReplayLoadingFailed>();
state.SetData(exception, cancelAction, retryAction);
} }
public void ReturnToDefaultState() public void ReturnToDefaultState()

View File

@@ -0,0 +1,36 @@
using Content.Client.Stylesheets;
using Robust.Client.State;
using Robust.Client.UserInterface;
using Robust.Shared.Utility;
namespace Content.Client.Replay.UI.Loading;
/// <summary>
/// State used to display an error message if a replay failed to load.
/// </summary>
/// <seealso cref="ReplayLoadingFailedControl"/>
/// <seealso cref="ContentReplayPlaybackManager"/>
public sealed class ReplayLoadingFailed : State
{
[Dependency] private readonly IStylesheetManager _stylesheetManager = default!;
[Dependency] private readonly IUserInterfaceManager _userInterface = default!;
private ReplayLoadingFailedControl? _control;
public void SetData(Exception exception, Action? cancelPressed, Action? retryPressed)
{
DebugTools.Assert(_control != null);
_control.SetData(exception, cancelPressed, retryPressed);
}
protected override void Startup()
{
_control = new ReplayLoadingFailedControl(_stylesheetManager);
_userInterface.StateRoot.AddChild(_control);
}
protected override void Shutdown()
{
_control?.Orphan();
}
}

View File

@@ -0,0 +1,14 @@
<Control xmlns="https://spacestation14.io"
xmlns:pllax="clr-namespace:Content.Client.Parallax">
<pllax:ParallaxControl />
<Control HorizontalAlignment="Center" VerticalAlignment="Center">
<PanelContainer StyleClasses="AngleRect" />
<BoxContainer Orientation="Vertical" SetSize="800 600" Margin="2">
<ScrollContainer Name="what" VerticalExpand="True" HScrollEnabled="False" Margin="0 0 0 2" ReturnMeasure="True">
<RichTextLabel Name="ReasonLabel" VerticalAlignment="Top" />
</ScrollContainer>
<Button Name="RetryButton" StyleClasses="Caution" Text="{Loc 'replay-loading-retry'}" Visible="False" />
<Button Name="CancelButton" Text="{Loc 'replay-loading-cancel'}" Visible="False" />
</BoxContainer>
</Control>
</Control>

View File

@@ -0,0 +1,44 @@
using Content.Client.Stylesheets;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client.Replay.UI.Loading;
[GenerateTypedNameReferences]
public sealed partial class ReplayLoadingFailedControl : Control
{
public ReplayLoadingFailedControl(IStylesheetManager stylesheet)
{
RobustXamlLoader.Load(this);
Stylesheet = stylesheet.SheetSpace;
LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
}
public void SetData(Exception exception, Action? cancelPressed, Action? retryPressed)
{
ReasonLabel.SetMessage(
FormattedMessage.FromUnformatted(Loc.GetString("replay-loading-failed", ("reason", exception))));
if (cancelPressed != null)
{
CancelButton.Visible = true;
CancelButton.OnPressed += _ =>
{
cancelPressed();
};
}
if (retryPressed != null)
{
RetryButton.Visible = true;
RetryButton.OnPressed += _ =>
{
retryPressed();
};
}
}
}

View File

@@ -9,6 +9,7 @@ replay-loading-starting= Starting Entities
replay-loading-failed = Failed to load replay. Error: replay-loading-failed = Failed to load replay. Error:
{$reason} {$reason}
replay-loading-retry = Try load with more exception tolerance - MAY CAUSE BUGS! replay-loading-retry = Try load with more exception tolerance - MAY CAUSE BUGS!
replay-loading-cancel = Cancel
# Main Menu # Main Menu
replay-menu-subtext = Replay Client replay-menu-subtext = Replay Client