Add a lint check for trait placement on hardcoded actor names.

This commit is contained in:
Matthias Mailänder
2021-04-11 14:30:00 +01:00
committed by reaperrr
parent 0d3c624bbc
commit 5a0bcc01a6
129 changed files with 266 additions and 67 deletions

View File

@@ -24,6 +24,15 @@ using OpenRA.Traits;
namespace OpenRA
{
[Flags]
public enum SystemActors
{
Player = 0,
EditorPlayer = 1,
World = 2,
EditorWorld = 4
}
public sealed class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding, IEquatable<Actor>, IDisposable
{
internal readonly struct SyncHash

View File

@@ -22,7 +22,7 @@ namespace OpenRA
{
public class Ruleset
{
public readonly IReadOnlyDictionary<string, ActorInfo> Actors;
public readonly ActorInfoDictionary Actors;
public readonly IReadOnlyDictionary<string, WeaponInfo> Weapons;
public readonly IReadOnlyDictionary<string, SoundInfo> Voices;
public readonly IReadOnlyDictionary<string, SoundInfo> Notifications;
@@ -41,7 +41,7 @@ namespace OpenRA
SequenceProvider sequences,
IReadOnlyDictionary<string, MiniYamlNode> modelSequences)
{
Actors = actors;
Actors = new ActorInfoDictionary(actors);
Weapons = weapons;
Voices = voices;
Notifications = notifications;

View File

@@ -31,7 +31,7 @@ namespace OpenRA.Graphics
// Overwrite previous definitions if there are duplicates
var pals = new Dictionary<string, IProvidesCursorPaletteInfo>();
foreach (var p in modData.DefaultRules.Actors["world"].TraitInfos<IProvidesCursorPaletteInfo>())
foreach (var p in modData.DefaultRules.Actors[SystemActors.World].TraitInfos<IProvidesCursorPaletteInfo>())
if (p.Palette != null)
pals[p.Palette] = p;

View File

@@ -708,7 +708,7 @@ namespace OpenRA
}
// ResourceLayer is on world actor, which isn't caught above, so an extra check for it.
var worldActorInfo = Rules.Actors["world"];
var worldActorInfo = Rules.Actors[SystemActors.World];
var worldimpsis = worldActorInfo.TraitInfos<IMapPreviewSignatureInfo>();
foreach (var worldimpsi in worldimpsis)
worldimpsi.PopulateMapPreviewSignatureCells(this, worldActorInfo, null, positions);

View File

@@ -30,7 +30,7 @@ namespace OpenRA
public MapPlayers(Ruleset rules, int playerCount)
{
var firstFaction = rules.Actors["world"].TraitInfos<FactionInfo>()
var firstFaction = rules.Actors[SystemActors.World].TraitInfos<FactionInfo>()
.First(f => f.Selectable).InternalName;
Players = new Dictionary<string, PlayerReference>

View File

@@ -37,9 +37,6 @@ namespace OpenRA
public class Player : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding
{
public const string PlayerActorType = "Player";
public const string EditorPlayerActorType = "EditorPlayer";
struct StanceColors
{
public Color Self;
@@ -129,13 +126,13 @@ namespace OpenRA
static FactionInfo ResolveFaction(World world, string factionName, MersenneTwister playerRandom, bool requireSelectable)
{
var factionInfos = world.Map.Rules.Actors["world"].TraitInfos<FactionInfo>();
var factionInfos = world.Map.Rules.Actors[SystemActors.World].TraitInfos<FactionInfo>();
return ResolveFaction(factionName, factionInfos, playerRandom, requireSelectable);
}
static FactionInfo ResolveDisplayFaction(World world, string factionName)
{
var factions = world.Map.Rules.Actors["world"].TraitInfos<FactionInfo>().ToArray();
var factions = world.Map.Rules.Actors[SystemActors.World].TraitInfos<FactionInfo>().ToArray();
return factions.FirstOrDefault(f => f.InternalName == factionName) ?? factions.First();
}
@@ -165,7 +162,7 @@ namespace OpenRA
{
ClientIndex = client.Index;
Color = client.Color;
PlayerName = ResolvePlayerName(client, world.LobbyInfo.Clients, world.Map.Rules.Actors["player"].TraitInfos<IBotInfo>());
PlayerName = ResolvePlayerName(client, world.LobbyInfo.Clients, world.Map.Rules.Actors[SystemActors.Player].TraitInfos<IBotInfo>());
BotType = client.Bot;
Faction = ResolveFaction(world, client.Faction, playerRandom, !pr.LockFaction);
@@ -206,8 +203,8 @@ namespace OpenRA
// querying player traits in INotifyCreated.Created would crash.
// Therefore assign the uninitialized actor and run the Created callbacks
// by calling Initialize ourselves.
var playerActorType = world.Type == WorldType.Editor ? EditorPlayerActorType : PlayerActorType;
PlayerActor = new Actor(world, playerActorType, new TypeDictionary { new OwnerInit(this) });
var playerActorType = world.Type == WorldType.Editor ? SystemActors.EditorPlayer : SystemActors.Player;
PlayerActor = new Actor(world, playerActorType.ToString(), new TypeDictionary { new OwnerInit(this) });
PlayerActor.Initialize(true);
Shroud = PlayerActor.Trait<Shroud>();

View File

@@ -0,0 +1,48 @@
#region Copyright & License Information
/*
* Copyright 2007-2021 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;
using System.Collections.Generic;
namespace OpenRA
{
public class ActorInfoDictionary : IReadOnlyDictionary<string, ActorInfo>
{
readonly Dictionary<string, ActorInfo> dict;
public ActorInfoDictionary(IReadOnlyDictionary<string, ActorInfo> dict)
{
if (dict == null)
throw new ArgumentNullException(nameof(dict));
this.dict = new Dictionary<string, ActorInfo>(dict);
}
public bool ContainsKey(string key) => dict.ContainsKey(key);
public bool TryGetValue(string key, out ActorInfo value) => dict.TryGetValue(key, out value);
public int Count => dict.Count;
public ActorInfo this[string key] => dict[key];
public ActorInfo this[SystemActors key] => dict[key.ToString().ToLowerInvariant()];
IEnumerable<string> IReadOnlyDictionary<string, ActorInfo>.Keys => dict.Keys;
IEnumerable<ActorInfo> IReadOnlyDictionary<string, ActorInfo>.Values => dict.Values;
public ICollection<string> Keys => dict.Keys;
public ICollection<ActorInfo> Values => dict.Values;
public IEnumerator<KeyValuePair<string, ActorInfo>> GetEnumerator() => dict.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => dict.GetEnumerator();
}
}

View File

@@ -159,7 +159,7 @@ namespace OpenRA.Scripting
var knownPlayerCommands = Game.ModData.ObjectCreator
.GetTypesImplementing<ScriptPlayerProperties>()
.ToArray();
PlayerCommands = FilterCommands(world.Map.Rules.Actors["player"], knownPlayerCommands);
PlayerCommands = FilterCommands(world.Map.Rules.Actors[SystemActors.Player], knownPlayerCommands);
runtime.Globals["EngineDir"] = Platform.EngineDir;
runtime.DoBuffer(File.Open(Path.Combine(Platform.EngineDir, "lua", "scriptwrapper.lua"), FileMode.Open, FileAccess.Read).ReadAllText(), "scriptwrapper.lua").Dispose();

View File

@@ -1209,7 +1209,7 @@ namespace OpenRA.Server
// The null padding is needed to keep the player indexes in sync with world.Players on the clients
// This will need to change if future code wants to use worldPlayers for other purposes
var playerRandom = new MersenneTwister(LobbyInfo.GlobalSettings.RandomSeed);
foreach (var cmpi in Map.Rules.Actors["world"].TraitInfos<ICreatePlayersInfo>())
foreach (var cmpi in Map.Rules.Actors[SystemActors.World].TraitInfos<ICreatePlayersInfo>())
cmpi.CreateServerPlayers(Map, LobbyInfo, worldPlayers, playerRandom);
if (recorder != null)

View File

@@ -403,7 +403,7 @@ namespace OpenRA
public static string SanitizedPlayerName(string dirty)
{
var forbiddenNames = new string[] { "Open", "Closed" };
var botNames = OpenRA.Game.ModData.DefaultRules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name);
var botNames = OpenRA.Game.ModData.DefaultRules.Actors[SystemActors.Player].TraitInfos<IBotInfo>().Select(t => t.Name);
var clean = SanitizedName(dirty);

View File

@@ -11,6 +11,7 @@
namespace OpenRA.Traits
{
[TraitLocation(SystemActors.World)]
[Desc("Checks for pause related desyncs. Attach this to the world actor.")]
public class DebugPauseStateInfo : TraitInfo
{

View File

@@ -105,4 +105,14 @@ namespace OpenRA.Traits
PlayerPaletteReferenceSwitch = playerPaletteReferenceSwitch;
}
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class TraitLocationAttribute : Attribute
{
public readonly SystemActors SystemActors;
public TraitLocationAttribute(SystemActors systemActors)
{
SystemActors = systemActors;
}
}
}

View File

@@ -22,6 +22,7 @@ namespace OpenRA.Traits
void OnVisibilityChanged(FrozenActor frozen);
}
[TraitLocation(SystemActors.Player)]
[Desc("Required for FrozenUnderFog to work. Attach this to the player actor.")]
public class FrozenActorLayerInfo : TraitInfo, Requires<ShroudInfo>
{

View File

@@ -14,7 +14,8 @@ using OpenRA.Primitives;
namespace OpenRA.Traits
{
[Desc("Add this to the Player actor definition.")]
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
[Desc("Add this to the World actor definition.")]
public class PlayerColorPaletteInfo : TraitInfo
{
[PaletteReference]

View File

@@ -14,6 +14,7 @@ using System.Collections.Generic;
namespace OpenRA.Traits
{
[TraitLocation(SystemActors.Player | SystemActors.EditorPlayer)]
[Desc("Required for shroud and fog visibility checks. Add this to the player actor.")]
public class ShroudInfo : TraitInfo, ILobbyOptions
{

View File

@@ -11,6 +11,7 @@
namespace OpenRA.Traits
{
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
[Desc("Enables visualization commands. Attach this to the world actor.")]
public class DebugVisualizationsInfo : TraitInfo<DebugVisualizations> { }

View File

@@ -14,6 +14,7 @@ using System.Collections.Generic;
namespace OpenRA.Traits
{
[Desc("Attach this to the `World` actor.")]
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
public class FactionInfo : TraitInfo<Faction>
{
[Desc("This is the name exposed to the players.")]

View File

@@ -30,6 +30,7 @@ namespace OpenRA.Traits
public override string ToString() { return "{0}->{1}".F(Actor.Info.Name, Bounds.GetType().Name); }
}
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
public class ScreenMapInfo : TraitInfo
{
[Desc("Size of partition bins (world pixels)")]

View File

@@ -16,6 +16,7 @@ using OpenRA.Graphics;
namespace OpenRA.Traits
{
[TraitLocation(SystemActors.World)]
public class ScreenShakerInfo : TraitInfo
{
public readonly float2 MinMultiplier = new float2(-3, -3);

View File

@@ -202,8 +202,8 @@ namespace OpenRA
ModelCache = modData.ModelSequenceLoader.CacheModels(map, modData, map.Rules.ModelSequences);
var worldActorType = type == WorldType.Editor ? "EditorWorld" : "World";
WorldActor = CreateActor(worldActorType, new TypeDictionary());
var worldActorType = type == WorldType.Editor ? SystemActors.EditorWorld : SystemActors.World;
WorldActor = CreateActor(worldActorType.ToString(), new TypeDictionary());
ActorMap = WorldActor.Trait<IActorMap>();
ScreenMap = WorldActor.Trait<ScreenMap>();
Selection = WorldActor.Trait<ISelection>();