diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 9883a70f6..83a29d814 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -50,6 +50,7 @@ END TEMPLATE--> * The engine can now load system fonts. * At the moment only available on Windows. * See `ISystemFontManager` for API. +* The client now display a loading screen during startup. ### Bugfixes diff --git a/Robust.Client/ClientIoC.cs b/Robust.Client/ClientIoC.cs index 15d25740f..ccf2e51c5 100644 --- a/Robust.Client/ClientIoC.cs +++ b/Robust.Client/ClientIoC.cs @@ -110,6 +110,8 @@ namespace Robust.Client deps.Register(); deps.Register(); deps.Register(); + deps.Register(); + deps.Register(); switch (mode) { diff --git a/Robust.Client/GameController/GameController.Standalone.cs b/Robust.Client/GameController/GameController.Standalone.cs index 5d2d24032..e899f8c7a 100644 --- a/Robust.Client/GameController/GameController.Standalone.cs +++ b/Robust.Client/GameController/GameController.Standalone.cs @@ -15,6 +15,7 @@ namespace Robust.Client internal partial class GameController : IPostInjectInit { private IGameLoop? _mainLoop; + private bool _dontStart; [Dependency] private readonly IClientGameTiming _gameTiming = default!; [Dependency] private readonly IDependencyCollection _dependencyCollection = default!; @@ -162,8 +163,11 @@ namespace Robust.Client return; } - DebugTools.AssertNotNull(_mainLoop); - _mainLoop!.Run(); + if (!_dontStart) + { + DebugTools.AssertNotNull(_mainLoop); + _mainLoop!.Run(); + } CleanupGameThread(); } diff --git a/Robust.Client/GameController/GameController.cs b/Robust.Client/GameController/GameController.cs index 5591f5881..0fbdfb918 100644 --- a/Robust.Client/GameController/GameController.cs +++ b/Robust.Client/GameController/GameController.cs @@ -97,6 +97,7 @@ namespace Robust.Client [Dependency] private readonly IReloadManager _reload = default!; [Dependency] private readonly ILocalizationManager _loc = default!; [Dependency] private readonly ISystemFontManagerInternal _systemFontManager = default!; + [Dependency] private readonly LoadingScreenManager _loadscr = default!; private IWebViewManagerHook? _webViewHook; @@ -133,28 +134,39 @@ namespace Robust.Client return Options.SplashLogo?.ToString() ?? _resourceManifest!.SplashLogo ?? ""; } + public bool ShowLoadingBar() + { + return _resourceManifest!.ShowLoadingBar ?? _configurationManager.GetCVar(CVars.LoadingShowBar); + } + internal bool StartupContinue(DisplayMode displayMode) { DebugTools.AssertNotNull(_resourceManifest); - _clyde.InitializePostWindowing(); - _audio.InitializePostWindowing(); - _clyde.SetWindowTitle(GameTitle()); + _loadscr.Initialize(42); - _taskManager.Initialize(); - _parallelMgr.Initialize(); + _loadscr.BeginLoadingSection("Init graphics", dontRender: true); + _clyde.InitializePostWindowing(); + _clyde.SetWindowTitle(GameTitle()); + _loadscr.EndLoadingSection(); + + _loadscr.LoadingStep(_audio.InitializePostWindowing, "Init audio"); + + _loadscr.LoadingStep(_taskManager.Initialize, _taskManager); + _loadscr.LoadingStep(_parallelMgr.Initialize, _parallelMgr); _fontManager.SetFontDpi((uint)_configurationManager.GetCVar(CVars.DisplayFontDpi)); - _systemFontManager.Initialize(); + + _loadscr.LoadingStep(_systemFontManager.Initialize, "System fonts"); // Load optional Robust modules. - LoadOptionalRobustModules(displayMode, _resourceManifest!); + _loadscr.LoadingStep(() => LoadOptionalRobustModules(displayMode, _resourceManifest!), "Robust Modules"); + _loadscr.BeginLoadingSection(_modLoader); // Disable load context usage on content start. // This prevents Content.Client being loaded twice and things like csi blowing up because of it. _modLoader.SetUseLoadContext(!ContentStart); var disableSandbox = Environment.GetEnvironmentVariable("ROBUST_DISABLE_SANDBOX") == "1"; _modLoader.SetEnableSandboxing(!disableSandbox && Options.Sandboxing); - if (!LoadModules()) return false; @@ -163,16 +175,23 @@ namespace Robust.Client _configurationManager.LoadCVarsFromAssembly(loadedModule); } - _serializationManager.Initialize(); - _loc.Initialize(); + _loadscr.EndLoadingSection(); + + _loadscr.LoadingStep(_serializationManager.Initialize, _serializationManager); + _loadscr.LoadingStep(_loc.Initialize, _loc); // Call Init in game assemblies. - _modLoader.BroadcastRunLevel(ModRunLevel.PreInit); + _loadscr.LoadingStep(() => _modLoader.BroadcastRunLevel(ModRunLevel.PreInit), "Content PreInit"); - // Finish initialization of WebView if loaded. - _webViewHook?.Initialize(); + _loadscr.LoadingStep(() => + { + // Finish initialization of WebView if loaded. + if (_webViewHook != null) + _loadscr.LoadingStep(_webViewHook.Initialize, _webViewHook); + }, + "WebView init"); - _modLoader.BroadcastRunLevel(ModRunLevel.Init); + _loadscr.LoadingStep(() => _modLoader.BroadcastRunLevel(ModRunLevel.Init), "Content Init"); // Start bad file extensions check after content init, // in case content screws with the VFS. @@ -181,42 +200,51 @@ namespace Robust.Client _configurationManager, _logManager.GetSawmill("res")); - _resourceCache.PreloadTextures(); - _networkManager.Initialize(false); - _configurationManager.SetupNetworking(); - _serializer.Initialize(); - _inputManager.Initialize(); - _console.Initialize(); + _loadscr.LoadingStep(_resourceCache.PreloadTextures, "Texture preload"); + _loadscr.LoadingStep(() => { _networkManager.Initialize(false); }, _networkManager); + _loadscr.LoadingStep(_configurationManager.SetupNetworking, _configurationManager); + _loadscr.LoadingStep(_serializer.Initialize, _serializer); + _loadscr.LoadingStep(_inputManager.Initialize, _inputManager); + _loadscr.LoadingStep(_console.Initialize, _console); // Make sure this is done before we try to load prototypes, // avoid any possibility of race conditions causing the check to not finish // before prototype load. - ProgramShared.FinishCheckBadFileExtensions(checkBadExtensions); + _loadscr.LoadingStep( + () => ProgramShared.FinishCheckBadFileExtensions(checkBadExtensions), + "Check bad file extensions"); - _reload.Initialize(); - _reflectionManager.Initialize(); + _loadscr.LoadingStep(_reload.Initialize, _reload); + _loadscr.LoadingStep(_reflectionManager.Initialize, _reflectionManager); + _loadscr.BeginLoadingSection(_prototypeManager); _prototypeManager.Initialize(); _prototypeManager.LoadDefaultPrototypes(); - _xamlProxyManager.Initialize(); - _xamlHotReloadManager.Initialize(); - _userInterfaceManager.Initialize(); - _eyeManager.Initialize(); - _entityManager.Initialize(); - _mapManager.Initialize(); - _gameStateManager.Initialize(); - _placementManager.Initialize(); - _viewVariablesManager.Initialize(); - _scriptClient.Initialize(); - _client.Initialize(); - _discord.Initialize(); - _tagManager.Initialize(); - _protoLoadMan.Initialize(); - _netResMan.Initialize(); - _replayLoader.Initialize(); - _replayPlayback.Initialize(); - _replayRecording.Initialize(); - _userInterfaceManager.PostInitialize(); - _modLoader.BroadcastRunLevel(ModRunLevel.PostInit); + _loadscr.EndLoadingSection(); + _loadscr.LoadingStep(_xamlProxyManager.Initialize, _xamlProxyManager); + _loadscr.LoadingStep(_xamlHotReloadManager.Initialize, _xamlHotReloadManager); + _loadscr.LoadingStep(_userInterfaceManager.Initialize, "UI init"); + _loadscr.LoadingStep(_eyeManager.Initialize, _eyeManager); + _loadscr.LoadingStep(_entityManager.Initialize, _entityManager); + _loadscr.LoadingStep(_mapManager.Initialize, _mapManager); + _loadscr.LoadingStep(_gameStateManager.Initialize, _gameStateManager); + _loadscr.LoadingStep(_placementManager.Initialize, _placementManager); + _loadscr.LoadingStep(_viewVariablesManager.Initialize, _viewVariablesManager); + _loadscr.LoadingStep(_scriptClient.Initialize, _scriptClient); + _loadscr.LoadingStep(_client.Initialize, _client); + _loadscr.LoadingStep(_discord.Initialize, _discord); + _loadscr.LoadingStep(_tagManager.Initialize, _tagManager); + _loadscr.LoadingStep(_protoLoadMan.Initialize, _protoLoadMan); + _loadscr.LoadingStep(_netResMan.Initialize, _netResMan); + _loadscr.LoadingStep(_replayLoader.Initialize, _replayLoader); + _loadscr.LoadingStep(_replayPlayback.Initialize, _replayPlayback); + _loadscr.LoadingStep(_replayRecording.Initialize, _replayRecording); + _loadscr.LoadingStep(_userInterfaceManager.PostInitialize, "UI postinit"); + + // Init stuff before this if at all possible. + + _loadscr.LoadingStep(() => _modLoader.BroadcastRunLevel(ModRunLevel.PostInit), "Content PostInit"); + + _loadscr.Finish(); if (_commandLineArgs?.Username != null) { @@ -423,7 +451,8 @@ namespace Robust.Client _configurationManager.OverrideConVars(new[] { (CVars.DisplayWindowIconSet.Name, WindowIconSet()), - (CVars.DisplaySplashLogo.Name, SplashLogo()) + (CVars.DisplaySplashLogo.Name, SplashLogo()), + (CVars.LoadingShowBar.Name, ShowLoadingBar().ToString()), }); } @@ -488,10 +517,18 @@ namespace Robust.Client public void Shutdown(string? reason = null) { - DebugTools.AssertNotNull(_mainLoop); + if (_mainLoop == null) + { + if (!_dontStart) + { + _logger.Info($"Shutdown called before client init completed: {reason ?? "No reason provided"}"); + _dontStart = true; + } + return; + } // Already got shut down I assume, - if (!_mainLoop!.Running) + if (!_mainLoop.Running) { return; } diff --git a/Robust.Client/Graphics/Clyde/Clyde.HLR.cs b/Robust.Client/Graphics/Clyde/Clyde.HLR.cs index f65230431..65f55ce4f 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.HLR.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.HLR.cs @@ -76,9 +76,9 @@ namespace Robust.Client.Graphics.Clyde } // Short path to render only the splash. - if (_drawingSplash) + if (_drawingLoadingScreen) { - DrawSplash(_renderHandle); + DrawLoadingScreen(_renderHandle); FlushRenderQueue(); SwapAllBuffers(); return; @@ -430,18 +430,11 @@ namespace Robust.Client.Graphics.Clyde FlushRenderQueue(); } - private void DrawSplash(IRenderHandle handle) + private void DrawLoadingScreen(IRenderHandle handle) { - // Clear screen to black for splash. ClearFramebuffer(Color.Black); - var splashTex = _cfg.GetCVar(CVars.DisplaySplashLogo); - if (string.IsNullOrEmpty(splashTex)) - return; - - var texture = _resourceCache.GetResource(splashTex).Texture; - - handle.DrawingHandleScreen.DrawTexture(texture, (ScreenSize - texture.Size) / 2); + _loadingScreenManager.DrawLoadingScreen(handle, ScreenSize); } private void RenderInRenderTarget(RenderTargetBase rt, Action a, Color? clearColor=default) diff --git a/Robust.Client/Graphics/Clyde/Clyde.cs b/Robust.Client/Graphics/Clyde/Clyde.cs index 2f16de31e..cd40da5bc 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.cs @@ -52,6 +52,7 @@ namespace Robust.Client.Graphics.Clyde [Dependency] private readonly ClientEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IReloadManager _reloads = default!; + [Dependency] private readonly LoadingScreenManager _loadingScreenManager = default!; private GLUniformBuffer ProjViewUBO = default!; private GLUniformBuffer UniformConstantsUBO = default!; @@ -68,7 +69,7 @@ namespace Robust.Client.Graphics.Clyde // VAO is per-window and not stored (not necessary!) private GLBuffer WindowVBO = default!; - private bool _drawingSplash = true; + private bool _drawingLoadingScreen = true; private GLShaderProgram? _currentProgram; @@ -213,7 +214,7 @@ namespace Robust.Client.Graphics.Clyde public void Ready() { - _drawingSplash = false; + _drawingLoadingScreen = false; InitLighting(); } diff --git a/Robust.Client/Graphics/LoadingScreenManager.cs b/Robust.Client/Graphics/LoadingScreenManager.cs new file mode 100644 index 000000000..7f2e4fea6 --- /dev/null +++ b/Robust.Client/Graphics/LoadingScreenManager.cs @@ -0,0 +1,306 @@ +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); + + /// + /// Start a loading bar "section" for the given method. + /// Must be ended with EndSection. + /// + void BeginLoadingSection(object method); + + void EndLoadingSection(); + + /// + /// Will run the giving function and add a custom "section" for it on the loading screen. + /// + void LoadingStep(Action action, object method); +} + +/// +/// Manager that creates and displays a basic splash screen and loading bar. +/// +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(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 (!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(); + } + } + + /// + /// Start a loading bar "section" for the given method. + /// Must be ended with EndSection. + /// + 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; + } + + /// + /// Will run the giving function and add a custom "section" for it on the loading screen. + /// + 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}"); + + _finished = true; + } + + #region Drawing functions + + /// + /// Draw out the splash and loading screen. + /// + 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 (!_resourceCache.TryGetResource(_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}"); + } + } +} diff --git a/Robust.Client/UserInterface/UserInterfaceManager.Scaling.cs b/Robust.Client/UserInterface/UserInterfaceManager.Scaling.cs index 7e76ecc94..56d96c1c2 100644 --- a/Robust.Client/UserInterface/UserInterfaceManager.Scaling.cs +++ b/Robust.Client/UserInterface/UserInterfaceManager.Scaling.cs @@ -2,6 +2,7 @@ using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Shared; +using Robust.Shared.Configuration; using Robust.Shared.Maths; using Robust.Shared.ViewVariables; @@ -97,11 +98,16 @@ internal partial class UserInterfaceManager }, true); } + internal static float CalculateUIScale(float osScale, IConfigurationManager cfg) + { + var cfgScale = cfg.GetCVar(CVars.DisplayUIScale); + return cfgScale == 0 ? osScale : cfgScale; + } + private float CalculateAutoScale(WindowRoot root) { //Grab the OS UIScale or the value set through CVAR debug - var osScale = _configurationManager.GetCVar(CVars.DisplayUIScale); - osScale = osScale == 0f ? root.Window.ContentScale.X : osScale; + var osScale = CalculateUIScale(root.Window.ContentScale.X, _configurationManager); var windowSize = root.Window.RenderTarget.Size; //Only run autoscale if it is enabled, otherwise default to just use OS UIScale diff --git a/Robust.Shared/CVars.cs b/Robust.Shared/CVars.cs index f92e59ccd..a03bd798a 100644 --- a/Robust.Shared/CVars.cs +++ b/Robust.Shared/CVars.cs @@ -1940,5 +1940,27 @@ namespace Robust.Shared /// public static readonly CVarDef FontWindowsDownloadable = CVarDef.Create("font.windows_downloadable", false, CVar.CLIENTONLY | CVar.ARCHIVE); + + /* + * LOADING + */ + + /// + /// Whether to show explicit loading bar during client initialization. + /// + public static readonly CVarDef LoadingShowBar = + CVarDef.Create("loading.show_bar", true, CVar.CLIENTONLY); + +#if TOOLS + private const bool DefaultShowDebug = true; +#else + private const bool DefaultShowDebug = false; +#endif + + /// + /// Whether to show "debug" info in the loading screen. + /// + public static readonly CVarDef LoadingShowDebug = + CVarDef.Create("loading.show_debug", DefaultShowDebug, CVar.CLIENTONLY); } } diff --git a/Robust.Shared/ContentPack/ResourceManifestData.cs b/Robust.Shared/ContentPack/ResourceManifestData.cs index 123026dc2..a58a10f45 100644 --- a/Robust.Shared/ContentPack/ResourceManifestData.cs +++ b/Robust.Shared/ContentPack/ResourceManifestData.cs @@ -11,12 +11,13 @@ internal sealed record ResourceManifestData( string? DefaultWindowTitle, string? WindowIconSet, string? SplashLogo, + bool? ShowLoadingBar, bool AutoConnect, string[]? ClientAssemblies ) { public static readonly ResourceManifestData Default = - new ResourceManifestData(Array.Empty(), null, null, null, null, true, null); + new ResourceManifestData(Array.Empty(), null, null, null, null, null, true, null); public static ResourceManifestData LoadResourceManifest(IResourceManager res) { @@ -58,6 +59,10 @@ internal sealed record ResourceManifestData( if (mapping.TryGetNode("splashLogo", out var splashNode)) splashLogo = splashNode.AsString(); + bool? showBar = null; + if (mapping.TryGetNode("show_loading_bar", out var showBarNode)) + showBar = showBarNode.AsBool(); + bool autoConnect = true; if (mapping.TryGetNode("autoConnect", out var autoConnectNode)) autoConnect = autoConnectNode.AsBool(); @@ -70,6 +75,7 @@ internal sealed record ResourceManifestData( defaultWindowTitle, windowIconSet, splashLogo, + showBar, autoConnect, clientAssemblies );