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 rules;
public readonly string players_block;
public readonly int mapformat;
}
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>
class InnerData
{
public int MapFormat;
public string Title;
public string[] Categories;
public string Author;
@@ -168,6 +170,7 @@ namespace OpenRA
volatile InnerData innerData;
public int MapFormat => innerData.MapFormat;
public string Title => innerData.Title;
public string[] Categories => innerData.Categories;
public string Author => innerData.Author;
@@ -183,6 +186,7 @@ namespace OpenRA
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;
@@ -234,6 +238,7 @@ namespace OpenRA
Uid = uid;
innerData = new InnerData
{
MapFormat = 0,
Title = "Unknown Map",
Categories = new[] { "Unknown" },
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)
{
Dictionary<string, MiniYaml> yaml;
@@ -297,6 +349,9 @@ namespace OpenRA
if (yaml.TryGetValue("RequiresMod", out temp))
requiresMod = temp.Value;
if (yaml.TryGetValue("MapFormat", out temp))
newData.MapFormat = FieldLoader.GetValue<int>("MapFormat", temp.Value);
newData.Status = mapCompatibility == null || mapCompatibility.Contains(requiresMod) ?
MapStatus.Available : MapStatus.Unavailable;
@@ -372,6 +427,7 @@ namespace OpenRA
newData.PlayerCount = r.players;
newData.Bounds = r.bounds;
newData.TileSet = r.tileset;
newData.MapFormat = r.mapformat;
var spawns = new CPos[r.spawnpoints.Length / 2];
for (var j = 0; j < r.spawnpoints.Length; j += 2)

View File

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

View File

@@ -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<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)
{

View File

@@ -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<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)
{

View File

@@ -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<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)
{

View File

@@ -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<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 sequenceYaml = MiniYaml.Merge(modData.Manifest.Cursors.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s)));

View File

@@ -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<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)
{

View File

@@ -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<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)
{

View File

@@ -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<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 locomotorInfos = worldActor.TraitInfos<LocomotorInfo>().ToArray();

View File

@@ -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<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)
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);
}
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.");
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.");
}
}

View File

@@ -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<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)
{

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.Collections.Generic;
using System.Linq;
using OpenRA.Server;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Lint
{
class CheckPalettes : ILintRulesPass
class CheckPalettes : ILintRulesPass, ILintServerMapPass
{
List<string> palettes = new List<string>();
List<string> playerPalettes = new List<string>();
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)
{
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)
{
@@ -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)
{

View File

@@ -13,21 +13,39 @@ using System;
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<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;
if (players.Count > 64)
var players = 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);
}
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.");
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 @@ namespace OpenRA.Mods.Common.Lint
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 @@ namespace OpenRA.Mods.Common.Lint
if (!worldOwnerFound)
emitError("Found no player owning the world.");
var worldActor = map.Rules.Actors[SystemActors.World];
var factions = worldActor.TraitInfos<FactionInfo>().Select(f => f.InternalName).ToHashSet();
foreach (var player in players.Values)
var factions = worldActorInfo.TraitInfos<FactionInfo>().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<MapStartingLocationsInfo>())
if (worldActorInfo.HasTraitInfo<MapStartingLocationsInfo>())
{
var playerCount = players.Count(p => p.Value.Playable);
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);
}
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 (playerCount > spawns.Count)
emitError("The map allows {0} possible players, but defines only {1} spawn points".F(playerCount, spawns.Count));
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<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 OpenRA.Mods.Common.Projectiles;
using OpenRA.Server;
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)
{

View File

@@ -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<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)
{

View File

@@ -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<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)
{

View File

@@ -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<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)
{

View File

@@ -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<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)
{

View File

@@ -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<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)
{
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)
CheckActors(MiniYaml.FromStream(modData.DefaultFileSystem.Open(f), f), emitError, modData);
}
if (ruleDefinitions == null)
return;
void ILintMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map)
{
if (map.RuleDefinitions != null && map.RuleDefinitions.Value != null)
{
var mapFiles = FieldLoader.GetValue<string[]>("value", map.RuleDefinitions.Value);
foreach (var f in mapFiles)
CheckActors(MiniYaml.FromStream(map.Open(f), f), emitError, modData);
var mapFiles = FieldLoader.GetValue<string[]>("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);
}
}
}

View File

@@ -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<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)
{
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)
CheckWeapons(MiniYaml.FromStream(modData.DefaultFileSystem.Open(f), f), emitError, emitWarning, modData);
}
if (weaponDefinitions == null)
return;
void ILintMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map)
{
if (map.WeaponDefinitions != null && map.WeaponDefinitions.Value != null)
{
var mapFiles = FieldLoader.GetValue<string[]>("value", map.WeaponDefinitions.Value);
foreach (var f in mapFiles)
CheckWeapons(MiniYaml.FromStream(map.Open(f), f), emitError, emitWarning, modData);
var mapFiles = FieldLoader.GetValue<string[]>("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);
}
}
}

View File

@@ -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<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)
{

View File

@@ -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<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)));