From 0d8032e3dfda050713147b903568b35f55be5265 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 11 Feb 2023 22:12:27 +0100 Subject: [PATCH] Warn on startup if .yaml files detected. (#3764) --- RELEASE-NOTES.md | 1 + .../GameController/GameController.cs | 14 +++++++++ Robust.Server/BaseServer.cs | 13 ++++++++ Robust.Shared/CVars.cs | 9 ++++++ Robust.Shared/ProgramShared.cs | 30 +++++++++++++++++++ Robust.UnitTesting/RobustIntegrationTest.cs | 6 +++- 6 files changed, 72 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index aee779a5b..f5c0c249a 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -49,6 +49,7 @@ END TEMPLATE--> * SDL2 backend now handles quit events (⌘+Q on macOS). * SDL2 backend now logs video driver backend used on initialization. +* The engine will now warn on startup if `*.yaml` files are found in resources, as this most likely indicates an accident. ### Internal diff --git a/Robust.Client/GameController/GameController.cs b/Robust.Client/GameController/GameController.cs index 1d50d8810..d060728ef 100644 --- a/Robust.Client/GameController/GameController.cs +++ b/Robust.Client/GameController/GameController.cs @@ -130,12 +130,26 @@ namespace Robust.Client // Call Init in game assemblies. _modLoader.BroadcastRunLevel(ModRunLevel.PreInit); _modLoader.BroadcastRunLevel(ModRunLevel.Init); + + // Start bad file extensions check after content init, + // in case content screws with the VFS. + var checkBadExtensions = ProgramShared.CheckBadFileExtensions( + _resourceCache, + _configurationManager, + _logManager.GetSawmill("res")); + _resourceCache.PreloadTextures(); _networkManager.Initialize(false); _configurationManager.SetupNetworking(); _serializer.Initialize(); _inputManager.Initialize(); _console.Initialize(); + + // 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); + _prototypeManager.Initialize(); _prototypeManager.LoadDirectory(new ResourcePath("/EnginePrototypes/")); _prototypeManager.LoadDirectory(Options.PrototypeDirectory); diff --git a/Robust.Server/BaseServer.cs b/Robust.Server/BaseServer.cs index 304280525..e840a1b64 100644 --- a/Robust.Server/BaseServer.cs +++ b/Robust.Server/BaseServer.cs @@ -338,11 +338,24 @@ namespace Robust.Server // Call Init in game assemblies. _modLoader.BroadcastRunLevel(ModRunLevel.Init); + + // Start bad file extensions check after content init, + // in case content screws with the VFS. + var checkBadExtensions = ProgramShared.CheckBadFileExtensions( + _resources, + _config, + _log.GetSawmill("res")); + _entityManager.Initialize(); _mapManager.Initialize(); _serialization.Initialize(); + // 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 and maybe break something. + ProgramShared.FinishCheckBadFileExtensions(checkBadExtensions); + // because of 'reasons' this has to be called after the last assembly is loaded // otherwise the prototypes will be cleared _prototype.Initialize(); diff --git a/Robust.Shared/CVars.cs b/Robust.Shared/CVars.cs index 38c643466..3b6fef5f5 100644 --- a/Robust.Shared/CVars.cs +++ b/Robust.Shared/CVars.cs @@ -1215,6 +1215,15 @@ namespace Robust.Shared public static readonly CVarDef ResPrototypeReloadWatch = CVarDef.Create("res.prototype_reload_watch", true, CVar.CLIENTONLY); + /// + /// If true, do a warning check at startup for probably-erroneous file extensions like .yaml in resources. + /// + /// + /// This check is always skipped on FULL_RELEASE. + /// + public static readonly CVarDef ResCheckBadFileExtensions = + CVarDef.Create("res.check_bad_file_extensions", true); + /* * DEBUG */ diff --git a/Robust.Shared/ProgramShared.cs b/Robust.Shared/ProgramShared.cs index 5755a5299..8e9362a9d 100644 --- a/Robust.Shared/ProgramShared.cs +++ b/Robust.Shared/ProgramShared.cs @@ -1,8 +1,13 @@ +using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.ContentPack; using Robust.Shared.Log; +using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Robust.Shared; @@ -70,4 +75,29 @@ internal static class ProgramShared res.MountContentPack(diskPath); } } + + internal static Task CheckBadFileExtensions(IResourceManager res, IConfigurationManager cfg, ISawmill sawmill) + { +#if FULL_RELEASE + return Task.CompletedTask; +#else + if (!cfg.GetCVar(CVars.ResCheckBadFileExtensions)) + return Task.CompletedTask; + + // Run on thread pool to avoid slowing down init. + return Task.Run(() => + { + foreach (var file in res.ContentFindFiles("/")) + { + if (file.Extension == "yaml") + sawmill.Warning($"{file} has extension \".yaml\". Robust only loads .yml files by convention, file will be ignored for prototypes and similar."); + } + }); +#endif + } + + internal static void FinishCheckBadFileExtensions(Task task) + { + task.Wait(); + } } diff --git a/Robust.UnitTesting/RobustIntegrationTest.cs b/Robust.UnitTesting/RobustIntegrationTest.cs index 8dbb8d958..b65980037 100644 --- a/Robust.UnitTesting/RobustIntegrationTest.cs +++ b/Robust.UnitTesting/RobustIntegrationTest.cs @@ -653,7 +653,9 @@ namespace Robust.UnitTesting ("log.runtimelog", "false"), (CVars.SysWinTickPeriod.Name, "-1"), (CVars.SysGCCollectStart.Name, "false"), - (RTCVars.FailureLogLevel.Name, (Options?.FailureLogLevel ?? RTCVars.FailureLogLevel.DefaultValue).ToString()) + (RTCVars.FailureLogLevel.Name, (Options?.FailureLogLevel ?? RTCVars.FailureLogLevel.DefaultValue).ToString()), + + (CVars.ResCheckBadFileExtensions.Name, "false") }); server.ContentStart = Options?.ContentStart ?? false; @@ -827,6 +829,8 @@ namespace Robust.UnitTesting (RTCVars.FailureLogLevel.Name, (Options?.FailureLogLevel ?? RTCVars.FailureLogLevel.DefaultValue).ToString()), (CVars.ResPrototypeReloadWatch.Name, "false"), + + (CVars.ResCheckBadFileExtensions.Name, "false") }); GameLoop = new IntegrationGameLoop(DependencyCollection.Resolve(),