This commit is contained in:
2025-06-16 15:14:23 +02:00
commit 074e590073
3174 changed files with 428263 additions and 0 deletions

View File

@ -0,0 +1,164 @@
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);
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d09f5c8bf2f4747b7a9284ef5d9ce2a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Components/InterestManagement/Match/MatchInterestManagement.cs
uploadId: 736421

View File

@ -0,0 +1,42 @@
// simple component that holds match information
using System;
using UnityEngine;
namespace Mirror
{
[DisallowMultipleComponent]
[AddComponentMenu("Network/ Interest Management/ Match/Network Match")]
[HelpURL("https://mirror-networking.gitbook.io/docs/guides/interest-management")]
public class NetworkMatch : NetworkBehaviour
{
Guid _matchId;
#pragma warning disable IDE0052 // Suppress warning for unused field...this is for debugging purposes
[SerializeField, ReadOnly]
[Tooltip("Match ID is shown here on server for debugging purposes.")]
string MatchID = string.Empty;
#pragma warning restore IDE0052
///<summary>Set this to the same value on all networked objects that belong to a given match</summary>
public Guid matchId
{
get => _matchId;
set
{
if (!NetworkServer.active)
throw new InvalidOperationException("matchId can only be set at runtime on active server");
if (_matchId == value)
return;
Guid oldMatch = _matchId;
_matchId = value;
MatchID = value.ToString();
// Only inform the AOI if this netIdentity has been spawned (isServer) and only if using a MatchInterestManagement
if (isServer && NetworkServer.aoi is MatchInterestManagement matchInterestManagement)
matchInterestManagement.OnMatchChanged(this, oldMatch);
}
}
}
}

View File

@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5d17e718851449a6879986e45c458fb7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 129321
packageName: Mirror
packageVersion: 96.0.1
assetPath: Assets/Mirror/Components/InterestManagement/Match/NetworkMatch.cs
uploadId: 736421