Move GlobalModData state to ModData.

This commit is contained in:
Paul Chote
2025-12-09 15:43:03 +00:00
committed by Gustas Kažukauskas
parent 13313b4270
commit a04cfaec78
44 changed files with 135 additions and 192 deletions

View File

@@ -500,7 +500,7 @@ namespace OpenRA
ModData = new ModData(manifest, Mods, true);
LocalPlayerProfile = new LocalPlayerProfile(Path.Combine(Platform.SupportDir, Settings.Game.AuthProfile), ModData.Manifest.Get<PlayerDatabase>());
LocalPlayerProfile = new LocalPlayerProfile(Path.Combine(Platform.SupportDir, Settings.Game.AuthProfile), ModData.GetOrCreate<PlayerDatabase>());
if (!ModData.LoadScreen.BeforeLoad())
return;

View File

@@ -149,7 +149,7 @@ namespace OpenRA.Graphics
{
worldRenderer = wr;
tileSize = map.Rules.TerrainInfo.TileSize;
viewportSizes = Game.ModData.Manifest.Get<WorldViewportSizes>();
viewportSizes = Game.ModData.GetOrCreate<WorldViewportSizes>();
graphicSettings = Game.Settings.Graphics;
defaultScale = viewportSizes.DefaultScale;

View File

@@ -56,7 +56,7 @@ namespace OpenRA.Graphics
createPaletteReference = CreatePaletteReference;
var mapGrid = modData.Manifest.Get<MapGrid>();
var mapGrid = modData.GetOrCreate<MapGrid>();
enableDepthBuffer = mapGrid.EnableDepthBuffer;
foreach (var pal in world.TraitDict.ActorsWithTrait<ILoadsPalettes>())

View File

@@ -9,42 +9,15 @@
*/
#endregion
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA
{
public interface IGlobalModData { }
public sealed class TerrainFormat : IGlobalModData
{
public readonly string Type;
public readonly IReadOnlyDictionary<string, MiniYaml> Metadata;
public TerrainFormat(MiniYaml yaml)
{
Type = yaml.Value;
Metadata = new ReadOnlyDictionary<string, MiniYaml>(yaml.ToDictionary());
}
}
public sealed class SpriteSequenceFormat : IGlobalModData
{
public readonly string Type;
public readonly IReadOnlyDictionary<string, MiniYaml> Metadata;
public SpriteSequenceFormat(MiniYaml yaml)
{
Type = yaml.Value;
Metadata = new ReadOnlyDictionary<string, MiniYaml>(yaml.ToDictionary());
}
}
public class ModMetadata
{
// FieldLoader used here, must matching naming in YAML.
@@ -75,7 +48,7 @@ namespace OpenRA
}
/// <summary>Describes what is to be loaded in order to run a mod.</summary>
public sealed class Manifest : IDisposable
public sealed class Manifest
{
public readonly string Id;
public readonly IReadOnlyPackage Package;
@@ -111,10 +84,7 @@ namespace OpenRA
"RequiresMods", "PackageFormats", "AllowUnusedFluentMessagesInExternalPackages", "RendererConstants"
}.ToFrozenSet();
readonly TypeDictionary modules = [];
readonly Dictionary<string, MiniYaml> yaml;
bool customDataLoaded;
public readonly FrozenDictionary<string, MiniYaml> GlobalModData;
public Manifest(string modId, IReadOnlyPackage package)
{
@@ -139,7 +109,7 @@ namespace OpenRA
}
// Merge inherited overrides
yaml = new MiniYaml(null, MiniYaml.Merge([nodes])).ToDictionary();
var yaml = new MiniYaml(null, MiniYaml.Merge([nodes])).ToDictionary();
Metadata = FieldLoader.Load<ModMetadata>(yaml["Metadata"]);
@@ -204,37 +174,9 @@ namespace OpenRA
RendererConstants = FieldLoader.Load<RendererConstants>(entry);
else
RendererConstants = new RendererConstants();
}
public void LoadCustomData(ObjectCreator oc)
{
foreach (var kv in yaml)
{
if (ReservedModuleNames.Contains(kv.Key))
continue;
var t = oc.FindType(kv.Key);
if (t == null || !typeof(IGlobalModData).IsAssignableFrom(t))
throw new InvalidDataException($"`{kv.Key}` is not a valid mod manifest entry.");
IGlobalModData module;
var ctor = t.GetConstructor([typeof(MiniYaml)]);
if (ctor != null)
{
// Class has opted-in to DIY initialization
module = (IGlobalModData)ctor.Invoke([kv.Value]);
}
else
{
// Automatically load the child nodes using FieldLoader
module = oc.CreateObject<IGlobalModData>(kv.Key);
FieldLoader.Load(module, kv.Value);
}
modules.Add(module);
}
customDataLoaded = true;
GlobalModData = yaml.Where(n => !ReservedModuleNames.Contains(n.Key))
.ToFrozenDictionary(n => n.Key, n => n.Value);
}
static ImmutableArray<string> YamlList(Dictionary<string, MiniYaml> yaml, string key)
@@ -252,67 +194,5 @@ namespace OpenRA
return value.ToDictionary(my => my.Value).ToFrozenDictionary();
}
public bool Contains<T>() where T : IGlobalModData
{
return modules.Contains<T>();
}
/// <summary>Load a cached IGlobalModData instance.</summary>
public T Get<T>() where T : IGlobalModData
{
if (!customDataLoaded)
throw new InvalidOperationException("Attempted to call Manifest.Get() before loading custom data!");
var module = modules.GetOrDefault<T>();
// Lazily create the default values if not explicitly defined.
if (module == null)
{
module = (T)Game.ModData.ObjectCreator.CreateBasic(typeof(T));
modules.Add(module);
}
return module;
}
/// <summary>
/// Load an uncached IGlobalModData instance directly from the manifest yaml.
/// This should only be used by external mods that want to query data from this mod.
/// </summary>
public T Get<T>(ObjectCreator oc) where T : IGlobalModData
{
var t = typeof(T);
if (!yaml.TryGetValue(t.Name, out var data))
{
// Lazily create the default values if not explicitly defined.
return (T)oc.CreateBasic(t);
}
IGlobalModData module;
var ctor = t.GetConstructor([typeof(MiniYaml)]);
if (ctor != null)
{
// Class has opted-in to DIY initialization
module = (IGlobalModData)ctor.Invoke([data.Value]);
}
else
{
// Automatically load the child nodes using FieldLoader
module = oc.CreateObject<IGlobalModData>(t.Name);
FieldLoader.Load(module, data);
}
return (T)module;
}
public void Dispose()
{
foreach (var module in modules)
{
var disposableModule = module as IDisposable;
disposableModule?.Dispose();
}
}
}
}

View File

@@ -325,7 +325,7 @@ namespace OpenRA
{
this.modData = modData;
MapSize = size;
Grid = modData.Manifest.Get<MapGrid>();
Grid = modData.GetOrCreate<MapGrid>();
Title = "Name your map here";
Author = "Your name here";
@@ -370,7 +370,7 @@ namespace OpenRA
PlayerDefinitions = yaml.NodeWithKeyOrDefault("Players")?.Value.Nodes ?? [];
ActorDefinitions = yaml.NodeWithKeyOrDefault("Actors")?.Value.Nodes ?? [];
Grid = modData.Manifest.Get<MapGrid>();
Grid = modData.GetOrCreate<MapGrid>();
Tiles = new CellLayer<TerrainTile>(Grid.Type, MapSize);
Resources = new CellLayer<ResourceTile>(Grid.Type, MapSize);

View File

@@ -71,7 +71,7 @@ namespace OpenRA
{
this.modData = modData;
var gridType = Exts.Lazy(() => modData.Manifest.Get<MapGrid>().Type);
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);
}
@@ -85,7 +85,7 @@ namespace OpenRA
public void LoadMaps()
{
// Utility mod that does not support maps
if (!modData.Manifest.Contains<MapGrid>())
if (modData.Manifest.MapFolders.Count == 0)
return;
// Enumerate map directories
@@ -124,7 +124,7 @@ 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.Manifest.Get<MapGrid>().Type;
var gridType = modData.GetOrCreate<MapGrid>().Type;
foreach (var kv in MapLocations)
{
foreach (var map in kv.Key.Contents)
@@ -178,10 +178,6 @@ namespace OpenRA
public IEnumerable<IReadWritePackage> EnumerateMapDirPackages(MapClassification classification = MapClassification.System)
{
// Utility mod that does not support maps
if (!modData.Manifest.Contains<MapGrid>())
yield break;
// Enumerate map directories
foreach (var kv in modData.Manifest.MapFolders)
{

View File

@@ -384,7 +384,7 @@ namespace OpenRA
var newData = innerData.Clone();
newData.Class = classification;
newData.GridType = gridType ?? modData.Manifest.Get<MapGrid>().Type;
newData.GridType = gridType ?? modData.GetOrCreate<MapGrid>().Type;
if (yaml.TryGetValue("MapFormat", out var temp))
{

View File

@@ -31,7 +31,7 @@ namespace OpenRA
public string Faction;
public bool LockColor = false;
public Color Color = Game.ModData.Manifest.Get<DefaultPlayer>().Color;
public Color Color = Game.ModData.GetOrCreate<DefaultPlayer>().Color;
/// <summary>
/// Sets the "Home" location, which can be used by traits and scripts to e.g. set the initial camera

View File

@@ -13,15 +13,41 @@ using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Video;
using OpenRA.Widgets;
using FS = OpenRA.FileSystem.FileSystem;
namespace OpenRA
{
public interface IGlobalModData { }
public sealed class TerrainFormat : IGlobalModData
{
public readonly string Type;
public readonly IReadOnlyDictionary<string, MiniYaml> Metadata;
public TerrainFormat(MiniYaml yaml)
{
Type = yaml.Value;
Metadata = new ReadOnlyDictionary<string, MiniYaml>(yaml.ToDictionary());
}
}
public sealed class SpriteSequenceFormat : IGlobalModData
{
public readonly string Type;
public readonly IReadOnlyDictionary<string, MiniYaml> Metadata;
public SpriteSequenceFormat(MiniYaml yaml)
{
Type = yaml.Value;
Metadata = new ReadOnlyDictionary<string, MiniYaml>(yaml.ToDictionary());
}
}
public sealed class ModData : IDisposable
{
public readonly Manifest Manifest;
@@ -48,6 +74,8 @@ namespace OpenRA
readonly Lazy<IReadOnlyDictionary<string, ITerrainInfo>> defaultTerrainInfo;
public IReadOnlyDictionary<string, ITerrainInfo> DefaultTerrainInfo => defaultTerrainInfo.Value;
readonly TypeDictionary modules = [];
public ModData(Manifest mod, InstalledMods mods, bool useLoadScreen = false)
{
Languages = [];
@@ -63,7 +91,28 @@ namespace OpenRA
FileSystemLoader.Mount(ModFiles, ObjectCreator);
ModFiles.TrimExcess();
Manifest.LoadCustomData(ObjectCreator);
foreach (var kv in Manifest.GlobalModData)
{
var t = ObjectCreator.FindType(kv.Key);
if (t == null || !typeof(IGlobalModData).IsAssignableFrom(t))
throw new InvalidDataException($"`{kv.Key}` is not a valid mod manifest entry.");
IGlobalModData module;
var ctor = t.GetConstructor([typeof(MiniYaml)]);
if (ctor != null)
{
// Class has opted-in to DIY initialization
module = (IGlobalModData)ctor.Invoke([kv.Value]);
}
else
{
// Automatically load the child nodes using FieldLoader
module = ObjectCreator.CreateObject<IGlobalModData>(kv.Key);
FieldLoader.Load(module, kv.Value);
}
modules.Add(module);
}
FluentProvider.Initialize(this, DefaultFileSystem);
@@ -81,7 +130,7 @@ namespace OpenRA
SpriteLoaders = ObjectCreator.GetLoaders<ISpriteLoader>(Manifest.SpriteFormats, "sprite");
VideoLoaders = ObjectCreator.GetLoaders<IVideoLoader>(Manifest.VideoFormats, "video");
var terrainFormat = Manifest.Get<TerrainFormat>();
var terrainFormat = GetOrCreate<TerrainFormat>();
var terrainLoader = ObjectCreator.FindType(terrainFormat.Type + "Loader");
var terrainCtor = terrainLoader?.GetConstructor([typeof(ModData)]);
if (terrainLoader == null || !terrainLoader.GetInterfaces().Contains(typeof(ITerrainLoader)) || terrainCtor == null)
@@ -89,7 +138,7 @@ namespace OpenRA
TerrainLoader = (ITerrainLoader)terrainCtor.Invoke([this]);
var sequenceFormat = Manifest.Get<SpriteSequenceFormat>();
var sequenceFormat = GetOrCreate<SpriteSequenceFormat>();
var sequenceLoader = ObjectCreator.FindType(sequenceFormat.Type + "Loader");
var sequenceCtor = sequenceLoader?.GetConstructor([typeof(ModData)]);
if (sequenceLoader == null || !sequenceLoader.GetInterfaces().Contains(typeof(ISpriteSequenceLoader)) || sequenceCtor == null)
@@ -177,6 +226,26 @@ namespace OpenRA
return Manifest.Rules.Select(s => MiniYaml.FromStream(DefaultFileSystem.Open(s), s, stringPool: stringPool).ToArray()).ToArray();
}
/// <summary>Load a cached IGlobalModData instance.</summary>
public T GetOrCreate<T>() where T : IGlobalModData
{
var module = modules.GetOrDefault<T>();
// Lazily create the default values if not explicitly defined.
if (module == null)
{
module = (T)ObjectCreator.CreateBasic(typeof(T));
modules.Add(module);
}
return module;
}
public T GetOrNull<T>() where T : IGlobalModData
{
return modules.GetOrDefault<T>();
}
public void Dispose()
{
LoadScreen?.Dispose();
@@ -184,7 +253,11 @@ namespace OpenRA
ObjectCreator?.Dispose();
Manifest.Dispose();
foreach (var module in modules)
{
var disposableModule = module as IDisposable;
disposableModule?.Dispose();
}
}
}

View File

@@ -87,7 +87,7 @@ namespace OpenRA.Network
}
}
var gameSpeeds = Game.ModData.Manifest.Get<GameSpeeds>();
var gameSpeeds = Game.ModData.GetOrCreate<GameSpeeds>();
var gameSpeedName = LobbyInfo.GlobalSettings.OptionOrDefault("gamespeed", gameSpeeds.DefaultSpeed);
orderLatency = gameSpeeds.Speeds[gameSpeedName].OrderLatency;
}

View File

@@ -33,7 +33,7 @@ namespace OpenRA
var badgesNode = yaml.NodeWithKeyOrDefault("Badges");
if (badgesNode != null)
{
var playerDatabase = Game.ModData.Manifest.Get<PlayerDatabase>();
var playerDatabase = Game.ModData.GetOrCreate<PlayerDatabase>();
foreach (var badgeNode in badgesNode.Value.Nodes)
{
Game.RunAfterTick(() =>

View File

@@ -132,7 +132,7 @@ namespace OpenRA
{
fontSheetBuilder?.Dispose();
fontSheetBuilder = new SheetBuilder(SheetType.BGRA, modData.Manifest.RendererConstants.FontSheetSize);
Fonts = modData.Manifest.Get<Fonts>().FontList.ToDictionary(x => x.Key,
Fonts = modData.GetOrCreate<Fonts>().FontList.ToDictionary(x => x.Key,
x => new SpriteFont(
platform, x.Value.Font, modData.DefaultFileSystem.Open(x.Value.Font).ReadAllBytes(),
x.Value.Size, x.Value.Ascender, Window.EffectiveWindowScale, fontSheetBuilder));

View File

@@ -304,7 +304,7 @@ namespace OpenRA.Server
ModData = modData;
playerDatabase = modData.Manifest.Get<PlayerDatabase>();
playerDatabase = modData.GetOrCreate<PlayerDatabase>();
randomSeed = (int)DateTime.Now.ToBinary();
@@ -1376,7 +1376,7 @@ namespace OpenRA.Server
SyncLobbyInfo();
var gameSpeeds = Game.ModData.Manifest.Get<GameSpeeds>();
var gameSpeeds = Game.ModData.GetOrCreate<GameSpeeds>();
var gameSpeedName = LobbyInfo.GlobalSettings.OptionOrDefault("gamespeed", gameSpeeds.DefaultSpeed);
var gameSpeed = gameSpeeds.Speeds[gameSpeedName];

View File

@@ -205,7 +205,7 @@ namespace OpenRA
orderGenerator = (IOrderGenerator)modData.ObjectCreator.CreateBasic(defaultOrderGeneratorType);
var gameSpeeds = modData.Manifest.Get<GameSpeeds>();
var gameSpeeds = modData.GetOrCreate<GameSpeeds>();
var gameSpeedName = orderManager.LobbyInfo.GlobalSettings.OptionOrDefault("gamespeed", gameSpeeds.DefaultSpeed);
GameSpeed = gameSpeeds.Speeds[gameSpeedName];
Timestep = ReplayTimestep = GameSpeed.Timestep;

View File

@@ -47,10 +47,7 @@ namespace OpenRA.Mods.Common
if (!Game.Settings.Game.EnableDiscordService)
return null;
if (!Game.ModData.Manifest.Contains<DiscordService>())
return null;
instance = Game.ModData.Manifest.Get<DiscordService>();
instance = Game.ModData.GetOrNull<DiscordService>();
return instance;
}
}

View File

@@ -199,16 +199,13 @@ namespace OpenRA.Mods.Common.Lint
ExtractConstFluentKeys(modData, traitType, keys);
}
var hasModule = modData.Manifest.GetType().GetMethod(nameof(Manifest.Contains));
var getModule = modData.Manifest.GetType().GetMethod(nameof(Manifest.Get), []);
var getModule = modData.GetType().GetMethod(nameof(ModData.GetOrNull), []);
var globalModData = modData.ObjectCreator.GetTypesImplementing<IGlobalModData>()
.Where(t => (bool)hasModule.MakeGenericMethod(t).Invoke(modData.Manifest, []));
.Select(t => getModule?.MakeGenericMethod(t).Invoke(modData, []))
.Where(x => x != null);
foreach (var module in globalModData)
{
var value = getModule.MakeGenericMethod(module).Invoke(modData.Manifest, []);
ExtractFluentKeys(modData, value, "mod.yaml", keys);
}
ExtractFluentKeys(modData, module, "mod.yaml", keys);
// Load screen
var loadScreenType = modData.ObjectCreator.FindType(modData.Manifest.LoadScreen.Value);

View File

@@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.Lint
// As the map has not been created we need to get MapGrid info directly from manifest.
var tileSize = defaultTerrainInfo.Values.First().TileSize;
var tileScale = modData.Manifest.Get<MapGrid>().TileScale;
var tileScale = modData.GetOrCreate<MapGrid>().TileScale;
foreach (var actorInfo in rules.Actors)
{
// Catch TypeDictionary errors.

View File

@@ -113,7 +113,7 @@ namespace OpenRA.Mods.Common.LoadScreens
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.Manifest.Get<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)
{

View File

@@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Scripting
: base(context)
{
tlm = context.World.WorldActor.TraitOrDefault<TimeLimitManager>();
var gameSpeeds = Game.ModData.Manifest.Get<GameSpeeds>();
var gameSpeeds = Game.ModData.GetOrCreate<GameSpeeds>();
var defaultGameSpeed = gameSpeeds.Speeds[gameSpeeds.DefaultSpeed];
ticksPerSecond = 1000 / defaultGameSpeed.Timestep;
}

View File

@@ -686,7 +686,7 @@ namespace OpenRA.Mods.Common.Server
else if (server.Settings.QueryMapRepository)
{
server.SendFluentMessageTo(conn, SearchingMap);
var mapRepository = server.ModData.Manifest.Get<WebServices>().MapRepository;
var mapRepository = server.ModData.GetOrCreate<WebServices>().MapRepository;
var reported = false;
server.ModData.MapCache.QueryRemoteMapDetails(mapRepository, [s], SelectMap, _ =>
{
@@ -1319,7 +1319,7 @@ namespace OpenRA.Mods.Common.Server
// Query any missing maps and wait up to 10 seconds for a response
// Maps that have not resolved will not be valid for the initial map choice
var mapRepository = server.ModData.Manifest.Get<WebServices>().MapRepository;
var mapRepository = server.ModData.GetOrCreate<WebServices>().MapRepository;
mapCache.QueryRemoteMapDetails(mapRepository, unknownMaps);
var searchingMaps = server.MapPool.Where(uid => mapCache[uid].Status == MapStatus.Searching);

View File

@@ -148,7 +148,7 @@ namespace OpenRA.Mods.Common.Server
{
try
{
var endpoint = server.ModData.Manifest.Get<WebServices>().ServerAdvertise;
var endpoint = server.ModData.GetOrCreate<WebServices>().ServerAdvertise;
var client = HttpClientFactory.Create();
var response = await client.PostAsync(endpoint, new StringContent(postData));

View File

@@ -61,7 +61,7 @@ namespace OpenRA.Mods.Common.Traits
public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
var grid = Game.ModData.Manifest.Get<MapGrid>();
var grid = Game.ModData.GetOrCreate<MapGrid>();
if (grid.Type != MapGridType.RectangularIsometric)
throw new YamlException($"{nameof(IsometricSelectable)} can only be used in mods that use the {nameof(MapGridType.RectangularIsometric)} MapGrid type.");

View File

@@ -34,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
if (!Game.ModData.Manifest.Get<Fonts>().FontList.ContainsKey(Font))
if (!Game.ModData.GetOrCreate<Fonts>().FontList.ContainsKey(Font))
throw new YamlException($"Font '{Font}' is not listed in the mod.yaml's Fonts section");
base.RulesetLoaded(rules, ai);

View File

@@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.Traits.Render
void IRulesetLoaded<ActorInfo>.RulesetLoaded(Ruleset rules, ActorInfo info)
{
if (!Game.ModData.Manifest.Get<Fonts>().FontList.ContainsKey(Font))
if (!Game.ModData.GetOrCreate<Fonts>().FontList.ContainsKey(Font))
throw new YamlException($"Font '{Font}' is not listed in the mod.yaml's Fonts section");
}

View File

@@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
if (!Game.ModData.Manifest.Get<Fonts>().FontList.ContainsKey(Font))
if (!Game.ModData.GetOrCreate<Fonts>().FontList.ContainsKey(Font))
throw new YamlException($"Font '{Font}' is not listed in the mod.yaml's Fonts section");
base.RulesetLoaded(rules, ai);

View File

@@ -97,7 +97,7 @@ namespace OpenRA.Mods.Common.Traits
TechLevelDropdownLabel, TechLevelDropdownDescription, TechLevelDropdownVisible, TechLevelDropdownDisplayOrder,
techLevels, TechLevel, TechLevelDropdownLocked);
var gameSpeeds = Game.ModData.Manifest.Get<GameSpeeds>();
var gameSpeeds = Game.ModData.GetOrCreate<GameSpeeds>();
var speeds = gameSpeeds.Speeds.ToDictionary(s => s.Key, s => FluentProvider.GetMessage(s.Value.Name));
// NOTE: This is just exposing the UI, the backend logic for this option is hardcoded in World.
@@ -109,7 +109,7 @@ namespace OpenRA.Mods.Common.Traits
void IRulesetLoaded<ActorInfo>.RulesetLoaded(Ruleset rules, ActorInfo info)
{
var gameSpeeds = Game.ModData.Manifest.Get<GameSpeeds>().Speeds;
var gameSpeeds = Game.ModData.GetOrCreate<GameSpeeds>().Speeds;
if (GameSpeed != null && !gameSpeeds.ContainsKey(GameSpeed))
throw new YamlException($"Invalid default game speed '{GameSpeed}'.");
}

View File

@@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public IEnumerable<string> BeforeUpdateActors(ModData modData, List<MiniYamlNodeBuilder> resolvedActors)
{
grid = modData.Manifest.Get<MapGrid>();
grid = modData.GetOrCreate<MapGrid>();
var harvesters = new Dictionary<string, HashSet<string>>();
var refineries = new List<string>();
foreach (var actorNode in resolvedActors)

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.Widgets
[ObjectCreator.UseCtor]
public ActorPreviewWidget(ModData modData, WorldRenderer worldRenderer)
{
viewportSizes = modData.Manifest.Get<WorldViewportSizes>();
viewportSizes = modData.GetOrCreate<WorldViewportSizes>();
this.worldRenderer = worldRenderer;
}

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.Widgets
[ObjectCreator.UseCtor]
public BadgeWidget(ModData modData)
{
playerDatabase = modData.Manifest.Get<PlayerDatabase>();
playerDatabase = modData.GetOrCreate<PlayerDatabase>();
}
protected BadgeWidget(BadgeWidget other)

View File

@@ -395,7 +395,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
panel.GetOrNull<LabelWidget>("MODEL_SCALE").IsVisible = () => currentVoxel != null;
}
var assetBrowserModData = modData.Manifest.Get<AssetBrowser>();
var assetBrowserModData = modData.GetOrCreate<AssetBrowser>();
allowedSpriteExtensions = assetBrowserModData.SpriteExtensions.Select(x => x.ToLowerInvariant()).ToArray();
allowedModelExtensions = assetBrowserModData.ModelExtensions.Select(x => x.ToLowerInvariant()).ToArray();
allowedAudioExtensions = assetBrowserModData.AudioExtensions.Select(x => x.ToLowerInvariant()).ToArray();

View File

@@ -37,7 +37,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
onExit();
};
var modCredits = modData.Manifest.Get<ModCredits>();
var modCredits = modData.GetOrCreate<ModCredits>();
var tabContainer = panel.Get("TAB_CONTAINER");
if (modCredits.ModCreditsFile != null)

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
[ObjectCreator.UseCtor]
public ModContentLogic(ModData modData)
{
var content = modData.Manifest.Get<ModContent>();
var content = modData.GetOrCreate<ModContent>();
var mod = Game.Mods[content.Mod];
if (!IsModInstalled(content))
{

View File

@@ -51,7 +51,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
nameTextfield.IsDisabled = () => worldRenderer.World.Type != WorldType.Shellmap;
nameTextfield.Text = Settings.SanitizedPlayerName(ps.Name);
var itchIntegration = modData.Manifest.Get<ItchIntegration>();
var itchIntegration = modData.GetOrCreate<ItchIntegration>();
itchIntegration.GetPlayerName(name => nameTextfield.Text = Settings.SanitizedPlayerName(name));
nameTextfield.OnLoseFocus = () =>
@@ -127,7 +127,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
});
colorDropdown.Get<ColorBlockWidget>("COLORBLOCK").GetColor = () => ps.Color;
var viewportSizes = modData.Manifest.Get<WorldViewportSizes>();
var viewportSizes = modData.GetOrCreate<WorldViewportSizes>();
var battlefieldCameraDropDown = widget.Get<DropDownButtonWidget>("BATTLEFIELD_CAMERA_DROPDOWN");
var battlefieldCameraLabel = new CachedTransform<WorldViewport, string>(vs => DisplaySettingsLogic.GetViewportSizeName(modData, vs));
battlefieldCameraDropDown.OnMouseDown = _ => DisplaySettingsLogic.ShowBattlefieldCameraDropdown(modData, battlefieldCameraDropDown, viewportSizes, ds);

View File

@@ -165,7 +165,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
// TODO: This needs to be reworked to support per-map tech levels, bots, etc.
modRules = modData.DefaultRules;
services = modData.Manifest.Get<WebServices>();
services = modData.GetOrCreate<WebServices>();
Game.LobbyInfoChanged += UpdateCurrentMap;
Game.LobbyInfoChanged += UpdatePlayerList;

View File

@@ -127,7 +127,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return parent;
}
var mapRepository = modData.Manifest.Get<WebServices>().MapRepository;
var mapRepository = modData.GetOrCreate<WebServices>().MapRepository;
Widget SetUpInstallButton(Widget parent)
{

View File

@@ -233,7 +233,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Game.OnRemoteDirectConnect += OnRemoteDirectConnect;
// Check for updates in the background
var webServices = modData.Manifest.Get<WebServices>();
var webServices = modData.GetOrCreate<WebServices>();
if (Game.Settings.Debug.CheckVersion)
webServices.CheckModVersion();

View File

@@ -279,7 +279,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
// SetupMapTab (through RefreshMap) depends on the map search having already started
if (remoteMapPool != null && Game.Settings.Game.AllowDownloading)
{
var services = modData.Manifest.Get<WebServices>();
var services = modData.GetOrCreate<WebServices>();
modData.MapCache.QueryRemoteMapDetails(services.MapRepository, remoteMapPool);
}

View File

@@ -246,7 +246,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
void RandomizeSize()
{
var mapGrid = modData.Manifest.Get<MapGrid>();
var mapGrid = modData.GetOrCreate<MapGrid>();
var sizeRange = MapSizes[selectedSize];
var width = Game.CosmeticRandom.Next(sizeRange.X, sizeRange.Y);
var height =

View File

@@ -34,7 +34,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
[ObjectCreator.UseCtor]
public RegisteredProfileTooltipLogic(Widget widget, WorldRenderer worldRenderer, ModData modData, Session.Client client)
{
playerDatabase = modData.Manifest.Get<PlayerDatabase>();
playerDatabase = modData.GetOrCreate<PlayerDatabase>();
var header = widget.Get("HEADER");
var badgeContainer = widget.Get("BADGES_CONTAINER");

View File

@@ -128,7 +128,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
map = MapCache.UnknownMap;
panel = widget;
services = modData.Manifest.Get<WebServices>();
services = modData.GetOrCreate<WebServices>();
this.modData = modData;
this.onStart = onStart;
Game.BeforeGameStart += OnGameStart;

View File

@@ -178,7 +178,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
serverShuttingDown = FluentProvider.GetMessage(ServerShuttingDown);
unknownServerState = FluentProvider.GetMessage(UnknownServerState);
services = modData.Manifest.Get<WebServices>();
services = modData.GetOrCreate<WebServices>();
incompatibleVersionColor = ChromeMetrics.Get<Color>("IncompatibleVersionColor");
incompatibleGameColor = ChromeMetrics.Get<Color>("IncompatibleGameColor");

View File

@@ -104,7 +104,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
this.worldRenderer = worldRenderer;
this.modData = modData;
viewportSizes = modData.Manifest.Get<WorldViewportSizes>();
viewportSizes = modData.GetOrCreate<WorldViewportSizes>();
legacyFullscreen = FluentProvider.GetMessage(LegacyFullscreen);
fullscreen = FluentProvider.GetMessage(Fullscreen);
@@ -567,7 +567,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return item;
}
var viewportSizes = Game.ModData.Manifest.Get<WorldViewportSizes>();
var viewportSizes = Game.ModData.GetOrCreate<WorldViewportSizes>();
var maxScales = new float2(Game.Renderer.NativeResolution) / new float2(viewportSizes.MinEffectiveResolution);
var maxScale = Math.Min(maxScales.X, maxScales.Y);

View File

@@ -59,7 +59,7 @@ namespace OpenRA.Mods.Common.Widgets
public ResourcePreviewWidget(ModData modData, WorldRenderer worldRenderer, World world)
{
this.worldRenderer = worldRenderer;
viewportSizes = modData.Manifest.Get<WorldViewportSizes>();
viewportSizes = modData.GetOrCreate<WorldViewportSizes>();
resourceRenderers = world.WorldActor.TraitsImplementing<IResourceRenderer>().ToArray();
tileSize = world.Map.Rules.TerrainInfo.TileSize;
}

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.Widgets
public TerrainTemplatePreviewWidget(ModData modData, WorldRenderer worldRenderer, World world)
{
this.worldRenderer = worldRenderer;
viewportSizes = modData.Manifest.Get<WorldViewportSizes>();
viewportSizes = modData.GetOrCreate<WorldViewportSizes>();
terrainRenderer = world.WorldActor.TraitOrDefault<ITiledTerrainRenderer>();
if (terrainRenderer == null)