Refactor: Remove TowerRegistry, replicate towers by TypeID

Removing redundant workflow and making sure there's just once source of truth for the list of available towers.
This commit is contained in:
Matt F 2026-06-25 11:21:43 -07:00
parent 63b8195985
commit a87b221d87
5 changed files with 33 additions and 159 deletions

View file

@ -1,5 +1,4 @@
// Assets/_Project/Scripts/Gameplay/TowerInstance.cs
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using TD.Core;
@ -32,11 +31,11 @@ namespace TD.Gameplay
/// <see cref="SetOccupied"/> are idempotent writes, so double-stamping is safe.</para>
///
/// <para><b>Definition reference replication.</b> TowerDefinition assets live in the
/// project on all clients. We replicate the asset by name via a
/// <see cref="NetworkVariable{T}"/> holding a <c>FixedString64Bytes</c>, then look
/// up the asset locally. This avoids serializing the full ScriptableObject over the
/// network. The lookup uses a <see cref="TowerRegistry"/> singleton that must be
/// present in the scene. (Temporary: will be driven by RaceDefinition in Path E.)</para>
/// project on all clients. We replicate the tower's <c>TowerTypeId</c> (its index in
/// <see cref="TowerPlacementManager"/>'s catalog) via a <see cref="NetworkVariable{T}"/>,
/// then resolve the full asset locally with <see cref="TowerPlacementManager.GetDefinition"/>.
/// This avoids serializing the ScriptableObject over the network and reuses the same
/// identifier placement and the deck already use — a single source of truth.</para>
///
/// <para><b>Combat.</b> No combat logic here yet. Combat fields live stubbed on
/// <see cref="TowerDefinition"/>; they will be consumed by a future
@ -58,12 +57,13 @@ namespace TD.Gameplay
// ----- Networked state ------------------------------------------------
// The name of the TowerDefinition asset for this tower. Replicated so all
// clients can look up the full definition locally without sending the whole
// ScriptableObject over the wire.
private readonly NetworkVariable<FixedString64Bytes> definitionName =
new NetworkVariable<FixedString64Bytes>(
default,
// The TowerTypeId (index into TowerPlacementManager's catalog) for this tower.
// Replicated so every client resolves the full definition locally via the catalog —
// the same identifier placement and the deck already use. 0 = unset/invalid (the
// reserved catalog index). Replaces the old replicate-by-name + TowerRegistry path.
private readonly NetworkVariable<int> definitionTypeId =
new NetworkVariable<int>(
0,
readPerm: NetworkVariableReadPermission.Everyone,
writePerm: NetworkVariableWritePermission.Server);
@ -95,8 +95,8 @@ namespace TD.Gameplay
// ----- Local resolved state -------------------------------------------
// Resolved on every client in OnNetworkSpawn from definitionName.
// Null if the lookup fails (missing TowerRegistry or unknown name).
// Resolved on every client in OnNetworkSpawn from definitionTypeId via the catalog.
// Null if the lookup fails (TypeId not in the catalog on this peer).
private TowerDefinition resolvedDefinition;
// ----- Pre-spawn initialization data ----------------------------------
@ -192,7 +192,7 @@ namespace TD.Gameplay
hasPendingInit = true;
// Cache the resolved definition on the server immediately — clients
// will resolve via the registry lookup once definitionName arrives.
// resolve from the replicated TowerTypeId via the catalog once it arrives.
resolvedDefinition = def;
}
@ -206,7 +206,17 @@ namespace TD.Gameplay
// every client sees correct values on its very first OnNetworkSpawn.
if (IsServer && hasPendingInit)
{
definitionName.Value = new FixedString64Bytes(pendingDefinition.name);
// Resolve the catalog index for this definition and replicate that.
int typeId = 0;
var pm = TowerPlacementManager.Instance;
if (pm == null || !pm.TryGetTypeId(pendingDefinition, out typeId))
{
Debug.LogError($"[TowerInstance] Could not resolve a TowerTypeId for " +
$"'{pendingDefinition?.name}'. Ensure it is in the " +
$"TowerPlacementManager catalog (towerDefinitions).");
}
definitionTypeId.Value = typeId;
anchorTile.Value = pendingAnchor;
ownerSlot.Value = pendingOwner;
@ -331,27 +341,13 @@ namespace TD.Gameplay
// Already resolved (server path via InitializeServer).
if (resolvedDefinition != null) return;
string defName = definitionName.Value.ToString();
if (string.IsNullOrEmpty(defName))
{
Debug.LogError($"[TowerInstance] NetworkObject {NetworkObjectId}: " +
$"definitionName is empty. Cannot resolve TowerDefinition.");
return;
}
var registry = TowerRegistry.Instance;
if (registry == null)
{
Debug.LogError($"[TowerInstance] NetworkObject {NetworkObjectId}: " +
$"No TowerRegistry found in scene. Cannot resolve '{defName}'.");
return;
}
resolvedDefinition = registry.Get(defName);
int typeId = definitionTypeId.Value;
resolvedDefinition = TowerPlacementManager.GetDefinition(typeId);
if (resolvedDefinition == null)
{
Debug.LogError($"[TowerInstance] NetworkObject {NetworkObjectId}: " +
$"TowerRegistry does not contain a definition named '{defName}'.");
$"no TowerDefinition for TypeId {typeId}. Is the " +
$"TowerPlacementManager catalog populated on this peer?");
}
}