mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 11:00:32 +00:00
feat: NetworkMatchChecker Component (#1688)
* feat: NetworkMatchChecker Component * Added docs * feat: Use logger framework for NetworkClient (#1685) * Use logger framework for NetworkClient * Update Assets/Mirror/Runtime/NetworkClient.cs Co-authored-by: vis2k <info@noobtuts.com> * breaking: NetworkVisbility component (#1681) * backup * breaking: NetworkProximityCheck abstract class. Simplifies code, reduces complexity, improves performance because if 10k identities have 10 components each, we don't have to iterate 100k components each time we rebuild observers. * update tests and checkers * DisallowMultipleComponents * fix tests * split OnCheckObserver check * fix tests * syntax * update comment * renamed to NetworkVisibility * forgot to remove comment * breaking: Network Visibility Component * changing namespaces of performance tests (#1689) * Updated to use NetworkVisibility * Updated comments * Updated OnCheckObserver and removed OnSetHostVisibility * tests for OnCheckObserver * adding check for empty guid * tests for changing matchId * RebuildObservers if player left a match * Refactored to make it simpler Co-authored-by: Paul Pacheco <paulpach@gmail.com> Co-authored-by: vis2k <info@noobtuts.com> Co-authored-by: James Frowen <jamesfrowendev@gmail.com>
This commit is contained in:
parent
cbc2a47729
commit
21acf66190
131
Assets/Mirror/Components/NetworkMatchChecker.cs
Normal file
131
Assets/Mirror/Components/NetworkMatchChecker.cs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Mirror
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Component that controls visibility of networked objects based on match id.
|
||||||
|
/// <para>Any object with this component on it will only be visible to other objects in the same match.</para>
|
||||||
|
/// <para>This would be used to isolate players to their respective matches within a single game server instance. </para>
|
||||||
|
/// </summary>
|
||||||
|
[AddComponentMenu("Network/NetworkMatchChecker")]
|
||||||
|
[RequireComponent(typeof(NetworkIdentity))]
|
||||||
|
[HelpURL("https://mirror-networking.com/docs/Components/NetworkMatchChecker.html")]
|
||||||
|
public class NetworkMatchChecker : NetworkVisibility
|
||||||
|
{
|
||||||
|
static readonly Dictionary<Guid, HashSet<NetworkIdentity>> matchPlayers = new Dictionary<Guid, HashSet<NetworkIdentity>>();
|
||||||
|
|
||||||
|
Guid currentMatch = Guid.Empty;
|
||||||
|
|
||||||
|
[Header("Diagnostics")]
|
||||||
|
[SyncVar]
|
||||||
|
public string currentMatchDebug;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set this to the same value on all networked objects that belong to a given match
|
||||||
|
/// </summary>
|
||||||
|
public Guid matchId
|
||||||
|
{
|
||||||
|
get { return currentMatch; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (currentMatch == value) return;
|
||||||
|
|
||||||
|
// cache previous match so observers in that match can be rebuilt
|
||||||
|
Guid previousMatch = currentMatch;
|
||||||
|
|
||||||
|
// Set this to the new match this object just entered ...
|
||||||
|
currentMatch = value;
|
||||||
|
// ... and copy the string for the inspector because Unity can't show Guid directly
|
||||||
|
currentMatchDebug = currentMatch.ToString();
|
||||||
|
|
||||||
|
if (previousMatch != Guid.Empty)
|
||||||
|
{
|
||||||
|
// Remove this object from the hashset of the match it just left
|
||||||
|
matchPlayers[previousMatch].Remove(netIdentity);
|
||||||
|
|
||||||
|
// RebuildObservers of all NetworkIdentity's in the match this object just left
|
||||||
|
RebuildMatchObservers(previousMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMatch != Guid.Empty)
|
||||||
|
{
|
||||||
|
// Make sure this new match is in the dictionary
|
||||||
|
if (!matchPlayers.ContainsKey(currentMatch))
|
||||||
|
matchPlayers.Add(currentMatch, new HashSet<NetworkIdentity>());
|
||||||
|
|
||||||
|
// Add this object to the hashset of the new match
|
||||||
|
matchPlayers[currentMatch].Add(netIdentity);
|
||||||
|
|
||||||
|
// RebuildObservers of all NetworkIdentity's in the match this object just entered
|
||||||
|
RebuildMatchObservers(currentMatch);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not in any match now...RebuildObservers will clear and add self
|
||||||
|
netIdentity.RebuildObservers(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStartServer()
|
||||||
|
{
|
||||||
|
if (currentMatch == Guid.Empty) return;
|
||||||
|
|
||||||
|
if (!matchPlayers.ContainsKey(currentMatch))
|
||||||
|
matchPlayers.Add(currentMatch, new HashSet<NetworkIdentity>());
|
||||||
|
|
||||||
|
matchPlayers[currentMatch].Add(netIdentity);
|
||||||
|
|
||||||
|
// No need to rebuild anything here.
|
||||||
|
// identity.RebuildObservers is called right after this from NetworkServer.SpawnObject
|
||||||
|
}
|
||||||
|
|
||||||
|
void RebuildMatchObservers(Guid specificMatch)
|
||||||
|
{
|
||||||
|
foreach (NetworkIdentity networkIdentity in matchPlayers[specificMatch])
|
||||||
|
if (networkIdentity != null)
|
||||||
|
networkIdentity.RebuildObservers(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Observers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Callback used by the visibility system to determine if an observer (player) can see this object.
|
||||||
|
/// <para>If this function returns true, the network connection will be added as an observer.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="conn">Network connection of a player.</param>
|
||||||
|
/// <returns>True if the player can see this object.</returns>
|
||||||
|
public override bool OnCheckObserver(NetworkConnection conn)
|
||||||
|
{
|
||||||
|
// Not Visible if not in a match
|
||||||
|
if (matchId == Guid.Empty)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
NetworkMatchChecker networkMatchChecker = conn.identity.GetComponent<NetworkMatchChecker>();
|
||||||
|
|
||||||
|
if (networkMatchChecker == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return networkMatchChecker.matchId == matchId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Callback used by the visibility system to (re)construct the set of observers that can see this object.
|
||||||
|
/// <para>Implementations of this callback should add network connections of players that can see this object to the observers set.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="observers">The new set of observers for this object.</param>
|
||||||
|
/// <param name="initialize">True if the set of observers is being built for the first time.</param>
|
||||||
|
public override void OnRebuildObservers(HashSet<NetworkConnection> observers, bool initialize)
|
||||||
|
{
|
||||||
|
if (currentMatch == Guid.Empty) return;
|
||||||
|
|
||||||
|
foreach (NetworkIdentity networkIdentity in matchPlayers[currentMatch])
|
||||||
|
if (networkIdentity != null && networkIdentity.connectionToClient != null)
|
||||||
|
observers.Add(networkIdentity.connectionToClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Mirror/Components/NetworkMatchChecker.cs.meta
Normal file
11
Assets/Mirror/Components/NetworkMatchChecker.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1020a74962faada4b807ac5dc053a4cf
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
196
Assets/Mirror/Tests/Editor/NetworkMatchCheckerTest.cs
Normal file
196
Assets/Mirror/Tests/Editor/NetworkMatchCheckerTest.cs
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Mirror.Tests
|
||||||
|
{
|
||||||
|
public class NetworkMatchCheckerTest
|
||||||
|
{
|
||||||
|
private GameObject player1;
|
||||||
|
private GameObject player2;
|
||||||
|
private GameObject player3;
|
||||||
|
private NetworkMatchChecker player1MatchChecker;
|
||||||
|
private NetworkMatchChecker player2MatchChecker;
|
||||||
|
private NetworkConnection player1Connection;
|
||||||
|
private NetworkConnection player2Connection;
|
||||||
|
private NetworkConnection player3Connection;
|
||||||
|
private GameObject transportGO;
|
||||||
|
static int nextConnectionId;
|
||||||
|
private Dictionary<Guid, HashSet<NetworkIdentity>> matchPlayers;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
transportGO = new GameObject("transportGO");
|
||||||
|
Transport.activeTransport = transportGO.AddComponent<TelepathyTransport>();
|
||||||
|
|
||||||
|
player1 = new GameObject("TestPlayer1", typeof(NetworkIdentity), typeof(NetworkMatchChecker));
|
||||||
|
player2 = new GameObject("TestPlayer2", typeof(NetworkIdentity), typeof(NetworkMatchChecker));
|
||||||
|
player3 = new GameObject("TestPlayer3", typeof(NetworkIdentity));
|
||||||
|
|
||||||
|
player1MatchChecker = player1.GetComponent<NetworkMatchChecker>();
|
||||||
|
player2MatchChecker = player2.GetComponent<NetworkMatchChecker>();
|
||||||
|
|
||||||
|
|
||||||
|
player1Connection = CreateNetworkConnection(player1);
|
||||||
|
player2Connection = CreateNetworkConnection(player2);
|
||||||
|
player3Connection = CreateNetworkConnection(player3);
|
||||||
|
Dictionary<Guid, HashSet<NetworkIdentity>> g = GetMatchPlayersDictionary();
|
||||||
|
matchPlayers = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<Guid, HashSet<NetworkIdentity>> GetMatchPlayersDictionary()
|
||||||
|
{
|
||||||
|
Type type = typeof(NetworkMatchChecker);
|
||||||
|
FieldInfo fieldInfo = type.GetField("matchPlayers", BindingFlags.Static | BindingFlags.NonPublic);
|
||||||
|
return (Dictionary<Guid, HashSet<NetworkIdentity>>)fieldInfo.GetValue(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NetworkConnection CreateNetworkConnection(GameObject player)
|
||||||
|
{
|
||||||
|
NetworkConnectionToClient connection = new NetworkConnectionToClient(++nextConnectionId);
|
||||||
|
connection.identity = player.GetComponent<NetworkIdentity>();
|
||||||
|
connection.identity.connectionToClient = connection;
|
||||||
|
connection.identity.observers = new Dictionary<int, NetworkConnection>();
|
||||||
|
connection.isReady = true;
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TearDown]
|
||||||
|
public void TearDown()
|
||||||
|
{
|
||||||
|
UnityEngine.Object.DestroyImmediate(player1);
|
||||||
|
UnityEngine.Object.DestroyImmediate(player2);
|
||||||
|
UnityEngine.Object.DestroyImmediate(player3);
|
||||||
|
UnityEngine.Object.DestroyImmediate(transportGO);
|
||||||
|
|
||||||
|
matchPlayers.Clear();
|
||||||
|
matchPlayers = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetMatchId(NetworkMatchChecker target, Guid guid)
|
||||||
|
{
|
||||||
|
// set using reflection so bypass property
|
||||||
|
FieldInfo field = typeof(NetworkMatchChecker).GetField("currentMatch", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
field.SetValue(target, guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void OnCheckObserverShouldBeTrueForSameMatchId()
|
||||||
|
{
|
||||||
|
string guid = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
SetMatchId(player1MatchChecker, new Guid(guid));
|
||||||
|
SetMatchId(player2MatchChecker, new Guid(guid));
|
||||||
|
|
||||||
|
bool player1Visable = player1MatchChecker.OnCheckObserver(player1Connection);
|
||||||
|
Assert.IsTrue(player1Visable);
|
||||||
|
|
||||||
|
bool player2Visable = player1MatchChecker.OnCheckObserver(player2Connection);
|
||||||
|
Assert.IsTrue(player2Visable);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void OnCheckObserverShouldBeFalseForDifferentMatchId()
|
||||||
|
{
|
||||||
|
string guid1 = Guid.NewGuid().ToString();
|
||||||
|
string guid2 = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
SetMatchId(player1MatchChecker, new Guid(guid1));
|
||||||
|
SetMatchId(player2MatchChecker, new Guid(guid2));
|
||||||
|
|
||||||
|
bool player1VisableToPlayer1 = player1MatchChecker.OnCheckObserver(player1Connection);
|
||||||
|
Assert.IsTrue(player1VisableToPlayer1);
|
||||||
|
|
||||||
|
bool player2VisableToPlayer1 = player1MatchChecker.OnCheckObserver(player2Connection);
|
||||||
|
Assert.IsFalse(player2VisableToPlayer1);
|
||||||
|
|
||||||
|
|
||||||
|
bool player1VisableToPlayer2 = player2MatchChecker.OnCheckObserver(player1Connection);
|
||||||
|
Assert.IsFalse(player1VisableToPlayer2);
|
||||||
|
|
||||||
|
bool player2VisableToPlayer2 = player2MatchChecker.OnCheckObserver(player2Connection);
|
||||||
|
Assert.IsTrue(player2VisableToPlayer2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void OnCheckObserverShouldBeFalseIfObjectDoesNotHaveNetworkMatchChecker()
|
||||||
|
{
|
||||||
|
string guid = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
SetMatchId(player1MatchChecker, new Guid(guid));
|
||||||
|
|
||||||
|
bool player3Visable = player1MatchChecker.OnCheckObserver(player3Connection);
|
||||||
|
Assert.IsFalse(player3Visable);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void OnCheckObserverShouldBeFalseForEmptyGuid()
|
||||||
|
{
|
||||||
|
string guid = Guid.Empty.ToString();
|
||||||
|
|
||||||
|
SetMatchId(player1MatchChecker, new Guid(guid));
|
||||||
|
SetMatchId(player2MatchChecker, new Guid(guid));
|
||||||
|
|
||||||
|
bool player1Visable = player1MatchChecker.OnCheckObserver(player1Connection);
|
||||||
|
Assert.IsFalse(player1Visable);
|
||||||
|
|
||||||
|
bool player2Visable = player1MatchChecker.OnCheckObserver(player2Connection);
|
||||||
|
Assert.IsFalse(player2Visable);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SettingMatchIdShouldRebuildObservers()
|
||||||
|
{
|
||||||
|
string guidMatch1 = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
// make players join same match
|
||||||
|
player1MatchChecker.matchId = new Guid(guidMatch1);
|
||||||
|
player2MatchChecker.matchId = new Guid(guidMatch1);
|
||||||
|
|
||||||
|
// check player1's observers contains player 2
|
||||||
|
Assert.IsTrue(player1MatchChecker.netIdentity.observers.ContainsValue(player2MatchChecker.connectionToClient));
|
||||||
|
// check player2's observers contains player 1
|
||||||
|
Assert.IsTrue(player2MatchChecker.netIdentity.observers.ContainsValue(player1MatchChecker.connectionToClient));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ChangingMatchIdShouldRebuildObservers()
|
||||||
|
{
|
||||||
|
string guidMatch1 = Guid.NewGuid().ToString();
|
||||||
|
string guidMatch2 = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
// make players join same match
|
||||||
|
player1MatchChecker.matchId = new Guid(guidMatch1);
|
||||||
|
player2MatchChecker.matchId = new Guid(guidMatch1);
|
||||||
|
|
||||||
|
// make player2 join different match
|
||||||
|
player2MatchChecker.matchId = new Guid(guidMatch2);
|
||||||
|
|
||||||
|
// check player1's observers does NOT contain player 2
|
||||||
|
Assert.IsFalse(player1MatchChecker.netIdentity.observers.ContainsValue(player2MatchChecker.connectionToClient));
|
||||||
|
// check player2's observers does NOT contain player 1
|
||||||
|
Assert.IsFalse(player2MatchChecker.netIdentity.observers.ContainsValue(player1MatchChecker.connectionToClient));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ClearingMatchIdShouldRebuildObservers()
|
||||||
|
{
|
||||||
|
string guidMatch1 = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
// make players join same match
|
||||||
|
player1MatchChecker.matchId = new Guid(guidMatch1);
|
||||||
|
player2MatchChecker.matchId = new Guid(guidMatch1);
|
||||||
|
|
||||||
|
// make player 2 leave match
|
||||||
|
player2MatchChecker.matchId = Guid.Empty;
|
||||||
|
|
||||||
|
// check player1's observers does NOT contain player 2
|
||||||
|
Assert.IsFalse(player1MatchChecker.netIdentity.observers.ContainsValue(player2MatchChecker.connectionToClient));
|
||||||
|
// check player2's observers does NOT contain player 1
|
||||||
|
Assert.IsFalse(player2MatchChecker.netIdentity.observers.ContainsValue(player1MatchChecker.connectionToClient));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Assets/Mirror/Tests/Editor/NetworkMatchCheckerTest.cs.meta
Normal file
11
Assets/Mirror/Tests/Editor/NetworkMatchCheckerTest.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2fa0a455ab9b4cf47b9eab0f2b03ce0c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
13
doc/Components/NetworkMatchChecker.md
Normal file
13
doc/Components/NetworkMatchChecker.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Network Scene Checker
|
||||||
|
|
||||||
|
The Network Match Checker component controls visibility of networked objects based on match id.
|
||||||
|
|
||||||
|
![Network Scene Checker component](NetworkMatchChecker.png)
|
||||||
|
|
||||||
|
Any object with this component on it will only be visible to other objects in the same match.
|
||||||
|
|
||||||
|
This would be used to isolate players to their respective matches within a single game server instance.
|
||||||
|
|
||||||
|
When you create a match, generate and store, in a List for example, a new match id with `System.Guid.NewGuid();` and assign the same match id to the Network Scene Checker via `GetComponent<NetworkMatchChecker>().matchId`.
|
||||||
|
|
||||||
|
Mirror's built-in Observers system will isolate SyncVar's and ClientRpc's on networked objects to only send updates to clients with the same match id.
|
BIN
doc/Components/NetworkMatchChecker.png
Normal file
BIN
doc/Components/NetworkMatchChecker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
@ -16,6 +16,8 @@ These core components are included in Mirror:
|
|||||||
The Network Proximity Checker component controls the visibility of game objects for network clients, based on proximity to players.
|
The Network Proximity Checker component controls the visibility of game objects for network clients, based on proximity to players.
|
||||||
- [Network Scene Checker](NetworkSceneChecker.md)
|
- [Network Scene Checker](NetworkSceneChecker.md)
|
||||||
The Network Scene Checker component controls visibility of networked objects between scenes.
|
The Network Scene Checker component controls visibility of networked objects between scenes.
|
||||||
|
- [Network Match Checker](NetworkMatchChecker.md)
|
||||||
|
The Network Match Checker component controls visibility of networked objects based on match id.
|
||||||
- [Network Room Manager](NetworkRoomManager.md)
|
- [Network Room Manager](NetworkRoomManager.md)
|
||||||
The Network Room Manager is an extension component of Network Manager that provides a basic functional room.
|
The Network Room Manager is an extension component of Network Manager that provides a basic functional room.
|
||||||
- [Network Room Player](NetworkRoomPlayer.md)
|
- [Network Room Player](NetworkRoomPlayer.md)
|
||||||
|
Loading…
Reference in New Issue
Block a user