From bf3c0087777009387d790c2cce93a3d60de3c13a Mon Sep 17 00:00:00 2001 From: Ian Woods Date: Tue, 7 Jul 2026 23:43:15 -0700 Subject: [PATCH] bit field swapped for normal list at API boundaries --- .../Scripts/Gameplay/BuilderUpgradeManager.cs | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs b/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs index 0013ef9..5f07d31 100644 --- a/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs +++ b/Assets/_Project/Scripts/Gameplay/BuilderUpgradeManager.cs @@ -13,10 +13,12 @@ namespace TD.Gameplay /// . /// /// - /// Bitmask, not a list. A builder effect carries no per-grant state (no level, - /// no enable/disable toggle) — it's either granted or it isn't — so the granted set is one - /// bitmask keyed by rather - /// than a NetworkList. "Has effect" and "grant effect" are both O(1) bit ops. + /// List of granted kinds. A builder effect carries no per-grant state (no + /// level, no enable/disable toggle) — it's either granted or it isn't — so the granted set + /// is a of values. Stored as + /// byte because NetworkList<T> requires T : IEquatable<T> + /// and plain enums don't implement that (see for the + /// same constraint on a struct); byte already does, for free. /// /// Granted via draft. /// calls when a player picks it. Effects are permanent for @@ -60,13 +62,10 @@ namespace TD.Gameplay // ----- Networked state -------------------------------------------- - // One bit per BuilderEffectKind. readPerm Everyone (consistent with the other - // per-player managers); writePerm Server. - private readonly NetworkVariable grantedMask = new NetworkVariable( - value: 0, - readPerm: NetworkVariableReadPermission.Everyone, - writePerm: NetworkVariableWritePermission.Server - ); + // Granted effect kinds, stored as byte (see class remarks for why not the enum + // directly). readPerm Everyone (consistent with the other per-player managers); + // writePerm Server — both are NetworkList's defaults. + private readonly NetworkList grantedKinds = new NetworkList(); /// Fired on every peer when a new effect is granted. public event System.Action OnUpgradesChanged; @@ -76,23 +75,23 @@ namespace TD.Gameplay public override void OnNetworkSpawn() { s_byClientId[OwnerClientId] = this; - grantedMask.OnValueChanged += HandleMaskChanged; + grantedKinds.OnListChanged += HandleGrantedKindsChanged; } public override void OnNetworkDespawn() { - grantedMask.OnValueChanged -= HandleMaskChanged; + grantedKinds.OnListChanged -= HandleGrantedKindsChanged; if (s_byClientId.TryGetValue(OwnerClientId, out var registered) && registered == this) s_byClientId.Remove(OwnerClientId); } - private void HandleMaskChanged(uint previous, uint current) => OnUpgradesChanged?.Invoke(); + private void HandleGrantedKindsChanged(NetworkListEvent change) => OnUpgradesChanged?.Invoke(); // ----- Read API ----------------------------------------------------- /// True if this player has been granted the given effect. - public bool PlayerHasEffect(BuilderEffectKind kind) => (grantedMask.Value & BitFor(kind)) != 0; + public bool PlayerHasEffect(BuilderEffectKind kind) => grantedKinds.Contains((byte)kind); /// /// Returns this player's for @@ -113,13 +112,11 @@ namespace TD.Gameplay { if (!IsServer) return false; - uint bit = BitFor(kind); - if ((grantedMask.Value & bit) != 0) return false; + byte value = (byte)kind; + if (grantedKinds.Contains(value)) return false; - grantedMask.Value |= bit; + grantedKinds.Add(value); return true; } - - private static uint BitFor(BuilderEffectKind kind) => 1u << (int)kind; } }