Skip to content

Commit

Permalink
feat: Team Interest Management (#2979)
Browse files Browse the repository at this point in the history
* feat: Team Interest Management

* meta file

* Added forceShown

* Code Smells

* code smell

* simplified
  • Loading branch information
MrGadget1024 committed Oct 29, 2021
1 parent cf76d03 commit 1a43b79
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Assets/Mirror/Components/InterestManagement/Team.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions Assets/Mirror/Components/InterestManagement/Team/NetworkTeam.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// simple component that holds team information
using System;
using UnityEngine;

namespace Mirror
{
[DisallowMultipleComponent]
[AddComponentMenu("Network/NetworkTeam")]
[HelpURL("https://mirror-networking.gitbook.io/docs/guides/interest-management")]
public class NetworkTeam : NetworkBehaviour
{
[Tooltip("Set this to the same value on all networked objects that belong to a given team")]
public string teamId = string.Empty;

[Tooltip("When enabled this object is visible to all clients. Typically this would be true for player objects")]
public bool forceShown;
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
using System;
using System.Collections.Generic;

namespace Mirror
{
public class TeamInterestManagement : InterestManagement
{
readonly Dictionary<string, HashSet<NetworkIdentity>> teamObjects =
new Dictionary<string, HashSet<NetworkIdentity>>();

readonly Dictionary<NetworkIdentity, string> lastObjectTeam =
new Dictionary<NetworkIdentity, string>();

readonly HashSet<string> dirtyTeams = new HashSet<string>();

public override void OnSpawned(NetworkIdentity identity)
{
if (!identity.TryGetComponent<NetworkTeam>(out NetworkTeam networkTeam))
return;

string currentTeam = networkTeam.teamId;
lastObjectTeam[identity] = currentTeam;

// string.Empty is never a valid teamId...do not add to teamObjects collection
if (currentTeam == string.Empty)
return;

if (!teamObjects.TryGetValue(currentTeam, out HashSet<NetworkIdentity> objects))
{
objects = new HashSet<NetworkIdentity>();
teamObjects.Add(currentTeam, objects);
}

objects.Add(identity);
}

public override void OnDestroyed(NetworkIdentity identity)
{
// Do nothing if server has shut down (or not started)
if (!NetworkServer.active) return;

lastObjectTeam.TryGetValue(identity, out string currentTeam);
lastObjectTeam.Remove(identity);
if (currentTeam != string.Empty && teamObjects.TryGetValue(currentTeam, out HashSet<NetworkIdentity> objects) && objects.Remove(identity))
RebuildTeamObservers(currentTeam);
}

[ServerCallback]
void Update()
{
// for each spawned:
// if team changed:
// add previous to dirty
// add new to dirty
foreach (NetworkIdentity netIdentity in NetworkServer.spawned.Values)
{
// Ignore objects that don't have a NetworkTeam component
if (!netIdentity.TryGetComponent<NetworkTeam>(out NetworkTeam networkTeam))
continue;

string newTeam = networkTeam.teamId;
lastObjectTeam.TryGetValue(netIdentity, out string currentTeam);

// string.Empty is never a valid teamId
// Nothing to do if teamId hasn't changed
if (newTeam == string.Empty || newTeam == currentTeam)
continue;

// Mark new/old Teams as dirty so they get rebuilt
UpdateDirtyTeams(newTeam, currentTeam);

// This object is in a new team so observers in the prior team
// and the new team need to rebuild their respective observers lists.
UpdateTeamObjects(netIdentity, newTeam, currentTeam);
}

// rebuild all dirty teams
foreach (string dirtyTeam in dirtyTeams)
RebuildTeamObservers(dirtyTeam);

dirtyTeams.Clear();
}

void UpdateDirtyTeams(string newTeam, string currentTeam)
{
// string.Empty is never a valid teamId
if (currentTeam != string.Empty)
dirtyTeams.Add(currentTeam);

dirtyTeams.Add(newTeam);
}

void UpdateTeamObjects(NetworkIdentity netIdentity, string newTeam, string currentTeam)
{
// Remove this object from the hashset of the team it just left
// string.Empty is never a valid teamId
if (currentTeam != string.Empty)
teamObjects[currentTeam].Remove(netIdentity);

// Set this to the new team this object just entered
lastObjectTeam[netIdentity] = newTeam;

// Make sure this new team is in the dictionary
if (!teamObjects.ContainsKey(newTeam))
teamObjects.Add(newTeam, new HashSet<NetworkIdentity>());

// Add this object to the hashset of the new team
teamObjects[newTeam].Add(netIdentity);
}

void RebuildTeamObservers(string teamId)
{
foreach (NetworkIdentity netIdentity in teamObjects[teamId])
if (netIdentity != null)
NetworkServer.RebuildObservers(netIdentity, false);
}

public override bool OnCheckObserver(NetworkIdentity identity, NetworkConnection newObserver)
{
// Do nothing if server has shut down (or not started)
if (!NetworkServer.active) return false;

// Always observed if no NetworkTeam component
if (!identity.TryGetComponent<NetworkTeam>(out NetworkTeam identityNetworkTeam))
return true;

// Always observed if no NetworkTeam component
if (!newObserver.identity.TryGetComponent<NetworkTeam>(out NetworkTeam newObserverNetworkTeam))
return true;

// Observed only if teamId's match
return identityNetworkTeam.forceShown || identityNetworkTeam.teamId == newObserverNetworkTeam.teamId;
}

public override void OnRebuildObservers(NetworkIdentity identity, HashSet<NetworkConnection> newObservers, bool initialize)
{
// Do nothing if server has shut down (or not started)
if (!NetworkServer.active) return;

// If this object doesn't have a NetworkTeam then it's visible to all clients
if (!identity.TryGetComponent<NetworkTeam>(out NetworkTeam networkTeam))
{
AddAllConnections(newObservers);
return;
}

// If this object has NetworkTeam and forceShown == true then it's visible to all clients
if (networkTeam.forceShown)
{
AddAllConnections(newObservers);
return;
}

// string.Empty is never a valid teamId
if (networkTeam.teamId == string.Empty)
return;

// Abort if this team hasn't been created yet by OnSpawned or UpdateTeamObjects
if (!teamObjects.TryGetValue(networkTeam.teamId, out HashSet<NetworkIdentity> objects))
return;

// Add everything in the hashset for this object's current team
foreach (NetworkIdentity networkIdentity in objects)
if (networkIdentity != null && networkIdentity.connectionToClient != null)
newObservers.Add(networkIdentity.connectionToClient);
}

void AddAllConnections(HashSet<NetworkConnection> newObservers)
{
foreach (NetworkConnectionToClient conn in NetworkServer.connections.Values)
{
// authenticated and joined world with a player?
if (conn != null && conn.isAuthenticated && conn.identity != null)
newObservers.Add(conn);
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1a43b79

Please sign in to comment.