Change classes that use FieldLoader to use read-only collections.

This commit is contained in:
RoosterDragon
2025-11-08 12:40:56 +00:00
committed by Paul Chote
parent 797c71e500
commit 649e7e8c28
308 changed files with 1233 additions and 901 deletions

View File

@@ -12,6 +12,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using OpenRA.FileFormats; using OpenRA.FileFormats;
@@ -28,7 +29,7 @@ namespace OpenRA
public readonly string Id; public readonly string Id;
public readonly string Version; public readonly string Version;
public readonly string LaunchPath; public readonly string LaunchPath;
public readonly string[] LaunchArgs; public readonly ImmutableArray<string> LaunchArgs;
public Sprite Icon { get; internal set; } public Sprite Icon { get; internal set; }
public Sprite Icon2x { get; internal set; } public Sprite Icon2x { get; internal set; }
public Sprite Icon3x { get; internal set; } public Sprite Icon3x { get; internal set; }

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -78,7 +79,7 @@ namespace OpenRA
return Math.Sign((v1.X - v0.X) * (p.Y - v0.Y) - (p.X - v0.X) * (v1.Y - v0.Y)); return Math.Sign((v1.X - v0.X) * (p.Y - v0.Y) - (p.X - v0.X) * (v1.Y - v0.Y));
} }
public static bool PolygonContains(this int2[] polygon, int2 p) public static bool PolygonContains(this ImmutableArray<int2> polygon, int2 p)
{ {
var windingNumber = 0; var windingNumber = 0;

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using Linguini.Bundle; using Linguini.Bundle;
@@ -87,19 +88,19 @@ namespace OpenRA
{ {
readonly Linguini.Bundle.FluentBundle bundle; readonly Linguini.Bundle.FluentBundle bundle;
public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem) public FluentBundle(string culture, ImmutableArray<string> paths, IReadOnlyFileSystem fileSystem)
: this(culture, paths, fileSystem, error => Log.Write("debug", error.Message)) { } : this(culture, paths, fileSystem, error => Log.Write("debug", error.Message)) { }
public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem, string text) public FluentBundle(string culture, ImmutableArray<string> paths, IReadOnlyFileSystem fileSystem, string text)
: this(culture, paths, fileSystem, text, error => Log.Write("debug", error.Message)) { } : this(culture, paths, fileSystem, text, error => Log.Write("debug", error.Message)) { }
public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem, Action<ParseError> onError) public FluentBundle(string culture, ImmutableArray<string> paths, IReadOnlyFileSystem fileSystem, Action<ParseError> onError)
: this(culture, paths, fileSystem, null, onError) { } : this(culture, paths, fileSystem, null, onError) { }
public FluentBundle(string culture, string text, Action<ParseError> onError) public FluentBundle(string culture, string text, Action<ParseError> onError)
: this(culture, null, null, text, onError) { } : this(culture, default, null, text, onError) { }
public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem, string text, Action<ParseError> onError) public FluentBundle(string culture, ImmutableArray<string> paths, IReadOnlyFileSystem fileSystem, string text, Action<ParseError> onError)
{ {
bundle = LinguiniBuilder.Builder() bundle = LinguiniBuilder.Builder()
.CultureInfo(new CultureInfo(culture)) .CultureInfo(new CultureInfo(culture))

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Immutable;
using System.Text; using System.Text;
using OpenRA.FileSystem; using OpenRA.FileSystem;
@@ -29,9 +30,9 @@ namespace OpenRA
modFluentBundle = new FluentBundle(modData.Manifest.FluentCulture, modData.Manifest.FluentMessages, fileSystem); modFluentBundle = new FluentBundle(modData.Manifest.FluentCulture, modData.Manifest.FluentMessages, fileSystem);
if (fileSystem is Map map && map.FluentMessageDefinitions != null) if (fileSystem is Map map && map.FluentMessageDefinitions != null)
{ {
var files = Array.Empty<string>(); var files = ImmutableArray<string>.Empty;
if (map.FluentMessageDefinitions.Value != null) if (map.FluentMessageDefinitions.Value != null)
files = FieldLoader.GetValue<string[]>("value", map.FluentMessageDefinitions.Value); files = FieldLoader.GetValue<ImmutableArray<string>>("value", map.FluentMessageDefinitions.Value);
string text = null; string text = null;
if (map.FluentMessageDefinitions.Nodes.Length > 0) if (map.FluentMessageDefinitions.Nodes.Length > 0)

View File

@@ -9,6 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
namespace OpenRA namespace OpenRA
@@ -23,15 +24,15 @@ namespace OpenRA
public class Fonts : IGlobalModData public class Fonts : IGlobalModData
{ {
[FieldLoader.LoadUsing(nameof(LoadFonts))] [FieldLoader.LoadUsing(nameof(LoadFonts))]
public readonly Dictionary<string, FontData> FontList; public readonly FrozenDictionary<string, FontData> FontList;
static object LoadFonts(MiniYaml y) static object LoadFonts(MiniYaml y)
{ {
var ret = new Dictionary<string, FontData>(); var ret = new Dictionary<string, FontData>(y.Nodes.Length);
foreach (var node in y.Nodes) foreach (var node in y.Nodes)
ret.Add(node.Key, FieldLoader.Load<FontData>(node.Value)); ret.Add(node.Key, FieldLoader.Load<FontData>(node.Value));
return ret; return ret.ToFrozenDictionary();
} }
} }
} }

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@@ -415,7 +416,7 @@ namespace OpenRA
var modSearchArg = args.GetValue("Engine.ModSearchPaths", null); var modSearchArg = args.GetValue("Engine.ModSearchPaths", null);
var modSearchPaths = modSearchArg != null ? var modSearchPaths = modSearchArg != null ?
FieldLoader.GetValue<string[]>("Engine.ModsPath", modSearchArg) : FieldLoader.GetValue<ImmutableArray<string>>("Engine.ModsPath", modSearchArg) :
[Path.Combine(Platform.EngineDir, "mods")]; [Path.Combine(Platform.EngineDir, "mods")];
Mods = new InstalledMods(modSearchPaths, explicitModPaths); Mods = new InstalledMods(modSearchPaths, explicitModPaths);

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.FileSystem; using OpenRA.FileSystem;
@@ -41,7 +42,7 @@ namespace OpenRA
public TimeSpan Duration => EndTimeUtc > StartTimeUtc ? EndTimeUtc - StartTimeUtc : TimeSpan.Zero; public TimeSpan Duration => EndTimeUtc > StartTimeUtc ? EndTimeUtc - StartTimeUtc : TimeSpan.Zero;
public IList<Player> Players { get; } public IList<Player> Players { get; }
public HashSet<int> DisabledSpawnPoints = []; public FrozenSet<int> DisabledSpawnPoints = FrozenSet<int>.Empty;
public MapPreview MapPreview public MapPreview MapPreview
{ {

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using OpenRA.FileSystem; using OpenRA.FileSystem;
@@ -268,7 +269,7 @@ namespace OpenRA
if (mapRules.Value != null) if (mapRules.Value != null)
{ {
var mapFiles = FieldLoader.GetValue<string[]>("value", mapRules.Value); var mapFiles = FieldLoader.GetValue<ImmutableArray<string>>("value", mapRules.Value);
foreach (var f in mapFiles) foreach (var f in mapFiles)
if (AnyFlaggedTraits(modData, MiniYaml.FromStream(fileSystem.Open(f), f))) if (AnyFlaggedTraits(modData, MiniYaml.FromStream(fileSystem.Open(f), f)))
return true; return true;

View File

@@ -10,37 +10,38 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Collections.Immutable;
namespace OpenRA.GameRules namespace OpenRA.GameRules
{ {
public class SoundInfo public class SoundInfo
{ {
public readonly Dictionary<string, string[]> Variants = []; public readonly FrozenDictionary<string, ImmutableArray<string>> Variants = FrozenDictionary<string, ImmutableArray<string>>.Empty;
public readonly Dictionary<string, string[]> Prefixes = []; public readonly FrozenDictionary<string, ImmutableArray<string>> Prefixes = FrozenDictionary<string, ImmutableArray<string>>.Empty;
public readonly Dictionary<string, string[]> Voices = []; public readonly FrozenDictionary<string, ImmutableArray<string>> Voices = FrozenDictionary<string, ImmutableArray<string>>.Empty;
public readonly Dictionary<string, string[]> Notifications = []; public readonly FrozenDictionary<string, ImmutableArray<string>> Notifications = FrozenDictionary<string, ImmutableArray<string>>.Empty;
public readonly string DefaultVariant = ".aud"; public readonly string DefaultVariant = ".aud";
public readonly string DefaultPrefix = ""; public readonly string DefaultPrefix = "";
public readonly HashSet<string> DisableVariants = []; public readonly FrozenSet<string> DisableVariants = FrozenSet<string>.Empty;
public readonly HashSet<string> DisablePrefixes = []; public readonly FrozenSet<string> DisablePrefixes = FrozenSet<string>.Empty;
public readonly Lazy<Dictionary<string, SoundPool>> VoicePools; public readonly Lazy<FrozenDictionary<string, SoundPool>> VoicePools;
public readonly Lazy<Dictionary<string, SoundPool>> NotificationsPools; public readonly Lazy<FrozenDictionary<string, SoundPool>> NotificationsPools;
public SoundInfo(MiniYaml y) public SoundInfo(MiniYaml y)
{ {
FieldLoader.Load(this, y); FieldLoader.Load(this, y);
VoicePools = Exts.Lazy(() => Voices.ToDictionary(a => a.Key, a => new SoundPool(1f, SoundPool.DefaultInterruptType, a.Value))); VoicePools = Exts.Lazy(() => Voices.ToFrozenDictionary(a => a.Key, a => new SoundPool(1f, SoundPool.DefaultInterruptType, a.Value)));
NotificationsPools = Exts.Lazy(() => ParseSoundPool(y, "Notifications")); NotificationsPools = Exts.Lazy(() => ParseSoundPool(y, "Notifications"));
} }
static Dictionary<string, SoundPool> ParseSoundPool(MiniYaml y, string key) static FrozenDictionary<string, SoundPool> ParseSoundPool(MiniYaml y, string key)
{ {
var ret = new Dictionary<string, SoundPool>();
var classifiction = y.NodeWithKey(key); var classifiction = y.NodeWithKey(key);
var ret = new Dictionary<string, SoundPool>(classifiction.Value.Nodes.Length);
foreach (var t in classifiction.Value.Nodes) foreach (var t in classifiction.Value.Nodes)
{ {
var volumeModifier = 1f; var volumeModifier = 1f;
@@ -53,12 +54,12 @@ namespace OpenRA.GameRules
if (interruptTypeNode != null) if (interruptTypeNode != null)
interruptType = FieldLoader.GetValue<SoundPool.InterruptType>(interruptTypeNode.Key, interruptTypeNode.Value.Value); interruptType = FieldLoader.GetValue<SoundPool.InterruptType>(interruptTypeNode.Key, interruptTypeNode.Value.Value);
var names = FieldLoader.GetValue<string[]>(t.Key, t.Value.Value); var names = FieldLoader.GetValue<ImmutableArray<string>>(t.Key, t.Value.Value);
var sp = new SoundPool(volumeModifier, interruptType, names); var sp = new SoundPool(volumeModifier, interruptType, names);
ret.Add(t.Key, sp); ret.Add(t.Key, sp);
} }
return ret; return ret.ToFrozenDictionary();
} }
} }
@@ -68,10 +69,10 @@ namespace OpenRA.GameRules
public const InterruptType DefaultInterruptType = InterruptType.DoNotPlay; public const InterruptType DefaultInterruptType = InterruptType.DoNotPlay;
public readonly float VolumeModifier; public readonly float VolumeModifier;
public readonly InterruptType Type; public readonly InterruptType Type;
readonly string[] clips; readonly ImmutableArray<string> clips;
readonly List<string> liveclips = []; readonly List<string> liveclips = [];
public SoundPool(float volumeModifier, InterruptType interruptType, params string[] clips) public SoundPool(float volumeModifier, InterruptType interruptType, ImmutableArray<string> clips)
{ {
VolumeModifier = volumeModifier; VolumeModifier = volumeModifier;
Type = interruptType; Type = interruptType;

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Effects; using OpenRA.Effects;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -82,13 +83,13 @@ namespace OpenRA.GameRules
public readonly WVec FollowingBurstTargetOffset = WVec.Zero; public readonly WVec FollowingBurstTargetOffset = WVec.Zero;
[Desc("The sound played each time the weapon is fired.")] [Desc("The sound played each time the weapon is fired.")]
public readonly string[] Report = null; public readonly ImmutableArray<string> Report = default;
[Desc("Sound played only on first burst in a salvo.")] [Desc("Sound played only on first burst in a salvo.")]
public readonly string[] StartBurstReport = null; public readonly ImmutableArray<string> StartBurstReport = default;
[Desc("The sound played when the weapon is reloaded.")] [Desc("The sound played when the weapon is reloaded.")]
public readonly string[] AfterFireSound = null; public readonly ImmutableArray<string> AfterFireSound = default;
[Desc("Delay in ticks to play reloading sound.")] [Desc("Delay in ticks to play reloading sound.")]
public readonly int AfterFireSoundDelay = 0; public readonly int AfterFireSoundDelay = 0;
@@ -116,7 +117,7 @@ namespace OpenRA.GameRules
[Desc("Delay in ticks between firing shots from the same ammo magazine. If one entry, it will be used for all bursts.", [Desc("Delay in ticks between firing shots from the same ammo magazine. If one entry, it will be used for all bursts.",
"If multiple entries, their number needs to match Burst - 1.")] "If multiple entries, their number needs to match Burst - 1.")]
public readonly int[] BurstDelays = [5]; public readonly ImmutableArray<int> BurstDelays = [5];
[Desc("The minimum range the weapon can fire.")] [Desc("The minimum range the weapon can fire.")]
public readonly WDist MinRange = WDist.Zero; public readonly WDist MinRange = WDist.Zero;
@@ -128,7 +129,7 @@ namespace OpenRA.GameRules
public readonly IProjectileInfo Projectile; public readonly IProjectileInfo Projectile;
[FieldLoader.LoadUsing(nameof(LoadWarheads))] [FieldLoader.LoadUsing(nameof(LoadWarheads))]
public readonly List<IWarhead> Warheads = []; public readonly ImmutableArray<IWarhead> Warheads = [];
/// <summary> /// <summary>
/// This constructor is used solely for documentation generation. /// This constructor is used solely for documentation generation.
@@ -170,7 +171,7 @@ namespace OpenRA.GameRules
retList.Add(ret); retList.Add(ret);
} }
return retList; return retList.ToImmutableArray();
} }
public bool IsValidTarget(BitSet<TargetableType> targetTypes) public bool IsValidTarget(BitSet<TargetableType> targetTypes)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Primitives; using OpenRA.Primitives;
using OpenRA.Support; using OpenRA.Support;
@@ -250,7 +251,7 @@ namespace OpenRA.Graphics
return sequences.GetSequence(Name, sequenceName); return sequences.GetSequence(Name, sequenceName);
} }
public string GetRandomExistingSequence(string[] sequences, MersenneTwister random) public string GetRandomExistingSequence(ImmutableArray<string> sequences, MersenneTwister random)
{ {
return sequences.Where(HasSequence).RandomOrDefault(random); return sequences.Where(HasSequence).RandomOrDefault(random);
} }

View File

@@ -10,7 +10,9 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.FileSystem; using OpenRA.FileSystem;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -47,9 +49,9 @@ namespace OpenRA.Graphics
public readonly string Image2x = null; public readonly string Image2x = null;
public readonly string Image3x = null; public readonly string Image3x = null;
public readonly int[] PanelRegion = null; public readonly ImmutableArray<int> PanelRegion = default;
public readonly PanelSides PanelSides = PanelSides.All; public readonly PanelSides PanelSides = PanelSides.All;
public readonly Dictionary<string, Rectangle> Regions = []; public readonly FrozenDictionary<string, Rectangle> Regions = FrozenDictionary<string, Rectangle>.Empty;
} }
public static IReadOnlyDictionary<string, Collection> Collections => collections; public static IReadOnlyDictionary<string, Collection> Collections => collections;

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -64,18 +65,18 @@ namespace OpenRA.Graphics
Buffer.BlockCopy(colors, 0, destination, destinationOffset * 4, Palette.Size * 4); Buffer.BlockCopy(colors, 0, destination, destinationOffset * 4, Palette.Size * 4);
} }
public ImmutablePalette(string filename, int[] remapTransparent, int[] remap) public ImmutablePalette(string filename, ImmutableArray<int> remapTransparent, ImmutableArray<int> remap)
{ {
using (var s = File.OpenRead(filename)) using (var s = File.OpenRead(filename))
LoadFromStream(s, remapTransparent, remap); LoadFromStream(s, remapTransparent, remap);
} }
public ImmutablePalette(Stream s, int[] remapTransparent, int[] remapShadow) public ImmutablePalette(Stream s, ImmutableArray<int> remapTransparent, ImmutableArray<int> remapShadow)
{ {
LoadFromStream(s, remapTransparent, remapShadow); LoadFromStream(s, remapTransparent, remapShadow);
} }
void LoadFromStream(Stream s, int[] remapTransparent, int[] remapShadow) void LoadFromStream(Stream s, ImmutableArray<int> remapTransparent, ImmutableArray<int> remapShadow)
{ {
using (var reader = new BinaryReader(s)) using (var reader = new BinaryReader(s))
for (var i = 0; i < Palette.Size; i++) for (var i = 0; i < Palette.Size; i++)

View File

@@ -135,7 +135,7 @@ namespace OpenRA
void SetVec(string name, float x); void SetVec(string name, float x);
void SetVec(string name, float x, float y); void SetVec(string name, float x, float y);
void SetVec(string name, float x, float y, float z); void SetVec(string name, float x, float y, float z);
void SetVec(string name, float[] vec, int length); void SetVec(string name, ReadOnlyMemory<float> vec, int length);
void SetTexture(string param, ITexture texture); void SetTexture(string param, ITexture texture);
void SetMatrix(string param, float[] mtx); void SetMatrix(string param, float[] mtx);
void PrepareRender(); void PrepareRender();

View File

@@ -10,19 +10,19 @@
#endregion #endregion
using System; using System;
using System.Linq; using System.Collections.Immutable;
using OpenRA.Primitives; using OpenRA.Primitives;
namespace OpenRA.Graphics namespace OpenRA.Graphics
{ {
public class PlayerColorRemap : IPaletteRemap public class PlayerColorRemap : IPaletteRemap
{ {
readonly int[] remapIndices; readonly ImmutableArray<int> remapIndices;
readonly float hue; readonly float hue;
readonly float saturation; readonly float saturation;
readonly float value; readonly float value;
public PlayerColorRemap(int[] remapIndices, Color color) public PlayerColorRemap(ImmutableArray<int> remapIndices, Color color)
{ {
this.remapIndices = remapIndices; this.remapIndices = remapIndices;

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using OpenRA.FileSystem; using OpenRA.FileSystem;
@@ -27,7 +28,7 @@ namespace OpenRA.Graphics
readonly Dictionary< readonly Dictionary<
int, int,
(int[] Frames, MiniYamlNode.SourceLocation Location, AdjustFrame AdjustFrame, bool Premultiplied)> spriteReservations = []; (ImmutableArray<int> Frames, MiniYamlNode.SourceLocation Location, AdjustFrame AdjustFrame, bool Premultiplied)> spriteReservations = [];
readonly Dictionary<string, List<int>> reservationsByFilename = []; readonly Dictionary<string, List<int>> reservationsByFilename = [];
readonly Dictionary<int, Sprite[]> resolvedSprites = []; readonly Dictionary<int, Sprite[]> resolvedSprites = [];
@@ -49,11 +50,11 @@ namespace OpenRA.Graphics
this.loaders = loaders; this.loaders = loaders;
} }
public int ReserveSprites(string filename, IEnumerable<int> frames, MiniYamlNode.SourceLocation location, public int ReserveSprites(string filename, ImmutableArray<int> frames, MiniYamlNode.SourceLocation location,
AdjustFrame adjustFrame = null, bool premultiplied = false) AdjustFrame adjustFrame = null, bool premultiplied = false)
{ {
var token = nextReservationToken++; var token = nextReservationToken++;
spriteReservations[token] = (frames?.ToArray(), location, adjustFrame, premultiplied); spriteReservations[token] = (frames, location, adjustFrame, premultiplied);
reservationsByFilename.GetOrAdd(filename, _ => []).Add(token); reservationsByFilename.GetOrAdd(filename, _ => []).Add(token);
return token; return token;
} }
@@ -103,8 +104,8 @@ namespace OpenRA.Graphics
throw new InvalidOperationException($"{rs.Location}: {filename} does not contain frames: " + throw new InvalidOperationException($"{rs.Location}: {filename} does not contain frames: " +
string.Join(',', rs.Frames.Where(f => f >= loadedFrames.Length))); string.Join(',', rs.Frames.Where(f => f >= loadedFrames.Length)));
var frames = rs.Frames ?? Enumerable.Range(0, loadedFrames.Length); var frames = rs.Frames != null ? rs.Frames : Enumerable.Range(0, loadedFrames.Length);
var total = rs.Frames?.Length ?? loadedFrames.Length; var total = rs.Frames != null ? rs.Frames.Length : loadedFrames.Length;
var j = 0; var j = 0;
foreach (var i in frames) foreach (var i in frames)

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -268,7 +269,7 @@ namespace OpenRA.Graphics
{ {
var ramp = map.Grid.Ramps[map.Ramp.Contains(uv) ? map.Ramp[uv] : 0]; var ramp = map.Grid.Ramps[map.Ramp.Contains(uv) ? map.Ramp[uv] : 0];
var pos = map.CenterOfCell(uv.ToCPos(map)) - new WVec(0, 0, ramp.CenterHeightOffset); var pos = map.CenterOfCell(uv.ToCPos(map)) - new WVec(0, 0, ramp.CenterHeightOffset);
var screen = ramp.Corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray(); var screen = ramp.Corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToImmutableArray();
if (screen.PolygonContains(world)) if (screen.PolygonContains(world))
return uv.ToCPos(map); return uv.ToCPos(map);
} }

View File

@@ -9,7 +9,8 @@
*/ */
#endregion #endregion
using System.Collections.Generic; using System.Collections.Frozen;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
namespace OpenRA namespace OpenRA
@@ -24,10 +25,10 @@ namespace OpenRA
[FluentReference] [FluentReference]
public readonly string Description = ""; public readonly string Description = "";
public readonly HashSet<string> Types = []; public readonly FrozenSet<string> Types = FrozenSet<string>.Empty;
[FluentReference] [FluentReference]
public readonly HashSet<string> Contexts = []; public readonly FrozenSet<string> Contexts = FrozenSet<string>.Empty;
public readonly bool Readonly = false; public readonly bool Readonly = false;
public bool HasDuplicates { get; internal set; } public bool HasDuplicates { get; internal set; }
@@ -45,11 +46,11 @@ namespace OpenRA
Description = descriptionYaml.Value; Description = descriptionYaml.Value;
if (nodeDict.TryGetValue("Types", out var typesYaml)) if (nodeDict.TryGetValue("Types", out var typesYaml))
Types = FieldLoader.GetValue<HashSet<string>>("Types", typesYaml.Value); Types = FieldLoader.GetValue<FrozenSet<string>>("Types", typesYaml.Value);
if (nodeDict.TryGetValue("Contexts", out var contextYaml)) if (nodeDict.TryGetValue("Contexts", out var contextYaml))
Contexts = FieldLoader.GetValue<HashSet<string>>("Contexts", contextYaml.Value) Contexts = FieldLoader.GetValue<ImmutableArray<string>>("Contexts", contextYaml.Value)
.Select(c => ContextFluentPrefix + "." + c).ToHashSet(); .Select(c => ContextFluentPrefix + "." + c).ToFrozenSet();
if (nodeDict.TryGetValue("Platform", out var platformYaml)) if (nodeDict.TryGetValue("Platform", out var platformYaml))
{ {

View File

@@ -10,7 +10,9 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -68,22 +70,22 @@ namespace OpenRA
public readonly string Id; public readonly string Id;
public readonly IReadOnlyPackage Package; public readonly IReadOnlyPackage Package;
public readonly ModMetadata Metadata; public readonly ModMetadata Metadata;
public readonly string[] public readonly ImmutableArray<string>
Rules, ServerTraits, Rules, ServerTraits,
Sequences, ModelSequences, Cursors, Chrome, ChromeLayout, Sequences, ModelSequences, Cursors, Chrome, ChromeLayout,
Weapons, Voices, Notifications, Music, FluentMessages, TileSets, Weapons, Voices, Notifications, Music, FluentMessages, TileSets,
ChromeMetrics, MapCompatibility, Missions, Hotkeys; ChromeMetrics, MapCompatibility, Missions, Hotkeys;
public readonly IReadOnlyDictionary<string, string> MapFolders; public readonly FrozenDictionary<string, string> MapFolders;
public readonly MiniYaml FileSystem; public readonly MiniYaml FileSystem;
public readonly MiniYaml LoadScreen; public readonly MiniYaml LoadScreen;
public readonly string DefaultOrderGenerator; public readonly string DefaultOrderGenerator;
public readonly string[] Assemblies = []; public readonly ImmutableArray<string> Assemblies = [];
public readonly string[] SoundFormats = []; public readonly ImmutableArray<string> SoundFormats = [];
public readonly string[] SpriteFormats = []; public readonly ImmutableArray<string> SpriteFormats = [];
public readonly string[] PackageFormats = []; public readonly ImmutableArray<string> PackageFormats = [];
public readonly string[] VideoFormats = []; public readonly ImmutableArray<string> VideoFormats = [];
public readonly int FontSheetSize = 512; public readonly int FontSheetSize = 512;
public readonly int CursorSheetSize = 512; public readonly int CursorSheetSize = 512;
@@ -91,14 +93,14 @@ namespace OpenRA
public readonly string FluentCulture = "en"; public readonly string FluentCulture = "en";
public readonly bool AllowUnusedFluentMessagesInExternalPackages = true; public readonly bool AllowUnusedFluentMessagesInExternalPackages = true;
readonly string[] reservedModuleNames = static readonly FrozenSet<string> ReservedModuleNames = new HashSet<string>
[ {
"Include", "Metadata", "FileSystem", "MapFolders", "Rules", "Include", "Metadata", "FileSystem", "MapFolders", "Rules",
"Sequences", "ModelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons", "Sequences", "ModelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons",
"Voices", "Notifications", "Music", "FluentMessages", "TileSets", "ChromeMetrics", "Missions", "Hotkeys", "Voices", "Notifications", "Music", "FluentMessages", "TileSets", "ChromeMetrics", "Missions", "Hotkeys",
"ServerTraits", "LoadScreen", "DefaultOrderGenerator", "SupportsMapsFrom", "SoundFormats", "SpriteFormats", "VideoFormats", "ServerTraits", "LoadScreen", "DefaultOrderGenerator", "SupportsMapsFrom", "SoundFormats", "SpriteFormats", "VideoFormats",
"RequiresMods", "PackageFormats", "AllowUnusedFluentMessagesInExternalPackages", "FontSheetSize", "CursorSheetSize" "RequiresMods", "PackageFormats", "AllowUnusedFluentMessagesInExternalPackages", "FontSheetSize", "CursorSheetSize"
]; }.ToFrozenSet();
readonly TypeDictionary modules = []; readonly TypeDictionary modules = [];
readonly Dictionary<string, MiniYaml> yaml; readonly Dictionary<string, MiniYaml> yaml;
@@ -165,25 +167,25 @@ namespace OpenRA
if (yaml.TryGetValue("SupportsMapsFrom", out var entry)) if (yaml.TryGetValue("SupportsMapsFrom", out var entry))
compat.AddRange(entry.Value.Split(',').Select(c => c.Trim())); compat.AddRange(entry.Value.Split(',').Select(c => c.Trim()));
MapCompatibility = compat.ToArray(); MapCompatibility = compat.ToImmutableArray();
if (yaml.TryGetValue("DefaultOrderGenerator", out entry)) if (yaml.TryGetValue("DefaultOrderGenerator", out entry))
DefaultOrderGenerator = entry.Value; DefaultOrderGenerator = entry.Value;
if (yaml.TryGetValue("Assemblies", out entry)) if (yaml.TryGetValue("Assemblies", out entry))
Assemblies = FieldLoader.GetValue<string[]>("Assemblies", entry.Value); Assemblies = FieldLoader.GetValue<ImmutableArray<string>>("Assemblies", entry.Value);
if (yaml.TryGetValue("PackageFormats", out entry)) if (yaml.TryGetValue("PackageFormats", out entry))
PackageFormats = FieldLoader.GetValue<string[]>("PackageFormats", entry.Value); PackageFormats = FieldLoader.GetValue<ImmutableArray<string>>("PackageFormats", entry.Value);
if (yaml.TryGetValue("SoundFormats", out entry)) if (yaml.TryGetValue("SoundFormats", out entry))
SoundFormats = FieldLoader.GetValue<string[]>("SoundFormats", entry.Value); SoundFormats = FieldLoader.GetValue<ImmutableArray<string>>("SoundFormats", entry.Value);
if (yaml.TryGetValue("SpriteFormats", out entry)) if (yaml.TryGetValue("SpriteFormats", out entry))
SpriteFormats = FieldLoader.GetValue<string[]>("SpriteFormats", entry.Value); SpriteFormats = FieldLoader.GetValue<ImmutableArray<string>>("SpriteFormats", entry.Value);
if (yaml.TryGetValue("VideoFormats", out entry)) if (yaml.TryGetValue("VideoFormats", out entry))
VideoFormats = FieldLoader.GetValue<string[]>("VideoFormats", entry.Value); VideoFormats = FieldLoader.GetValue<ImmutableArray<string>>("VideoFormats", entry.Value);
if (yaml.TryGetValue("AllowUnusedFluentMessagesInExternalPackages", out entry)) if (yaml.TryGetValue("AllowUnusedFluentMessagesInExternalPackages", out entry))
AllowUnusedFluentMessagesInExternalPackages = AllowUnusedFluentMessagesInExternalPackages =
@@ -200,7 +202,7 @@ namespace OpenRA
{ {
foreach (var kv in yaml) foreach (var kv in yaml)
{ {
if (reservedModuleNames.Contains(kv.Key)) if (ReservedModuleNames.Contains(kv.Key))
continue; continue;
var t = oc.FindType(kv.Key); var t = oc.FindType(kv.Key);
@@ -227,20 +229,20 @@ namespace OpenRA
customDataLoaded = true; customDataLoaded = true;
} }
static string[] YamlList(Dictionary<string, MiniYaml> yaml, string key) static ImmutableArray<string> YamlList(Dictionary<string, MiniYaml> yaml, string key)
{ {
if (!yaml.TryGetValue(key, out var value)) if (!yaml.TryGetValue(key, out var value))
return []; return [];
return value.Nodes.Select(n => n.Key).ToArray(); return value.Nodes.Select(n => n.Key).ToImmutableArray();
} }
static IReadOnlyDictionary<string, string> YamlDictionary(Dictionary<string, MiniYaml> yaml, string key) static FrozenDictionary<string, string> YamlDictionary(Dictionary<string, MiniYaml> yaml, string key)
{ {
if (!yaml.TryGetValue(key, out var value)) if (!yaml.TryGetValue(key, out var value))
return new Dictionary<string, string>(); return FrozenDictionary<string, string>.Empty;
return value.ToDictionary(my => my.Value); return value.ToDictionary(my => my.Value).ToFrozenDictionary();
} }
public bool Contains<T>() where T : IGlobalModData public bool Contains<T>() where T : IGlobalModData

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -192,7 +193,7 @@ namespace OpenRA
public bool LockPreview; public bool LockPreview;
public Rectangle Bounds; public Rectangle Bounds;
public MapVisibility Visibility = MapVisibility.Lobby; public MapVisibility Visibility = MapVisibility.Lobby;
public string[] Categories = ["Conquest"]; public ImmutableArray<string> Categories = ["Conquest"];
public Size MapSize { get; private set; } public Size MapSize { get; private set; }

View File

@@ -117,7 +117,7 @@ namespace OpenRA
public readonly bool EnableDepthBuffer = false; public readonly bool EnableDepthBuffer = false;
public readonly WVec[] SubCellOffsets = public readonly ImmutableArray<WVec> SubCellOffsets =
[ [
new(0, 0, 0), // full cell - index 0 new(0, 0, 0), // full cell - index 0
new(-299, -256, 0), // top left - index 1 new(-299, -256, 0), // top left - index 1
@@ -127,9 +127,9 @@ namespace OpenRA
new(256, 256, 0), // bottom right - index 5 new(256, 256, 0), // bottom right - index 5
]; ];
public CellRamp[] Ramps { get; } public ImmutableArray<CellRamp> Ramps { get; }
internal readonly CVec[][] TilesByDistance; internal readonly ImmutableArray<ImmutableArray<CVec>> TilesByDistance;
public int TileScale { get; } public int TileScale { get; }
@@ -202,7 +202,7 @@ namespace OpenRA
TilesByDistance = CreateTilesByDistance(); TilesByDistance = CreateTilesByDistance();
} }
CVec[][] CreateTilesByDistance() ImmutableArray<ImmutableArray<CVec>> CreateTilesByDistance()
{ {
var ts = new List<CVec>[MaximumTileSearchRange + 1]; var ts = new List<CVec>[MaximumTileSearchRange + 1];
for (var i = 0; i < MaximumTileSearchRange + 1; i++) for (var i = 0; i < MaximumTileSearchRange + 1; i++)
@@ -238,7 +238,7 @@ namespace OpenRA
}); });
} }
return ts.Select(list => list.ToArray()).ToArray(); return ts.Select(list => list.ToImmutableArray()).ToImmutableArray();
} }
public WVec OffsetOfSubCell(SubCell subCell) public WVec OffsetOfSubCell(SubCell subCell)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Traits; using OpenRA.Traits;
@@ -54,7 +55,7 @@ namespace OpenRA
Name = "Creeps", Name = "Creeps",
Faction = firstFaction, Faction = firstFaction,
NonCombatant = true, NonCombatant = true,
Enemies = Exts.MakeArray(playerCount, i => $"Multi{i}") Enemies = Exts.MakeArray(playerCount, i => $"Multi{i}").ToImmutableArray()
} }
} }
}; };

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -49,10 +50,10 @@ namespace OpenRA
{ {
public readonly string title; public readonly string title;
public readonly string author; public readonly string author;
public readonly string[] categories; public readonly ImmutableArray<string> categories;
public readonly int players; public readonly int players;
public readonly Rectangle bounds; public readonly Rectangle bounds;
public readonly short[] spawnpoints = []; public readonly ImmutableArray<short> spawnpoints = [];
public readonly MapGridType map_grid_type; public readonly MapGridType map_grid_type;
public readonly string minimap; public readonly string minimap;
public readonly bool downloading; public readonly bool downloading;
@@ -70,12 +71,12 @@ namespace OpenRA
{ {
public int MapFormat; public int MapFormat;
public string Title; public string Title;
public string[] Categories; public ImmutableArray<string> Categories;
public string Author; public string Author;
public string TileSet; public string TileSet;
public MapPlayers Players; public MapPlayers Players;
public int PlayerCount; public int PlayerCount;
public CPos[] SpawnPoints; public ImmutableArray<CPos> SpawnPoints;
public MapGridType GridType; public MapGridType GridType;
public Rectangle Bounds; public Rectangle Bounds;
public Png Preview; public Png Preview;
@@ -129,9 +130,9 @@ namespace OpenRA
{ {
if (FluentMessageDefinitions != null) if (FluentMessageDefinitions != null)
{ {
var files = Array.Empty<string>(); var files = ImmutableArray<string>.Empty;
if (FluentMessageDefinitions.Value != null) if (FluentMessageDefinitions.Value != null)
files = FieldLoader.GetValue<string[]>("value", FluentMessageDefinitions.Value); files = FieldLoader.GetValue<ImmutableArray<string>>("value", FluentMessageDefinitions.Value);
string text = null; string text = null;
if (FluentMessageDefinitions.Nodes.Length > 0) if (FluentMessageDefinitions.Nodes.Length > 0)
@@ -157,8 +158,8 @@ namespace OpenRA
var files = Enumerable.Empty<string>(); var files = Enumerable.Empty<string>();
if (RuleDefinitions.Value != null) if (RuleDefinitions.Value != null)
{ {
var mapFiles = FieldLoader.GetValue<string[]>("value", RuleDefinitions.Value); var mapFiles = FieldLoader.GetValue<ImmutableArray<string>>("value", RuleDefinitions.Value);
files = files.Append(mapFiles); files = files.Concat(mapFiles);
} }
var stringPool = new HashSet<string>(); // Reuse common strings in YAML var stringPool = new HashSet<string>(); // Reuse common strings in YAML
@@ -196,7 +197,6 @@ namespace OpenRA
} }
} }
static readonly CPos[] NoSpawns = [];
readonly object syncRoot = new(); readonly object syncRoot = new();
readonly MapCache cache; readonly MapCache cache;
readonly ModData modData; readonly ModData modData;
@@ -238,12 +238,12 @@ namespace OpenRA
public int MapFormat => innerData.MapFormat; public int MapFormat => innerData.MapFormat;
public string Title => innerData.Title; public string Title => innerData.Title;
public string[] Categories => innerData.Categories; public ImmutableArray<string> Categories => innerData.Categories;
public string Author => innerData.Author; public string Author => innerData.Author;
public string TileSet => innerData.TileSet; public string TileSet => innerData.TileSet;
public MapPlayers Players => innerData.Players; public MapPlayers Players => innerData.Players;
public int PlayerCount => innerData.PlayerCount; public int PlayerCount => innerData.PlayerCount;
public CPos[] SpawnPoints => innerData.SpawnPoints; public ImmutableArray<CPos> SpawnPoints => innerData.SpawnPoints;
public MapGridType GridType => innerData.GridType; public MapGridType GridType => innerData.GridType;
public Rectangle Bounds => innerData.Bounds; public Rectangle Bounds => innerData.Bounds;
public Png Preview => innerData.Preview; public Png Preview => innerData.Preview;
@@ -341,7 +341,7 @@ namespace OpenRA
TileSet = "unknown", TileSet = "unknown",
Players = null, Players = null,
PlayerCount = 0, PlayerCount = 0,
SpawnPoints = NoSpawns, SpawnPoints = [],
GridType = gridType, GridType = gridType,
Bounds = Rectangle.Empty, Bounds = Rectangle.Empty,
Preview = null, Preview = null,
@@ -397,7 +397,7 @@ namespace OpenRA
newData.Title = temp.Value; newData.Title = temp.Value;
if (yaml.TryGetValue("Categories", out temp)) if (yaml.TryGetValue("Categories", out temp))
newData.Categories = FieldLoader.GetValue<string[]>("Categories", temp.Value); newData.Categories = FieldLoader.GetValue<ImmutableArray<string>>("Categories", temp.Value);
if (yaml.TryGetValue("Tileset", out temp)) if (yaml.TryGetValue("Tileset", out temp))
newData.TileSet = temp.Value; newData.TileSet = temp.Value;
@@ -433,7 +433,7 @@ namespace OpenRA
spawns.Add(s.Get<LocationInit>().Value); spawns.Add(s.Get<LocationInit>().Value);
} }
newData.SpawnPoints = spawns.ToArray(); newData.SpawnPoints = spawns.ToImmutableArray();
} }
else else
newData.SpawnPoints = []; newData.SpawnPoints = [];
@@ -526,7 +526,7 @@ namespace OpenRA
var spawns = new CPos[r.spawnpoints.Length / 2]; var spawns = new CPos[r.spawnpoints.Length / 2];
for (var j = 0; j < r.spawnpoints.Length; j += 2) for (var j = 0; j < r.spawnpoints.Length; j += 2)
spawns[j / 2] = new CPos(r.spawnpoints[j], r.spawnpoints[j + 1]); spawns[j / 2] = new CPos(r.spawnpoints[j], r.spawnpoints[j + 1]);
newData.SpawnPoints = spawns; newData.SpawnPoints = spawns.ToImmutableArray();
newData.GridType = r.map_grid_type; newData.GridType = r.map_grid_type;
if (cache.LoadPreviewImages) if (cache.LoadPreviewImages)
{ {

View File

@@ -9,6 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Immutable;
using OpenRA.Primitives; using OpenRA.Primitives;
namespace OpenRA namespace OpenRA
@@ -53,8 +54,8 @@ namespace OpenRA
public bool LockHandicap = false; public bool LockHandicap = false;
public int Handicap = 0; public int Handicap = 0;
public string[] Allies = []; public ImmutableArray<string> Allies = [];
public string[] Enemies = []; public ImmutableArray<string> Enemies = [];
public PlayerReference() { } public PlayerReference() { }
public PlayerReference(MiniYaml my) { FieldLoader.Load(this, my); } public PlayerReference(MiniYaml my) { FieldLoader.Load(this, my); }

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.FileSystem; using OpenRA.FileSystem;
using OpenRA.Primitives; using OpenRA.Primitives;
using OpenRA.Support; using OpenRA.Support;
@@ -27,14 +28,14 @@ namespace OpenRA
string Id { get; } string Id { get; }
string Name { get; } string Name { get; }
Size TileSize { get; } Size TileSize { get; }
TerrainTypeInfo[] TerrainTypes { get; } ImmutableArray<TerrainTypeInfo> TerrainTypes { get; }
TerrainTileInfo GetTerrainInfo(TerrainTile r); TerrainTileInfo GetTerrainInfo(TerrainTile r);
bool TryGetTerrainInfo(TerrainTile r, out TerrainTileInfo info); bool TryGetTerrainInfo(TerrainTile r, out TerrainTileInfo info);
byte GetTerrainIndex(string type); byte GetTerrainIndex(string type);
byte GetTerrainIndex(TerrainTile r); byte GetTerrainIndex(TerrainTile r);
TerrainTile DefaultTerrainTile { get; } TerrainTile DefaultTerrainTile { get; }
Color[] HeightDebugColors { get; } ImmutableArray<Color> HeightDebugColors { get; }
IEnumerable<Color> RestrictedPlayerColors { get; } IEnumerable<Color> RestrictedPlayerColors { get; }
float MinHeightColorBrightness { get; } float MinHeightColorBrightness { get; }
float MaxHeightColorBrightness { get; } float MaxHeightColorBrightness { get; }
@@ -62,7 +63,7 @@ namespace OpenRA
{ {
public readonly string Type; public readonly string Type;
public readonly BitSet<TargetableType> TargetTypes; public readonly BitSet<TargetableType> TargetTypes;
public readonly HashSet<string> AcceptsSmudgeType = []; public readonly ImmutableArray<string> AcceptsSmudgeType = [];
public readonly Color Color; public readonly Color Color;
public readonly bool RestrictPlayerColor = false; public readonly bool RestrictPlayerColor = false;

View File

@@ -678,8 +678,8 @@ namespace OpenRA
{ {
if (mapRules != null && mapRules.Value != null) if (mapRules != null && mapRules.Value != null)
{ {
var mapFiles = FieldLoader.GetValue<string[]>("value", mapRules.Value); var mapFiles = FieldLoader.GetValue<ImmutableArray<string>>("value", mapRules.Value);
files = files.Append(mapFiles); files = files.Concat(mapFiles);
} }
var stringPool = new HashSet<string>(); // Reuse common strings in YAML var stringPool = new HashSet<string>(); // Reuse common strings in YAML

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using Linguini.Shared.Types.Bundle; using Linguini.Shared.Types.Bundle;
namespace OpenRA.Network namespace OpenRA.Network
@@ -55,31 +56,32 @@ namespace OpenRA.Network
public readonly string Key = string.Empty; public readonly string Key = string.Empty;
[FieldLoader.LoadUsing(nameof(LoadArguments))] [FieldLoader.LoadUsing(nameof(LoadArguments))]
public readonly object[] Arguments; public readonly ImmutableArray<object> Arguments;
static object LoadArguments(MiniYaml yaml) static object LoadArguments(MiniYaml yaml)
{ {
var arguments = new List<object>();
var argumentsNode = yaml.NodeWithKeyOrDefault("Arguments"); var argumentsNode = yaml.NodeWithKeyOrDefault("Arguments");
if (argumentsNode != null)
{
foreach (var argumentNode in argumentsNode.Value.Nodes)
{
var argument = FieldLoader.Load<FluentArgument>(argumentNode.Value);
arguments.Add(argument.Key);
if (argument.Type == FluentArgument.FluentArgumentType.Number)
{
if (!double.TryParse(argument.Value, out var number))
Log.Write("debug", $"Failed to parse {argument.Value}");
arguments.Add(number); if (argumentsNode == null)
} return ImmutableArray<object>.Empty;
else
arguments.Add(argument.Value); var arguments = new List<object>(argumentsNode.Value.Nodes.Length * 2);
foreach (var argumentNode in argumentsNode.Value.Nodes)
{
var argument = FieldLoader.Load<FluentArgument>(argumentNode.Value);
arguments.Add(argument.Key);
if (argument.Type == FluentArgument.FluentArgumentType.Number)
{
if (!double.TryParse(argument.Value, out var number))
Log.Write("debug", $"Failed to parse {argument.Value}");
arguments.Add(number);
} }
else
arguments.Add(argument.Value);
} }
return arguments.ToArray(); return arguments.ToImmutableArray();
} }
public FluentMessage(MiniYaml yaml) public FluentMessage(MiniYaml yaml)

View File

@@ -10,7 +10,9 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -47,7 +49,7 @@ namespace OpenRA.Network
public class GameServer public class GameServer
{ {
static readonly string[] SerializeFields = static readonly ImmutableArray<string> SerializeFields =
[ [
// Server information // Server information
@@ -131,10 +133,10 @@ namespace OpenRA.Network
public readonly bool IsJoinable = false; public readonly bool IsJoinable = false;
[FieldLoader.LoadUsing(nameof(LoadClients))] [FieldLoader.LoadUsing(nameof(LoadClients))]
public readonly GameClient[] Clients; public readonly ImmutableArray<GameClient> Clients;
/// <summary>The list of spawnpoints that are disabled for this game.</summary> /// <summary>The list of spawnpoints that are disabled for this game.</summary>
public readonly int[] DisabledSpawnPoints = []; public readonly FrozenSet<int> DisabledSpawnPoints = FrozenSet<int>.Empty;
public string ModLabel => $"{ModTitle} ({Version})"; public string ModLabel => $"{ModTitle} ({Version})";
@@ -150,7 +152,7 @@ namespace OpenRA.Network
clients.Add(FieldLoader.Load<GameClient>(client.Value)); clients.Add(FieldLoader.Load<GameClient>(client.Value));
} }
return clients.ToArray(); return clients.ToImmutableArray();
} }
public GameServer(MiniYaml yaml) public GameServer(MiniYaml yaml)
@@ -227,9 +229,9 @@ namespace OpenRA.Network
ModWebsite = manifest.Metadata.Website; ModWebsite = manifest.Metadata.Website;
ModIcon32 = manifest.Metadata.WebIcon32; ModIcon32 = manifest.Metadata.WebIcon32;
Protected = !string.IsNullOrEmpty(server.Settings.Password); Protected = !string.IsNullOrEmpty(server.Settings.Password);
Authentication = server.Settings.RequireAuthentication || server.Settings.ProfileIDWhitelist.Length > 0; Authentication = server.Settings.RequireAuthentication || server.Settings.ProfileIDWhitelist.Count > 0;
Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToArray(); Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToImmutableArray();
DisabledSpawnPoints = server.LobbyInfo.DisabledSpawnPoints?.ToArray() ?? []; DisabledSpawnPoints = server.LobbyInfo.DisabledSpawnPoints?.ToFrozenSet() ?? FrozenSet<int>.Empty;
} }
public string ToPOSTData(bool lanGame) public string ToPOSTData(bool lanGame)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -40,7 +41,7 @@ namespace OpenRA.Network
public bool AuthenticationFailed = false; public bool AuthenticationFailed = false;
// The default null means "no map restriction" while an empty set means "all maps restricted" // The default null means "no map restriction" while an empty set means "all maps restricted"
public HashSet<string> ServerMapPool = null; public FrozenSet<string> ServerMapPool = null;
public int NetFrameNumber { get; private set; } public int NetFrameNumber { get; private set; }
public int LocalFrameNumber; public int LocalFrameNumber;

View File

@@ -9,6 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Server; using OpenRA.Server;
@@ -67,11 +68,11 @@ namespace OpenRA.Network
{ {
var message = new FluentMessage(node.Value); var message = new FluentMessage(node.Value);
if (message.Key == Joined) if (message.Key == Joined)
TextNotificationsManager.AddPlayerJoinedLine(message.Key, message.Arguments); TextNotificationsManager.AddPlayerJoinedLine(message.Key, message.Arguments.ToArray());
else if (message.Key == Left) else if (message.Key == Left)
TextNotificationsManager.AddPlayerLeftLine(message.Key, message.Arguments); TextNotificationsManager.AddPlayerLeftLine(message.Key, message.Arguments.ToArray());
else else
TextNotificationsManager.AddSystemLine(message.Key, message.Arguments); TextNotificationsManager.AddSystemLine(message.Key, message.Arguments.ToArray());
} }
break; break;
@@ -385,7 +386,7 @@ namespace OpenRA.Network
case "SyncMapPool": case "SyncMapPool":
{ {
orderManager.ServerMapPool = FieldLoader.GetValue<HashSet<string>>("SyncMapPool", order.TargetString); orderManager.ServerMapPool = FieldLoader.GetValue<FrozenSet<string>>("SyncMapPool", order.TargetString);
break; break;
} }

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
namespace OpenRA.Primitives namespace OpenRA.Primitives
@@ -19,7 +20,7 @@ namespace OpenRA.Primitives
public static readonly Polygon Empty = new(Rectangle.Empty); public static readonly Polygon Empty = new(Rectangle.Empty);
public readonly Rectangle BoundingRect; public readonly Rectangle BoundingRect;
public readonly int2[] Vertices; public readonly ImmutableArray<int2> Vertices;
readonly bool isRectangle; readonly bool isRectangle;
public Polygon(Rectangle bounds) public Polygon(Rectangle bounds)
@@ -29,7 +30,7 @@ namespace OpenRA.Primitives
isRectangle = true; isRectangle = true;
} }
public Polygon(int2[] vertices) public Polygon(ImmutableArray<int2> vertices)
{ {
if (vertices != null && vertices.Length > 0) if (vertices != null && vertices.Length > 0)
{ {
@@ -53,7 +54,7 @@ namespace OpenRA.Primitives
{ {
isRectangle = true; isRectangle = true;
BoundingRect = Rectangle.Empty; BoundingRect = Rectangle.Empty;
Vertices = Exts.MakeArray(4, _ => int2.Zero); Vertices = Exts.MakeArray(4, _ => int2.Zero).ToImmutableArray();
} }
} }

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
@@ -132,7 +133,7 @@ namespace OpenRA.Server
public MapPreview Map; public MapPreview Map;
public readonly MapStatusCache MapStatusCache; public readonly MapStatusCache MapStatusCache;
public GameSave GameSave; public GameSave GameSave;
public HashSet<string> MapPool; public FrozenSet<string> MapPool;
// Default to the next frame for ServerType.Local - MP servers take the value from the selected GameSpeed. // Default to the next frame for ServerType.Local - MP servers take the value from the selected GameSpeed.
public int OrderLatency = 1; public int OrderLatency = 1;
@@ -661,9 +662,9 @@ namespace OpenRA.Server
events.Add(new CallbackEvent(() => events.Add(new CallbackEvent(() =>
{ {
var notAuthenticated = Type == ServerType.Dedicated && profile == null && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Length > 0); var notAuthenticated = Type == ServerType.Dedicated && profile == null && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Count > 0);
var blacklisted = Type == ServerType.Dedicated && profile != null && Settings.ProfileIDBlacklist.Contains(profile.ProfileID); var blacklisted = Type == ServerType.Dedicated && profile != null && Settings.ProfileIDBlacklist.Contains(profile.ProfileID);
var notWhitelisted = Type == ServerType.Dedicated && Settings.ProfileIDWhitelist.Length > 0 && var notWhitelisted = Type == ServerType.Dedicated && Settings.ProfileIDWhitelist.Count > 0 &&
(profile == null || !Settings.ProfileIDWhitelist.Contains(profile.ProfileID)); (profile == null || !Settings.ProfileIDWhitelist.Contains(profile.ProfileID));
if (notAuthenticated) if (notAuthenticated)
@@ -689,7 +690,7 @@ namespace OpenRA.Server
} }
else else
{ {
if (Type == ServerType.Dedicated && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Length > 0)) if (Type == ServerType.Dedicated && (Settings.RequireAuthentication || Settings.ProfileIDWhitelist.Count > 0))
{ {
Log.Write("server", $"Rejected connection from {newConn.EndPoint}; Not authenticated."); Log.Write("server", $"Rejected connection from {newConn.EndPoint}; Not authenticated.");
SendOrderTo(newConn, "ServerError", RequiresAuthentication); SendOrderTo(newConn, "ServerError", RequiresAuthentication);

View File

@@ -10,7 +10,9 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -69,16 +71,16 @@ namespace OpenRA
public string Map = null; public string Map = null;
[Desc("Takes a comma separated list of IP addresses that are not allowed to join.")] [Desc("Takes a comma separated list of IP addresses that are not allowed to join.")]
public string[] Ban = []; public FrozenSet<string> Ban = FrozenSet<string>.Empty;
[Desc("For dedicated servers only, allow anonymous clients to join.")] [Desc("For dedicated servers only, allow anonymous clients to join.")]
public bool RequireAuthentication = false; public bool RequireAuthentication = false;
[Desc("For dedicated servers only, if non-empty, only allow authenticated players with these profile IDs to join.")] [Desc("For dedicated servers only, if non-empty, only allow authenticated players with these profile IDs to join.")]
public int[] ProfileIDWhitelist = []; public FrozenSet<int> ProfileIDWhitelist = FrozenSet<int>.Empty;
[Desc("For dedicated servers only, if non-empty, always reject players with these user IDs from joining.")] [Desc("For dedicated servers only, if non-empty, always reject players with these user IDs from joining.")]
public int[] ProfileIDBlacklist = []; public FrozenSet<int> ProfileIDBlacklist = FrozenSet<int>.Empty;
[Desc("For dedicated servers only, controls whether a game can be started with just one human player in the lobby.")] [Desc("For dedicated servers only, controls whether a game can be started with just one human player in the lobby.")]
public bool EnableSingleplayer = false; public bool EnableSingleplayer = false;
@@ -105,7 +107,7 @@ namespace OpenRA
public bool EnableLintChecks = true; public bool EnableLintChecks = true;
[Desc("For dedicated servers only, a comma separated list of map uids that are allowed to be used.")] [Desc("For dedicated servers only, a comma separated list of map uids that are allowed to be used.")]
public string[] MapPool = []; public FrozenSet<string> MapPool = FrozenSet<string>.Empty;
[Desc("Delay in milliseconds before newly joined players can send chat messages.")] [Desc("Delay in milliseconds before newly joined players can send chat messages.")]
public int FloodLimitJoinCooldown = 5000; public int FloodLimitJoinCooldown = 5000;
@@ -251,7 +253,7 @@ namespace OpenRA
public string Name = "Commander"; public string Name = "Commander";
public Color Color = Color.FromArgb(200, 32, 32); public Color Color = Color.FromArgb(200, 32, 32);
public string LastServer = "localhost:1234"; public string LastServer = "localhost:1234";
public Color[] CustomColors = []; public ImmutableArray<Color> CustomColors = [];
} }
public class SinglePlayerGameSettings public class SinglePlayerGameSettings

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using OpenRA.FileSystem; using OpenRA.FileSystem;
using OpenRA.GameRules; using OpenRA.GameRules;
@@ -165,12 +166,12 @@ namespace OpenRA
public ISound PlayLooped(SoundType type, string name) { return Play(type, null, name, true, WPos.Zero, 1f, true); } public ISound PlayLooped(SoundType type, string name) { return Play(type, null, name, true, WPos.Zero, 1f, true); }
public ISound PlayLooped(SoundType type, string name, WPos pos) { return Play(type, null, name, false, pos, 1f, true); } public ISound PlayLooped(SoundType type, string name, WPos pos) { return Play(type, null, name, false, pos, 1f, true); }
public ISound Play(SoundType type, string[] names, World world, Player player = null, float volumeModifier = 1f) public ISound Play(SoundType type, ImmutableArray<string> names, World world, Player player = null, float volumeModifier = 1f)
{ {
return Play(type, player, names.Random(world.LocalRandom), true, WPos.Zero, volumeModifier); return Play(type, player, names.Random(world.LocalRandom), true, WPos.Zero, volumeModifier);
} }
public ISound Play(SoundType type, string[] names, World world, WPos pos, Player player = null, float volumeModifier = 1f) public ISound Play(SoundType type, ImmutableArray<string> names, World world, WPos pos, Player player = null, float volumeModifier = 1f)
{ {
return Play(type, player, names.Random(world.LocalRandom), false, pos, volumeModifier); return Play(type, player, names.Random(world.LocalRandom), false, pos, volumeModifier);
} }
@@ -399,9 +400,9 @@ namespace OpenRA
if (variant != null) if (variant != null)
{ {
if (rules.Variants.TryGetValue(variant, out var v) && !rules.DisableVariants.Contains(definition)) if (rules.Variants.TryGetValue(variant, out var v) && !rules.DisableVariants.Contains(definition))
suffix = v[id % v.Length]; suffix = v[(int)(id % v.Length)];
if (rules.Prefixes.TryGetValue(variant, out var p) && !rules.DisablePrefixes.Contains(definition)) if (rules.Prefixes.TryGetValue(variant, out var p) && !rules.DisablePrefixes.Contains(definition))
prefix = p[id % p.Length]; prefix = p[(int)(id % p.Length)];
} }
var name = prefix + clip + suffix; var name = prefix + clip + suffix;

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
@@ -175,7 +176,7 @@ namespace OpenRA.Traits
[RequireExplicitImplementation] [RequireExplicitImplementation]
public interface IStoresResourcesInfo : ITraitInfoInterface public interface IStoresResourcesInfo : ITraitInfoInterface
{ {
string[] ResourceTypes { get; } ImmutableArray<string> ResourceTypes { get; }
} }
public interface IStoresResources public interface IStoresResources
@@ -505,12 +506,12 @@ namespace OpenRA.Traits
public interface IControlGroupsInfo : ITraitInfoInterface public interface IControlGroupsInfo : ITraitInfoInterface
{ {
string[] Groups { get; } ImmutableArray<string> Groups { get; }
} }
public interface IControlGroups public interface IControlGroups
{ {
string[] Groups { get; } ImmutableArray<string> Groups { get; }
void SelectControlGroup(int group); void SelectControlGroup(int group);
void CreateControlGroup(int group); void CreateControlGroup(int group);

View File

@@ -9,7 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Generic; using System.Collections.Frozen;
namespace OpenRA.Traits namespace OpenRA.Traits
{ {
@@ -25,7 +25,7 @@ namespace OpenRA.Traits
public readonly string InternalName = null; public readonly string InternalName = null;
[Desc("Pick a random faction as the player's faction out of this list.")] [Desc("Pick a random faction as the player's faction out of this list.")]
public readonly HashSet<string> RandomFactionMembers = []; public readonly FrozenSet<string> RandomFactionMembers = FrozenSet<string>.Empty;
[Desc("The side that the faction belongs to. For example, England belongs to the 'Allies' side.")] [Desc("The side that the faction belongs to. For example, England belongs to the 'Allies' side.")]
public readonly string Side = null; public readonly string Side = null;

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -208,8 +209,8 @@ namespace OpenRA.Widgets
public IntegerExpression Y; public IntegerExpression Y;
public IntegerExpression Width; public IntegerExpression Width;
public IntegerExpression Height; public IntegerExpression Height;
public string[] Logic = []; public ImmutableArray<string> Logic = [];
public ChromeLogic[] LogicObjects { get; private set; } public ImmutableArray<ChromeLogic> LogicObjects { get; private set; }
public bool Visible = true; public bool Visible = true;
public bool IgnoreMouseOver; public bool IgnoreMouseOver;
public bool IgnoreChildMouseOver; public bool IgnoreChildMouseOver;
@@ -307,7 +308,7 @@ namespace OpenRA.Widgets
args["widget"] = this; args["widget"] = this;
LogicObjects = Logic.Select(l => Game.ModData.ObjectCreator.CreateObject<ChromeLogic>(l, args)) LogicObjects = Logic.Select(l => Game.ModData.ObjectCreator.CreateObject<ChromeLogic>(l, args))
.ToArray(); .ToImmutableArray();
foreach (var logicObject in LogicObjects) foreach (var logicObject in LogicObjects)
Ui.Subscribe(logicObject); Ui.Subscribe(logicObject);

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -302,7 +303,7 @@ namespace OpenRA
foreach (var player in Players) foreach (var player in Players)
gameInfo.AddPlayer(player, OrderManager.LobbyInfo); gameInfo.AddPlayer(player, OrderManager.LobbyInfo);
gameInfo.DisabledSpawnPoints = OrderManager.LobbyInfo.DisabledSpawnPoints; gameInfo.DisabledSpawnPoints = OrderManager.LobbyInfo.DisabledSpawnPoints.ToFrozenSet();
gameInfo.StartTimeUtc = DateTime.UtcNow; gameInfo.StartTimeUtc = DateTime.UtcNow;

View File

@@ -9,7 +9,9 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics; using OpenRA.Mods.Common.Graphics;
@@ -33,15 +35,15 @@ namespace OpenRA.Mods.Cnc.Graphics
public class ClassicTilesetSpecificSpriteSequence : ClassicSpriteSequence public class ClassicTilesetSpecificSpriteSequence : ClassicSpriteSequence
{ {
[Desc("Dictionary of <tileset name>: filename to override the Filename key.")] [Desc("Dictionary of <tileset name>: filename to override the Filename key.")]
static readonly SpriteSequenceField<Dictionary<string, string>> TilesetFilenames = new(nameof(TilesetFilenames), null); static readonly SpriteSequenceField<FrozenDictionary<string, string>> TilesetFilenames = new(nameof(TilesetFilenames), null);
[Desc("Dictionary of <tileset name>: <filename pattern> to override the FilenamePattern key.")] [Desc("Dictionary of <tileset name>: <filename pattern> to override the FilenamePattern key.")]
static readonly SpriteSequenceField<Dictionary<string, string>> TilesetFilenamesPattern = new(nameof(TilesetFilenamesPattern), null); static readonly SpriteSequenceField<FrozenDictionary<string, string>> TilesetFilenamesPattern = new(nameof(TilesetFilenamesPattern), null);
public ClassicTilesetSpecificSpriteSequence(SpriteCache cache, ISpriteSequenceLoader loader, string image, string sequence, MiniYaml data, MiniYaml defaults) public ClassicTilesetSpecificSpriteSequence(SpriteCache cache, ISpriteSequenceLoader loader, string image, string sequence, MiniYaml data, MiniYaml defaults)
: base(cache, loader, image, sequence, data, defaults) { } : base(cache, loader, image, sequence, data, defaults) { }
protected override IEnumerable<ReservationInfo> ParseFilenames(ModData modData, string tileset, int[] frames, MiniYaml data, MiniYaml defaults) protected override IEnumerable<ReservationInfo> ParseFilenames(ModData modData, string tileset, ImmutableArray<int> frames, MiniYaml data, MiniYaml defaults)
{ {
var tilesetFilenamesPatternNode = data.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key) ?? defaults.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key); var tilesetFilenamesPatternNode = data.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key) ?? defaults.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key);
if (tilesetFilenamesPatternNode != null) if (tilesetFilenamesPatternNode != null)
@@ -71,7 +73,7 @@ namespace OpenRA.Mods.Cnc.Graphics
return base.ParseFilenames(modData, tileset, frames, data, defaults); return base.ParseFilenames(modData, tileset, frames, data, defaults);
} }
protected override IEnumerable<ReservationInfo> ParseCombineFilenames(ModData modData, string tileset, int[] frames, MiniYaml data) protected override IEnumerable<ReservationInfo> ParseCombineFilenames(ModData modData, string tileset, ImmutableArray<int> frames, MiniYaml data)
{ {
var node = data.NodeWithKeyOrDefault(TilesetFilenames.Key); var node = data.NodeWithKeyOrDefault(TilesetFilenames.Key);
if (node != null) if (node != null)
@@ -83,7 +85,7 @@ namespace OpenRA.Mods.Cnc.Graphics
{ {
var subStart = LoadField("Start", 0, data); var subStart = LoadField("Start", 0, data);
var subLength = LoadField("Length", 1, data); var subLength = LoadField("Length", 1, data);
frames = Exts.MakeArray(subLength, i => subStart + i); frames = Exts.MakeArray(subLength, i => subStart + i).ToImmutableArray();
} }
return [new ReservationInfo(tilesetNode.Value.Value, frames, frames, tilesetNode.Location)]; return [new ReservationInfo(tilesetNode.Value.Value, frames, frames, tilesetNode.Location)];

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Traits; using OpenRA.Mods.Cnc.Traits;
using OpenRA.Mods.Common.Graphics; using OpenRA.Mods.Common.Graphics;
@@ -22,8 +23,8 @@ namespace OpenRA.Mods.Cnc.Graphics
readonly ModelRenderer renderer; readonly ModelRenderer renderer;
readonly ModelAnimation[] components; readonly ModelAnimation[] components;
readonly float scale; readonly float scale;
readonly float[] lightAmbientColor; readonly ImmutableArray<float> lightAmbientColor;
readonly float[] lightDiffuseColor; readonly ImmutableArray<float> lightDiffuseColor;
readonly WRot lightSource; readonly WRot lightSource;
readonly WRot camera; readonly WRot camera;
readonly PaletteReference colorPalette; readonly PaletteReference colorPalette;
@@ -33,7 +34,7 @@ namespace OpenRA.Mods.Cnc.Graphics
readonly int zOffset; readonly int zOffset;
public ModelPreview(ModelRenderer renderer, ModelAnimation[] components, in WVec offset, int zOffset, float scale, WAngle lightPitch, WAngle lightYaw, public ModelPreview(ModelRenderer renderer, ModelAnimation[] components, in WVec offset, int zOffset, float scale, WAngle lightPitch, WAngle lightYaw,
float[] lightAmbientColor, float[] lightDiffuseColor, WAngle cameraPitch, ImmutableArray<float> lightAmbientColor, ImmutableArray<float> lightDiffuseColor, WAngle cameraPitch,
PaletteReference colorPalette, PaletteReference normalsPalette, PaletteReference shadowPalette) PaletteReference colorPalette, PaletteReference normalsPalette, PaletteReference shadowPalette)
{ {
this.renderer = renderer; this.renderer = renderer;

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Traits; using OpenRA.Mods.Cnc.Traits;
@@ -24,15 +25,15 @@ namespace OpenRA.Mods.Cnc.Graphics
readonly IEnumerable<ModelAnimation> models; readonly IEnumerable<ModelAnimation> models;
readonly WRot camera; readonly WRot camera;
readonly WRot lightSource; readonly WRot lightSource;
readonly float[] lightAmbientColor; readonly ImmutableArray<float> lightAmbientColor;
readonly float[] lightDiffuseColor; readonly ImmutableArray<float> lightDiffuseColor;
readonly PaletteReference normalsPalette; readonly PaletteReference normalsPalette;
readonly PaletteReference shadowPalette; readonly PaletteReference shadowPalette;
readonly float scale; readonly float scale;
public ModelRenderable( public ModelRenderable(
ModelRenderer renderer, IEnumerable<ModelAnimation> models, WPos pos, int zOffset, in WRot camera, float scale, ModelRenderer renderer, IEnumerable<ModelAnimation> models, WPos pos, int zOffset, in WRot camera, float scale,
in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, in WRot lightSource, ImmutableArray<float> lightAmbientColor, ImmutableArray<float> lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadow) PaletteReference color, PaletteReference normals, PaletteReference shadow)
: this(renderer, models, pos, zOffset, camera, scale, : this(renderer, models, pos, zOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor, lightSource, lightAmbientColor, lightDiffuseColor,
@@ -42,7 +43,7 @@ namespace OpenRA.Mods.Cnc.Graphics
public ModelRenderable( public ModelRenderable(
ModelRenderer renderer, IEnumerable<ModelAnimation> models, WPos pos, int zOffset, in WRot camera, float scale, ModelRenderer renderer, IEnumerable<ModelAnimation> models, WPos pos, int zOffset, in WRot camera, float scale,
in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, in WRot lightSource, ImmutableArray<float> lightAmbientColor, ImmutableArray<float> lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadow, PaletteReference color, PaletteReference normals, PaletteReference shadow,
float alpha, in float3 tint, TintModifiers tintModifiers) float alpha, in float3 tint, TintModifiers tintModifiers)
{ {

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Traits; using OpenRA.Mods.Cnc.Traits;
@@ -25,15 +26,15 @@ namespace OpenRA.Mods.Cnc.Graphics
readonly int2 screenPos; readonly int2 screenPos;
readonly WRot camera; readonly WRot camera;
readonly WRot lightSource; readonly WRot lightSource;
readonly float[] lightAmbientColor; readonly ImmutableArray<float> lightAmbientColor;
readonly float[] lightDiffuseColor; readonly ImmutableArray<float> lightDiffuseColor;
readonly PaletteReference normalsPalette; readonly PaletteReference normalsPalette;
readonly PaletteReference shadowPalette; readonly PaletteReference shadowPalette;
readonly float scale; readonly float scale;
public UIModelRenderable( public UIModelRenderable(
ModelRenderer renderer, IEnumerable<ModelAnimation> models, WPos effectiveWorldPos, int2 screenPos, int zOffset, ModelRenderer renderer, IEnumerable<ModelAnimation> models, WPos effectiveWorldPos, int2 screenPos, int zOffset,
in WRot camera, float scale, in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, in WRot camera, float scale, in WRot lightSource, ImmutableArray<float> lightAmbientColor, ImmutableArray<float> lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadow) PaletteReference color, PaletteReference normals, PaletteReference shadow)
{ {
this.renderer = renderer; this.renderer = renderer;

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Orders; using OpenRA.Mods.Common.Orders;
@@ -87,7 +88,7 @@ namespace OpenRA.Mods.Cnc.Traits
[ActorReference(dictionaryReference: LintDictionaryReference.Keys)] [ActorReference(dictionaryReference: LintDictionaryReference.Keys)]
[Desc("Conditions to grant when disguised as specified actor.", [Desc("Conditions to grant when disguised as specified actor.",
"A dictionary of [actor id]: [condition].")] "A dictionary of [actor id]: [condition].")]
public readonly Dictionary<string, string> DisguisedAsConditions = []; public readonly FrozenDictionary<string, string> DisguisedAsConditions = FrozenDictionary<string, string>.Empty;
[CursorReference] [CursorReference]
[Desc("Cursor to display when hovering over a valid actor to disguise as.")] [Desc("Cursor to display when hovering over a valid actor to disguise as.")]

View File

@@ -9,7 +9,9 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Traits; using OpenRA.Traits;
@@ -20,7 +22,7 @@ namespace OpenRA.Mods.Cnc.Traits
sealed class LightPaletteRotatorInfo : TraitInfo sealed class LightPaletteRotatorInfo : TraitInfo
{ {
[Desc("Palettes this effect should not apply to.")] [Desc("Palettes this effect should not apply to.")]
public readonly HashSet<string> ExcludePalettes = []; public readonly FrozenSet<string> ExcludePalettes = FrozenSet<string>.Empty;
[Desc("'Speed' at which the effect cycles through palette indices.")] [Desc("'Speed' at which the effect cycles through palette indices.")]
public readonly float TimeStep = .5f; public readonly float TimeStep = .5f;
@@ -29,7 +31,7 @@ namespace OpenRA.Mods.Cnc.Traits
public readonly int ModifyIndex = 103; public readonly int ModifyIndex = 103;
[Desc("Palette indices to rotate through.")] [Desc("Palette indices to rotate through.")]
public readonly int[] RotationIndices = [230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 238, 237, 236, 235, 234, 233, 232, 231]; public readonly ImmutableArray<int> RotationIndices = [230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 238, 237, 236, 235, 234, 233, 232, 231];
public override object Create(ActorInitializer init) { return new LightPaletteRotator(this); } public override object Create(ActorInitializer init) { return new LightPaletteRotator(this); }
} }

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Graphics; using OpenRA.Mods.Cnc.Graphics;
@@ -52,8 +53,8 @@ namespace OpenRA.Mods.Cnc.Traits.Render
public readonly WAngle LightPitch = WAngle.FromDegrees(50); public readonly WAngle LightPitch = WAngle.FromDegrees(50);
public readonly WAngle LightYaw = WAngle.FromDegrees(240); public readonly WAngle LightYaw = WAngle.FromDegrees(240);
public readonly float[] LightAmbientColor = [0.6f, 0.6f, 0.6f]; public readonly ImmutableArray<float> LightAmbientColor = [0.6f, 0.6f, 0.6f];
public readonly float[] LightDiffuseColor = [0.4f, 0.4f, 0.4f]; public readonly ImmutableArray<float> LightDiffuseColor = [0.4f, 0.4f, 0.4f];
public override object Create(ActorInitializer init) { return new RenderVoxels(init.Self, this); } public override object Create(ActorInitializer init) { return new RenderVoxels(init.Self, this); }

View File

@@ -9,7 +9,9 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common; using OpenRA.Mods.Common;
@@ -24,10 +26,10 @@ namespace OpenRA.Mods.Cnc.Traits.Render
public class WithCargoInfo : TraitInfo, Requires<CargoInfo>, Requires<BodyOrientationInfo> public class WithCargoInfo : TraitInfo, Requires<CargoInfo>, Requires<BodyOrientationInfo>
{ {
[Desc("Cargo position relative to turret or body in (forward, right, up) triples. The default offset should be in the middle of the list.")] [Desc("Cargo position relative to turret or body in (forward, right, up) triples. The default offset should be in the middle of the list.")]
public readonly WVec[] LocalOffset = [WVec.Zero]; public readonly ImmutableArray<WVec> LocalOffset = [WVec.Zero];
[Desc("Passenger CargoType to display.")] [Desc("Passenger CargoType to display.")]
public readonly HashSet<string> DisplayTypes = []; public readonly FrozenSet<string> DisplayTypes = FrozenSet<string>.Empty;
public override object Create(ActorInitializer init) { return new WithCargo(init.Self, this); } public override object Create(ActorInitializer init) { return new WithCargo(init.Self, this); }
} }

View File

@@ -9,6 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Immutable;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.Common.Traits.Render; using OpenRA.Mods.Common.Traits.Render;
using OpenRA.Traits; using OpenRA.Traits;
@@ -18,7 +19,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render
public class WithHarvesterSpriteBodyInfo : WithFacingSpriteBodyInfo, Requires<HarvesterInfo> public class WithHarvesterSpriteBodyInfo : WithFacingSpriteBodyInfo, Requires<HarvesterInfo>
{ {
[Desc("Images switched between depending on fullness of harvester. Overrides RenderSprites.Image.")] [Desc("Images switched between depending on fullness of harvester. Overrides RenderSprites.Image.")]
public readonly string[] ImageByFullness = []; public readonly ImmutableArray<string> ImageByFullness = [];
public override object Create(ActorInitializer init) { return new WithHarvesterSpriteBody(init, this); } public override object Create(ActorInitializer init) { return new WithHarvesterSpriteBody(init, this); }
} }

View File

@@ -9,6 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
@@ -19,7 +20,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render
{ {
public class WithLandingCraftAnimationInfo : TraitInfo, Requires<IMoveInfo>, Requires<WithSpriteBodyInfo>, Requires<CargoInfo> public class WithLandingCraftAnimationInfo : TraitInfo, Requires<IMoveInfo>, Requires<WithSpriteBodyInfo>, Requires<CargoInfo>
{ {
public readonly HashSet<string> OpenTerrainTypes = ["Clear"]; public readonly FrozenSet<string> OpenTerrainTypes = new HashSet<string> { "Clear" }.ToFrozenSet();
[SequenceReference] [SequenceReference]
public readonly string OpenSequence = "open"; public readonly string OpenSequence = "open";

View File

@@ -10,7 +10,9 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Mods.Cnc.Effects; using OpenRA.Mods.Cnc.Effects;
@@ -26,7 +28,7 @@ namespace OpenRA.Mods.Cnc.Traits
[FieldLoader.Require] [FieldLoader.Require]
[Desc("Drop pod unit")] [Desc("Drop pod unit")]
[ActorReference([typeof(AircraftInfo), typeof(FallsToEarthInfo)])] [ActorReference([typeof(AircraftInfo), typeof(FallsToEarthInfo)])]
public readonly string[] UnitTypes = null; public readonly ImmutableArray<string> UnitTypes = default;
[Desc("Number of drop pods spawned.")] [Desc("Number of drop pods spawned.")]
public readonly int2 Drops = new(5, 8); public readonly int2 Drops = new(5, 8);
@@ -82,7 +84,7 @@ namespace OpenRA.Mods.Cnc.Traits
readonly DropPodsPowerInfo info; readonly DropPodsPowerInfo info;
readonly string[] unitTypes; readonly string[] unitTypes;
readonly Dictionary<string, Func<CPos, WPos>> getLaunchLocation = []; readonly Dictionary<string, Func<CPos, WPos>> getLaunchLocation = [];
readonly Dictionary<string, HashSet<string>> landableTerrainTypes = []; readonly Dictionary<string, FrozenSet<string>> landableTerrainTypes = [];
public DropPodsPower(Actor self, DropPodsPowerInfo info) public DropPodsPower(Actor self, DropPodsPowerInfo info)
: base(self, info) : base(self, info)

View File

@@ -9,6 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Immutable;
using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Traits; using OpenRA.Traits;
@@ -38,7 +39,7 @@ namespace OpenRA.Mods.Cnc.Traits
public readonly int Adjacency = 1; public readonly int Adjacency = 1;
[Desc("The range of time (in ticks) until the transformation starts.")] [Desc("The range of time (in ticks) until the transformation starts.")]
public readonly int[] Delay = [1000, 3000]; public readonly ImmutableArray<int> Delay = [1000, 3000];
public override object Create(ActorInitializer init) { return new TransformsNearResources(init.Self, this); } public override object Create(ActorInitializer init) { return new TransformsNearResources(init.Self, this); }
} }

View File

@@ -9,7 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Generic; using System.Collections.Frozen;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Traits; using OpenRA.Traits;
@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Cnc.Traits
public readonly short JumpjetTransitionCost = 0; public readonly short JumpjetTransitionCost = 0;
[Desc("The terrain types that this actor can transition on. Leave empty to allow any.")] [Desc("The terrain types that this actor can transition on. Leave empty to allow any.")]
public readonly HashSet<string> JumpjetTransitionTerrainTypes = []; public readonly FrozenSet<string> JumpjetTransitionTerrainTypes = FrozenSet<string>.Empty;
[Desc("Can this actor transition on slopes?")] [Desc("Can this actor transition on slopes?")]
public readonly bool JumpjetTransitionOnRamps = true; public readonly bool JumpjetTransitionOnRamps = true;

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -44,8 +45,8 @@ namespace OpenRA.Mods.Cnc.Traits
public sealed class ModelRenderer : IDisposable, IRenderer, INotifyActorDisposing public sealed class ModelRenderer : IDisposable, IRenderer, INotifyActorDisposing
{ {
// Static constants // Static constants
static readonly float[] ShadowDiffuse = [0, 0, 0]; static readonly ImmutableArray<float> ShadowDiffuse = [0, 0, 0];
static readonly float[] ShadowAmbient = [1, 1, 1]; static readonly ImmutableArray<float> ShadowAmbient = [1, 1, 1];
static readonly float2 SpritePadding = new(2, 2); static readonly float2 SpritePadding = new(2, 2);
static readonly float[] ZeroVector = [0, 0, 0, 1]; static readonly float[] ZeroVector = [0, 0, 0, 1];
static readonly float[] ZVector = [0, 0, 1, 1]; static readonly float[] ZVector = [0, 0, 1, 1];
@@ -94,7 +95,7 @@ namespace OpenRA.Mods.Cnc.Traits
public ModelRenderProxy RenderAsync( public ModelRenderProxy RenderAsync(
WorldRenderer wr, IEnumerable<ModelAnimation> models, in WRot camera, float scale, WorldRenderer wr, IEnumerable<ModelAnimation> models, in WRot camera, float scale,
in WRot groundOrientation, in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, in WRot groundOrientation, in WRot lightSource, ImmutableArray<float> lightAmbientColor, ImmutableArray<float> lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadowPalette) PaletteReference color, PaletteReference normals, PaletteReference shadowPalette)
{ {
if (!isInFrame) if (!isInFrame)
@@ -281,15 +282,15 @@ namespace OpenRA.Mods.Cnc.Traits
ModelRenderData renderData, ModelRenderData renderData,
IModelCache cache, IModelCache cache,
float[] t, float[] lightDirection, float[] t, float[] lightDirection,
float[] ambientLight, float[] diffuseLight, ImmutableArray<float> ambientLight, ImmutableArray<float> diffuseLight,
float colorPaletteTextureIndex, float normalsPaletteTextureIndex) float colorPaletteTextureIndex, float normalsPaletteTextureIndex)
{ {
shader.SetTexture("DiffuseTexture", renderData.Sheet.GetTexture()); shader.SetTexture("DiffuseTexture", renderData.Sheet.GetTexture());
shader.SetVec("Palettes", colorPaletteTextureIndex, normalsPaletteTextureIndex); shader.SetVec("Palettes", colorPaletteTextureIndex, normalsPaletteTextureIndex);
shader.SetMatrix("TransformMatrix", t); shader.SetMatrix("TransformMatrix", t);
shader.SetVec("LightDirection", lightDirection, 4); shader.SetVec("LightDirection", lightDirection, 4);
shader.SetVec("AmbientLight", ambientLight, 3); shader.SetVec("AmbientLight", ambientLight.AsMemory(), 3);
shader.SetVec("DiffuseLight", diffuseLight, 3); shader.SetVec("DiffuseLight", diffuseLight.AsMemory(), 3);
shader.PrepareRender(); shader.PrepareRender();
renderer.DrawBatch(cache.VertexBuffer, shader, renderData.Start, renderData.Count, PrimitiveType.TriangleList); renderer.DrawBatch(cache.VertexBuffer, shader, renderData.Start, renderData.Count, PrimitiveType.TriangleList);

View File

@@ -9,7 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Generic; using System.Collections.Frozen;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Traits; using OpenRA.Traits;
@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Cnc.Traits
[ActorReference] [ActorReference]
[Desc("Actor types that should be treated as veins for adjacency.")] [Desc("Actor types that should be treated as veins for adjacency.")]
public readonly HashSet<string> VeinholeActors = []; public readonly FrozenSet<string> VeinholeActors = FrozenSet<string>.Empty;
public override object Create(ActorInitializer init) { return new TSEditorResourceLayer(init.Self, this); } public override object Create(ActorInitializer init) { return new TSEditorResourceLayer(init.Self, this); }
} }

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -25,7 +26,7 @@ namespace OpenRA.Mods.Cnc.Traits
[ActorReference] [ActorReference]
[Desc("Actor types that should be treated as veins for adjacency.")] [Desc("Actor types that should be treated as veins for adjacency.")]
public readonly HashSet<string> VeinholeActors = []; public readonly FrozenSet<string> VeinholeActors = FrozenSet<string>.Empty;
public override object Create(ActorInitializer init) { return new TSResourceLayer(init.Self, this); } public override object Create(ActorInitializer init) { return new TSResourceLayer(init.Self, this); }
} }

View File

@@ -9,7 +9,9 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
@@ -22,16 +24,16 @@ namespace OpenRA.Mods.Cnc.Traits
public class TSTiberiumRendererInfo : ResourceRendererInfo public class TSTiberiumRendererInfo : ResourceRendererInfo
{ {
[Desc("Sequences to use for ramp type 1.", "Dictionary of [resource type]: [list of sequences].")] [Desc("Sequences to use for ramp type 1.", "Dictionary of [resource type]: [list of sequences].")]
public readonly Dictionary<string, string[]> Ramp1Sequences = []; public readonly FrozenDictionary<string, ImmutableArray<string>> Ramp1Sequences = FrozenDictionary<string, ImmutableArray<string>>.Empty;
[Desc("Sequences to use for ramp type 2.", "Dictionary of [resource type]: [list of sequences].")] [Desc("Sequences to use for ramp type 2.", "Dictionary of [resource type]: [list of sequences].")]
public readonly Dictionary<string, string[]> Ramp2Sequences = []; public readonly FrozenDictionary<string, ImmutableArray<string>> Ramp2Sequences = FrozenDictionary<string, ImmutableArray<string>>.Empty;
[Desc("Sequences to use for ramp type 3.", "Dictionary of [resource type]: [list of sequences].")] [Desc("Sequences to use for ramp type 3.", "Dictionary of [resource type]: [list of sequences].")]
public readonly Dictionary<string, string[]> Ramp3Sequences = []; public readonly FrozenDictionary<string, ImmutableArray<string>> Ramp3Sequences = FrozenDictionary<string, ImmutableArray<string>>.Empty;
[Desc("Sequences to use for ramp type 4.", "Dictionary of [resource type]: [list of sequences].")] [Desc("Sequences to use for ramp type 4.", "Dictionary of [resource type]: [list of sequences].")]
public readonly Dictionary<string, string[]> Ramp4Sequences = []; public readonly FrozenDictionary<string, ImmutableArray<string>> Ramp4Sequences = FrozenDictionary<string, ImmutableArray<string>>.Empty;
public override object Create(ActorInitializer init) { return new TSTiberiumRenderer(init.Self, this); } public override object Create(ActorInitializer init) { return new TSTiberiumRenderer(init.Self, this); }
} }
@@ -52,7 +54,7 @@ namespace OpenRA.Mods.Cnc.Traits
world = self.World; world = self.World;
} }
void LoadVariants(Dictionary<string, string[]> rampSequences, Dictionary<string, Dictionary<string, ISpriteSequence>> rampVariants) void LoadVariants(FrozenDictionary<string, ImmutableArray<string>> rampSequences, Dictionary<string, Dictionary<string, ISpriteSequence>> rampVariants)
{ {
var sequences = world.Map.Sequences; var sequences = world.Map.Sequences;
foreach (var kv in rampSequences) foreach (var kv in rampSequences)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -45,7 +46,7 @@ namespace OpenRA.Mods.Cnc.Traits
[ActorReference] [ActorReference]
[Desc("Actor types that should be treated as veins for adjacency.")] [Desc("Actor types that should be treated as veins for adjacency.")]
public readonly HashSet<string> VeinholeActors = []; public readonly FrozenSet<string> VeinholeActors = FrozenSet<string>.Empty;
void IMapPreviewSignatureInfo.PopulateMapPreviewSignatureCells(Map map, ActorInfo ai, ActorReference s, List<(MPos Uv, Color Color)> destinationBuffer) void IMapPreviewSignatureInfo.PopulateMapPreviewSignatureCells(Map map, ActorInfo ai, ActorReference s, List<(MPos Uv, Color Color)> destinationBuffer)
{ {

View File

@@ -10,7 +10,9 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common; using OpenRA.Mods.Common;
@@ -26,13 +28,13 @@ namespace OpenRA.Mods.Cnc.Traits
{ {
[FieldLoader.Require] [FieldLoader.Require]
[Desc("Resource types to animate.")] [Desc("Resource types to animate.")]
public readonly HashSet<string> Types = null; public readonly FrozenSet<string> Types = null;
[Desc("The percentage of resource cells to play the animation on.", "Use two values to randomize between them.")] [Desc("The percentage of resource cells to play the animation on.", "Use two values to randomize between them.")]
public readonly int[] Ratio = [1, 10]; public readonly ImmutableArray<int> Ratio = [1, 10];
[Desc("Tick interval between two animation spawning.", "Use two values to randomize between them.")] [Desc("Tick interval between two animation spawning.", "Use two values to randomize between them.")]
public readonly int[] Interval = [200, 500]; public readonly ImmutableArray<int> Interval = [200, 500];
[FieldLoader.Require] [FieldLoader.Require]
[Desc("Animation image.")] [Desc("Animation image.")]
@@ -40,7 +42,7 @@ namespace OpenRA.Mods.Cnc.Traits
[SequenceReference(nameof(Image))] [SequenceReference(nameof(Image))]
[Desc("Randomly select one of these sequences to render.")] [Desc("Randomly select one of these sequences to render.")]
public readonly string[] Sequences = ["idle"]; public readonly ImmutableArray<string> Sequences = ["idle"];
[PaletteReference] [PaletteReference]
[Desc("Animation palette.")] [Desc("Animation palette.")]

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -394,8 +395,8 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
switch (s.Key) switch (s.Key)
{ {
case "Allies": case "Allies":
pr.Allies = s.Value.Split(',').Intersect(players).Except(neutral).ToArray(); pr.Allies = s.Value.Split(',').Intersect(players).Except(neutral).ToImmutableArray();
pr.Enemies = s.Value.Split(',').SymmetricDifference(players).Except(neutral).ToArray(); pr.Enemies = s.Value.Split(',').SymmetricDifference(players).Except(neutral).ToImmutableArray();
break; break;
default: default:
Console.WriteLine("Ignoring unknown {0}={1} for player {2}", s.Key, s.Value, pr.Name); Console.WriteLine("Ignoring unknown {0}={1} for player {2}", s.Key, s.Value, pr.Name);

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -50,7 +51,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
Game.ModData = destModData; Game.ModData = destModData;
var destPaletteInfo = destModData.DefaultRules.Actors[SystemActors.Player].TraitInfo<PlayerColorPaletteInfo>(); var destPaletteInfo = destModData.DefaultRules.Actors[SystemActors.Player].TraitInfo<PlayerColorPaletteInfo>();
var destRemapIndex = destPaletteInfo.RemapIndex; var destRemapIndex = destPaletteInfo.RemapIndex;
var shadowIndex = Array.Empty<int>(); var shadowIndex = ImmutableArray<int>.Empty;
// the remap range is always 16 entries, but their location and order changes // the remap range is always 16 entries, but their location and order changes
for (var i = 0; i < 16; i++) for (var i = 0; i < 16; i++)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Immutable;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Graphics; using OpenRA.Mods.Cnc.Graphics;
using OpenRA.Mods.Cnc.Traits; using OpenRA.Mods.Cnc.Traits;
@@ -26,8 +27,8 @@ namespace OpenRA.Mods.Cnc.Widgets
public float Scale = 10f; public float Scale = 10f;
public int LightPitch = 142; public int LightPitch = 142;
public int LightYaw = 682; public int LightYaw = 682;
public float[] LightAmbientColor = [0.6f, 0.6f, 0.6f]; public ImmutableArray<float> LightAmbientColor = [0.6f, 0.6f, 0.6f];
public float[] LightDiffuseColor = [0.4f, 0.4f, 0.4f]; public ImmutableArray<float> LightDiffuseColor = [0.4f, 0.4f, 0.4f];
public WRot Rotation = WRot.None; public WRot Rotation = WRot.None;
public WAngle CameraAngle = WAngle.FromDegrees(40); public WAngle CameraAngle = WAngle.FromDegrees(40);
@@ -35,8 +36,8 @@ namespace OpenRA.Mods.Cnc.Widgets
public Func<string> GetPlayerPalette; public Func<string> GetPlayerPalette;
public Func<string> GetNormalsPalette; public Func<string> GetNormalsPalette;
public Func<string> GetShadowPalette; public Func<string> GetShadowPalette;
public Func<float[]> GetLightAmbientColor; public Func<ImmutableArray<float>> GetLightAmbientColor;
public Func<float[]> GetLightDiffuseColor; public Func<ImmutableArray<float>> GetLightDiffuseColor;
public Func<float> GetScale; public Func<float> GetScale;
public Func<int> GetLightPitch; public Func<int> GetLightPitch;
public Func<int> GetLightYaw; public Func<int> GetLightYaw;

View File

@@ -9,6 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
@@ -22,7 +23,7 @@ namespace OpenRA.Mods.Common
public static class AIUtils public static class AIUtils
{ {
public static bool IsAreaAvailable<T>(World world, Player player, Map map, int radius, HashSet<string> terrainTypes) public static bool IsAreaAvailable<T>(World world, Player player, Map map, int radius, FrozenSet<string> terrainTypes)
{ {
var cells = world.ActorsHavingTrait<T>().Where(a => a.Owner == player); var cells = world.ActorsHavingTrait<T>().Where(a => a.Owner == player);

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.Activities; using OpenRA.Activities;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.Common.Traits.Render; using OpenRA.Mods.Common.Traits.Render;
@@ -23,7 +24,7 @@ namespace OpenRA.Mods.Common.Activities
public readonly string ToActor; public readonly string ToActor;
public CVec Offset = CVec.Zero; public CVec Offset = CVec.Zero;
public WAngle Facing = new(384); public WAngle Facing = new(384);
public string[] Sounds = []; public ImmutableArray<string> Sounds = [];
public string Notification = null; public string Notification = null;
public string TextNotification = null; public string TextNotification = null;
public int ForceHealthPercentage = 0; public int ForceHealthPercentage = 0;

View File

@@ -9,13 +9,15 @@
*/ */
#endregion #endregion
using System.Collections.Immutable;
namespace OpenRA namespace OpenRA
{ {
public class AssetBrowser : IGlobalModData public class AssetBrowser : IGlobalModData
{ {
public readonly string[] SpriteExtensions = []; public readonly ImmutableArray<string> SpriteExtensions = [];
public readonly string[] ModelExtensions = []; public readonly ImmutableArray<string> ModelExtensions = [];
public readonly string[] AudioExtensions = []; public readonly ImmutableArray<string> AudioExtensions = [];
public readonly string[] VideoExtensions = []; public readonly ImmutableArray<string> VideoExtensions = [];
} }
} }

View File

@@ -9,6 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
@@ -234,13 +235,13 @@ namespace OpenRA.Mods.Common.Widgets
public string Text { get; } public string Text { get; }
readonly MarkerLayerOverlay markerLayerOverlay; readonly MarkerLayerOverlay markerLayerOverlay;
readonly ImmutableDictionary<int, ImmutableArray<CPos>> tiles; readonly FrozenDictionary<int, ImmutableArray<CPos>> tiles;
public ClearAllMarkerTilesEditorAction( public ClearAllMarkerTilesEditorAction(
MarkerLayerOverlay markerLayerOverlay) MarkerLayerOverlay markerLayerOverlay)
{ {
this.markerLayerOverlay = markerLayerOverlay; this.markerLayerOverlay = markerLayerOverlay;
tiles = markerLayerOverlay.Tiles.ToImmutableDictionary(t => t.Key, t => t.Value.ToImmutableArray()); tiles = markerLayerOverlay.Tiles.ToFrozenDictionary(t => t.Key, t => t.Value.ToImmutableArray());
var allTilesCount = tiles.Values.Sum(x => x.Length); var allTilesCount = tiles.Values.Sum(x => x.Length);
Text = FluentProvider.GetMessage(ClearedAllMarkerTiles, "count", allTilesCount); Text = FluentProvider.GetMessage(ClearedAllMarkerTiles, "count", allTilesCount);

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.Effects; using OpenRA.Effects;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -17,8 +18,8 @@ namespace OpenRA.Mods.Common.Effects
{ {
public sealed class FloatingSprite : IEffect, ISpatiallyPartitionable public sealed class FloatingSprite : IEffect, ISpatiallyPartitionable
{ {
readonly WDist[] speed; readonly ImmutableArray<WDist> speed;
readonly WDist[] gravity; readonly ImmutableArray<WDist> gravity;
readonly Animation anim; readonly Animation anim;
readonly bool visibleThroughFog; readonly bool visibleThroughFog;
@@ -32,8 +33,8 @@ namespace OpenRA.Mods.Common.Effects
int ticks; int ticks;
WAngle facing; WAngle facing;
public FloatingSprite(Actor emitter, string image, string[] sequences, string palette, bool isPlayerPalette, public FloatingSprite(Actor emitter, string image, ImmutableArray<string> sequences, string palette, bool isPlayerPalette,
int[] lifetime, WDist[] speed, WDist[] gravity, int turnRate, int randomRate, WPos pos, WAngle facing, ImmutableArray<int> lifetime, ImmutableArray<WDist> speed, ImmutableArray<WDist> gravity, int turnRate, int randomRate, WPos pos, WAngle facing,
bool visibleThroughFog = false) bool visibleThroughFog = false)
{ {
var world = emitter.World; var world = emitter.World;

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
namespace OpenRA.Mods.Common.FileSystem namespace OpenRA.Mods.Common.FileSystem
{ {
@@ -20,18 +21,52 @@ namespace OpenRA.Mods.Common.FileSystem
[Desc("Mod to use for content installation.")] [Desc("Mod to use for content installation.")]
public readonly string ContentInstallerMod = null; public readonly string ContentInstallerMod = null;
[FieldLoader.Require]
[Desc("A list of mod-provided packages. Anything required to display the initial load screen must be listed here.")] [Desc("A list of mod-provided packages. Anything required to display the initial load screen must be listed here.")]
public readonly Dictionary<string, string> SystemPackages = null; [FieldLoader.LoadUsing(nameof(LoadSystemPackages))]
public readonly ImmutableArray<KeyValuePair<string, string>> SystemPackages = default;
[Desc("A list of user-installed packages. If missing (and not marked as optional), these will trigger the content installer.")] [Desc("A list of user-installed packages. If missing (and not marked as optional), these will trigger the content installer.")]
public readonly Dictionary<string, string> ContentPackages = null; [FieldLoader.LoadUsing(nameof(LoadContentPackages))]
public readonly ImmutableArray<KeyValuePair<string, string>> ContentPackages = default;
[Desc("Files that aren't mounted as packages, but still need to trigger the content installer if missing.")] [Desc("Files that aren't mounted as packages, but still need to trigger the content installer if missing.")]
public readonly Dictionary<string, string> RequiredContentFiles = null; [FieldLoader.LoadUsing(nameof(LoadRequiredContentFiles))]
public readonly ImmutableArray<KeyValuePair<string, string>> RequiredContentFiles = default;
bool isContentAvailable = true; bool isContentAvailable = true;
static object LoadSystemPackages(MiniYaml yaml)
{
return LoadPackages(yaml, nameof(SystemPackages), true);
}
static object LoadContentPackages(MiniYaml yaml)
{
return LoadPackages(yaml, nameof(ContentPackages), false);
}
static object LoadRequiredContentFiles(MiniYaml yaml)
{
return LoadPackages(yaml, nameof(RequiredContentFiles), false);
}
static object LoadPackages(MiniYaml yaml, string key, bool required)
{
var packageNode = yaml.NodeWithKeyOrDefault(key);
if (packageNode == null)
{
if (required)
throw new FieldLoader.MissingFieldsException([key]);
return default(ImmutableArray<KeyValuePair<string, string>>);
}
var packages = new List<KeyValuePair<string, string>>(packageNode.Value.Nodes.Length);
foreach (var node in packageNode.Value.Nodes)
packages.Add(KeyValuePair.Create(node.Key, node.Value.Value));
return packages.ToImmutableArray();
}
public void Mount(OpenRA.FileSystem.FileSystem fileSystem, ObjectCreator objectCreator) public void Mount(OpenRA.FileSystem.FileSystem fileSystem, ObjectCreator objectCreator)
{ {
foreach (var kv in SystemPackages) foreach (var kv in SystemPackages)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.FileSystem namespace OpenRA.Mods.Common.FileSystem
@@ -23,7 +24,21 @@ namespace OpenRA.Mods.Common.FileSystem
public class DefaultFileSystemLoader : IFileSystemLoader public class DefaultFileSystemLoader : IFileSystemLoader
{ {
public readonly Dictionary<string, string> Packages = null; [FieldLoader.LoadUsing(nameof(LoadPackages))]
public readonly ImmutableArray<KeyValuePair<string, string>> Packages = default;
static object LoadPackages(MiniYaml yaml)
{
var packageNode = yaml.NodeWithKeyOrDefault(nameof(Packages));
if (packageNode == null)
return default(ImmutableArray<KeyValuePair<string, string>>);
var packages = new List<KeyValuePair<string, string>>(packageNode.Value.Nodes.Length);
foreach (var node in packageNode.Value.Nodes)
packages.Add(KeyValuePair.Create(node.Key, node.Value.Value));
return packages.ToImmutableArray();
}
public void Mount(OpenRA.FileSystem.FileSystem fileSystem, ObjectCreator objectCreator) public void Mount(OpenRA.FileSystem.FileSystem fileSystem, ObjectCreator objectCreator)
{ {

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -87,20 +88,17 @@ namespace OpenRA.Mods.Common.Graphics
public bool FlipY; public bool FlipY;
public float ZRamp; public float ZRamp;
public BlendMode BlendMode; public BlendMode BlendMode;
public int[] Frames; public ImmutableArray<int> Frames;
} }
protected readonly struct ReservationInfo protected readonly struct ReservationInfo
{ {
public readonly string Filename; public readonly string Filename;
public readonly List<int> LoadFrames; public readonly ImmutableArray<int> LoadFrames;
public readonly int[] Frames; public readonly ImmutableArray<int> Frames;
public readonly MiniYamlNode.SourceLocation Location; public readonly MiniYamlNode.SourceLocation Location;
public ReservationInfo(string filename, int[] loadFrames, int[] frames, MiniYamlNode.SourceLocation location) public ReservationInfo(string filename, ImmutableArray<int> loadFrames, ImmutableArray<int> frames, MiniYamlNode.SourceLocation location)
: this(filename, loadFrames?.ToList(), frames, location) { }
public ReservationInfo(string filename, List<int> loadFrames, int[] frames, MiniYamlNode.SourceLocation location)
{ {
Filename = filename; Filename = filename;
LoadFrames = loadFrames; LoadFrames = loadFrames;
@@ -150,7 +148,7 @@ namespace OpenRA.Mods.Common.Graphics
protected static readonly SpriteSequenceField<WDist> ShadowZOffset = new(nameof(ShadowZOffset), new WDist(-5)); protected static readonly SpriteSequenceField<WDist> ShadowZOffset = new(nameof(ShadowZOffset), new WDist(-5));
[Desc("The individual frames to play instead of going through them sequentially from the `Start`.")] [Desc("The individual frames to play instead of going through them sequentially from the `Start`.")]
protected static readonly SpriteSequenceField<int[]> Frames = new(nameof(Frames), null); protected static readonly SpriteSequenceField<ImmutableArray<int>> Frames = new(nameof(Frames), default);
[Desc("Don't apply terrain lighting or colored overlays.")] [Desc("Don't apply terrain lighting or colored overlays.")]
protected static readonly SpriteSequenceField<bool> IgnoreWorldTint = new(nameof(IgnoreWorldTint), false); protected static readonly SpriteSequenceField<bool> IgnoreWorldTint = new(nameof(IgnoreWorldTint), false);
@@ -181,7 +179,7 @@ namespace OpenRA.Mods.Common.Graphics
protected static readonly SpriteSequenceField<MiniYaml> Combine = new(nameof(Combine), null); protected static readonly SpriteSequenceField<MiniYaml> Combine = new(nameof(Combine), null);
[Desc("Sets transparency - use one value to set for all frames or provide a value for each frame.")] [Desc("Sets transparency - use one value to set for all frames or provide a value for each frame.")]
protected static readonly SpriteSequenceField<float[]> Alpha = new(nameof(Alpha), null); protected static readonly SpriteSequenceField<ImmutableArray<float>> Alpha = new(nameof(Alpha), default);
[Desc("Fade the animation from fully opaque on the first frame to fully transparent after the last frame.")] [Desc("Fade the animation from fully opaque on the first frame to fully transparent after the last frame.")]
protected static readonly SpriteSequenceField<bool> AlphaFade = new(nameof(AlphaFade), false); protected static readonly SpriteSequenceField<bool> AlphaFade = new(nameof(AlphaFade), false);
@@ -196,7 +194,7 @@ namespace OpenRA.Mods.Common.Graphics
protected static readonly SpriteSequenceField<float2> DepthSpriteOffset = new(nameof(DepthSpriteOffset), float2.Zero); protected static readonly SpriteSequenceField<float2> DepthSpriteOffset = new(nameof(DepthSpriteOffset), float2.Zero);
protected static readonly MiniYaml NoData = new(null); protected static readonly MiniYaml NoData = new(null);
protected static readonly int[] FirstFrame = [0]; protected static readonly ImmutableArray<int> FirstFrame = [0];
protected readonly ISpriteSequenceLoader Loader; protected readonly ISpriteSequenceLoader Loader;
@@ -220,7 +218,7 @@ namespace OpenRA.Mods.Common.Graphics
protected int shadowZOffset; protected int shadowZOffset;
protected bool ignoreWorldTint; protected bool ignoreWorldTint;
protected float scale; protected float scale;
protected float[] alpha; protected ImmutableArray<float> alpha;
protected bool alphaFade; protected bool alphaFade;
protected Rectangle? bounds; protected Rectangle? bounds;
@@ -296,12 +294,12 @@ namespace OpenRA.Mods.Common.Graphics
return Rectangle.FromLTRB(left, top, right, bottom); return Rectangle.FromLTRB(left, top, right, bottom);
} }
protected static List<int> CalculateFrameIndices( protected static ImmutableArray<int> CalculateFrameIndices(
int start, int? length, int stride, int facings, int[] frames, bool transpose, bool reverseFacings, int shadowStart) int start, int? length, int stride, int facings, ImmutableArray<int> frames, bool transpose, bool reverseFacings, int shadowStart)
{ {
// Request all frames // Request all frames
if (length == null) if (length == null)
return null; return default;
// Only request the subset of frames that we actually need // Only request the subset of frames that we actually need
var usedFrames = new List<int>(); var usedFrames = new List<int>();
@@ -313,7 +311,7 @@ namespace OpenRA.Mods.Common.Graphics
var i = transpose ? frame * facings + facingInner : var i = transpose ? frame * facings + facingInner :
facingInner * stride + frame; facingInner * stride + frame;
usedFrames.Add(frames?[i] ?? start + i); usedFrames.Add(frames != null ? frames[i] : start + i);
} }
} }
@@ -325,10 +323,10 @@ namespace OpenRA.Mods.Common.Graphics
usedFrames.Add(usedFrames[i] + shadowOffset); usedFrames.Add(usedFrames[i] + shadowOffset);
} }
return usedFrames; return usedFrames.ToImmutableArray();
} }
protected virtual IEnumerable<ReservationInfo> ParseFilenames(ModData modData, string tileset, int[] frames, MiniYaml data, MiniYaml defaults) protected virtual IEnumerable<ReservationInfo> ParseFilenames(ModData modData, string tileset, ImmutableArray<int> frames, MiniYaml data, MiniYaml defaults)
{ {
var filenamePatternNode = data.NodeWithKeyOrDefault(FilenamePattern.Key) ?? defaults.NodeWithKeyOrDefault(FilenamePattern.Key); var filenamePatternNode = data.NodeWithKeyOrDefault(FilenamePattern.Key) ?? defaults.NodeWithKeyOrDefault(FilenamePattern.Key);
if (!string.IsNullOrEmpty(filenamePatternNode?.Value.Value)) if (!string.IsNullOrEmpty(filenamePatternNode?.Value.Value))
@@ -347,14 +345,14 @@ namespace OpenRA.Mods.Common.Graphics
return [new ReservationInfo(filename, loadFrames, frames, location)]; return [new ReservationInfo(filename, loadFrames, frames, location)];
} }
protected virtual IEnumerable<ReservationInfo> ParseCombineFilenames(ModData modData, string tileset, int[] frames, MiniYaml data) protected virtual IEnumerable<ReservationInfo> ParseCombineFilenames(ModData modData, string tileset, ImmutableArray<int> frames, MiniYaml data)
{ {
var filename = LoadField(Filename, data, null, out var location); var filename = LoadField(Filename, data, null, out var location);
if (frames == null && LoadField<string>(Length.Key, null, data) != "*") if (frames == null && LoadField<string>(Length.Key, null, data) != "*")
{ {
var subStart = LoadField("Start", 0, data); var subStart = LoadField("Start", 0, data);
var subLength = LoadField("Length", 1, data); var subLength = LoadField("Length", 1, data);
frames = Exts.MakeArray(subLength, i => subStart + i); frames = Exts.MakeArray(subLength, i => subStart + i).ToImmutableArray();
} }
yield return new ReservationInfo(filename, frames, frames, location); yield return new ReservationInfo(filename, frames, frames, location);
@@ -522,24 +520,25 @@ namespace OpenRA.Mods.Common.Graphics
if (alpha != null) if (alpha != null)
{ {
if (alpha.Length == 1) if (alpha.Length == 1)
alpha = Exts.MakeArray(length.Value, _ => alpha[0]); alpha = Exts.MakeArray(length.Value, _ => alpha[0]).ToImmutableArray();
else if (alpha.Length != length.Value) else if (alpha.Length != length.Value)
throw new YamlException($"Sequence {image}.{Name} must define either 1 or {length.Value} Alpha values."); throw new YamlException($"Sequence {image}.{Name} must define either 1 or {length.Value} Alpha values.");
} }
else if (alphaFade) else if (alphaFade)
alpha = Exts.MakeArray(length.Value, i => float2.Lerp(1f, 0f, i / (length.Value - 1f))); alpha = Exts.MakeArray(length.Value, i => float2.Lerp(1f, 0f, i / (length.Value - 1f))).ToImmutableArray();
// Reindex sprites to order facings anti-clockwise and remove unused frames // Reindex sprites to order facings anti-clockwise and remove unused frames
var index = CalculateFrameIndices(start, length.Value, stride ?? length.Value, facings, null, transpose, reverseFacings, -1); var index = CalculateFrameIndices(start, length.Value, stride ?? length.Value, facings, default, transpose, reverseFacings, -1);
if (reverses) if (reverses)
{ {
index.AddRange(index.Skip(1).Take(length.Value - 2).Reverse()); index = index.AddRange(index.Skip(1).Take(length.Value - 2).Reverse());
alpha = alpha?.Concat(alpha.Skip(1).Take(length.Value - 2).Reverse()).ToArray(); if (alpha != null)
alpha = alpha.AddRange(alpha.Skip(1).Take(length.Value - 2).Reverse());
length = 2 * length - 2; length = 2 * length - 2;
} }
if (index.Count == 0) if (index.Length == 0)
throw new YamlException($"Sequence {image}.{Name} does not define any frames."); throw new YamlException($"Sequence {image}.{Name} does not define any frames.");
var minIndex = index.Min(); var minIndex = index.Min();
@@ -610,7 +609,7 @@ namespace OpenRA.Mods.Common.Graphics
public virtual float GetAlpha(int frame) public virtual float GetAlpha(int frame)
{ {
return alpha?[frame] ?? 1f; return alpha != null ? alpha[frame] : 1f;
} }
protected virtual float GetScale() protected virtual float GetScale()

View File

@@ -9,7 +9,9 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -31,15 +33,15 @@ namespace OpenRA.Mods.Common.Graphics
public class TilesetSpecificSpriteSequence : DefaultSpriteSequence public class TilesetSpecificSpriteSequence : DefaultSpriteSequence
{ {
[Desc("Dictionary of <tileset name>: filename to override the Filename key.")] [Desc("Dictionary of <tileset name>: filename to override the Filename key.")]
static readonly SpriteSequenceField<Dictionary<string, string>> TilesetFilenames = new(nameof(TilesetFilenames), null); static readonly SpriteSequenceField<FrozenDictionary<string, string>> TilesetFilenames = new(nameof(TilesetFilenames), null);
[Desc("Dictionary of <tileset name>: <filename pattern> to override the FilenamePattern key.")] [Desc("Dictionary of <tileset name>: <filename pattern> to override the FilenamePattern key.")]
static readonly SpriteSequenceField<Dictionary<string, string>> TilesetFilenamesPattern = new(nameof(TilesetFilenamesPattern), null); static readonly SpriteSequenceField<FrozenDictionary<string, string>> TilesetFilenamesPattern = new(nameof(TilesetFilenamesPattern), null);
public TilesetSpecificSpriteSequence(SpriteCache cache, ISpriteSequenceLoader loader, string image, string sequence, MiniYaml data, MiniYaml defaults) public TilesetSpecificSpriteSequence(SpriteCache cache, ISpriteSequenceLoader loader, string image, string sequence, MiniYaml data, MiniYaml defaults)
: base(cache, loader, image, sequence, data, defaults) { } : base(cache, loader, image, sequence, data, defaults) { }
protected override IEnumerable<ReservationInfo> ParseFilenames(ModData modData, string tileset, int[] frames, MiniYaml data, MiniYaml defaults) protected override IEnumerable<ReservationInfo> ParseFilenames(ModData modData, string tileset, ImmutableArray<int> frames, MiniYaml data, MiniYaml defaults)
{ {
var tilesetFilenamesPatternNode = data.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key) ?? defaults.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key); var tilesetFilenamesPatternNode = data.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key) ?? defaults.NodeWithKeyOrDefault(TilesetFilenamesPattern.Key);
if (tilesetFilenamesPatternNode != null) if (tilesetFilenamesPatternNode != null)
@@ -69,7 +71,7 @@ namespace OpenRA.Mods.Common.Graphics
return base.ParseFilenames(modData, tileset, frames, data, defaults); return base.ParseFilenames(modData, tileset, frames, data, defaults);
} }
protected override IEnumerable<ReservationInfo> ParseCombineFilenames(ModData modData, string tileset, int[] frames, MiniYaml data) protected override IEnumerable<ReservationInfo> ParseCombineFilenames(ModData modData, string tileset, ImmutableArray<int> frames, MiniYaml data)
{ {
var node = data.NodeWithKeyOrDefault(TilesetFilenames.Key); var node = data.NodeWithKeyOrDefault(TilesetFilenames.Key);
if (node != null) if (node != null)
@@ -81,7 +83,7 @@ namespace OpenRA.Mods.Common.Graphics
{ {
var subStart = LoadField("Start", 0, data); var subStart = LoadField("Start", 0, data);
var subLength = LoadField("Length", 1, data); var subLength = LoadField("Length", 1, data);
frames = Exts.MakeArray(subLength, i => subStart + i); frames = Exts.MakeArray(subLength, i => subStart + i).ToImmutableArray();
} }
return [new ReservationInfo(tilesetNode.Value.Value, frames, frames, tilesetNode.Location)]; return [new ReservationInfo(tilesetNode.Value.Value, frames, frames, tilesetNode.Location)];

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics; using OpenRA.Mods.Common.Graphics;
@@ -24,7 +25,7 @@ namespace OpenRA.Mods.Common.HitShapes
public WDist OuterRadius { get; private set; } public WDist OuterRadius { get; private set; }
[FieldLoader.Require] [FieldLoader.Require]
public readonly int2[] Points; public readonly ImmutableArray<int2> Points;
[Desc("Defines the top offset relative to the actor's center.")] [Desc("Defines the top offset relative to the actor's center.")]
public readonly int VerticalTopOffset = 0; public readonly int VerticalTopOffset = 0;
@@ -42,7 +43,7 @@ namespace OpenRA.Mods.Common.HitShapes
public PolygonShape() { } public PolygonShape() { }
public PolygonShape(int2[] points) { Points = points; } public PolygonShape(ImmutableArray<int2> points) { Points = points; }
public void Initialize() public void Initialize()
{ {

View File

@@ -100,7 +100,7 @@ namespace OpenRA.Mods.Common.Lint
// Logic classes can declare the data key names that specify hotkeys. // Logic classes can declare the data key names that specify hotkeys.
if (node.Key == "Logic" && node.Value.Nodes.Length > 0) if (node.Key == "Logic" && node.Value.Nodes.Length > 0)
{ {
var typeNames = FieldLoader.GetValue<string[]>(node.Key, node.Value.Value); var typeNames = FieldLoader.GetValue<ImmutableArray<string>>(node.Key, node.Value.Value);
var checkArgKeys = new List<string>(); var checkArgKeys = new List<string>();
foreach (var typeName in typeNames) foreach (var typeName in typeNames)
{ {

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.Widgets; using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
@@ -32,7 +33,7 @@ namespace OpenRA.Mods.Common.Lint
if (node.Key == "Logic") if (node.Key == "Logic")
{ {
var typeNames = FieldLoader.GetValue<string[]>(node.Key, node.Value.Value); var typeNames = FieldLoader.GetValue<ImmutableArray<string>>(node.Key, node.Value.Value);
foreach (var typeName in typeNames) foreach (var typeName in typeNames)
{ {
var type = Game.ModData.ObjectCreator.FindType(typeName); var type = Game.ModData.ObjectCreator.FindType(typeName);

View File

@@ -12,6 +12,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -39,7 +40,7 @@ namespace OpenRA.Mods.Common.Lint
foreach (var context in usedKeys.EmptyKeyContexts) foreach (var context in usedKeys.EmptyKeyContexts)
emitWarning($"Empty key in map ftl files required by {context}"); emitWarning($"Empty key in map ftl files required by {context}");
var mapMessages = FieldLoader.GetValue<string[]>("value", map.FluentMessageDefinitions.Value); var mapMessages = FieldLoader.GetValue<ImmutableArray<string>>("value", map.FluentMessageDefinitions.Value);
var modMessages = modData.Manifest.FluentMessages; var modMessages = modData.Manifest.FluentMessages;
// For maps we don't warn on unused keys. They might be unused on *this* map, // For maps we don't warn on unused keys. They might be unused on *this* map,
@@ -363,7 +364,7 @@ namespace OpenRA.Mods.Common.Lint
{ {
if (childNode.Key == "Logic") if (childNode.Key == "Logic")
{ {
foreach (var logicName in FieldLoader.GetValue<string[]>(childNode.Key, childNode.Value.Value)) foreach (var logicName in FieldLoader.GetValue<ImmutableArray<string>>(childNode.Key, childNode.Value.Value))
{ {
var logicType = modData.ObjectCreator.FindType(logicName); var logicType = modData.ObjectCreator.FindType(logicName);
if (logicType == null) if (logicType == null)

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using Linguini.Syntax.Ast; using Linguini.Syntax.Ast;
using Linguini.Syntax.Parser; using Linguini.Syntax.Parser;
@@ -25,7 +26,7 @@ namespace OpenRA.Mods.Common.Lint
if (map.FluentMessageDefinitions == null) if (map.FluentMessageDefinitions == null)
return; return;
Run(emitError, emitWarning, map, FieldLoader.GetValue<string[]>("value", map.FluentMessageDefinitions.Value)); Run(emitError, emitWarning, map, FieldLoader.GetValue<ImmutableArray<string>>("value", map.FluentMessageDefinitions.Value));
} }
void ILintPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData) void ILintPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -60,7 +61,7 @@ namespace OpenRA.Mods.Common.Lint
} }
} }
static bool HasInvalidBounds(WDist[] bounds, Size tileSize, int tileScale) static bool HasInvalidBounds(ImmutableArray<WDist> bounds, Size tileSize, int tileScale)
{ {
if (bounds == null) if (bounds == null)
return false; return false;

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Immutable;
using OpenRA.Server; using OpenRA.Server;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
@@ -26,7 +27,7 @@ namespace OpenRA.Mods.Common.Lint
Run(emitError, map.MapFormat, map.Author, map.Title, map.Categories); Run(emitError, map.MapFormat, map.Author, map.Title, map.Categories);
} }
static void Run(Action<string> emitError, int mapFormat, string author, string title, string[] categories) static void Run(Action<string> emitError, int mapFormat, string author, string title, ImmutableArray<string> categories)
{ {
if (mapFormat < Map.SupportedMapFormat) if (mapFormat < Map.SupportedMapFormat)
emitError($"Map format `{mapFormat}` does not match the supported version `{Map.CurrentMapFormat}`."); emitError($"Map format `{mapFormat}` does not match the supported version `{Map.CurrentMapFormat}`.");

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server; using OpenRA.Server;
@@ -30,7 +31,7 @@ namespace OpenRA.Mods.Common.Lint
spawns.Add(s.Get<LocationInit>().Value); spawns.Add(s.Get<LocationInit>().Value);
} }
Run(emitError, emitWarning, players, map.Visibility, map.Rules.Actors[SystemActors.World], spawns.ToArray()); Run(emitError, emitWarning, players, map.Visibility, map.Rules.Actors[SystemActors.World], spawns.ToImmutableArray());
} }
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules) void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
@@ -39,7 +40,7 @@ namespace OpenRA.Mods.Common.Lint
} }
static void Run(Action<string> emitError, Action<string> emitWarning, static void Run(Action<string> emitError, Action<string> emitWarning,
MapPlayers players, MapVisibility visibility, ActorInfo worldActorInfo, CPos[] spawnPoints) MapPlayers players, MapVisibility visibility, ActorInfo worldActorInfo, ImmutableArray<CPos> spawnPoints)
{ {
if (players.Players.Count > 64) if (players.Players.Count > 64)
emitError("Defining more than 64 players is not allowed."); emitError("Defining more than 64 players is not allowed.");

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.FileSystem; using OpenRA.FileSystem;
using OpenRA.Server; using OpenRA.Server;
@@ -94,7 +95,7 @@ namespace OpenRA.Mods.Common.Lint
if (ruleDefinitions == null) if (ruleDefinitions == null)
return; return;
var mapFiles = FieldLoader.GetValue<string[]>("value", ruleDefinitions.Value); var mapFiles = FieldLoader.GetValue<ImmutableArray<string>>("value", ruleDefinitions.Value);
foreach (var f in mapFiles) foreach (var f in mapFiles)
CheckActors(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, modData); CheckActors(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, modData);

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.FileSystem; using OpenRA.FileSystem;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Server; using OpenRA.Server;
@@ -115,7 +116,7 @@ namespace OpenRA.Mods.Common.Lint
if (weaponDefinitions == null) if (weaponDefinitions == null)
return; return;
var mapFiles = FieldLoader.GetValue<string[]>("value", weaponDefinitions.Value); var mapFiles = FieldLoader.GetValue<ImmutableArray<string>>("value", weaponDefinitions.Value);
foreach (var f in mapFiles) foreach (var f in mapFiles)
CheckWeapons(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, emitWarning, modData); CheckWeapons(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, emitWarning, modData);

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.FileSystem; using OpenRA.FileSystem;
using OpenRA.Mods.Common.UpdateRules; using OpenRA.Mods.Common.UpdateRules;
@@ -47,8 +48,8 @@ namespace OpenRA.Mods.Common.Lint
var files = modData.Manifest.Rules.AsEnumerable(); var files = modData.Manifest.Rules.AsEnumerable();
if (ruleDefinitions.Value != null) if (ruleDefinitions.Value != null)
{ {
var mapFiles = FieldLoader.GetValue<string[]>("value", ruleDefinitions.Value); var mapFiles = FieldLoader.GetValue<ImmutableArray<string>>("value", ruleDefinitions.Value);
files = files.Append(mapFiles); files = files.Concat(mapFiles);
} }
var nodes = new List<MiniYamlNode>(); var nodes = new List<MiniYamlNode>();

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
@@ -85,16 +86,16 @@ namespace OpenRA.Mods.Common.MapGenerator
public class MapGeneratorDropdownChoice public class MapGeneratorDropdownChoice
{ {
public readonly string Label = null; public readonly string Label = null;
public readonly string[] Tileset = null; public readonly ImmutableArray<string> Tileset = default;
public readonly int[] Players = null; public readonly ImmutableArray<int> Players = default;
[FieldLoader.LoadUsing(nameof(LoadSettings))] [FieldLoader.LoadUsing(nameof(LoadSettings))]
[FieldLoader.Require] [FieldLoader.Require]
public readonly ImmutableList<MiniYamlNode> Settings = null; public readonly ImmutableArray<MiniYamlNode> Settings = default;
static ImmutableList<MiniYamlNode> LoadSettings(MiniYaml yaml) static object LoadSettings(MiniYaml yaml)
{ {
return yaml.NodeWithKey("Settings").Value.Nodes.ToImmutableList(); return yaml.NodeWithKey("Settings").Value.Nodes.ToImmutableArray();
} }
} }
@@ -114,7 +115,7 @@ namespace OpenRA.Mods.Common.MapGenerator
return ret; return ret;
} }
public readonly string[] Default = null; public readonly ImmutableArray<string> Default = default;
string value = null; string value = null;
public MapGeneratorMultiChoiceOption(string id, MiniYaml yaml) public MapGeneratorMultiChoiceOption(string id, MiniYaml yaml)
@@ -126,7 +127,7 @@ namespace OpenRA.Mods.Common.MapGenerator
if (validChoices.Contains(value)) if (validChoices.Contains(value))
return Choices[value].Settings; return Choices[value].Settings;
var fallback = Default?.FirstOrDefault(validChoices.Contains) ?? validChoices.FirstOrDefault(); var fallback = Default != null ? Default.FirstOrDefault(validChoices.Contains) : validChoices.FirstOrDefault();
return fallback != null ? Choices[fallback].Settings : []; return fallback != null ? Choices[fallback].Settings : [];
} }
@@ -146,8 +147,8 @@ namespace OpenRA.Mods.Common.MapGenerator
{ {
return Choices return Choices
.Where(kv => .Where(kv =>
(kv.Value.Tileset?.Contains(terrainInfo.Id) ?? true) && (kv.Value.Tileset == null || kv.Value.Tileset.Contains(terrainInfo.Id)) &&
(kv.Value.Players?.Contains(playerCount) ?? true)) (kv.Value.Players == null || kv.Value.Players.Contains(playerCount)))
.Select(kv => kv.Key) .Select(kv => kv.Key)
.ToList(); .ToList();
} }
@@ -177,7 +178,7 @@ namespace OpenRA.Mods.Common.MapGenerator
public readonly string Parameter = null; public readonly string Parameter = null;
[FieldLoader.Require] [FieldLoader.Require]
public readonly int[] Choices = null; public readonly ImmutableArray<int> Choices = default;
public readonly int? Default = null; public readonly int? Default = null;
int value; int value;
@@ -185,7 +186,7 @@ namespace OpenRA.Mods.Common.MapGenerator
public MapGeneratorMultiIntegerChoiceOption(string id, MiniYaml yaml) public MapGeneratorMultiIntegerChoiceOption(string id, MiniYaml yaml)
: base(id, yaml) : base(id, yaml)
{ {
Value = Default ?? Choices?.First() ?? 0; Value = Default ?? (Choices != null ? Choices[0] : 0);
} }
public int Value public int Value
@@ -210,7 +211,7 @@ namespace OpenRA.Mods.Common.MapGenerator
{ {
sealed class MapGenerationArgsWithOptions : MapGenerationArgs sealed class MapGenerationArgsWithOptions : MapGenerationArgs
{ {
public Dictionary<string, string> Options = []; public FrozenDictionary<string, string> Options = FrozenDictionary<string, string>.Empty;
} }
readonly IMapGeneratorInfo generatorInfo; readonly IMapGeneratorInfo generatorInfo;
@@ -218,6 +219,8 @@ namespace OpenRA.Mods.Common.MapGenerator
public MapGeneratorSettings(IMapGeneratorInfo generatorInfo, MiniYaml yaml) public MapGeneratorSettings(IMapGeneratorInfo generatorInfo, MiniYaml yaml)
{ {
this.generatorInfo = generatorInfo; this.generatorInfo = generatorInfo;
var options = new List<MapGeneratorOption>();
foreach (var node in yaml.Nodes) foreach (var node in yaml.Nodes)
{ {
var split = node.Key.Split('@'); var split = node.Key.Split('@');
@@ -225,17 +228,19 @@ namespace OpenRA.Mods.Common.MapGenerator
continue; continue;
if (split[0] == "BooleanOption") if (split[0] == "BooleanOption")
Options.Add(new MapGeneratorBooleanOption(split[1], node.Value)); options.Add(new MapGeneratorBooleanOption(split[1], node.Value));
else if (split[0] == "IntegerOption") else if (split[0] == "IntegerOption")
Options.Add(new MapGeneratorIntegerOption(split[1], node.Value)); options.Add(new MapGeneratorIntegerOption(split[1], node.Value));
else if (split[0] == "MultiIntegerChoiceOption") else if (split[0] == "MultiIntegerChoiceOption")
Options.Add(new MapGeneratorMultiIntegerChoiceOption(split[1], node.Value)); options.Add(new MapGeneratorMultiIntegerChoiceOption(split[1], node.Value));
else if (split[0] == "MultiChoiceOption") else if (split[0] == "MultiChoiceOption")
Options.Add(new MapGeneratorMultiChoiceOption(split[1], node.Value)); options.Add(new MapGeneratorMultiChoiceOption(split[1], node.Value));
} }
Options = options.ToImmutableArray();
} }
public List<MapGeneratorOption> Options { get; } = []; public ImmutableArray<MapGeneratorOption> Options { get; } = [];
public void Randomize(MersenneTwister random) public void Randomize(MersenneTwister random)
{ {
@@ -306,7 +311,7 @@ namespace OpenRA.Mods.Common.MapGenerator
Title = FluentProvider.GetMessage(generatorInfo.MapTitle), Title = FluentProvider.GetMessage(generatorInfo.MapTitle),
Author = FluentProvider.GetMessage(generatorInfo.Name), Author = FluentProvider.GetMessage(generatorInfo.Name),
Settings = new MiniYaml(null, MiniYaml.Merge(layers)), Settings = new MiniYaml(null, MiniYaml.Merge(layers)),
Options = options Options = options.ToFrozenDictionary()
}; };
} }
} }

View File

@@ -1951,7 +1951,7 @@ namespace OpenRA.Mods.Common.MapGenerator
var allowedTerrainResourceCombos = resourceTypes var allowedTerrainResourceCombos = resourceTypes
.SelectMany(resourceTypeInfo => resourceTypeInfo.AllowedTerrainTypes .SelectMany(resourceTypeInfo => resourceTypeInfo.AllowedTerrainTypes
.Select(terrainName => (resourceTypeInfo, terrainInfo.GetTerrainIndex(terrainName)))) .Select(terrainName => (resourceTypeInfo, terrainInfo.GetTerrainIndex(terrainName))))
.ToImmutableHashSet(); .ToHashSet();
var strengths = new Dictionary<ResourceTypeInfo, CellLayer<int>>(); var strengths = new Dictionary<ResourceTypeInfo, CellLayer<int>>();
foreach (var resourceType in resourceTypes) foreach (var resourceType in resourceTypes)

View File

@@ -597,10 +597,9 @@ namespace OpenRA.Mods.Common.MapGenerator
var pathStart = points[0]; var pathStart = points[0];
var pathEnd = points[^1]; var pathEnd = points[^1];
var orderedPermittedBrushes = Brushes.All.ToImmutableArray(); var orderedPermittedBrushes = Brushes.All.ToImmutableArray();
var permittedBrushes = orderedPermittedBrushes.ToImmutableHashSet(); var permittedStartBrushes = Brushes.Start.ToHashSet();
var permittedStartBrushes = Brushes.Start.ToImmutableHashSet(); var permittedInnerBrushes = Brushes.Inner.ToHashSet();
var permittedInnerBrushes = Brushes.Inner.ToImmutableHashSet(); var permittedEndBrushes = Brushes.End.ToHashSet();
var permittedEndBrushes = Brushes.End.ToImmutableHashSet();
const int MaxCost = int.MaxValue; const int MaxCost = int.MaxValue;
var segmentTypeToId = new Dictionary<string, int>(); var segmentTypeToId = new Dictionary<string, int>();

View File

@@ -9,6 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.IO; using System.IO;
@@ -24,8 +25,8 @@ namespace OpenRA.Mods.Common
[FluentReference] [FluentReference]
public readonly string Title; public readonly string Title;
public readonly string Identifier; public readonly string Identifier;
public readonly string[] TestFiles = []; public readonly ImmutableArray<string> TestFiles = [];
public readonly string[] Sources = []; public readonly ImmutableArray<string> Sources = [];
public readonly bool Required; public readonly bool Required;
public readonly string Download; public readonly string Download;
@@ -46,7 +47,7 @@ namespace OpenRA.Mods.Common
public readonly MiniYaml Type; public readonly MiniYaml Type;
// Used to find installation locations for SourceType.Install // Used to find installation locations for SourceType.Install
public readonly string[] RegistryPrefixes = [string.Empty]; public readonly ImmutableArray<string> RegistryPrefixes = [string.Empty];
public readonly string RegistryKey; public readonly string RegistryKey;
public readonly string RegistryValue; public readonly string RegistryValue;
@@ -87,7 +88,7 @@ namespace OpenRA.Mods.Common
public readonly string MirrorList; public readonly string MirrorList;
public readonly string SHA1; public readonly string SHA1;
public readonly string Type; public readonly string Type;
public readonly Dictionary<string, string> Extract; public readonly FrozenDictionary<string, string> Extract;
public ModDownload(MiniYaml yaml) public ModDownload(MiniYaml yaml)
{ {
@@ -103,35 +104,35 @@ namespace OpenRA.Mods.Common
[IncludeFluentReferences(LintDictionaryReference.Values)] [IncludeFluentReferences(LintDictionaryReference.Values)]
[FieldLoader.LoadUsing(nameof(LoadPackages))] [FieldLoader.LoadUsing(nameof(LoadPackages))]
public readonly Dictionary<string, ModPackage> Packages = []; public readonly ImmutableArray<KeyValuePair<string, ModPackage>> Packages = [];
static object LoadPackages(MiniYaml yaml) static object LoadPackages(MiniYaml yaml)
{ {
var packages = new Dictionary<string, ModPackage>(); var packages = new List<KeyValuePair<string, ModPackage>>();
var packageNode = yaml.NodeWithKeyOrDefault("Packages"); var packageNode = yaml.NodeWithKeyOrDefault("Packages");
if (packageNode != null) if (packageNode != null)
foreach (var node in packageNode.Value.Nodes) foreach (var node in packageNode.Value.Nodes)
packages.Add(node.Key, new ModPackage(node.Value)); packages.Add(KeyValuePair.Create(node.Key, new ModPackage(node.Value)));
return packages; return packages.ToImmutableArray();
} }
[FieldLoader.LoadUsing(nameof(LoadDownloads))] [FieldLoader.LoadUsing(nameof(LoadDownloads))]
public readonly string[] Downloads = []; public readonly ImmutableArray<string> Downloads = [];
static object LoadDownloads(MiniYaml yaml) static object LoadDownloads(MiniYaml yaml)
{ {
var downloadNode = yaml.NodeWithKeyOrDefault("Downloads"); var downloadNode = yaml.NodeWithKeyOrDefault("Downloads");
return downloadNode != null ? downloadNode.Value.Nodes.Select(n => n.Key).ToArray() : []; return downloadNode != null ? downloadNode.Value.Nodes.Select(n => n.Key).ToImmutableArray() : [];
} }
[FieldLoader.LoadUsing(nameof(LoadSources))] [FieldLoader.LoadUsing(nameof(LoadSources))]
public readonly string[] Sources = []; public readonly ImmutableArray<string> Sources = [];
static object LoadSources(MiniYaml yaml) static object LoadSources(MiniYaml yaml)
{ {
var sourceNode = yaml.NodeWithKeyOrDefault("Sources"); var sourceNode = yaml.NodeWithKeyOrDefault("Sources");
return sourceNode != null ? sourceNode.Value.Nodes.Select(n => n.Key).ToArray() : []; return sourceNode != null ? sourceNode.Value.Nodes.Select(n => n.Key).ToImmutableArray() : [];
} }
} }
} }

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -25,7 +26,7 @@ namespace OpenRA.Mods.Common.Projectiles
public class AreaBeamInfo : IProjectileInfo public class AreaBeamInfo : IProjectileInfo
{ {
[Desc("Projectile speed in WDist / tick, two values indicate a randomly picked velocity per beam.")] [Desc("Projectile speed in WDist / tick, two values indicate a randomly picked velocity per beam.")]
public readonly WDist[] Speed = [new(128)]; public readonly ImmutableArray<WDist> Speed = [new(128)];
[Desc("The maximum duration (in ticks) of each beam burst.")] [Desc("The maximum duration (in ticks) of each beam burst.")]
public readonly int Duration = 10; public readonly int Duration = 10;
@@ -46,10 +47,10 @@ namespace OpenRA.Mods.Common.Projectiles
public readonly WDist MinDistance = WDist.Zero; public readonly WDist MinDistance = WDist.Zero;
[Desc("Damage modifier applied at each range step.")] [Desc("Damage modifier applied at each range step.")]
public readonly int[] Falloff = [100, 100]; public readonly ImmutableArray<int> Falloff = [100, 100];
[Desc("Ranges at which each Falloff step is defined.")] [Desc("Ranges at which each Falloff step is defined.")]
public readonly WDist[] Range = [WDist.Zero, new(int.MaxValue)]; public readonly ImmutableArray<WDist> Range = [WDist.Zero, new(int.MaxValue)];
[Desc("The maximum/constant/incremental inaccuracy used in conjunction with the InaccuracyType property.")] [Desc("The maximum/constant/incremental inaccuracy used in conjunction with the InaccuracyType property.")]
public readonly WDist Inaccuracy = WDist.Zero; public readonly WDist Inaccuracy = WDist.Zero;

View File

@@ -10,7 +10,9 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Effects; using OpenRA.Mods.Common.Effects;
@@ -25,7 +27,7 @@ namespace OpenRA.Mods.Common.Projectiles
public class BulletInfo : IProjectileInfo public class BulletInfo : IProjectileInfo
{ {
[Desc("Projectile speed in WDist / tick, two values indicate variable velocity.")] [Desc("Projectile speed in WDist / tick, two values indicate variable velocity.")]
public readonly WDist[] Speed = [new(17)]; public readonly ImmutableArray<WDist> Speed = [new(17)];
[Desc("The maximum/constant/incremental inaccuracy used in conjunction with the InaccuracyType property.")] [Desc("The maximum/constant/incremental inaccuracy used in conjunction with the InaccuracyType property.")]
public readonly WDist Inaccuracy = WDist.Zero; public readonly WDist Inaccuracy = WDist.Zero;
@@ -41,7 +43,7 @@ namespace OpenRA.Mods.Common.Projectiles
[SequenceReference(nameof(Image), allowNullImage: true)] [SequenceReference(nameof(Image), allowNullImage: true)]
[Desc("Loop a randomly chosen sequence of Image from this list while this projectile is moving.")] [Desc("Loop a randomly chosen sequence of Image from this list while this projectile is moving.")]
public readonly string[] Sequences = ["idle"]; public readonly ImmutableArray<string> Sequences = ["idle"];
[PaletteReference(nameof(IsPlayerPalette))] [PaletteReference(nameof(IsPlayerPalette))]
[Desc("The palette used to draw this projectile.")] [Desc("The palette used to draw this projectile.")]
@@ -61,7 +63,7 @@ namespace OpenRA.Mods.Common.Projectiles
[SequenceReference(nameof(TrailImage), allowNullImage: true)] [SequenceReference(nameof(TrailImage), allowNullImage: true)]
[Desc("Loop a randomly chosen sequence of TrailImage from this list while this projectile is moving.")] [Desc("Loop a randomly chosen sequence of TrailImage from this list while this projectile is moving.")]
public readonly string[] TrailSequences = ["idle"]; public readonly ImmutableArray<string> TrailSequences = ["idle"];
[Desc("Interval in ticks between each spawned Trail animation.")] [Desc("Interval in ticks between each spawned Trail animation.")]
public readonly int TrailInterval = 2; public readonly int TrailInterval = 2;
@@ -83,7 +85,7 @@ namespace OpenRA.Mods.Common.Projectiles
public readonly WDist Width = new(1); public readonly WDist Width = new(1);
[Desc("Arc in WAngles, two values indicate variable arc.")] [Desc("Arc in WAngles, two values indicate variable arc.")]
public readonly WAngle[] LaunchAngle = [WAngle.Zero]; public readonly ImmutableArray<WAngle> LaunchAngle = [WAngle.Zero];
[Desc("Up to how many times does this bullet bounce when touching ground without hitting a target.", [Desc("Up to how many times does this bullet bounce when touching ground without hitting a target.",
"0 implies exploding on contact with the originally targeted position.")] "0 implies exploding on contact with the originally targeted position.")]
@@ -96,7 +98,7 @@ namespace OpenRA.Mods.Common.Projectiles
public readonly string BounceSound = null; public readonly string BounceSound = null;
[Desc("Terrain where the projectile explodes instead of bouncing.")] [Desc("Terrain where the projectile explodes instead of bouncing.")]
public readonly HashSet<string> InvalidBounceTerrain = []; public readonly FrozenSet<string> InvalidBounceTerrain = FrozenSet<string>.Empty;
[Desc("Trigger the explosion if the projectile touches an actor thats owner has these player relationships.")] [Desc("Trigger the explosion if the projectile touches an actor thats owner has these player relationships.")]
public readonly PlayerRelationship ValidBounceBlockerRelationships = PlayerRelationship.Enemy | PlayerRelationship.Neutral; public readonly PlayerRelationship ValidBounceBlockerRelationships = PlayerRelationship.Enemy | PlayerRelationship.Neutral;

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -24,7 +25,7 @@ namespace OpenRA.Mods.Common.Projectiles
[SequenceReference(nameof(Image), allowNullImage: true)] [SequenceReference(nameof(Image), allowNullImage: true)]
[Desc("Loop a randomly chosen sequence of Image from this list while falling.")] [Desc("Loop a randomly chosen sequence of Image from this list while falling.")]
public readonly string[] Sequences = ["idle"]; public readonly ImmutableArray<string> Sequences = ["idle"];
[SequenceReference(nameof(Image), allowNullImage: true)] [SequenceReference(nameof(Image), allowNullImage: true)]
[Desc("Sequence to play when launched. Skipped if null or empty.")] [Desc("Sequence to play when launched. Skipped if null or empty.")]

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -29,7 +30,7 @@ namespace OpenRA.Mods.Common.Projectiles
[SequenceReference(nameof(Image), allowNullImage: true)] [SequenceReference(nameof(Image), allowNullImage: true)]
[Desc("Loop a randomly chosen sequence of Image from this list while this projectile is moving.")] [Desc("Loop a randomly chosen sequence of Image from this list while this projectile is moving.")]
public readonly string[] Sequences = ["idle"]; public readonly ImmutableArray<string> Sequences = ["idle"];
[PaletteReference(nameof(IsPlayerPalette))] [PaletteReference(nameof(IsPlayerPalette))]
[Desc("Palette used to render the projectile sequence.")] [Desc("Palette used to render the projectile sequence.")]
@@ -118,7 +119,7 @@ namespace OpenRA.Mods.Common.Projectiles
[SequenceReference(nameof(TrailImage), allowNullImage: true)] [SequenceReference(nameof(TrailImage), allowNullImage: true)]
[Desc("Loop a randomly chosen sequence of TrailImage from this list while this projectile is moving.")] [Desc("Loop a randomly chosen sequence of TrailImage from this list while this projectile is moving.")]
public readonly string[] TrailSequences = ["idle"]; public readonly ImmutableArray<string> TrailSequences = ["idle"];
[PaletteReference(nameof(TrailUsePlayerPalette))] [PaletteReference(nameof(TrailUsePlayerPalette))]
[Desc("Palette used to render the trail sequence.")] [Desc("Palette used to render the trail sequence.")]

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using OpenRA.Effects; using OpenRA.Effects;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -35,7 +36,7 @@ namespace OpenRA.Mods.Common.Effects
readonly int impactDelay; readonly int impactDelay;
readonly int turn; readonly int turn;
readonly string trailImage; readonly string trailImage;
readonly string[] trailSequences; readonly ImmutableArray<string> trailSequences;
readonly string trailPalette; readonly string trailPalette;
readonly int trailInterval; readonly int trailInterval;
readonly int trailDelay; readonly int trailDelay;
@@ -49,7 +50,7 @@ namespace OpenRA.Mods.Common.Effects
public NukeLaunch(Player firedBy, string image, WeaponInfo weapon, string weaponPalette, string upSequence, string downSequence, public NukeLaunch(Player firedBy, string image, WeaponInfo weapon, string weaponPalette, string upSequence, string downSequence,
WPos launchPos, WPos targetPos, WDist detonationAltitude, bool removeOnDetonation, WDist velocity, int launchDelay, int impactDelay, WPos launchPos, WPos targetPos, WDist detonationAltitude, bool removeOnDetonation, WDist velocity, int launchDelay, int impactDelay,
bool skipAscent, bool skipAscent,
string trailImage, string[] trailSequences, string trailPalette, bool trailUsePlayerPalette, int trailDelay, int trailInterval) string trailImage, ImmutableArray<string> trailSequences, string trailPalette, bool trailUsePlayerPalette, int trailDelay, int trailInterval)
{ {
this.firedBy = firedBy; this.firedBy = firedBy;
this.weapon = weapon; this.weapon = weapon;

View File

@@ -9,7 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Generic; using System.Collections.Frozen;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.Scripting
public class LuaScriptInfo : TraitInfo, Requires<SpawnMapActorsInfo>, NotBefore<SpawnStartingUnitsInfo> public class LuaScriptInfo : TraitInfo, Requires<SpawnMapActorsInfo>, NotBefore<SpawnStartingUnitsInfo>
{ {
[Desc("File names with location relative to the map.")] [Desc("File names with location relative to the map.")]
public readonly HashSet<string> Scripts = []; public readonly FrozenSet<string> Scripts = FrozenSet<string>.Empty;
public override object Create(ActorInitializer init) { return new LuaScript(this); } public override object Create(ActorInitializer init) { return new LuaScript(this); }
} }

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
@@ -1298,13 +1299,13 @@ namespace OpenRA.Mods.Common.Server
return; return;
var mapCache = server.ModData.MapCache; var mapCache = server.ModData.MapCache;
if (server.Settings.MapPool.Length > 0) if (server.Settings.MapPool.Count > 0)
server.MapPool = server.Settings.MapPool.ToHashSet(); server.MapPool = server.Settings.MapPool;
else if (!server.Settings.QueryMapRepository) else if (!server.Settings.QueryMapRepository)
server.MapPool = mapCache server.MapPool = mapCache
.Where(p => p.Status == MapStatus.Available && p.Visibility.HasFlag(MapVisibility.Lobby)) .Where(p => p.Status == MapStatus.Available && p.Visibility.HasFlag(MapVisibility.Lobby))
.Select(p => p.Uid) .Select(p => p.Uid)
.ToHashSet(); .ToFrozenSet();
else else
return; return;

View File

@@ -9,6 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.IO; using System.IO;
@@ -40,9 +41,9 @@ namespace OpenRA.Mods.Common.Terrain
public class DefaultTerrainTemplateInfo : TerrainTemplateInfo public class DefaultTerrainTemplateInfo : TerrainTemplateInfo
{ {
public readonly string[] Images; public readonly ImmutableArray<string> Images;
public readonly string[] DepthImages; public readonly ImmutableArray<string> DepthImages;
public readonly int[] Frames; public readonly ImmutableArray<int> Frames;
public readonly string Palette; public readonly string Palette;
public DefaultTerrainTemplateInfo(ITerrainInfo terrainInfo, MiniYaml my) public DefaultTerrainTemplateInfo(ITerrainInfo terrainInfo, MiniYaml my)
@@ -75,8 +76,8 @@ namespace OpenRA.Mods.Common.Terrain
public readonly string Id; public readonly string Id;
public readonly Size TileSize = new(24, 24); public readonly Size TileSize = new(24, 24);
public readonly int SheetSize = 512; public readonly int SheetSize = 512;
public readonly Color[] HeightDebugColors = [Color.Red]; public readonly ImmutableArray<Color> HeightDebugColors = [Color.Red];
public readonly string[] EditorTemplateOrder; public readonly ImmutableArray<string> EditorTemplateOrder;
public readonly bool IgnoreTileSpriteOffsets; public readonly bool IgnoreTileSpriteOffsets;
public readonly bool EnableDepth = false; public readonly bool EnableDepth = false;
public readonly float MinHeightColorBrightness = 1.0f; public readonly float MinHeightColorBrightness = 1.0f;
@@ -84,13 +85,13 @@ namespace OpenRA.Mods.Common.Terrain
public readonly string Palette = TileSet.TerrainPaletteInternalName; public readonly string Palette = TileSet.TerrainPaletteInternalName;
[FieldLoader.Ignore] [FieldLoader.Ignore]
public readonly IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates; public readonly FrozenDictionary<ushort, TerrainTemplateInfo> Templates;
[FieldLoader.Ignore] [FieldLoader.Ignore]
public readonly IReadOnlyDictionary<string, IEnumerable<MultiBrushInfo>> MultiBrushCollections; public readonly FrozenDictionary<string, ImmutableArray<MultiBrushInfo>> MultiBrushCollections;
[FieldLoader.Ignore] [FieldLoader.Ignore]
public readonly TerrainTypeInfo[] TerrainInfo; public readonly ImmutableArray<TerrainTypeInfo> TerrainInfo;
readonly Dictionary<string, byte> terrainIndexByType = []; readonly FrozenDictionary<string, byte> terrainIndexByType;
readonly byte defaultWalkableTerrainIndex; readonly byte defaultWalkableTerrainIndex;
public DefaultTerrain(IReadOnlyFileSystem fileSystem, string filepath) public DefaultTerrain(IReadOnlyFileSystem fileSystem, string filepath)
@@ -105,35 +106,38 @@ namespace OpenRA.Mods.Common.Terrain
TerrainInfo = yaml["Terrain"].ToDictionary().Values TerrainInfo = yaml["Terrain"].ToDictionary().Values
.Select(y => new TerrainTypeInfo(y)) .Select(y => new TerrainTypeInfo(y))
.OrderBy(tt => tt.Type) .OrderBy(tt => tt.Type)
.ToArray(); .ToImmutableArray();
if (TerrainInfo.Length >= byte.MaxValue) if (TerrainInfo.Length >= byte.MaxValue)
throw new YamlException("Too many terrain types."); throw new YamlException("Too many terrain types.");
var tiby = new Dictionary<string, byte>(TerrainInfo.Length);
for (byte i = 0; i < TerrainInfo.Length; i++) for (byte i = 0; i < TerrainInfo.Length; i++)
{ {
var tt = TerrainInfo[i].Type; var tt = TerrainInfo[i].Type;
if (terrainIndexByType.ContainsKey(tt)) if (!tiby.TryAdd(tt, i))
throw new YamlException($"Duplicate terrain type '{tt}' in '{filepath}'."); throw new YamlException($"Duplicate terrain type '{tt}' in '{filepath}'.");
terrainIndexByType.Add(tt, i);
} }
terrainIndexByType = tiby.ToFrozenDictionary();
defaultWalkableTerrainIndex = GetTerrainIndex("Clear"); defaultWalkableTerrainIndex = GetTerrainIndex("Clear");
// Templates // Templates
Templates = yaml["Templates"].ToDictionary().Values Templates = yaml["Templates"].ToDictionary().Values
.Select(y => (TerrainTemplateInfo)new DefaultTerrainTemplateInfo(this, y)).ToDictionary(t => t.Id); .Select(y => (TerrainTemplateInfo)new DefaultTerrainTemplateInfo(this, y))
.ToDictionary(t => t.Id)
.ToFrozenDictionary();
MultiBrushCollections = MultiBrushCollections =
yaml.TryGetValue("MultiBrushCollections", out var collectionDefinitions) yaml.TryGetValue("MultiBrushCollections", out var collectionDefinitions)
? collectionDefinitions.ToDictionary() ? collectionDefinitions.ToDictionary()
.Select(kv => new KeyValuePair<string, IEnumerable<MultiBrushInfo>>( .Select(kv => new KeyValuePair<string, ImmutableArray<MultiBrushInfo>>(
kv.Key, kv.Key,
MultiBrushInfo.ParseCollection(kv.Value))) MultiBrushInfo.ParseCollection(kv.Value)))
.ToImmutableDictionary() .ToFrozenDictionary()
: ImmutableDictionary<string, IEnumerable<MultiBrushInfo>>.Empty; : FrozenDictionary<string, ImmutableArray<MultiBrushInfo>>.Empty;
} }
public TerrainTypeInfo this[byte index] => TerrainInfo[index]; public TerrainTypeInfo this[byte index] => TerrainInfo[index];
@@ -175,19 +179,19 @@ namespace OpenRA.Mods.Common.Terrain
string ITerrainInfo.Id => Id; string ITerrainInfo.Id => Id;
string ITerrainInfo.Name => Name; string ITerrainInfo.Name => Name;
Size ITerrainInfo.TileSize => TileSize; Size ITerrainInfo.TileSize => TileSize;
TerrainTypeInfo[] ITerrainInfo.TerrainTypes => TerrainInfo; ImmutableArray<TerrainTypeInfo> ITerrainInfo.TerrainTypes => TerrainInfo;
TerrainTileInfo ITerrainInfo.GetTerrainInfo(TerrainTile r) { return GetTileInfo(r); } TerrainTileInfo ITerrainInfo.GetTerrainInfo(TerrainTile r) { return GetTileInfo(r); }
bool ITerrainInfo.TryGetTerrainInfo(TerrainTile r, out TerrainTileInfo info) { return TryGetTileInfo(r, out info); } bool ITerrainInfo.TryGetTerrainInfo(TerrainTile r, out TerrainTileInfo info) { return TryGetTileInfo(r, out info); }
Color[] ITerrainInfo.HeightDebugColors => HeightDebugColors; ImmutableArray<Color> ITerrainInfo.HeightDebugColors => HeightDebugColors;
IEnumerable<Color> ITerrainInfo.RestrictedPlayerColors { get { return TerrainInfo.Where(ti => ti.RestrictPlayerColor).Select(ti => ti.Color); } } IEnumerable<Color> ITerrainInfo.RestrictedPlayerColors { get { return TerrainInfo.Where(ti => ti.RestrictPlayerColor).Select(ti => ti.Color); } }
float ITerrainInfo.MinHeightColorBrightness => MinHeightColorBrightness; float ITerrainInfo.MinHeightColorBrightness => MinHeightColorBrightness;
float ITerrainInfo.MaxHeightColorBrightness => MaxHeightColorBrightness; float ITerrainInfo.MaxHeightColorBrightness => MaxHeightColorBrightness;
TerrainTile ITerrainInfo.DefaultTerrainTile => new(Templates.First().Key, 0); TerrainTile ITerrainInfo.DefaultTerrainTile => new(Templates.First().Key, 0);
string[] ITemplatedTerrainInfo.EditorTemplateOrder => EditorTemplateOrder; ImmutableArray<string> ITemplatedTerrainInfo.EditorTemplateOrder => EditorTemplateOrder;
IReadOnlyDictionary<ushort, TerrainTemplateInfo> ITemplatedTerrainInfo.Templates => Templates; FrozenDictionary<ushort, TerrainTemplateInfo> ITemplatedTerrainInfo.Templates => Templates;
IReadOnlyDictionary<string, IEnumerable<MultiBrushInfo>> ITemplatedTerrainInfo.MultiBrushCollections => MultiBrushCollections; FrozenDictionary<string, ImmutableArray<MultiBrushInfo>> ITemplatedTerrainInfo.MultiBrushCollections => MultiBrushCollections;
void IDumpSheetsTerrainInfo.DumpSheets(string terrainName, ImmutablePalette palette, ref int sheetCount) void IDumpSheetsTerrainInfo.DumpSheets(string terrainName, ImmutablePalette palette, ref int sheetCount)
{ {

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -83,7 +84,7 @@ namespace OpenRA.Mods.Common.Terrain
} }
var frameCount = terrainInfo.EnableDepth && depthFrames == null ? allFrames.Length / 2 : allFrames.Length; var frameCount = terrainInfo.EnableDepth && depthFrames == null ? allFrames.Length / 2 : allFrames.Length;
var indices = templateInfo.Frames ?? Exts.MakeArray(t.Value.TilesCount, j => j); var indices = templateInfo.Frames != null ? templateInfo.Frames : Exts.MakeArray(t.Value.TilesCount, j => j).ToImmutableArray();
var start = indices.Min(); var start = indices.Min();
var end = indices.Max(); var end = indices.Max();

View File

@@ -9,16 +9,17 @@
*/ */
#endregion #endregion
using System.Collections.Generic; using System.Collections.Frozen;
using System.Collections.Immutable;
using OpenRA.Mods.Common.MapGenerator; using OpenRA.Mods.Common.MapGenerator;
namespace OpenRA.Mods.Common.Terrain namespace OpenRA.Mods.Common.Terrain
{ {
public interface ITemplatedTerrainInfo : ITerrainInfo public interface ITemplatedTerrainInfo : ITerrainInfo
{ {
string[] EditorTemplateOrder { get; } ImmutableArray<string> EditorTemplateOrder { get; }
IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates { get; } FrozenDictionary<ushort, TerrainTemplateInfo> Templates { get; }
IReadOnlyDictionary<string, IEnumerable<MultiBrushInfo>> MultiBrushCollections { get; } FrozenDictionary<string, ImmutableArray<MultiBrushInfo>> MultiBrushCollections { get; }
} }
public interface ITerrainInfoNotifyMapCreated : ITerrainInfo public interface ITerrainInfoNotifyMapCreated : ITerrainInfo
@@ -31,7 +32,7 @@ namespace OpenRA.Mods.Common.Terrain
public readonly ushort Id; public readonly ushort Id;
public readonly int2 Size; public readonly int2 Size;
public readonly bool PickAny; public readonly bool PickAny;
public readonly string[] Categories; public readonly ImmutableArray<string> Categories;
readonly TerrainTileInfo[] tileInfo; readonly TerrainTileInfo[] tileInfo;

View File

@@ -9,7 +9,8 @@
*/ */
#endregion #endregion
using System.Collections.Generic; using System.Collections.Frozen;
using System.Collections.Immutable;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
@@ -18,13 +19,13 @@ namespace OpenRA.Mods.Common.Traits
public class AcceptsDeliveredCashInfo : TraitInfo public class AcceptsDeliveredCashInfo : TraitInfo
{ {
[Desc("Accepted `DeliversCash` types. Leave empty to accept all types.")] [Desc("Accepted `DeliversCash` types. Leave empty to accept all types.")]
public readonly HashSet<string> ValidTypes = []; public readonly FrozenSet<string> ValidTypes = FrozenSet<string>.Empty;
[Desc("Player relationships the owner of the delivering actor needs.")] [Desc("Player relationships the owner of the delivering actor needs.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally; public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally;
[Desc("Play a randomly selected sound from this list when accepting cash.")] [Desc("Play a randomly selected sound from this list when accepting cash.")]
public readonly string[] Sounds = []; public readonly ImmutableArray<string> Sounds = [];
public override object Create(ActorInitializer init) { return new AcceptsDeliveredCash(this); } public override object Create(ActorInitializer init) { return new AcceptsDeliveredCash(this); }
} }

View File

@@ -9,7 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Generic; using System.Collections.Frozen;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
@@ -18,7 +18,7 @@ namespace OpenRA.Mods.Common.Traits
public class AcceptsDeliveredExperienceInfo : TraitInfo, Requires<GainsExperienceInfo> public class AcceptsDeliveredExperienceInfo : TraitInfo, Requires<GainsExperienceInfo>
{ {
[Desc("Accepted `DeliversExperience` types. Leave empty to accept all types.")] [Desc("Accepted `DeliversExperience` types. Leave empty to accept all types.")]
public readonly HashSet<string> ValidTypes = []; public readonly FrozenSet<string> ValidTypes = FrozenSet<string>.Empty;
[Desc("Player relationships the owner of the delivering actor needs.")] [Desc("Player relationships the owner of the delivering actor needs.")]
public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally; public readonly PlayerRelationship ValidRelationships = PlayerRelationship.Ally;

View File

@@ -9,7 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Generic; using System.Collections.Frozen;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
@@ -17,7 +17,7 @@ namespace OpenRA.Mods.Common.Traits
public class ActorSpawnerInfo : ConditionalTraitInfo public class ActorSpawnerInfo : ConditionalTraitInfo
{ {
[Desc("Type of ActorSpawner with which it connects.")] [Desc("Type of ActorSpawner with which it connects.")]
public readonly HashSet<string> Types = []; public readonly FrozenSet<string> Types = FrozenSet<string>.Empty;
public override object Create(ActorInitializer init) { return new ActorSpawner(this); } public override object Create(ActorInitializer init) { return new ActorSpawner(this); }
} }
@@ -27,6 +27,6 @@ namespace OpenRA.Mods.Common.Traits
public ActorSpawner(ActorSpawnerInfo info) public ActorSpawner(ActorSpawnerInfo info)
: base(info) { } : base(info) { }
public HashSet<string> Types => Info.Types; public FrozenSet<string> Types => Info.Types;
} }
} }

View File

@@ -10,7 +10,9 @@
#endregion #endregion
using System; using System;
using System.Collections.Frozen;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.Activities; using OpenRA.Activities;
using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Activities;
@@ -83,7 +85,7 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Minimum altitude where this aircraft is considered airborne.")] [Desc("Minimum altitude where this aircraft is considered airborne.")]
public readonly int MinAirborneAltitude = 1; public readonly int MinAirborneAltitude = 1;
public readonly HashSet<string> LandableTerrainTypes = []; public readonly FrozenSet<string> LandableTerrainTypes = FrozenSet<string>.Empty;
[Desc("Can the actor be ordered to move in to shroud?")] [Desc("Can the actor be ordered to move in to shroud?")]
public readonly bool MoveIntoShroud = true; public readonly bool MoveIntoShroud = true;
@@ -142,10 +144,10 @@ namespace OpenRA.Mods.Common.Traits
public readonly WDist AltitudeVelocity = new(43); public readonly WDist AltitudeVelocity = new(43);
[Desc("Sounds to play when the actor is taking off.")] [Desc("Sounds to play when the actor is taking off.")]
public readonly string[] TakeoffSounds = []; public readonly ImmutableArray<string> TakeoffSounds = [];
[Desc("Sounds to play when the actor is landing.")] [Desc("Sounds to play when the actor is landing.")]
public readonly string[] LandingSounds = []; public readonly ImmutableArray<string> LandingSounds = [];
[Desc("The distance of the resupply base that the aircraft will wait for its turn.")] [Desc("The distance of the resupply base that the aircraft will wait for its turn.")]
public readonly WDist WaitDistanceFromResupplyBase = new(3072); public readonly WDist WaitDistanceFromResupplyBase = new(3072);

View File

@@ -10,7 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Collections.Immutable;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
@@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits
public readonly string Name = "primary"; public readonly string Name = "primary";
[Desc("Name(s) of armament(s) that use this pool.")] [Desc("Name(s) of armament(s) that use this pool.")]
public readonly string[] Armaments = ["primary", "secondary"]; public readonly ImmutableArray<string> Armaments = ["primary", "secondary"];
[Desc("How much ammo does this pool contain when fully loaded.")] [Desc("How much ammo does this pool contain when fully loaded.")]
public readonly int Ammo = 1; public readonly int Ammo = 1;

View File

@@ -11,6 +11,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Mods.Common.Traits.Render; using OpenRA.Mods.Common.Traits.Render;
@@ -42,10 +43,10 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Muzzle position relative to turret or body, (forward, right, up) triples.", [Desc("Muzzle position relative to turret or body, (forward, right, up) triples.",
"If weapon Burst = 1, it cycles through all listed offsets, otherwise the offset corresponding to current burst is used.")] "If weapon Burst = 1, it cycles through all listed offsets, otherwise the offset corresponding to current burst is used.")]
public readonly WVec[] LocalOffset = []; public readonly ImmutableArray<WVec> LocalOffset = [];
[Desc("Muzzle yaw relative to turret or body.")] [Desc("Muzzle yaw relative to turret or body.")]
public readonly WAngle[] LocalYaw = []; public readonly ImmutableArray<WAngle> LocalYaw = [];
[Desc("Move the turret backwards when firing.")] [Desc("Move the turret backwards when firing.")]
public readonly WDist Recoil = WDist.Zero; public readonly WDist Recoil = WDist.Zero;

Some files were not shown because too many files have changed in this diff Show More