This repository has been archived on 2023-09-13. You can view files and clone it, but cannot push or open issues or pull requests.
station_obscurum_unity/Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs

586 lines
25 KiB
C#

using FishNet.Connection;
using FishNet.Managing;
using FishNet.Managing.Logging;
using FishNet.Managing.Server;
using FishNet.Object;
using FishNet.Observing;
using FishNet.Utility.Extension;
using FishNet.Utility.Performance;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace FishNet.Component.Observing
{
/// <summary>
/// When this observer condition is placed on an object, a client must be within the same match to view the object.
/// </summary>
[CreateAssetMenu(menuName = "FishNet/Observers/Match Condition", fileName = "New Match Condition")]
public class MatchCondition : ObserverCondition
{
#region Private.
/// <summary>
///
/// </summary>
private static Dictionary<int, HashSet<NetworkConnection>> _matchConnections = new Dictionary<int, HashSet<NetworkConnection>>();
/// <summary>
/// Matches and connections in each match.
/// </summary>
public static IReadOnlyDictionary<int, HashSet<NetworkConnection>> MatchConnections => _matchConnections;
/// <summary>
///
/// </summary>
/// //todo this needs to hold hashset so conns can be in multiple matches.
private static Dictionary<NetworkConnection, int> _connectionMatch = new Dictionary<NetworkConnection, int>();
/// <summary>
/// Match a connection is in.
/// </summary>
public static IReadOnlyDictionary<NetworkConnection, int> ConnectionMatch => _connectionMatch;
/// <summary>
///
/// </summary>
private static Dictionary<int, HashSet<NetworkObject>> _matchObjects = new Dictionary<int, HashSet<NetworkObject>>();
/// <summary>
/// Matches and connections in each match.
/// </summary>
public static IReadOnlyDictionary<int, HashSet<NetworkObject>> MatchObjects => _matchObjects;
/// <summary>
///
/// </summary>
/// //todo this needs to hold hashset so conns can be in multiple matches.
private static Dictionary<NetworkObject, int> _objectMatch = new Dictionary<NetworkObject, int>();
/// <summary>
/// Match a connection is in.
/// </summary>
public static IReadOnlyDictionary<NetworkObject, int> ObjectMatch => _objectMatch;
#endregion
public void ConditionConstructor() { }
#region Add to match NetworkConnection.
/// <summary>
/// Adds a connection to a match.
/// </summary>
/// <param name="match">Match to add conn to.</param>
/// <param name="conn">Connection to add to match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
/// <param name="replaceMatch">True to replace other matches with the new match.</param>
public static void AddToMatch(int match, NetworkConnection conn, NetworkManager manager = null, bool replaceMatch = false)
{
if (replaceMatch)
RemoveFromMatchWithoutRebuild(conn, manager);
HashSet<NetworkConnection> results;
if (!_matchConnections.TryGetValueIL2CPP(match, out results))
{
results = new HashSet<NetworkConnection>();
_matchConnections.Add(match, results);
}
bool r = results.Add(conn);
_connectionMatch[conn] = match;
if (r)
FinalizeChange(match, results, manager);
}
/// <summary>
/// Adds connections to a match.
/// </summary>
/// <param name="match">Match to add conns to.</param>
/// <param name="conns">Connections to add to match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
/// <param name="replaceMatch">True to replace other matches with the new match.</param>
public static void AddToMatch(int match, NetworkConnection[] conns, NetworkManager manager = null, bool replaceMatch = false)
{
AddToMatch(match, conns.ToList(), manager, replaceMatch);
}
/// <summary>
/// Adds connections to a match.
/// </summary>
/// <param name="match">Match to add conns to.</param>
/// <param name="conns">Connections to add to match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
/// <param name="replaceMatch">True to replace other matches with the new match.</param>
public static void AddToMatch(int match, List<NetworkConnection> conns, NetworkManager manager = null, bool replaceMatch = false)
{
if (replaceMatch)
{
foreach (NetworkConnection nc in conns)
RemoveFromMatchWithoutRebuild(nc, manager);
}
HashSet<NetworkConnection> results;
if (!_matchConnections.TryGetValueIL2CPP(match, out results))
{
results = new HashSet<NetworkConnection>();
_matchConnections.Add(match, results);
}
bool r = false;
for (int i = 0; i < conns.Count; i++)
{
NetworkConnection c = conns[i];
r |= results.Add(c);
_connectionMatch[c] = match;
}
if (r)
FinalizeChange(match, results, manager);
}
#endregion
#region Add to match NetworkObject.
/// <summary>
/// Adds an object to a match.
/// </summary>
/// <param name="match">Match to add conn to.</param>
/// <param name="nob">Connection to add to match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
/// <param name="replaceMatch">True to replace other matches with the new match.</param>
public static void AddToMatch(int match, NetworkObject nob, NetworkManager manager = null, bool replaceMatch = false)
{
if (replaceMatch)
RemoveFromMatchWithoutRebuild(nob, manager);
HashSet<NetworkObject> results;
if (!_matchObjects.TryGetValueIL2CPP(match, out results))
{
results = new HashSet<NetworkObject>();
_matchObjects.Add(match, results);
}
bool r = results.Add(nob);
_objectMatch[nob] = match;
if (r)
FinalizeChange(match, results, nob, manager);
}
/// <summary>
/// Adds objects to a match.
/// </summary>
/// <param name="match">Match to add conns to.</param>
/// <param name="nobs">Connections to add to match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
/// <param name="replaceMatch">True to replace other matches with the new match.</param>
public static void AddToMatch(int match, NetworkObject[] nobs, NetworkManager manager = null, bool replaceMatch = false)
{
AddToMatch(match, nobs.ToList(), manager, replaceMatch);
}
/// <summary>
/// Adds objects to a match.
/// </summary>
/// <param name="match">Match to add conns to.</param>
/// <param name="nobs">Connections to add to match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
/// <param name="replaceMatch">True to replace other matches with the new match.</param>
public static void AddToMatch(int match, List<NetworkObject> nobs, NetworkManager manager = null, bool replaceMatch = false)
{
if (replaceMatch)
{
foreach (NetworkObject n in nobs)
RemoveFromMatchWithoutRebuild(n, manager);
}
HashSet<NetworkObject> results;
if (!_matchObjects.TryGetValueIL2CPP(match, out results))
{
results = new HashSet<NetworkObject>();
_matchObjects.Add(match, results);
}
bool r = false;
for (int i = 0; i < nobs.Count; i++)
{
NetworkObject n = nobs[i];
r |= results.Add(n);
_objectMatch[n] = match;
}
if (r)
FinalizeChange(match, results, nobs, manager);
}
#endregion
#region Remove from match NetworkConnection.
/// <summary>
/// Removes a connection from any match without rebuilding observers.
/// </summary>
/// <param name="conn">Connection to remove from matches.</param>
/// <param name="manager">NetworkManager connection belongs to. This is not currently used.</param>
internal static bool RemoveFromMatchWithoutRebuild(NetworkConnection conn, NetworkManager manager)
{
bool removed = false;
//If found to be in a match.
if (_connectionMatch.TryGetValueIL2CPP(conn, out int match))
{
//If match is found.
if (_matchConnections.TryGetValue(match, out HashSet<NetworkConnection> conns))
{
removed |= conns.Remove(conn);
//If no more in hashset remove match.
if (conns.Count == 0)
_matchConnections.Remove(match);
}
}
//Remove from connectionMatch.
_connectionMatch.Remove(conn);
return removed;
}
/// <summary>
/// Removes a connection from all matches.
/// </summary>
/// <param name="conn">NetworkConnection to remove.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
public static void RemoveFromMatch(NetworkConnection conn, NetworkManager manager)
{
bool removed = RemoveFromMatchWithoutRebuild(conn, manager);
if (removed)
GetServerObjects(manager).RebuildObservers();
}
/// <summary>
/// Removes a connection from a match.
/// </summary>
/// <param name="match">Match to remove conn from.</param>
/// <param name="conn">Connection to remove from match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveFromMatch(int match, NetworkConnection conn, NetworkManager manager)
{
if (_matchConnections.TryGetValueIL2CPP(match, out HashSet<NetworkConnection> results))
{
bool r = results.Remove(conn);
_connectionMatch.Remove(conn);
if (r)
FinalizeChange(match, results, manager);
}
}
/// <summary>
/// Removes connections from a match.
/// </summary>
/// <param name="match">Match to remove conns from.</param>
/// <param name="conns">Connections to remove from match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveFromMatch(int match, NetworkConnection[] conns, NetworkManager manager)
{
if (_matchConnections.TryGetValueIL2CPP(match, out HashSet<NetworkConnection> results))
{
bool r = false;
for (int i = 0; i < conns.Length; i++)
{
NetworkConnection c = conns[i];
r |= results.Remove(c);
_connectionMatch.Remove(c);
}
if (r)
FinalizeChange(match, results, manager);
}
}
/// <summary>
/// Removes connections from a match.
/// </summary>
/// <param name="match">Match to remove conns from.</param>
/// <param name="conns">Connections to remove from match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveFromMatch(int match, List<NetworkConnection> conns, NetworkManager manager)
{
if (_matchConnections.TryGetValueIL2CPP(match, out HashSet<NetworkConnection> results))
{
bool r = false;
for (int i = 0; i < conns.Count; i++)
{
NetworkConnection c = conns[i];
r |= results.Remove(c);
_connectionMatch.Remove(c);
}
if (r)
FinalizeChange(match, results, manager);
}
}
#endregion
#region Remove from match NetworkObject.
/// <summary>
/// Removes a network object from any match without rebuilding observers.
/// </summary>
/// <param name="nob">NetworkObject to remove.</param>
/// <param name="manager">Manager which the network object belongs to. This value is not yet used.</param>
internal static bool RemoveFromMatchWithoutRebuild(NetworkObject nob, NetworkManager manager = null)
{
bool removed = false;
//If found to be in a match.
if (_objectMatch.TryGetValueIL2CPP(nob, out int match))
{
//If match is found.
if (_matchObjects.TryGetValue(match, out HashSet<NetworkObject> nobs))
{
removed |= nobs.Remove(nob);
//If no more in hashset remove match.
if (nobs.Count == 0)
_matchObjects.Remove(match);
}
}
//Remove from connectionMatch.
_objectMatch.Remove(nob);
return removed;
}
/// <summary>
/// Removes nob from all matches.
/// </summary>
/// <param name="nob">NetworkObject to remove.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
public static void RemoveFromMatch(NetworkObject nob, NetworkManager manager = null)
{
bool removed = RemoveFromMatchWithoutRebuild(nob, manager);
if (removed)
GetServerObjects(manager).RebuildObservers(nob);
}
/// <summary>
/// Removes a network object from all matches.
/// </summary>
/// <param name="nobs">NetworkObjects to remove.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
public static void RemoveFromMatch(NetworkObject[] nobs, NetworkManager manager = null)
{
RemoveFromMatch(nobs.ToList(), manager);
}
/// <summary>
/// Removes network objects from all matches.
/// </summary>
/// <param name="nobs">NetworkObjects to remove.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
public static void RemoveFromMatch(List<NetworkObject> nobs, NetworkManager manager = null)
{
bool removed = false;
foreach (NetworkObject n in nobs)
removed |= RemoveFromMatchWithoutRebuild(n, manager);
if (removed)
GetServerObjects(manager).RebuildObservers(nobs);
}
/// <summary>
/// Removes a network object from a match.
/// </summary>
/// <param name="match">Match to remove conn from.</param>
/// <param name="nob">NetworkObject to remove from match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveFromMatch(int match, NetworkObject nob, NetworkManager manager = null)
{
if (_matchObjects.TryGetValueIL2CPP(match, out HashSet<NetworkObject> results))
{
bool r = results.Remove(nob);
_objectMatch.Remove(nob);
if (r)
FinalizeChange(match, results, nob, manager);
}
}
/// <summary>
/// Removes network objects from a match.
/// </summary>
/// <param name="match">Match to remove conns from.</param>
/// <param name="nobs">NetworkObjects to remove from match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveFromMatch(int match, NetworkObject[] nobs, NetworkManager manager = null)
{
if (_matchObjects.TryGetValueIL2CPP(match, out HashSet<NetworkObject> results))
{
bool r = false;
for (int i = 0; i < nobs.Length; i++)
{
NetworkObject n = nobs[i];
r |= results.Remove(n);
_objectMatch.Remove(n);
}
if (r)
FinalizeChange(match, results, nobs, manager);
}
}
/// <summary>
/// Removes network objects from a match.
/// </summary>
/// <param name="match">Match to remove conns from.</param>
/// <param name="nobs">NetworkObjects to remove from match.</param>
/// <param name="manager">NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void RemoveFromMatch(int match, List<NetworkObject> nobs, NetworkManager manager = null)
{
if (_matchObjects.TryGetValueIL2CPP(match, out HashSet<NetworkObject> results))
{
bool r = false;
for (int i = 0; i < nobs.Count; i++)
{
NetworkObject n = nobs[i];
r |= results.Remove(n);
_objectMatch.Remove(n);
}
if (r)
FinalizeChange(match, results, nobs, manager);
}
}
#endregion
#region FinalizeChange NetworkConnection.
/// <summary>
/// Finalizes changes to observers.
/// </summary>
private static void FinalizeChange(int match, HashSet<NetworkConnection> remainingConnsInMatch, NetworkManager manager)
{
if (remainingConnsInMatch.Count == 0)
_matchConnections.Remove(match);
/* Observers on all objects and all conditions have to be rebuilt.
* This is because the connection changing matches could
* require the connection to be visible for other players in the match,
* as well make other connections in the same match visible.
* But also make all the objects not associated with connections
* of that match visible. In result to tick all of those boxes
* all objects need to be rebuilt for all connections. */
GetServerObjects(manager).RebuildObservers();
}
#endregion
#region FinalizeChange NetworkObject.
/// <summary>
/// Finalizes changes to observers.
/// </summary>
private static void FinalizeChange(int match, HashSet<NetworkObject> results, List<NetworkObject> nobs, NetworkManager manager)
{
ListCache<NetworkObject> cache = ListCaches.GetNetworkObjectCache();
cache.AddValues(nobs);
FinalizeChange(match, results, cache, manager);
ListCaches.StoreCache(cache);
}
/// <summary>
/// Finalizes changes to observers.
/// </summary>
private static void FinalizeChange(int match, HashSet<NetworkObject> results, NetworkObject[] nobs, NetworkManager manager)
{
ListCache<NetworkObject> cache = ListCaches.GetNetworkObjectCache();
cache.AddValues(nobs);
FinalizeChange(match, results, cache, manager);
ListCaches.StoreCache(cache);
}
/// <summary>
/// Finalizes changes to observers.
/// </summary>
private static void FinalizeChange(int match, HashSet<NetworkObject> results, NetworkObject nob, NetworkManager manager)
{
ListCache<NetworkObject> cache = ListCaches.GetNetworkObjectCache();
cache.AddValue(nob);
FinalizeChange(match, results, cache, manager);
ListCaches.StoreCache(cache);
}
/// <summary>
/// Finalizes changes to observers.
/// </summary>
private static void FinalizeChange(int match, HashSet<NetworkObject> results, ListCache<NetworkObject> nobs, NetworkManager manager)
{
if (results.Count == 0)
_matchConnections.Remove(match);
GetServerObjects(manager).RebuildObservers(nobs);
}
#endregion
/// <summary>
/// Returns if the object which this condition resides should be visible to connection.
/// </summary>
/// <param name="connection">Connection which the condition is being checked for.</param>
/// <param name="currentlyAdded">True if the connection currently has visibility of this object.</param>
/// <param name="notProcessed">True if the condition was not processed. This can be used to skip processing for performance. While output as true this condition result assumes the previous ConditionMet value.</param>
public override bool ConditionMet(NetworkConnection connection, bool alreadyAdded, out bool notProcessed)
{
//If here then checks are being processed.
notProcessed = false;
NetworkConnection owner = base.NetworkObject.Owner;
/* If object is owned then check if owner
* and connection share a match. */
if (owner.IsValid)
{
//Connection isn't in a match.
if (!_connectionMatch.TryGetValueIL2CPP(connection, out int match))
{
//Return if this owner is also not in a match.
return !_connectionMatch.TryGetValueIL2CPP(owner, out int _);
}
//Match isn't found.
if (!_matchConnections.TryGetValueIL2CPP(match, out HashSet<NetworkConnection> conns))
return false;
//If owner is in same match return true.
return conns.Contains(owner);
}
/* If no owner see if the object is in a match and if so
* then compare that. */
else
{
//Object isn't in a match.
if (!_objectMatch.TryGetValueIL2CPP(base.NetworkObject, out int objectMatch))
return true;
/* See if connection is in the same match as the object.
* If connection isn't in a match then it fails. */
if (!_connectionMatch.TryGetValueIL2CPP(connection, out int connectionMatch))
return false;
return (connectionMatch == objectMatch);
}
}
/// <summary>
/// Returns which ServerObjects to rebuild observers on.
/// </summary>
/// <param name="nm"></param>
/// <returns></returns>
private static ServerObjects GetServerObjects(NetworkManager manager)
{
return (manager == null) ? InstanceFinder.ServerManager.Objects : manager.ServerManager.Objects;
}
/* //todo this needs to be passing in the network manager to clear on,
* otherwise only a single instance of NM is supported.
* Users are already forced to specify which NM to add
* matches for but the functionality separating different NMs in relation
* to such isn't done yet. */
/// <summary>
/// Clears all match information without rebuilding.
/// </summary>
internal static void ClearMatchesWithoutRebuilding()
{
_connectionMatch.Clear();
_matchConnections.Clear();
_objectMatch.Clear();
_matchObjects.Clear();
}
/// <summary>
/// True if the condition requires regular updates.
/// </summary>
/// <returns></returns>
public override bool Timed()
{
return false;
}
/// <summary>
/// Clones referenced ObserverCondition. This must be populated with your conditions settings.
/// </summary>
/// <returns></returns>
public override ObserverCondition Clone()
{
MatchCondition copy = ScriptableObject.CreateInstance<MatchCondition>();
copy.ConditionConstructor();
return copy;
}
}
}