From 8714fe2a02140dcf6ce2b779c7af68a27f1e1175 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 11 Apr 2021 21:24:28 +0100 Subject: [PATCH] Enable dedicated server lint checks. --- OpenRA.Game/Map/MapPreview.cs | 56 ++++++++++++++ .../Lint/CheckActorReferences.cs | 49 +++++------- OpenRA.Mods.Common/Lint/CheckAngle.cs | 15 +++- OpenRA.Mods.Common/Lint/CheckConditions.cs | 15 +++- .../Lint/CheckConflictingMouseBounds.cs | 3 +- OpenRA.Mods.Common/Lint/CheckCursors.cs | 15 +++- .../Lint/CheckDefaultVisibility.cs | 15 +++- OpenRA.Mods.Common/Lint/CheckHitShapes.cs | 15 +++- .../Lint/CheckLocomotorReferences.cs | 15 +++- OpenRA.Mods.Common/Lint/CheckMapMetadata.cs | 26 +++++-- OpenRA.Mods.Common/Lint/CheckNotifications.cs | 16 +++- OpenRA.Mods.Common/Lint/CheckOwners.cs | 52 +++++++++++++ OpenRA.Mods.Common/Lint/CheckPalettes.cs | 22 ++++-- OpenRA.Mods.Common/Lint/CheckPlayers.cs | 77 ++++++++----------- OpenRA.Mods.Common/Lint/CheckRangeLimit.cs | 15 +++- .../Lint/CheckRevealFootprint.cs | 15 +++- OpenRA.Mods.Common/Lint/CheckSpriteBodies.cs | 15 +++- OpenRA.Mods.Common/Lint/CheckTooltips.cs | 15 +++- .../Lint/CheckTraitPrerequisites.cs | 15 +++- .../Lint/CheckUnknownTraitFields.cs | 42 ++++++---- .../Lint/CheckUnknownWeaponFields.cs | 42 ++++++---- .../Lint/CheckVoiceReferences.cs | 15 +++- .../Lint/LintBuildablePrerequisites.cs | 15 +++- 23 files changed, 433 insertions(+), 147 deletions(-) create mode 100644 OpenRA.Mods.Common/Lint/CheckOwners.cs diff --git a/OpenRA.Game/Map/MapPreview.cs b/OpenRA.Game/Map/MapPreview.cs index 24f6b46edf54..1bac4465cce0 100644 --- a/OpenRA.Game/Map/MapPreview.cs +++ b/OpenRA.Game/Map/MapPreview.cs @@ -64,6 +64,7 @@ public class RemoteMapData public readonly string tileset; public readonly string rules; public readonly string players_block; + public readonly int mapformat; } public class MapPreview : IDisposable, IReadOnlyFileSystem @@ -71,6 +72,7 @@ public class MapPreview : IDisposable, IReadOnlyFileSystem /// Wrapper that enables map data to be replaced in an atomic fashion class InnerData { + public int MapFormat; public string Title; public string[] Categories; public string Author; @@ -171,6 +173,7 @@ public InnerData Clone() volatile InnerData innerData; + public int MapFormat => innerData.MapFormat; public string Title => innerData.Title; public string[] Categories => innerData.Categories; public string Author => innerData.Author; @@ -186,6 +189,7 @@ public InnerData Clone() public MapVisibility Visibility => innerData.Visibility; public MiniYaml RuleDefinitions => innerData.RuleDefinitions; + public MiniYaml WeaponDefinitions => innerData.WeaponDefinitions; public ActorInfo WorldActorInfo => innerData.WorldActorInfo; public ActorInfo PlayerActorInfo => innerData.PlayerActorInfo; @@ -243,6 +247,7 @@ public MapPreview(ModData modData, string uid, MapGridType gridType, MapCache ca Uid = uid; innerData = new InnerData { + MapFormat = 0, Title = "Unknown Map", Categories = new[] { "Unknown" }, Author = "Unknown Author", @@ -259,6 +264,53 @@ public MapPreview(ModData modData, string uid, MapGridType gridType, MapCache ca }; } + // For linting purposes only! + public MapPreview(Map map, ModData modData) + { + this.modData = modData; + cache = modData.MapCache; + + Uid = map.Uid; + Package = map.Package; + + var mapPlayers = new MapPlayers(map.PlayerDefinitions); + var spawns = new List(); + foreach (var kv in map.ActorDefinitions.Where(d => d.Value.Value == "mpspawn")) + { + var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary()); + spawns.Add(s.Get().Value); + } + + innerData = new InnerData + { + MapFormat = map.MapFormat, + Title = map.Title, + Categories = map.Categories, + Author = map.Author, + TileSet = map.Tileset, + Players = mapPlayers, + PlayerCount = mapPlayers.Players.Count(x => x.Value.Playable), + SpawnPoints = spawns.ToArray(), + GridType = map.Grid.Type, + Bounds = map.Bounds, + Preview = null, + Status = MapStatus.Available, + Class = MapClassification.Unknown, + Visibility = map.Visibility, + }; + + innerData.SetCustomRules(modData, this, new Dictionary() + { + { "Rules", map.RuleDefinitions }, + { "Weapons", map.WeaponDefinitions }, + { "Voices", map.VoiceDefinitions }, + { "Music", map.MusicDefinitions }, + { "Notifications", map.NotificationDefinitions }, + { "Sequences", map.SequenceDefinitions }, + { "ModelSequences", map.ModelSequenceDefinitions } + }); + } + public void UpdateFromMap(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassification classification, string[] mapCompatibility, MapGridType gridType) { Dictionary yaml; @@ -306,6 +358,9 @@ public void UpdateFromMap(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassi if (yaml.TryGetValue("RequiresMod", out temp)) requiresMod = temp.Value; + if (yaml.TryGetValue("MapFormat", out temp)) + newData.MapFormat = FieldLoader.GetValue("MapFormat", temp.Value); + newData.Status = mapCompatibility == null || mapCompatibility.Contains(requiresMod) ? MapStatus.Available : MapStatus.Unavailable; @@ -381,6 +436,7 @@ public void UpdateRemoteSearch(MapStatus status, MiniYaml yaml, Action emitError; - - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) { - this.emitError = emitError; - foreach (var actorInfo in rules.Actors) foreach (var traitInfo in actorInfo.Value.TraitInfos()) - CheckTrait(actorInfo.Value, traitInfo, rules); + CheckTrait(emitError, actorInfo.Value, traitInfo, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + foreach (var actorInfo in mapRules.Actors) + foreach (var traitInfo in actorInfo.Value.TraitInfos()) + CheckTrait(emitError, actorInfo.Value, traitInfo, mapRules); } - void CheckTrait(ActorInfo actorInfo, TraitInfo traitInfo, Ruleset rules) + void CheckTrait(Action emitError, ActorInfo actorInfo, TraitInfo traitInfo, Ruleset rules) { var actualType = traitInfo.GetType(); foreach (var field in actualType.GetFields()) { if (field.HasAttribute()) - CheckActorReference(actorInfo, traitInfo, field, rules.Actors, + CheckActorReference(emitError, actorInfo, traitInfo, field, rules.Actors, field.GetCustomAttributes(true)[0]); if (field.HasAttribute()) - CheckWeaponReference(actorInfo, traitInfo, field, rules.Weapons, - field.GetCustomAttributes(true)[0]); + CheckWeaponReference(emitError, actorInfo, traitInfo, field, rules.Weapons); if (field.HasAttribute()) - CheckVoiceReference(actorInfo, traitInfo, field, rules.Voices, - field.GetCustomAttributes(true)[0]); + CheckVoiceReference(emitError, actorInfo, traitInfo, field, rules.Voices); } } - void CheckActorReference(ActorInfo actorInfo, - TraitInfo traitInfo, - FieldInfo fieldInfo, - IReadOnlyDictionary dict, - ActorReferenceAttribute attribute) + void CheckActorReference(Action emitError, ActorInfo actorInfo, TraitInfo traitInfo, + FieldInfo fieldInfo, IReadOnlyDictionary dict, ActorReferenceAttribute attribute) { var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError, attribute.DictionaryReference); foreach (var value in values) @@ -82,11 +81,8 @@ void CheckTrait(ActorInfo actorInfo, TraitInfo traitInfo, Ruleset rules) } } - void CheckWeaponReference(ActorInfo actorInfo, - TraitInfo traitInfo, - FieldInfo fieldInfo, - IReadOnlyDictionary dict, - WeaponReferenceAttribute attribute) + void CheckWeaponReference(Action emitError, ActorInfo actorInfo, TraitInfo traitInfo, + FieldInfo fieldInfo, IReadOnlyDictionary dict) { var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError); foreach (var value in values) @@ -100,11 +96,8 @@ void CheckTrait(ActorInfo actorInfo, TraitInfo traitInfo, Ruleset rules) } } - void CheckVoiceReference(ActorInfo actorInfo, - TraitInfo traitInfo, - FieldInfo fieldInfo, - IReadOnlyDictionary dict, - VoiceSetReferenceAttribute attribute) + void CheckVoiceReference(Action emitError, ActorInfo actorInfo, TraitInfo traitInfo, + FieldInfo fieldInfo, IReadOnlyDictionary dict) { var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError); foreach (var value in values) diff --git a/OpenRA.Mods.Common/Lint/CheckAngle.cs b/OpenRA.Mods.Common/Lint/CheckAngle.cs index 3dfa4f94413a..ec6a68a84eae 100644 --- a/OpenRA.Mods.Common/Lint/CheckAngle.cs +++ b/OpenRA.Mods.Common/Lint/CheckAngle.cs @@ -11,12 +11,23 @@ using System; using OpenRA.Mods.Common.Projectiles; +using OpenRA.Server; namespace OpenRA.Mods.Common.Lint { - class CheckAngle : ILintRulesPass + class CheckAngle : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } + + void Run(Action emitError, Ruleset rules) { foreach (var weaponInfo in rules.Weapons) { diff --git a/OpenRA.Mods.Common/Lint/CheckConditions.cs b/OpenRA.Mods.Common/Lint/CheckConditions.cs index d30b55eaf2a7..2ef7ef9e6978 100644 --- a/OpenRA.Mods.Common/Lint/CheckConditions.cs +++ b/OpenRA.Mods.Common/Lint/CheckConditions.cs @@ -12,13 +12,24 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.Server; using OpenRA.Traits; namespace OpenRA.Mods.Common.Lint { - public class CheckConditions : ILintRulesPass + public class CheckConditions : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, emitWarning, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, emitWarning, mapRules); + } + + void Run(Action emitError, Action emitWarning, Ruleset rules) { foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/Lint/CheckConflictingMouseBounds.cs b/OpenRA.Mods.Common/Lint/CheckConflictingMouseBounds.cs index 8e4c03f06899..77d1b47848e3 100644 --- a/OpenRA.Mods.Common/Lint/CheckConflictingMouseBounds.cs +++ b/OpenRA.Mods.Common/Lint/CheckConflictingMouseBounds.cs @@ -12,12 +12,13 @@ using System; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Network; namespace OpenRA.Mods.Common.Lint { public class CheckConflictingMouseBounds : ILintRulesPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) { foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/Lint/CheckCursors.cs b/OpenRA.Mods.Common/Lint/CheckCursors.cs index acd1d057cf79..8343e9e9182f 100644 --- a/OpenRA.Mods.Common/Lint/CheckCursors.cs +++ b/OpenRA.Mods.Common/Lint/CheckCursors.cs @@ -12,13 +12,24 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.Server; using OpenRA.Traits; namespace OpenRA.Mods.Common.Lint { - class CheckCursors : ILintRulesPass + class CheckCursors : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, modData, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, modData, mapRules); + } + + void Run(Action emitError, ModData modData, Ruleset rules) { var fileSystem = modData.DefaultFileSystem; var sequenceYaml = MiniYaml.Merge(modData.Manifest.Cursors.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s))); diff --git a/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs b/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs index 2eae91a3cc25..fdca43635540 100644 --- a/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs +++ b/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs @@ -12,13 +12,24 @@ using System; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Server; using OpenRA.Traits; namespace OpenRA.Mods.Common.Lint { - class CheckDefaultVisibility : ILintRulesPass + class CheckDefaultVisibility : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } + + void Run(Action emitError, Ruleset rules) { foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/Lint/CheckHitShapes.cs b/OpenRA.Mods.Common/Lint/CheckHitShapes.cs index 214b64beaeb7..e308ec1d669f 100644 --- a/OpenRA.Mods.Common/Lint/CheckHitShapes.cs +++ b/OpenRA.Mods.Common/Lint/CheckHitShapes.cs @@ -12,13 +12,24 @@ using System; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Server; using OpenRA.Traits; namespace OpenRA.Mods.Common.Lint { - class CheckHitShapes : ILintRulesPass + class CheckHitShapes : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } + + void Run(Action emitError, Ruleset rules) { foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/Lint/CheckLocomotorReferences.cs b/OpenRA.Mods.Common/Lint/CheckLocomotorReferences.cs index e29e3729a00e..1e3f7abc0542 100644 --- a/OpenRA.Mods.Common/Lint/CheckLocomotorReferences.cs +++ b/OpenRA.Mods.Common/Lint/CheckLocomotorReferences.cs @@ -12,13 +12,24 @@ using System; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Server; using OpenRA.Traits; namespace OpenRA.Mods.Common.Lint { - public class CheckLocomotorReferences : ILintRulesPass + public class CheckLocomotorReferences : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } + + void Run(Action emitError, Ruleset rules) { var worldActor = rules.Actors[SystemActors.World]; var locomotorInfos = worldActor.TraitInfos().ToArray(); diff --git a/OpenRA.Mods.Common/Lint/CheckMapMetadata.cs b/OpenRA.Mods.Common/Lint/CheckMapMetadata.cs index f702a4805d1a..56f169f2083c 100644 --- a/OpenRA.Mods.Common/Lint/CheckMapMetadata.cs +++ b/OpenRA.Mods.Common/Lint/CheckMapMetadata.cs @@ -11,24 +11,34 @@ using System; using System.Linq; +using OpenRA.Server; namespace OpenRA.Mods.Common.Lint { - public class CheckMapMetadata : ILintMapPass + public class CheckMapMetadata : ILintMapPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Map map) + void ILintMapPass.Run(Action emitError, Action emitWarning, ModData modData, Map map) { - if (map.MapFormat != Map.SupportedMapFormat) - emitError("Map format {0} does not match the supported version {1}." - .F(map.MapFormat, Map.SupportedMapFormat)); + Run(emitError, map.MapFormat, map.Author, map.Title, map.Categories); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, map.MapFormat, map.Author, map.Title, map.Categories); + } + + void Run(Action emitError, int mapFormat, string author, string title, string[] categories) + { + if (mapFormat != Map.SupportedMapFormat) + emitError("Map format {0} does not match the supported version {1}.".F(mapFormat, Map.SupportedMapFormat)); - if (map.Author == null) + if (author == null) emitError("Map does not define a valid author."); - if (map.Title == null) + if (title == null) emitError("Map does not define a valid title."); - if (!map.Categories.Any()) + if (!categories.Any()) emitError("Map does not define any categories."); } } diff --git a/OpenRA.Mods.Common/Lint/CheckNotifications.cs b/OpenRA.Mods.Common/Lint/CheckNotifications.cs index 702ade149d9b..fa7490da9a20 100644 --- a/OpenRA.Mods.Common/Lint/CheckNotifications.cs +++ b/OpenRA.Mods.Common/Lint/CheckNotifications.cs @@ -11,15 +11,25 @@ using System; using System.Linq; -using OpenRA.GameRules; using OpenRA.Mods.Common.Traits; +using OpenRA.Server; using OpenRA.Traits; namespace OpenRA.Mods.Common.Lint { - class CheckNotifications : ILintRulesPass + class CheckNotifications : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } + + void Run(Action emitError, Ruleset rules) { foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/Lint/CheckOwners.cs b/OpenRA.Mods.Common/Lint/CheckOwners.cs new file mode 100644 index 000000000000..053868a8460e --- /dev/null +++ b/OpenRA.Mods.Common/Lint/CheckOwners.cs @@ -0,0 +1,52 @@ +#region Copyright & License Information +/* + * Copyright 2007-2020 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Mods.Common.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Lint +{ + public class CheckOwners : ILintMapPass + { + public void Run(Action emitError, Action emitWarning, ModData modData, Map map) + { + var playerNames = new MapPlayers(map.PlayerDefinitions).Players.Values + .Select(p => p.Name) + .ToHashSet(); + + // Check for actors that require specific owners + var actorsWithRequiredOwner = map.Rules.Actors + .Where(a => a.Value.HasTraitInfo()) + .ToDictionary(a => a.Key, a => a.Value.TraitInfo()); + + foreach (var kv in map.ActorDefinitions) + { + var actorReference = new ActorReference(kv.Value.Value, kv.Value.ToDictionary()); + var ownerInit = actorReference.GetOrDefault(); + if (ownerInit == null) + emitError("Actor {0} is not owned by any player.".F(kv.Key)); + else + { + var ownerName = ownerInit.InternalName; + if (!playerNames.Contains(ownerName)) + emitError("Actor {0} is owned by unknown player {1}.".F(kv.Key, ownerName)); + + if (actorsWithRequiredOwner.TryGetValue(kv.Value.Value, out var info)) + if (!info.ValidOwnerNames.Contains(ownerName)) + emitError("Actor {0} owner {1} is not one of ValidOwnerNames: {2}".F(kv.Key, ownerName, info.ValidOwnerNames.JoinWith(", "))); + } + } + } + } +} diff --git a/OpenRA.Mods.Common/Lint/CheckPalettes.cs b/OpenRA.Mods.Common/Lint/CheckPalettes.cs index 756b0120051c..128d1372de3b 100644 --- a/OpenRA.Mods.Common/Lint/CheckPalettes.cs +++ b/OpenRA.Mods.Common/Lint/CheckPalettes.cs @@ -12,18 +12,28 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.Server; using OpenRA.Traits; namespace OpenRA.Mods.Common.Lint { - class CheckPalettes : ILintRulesPass + class CheckPalettes : ILintRulesPass, ILintServerMapPass { - List palettes = new List(); - List playerPalettes = new List(); + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void Run(Action emitError, Ruleset rules) { - GetPalettes(emitError, rules); + var palettes = new List(); + var playerPalettes = new List(); + GetPalettes(emitError, rules, palettes, playerPalettes); foreach (var actorInfo in rules.Actors) { @@ -111,7 +121,7 @@ public void Run(Action emitError, Action emitWarning, ModData mo } } - void GetPalettes(Action emitError, Ruleset rules) + void GetPalettes(Action emitError, Ruleset rules, List palettes, List playerPalettes) { foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/Lint/CheckPlayers.cs b/OpenRA.Mods.Common/Lint/CheckPlayers.cs index 807696b7ee1a..710f2248ec32 100644 --- a/OpenRA.Mods.Common/Lint/CheckPlayers.cs +++ b/OpenRA.Mods.Common/Lint/CheckPlayers.cs @@ -13,21 +13,39 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Server; using OpenRA.Traits; namespace OpenRA.Mods.Common.Lint { - public class CheckPlayers : ILintMapPass + public class CheckPlayers : ILintMapPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Map map) + void ILintMapPass.Run(Action emitError, Action emitWarning, ModData modData, Map map) { - var players = new MapPlayers(map.PlayerDefinitions).Players; - if (players.Count > 64) + var players = new MapPlayers(map.PlayerDefinitions); + var spawns = new List(); + foreach (var kv in map.ActorDefinitions.Where(d => d.Value.Value == "mpspawn")) + { + var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary()); + spawns.Add(s.Get().Value); + } + + Run(emitError, emitWarning, players, map.Visibility, map.Rules.Actors[SystemActors.World], spawns.ToArray()); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, emitWarning, map.Players, map.Visibility, map.WorldActorInfo, map.SpawnPoints); + } + + void Run(Action emitError, Action emitWarning, MapPlayers players, MapVisibility visibility, ActorInfo worldActorInfo, CPos[] spawnPoints) + { + if (players.Players.Count > 64) emitError("Defining more than 64 players is not allowed."); var worldOwnerFound = false; - var playerNames = players.Values.Select(p => p.Name).ToHashSet(); - foreach (var player in players.Values) + var playerNames = players.Players.Values.Select(p => p.Name).ToHashSet(); + foreach (var player in players.Players.Values) { foreach (var ally in player.Allies) if (!playerNames.Contains(ally)) @@ -46,7 +64,7 @@ public void Run(Action emitError, Action emitWarning, ModData mo if (player.Playable) emitError("The player {0} owning the world can't be playable.".F(player.Name)); } - else if (map.Visibility == MapVisibility.MissionSelector && player.Playable && !player.LockFaction) + else if (visibility == MapVisibility.MissionSelector && player.Playable && !player.LockFaction) { // Missions must lock the faction of the player to force the server to override the default Random faction emitError("The player {0} must specify LockFaction: True.".F(player.Name)); @@ -56,51 +74,20 @@ public void Run(Action emitError, Action emitWarning, ModData mo if (!worldOwnerFound) emitError("Found no player owning the world."); - var worldActor = map.Rules.Actors[SystemActors.World]; - var factions = worldActor.TraitInfos().Select(f => f.InternalName).ToHashSet(); - foreach (var player in players.Values) + var factions = worldActorInfo.TraitInfos().Select(f => f.InternalName).ToHashSet(); + foreach (var player in players.Players.Values) if (!string.IsNullOrWhiteSpace(player.Faction) && !factions.Contains(player.Faction)) emitError("Invalid faction {0} chosen for player {1}.".F(player.Faction, player.Name)); - if (worldActor.HasTraitInfo()) + if (worldActorInfo.HasTraitInfo()) { - var playerCount = players.Count(p => p.Value.Playable); - var spawns = new List(); - foreach (var kv in map.ActorDefinitions.Where(d => d.Value.Value == "mpspawn")) - { - var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary()); - spawns.Add(s.Get().Value); - } - - if (playerCount > spawns.Count) - emitError("The map allows {0} possible players, but defines only {1} spawn points".F(playerCount, spawns.Count)); + var playerCount = players.Players.Count(p => p.Value.Playable); + if (playerCount > spawnPoints.Length) + emitError("The map allows {0} possible players, but defines only {1} spawn points".F(playerCount, spawnPoints.Length)); - if (spawns.Distinct().Count() != spawns.Count) + if (spawnPoints.Distinct().Count() != spawnPoints.Length) emitError("Duplicate spawn point locations detected."); } - - // Check for actors that require specific owners - var actorsWithRequiredOwner = map.Rules.Actors - .Where(a => a.Value.HasTraitInfo()) - .ToDictionary(a => a.Key, a => a.Value.TraitInfo()); - - foreach (var kv in map.ActorDefinitions) - { - var actorReference = new ActorReference(kv.Value.Value, kv.Value.ToDictionary()); - var ownerInit = actorReference.GetOrDefault(); - if (ownerInit == null) - emitError("Actor {0} is not owned by any player.".F(kv.Key)); - else - { - var ownerName = ownerInit.InternalName; - if (!playerNames.Contains(ownerName)) - emitError("Actor {0} is owned by unknown player {1}.".F(kv.Key, ownerName)); - - if (actorsWithRequiredOwner.TryGetValue(kv.Value.Value, out var info)) - if (!info.ValidOwnerNames.Contains(ownerName)) - emitError("Actor {0} owner {1} is not one of ValidOwnerNames: {2}".F(kv.Key, ownerName, info.ValidOwnerNames.JoinWith(", "))); - } - } } } } diff --git a/OpenRA.Mods.Common/Lint/CheckRangeLimit.cs b/OpenRA.Mods.Common/Lint/CheckRangeLimit.cs index 9cc4539725cd..a5cc5ba4b908 100644 --- a/OpenRA.Mods.Common/Lint/CheckRangeLimit.cs +++ b/OpenRA.Mods.Common/Lint/CheckRangeLimit.cs @@ -11,12 +11,23 @@ using System; using OpenRA.Mods.Common.Projectiles; +using OpenRA.Server; namespace OpenRA.Mods.Common.Lint { - class CheckRangeLimit : ILintRulesPass + class CheckRangeLimit : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } + + void Run(Action emitError, Ruleset rules) { foreach (var weaponInfo in rules.Weapons) { diff --git a/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs b/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs index 5f875cb1c510..d0726437fb83 100644 --- a/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs +++ b/OpenRA.Mods.Common/Lint/CheckRevealFootprint.cs @@ -12,13 +12,24 @@ using System; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Server; using OpenRA.Traits; namespace OpenRA.Mods.Common.Lint { - class CheckRevealFootprint : ILintRulesPass + class CheckRevealFootprint : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } + + void Run(Action emitError, Ruleset rules) { foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/Lint/CheckSpriteBodies.cs b/OpenRA.Mods.Common/Lint/CheckSpriteBodies.cs index 4e9edf4872ce..5989a27f2591 100644 --- a/OpenRA.Mods.Common/Lint/CheckSpriteBodies.cs +++ b/OpenRA.Mods.Common/Lint/CheckSpriteBodies.cs @@ -12,12 +12,23 @@ using System; using System.Linq; using OpenRA.Mods.Common.Traits.Render; +using OpenRA.Server; namespace OpenRA.Mods.Common.Lint { - class CheckSpriteBodies : ILintRulesPass + class CheckSpriteBodies : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } + + void Run(Action emitError, Ruleset rules) { foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/Lint/CheckTooltips.cs b/OpenRA.Mods.Common/Lint/CheckTooltips.cs index 551aca6ca96a..b38e160f344c 100644 --- a/OpenRA.Mods.Common/Lint/CheckTooltips.cs +++ b/OpenRA.Mods.Common/Lint/CheckTooltips.cs @@ -12,12 +12,23 @@ using System; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Server; namespace OpenRA.Mods.Common.Lint { - class CheckTooltips : ILintRulesPass + class CheckTooltips : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } + + void Run(Action emitError, Ruleset rules) { foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/Lint/CheckTraitPrerequisites.cs b/OpenRA.Mods.Common/Lint/CheckTraitPrerequisites.cs index 5ed12297d732..34cf2f5bbb60 100644 --- a/OpenRA.Mods.Common/Lint/CheckTraitPrerequisites.cs +++ b/OpenRA.Mods.Common/Lint/CheckTraitPrerequisites.cs @@ -11,12 +11,23 @@ using System; using System.Linq; +using OpenRA.Server; namespace OpenRA.Mods.Common.Lint { - public class CheckTraitPrerequisites : ILintRulesPass + public class CheckTraitPrerequisites : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, emitWarning, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, emitWarning, mapRules); + } + + void Run(Action emitError, Action emitWarning, Ruleset rules) { foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/Lint/CheckUnknownTraitFields.cs b/OpenRA.Mods.Common/Lint/CheckUnknownTraitFields.cs index 1e64bcc891a6..66d8f832fd62 100644 --- a/OpenRA.Mods.Common/Lint/CheckUnknownTraitFields.cs +++ b/OpenRA.Mods.Common/Lint/CheckUnknownTraitFields.cs @@ -12,11 +12,29 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.FileSystem; +using OpenRA.Server; namespace OpenRA.Mods.Common.Lint { - class CheckUnknownTraitFields : ILintPass, ILintMapPass + class CheckUnknownTraitFields : ILintPass, ILintMapPass, ILintServerMapPass { + void ILintPass.Run(Action emitError, Action emitWarning, ModData modData) + { + foreach (var f in modData.Manifest.Rules) + CheckActors(MiniYaml.FromStream(modData.DefaultFileSystem.Open(f), f), emitError, modData); + } + + void ILintMapPass.Run(Action emitError, Action emitWarning, ModData modData, Map map) + { + CheckMapYaml(emitError, modData, map, map.RuleDefinitions); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + CheckMapYaml(emitError, modData, map, map.RuleDefinitions); + } + string NormalizeName(string key) { var name = key.Split('@')[0]; @@ -64,23 +82,17 @@ void CheckActors(IEnumerable actors, Action emitError, Mod } } - void ILintPass.Run(Action emitError, Action emitWarning, ModData modData) + void CheckMapYaml(Action emitError, ModData modData, IReadOnlyFileSystem fileSystem, MiniYaml ruleDefinitions) { - foreach (var f in modData.Manifest.Rules) - CheckActors(MiniYaml.FromStream(modData.DefaultFileSystem.Open(f), f), emitError, modData); - } + if (ruleDefinitions == null) + return; - void ILintMapPass.Run(Action emitError, Action emitWarning, ModData modData, Map map) - { - if (map.RuleDefinitions != null && map.RuleDefinitions.Value != null) - { - var mapFiles = FieldLoader.GetValue("value", map.RuleDefinitions.Value); - foreach (var f in mapFiles) - CheckActors(MiniYaml.FromStream(map.Open(f), f), emitError, modData); + var mapFiles = FieldLoader.GetValue("value", ruleDefinitions.Value); + foreach (var f in mapFiles) + CheckActors(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, modData); - if (map.RuleDefinitions.Nodes.Any()) - CheckActors(map.RuleDefinitions.Nodes, emitError, modData); - } + if (ruleDefinitions.Nodes.Any()) + CheckActors(ruleDefinitions.Nodes, emitError, modData); } } } diff --git a/OpenRA.Mods.Common/Lint/CheckUnknownWeaponFields.cs b/OpenRA.Mods.Common/Lint/CheckUnknownWeaponFields.cs index 95fdf315f1de..d9b70f046512 100644 --- a/OpenRA.Mods.Common/Lint/CheckUnknownWeaponFields.cs +++ b/OpenRA.Mods.Common/Lint/CheckUnknownWeaponFields.cs @@ -12,12 +12,30 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.FileSystem; using OpenRA.GameRules; +using OpenRA.Server; namespace OpenRA.Mods.Common.Lint { - class CheckUnknownWeaponFields : ILintPass, ILintMapPass + class CheckUnknownWeaponFields : ILintPass, ILintMapPass, ILintServerMapPass { + void ILintPass.Run(Action emitError, Action emitWarning, ModData modData) + { + foreach (var f in modData.Manifest.Weapons) + CheckWeapons(MiniYaml.FromStream(modData.DefaultFileSystem.Open(f), f), emitError, emitWarning, modData); + } + + void ILintMapPass.Run(Action emitError, Action emitWarning, ModData modData, Map map) + { + CheckMapYaml(emitError, emitWarning, modData, map, map.WeaponDefinitions); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + CheckMapYaml(emitError, emitWarning, modData, map, map.WeaponDefinitions); + } + string NormalizeName(string key) { var name = key.Split('@')[0]; @@ -81,23 +99,17 @@ void CheckWeapons(IEnumerable weapons, Action emitError, A } } - void ILintPass.Run(Action emitError, Action emitWarning, ModData modData) + void CheckMapYaml(Action emitError, Action emitWarning, ModData modData, IReadOnlyFileSystem fileSystem, MiniYaml weaponDefinitions) { - foreach (var f in modData.Manifest.Weapons) - CheckWeapons(MiniYaml.FromStream(modData.DefaultFileSystem.Open(f), f), emitError, emitWarning, modData); - } + if (weaponDefinitions == null) + return; - void ILintMapPass.Run(Action emitError, Action emitWarning, ModData modData, Map map) - { - if (map.WeaponDefinitions != null && map.WeaponDefinitions.Value != null) - { - var mapFiles = FieldLoader.GetValue("value", map.WeaponDefinitions.Value); - foreach (var f in mapFiles) - CheckWeapons(MiniYaml.FromStream(map.Open(f), f), emitError, emitWarning, modData); + var mapFiles = FieldLoader.GetValue("value", weaponDefinitions.Value); + foreach (var f in mapFiles) + CheckWeapons(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, emitWarning, modData); - if (map.WeaponDefinitions.Nodes.Any()) - CheckWeapons(map.WeaponDefinitions.Nodes, emitError, emitWarning, modData); - } + if (weaponDefinitions.Nodes.Any()) + CheckWeapons(weaponDefinitions.Nodes, emitError, emitWarning, modData); } } } diff --git a/OpenRA.Mods.Common/Lint/CheckVoiceReferences.cs b/OpenRA.Mods.Common/Lint/CheckVoiceReferences.cs index 4595d558c38f..ddd888bd2ad8 100644 --- a/OpenRA.Mods.Common/Lint/CheckVoiceReferences.cs +++ b/OpenRA.Mods.Common/Lint/CheckVoiceReferences.cs @@ -12,13 +12,24 @@ using System; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Server; using OpenRA.Traits; namespace OpenRA.Mods.Common.Lint { - public class CheckVoiceReferences : ILintRulesPass + public class CheckVoiceReferences : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } + + void Run(Action emitError, Ruleset rules) { foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/Lint/LintBuildablePrerequisites.cs b/OpenRA.Mods.Common/Lint/LintBuildablePrerequisites.cs index 579418048c02..a0c9679dc832 100644 --- a/OpenRA.Mods.Common/Lint/LintBuildablePrerequisites.cs +++ b/OpenRA.Mods.Common/Lint/LintBuildablePrerequisites.cs @@ -12,12 +12,23 @@ using System; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Server; namespace OpenRA.Mods.Common.Lint { - class LintBuildablePrerequisites : ILintRulesPass + class LintBuildablePrerequisites : ILintRulesPass, ILintServerMapPass { - public void Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules); + } + + void Run(Action emitError, Ruleset rules) { var providedPrereqs = rules.Actors.SelectMany(a => a.Value.TraitInfos().SelectMany(p => p.Prerequisites(a.Value)));