mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
319 lines
9.7 KiB
C#
319 lines
9.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Robust.Client.ResourceManagement;
|
|
using Robust.Client.UserInterface;
|
|
using Robust.Shared;
|
|
using Robust.Shared.Configuration;
|
|
using Robust.Shared.Console;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Log;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Timing;
|
|
using Stopwatch = Robust.Shared.Timing.Stopwatch;
|
|
|
|
namespace Robust.Client.Graphics;
|
|
|
|
internal interface ILoadingScreenManager
|
|
{
|
|
void BeginLoadingSection(string sectionName);
|
|
|
|
/// <summary>
|
|
/// Start a loading bar "section" for the given method.
|
|
/// Must be ended with EndSection.
|
|
/// </summary>
|
|
void BeginLoadingSection(object method);
|
|
|
|
void EndLoadingSection();
|
|
|
|
/// <summary>
|
|
/// Will run the giving function and add a custom "section" for it on the loading screen.
|
|
/// </summary>
|
|
void LoadingStep(Action action, object method);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Manager that creates and displays a basic splash screen and loading bar.
|
|
/// </summary>
|
|
internal sealed class LoadingScreenManager : ILoadingScreenManager
|
|
{
|
|
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
|
[Dependency] private readonly IClydeInternal _clyde = default!;
|
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
|
[Dependency] private readonly ILogManager _logManager = default!;
|
|
|
|
private ISawmill _sawmill = default!;
|
|
|
|
private readonly Stopwatch _sw = new();
|
|
|
|
#region UI constants
|
|
|
|
private const int LoadingBarWidth = 250;
|
|
private const int LoadingBarHeight = 20;
|
|
private const int LoadingBarOutlineOffset = 5;
|
|
private static readonly Vector2i LogoLoadingBarOffset = (0, 20);
|
|
private static readonly Vector2i LoadTimesIndent = (20, 0);
|
|
|
|
private const int NumLongestLoadTimes = 5;
|
|
|
|
private static readonly Color LoadingBarColor = Color.White;
|
|
|
|
#endregion
|
|
|
|
#region Cvars
|
|
|
|
private string _splashLogo = "";
|
|
private bool _showLoadingBar;
|
|
private bool _showDebug;
|
|
|
|
#endregion
|
|
|
|
private const string FontLocation = "/EngineFonts/NotoSans/NotoSans-Regular.ttf";
|
|
private const int FontSize = 11;
|
|
private VectorFont? _font;
|
|
|
|
// Number of loading sections for the loading bar. This has to be manually set!
|
|
private int _numberOfLoadingSections;
|
|
|
|
// The name of the section and how much time it took to load
|
|
internal readonly List<(string Name, TimeSpan LoadTime)> Times = [];
|
|
|
|
private int _currentSection;
|
|
private string? _currentSectionName;
|
|
|
|
private bool _currentlyInSection;
|
|
private bool _finished;
|
|
|
|
public void Initialize(int sections)
|
|
{
|
|
if (_finished)
|
|
return;
|
|
|
|
_clyde.VsyncEnabled = false;
|
|
|
|
_numberOfLoadingSections = sections;
|
|
|
|
_sawmill = _logManager.GetSawmill("loading");
|
|
|
|
_splashLogo = _cfg.GetCVar(CVars.DisplaySplashLogo);
|
|
_showLoadingBar = _cfg.GetCVar(CVars.LoadingShowBar);
|
|
_showDebug = _cfg.GetCVar(CVars.LoadingShowDebug);
|
|
|
|
if (_resourceCache.TryGetResource<FontResource>(FontLocation, out var fontResource))
|
|
_font = new VectorFont(fontResource, FontSize);
|
|
else
|
|
_sawmill.Error($"Could not load font: {FontLocation}");
|
|
}
|
|
|
|
public void BeginLoadingSection(string sectionName) => BeginLoadingSection(sectionName, false);
|
|
public void BeginLoadingSection(string sectionName, bool dontRender)
|
|
{
|
|
if (_finished)
|
|
return;
|
|
|
|
if (_currentlyInSection)
|
|
throw new InvalidOperationException("You cannot begin more than one section at a time!");
|
|
|
|
_currentlyInSection = true;
|
|
|
|
_currentSectionName = sectionName;
|
|
|
|
if (_clyde.IsInitialized)
|
|
{
|
|
_clyde.MainWindow.SetWindowProgress(
|
|
WindowProgressState.Normal,
|
|
_currentSection / (float)_numberOfLoadingSections);
|
|
}
|
|
|
|
if (!dontRender)
|
|
{
|
|
// This ensures that if the screen was resized or something the new size is properly updated to clyde.
|
|
_clyde.ProcessInput(new FrameEventArgs((float)_sw.Elapsed.TotalSeconds));
|
|
_sw.Restart();
|
|
_clyde.Render();
|
|
}
|
|
else
|
|
{
|
|
_sw.Restart();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start a loading bar "section" for the given method.
|
|
/// Must be ended with EndSection.
|
|
/// </summary>
|
|
public void BeginLoadingSection(object method)
|
|
{
|
|
if (_finished)
|
|
return;
|
|
|
|
BeginLoadingSection(method.GetType().Name);
|
|
}
|
|
|
|
public void EndLoadingSection()
|
|
{
|
|
if (_finished)
|
|
return;
|
|
|
|
var time = _sw.Elapsed;
|
|
if (_currentSectionName != null)
|
|
Times.Add((_currentSectionName, time));
|
|
_currentSection++;
|
|
_currentlyInSection = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Will run the giving function and add a custom "section" for it on the loading screen.
|
|
/// </summary>
|
|
public void LoadingStep(Action action, object method)
|
|
{
|
|
if (_finished)
|
|
return;
|
|
|
|
BeginLoadingSection(method as string ?? method.GetType().Name);
|
|
action();
|
|
EndLoadingSection();
|
|
}
|
|
|
|
public void Finish()
|
|
{
|
|
if (_finished)
|
|
return;
|
|
|
|
if (_currentSection != _numberOfLoadingSections)
|
|
_sawmill.Error($"The number of seen loading sections isn't equal to the total number of loading sections! Seen: {_currentSection}, Total: {_numberOfLoadingSections}");
|
|
|
|
_clyde.MainWindow.SetWindowProgress(WindowProgressState.None, 1);
|
|
|
|
_finished = true;
|
|
}
|
|
|
|
#region Drawing functions
|
|
|
|
/// <summary>
|
|
/// Draw out the splash and loading screen.
|
|
/// </summary>
|
|
public void DrawLoadingScreen(IRenderHandle handle, Vector2i screenSize)
|
|
{
|
|
if (_finished)
|
|
return;
|
|
|
|
var scale = UserInterfaceManager.CalculateUIScale(_clyde.MainWindow.ContentScale.X, _cfg);
|
|
|
|
// Start at the center!
|
|
var location = screenSize / 2;
|
|
|
|
DrawSplash(handle, ref location, scale);
|
|
|
|
DrawLoadingBar(handle, ref location, scale);
|
|
|
|
if (_showDebug)
|
|
{
|
|
DrawCurrentLoading(handle, ref location, scale);
|
|
|
|
DrawTopTimes(handle, ref location, scale);
|
|
}
|
|
}
|
|
|
|
private void DrawSplash(IRenderHandle handle, ref Vector2i startLocation, float scale)
|
|
{
|
|
if (string.IsNullOrEmpty(_splashLogo))
|
|
return;
|
|
|
|
if (!_resourceCache.TryGetResource<TextureResource>(_splashLogo, out var textureResource))
|
|
return;
|
|
|
|
var drawSize = textureResource.Texture.Size * scale;
|
|
|
|
handle.DrawingHandleScreen.DrawTextureRect(textureResource.Texture, UIBox2.FromDimensions(startLocation - drawSize / 2, drawSize));
|
|
startLocation += Vector2i.Up * (int) drawSize.Y / 2;
|
|
}
|
|
|
|
private void DrawLoadingBar(IRenderHandle handle, ref Vector2i location, float scale)
|
|
{
|
|
var barWidth = (int)(LoadingBarWidth * scale);
|
|
var barHeight = (int)(LoadingBarHeight * scale);
|
|
var outlineOffset = (int)(LoadingBarOutlineOffset * scale);
|
|
|
|
// Always do the offsets, it looks a lot better!
|
|
location.X -= barWidth / 2;
|
|
location += (Vector2i) (LogoLoadingBarOffset * scale);
|
|
|
|
if (!_showLoadingBar)
|
|
return;
|
|
|
|
var sectionWidth = barWidth / _numberOfLoadingSections;
|
|
|
|
var barTopLeft = location;
|
|
var barBottomRight = new Vector2i(_currentSection * sectionWidth % barWidth, barHeight);
|
|
var barBottomRightMax = new Vector2i(barWidth, barHeight);
|
|
|
|
var outlinePosition = barTopLeft + Vector2i.DownLeft * outlineOffset;
|
|
var outlineSize = barBottomRightMax + Vector2i.UpRight * 2 * outlineOffset;
|
|
|
|
// Outline
|
|
handle.DrawingHandleScreen.DrawRect(UIBox2.FromDimensions(outlinePosition, outlineSize), LoadingBarColor, false);
|
|
|
|
// Progress bar
|
|
handle.DrawingHandleScreen.DrawRect(UIBox2.FromDimensions(barTopLeft, barBottomRight), LoadingBarColor);
|
|
|
|
location += Vector2i.Up * outlineSize;
|
|
}
|
|
|
|
// Draw the currently loading section to the screen.
|
|
private void DrawCurrentLoading(IRenderHandle handle, ref Vector2i location, float scale)
|
|
{
|
|
if (_font == null || _currentSectionName == null)
|
|
return;
|
|
|
|
handle.DrawingHandleScreen.DrawString(_font, location, _currentSectionName, scale, Color.White);
|
|
location += Vector2i.Up * _font.GetLineHeight(scale);
|
|
}
|
|
|
|
// Draw the slowest loading times to the screen.
|
|
private void DrawTopTimes(IRenderHandle handle, ref Vector2i location, float scale)
|
|
{
|
|
if (_font == null)
|
|
return;
|
|
|
|
location += (Vector2i)(LoadTimesIndent * scale);
|
|
|
|
var offset = 0;
|
|
var x = 0;
|
|
Times.Sort((a, b) => b.LoadTime.CompareTo(a.LoadTime));
|
|
|
|
foreach (var (name, time) in Times)
|
|
{
|
|
if (x >= NumLongestLoadTimes)
|
|
break;
|
|
|
|
var entry = $"{time.TotalSeconds:F2} - {name}";
|
|
handle.DrawingHandleScreen.DrawString(_font, location + new Vector2i(0, offset), entry, scale, Color.White);
|
|
offset += _font.GetLineHeight(scale);
|
|
x++;
|
|
}
|
|
|
|
location += Vector2i.Up * offset;
|
|
}
|
|
|
|
#endregion // Drawing functions
|
|
}
|
|
|
|
internal sealed class ShowTopLoadingTimesCommand : IConsoleCommand
|
|
{
|
|
[Dependency] private readonly LoadingScreenManager _mgr = default!;
|
|
|
|
public string Command => "loading_top";
|
|
public string Description => "";
|
|
public string Help => "";
|
|
|
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
|
{
|
|
var sorted = _mgr.Times.Where(x => x.LoadTime > TimeSpan.FromSeconds(0.01)).OrderByDescending(x => x.LoadTime);
|
|
foreach (var (name, time) in sorted)
|
|
{
|
|
shell.WriteLine($"{time.TotalSeconds:F2} - {name}");
|
|
}
|
|
}
|
|
}
|