165 lines
6.5 KiB
C#
Raw Normal View History

2024-10-17 17:23:05 +03:00
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Mirror
{
[AddComponentMenu("Network/ Interest Management/ Match/Match Interest Management")]
public class MatchInterestManagement : InterestManagement
{
[Header("Diagnostics")]
[ReadOnly, SerializeField]
internal ushort matchCount;
readonly Dictionary<Guid, HashSet<NetworkMatch>> matchObjects =
new Dictionary<Guid, HashSet<NetworkMatch>>();
readonly HashSet<Guid> dirtyMatches = new HashSet<Guid>();
// LateUpdate so that all spawns/despawns/changes are done
[ServerCallback]
void LateUpdate()
{
// Rebuild all dirty matches
// dirtyMatches will be empty if no matches changed members
// by spawning or destroying or changing matchId in this frame.
foreach (Guid dirtyMatch in dirtyMatches)
{
// rebuild always, even if matchObjects[dirtyMatch] is empty.
// Players might have left the match, but they may still be spawned.
RebuildMatchObservers(dirtyMatch);
// clean up empty entries in the dict
if (matchObjects[dirtyMatch].Count == 0)
matchObjects.Remove(dirtyMatch);
}
dirtyMatches.Clear();
matchCount = (ushort)matchObjects.Count;
}
[ServerCallback]
void RebuildMatchObservers(Guid matchId)
{
foreach (NetworkMatch networkMatch in matchObjects[matchId])
if (networkMatch.netIdentity != null)
NetworkServer.RebuildObservers(networkMatch.netIdentity, false);
}
// called by NetworkMatch.matchId setter
[ServerCallback]
internal void OnMatchChanged(NetworkMatch networkMatch, Guid oldMatch)
{
// This object is in a new match so observers in the prior match
// and the new match need to rebuild their respective observers lists.
// Remove this object from the hashset of the match it just left
// Guid.Empty is never a valid matchId
if (oldMatch != Guid.Empty)
{
dirtyMatches.Add(oldMatch);
matchObjects[oldMatch].Remove(networkMatch);
}
// Guid.Empty is never a valid matchId
if (networkMatch.matchId == Guid.Empty)
return;
dirtyMatches.Add(networkMatch.matchId);
// Make sure this new match is in the dictionary
if (!matchObjects.ContainsKey(networkMatch.matchId))
matchObjects[networkMatch.matchId] = new HashSet<NetworkMatch>();
// Add this object to the hashset of the new match
matchObjects[networkMatch.matchId].Add(networkMatch);
}
[ServerCallback]
public override void OnSpawned(NetworkIdentity identity)
{
if (!identity.TryGetComponent(out NetworkMatch networkMatch))
return;
Guid networkMatchId = networkMatch.matchId;
// Guid.Empty is never a valid matchId...do not add to matchObjects collection
if (networkMatchId == Guid.Empty)
return;
// Debug.Log($"MatchInterestManagement.OnSpawned({identity.name}) currentMatch: {currentMatch}");
if (!matchObjects.TryGetValue(networkMatchId, out HashSet<NetworkMatch> objects))
{
objects = new HashSet<NetworkMatch>();
matchObjects.Add(networkMatchId, objects);
}
objects.Add(networkMatch);
// Match ID could have been set in NetworkBehaviour::OnStartServer on this object.
// Since that's after OnCheckObserver is called it would be missed, so force Rebuild here.
// Add the current match to dirtyMatches for LateUpdate to rebuild it.
dirtyMatches.Add(networkMatchId);
}
[ServerCallback]
public override void OnDestroyed(NetworkIdentity identity)
{
// Don't RebuildSceneObservers here - that will happen in LateUpdate.
// Multiple objects could be destroyed in same frame and we don't
// want to rebuild for each one...let LateUpdate do it once.
// We must add the current match to dirtyMatches for LateUpdate to rebuild it.
if (identity.TryGetComponent(out NetworkMatch currentMatch))
{
if (currentMatch.matchId != Guid.Empty &&
matchObjects.TryGetValue(currentMatch.matchId, out HashSet<NetworkMatch> objects) &&
objects.Remove(currentMatch))
dirtyMatches.Add(currentMatch.matchId);
}
}
[ServerCallback]
public override bool OnCheckObserver(NetworkIdentity identity, NetworkConnectionToClient newObserver)
{
// Never observed if no NetworkMatch component
if (!identity.TryGetComponent(out NetworkMatch identityNetworkMatch))
return false;
// Guid.Empty is never a valid matchId
if (identityNetworkMatch.matchId == Guid.Empty)
return false;
// Never observed if no NetworkMatch component
if (!newObserver.identity.TryGetComponent(out NetworkMatch newObserverNetworkMatch))
return false;
// Guid.Empty is never a valid matchId
if (newObserverNetworkMatch.matchId == Guid.Empty)
return false;
return identityNetworkMatch.matchId == newObserverNetworkMatch.matchId;
}
[ServerCallback]
public override void OnRebuildObservers(NetworkIdentity identity, HashSet<NetworkConnectionToClient> newObservers)
{
if (!identity.TryGetComponent(out NetworkMatch networkMatch))
return;
// Guid.Empty is never a valid matchId
if (networkMatch.matchId == Guid.Empty)
return;
// Abort if this match hasn't been created yet by OnSpawned or OnMatchChanged
if (!matchObjects.TryGetValue(networkMatch.matchId, out HashSet<NetworkMatch> objects))
return;
// Add everything in the hashset for this object's current match
foreach (NetworkMatch netMatch in objects)
if (netMatch.netIdentity != null && netMatch.netIdentity.connectionToClient != null)
newObservers.Add(netMatch.netIdentity.connectionToClient);
}
}
}