Enable dedicated server lint checks.

This commit is contained in:
Paul Chote
2021-04-11 21:24:28 +01:00
committed by teinarss
parent 9770967b04
commit f9294f0e9e
23 changed files with 434 additions and 148 deletions

View File

@@ -61,6 +61,7 @@ namespace OpenRA
public readonly string tileset; public readonly string tileset;
public readonly string rules; public readonly string rules;
public readonly string players_block; public readonly string players_block;
public readonly int mapformat;
} }
public class MapPreview : IDisposable, IReadOnlyFileSystem public class MapPreview : IDisposable, IReadOnlyFileSystem
@@ -68,6 +69,7 @@ namespace OpenRA
/// <summary>Wrapper that enables map data to be replaced in an atomic fashion</summary> /// <summary>Wrapper that enables map data to be replaced in an atomic fashion</summary>
class InnerData class InnerData
{ {
public int MapFormat;
public string Title; public string Title;
public string[] Categories; public string[] Categories;
public string Author; public string Author;
@@ -168,6 +170,7 @@ namespace OpenRA
volatile InnerData innerData; volatile InnerData innerData;
public int MapFormat => innerData.MapFormat;
public string Title => innerData.Title; public string Title => innerData.Title;
public string[] Categories => innerData.Categories; public string[] Categories => innerData.Categories;
public string Author => innerData.Author; public string Author => innerData.Author;
@@ -183,6 +186,7 @@ namespace OpenRA
public MapVisibility Visibility => innerData.Visibility; public MapVisibility Visibility => innerData.Visibility;
public MiniYaml RuleDefinitions => innerData.RuleDefinitions; public MiniYaml RuleDefinitions => innerData.RuleDefinitions;
public MiniYaml WeaponDefinitions => innerData.WeaponDefinitions;
public ActorInfo WorldActorInfo => innerData.WorldActorInfo; public ActorInfo WorldActorInfo => innerData.WorldActorInfo;
public ActorInfo PlayerActorInfo => innerData.PlayerActorInfo; public ActorInfo PlayerActorInfo => innerData.PlayerActorInfo;
@@ -234,6 +238,7 @@ namespace OpenRA
Uid = uid; Uid = uid;
innerData = new InnerData innerData = new InnerData
{ {
MapFormat = 0,
Title = "Unknown Map", Title = "Unknown Map",
Categories = new[] { "Unknown" }, Categories = new[] { "Unknown" },
Author = "Unknown Author", Author = "Unknown Author",
@@ -250,6 +255,53 @@ namespace OpenRA
}; };
} }
// 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<CPos>();
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<LocationInit>().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<string, MiniYaml>()
{
{ "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) public void UpdateFromMap(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassification classification, string[] mapCompatibility, MapGridType gridType)
{ {
Dictionary<string, MiniYaml> yaml; Dictionary<string, MiniYaml> yaml;
@@ -297,6 +349,9 @@ namespace OpenRA
if (yaml.TryGetValue("RequiresMod", out temp)) if (yaml.TryGetValue("RequiresMod", out temp))
requiresMod = temp.Value; requiresMod = temp.Value;
if (yaml.TryGetValue("MapFormat", out temp))
newData.MapFormat = FieldLoader.GetValue<int>("MapFormat", temp.Value);
newData.Status = mapCompatibility == null || mapCompatibility.Contains(requiresMod) ? newData.Status = mapCompatibility == null || mapCompatibility.Contains(requiresMod) ?
MapStatus.Available : MapStatus.Unavailable; MapStatus.Available : MapStatus.Unavailable;
@@ -372,6 +427,7 @@ namespace OpenRA
newData.PlayerCount = r.players; newData.PlayerCount = r.players;
newData.Bounds = r.bounds; newData.Bounds = r.bounds;
newData.TileSet = r.tileset; newData.TileSet = r.tileset;
newData.MapFormat = r.mapformat;
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)

View File

@@ -15,47 +15,46 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
public class CheckActorReferences : ILintRulesPass public class CheckActorReferences : ILintRulesPass, ILintServerMapPass
{ {
Action<string> emitError; void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{ {
this.emitError = emitError;
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>()) foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
CheckTrait(actorInfo.Value, traitInfo, rules); CheckTrait(emitError, actorInfo.Value, traitInfo, rules);
} }
void CheckTrait(ActorInfo actorInfo, TraitInfo traitInfo, Ruleset rules) void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
foreach (var actorInfo in mapRules.Actors)
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
CheckTrait(emitError, actorInfo.Value, traitInfo, mapRules);
}
void CheckTrait(Action<string> emitError, ActorInfo actorInfo, TraitInfo traitInfo, Ruleset rules)
{ {
var actualType = traitInfo.GetType(); var actualType = traitInfo.GetType();
foreach (var field in actualType.GetFields()) foreach (var field in actualType.GetFields())
{ {
if (field.HasAttribute<ActorReferenceAttribute>()) if (field.HasAttribute<ActorReferenceAttribute>())
CheckActorReference(actorInfo, traitInfo, field, rules.Actors, CheckActorReference(emitError, actorInfo, traitInfo, field, rules.Actors,
field.GetCustomAttributes<ActorReferenceAttribute>(true)[0]); field.GetCustomAttributes<ActorReferenceAttribute>(true)[0]);
if (field.HasAttribute<WeaponReferenceAttribute>()) if (field.HasAttribute<WeaponReferenceAttribute>())
CheckWeaponReference(actorInfo, traitInfo, field, rules.Weapons, CheckWeaponReference(emitError, actorInfo, traitInfo, field, rules.Weapons);
field.GetCustomAttributes<WeaponReferenceAttribute>(true)[0]);
if (field.HasAttribute<VoiceSetReferenceAttribute>()) if (field.HasAttribute<VoiceSetReferenceAttribute>())
CheckVoiceReference(actorInfo, traitInfo, field, rules.Voices, CheckVoiceReference(emitError, actorInfo, traitInfo, field, rules.Voices);
field.GetCustomAttributes<VoiceSetReferenceAttribute>(true)[0]);
} }
} }
void CheckActorReference(ActorInfo actorInfo, void CheckActorReference(Action<string> emitError, ActorInfo actorInfo, TraitInfo traitInfo,
TraitInfo traitInfo, FieldInfo fieldInfo, IReadOnlyDictionary<string, ActorInfo> dict, ActorReferenceAttribute attribute)
FieldInfo fieldInfo,
IReadOnlyDictionary<string, ActorInfo> dict,
ActorReferenceAttribute attribute)
{ {
var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError, attribute.DictionaryReference); var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError, attribute.DictionaryReference);
foreach (var value in values) foreach (var value in values)
@@ -82,11 +81,8 @@ namespace OpenRA.Mods.Common.Lint
} }
} }
void CheckWeaponReference(ActorInfo actorInfo, void CheckWeaponReference(Action<string> emitError, ActorInfo actorInfo, TraitInfo traitInfo,
TraitInfo traitInfo, FieldInfo fieldInfo, IReadOnlyDictionary<string, WeaponInfo> dict)
FieldInfo fieldInfo,
IReadOnlyDictionary<string, WeaponInfo> dict,
WeaponReferenceAttribute attribute)
{ {
var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError); var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError);
foreach (var value in values) foreach (var value in values)
@@ -100,11 +96,8 @@ namespace OpenRA.Mods.Common.Lint
} }
} }
void CheckVoiceReference(ActorInfo actorInfo, void CheckVoiceReference(Action<string> emitError, ActorInfo actorInfo, TraitInfo traitInfo,
TraitInfo traitInfo, FieldInfo fieldInfo, IReadOnlyDictionary<string, SoundInfo> dict)
FieldInfo fieldInfo,
IReadOnlyDictionary<string, SoundInfo> dict,
VoiceSetReferenceAttribute attribute)
{ {
var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError); var values = LintExts.GetFieldValues(traitInfo, fieldInfo, emitError);
foreach (var value in values) foreach (var value in values)

View File

@@ -11,12 +11,23 @@
using System; using System;
using OpenRA.Mods.Common.Projectiles; using OpenRA.Mods.Common.Projectiles;
using OpenRA.Server;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckAngle : ILintRulesPass class CheckAngle : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{ {
foreach (var weaponInfo in rules.Weapons) foreach (var weaponInfo in rules.Weapons)
{ {

View File

@@ -12,13 +12,24 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
public class CheckConditions : ILintRulesPass public class CheckConditions : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, emitWarning, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, emitWarning, mapRules);
}
void Run(Action<string> emitError, Action<string> emitWarning, Ruleset rules)
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -12,12 +12,13 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Network;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
public class CheckConflictingMouseBounds : ILintRulesPass public class CheckConflictingMouseBounds : ILintRulesPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -12,13 +12,24 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckCursors : ILintRulesPass class CheckCursors : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, modData, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, modData, mapRules);
}
void Run(Action<string> emitError, ModData modData, Ruleset rules)
{ {
var fileSystem = modData.DefaultFileSystem; var fileSystem = modData.DefaultFileSystem;
var sequenceYaml = MiniYaml.Merge(modData.Manifest.Cursors.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s))); var sequenceYaml = MiniYaml.Merge(modData.Manifest.Cursors.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s)));

View File

@@ -12,13 +12,24 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckDefaultVisibility : ILintRulesPass class CheckDefaultVisibility : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -12,13 +12,24 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckHitShapes : ILintRulesPass class CheckHitShapes : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -12,13 +12,24 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
public class CheckLocomotorReferences : ILintRulesPass public class CheckLocomotorReferences : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{ {
var worldActor = rules.Actors[SystemActors.World]; var worldActor = rules.Actors[SystemActors.World];
var locomotorInfos = worldActor.TraitInfos<LocomotorInfo>().ToArray(); var locomotorInfos = worldActor.TraitInfos<LocomotorInfo>().ToArray();

View File

@@ -11,24 +11,34 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.Server;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
public class CheckMapMetadata : ILintMapPass public class CheckMapMetadata : ILintMapPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map) void ILintMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map)
{ {
if (map.MapFormat != Map.SupportedMapFormat) Run(emitError, map.MapFormat, map.Author, map.Title, map.Categories);
emitError("Map format {0} does not match the supported version {1}." }
.F(map.MapFormat, Map.SupportedMapFormat));
if (map.Author == null) void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, map.MapFormat, map.Author, map.Title, map.Categories);
}
void Run(Action<string> 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 (author == null)
emitError("Map does not define a valid author."); emitError("Map does not define a valid author.");
if (map.Title == null) if (title == null)
emitError("Map does not define a valid title."); emitError("Map does not define a valid title.");
if (!map.Categories.Any()) if (!categories.Any())
emitError("Map does not define any categories."); emitError("Map does not define any categories.");
} }
} }

View File

@@ -11,15 +11,25 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.GameRules;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckNotifications : ILintRulesPass class CheckNotifications : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -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<string> emitError, Action<string> 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<RequiresSpecificOwnersInfo>())
.ToDictionary(a => a.Key, a => a.Value.TraitInfo<RequiresSpecificOwnersInfo>());
foreach (var kv in map.ActorDefinitions)
{
var actorReference = new ActorReference(kv.Value.Value, kv.Value.ToDictionary());
var ownerInit = actorReference.GetOrDefault<OwnerInit>();
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(", ")));
}
}
}
}
}

View File

@@ -12,18 +12,28 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckPalettes : ILintRulesPass class CheckPalettes : ILintRulesPass, ILintServerMapPass
{ {
List<string> palettes = new List<string>(); void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
List<string> playerPalettes = new List<string>();
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{ {
GetPalettes(emitError, rules); Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{
var palettes = new List<string>();
var playerPalettes = new List<string>();
GetPalettes(emitError, rules, palettes, playerPalettes);
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {
@@ -111,7 +121,7 @@ namespace OpenRA.Mods.Common.Lint
} }
} }
void GetPalettes(Action<string> emitError, Ruleset rules) void GetPalettes(Action<string> emitError, Ruleset rules, List<string> palettes, List<string> playerPalettes)
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -13,21 +13,39 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
public class CheckPlayers : ILintMapPass public class CheckPlayers : ILintMapPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map) void ILintMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map)
{ {
var players = new MapPlayers(map.PlayerDefinitions).Players; var players = new MapPlayers(map.PlayerDefinitions);
if (players.Count > 64) var spawns = new List<CPos>();
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<LocationInit>().Value);
}
Run(emitError, emitWarning, players, map.Visibility, map.Rules.Actors[SystemActors.World], spawns.ToArray());
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, emitWarning, map.Players, map.Visibility, map.WorldActorInfo, map.SpawnPoints);
}
void Run(Action<string> emitError, Action<string> emitWarning, MapPlayers players, MapVisibility visibility, ActorInfo worldActorInfo, CPos[] spawnPoints)
{
if (players.Players.Count > 64)
emitError("Defining more than 64 players is not allowed."); emitError("Defining more than 64 players is not allowed.");
var worldOwnerFound = false; var worldOwnerFound = false;
var playerNames = players.Values.Select(p => p.Name).ToHashSet(); var playerNames = players.Players.Values.Select(p => p.Name).ToHashSet();
foreach (var player in players.Values) foreach (var player in players.Players.Values)
{ {
foreach (var ally in player.Allies) foreach (var ally in player.Allies)
if (!playerNames.Contains(ally)) if (!playerNames.Contains(ally))
@@ -46,7 +64,7 @@ namespace OpenRA.Mods.Common.Lint
if (player.Playable) if (player.Playable)
emitError("The player {0} owning the world can't be playable.".F(player.Name)); 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 // 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)); emitError("The player {0} must specify LockFaction: True.".F(player.Name));
@@ -56,51 +74,20 @@ namespace OpenRA.Mods.Common.Lint
if (!worldOwnerFound) if (!worldOwnerFound)
emitError("Found no player owning the world."); emitError("Found no player owning the world.");
var worldActor = map.Rules.Actors[SystemActors.World]; var factions = worldActorInfo.TraitInfos<FactionInfo>().Select(f => f.InternalName).ToHashSet();
var factions = worldActor.TraitInfos<FactionInfo>().Select(f => f.InternalName).ToHashSet(); foreach (var player in players.Players.Values)
foreach (var player in players.Values)
if (!string.IsNullOrWhiteSpace(player.Faction) && !factions.Contains(player.Faction)) if (!string.IsNullOrWhiteSpace(player.Faction) && !factions.Contains(player.Faction))
emitError("Invalid faction {0} chosen for player {1}.".F(player.Faction, player.Name)); emitError("Invalid faction {0} chosen for player {1}.".F(player.Faction, player.Name));
if (worldActor.HasTraitInfo<MapStartingLocationsInfo>()) if (worldActorInfo.HasTraitInfo<MapStartingLocationsInfo>())
{ {
var playerCount = players.Count(p => p.Value.Playable); var playerCount = players.Players.Count(p => p.Value.Playable);
var spawns = new List<CPos>(); if (playerCount > spawnPoints.Length)
foreach (var kv in map.ActorDefinitions.Where(d => d.Value.Value == "mpspawn")) emitError("The map allows {0} possible players, but defines only {1} spawn points".F(playerCount, spawnPoints.Length));
{
var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary());
spawns.Add(s.Get<LocationInit>().Value);
}
if (playerCount > spawns.Count) if (spawnPoints.Distinct().Count() != spawnPoints.Length)
emitError("The map allows {0} possible players, but defines only {1} spawn points".F(playerCount, spawns.Count));
if (spawns.Distinct().Count() != spawns.Count)
emitError("Duplicate spawn point locations detected."); emitError("Duplicate spawn point locations detected.");
} }
// Check for actors that require specific owners
var actorsWithRequiredOwner = map.Rules.Actors
.Where(a => a.Value.HasTraitInfo<RequiresSpecificOwnersInfo>())
.ToDictionary(a => a.Key, a => a.Value.TraitInfo<RequiresSpecificOwnersInfo>());
foreach (var kv in map.ActorDefinitions)
{
var actorReference = new ActorReference(kv.Value.Value, kv.Value.ToDictionary());
var ownerInit = actorReference.GetOrDefault<OwnerInit>();
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(", ")));
}
}
} }
} }
} }

View File

@@ -11,12 +11,23 @@
using System; using System;
using OpenRA.Mods.Common.Projectiles; using OpenRA.Mods.Common.Projectiles;
using OpenRA.Server;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckRangeLimit : ILintRulesPass class CheckRangeLimit : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{ {
foreach (var weaponInfo in rules.Weapons) foreach (var weaponInfo in rules.Weapons)
{ {

View File

@@ -12,13 +12,24 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckRevealFootprint : ILintRulesPass class CheckRevealFootprint : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -12,12 +12,23 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits.Render; using OpenRA.Mods.Common.Traits.Render;
using OpenRA.Server;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckSpriteBodies : ILintRulesPass class CheckSpriteBodies : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -12,12 +12,23 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckTooltips : ILintRulesPass class CheckTooltips : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -11,12 +11,23 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.Server;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
public class CheckTraitPrerequisites : ILintRulesPass public class CheckTraitPrerequisites : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, emitWarning, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, emitWarning, mapRules);
}
void Run(Action<string> emitError, Action<string> emitWarning, Ruleset rules)
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -12,11 +12,29 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Server;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckUnknownTraitFields : ILintPass, ILintMapPass class CheckUnknownTraitFields : ILintPass, ILintMapPass, ILintServerMapPass
{ {
void ILintPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData)
{
foreach (var f in modData.Manifest.Rules)
CheckActors(MiniYaml.FromStream(modData.DefaultFileSystem.Open(f), f), emitError, modData);
}
void ILintMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map)
{
CheckMapYaml(emitError, modData, map, map.RuleDefinitions);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
CheckMapYaml(emitError, modData, map, map.RuleDefinitions);
}
string NormalizeName(string key) string NormalizeName(string key)
{ {
var name = key.Split('@')[0]; var name = key.Split('@')[0];
@@ -64,23 +82,17 @@ namespace OpenRA.Mods.Common.Lint
} }
} }
void ILintPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData) void CheckMapYaml(Action<string> emitError, ModData modData, IReadOnlyFileSystem fileSystem, MiniYaml ruleDefinitions)
{ {
foreach (var f in modData.Manifest.Rules) if (ruleDefinitions == null)
CheckActors(MiniYaml.FromStream(modData.DefaultFileSystem.Open(f), f), emitError, modData); return;
}
void ILintMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map) var mapFiles = FieldLoader.GetValue<string[]>("value", ruleDefinitions.Value);
{ foreach (var f in mapFiles)
if (map.RuleDefinitions != null && map.RuleDefinitions.Value != null) CheckActors(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, modData);
{
var mapFiles = FieldLoader.GetValue<string[]>("value", map.RuleDefinitions.Value);
foreach (var f in mapFiles)
CheckActors(MiniYaml.FromStream(map.Open(f), f), emitError, modData);
if (map.RuleDefinitions.Nodes.Any()) if (ruleDefinitions.Nodes.Any())
CheckActors(map.RuleDefinitions.Nodes, emitError, modData); CheckActors(ruleDefinitions.Nodes, emitError, modData);
}
} }
} }
} }

View File

@@ -12,12 +12,30 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.FileSystem;
using OpenRA.GameRules; using OpenRA.GameRules;
using OpenRA.Server;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class CheckUnknownWeaponFields : ILintPass, ILintMapPass class CheckUnknownWeaponFields : ILintPass, ILintMapPass, ILintServerMapPass
{ {
void ILintPass.Run(Action<string> emitError, Action<string> 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<string> emitError, Action<string> emitWarning, ModData modData, Map map)
{
CheckMapYaml(emitError, emitWarning, modData, map, map.WeaponDefinitions);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
CheckMapYaml(emitError, emitWarning, modData, map, map.WeaponDefinitions);
}
string NormalizeName(string key) string NormalizeName(string key)
{ {
var name = key.Split('@')[0]; var name = key.Split('@')[0];
@@ -81,23 +99,17 @@ namespace OpenRA.Mods.Common.Lint
} }
} }
void ILintPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData) void CheckMapYaml(Action<string> emitError, Action<string> emitWarning, ModData modData, IReadOnlyFileSystem fileSystem, MiniYaml weaponDefinitions)
{ {
foreach (var f in modData.Manifest.Weapons) if (weaponDefinitions == null)
CheckWeapons(MiniYaml.FromStream(modData.DefaultFileSystem.Open(f), f), emitError, emitWarning, modData); return;
}
void ILintMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map) var mapFiles = FieldLoader.GetValue<string[]>("value", weaponDefinitions.Value);
{ foreach (var f in mapFiles)
if (map.WeaponDefinitions != null && map.WeaponDefinitions.Value != null) CheckWeapons(MiniYaml.FromStream(fileSystem.Open(f), f), emitError, emitWarning, modData);
{
var mapFiles = FieldLoader.GetValue<string[]>("value", map.WeaponDefinitions.Value);
foreach (var f in mapFiles)
CheckWeapons(MiniYaml.FromStream(map.Open(f), f), emitError, emitWarning, modData);
if (map.WeaponDefinitions.Nodes.Any()) if (weaponDefinitions.Nodes.Any())
CheckWeapons(map.WeaponDefinitions.Nodes, emitError, emitWarning, modData); CheckWeapons(weaponDefinitions.Nodes, emitError, emitWarning, modData);
}
} }
} }
} }

View File

@@ -12,13 +12,24 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
public class CheckVoiceReferences : ILintRulesPass public class CheckVoiceReferences : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {

View File

@@ -12,12 +12,23 @@
using System; using System;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
{ {
class LintBuildablePrerequisites : ILintRulesPass class LintBuildablePrerequisites : ILintRulesPass, ILintServerMapPass
{ {
public void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules) void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules);
}
void Run(Action<string> emitError, Ruleset rules)
{ {
var providedPrereqs = rules.Actors.SelectMany(a => a.Value.TraitInfos<ITechTreePrerequisiteInfo>().SelectMany(p => p.Prerequisites(a.Value))); var providedPrereqs = rules.Actors.SelectMany(a => a.Value.TraitInfos<ITechTreePrerequisiteInfo>().SelectMany(p => p.Prerequisites(a.Value)));