From 3a17b26405fc2d09b80582d268022670f871931e Mon Sep 17 00:00:00 2001 From: teinarss Date: Sun, 7 Jul 2019 19:45:48 +0200 Subject: [PATCH] Creating PlayerMask --- OpenRA.Game/Player.cs | 11 ++ OpenRA.Game/Primitives/LongBitSet.cs | 194 +++++++++++++++++++++++++++ OpenRA.Game/World.cs | 32 +++++ 3 files changed, 237 insertions(+) create mode 100644 OpenRA.Game/Primitives/LongBitSet.cs diff --git a/OpenRA.Game/Player.cs b/OpenRA.Game/Player.cs index 634664c8f4..4b49194700 100644 --- a/OpenRA.Game/Player.cs +++ b/OpenRA.Game/Player.cs @@ -33,6 +33,8 @@ namespace OpenRA public enum WinState { Undefined, Won, Lost } + public class PlayerBitMask { } + public class Player : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding { struct StanceColors @@ -71,6 +73,12 @@ namespace OpenRA readonly bool inMissionMap; readonly IUnlocksRenderPlayer[] unlockRenderPlayer; + // Each player is identified with a unique bit in the set + // Cache masks for the player's index and ally/enemy player indices for performance. + public LongBitSet PlayerMask; + public LongBitSet AllyMask = default(LongBitSet); + public LongBitSet EnemyMask = default(LongBitSet); + public bool UnlockedRenderPlayer { get @@ -153,6 +161,9 @@ namespace OpenRA DisplayFaction = ChooseDisplayFaction(world, pr.Faction); } + if (!Spectating) + PlayerMask = new LongBitSet(InternalName); + var playerActorType = world.Type == WorldType.Editor ? "EditorPlayer" : "Player"; PlayerActor = world.CreateActor(playerActorType, new TypeDictionary { new OwnerInit(this) }); Shroud = PlayerActor.Trait(); diff --git a/OpenRA.Game/Primitives/LongBitSet.cs b/OpenRA.Game/Primitives/LongBitSet.cs new file mode 100644 index 0000000000..2ef0e9826c --- /dev/null +++ b/OpenRA.Game/Primitives/LongBitSet.cs @@ -0,0 +1,194 @@ +#region Copyright & License Information +/* + * Copyright 2007-2019 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.Primitives +{ + static class LongBitSetAllocator where T : class + { + static readonly Cache Bits = new Cache(Allocate); + static long nextBits = 1; + + static long Allocate(string value) + { + lock (Bits) + { + var bits = nextBits; + nextBits <<= 1; + + if (nextBits == 0) + throw new OverflowException("Trying to allocate bit index outside of index 64."); + + return bits; + } + } + + public static long GetBits(string[] values) + { + long bits = 0; + lock (Bits) + foreach (var value in values) + bits |= Bits[value]; + + return bits; + } + + public static long GetBitsNoAlloc(string[] values) + { + // Map strings to existing bits; do not allocate missing values new bits + long bits = 0; + + lock (Bits) + { + foreach (var value in values) + { + long valueBit; + if (Bits.TryGetValue(value, out valueBit)) + bits |= valueBit; + } + } + + return bits; + } + + public static IEnumerable GetStrings(long bits) + { + var values = new List(); + lock (Bits) + foreach (var kvp in Bits) + if ((kvp.Value & bits) != 0) + values.Add(kvp.Key); + + return values; + } + + public static bool BitsContainString(long bits, string value) + { + long valueBit; + lock (Bits) + if (!Bits.TryGetValue(value, out valueBit)) + return false; + return (bits & valueBit) != 0; + } + + public static void Reset() + { + lock (Bits) + { + Bits.Clear(); + nextBits = 1; + } + } + } + + // Opitmized BitSet to be used only when guaranteed to be no more than 64 values. + public struct LongBitSet : IEnumerable, IEquatable> where T : class + { + readonly long bits; + + public LongBitSet(params string[] values) + : this(LongBitSetAllocator.GetBits(values)) { } + + LongBitSet(long bits) { this.bits = bits; } + + public static LongBitSet FromStringsNoAlloc(string[] values) + { + return new LongBitSet(LongBitSetAllocator.GetBitsNoAlloc(values)) { }; + } + + public static void Reset() + { + LongBitSetAllocator.Reset(); + } + + public override string ToString() + { + return BitSetAllocator.GetStrings(bits).JoinWith(","); + } + + public static bool operator ==(LongBitSet me, LongBitSet other) { return me.bits == other.bits; } + public static bool operator !=(LongBitSet me, LongBitSet other) { return !(me == other); } + + public bool Equals(LongBitSet other) { return other == this; } + public override bool Equals(object obj) { return obj is LongBitSet && Equals((LongBitSet)obj); } + public override int GetHashCode() { return bits.GetHashCode(); } + + public bool IsEmpty { get { return bits == 0; } } + + public bool IsProperSubsetOf(LongBitSet other) + { + return IsSubsetOf(other) && !SetEquals(other); + } + + public bool IsProperSupersetOf(LongBitSet other) + { + return IsSupersetOf(other) && !SetEquals(other); + } + + public bool IsSubsetOf(LongBitSet other) + { + return (bits | other.bits) == other.bits; + } + + public bool IsSupersetOf(LongBitSet other) + { + return (bits | other.bits) == bits; + } + + public bool Overlaps(LongBitSet other) + { + return (bits & other.bits) != 0; + } + + public bool SetEquals(LongBitSet other) + { + return bits == other.bits; + } + + public bool Contains(string value) + { + return BitSetAllocator.BitsContainString(bits, value); + } + + public IEnumerator GetEnumerator() + { + return BitSetAllocator.GetStrings(bits).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public LongBitSet Except(LongBitSet other) + { + return new LongBitSet(bits & ~other.bits); + } + + public LongBitSet Intersect(LongBitSet other) + { + return new LongBitSet(bits & other.bits); + } + + public LongBitSet SymmetricExcept(LongBitSet other) + { + return new LongBitSet(bits ^ other.bits); + } + + public LongBitSet Union(LongBitSet other) + { + return new LongBitSet(bits | other.bits); + } + } +} diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 6091264957..47bb2e647b 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -43,6 +43,7 @@ namespace OpenRA public readonly MersenneTwister SharedRandom; public readonly MersenneTwister LocalRandom; public readonly IModelCache ModelCache; + public LongBitSet AllPlayerMask = default(LongBitSet); public Player[] Players = new Player[0]; @@ -196,15 +197,27 @@ namespace OpenRA ScreenMap = WorldActor.Trait(); Selection = WorldActor.Trait(); + // Reset mask + LongBitSet.Reset(); + // Add players foreach (var cmp in WorldActor.TraitsImplementing()) cmp.CreatePlayers(this); // Set defaults for any unset stances foreach (var p in Players) + { + if (!p.Spectating) + AllPlayerMask = AllPlayerMask.Union(p.PlayerMask); + foreach (var q in Players) + { + SetUpPlayerMask(p, q); + if (!p.Stances.ContainsKey(q)) p.Stances[q] = Stance.Neutral; + } + } Game.Sound.SoundVolumeModifier = 1.0f; @@ -220,6 +233,25 @@ namespace OpenRA RulesContainTemporaryBlocker = map.Rules.Actors.Any(a => a.Value.HasTraitInfo()); } + void SetUpPlayerMask(Player p, Player q) + { + if (q.Spectating) + return; + + var bitSet = q.PlayerMask; + + switch (p.Stances[q]) + { + case Stance.Enemy: + case Stance.Neutral: + p.EnemyMask = p.EnemyMask.Union(bitSet); + break; + case Stance.Ally: + p.AllyMask = p.AllyMask.Union(bitSet); + break; + } + } + public void AddToMaps(Actor self, IOccupySpace ios) { ActorMap.AddInfluence(self, ios);