Stop passing around partially constructed ModData instances.

This commit is contained in:
Paul Chote
2025-12-06 21:36:34 +00:00
committed by Gustas Kažukauskas
parent 2b6977d53f
commit 31607bd0cf
15 changed files with 82 additions and 77 deletions

View File

@@ -23,11 +23,11 @@ namespace OpenRA
static FluentBundle modFluentBundle;
static FluentBundle mapFluentBundle;
public static void Initialize(ModData modData, IReadOnlyFileSystem fileSystem)
public static void Initialize(Manifest manifest, IReadOnlyFileSystem fileSystem)
{
lock (SyncObject)
{
modFluentBundle = new FluentBundle(modData.Manifest.FluentCulture, modData.Manifest.FluentMessages, fileSystem);
modFluentBundle = new FluentBundle(manifest.FluentCulture, manifest.FluentMessages, fileSystem);
if (fileSystem is Map map && map.FluentMessageDefinitions != null)
{
var files = ImmutableArray<string>.Empty;
@@ -45,7 +45,7 @@ namespace OpenRA
text = builder.ToString();
}
mapFluentBundle = new FluentBundle(modData.Manifest.FluentCulture, files, fileSystem, text);
mapFluentBundle = new FluentBundle(manifest.FluentCulture, files, fileSystem, text);
}
}
}

View File

@@ -168,6 +168,7 @@ namespace OpenRA
{
return ModData.WidgetLoader.LoadWidget(new WidgetArgs(args)
{
{ "modData", ModData },
{ "world", world },
{ "orderManager", OrderManager },
{ "worldRenderer", worldRenderer },
@@ -502,14 +503,14 @@ namespace OpenRA
LocalPlayerProfile = new LocalPlayerProfile(Path.Combine(Platform.SupportDir, Settings.Game.AuthProfile), ModData.GetOrCreate<PlayerDatabase>());
if (!ModData.LoadScreen.BeforeLoad())
if (!ModData.LoadScreen.BeforeLoad(ModData))
return;
ModData.InitializeLoaders(ModData.DefaultFileSystem);
Renderer.InitializeFonts(ModData);
using (new PerfTimer("LoadMaps"))
ModData.MapCache.LoadMaps();
ModData.MapCache.LoadMaps(ModData);
Cursor?.Dispose();
Cursor = new CursorManager(ModData);

View File

@@ -21,6 +21,7 @@ using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Support;
using OpenRA.Traits;
using FS = OpenRA.FileSystem.FileSystem;
namespace OpenRA
{
@@ -31,8 +32,9 @@ namespace OpenRA
readonly Dictionary<IReadOnlyPackage, MapClassification> mapLocations = [];
public bool LoadPreviewImages = true;
readonly Cache<string, MapPreview> previews;
readonly ModData modData;
readonly Manifest manifest;
readonly FS modFiles;
Cache<string, MapPreview> previews;
readonly SheetBuilder sheetBuilder;
Thread previewLoaderThread;
bool previewLoaderThreadShutDown = true;
@@ -67,13 +69,11 @@ namespace OpenRA
return null;
}
public MapCache(ModData modData)
public MapCache(Manifest manifest, FS modFiles)
{
this.modData = modData;
var gridType = Exts.Lazy(() => modData.GetOrCreate<MapGrid>().Type);
previews = new Cache<string, MapPreview>(uid => new MapPreview(modData, uid, gridType.Value, this));
sheetBuilder = new SheetBuilder(SheetType.BGRA, modData.Manifest.RendererConstants.MapPreviewSheetSize);
this.manifest = manifest;
this.modFiles = modFiles;
sheetBuilder = new SheetBuilder(SheetType.BGRA, manifest.RendererConstants.MapPreviewSheetSize);
}
public void UpdateMaps()
@@ -82,14 +82,17 @@ namespace OpenRA
tracker.UpdateMaps(this);
}
public void LoadMaps()
public void LoadMaps(ModData modData)
{
// Utility mod that does not support maps
if (modData.Manifest.MapFolders.Count == 0)
if (manifest.MapFolders.Count == 0)
return;
var gridType = modData.GetOrCreate<MapGrid>().Type;
previews = new Cache<string, MapPreview>(uid => new MapPreview(modData, uid, gridType, this));
// Enumerate map directories
foreach (var kv in modData.Manifest.MapFolders)
foreach (var kv in manifest.MapFolders)
{
var name = kv.Key;
var classification = string.IsNullOrEmpty(kv.Value)
@@ -108,7 +111,7 @@ namespace OpenRA
if (resolved.StartsWith(Platform.SupportDir, StringComparison.Ordinal) && !File.Exists(resolved))
Directory.CreateDirectory(resolved);
package = modData.ModFiles.OpenPackage(name);
package = modFiles.OpenPackage(name);
}
catch
{
@@ -124,12 +127,9 @@ namespace OpenRA
// PERF: Load the mod YAML once outside the loop, and reuse it when resolving each maps custom YAML.
var modDataRules = modData.GetRulesYaml();
var gridType = modData.GetOrCreate<MapGrid>().Type;
foreach (var kv in MapLocations)
{
foreach (var map in kv.Key.Contents)
LoadMapInternal(map, kv.Key, kv.Value, null, gridType, modDataRules);
}
// We only want to track maps in runtime, not at loadtime
LastModifiedMap = null;
@@ -148,7 +148,7 @@ namespace OpenRA
{
using (new PerfTimer(map))
{
mapPackage = package.OpenPackage(map, modData.ModFiles);
mapPackage = package.OpenPackage(map, modFiles);
if (mapPackage != null)
{
var uid = Map.ComputeUID(mapPackage);
@@ -179,7 +179,7 @@ namespace OpenRA
public IEnumerable<IReadWritePackage> EnumerateMapDirPackages(MapClassification classification = MapClassification.System)
{
// Enumerate map directories
foreach (var kv in modData.Manifest.MapFolders)
foreach (var kv in manifest.MapFolders)
{
if (!Enum.TryParse(kv.Value, out MapClassification packageClassification))
continue;
@@ -197,7 +197,7 @@ namespace OpenRA
if (resolved.StartsWith(Platform.SupportDir, StringComparison.Ordinal) && (!Directory.Exists(resolved) || !File.Exists(resolved)))
continue;
using (var package = (IReadWritePackage)modData.ModFiles.OpenPackage(name))
using (var package = (IReadWritePackage)modFiles.OpenPackage(name))
yield return package;
}
}
@@ -217,11 +217,11 @@ namespace OpenRA
foreach (var mapDirPackage in mapDirPackages)
foreach (var map in mapDirPackage.Contents)
if (mapDirPackage.OpenPackage(map, modData.ModFiles) is IReadWritePackage mapPackage)
if (mapDirPackage.OpenPackage(map, modFiles) is IReadWritePackage mapPackage)
yield return mapPackage;
}
public void GenerateMap(MapGenerationArgs args)
public void GenerateMap(ModData modData, MapGenerationArgs args)
{
var p = previews[args.Uid];
if (p.Class == MapClassification.Generated)

View File

@@ -35,7 +35,6 @@ namespace OpenRA
public readonly IPackageLoader[] PackageLoaders;
public readonly ISoundLoader[] SoundLoaders;
public readonly ISpriteLoader[] SpriteLoaders;
public readonly ITerrainLoader TerrainLoader;
public readonly ISpriteSequenceLoader SpriteSequenceLoader;
public readonly IVideoLoader[] VideoLoaders;
public readonly HotkeyManager Hotkeys;
@@ -92,41 +91,38 @@ namespace OpenRA
modules.Add(module);
}
FluentProvider.Initialize(this, DefaultFileSystem);
FluentProvider.Initialize(Manifest, DefaultFileSystem);
if (useLoadScreen)
{
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen.Value);
LoadScreen.Init(this, Manifest.LoadScreen.ToDictionary(my => my.Value));
LoadScreen.Init(Manifest, DefaultFileSystem);
LoadScreen.Display();
}
WidgetLoader = new WidgetLoader(this);
MapCache = new MapCache(this);
WidgetLoader = new WidgetLoader(Manifest, DefaultFileSystem);
MapCache = new MapCache(Manifest, ModFiles);
SoundLoaders = ObjectCreator.GetLoaders<ISoundLoader>(Manifest.SoundFormats, "sound");
SpriteLoaders = ObjectCreator.GetLoaders<ISpriteLoader>(Manifest.SpriteFormats, "sprite");
VideoLoaders = ObjectCreator.GetLoaders<IVideoLoader>(Manifest.VideoFormats, "video");
SpriteSequenceLoader = ObjectCreator.GetLoader<ISpriteSequenceLoader>(Manifest.SpriteSequenceFormat, "sequence");
var terrainLoader = ObjectCreator.FindType(Manifest.TerrainFormat + "Loader");
var terrainCtor = terrainLoader?.GetConstructor([typeof(ModData)]);
if (terrainLoader == null || !terrainLoader.GetInterfaces().Contains(typeof(ITerrainLoader)) || terrainCtor == null)
throw new InvalidOperationException($"Unable to find a terrain loader for type '{Manifest.TerrainFormat}'.");
TerrainLoader = (ITerrainLoader)terrainCtor.Invoke([this]);
Hotkeys = new HotkeyManager(ModFiles, Game.Settings.Keys, Manifest);
Cursors = ParseCursors(Manifest, DefaultFileSystem);
defaultRules = Exts.Lazy(() => Ruleset.LoadDefaults(this));
defaultTerrainInfo = Exts.Lazy(() =>
{
var items = new Dictionary<string, ITerrainInfo>();
var terrainType = ObjectCreator.FindType(Manifest.TerrainFormat + "Loader");
var terrainCtor = terrainType?.GetConstructor([typeof(ModData)]);
if (terrainType == null || !terrainType.GetInterfaces().Contains(typeof(ITerrainLoader)) || terrainCtor == null)
throw new InvalidOperationException($"Unable to find a terrain loader for type '{Manifest.TerrainFormat}'.");
var items = new Dictionary<string, ITerrainInfo>();
var terrainLoader = (ITerrainLoader)terrainCtor.Invoke([this]);
foreach (var file in Manifest.TileSets)
{
var t = TerrainLoader.ParseTerrain(DefaultFileSystem, file);
var t = terrainLoader.ParseTerrain(DefaultFileSystem, file);
items.Add(t.Id, t);
}
@@ -169,7 +165,8 @@ namespace OpenRA
// horribly when you use ModData in unexpected ways.
ChromeMetrics.Initialize(this);
ChromeProvider.Initialize(this);
FluentProvider.Initialize(this, fileSystem);
Ui.Initialize(this);
FluentProvider.Initialize(Manifest, fileSystem);
Game.Sound.Initialize(SoundLoaders, fileSystem);
}
@@ -234,7 +231,7 @@ namespace OpenRA
public interface ILoadScreen : IDisposable
{
/// <summary>Initializes the loadscreen with yaml data from the LoadScreen block in mod.yaml.</summary>
void Init(ModData m, Dictionary<string, string> info);
void Init(Manifest manifest, IReadOnlyFileSystem fileSystem);
/// <summary>Called at arbitrary times during mod load to rerender the loadscreen.</summary>
void Display();
@@ -243,7 +240,7 @@ namespace OpenRA
/// Called before loading the mod assets.
/// Returns false if mod loading should be aborted (e.g. switching to another mod instead).
/// </summary>
bool BeforeLoad();
bool BeforeLoad(ModData modData);
/// <summary>Called when the engine expects to connect to a server/replay or load the shellmap.</summary>
void StartGame(Arguments args);

View File

@@ -393,7 +393,7 @@ namespace OpenRA.Network
case "GenerateMap":
{
var yaml = new MiniYaml(order.OrderString, MiniYaml.FromString(order.TargetString, order.OrderString));
Game.ModData.MapCache.GenerateMap(FieldLoader.Load<MapGenerationArgs>(yaml));
Game.ModData.MapCache.GenerateMap(Game.ModData, FieldLoader.Load<MapGenerationArgs>(yaml));
break;
}

View File

@@ -1136,7 +1136,7 @@ namespace OpenRA.Server
var args = FieldLoader.Load<MapGenerationArgs>(yaml);
var preview = ModData.MapCache[args.Uid];
if (preview.Status != MapStatus.Available)
ModData.MapCache.GenerateMap(args);
ModData.MapCache.GenerateMap(ModData, args);
GeneratedMapData = o.TargetString;
DispatchServerOrdersToClients(Order.FromTargetString("GenerateMap", o.TargetString, true));

View File

@@ -36,6 +36,12 @@ namespace OpenRA.Widgets
public static Widget MouseOverWidget;
static readonly Mediator Mediator = new();
static ModData modData;
public static void Initialize(ModData modData)
{
Ui.modData = modData;
}
public static void CloseWindow()
{
@@ -66,6 +72,9 @@ namespace OpenRA.Widgets
public static Widget OpenWindow(string id, WidgetArgs args)
{
if (!args.ContainsKey("modData"))
args = new WidgetArgs(args) { { "modData", modData } };
var window = Game.ModData.WidgetLoader.LoadWidget(args, Root, id);
if (WindowList.Count > 0)
Root.HideChild(WindowList.Peek());
@@ -88,6 +97,9 @@ namespace OpenRA.Widgets
public static Widget LoadWidget(string id, Widget parent, WidgetArgs args)
{
if (!args.ContainsKey("modData"))
args = new WidgetArgs(args) { { "modData", modData } };
return Game.ModData.WidgetLoader.LoadWidget(args, parent, id);
}

View File

@@ -12,6 +12,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Widgets;
namespace OpenRA
@@ -19,15 +20,12 @@ namespace OpenRA
public class WidgetLoader
{
readonly Dictionary<string, MiniYamlNode> widgets = [];
readonly ModData modData;
public WidgetLoader(ModData modData)
public WidgetLoader(Manifest manifest, IReadOnlyFileSystem fileSystem)
{
this.modData = modData;
var stringPool = new HashSet<string>(); // Reuse common strings in YAML
foreach (var file in modData.Manifest.ChromeLayout.Select(
a => MiniYaml.FromStream(modData.DefaultFileSystem.Open(a), a, stringPool: stringPool)))
foreach (var file in manifest.ChromeLayout.Select(
a => MiniYaml.FromStream(fileSystem.Open(a), a, stringPool: stringPool)))
foreach (var w in file)
{
var key = w.Key[(w.Key.IndexOf('@') + 1)..];
@@ -47,9 +45,6 @@ namespace OpenRA
public Widget LoadWidget(WidgetArgs args, Widget parent, MiniYamlNode node)
{
if (!args.ContainsKey("modData"))
args = new WidgetArgs(args) { { "modData", modData } };
var widget = NewWidget(node.Key, args);
parent?.AddChild(widget);

View File

@@ -9,7 +9,7 @@
*/
#endregion
using System.Collections.Generic;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Mods.Common.LoadScreens;
using OpenRA.Mods.Common.Widgets;
@@ -36,11 +36,11 @@ namespace OpenRA.Mods.Cnc
string message = "";
public override void Init(ModData modData, Dictionary<string, string> info)
public override void Init(Manifest manifest, IReadOnlyFileSystem fileSystem)
{
base.Init(modData, info);
base.Init(manifest, fileSystem);
versionText = modData.Manifest.Metadata.Version;
versionText = manifest.Metadata.Version;
message = FluentProvider.GetMessage(Loading);
}

View File

@@ -10,9 +10,9 @@
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using OpenRA.FileFormats;
using OpenRA.FileSystem;
using OpenRA.Mods.Common.FileSystem;
using OpenRA.Mods.Common.Widgets.Logic;
using OpenRA.Widgets;
@@ -22,13 +22,12 @@ namespace OpenRA.Mods.Common.LoadScreens
public class BlankLoadScreen : ILoadScreen
{
public LaunchArguments Launch;
protected ModData ModData { get; private set; }
protected IReadOnlyFileSystem fileSystem;
bool initialized;
public virtual void Init(ModData modData, Dictionary<string, string> info)
public virtual void Init(Manifest manifest, IReadOnlyFileSystem fileSystem)
{
ModData = modData;
this.fileSystem = fileSystem;
}
public virtual void Display()
@@ -108,12 +107,12 @@ namespace OpenRA.Mods.Common.LoadScreens
GC.SuppressFinalize(this);
}
public virtual bool BeforeLoad()
public virtual bool BeforeLoad(ModData modData)
{
var graphicSettings = Game.Settings.Graphics;
// Reset the UI scaling if the user has configured a UI scale that pushes us below the minimum allowed effective resolution
var minResolution = ModData.GetOrCreate<WorldViewportSizes>().MinEffectiveResolution;
var minResolution = modData.GetOrCreate<WorldViewportSizes>().MinEffectiveResolution;
var resolution = Game.Renderer.Resolution;
if ((resolution.Width < minResolution.Width || resolution.Height < minResolution.Height) && Game.Settings.Graphics.UIScale > 1.0f)
{
@@ -126,10 +125,10 @@ namespace OpenRA.Mods.Common.LoadScreens
if (graphicSettings.GLProfile != GLProfile.Automatic && graphicSettings.GLProfile != Game.Renderer.GLProfile)
graphicSettings.GLProfile = GLProfile.Automatic;
if (ModData.FileSystemLoader is not IFileSystemExternalContent content)
if (modData.FileSystemLoader is not IFileSystemExternalContent content)
return true;
return !content.InstallContentIfRequired(ModData);
return !content.InstallContentIfRequired(modData);
}
}
}

View File

@@ -9,8 +9,8 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Widgets;
using OpenRA.Primitives;
@@ -32,9 +32,9 @@ namespace OpenRA.Mods.Common.LoadScreens
string[] messages = [];
public override void Init(ModData modData, Dictionary<string, string> info)
public override void Init(Manifest manifest, IReadOnlyFileSystem fileSystem)
{
base.Init(modData, info);
base.Init(manifest, fileSystem);
messages = FluentProvider.GetMessage(Loading).Split(',').Select(x => x.Trim()).ToArray();
}

View File

@@ -48,7 +48,7 @@ namespace OpenRA.Mods.Common.LoadScreens
Ui.LoadWidget("MODCONTENT_BACKGROUND", Ui.Root, []);
}
public override bool BeforeLoad()
public override bool BeforeLoad(ModData modData)
{
return true;
}

View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Primitives;
@@ -26,10 +27,10 @@ namespace OpenRA.Mods.Common.LoadScreens
Sheet sheet;
int density;
public override void Init(ModData modData, Dictionary<string, string> info)
public override void Init(Manifest manifest, IReadOnlyFileSystem fileSystem)
{
base.Init(modData, info);
Info = info;
base.Init(manifest, fileSystem);
Info = manifest.LoadScreen.ToDictionary(my => my.Value);
}
public abstract void DisplayInner(Renderer r, Sheet s, int density);
@@ -70,7 +71,7 @@ namespace OpenRA.Mods.Common.LoadScreens
density = 2;
}
using (var stream = ModData.DefaultFileSystem.Open(Platform.ResolvePath(image)))
using (var stream = fileSystem.Open(Platform.ResolvePath(image)))
{
sheet = new Sheet(SheetType.BGRA, stream);
sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;

View File

@@ -54,7 +54,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
// HACK: The engine code assumes that Game.modData is set.
// HACK: We know that maps can only be oramap or folders, which are ReadWrite
var modData = Game.ModData = utility.ModData;
modData.MapCache.LoadMaps();
modData.MapCache.LoadMaps(modData);
foreach (var kv in modData.MapCache.MapLocations)
{
foreach (var mapFilename in kv.Key.Contents)

View File

@@ -94,7 +94,7 @@ namespace OpenRA.Server
// HACK: The engine code *still* assumes that Game.ModData is set
var modData = Game.ModData = new ModData(mods[modID], mods);
modData.MapCache.LoadPreviewImages = false; // PERF: Server doesn't need previews, save memory by not loading them.
modData.MapCache.LoadMaps();
modData.MapCache.LoadMaps(modData);
var endpoints = new List<IPEndPoint> { new(IPAddress.IPv6Any, settings.ListenPort), new(IPAddress.Any, settings.ListenPort) };
var server = new Server(endpoints, settings, modData, ServerType.Dedicated);