Merge pull request #11082 from pchote/custom-rule-detection

Fix and improve lobby custom rules warning.
This commit is contained in:
Oliver Brakmann
2016-04-10 14:52:06 +02:00
7 changed files with 79 additions and 8 deletions

View File

@@ -200,5 +200,55 @@ namespace OpenRA
return ruleset;
}
static bool AnyCustomYaml(MiniYaml yaml)
{
return yaml != null && (yaml.Value != null || yaml.Nodes.Any());
}
static bool AnyFlaggedTraits(ModData modData, List<MiniYamlNode> actors)
{
foreach (var actorNode in actors)
{
foreach (var traitNode in actorNode.Value.Nodes)
{
try
{
var traitName = traitNode.Key.Split('@')[0];
var traitType = modData.ObjectCreator.FindType(traitName + "Info");
if (traitType.GetInterface("ILobbyCustomRulesIgnore") == null)
return true;
}
catch { }
}
}
return false;
}
public static bool DefinesUnsafeCustomRules(ModData modData, IReadOnlyFileSystem fileSystem,
MiniYaml mapRules, MiniYaml mapWeapons, MiniYaml mapVoices, MiniYaml mapNotifications, MiniYaml mapSequences)
{
// Maps that define any weapon, voice, notification, or sequence overrides are always flagged
if (AnyCustomYaml(mapWeapons) || AnyCustomYaml(mapVoices) || AnyCustomYaml(mapNotifications) || AnyCustomYaml(mapSequences))
return true;
// Any trait overrides that aren't explicitly whitelisted are flagged
if (mapRules != null)
{
if (AnyFlaggedTraits(modData, mapRules.Nodes))
return true;
if (mapRules.Value != null)
{
var mapFiles = FieldLoader.GetValue<string[]>("value", mapRules.Value);
foreach (var f in mapFiles)
if (AnyFlaggedTraits(modData, MiniYaml.FromStream(fileSystem.Open(f))))
return true;
}
}
return false;
}
}
}

View File

@@ -21,6 +21,7 @@ using System.Text;
using System.Threading;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA
{
@@ -80,12 +81,14 @@ namespace OpenRA
Lazy<Ruleset> rules;
public Ruleset Rules { get { return rules != null ? rules.Value : null; } }
public bool InvalidCustomRules { get; private set; }
public bool DefinesUnsafeCustomRules { get; private set; }
public bool RulesLoaded { get; private set; }
public void SetRulesetGenerator(ModData modData, Func<Ruleset> generator)
public void SetRulesetGenerator(ModData modData, Func<Pair<Ruleset, bool>> generator)
{
InvalidCustomRules = false;
RulesLoaded = false;
DefinesUnsafeCustomRules = false;
// Note: multiple threads may try to access the value at the same time
// We rely on the thread-safety guarantees given by Lazy<T> to prevent race conitions.
@@ -97,7 +100,9 @@ namespace OpenRA
try
{
return generator();
var ret = generator();
DefinesUnsafeCustomRules = ret.Second;
return ret.First;
}
catch (Exception e)
{
@@ -146,6 +151,15 @@ namespace OpenRA
public Ruleset Rules { get { return innerData.Rules; } }
public bool InvalidCustomRules { get { return innerData.InvalidCustomRules; } }
public bool RulesLoaded { get { return innerData.RulesLoaded; } }
public bool DefinesUnsafeCustomRules
{
get
{
// Force lazy rules to be evaluated
var force = innerData.Rules;
return innerData.DefinesUnsafeCustomRules;
}
}
Download download;
public long DownloadBytes { get; private set; }
@@ -297,8 +311,11 @@ namespace OpenRA
var musicDefinitions = LoadRuleSection(yaml, "Music");
var notificationDefinitions = LoadRuleSection(yaml, "Notifications");
var sequenceDefinitions = LoadRuleSection(yaml, "Sequences");
return Ruleset.Load(modData, this, TileSet, ruleDefinitions, weaponDefinitions,
var rules = Ruleset.Load(modData, this, TileSet, ruleDefinitions, weaponDefinitions,
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions);
var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
return Pair.New(rules, flagged);
});
if (p.Contains("map.png"))
@@ -384,8 +401,11 @@ namespace OpenRA
var musicDefinitions = LoadRuleSection(rulesYaml, "Music");
var notificationDefinitions = LoadRuleSection(rulesYaml, "Notifications");
var sequenceDefinitions = LoadRuleSection(rulesYaml, "Sequences");
return Ruleset.Load(modData, this, TileSet, ruleDefinitions, weaponDefinitions,
var rules = Ruleset.Load(modData, this, TileSet, ruleDefinitions, weaponDefinitions,
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions);
var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
return Pair.New(rules, flagged);
});
}
catch (Exception) { }

View File

@@ -392,7 +392,7 @@ namespace OpenRA.Server
SendOrderTo(newConn, "Message", motd);
}
if (Map.Rules != ModData.DefaultRules && !LobbyInfo.IsSinglePlayer)
if (!LobbyInfo.IsSinglePlayer && Map.DefinesUnsafeCustomRules)
SendOrderTo(newConn, "Message", "This map contains custom rules. Game experience may change.");
if (Settings.DisableSinglePlayer)

View File

@@ -319,6 +319,7 @@ namespace OpenRA.Traits
public interface ITraitInfo : ITraitInfoInterface { object Create(ActorInitializer init); }
public class TraitInfo<T> : ITraitInfo where T : new() { public virtual object Create(ActorInitializer init) { return new T(); } }
public interface ILobbyCustomRulesIgnore { }
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:InterfaceNamesMustBeginWithI", Justification = "Not a real interface, but more like a tag.")]
public interface Requires<T> where T : class, ITraitInfoInterface { }

View File

@@ -398,7 +398,7 @@ namespace OpenRA.Mods.Common.Server
server.SendMessage("{0} changed the map to {1}.".F(client.Name, server.Map.Title));
if (server.Map.Rules.Actors != server.ModData.DefaultRules.Actors)
if (server.Map.DefinesUnsafeCustomRules)
server.SendMessage("This map contains custom rules. Game experience may change.");
if (server.Settings.DisableSinglePlayer)

View File

@@ -17,7 +17,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Used for day/night effects.")]
class GlobalLightingPaletteEffectInfo : ITraitInfo
class GlobalLightingPaletteEffectInfo : ITraitInfo, ILobbyCustomRulesIgnore
{
[Desc("Do not modify graphics that use any palette in this list.")]
public readonly HashSet<string> ExcludePalettes = new HashSet<string> { "cursor", "chrome", "colorpicker", "fog", "shroud", "alpha" };

View File

@@ -17,7 +17,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Adds a particle-based overlay.")]
public class WeatherOverlayInfo : ITraitInfo
public class WeatherOverlayInfo : ITraitInfo, ILobbyCustomRulesIgnore
{
[Desc("Factor for particle density. As higher as more particles will get spawned.")]
public readonly float ParticleDensityFactor = 0.0007625f;