namespace Fusion.Addons.InterestManagement { using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using UnityEngine; /// /// Contains methods to for extending NetworkRunner public API. /// public static class NetworkRunnerExtensions { // PRIVATE MEMBERS private static bool _isInitialized; private static bool _isNotAvailable; private static FieldInfo _simulationFieldInfo; private static FieldInfo _playersConnectionsFieldInfo; private static FieldInfo _areaOfInterestCellsFieldInfo; private static List _interestCellPlayers = new List(); private static List _interestCellObjects = new List(); private static HashSet _uniqueInterestObjects = new HashSet(); private static Dictionary _playersAsObjects = new Dictionary(); // PUBLIC METHODS /// /// Returns global interest manager singleton. New one (of type GlobalInterestManager) is created on NetworkRunner if it doesn't exist. /// public static GlobalInterestManager GetGlobalInterestManager(this NetworkRunner runner) { TryGetGlobalInterestManager(runner, out GlobalInterestManager globalInterestManager, true); return globalInterestManager; } /// /// Returns global interest manager singleton of type T. /// /// Reference to NetworkRunner. /// Returned reference to global interest manager. /// Create new interest manager of type T on NetworkRunner if it doesn't exist. public static bool TryGetGlobalInterestManager(this NetworkRunner runner, out T globalInterestManager, bool createIfNotExists) where T : GlobalInterestManager { globalInterestManager = default; if (ReferenceEquals(runner, null) == true) return false; if (runner.TryGetComponent(out globalInterestManager) == true) return true; if (createIfNotExists == false) return false; if (runner.TryGetComponent(out GlobalInterestManager randomGlobalInterestManager) == true) { Debug.LogError($"Trying to get/create {typeof(T).Name} on {nameof(NetworkRunner)}({runner.name}), but {randomGlobalInterestManager.GetType().Name} already exists. This is not allowed!", runner.gameObject); return false; } Debug.Log($"Creating new {typeof(T).Name} on {nameof(NetworkRunner)}({runner.name}).", runner.gameObject); globalInterestManager = runner.gameObject.AddComponent(); return true; } /// /// Returns player interest manager for given player. /// public static bool TryGetPlayerInterestManager(this NetworkRunner runner, PlayerRef player, out PlayerInterestManager playerInterestManager) { if (runner.TryGetGlobalInterestManager(out GlobalInterestManager globalInterestManager, true) == true) return globalInterestManager.TryGetPlayerManager(player, out playerInterestManager); playerInterestManager = default; return false; } /// /// Returns player interest view for given player. /// public static bool TryGetPlayerInterestView(this NetworkRunner runner, PlayerRef player, out PlayerInterestView playerInterestView) { if (runner.TryGetGlobalInterestManager(out GlobalInterestManager globalInterestManager, true) == true) return globalInterestManager.TryGetPlayerView(player, out playerInterestView); playerInterestView = default; return false; } /// /// Returns reference to internal Fusion hashset with interest cells. /// DO NOT USE, THIS IS FOR INTERNAL PURPOSES ONLY! /// public static HashSet GetAreaOfInterestCells(NetworkRunner runner, PlayerRef player) { if (Initialize(runner, player) == false) return default; object simulation = _simulationFieldInfo.GetValue(runner); if (ReferenceEquals(simulation, null) == true) return default; IDictionary playerConnections = _playersConnectionsFieldInfo.GetValue(simulation) as IDictionary; if (ReferenceEquals(playerConnections, null) == true) return default; if (_playersAsObjects.TryGetValue(player, out object playerAsObject) == false) { playerAsObject = player; _playersAsObjects[player] = playerAsObject; } if (playerConnections.Contains(playerAsObject) == false) return default; object simulationConnection = playerConnections[playerAsObject]; if (ReferenceEquals(simulationConnection, null) == true) return default; return _areaOfInterestCellsFieldInfo.GetValue(simulationConnection) as HashSet; } /// /// Returns all objects in the player interest. /// DO NOT USE, THIS IS FOR INTERNAL PURPOSES ONLY! /// public static bool GetObjectsInPlayerInterest(NetworkRunner runner, PlayerRef player, List objects, HashSet playerInterestCells = null) { objects.Clear(); if (Initialize(runner, player) == false) return false; if (playerInterestCells == null) { playerInterestCells = GetAreaOfInterestCells(runner, player); } if (playerInterestCells == null) return false; _uniqueInterestObjects.Clear(); Simulation simulation = (Simulation)_simulationFieldInfo.GetValue(runner); foreach (int cell in playerInterestCells) { simulation.GetObjectsAndPlayersInAreaOfInterestCell(cell, _interestCellPlayers, _interestCellObjects); foreach (NetworkId interestCellObject in _interestCellObjects) { if (_uniqueInterestObjects.Add(interestCellObject) == true) { NetworkObject networkObject = runner.FindObject(interestCellObject); if (ReferenceEquals(networkObject, null) == false) { objects.Add(networkObject); } } } } return true; } // PRIVATE METHODS private static bool Initialize(NetworkRunner runner, PlayerRef player) { if (_isInitialized == true) return true; if (_isNotAvailable == true) return false; if (_simulationFieldInfo == null) { try { _simulationFieldInfo = typeof(NetworkRunner).GetField("_simulation", BindingFlags.Instance | BindingFlags.NonPublic); } catch (Exception exception) { Debug.LogException(exception); } if (_simulationFieldInfo == null) { _isNotAvailable = true; return false; } } object simulation = _simulationFieldInfo.GetValue(runner); if (ReferenceEquals(simulation, null) == true) return false; if (_playersConnectionsFieldInfo == null) { try { _playersConnectionsFieldInfo = typeof(Simulation).GetField("_playersConnections", BindingFlags.NonPublic | BindingFlags.Instance); } catch (Exception exception) { Debug.LogException(exception); } if (_playersConnectionsFieldInfo == null) { _isNotAvailable = true; return false; } } IDictionary playerConnections = _playersConnectionsFieldInfo.GetValue(simulation) as IDictionary; if (ReferenceEquals(playerConnections, null) == true) return false; if (_playersAsObjects.TryGetValue(player, out object playerAsObject) == false) { playerAsObject = player; _playersAsObjects[player] = playerAsObject; } if (playerConnections.Contains(playerAsObject) == false) return false; object simulationConnection = playerConnections[playerAsObject]; if (ReferenceEquals(simulationConnection, null) == true) return false; if (_areaOfInterestCellsFieldInfo == null) { try { _areaOfInterestCellsFieldInfo = simulationConnection.GetType().GetField("AreaOfInterestCells", BindingFlags.Instance | BindingFlags.Public); } catch (Exception exception) { Debug.LogException(exception); } if (_areaOfInterestCellsFieldInfo == null) { _isNotAvailable = true; return false; } } _isInitialized = true; return true; } } }