This repository has been archived on 2025-04-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
com.unity.netcode.gameobjects/Runtime/Messaging/RpcTargets/RpcTarget.cs
Unity Technologies 07f206ff9e com.unity.netcode.gameobjects@1.8.0
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).

## [1.8.0] - 2023-12-12

### Added

- Added a new RPC attribute, which is simply `Rpc`. (#2762)
  - This is a generic attribute that can perform the functions of both Server and Client RPCs, as well as enabling client-to-client RPCs. Includes several default targets: `Server`, `NotServer`, `Owner`, `NotOwner`, `Me`, `NotMe`, `ClientsAndHost`, and `Everyone`. Runtime overrides are available for any of these targets, as well as for sending to a specific ID or groups of IDs.
  - This attribute also includes the ability to defer RPCs that are sent to the local process to the start of the next frame instead of executing them immediately, treating them as if they had gone across the network. The default behavior is to execute immediately.
  - This attribute effectively replaces `ServerRpc` and `ClientRpc`. `ServerRpc` and `ClientRpc` remain in their existing forms for backward compatibility, but `Rpc` will be the recommended and most supported option.
- Added `NetworkManager.OnConnectionEvent` as a unified connection event callback to notify clients and servers of all client connections and disconnections within the session (#2762)
- Added `NetworkManager.ServerIsHost` and `NetworkBehaviour.ServerIsHost` to allow a client to tell if it is connected to a host or to a dedicated server (#2762)
- Added `SceneEventProgress.SceneManagementNotEnabled` return status to be returned when a `NetworkSceneManager` method is invoked and scene management is not enabled. (#2735)
- Added `SceneEventProgress.ServerOnlyAction` return status to be returned when a `NetworkSceneManager` method is invoked by a client. (#2735)
- Added `NetworkObject.InstantiateAndSpawn` and `NetworkSpawnManager.InstantiateAndSpawn` methods to simplify prefab spawning by assuring that the prefab is valid and applies any override prior to instantiating the `GameObject` and spawning the `NetworkObject` instance. (#2710)

### Fixed

- Fixed issue where a client disconnected by a server-host would not receive a local notification. (#2789)
- Fixed issue where a server-host could shutdown during a relay connection but periodically the transport disconnect message sent to any connected clients could be dropped. (#2789)
- Fixed issue where a host could disconnect its local client but remain running as a server. (#2789)
- Fixed issue where `OnClientDisconnectedCallback` was not being invoked under certain conditions. (#2789)
- Fixed issue where `OnClientDisconnectedCallback` was always returning 0 as the client identifier. (#2789)
- Fixed issue where if a host or server shutdown while a client owned NetworkObjects (other than the player) it would throw an exception. (#2789)
- Fixed issue where setting values on a `NetworkVariable` or `NetworkList` within `OnNetworkDespawn` during a shutdown sequence would throw an exception. (#2789)
- Fixed issue where a teleport state could potentially be overridden by a previous unreliable delta state. (#2777)
- Fixed issue where `NetworkTransform` was using the `NetworkManager.ServerTime.Tick` as opposed to `NetworkManager.NetworkTickSystem.ServerTime.Tick` during the authoritative side's tick update where it performed a delta state check. (#2777)
- Fixed issue where a parented in-scene placed NetworkObject would be destroyed upon a client or server exiting a network session but not unloading the original scene in which the NetworkObject was placed. (#2737)
- Fixed issue where during client synchronization and scene loading, when client synchronization or the scene loading mode are set to `LoadSceneMode.Single`, a `CreateObjectMessage` could be received, processed, and the resultant spawned `NetworkObject` could be instantiated in the client's currently active scene that could, towards the end of the client synchronization or loading process, be unloaded and cause the newly created `NetworkObject` to be destroyed (and throw and exception). (#2735)
- Fixed issue where a `NetworkTransform` instance with interpolation enabled would result in wide visual motion gaps (stuttering) under above normal latency conditions and a 1-5% or higher packet are drop rate. (#2713)
- Fixed issue where  you could not have multiple source network prefab overrides targeting the same network prefab as their override. (#2710)

### Changed
- Changed the server or host shutdown so it will now perform a "soft shutdown" when `NetworkManager.Shutdown` is invoked. This will send a disconnect notification to all connected clients and the server-host will wait for all connected clients to disconnect or timeout after a 5 second period before completing the shutdown process. (#2789)
- Changed `OnClientDisconnectedCallback` will now return the assigned client identifier on the local client side if the client was approved and assigned one prior to being disconnected. (#2789)
- Changed `NetworkTransform.SetState` (and related methods) now are cumulative during a fractional tick period and sent on the next pending tick. (#2777)
- `NetworkManager.ConnectedClientsIds` is now accessible on the client side and will contain the list of all clients in the session, including the host client if the server is operating in host mode (#2762)
- Changed `NetworkSceneManager` to return a `SceneEventProgress` status and not throw exceptions for methods invoked when scene management is disabled and when a client attempts to access a `NetworkSceneManager` method by a client. (#2735)
- Changed `NetworkTransform` authoritative instance tick registration so a single `NetworkTransform` specific tick event update will update all authoritative instances to improve perofmance. (#2713)
- Changed `NetworkPrefabs.OverrideToNetworkPrefab` dictionary is no longer used/populated due to it ending up being related to a regression bug and not allowing more than one override to be assigned to a network prefab asset. (#2710)
- Changed in-scene placed `NetworkObject`s now store their source network prefab asset's `GlobalObjectIdHash` internally that is used, when scene management is disabled, by clients to spawn the correct prefab even if the `NetworkPrefab` entry has an override. This does not impact dynamically spawning the same prefab which will yield the override on both host and client. (#2710)
- Changed in-scene placed `NetworkObject`s no longer require a `NetworkPrefab` entry with `GlobalObjectIdHash` override in order for clients to properly synchronize. (#2710)
- Changed in-scene placed `NetworkObject`s now set their `IsSceneObject` value when generating their `GlobalObjectIdHash` value. (#2710)
- Changed the default `NetworkConfig.SpawnTimeout` value from 1.0s to 10.0s. (#2710)
2023-12-12 00:00:00 +00:00

565 lines
24 KiB
C#

using System.Collections.Generic;
using Unity.Collections;
namespace Unity.Netcode
{
/// <summary>
/// Configuration for the default method by which an RPC is communicated across the network
/// </summary>
public enum SendTo
{
/// <summary>
/// Send to the NetworkObject's current owner.
/// Will execute locally if the local process is the owner.
/// </summary>
Owner,
/// <summary>
/// Send to everyone but the current owner, filtered to the current observer list.
/// Will execute locally if the local process is not the owner.
/// </summary>
NotOwner,
/// <summary>
/// Send to the server, regardless of ownership.
/// Will execute locally if invoked on the server.
/// </summary>
Server,
/// <summary>
/// Send to everyone but the server, filtered to the current observer list.
/// Will NOT send to a server running in host mode - it is still treated as a server.
/// If you want to send to servers when they are host, but not when they are dedicated server, use
/// <see cref="ClientsAndHost"/>.
/// <br />
/// <br />
/// Will execute locally if invoked on a client.
/// Will NOT execute locally if invoked on a server running in host mode.
/// </summary>
NotServer,
/// <summary>
/// Execute this RPC locally.
/// <br />
/// <br />
/// Normally this is no different from a standard function call.
/// <br />
/// <br />
/// Using the DeferLocal parameter of the attribute or the LocalDeferMode override in RpcSendParams,
/// this can allow an RPC to be processed on localhost with a one-frame delay as if it were sent over
/// the network.
/// </summary>
Me,
/// <summary>
/// Send this RPC to everyone but the local machine, filtered to the current observer list.
/// </summary>
NotMe,
/// <summary>
/// Send this RPC to everone, filtered to the current observer list.
/// Will execute locally.
/// </summary>
Everyone,
/// <summary>
/// Send this RPC to all clients, including the host, if a host exists.
/// If the server is running in host mode, this is the same as <see cref="Everyone" />.
/// If the server is running in dedicated server mode, this is the same as <see cref="NotServer" />.
/// </summary>
ClientsAndHost,
/// <summary>
/// This RPC cannot be sent without passing in a target in RpcSendParams.
/// </summary>
SpecifiedInParams
}
public enum RpcTargetUse
{
Temp,
Persistent
}
/// <summary>
/// Implementations of the various <see cref="SendTo"/> options, as well as additional runtime-only options
/// <see cref="Single"/>,
/// <see cref="Group(NativeArray{ulong})"/>,
/// <see cref="Group(NativeList{ulong})"/>,
/// <see cref="Group(ulong[])"/>,
/// <see cref="Group{T}(T)"/>, <see cref="Not(ulong)"/>,
/// <see cref="Not(NativeArray{ulong})"/>,
/// <see cref="Not(NativeList{ulong})"/>,
/// <see cref="Not(ulong[])"/>, and
/// <see cref="Not{T}(T)"/>
/// </summary>
public class RpcTarget
{
private NetworkManager m_NetworkManager;
internal RpcTarget(NetworkManager manager)
{
m_NetworkManager = manager;
Everyone = new EveryoneRpcTarget(manager);
Owner = new OwnerRpcTarget(manager);
NotOwner = new NotOwnerRpcTarget(manager);
Server = new ServerRpcTarget(manager);
NotServer = new NotServerRpcTarget(manager);
NotMe = new NotMeRpcTarget(manager);
Me = new LocalSendRpcTarget(manager);
ClientsAndHost = new ClientsAndHostRpcTarget(manager);
m_CachedProxyRpcTargetGroup = new ProxyRpcTargetGroup(manager);
m_CachedTargetGroup = new RpcTargetGroup(manager);
m_CachedDirectSendTarget = new DirectSendRpcTarget(manager);
m_CachedProxyRpcTarget = new ProxyRpcTarget(0, manager);
m_CachedProxyRpcTargetGroup.Lock();
m_CachedTargetGroup.Lock();
m_CachedDirectSendTarget.Lock();
m_CachedProxyRpcTarget.Lock();
}
public void Dispose()
{
Everyone.Dispose();
Owner.Dispose();
NotOwner.Dispose();
Server.Dispose();
NotServer.Dispose();
NotMe.Dispose();
Me.Dispose();
ClientsAndHost.Dispose();
m_CachedProxyRpcTargetGroup.Unlock();
m_CachedTargetGroup.Unlock();
m_CachedDirectSendTarget.Unlock();
m_CachedProxyRpcTarget.Unlock();
m_CachedProxyRpcTargetGroup.Dispose();
m_CachedTargetGroup.Dispose();
m_CachedDirectSendTarget.Dispose();
m_CachedProxyRpcTarget.Dispose();
}
/// <summary>
/// Send to the NetworkObject's current owner.
/// Will execute locally if the local process is the owner.
/// </summary>
public BaseRpcTarget Owner;
/// <summary>
/// Send to everyone but the current owner, filtered to the current observer list.
/// Will execute locally if the local process is not the owner.
/// </summary>
public BaseRpcTarget NotOwner;
/// <summary>
/// Send to the server, regardless of ownership.
/// Will execute locally if invoked on the server.
/// </summary>
public BaseRpcTarget Server;
/// <summary>
/// Send to everyone but the server, filtered to the current observer list.
/// Will NOT send to a server running in host mode - it is still treated as a server.
/// If you want to send to servers when they are host, but not when they are dedicated server, use
/// <see cref="SendTo.ClientsAndHost"/>.
/// <br />
/// <br />
/// Will execute locally if invoked on a client.
/// Will NOT execute locally if invoked on a server running in host mode.
/// </summary>
public BaseRpcTarget NotServer;
/// <summary>
/// Execute this RPC locally.
/// <br />
/// <br />
/// Normally this is no different from a standard function call.
/// <br />
/// <br />
/// Using the DeferLocal parameter of the attribute or the LocalDeferMode override in RpcSendParams,
/// this can allow an RPC to be processed on localhost with a one-frame delay as if it were sent over
/// the network.
/// </summary>
public BaseRpcTarget Me;
/// <summary>
/// Send this RPC to everyone but the local machine, filtered to the current observer list.
/// </summary>
public BaseRpcTarget NotMe;
/// <summary>
/// Send this RPC to everone, filtered to the current observer list.
/// Will execute locally.
/// </summary>
public BaseRpcTarget Everyone;
/// <summary>
/// Send this RPC to all clients, including the host, if a host exists.
/// If the server is running in host mode, this is the same as <see cref="Everyone" />.
/// If the server is running in dedicated server mode, this is the same as <see cref="NotServer" />.
/// </summary>
public BaseRpcTarget ClientsAndHost;
/// <summary>
/// Send to a specific single client ID.
/// </summary>
/// <param name="clientId"></param>
/// <param name="use"><see cref="RpcTargetUse.Temp"/> will return a cached target, which should not be stored as it will
/// be overwritten in future calls to Single(). Do not call Dispose() on Temp targets.<br /><br /><see cref="RpcTargetUse.Persistent"/> will
/// return a new target, which can be stored, but should not be done frequently because it results in a GC allocation. You must call Dispose() on Persistent targets when you are done with them.</param>
/// <returns></returns>
public BaseRpcTarget Single(ulong clientId, RpcTargetUse use)
{
if (clientId == m_NetworkManager.LocalClientId)
{
return Me;
}
if (m_NetworkManager.IsServer || clientId == NetworkManager.ServerClientId)
{
if (use == RpcTargetUse.Persistent)
{
return new DirectSendRpcTarget(clientId, m_NetworkManager);
}
m_CachedDirectSendTarget.SetClientId(clientId);
return m_CachedDirectSendTarget;
}
if (use == RpcTargetUse.Persistent)
{
return new ProxyRpcTarget(clientId, m_NetworkManager);
}
m_CachedProxyRpcTarget.SetClientId(clientId);
return m_CachedProxyRpcTarget;
}
/// <summary>
/// Send to everyone EXCEPT a specific single client ID.
/// </summary>
/// <param name="excludedClientId"></param>
/// <param name="use"><see cref="RpcTargetUse.Temp"/> will return a cached target, which should not be stored as it will
/// be overwritten in future calls to Not() or Group(). Do not call Dispose() on Temp targets.<br /><br /><see cref="RpcTargetUse.Persistent"/> will
/// return a new target, which can be stored, but should not be done frequently because it results in a GC allocation. You must call Dispose() on Persistent targets when you are done with them.</param>
/// <returns></returns>
public BaseRpcTarget Not(ulong excludedClientId, RpcTargetUse use)
{
IGroupRpcTarget target;
if (m_NetworkManager.IsServer)
{
if (use == RpcTargetUse.Persistent)
{
target = new RpcTargetGroup(m_NetworkManager);
}
else
{
target = m_CachedTargetGroup;
}
}
else
{
if (use == RpcTargetUse.Persistent)
{
target = new ProxyRpcTargetGroup(m_NetworkManager);
}
else
{
target = m_CachedProxyRpcTargetGroup;
}
}
target.Clear();
foreach (var clientId in m_NetworkManager.ConnectedClientsIds)
{
if (clientId != excludedClientId)
{
target.Add(clientId);
}
}
// If ServerIsHost, ConnectedClientIds already contains ServerClientId and this would duplicate it.
if (!m_NetworkManager.ServerIsHost && excludedClientId != NetworkManager.ServerClientId)
{
target.Add(NetworkManager.ServerClientId);
}
return target.Target;
}
/// <summary>
/// Sends to a group of client IDs.
/// NativeArrays can be trivially constructed using Allocator.Temp, making this an efficient
/// Group method if the group list is dynamically constructed.
/// </summary>
/// <param name="clientIds"></param>
/// <param name="use"><see cref="RpcTargetUse.Temp"/> will return a cached target, which should not be stored as it will
/// be overwritten in future calls to Not() or Group(). Do not call Dispose() on Temp targets.<br /><br /><see cref="RpcTargetUse.Persistent"/> will
/// return a new target, which can be stored, but should not be done frequently because it results in a GC allocation. You must call Dispose() on Persistent targets when you are done with them.</param>
/// <returns></returns>
public BaseRpcTarget Group(NativeArray<ulong> clientIds, RpcTargetUse use)
{
IGroupRpcTarget target;
if (m_NetworkManager.IsServer)
{
if (use == RpcTargetUse.Persistent)
{
target = new RpcTargetGroup(m_NetworkManager);
}
else
{
target = m_CachedTargetGroup;
}
}
else
{
if (use == RpcTargetUse.Persistent)
{
target = new ProxyRpcTargetGroup(m_NetworkManager);
}
else
{
target = m_CachedProxyRpcTargetGroup;
}
}
target.Clear();
foreach (var clientId in clientIds)
{
target.Add(clientId);
}
return target.Target;
}
/// <summary>
/// Sends to a group of client IDs.
/// NativeList can be trivially constructed using Allocator.Temp, making this an efficient
/// Group method if the group list is dynamically constructed.
/// </summary>
/// <param name="clientIds"></param>
/// <param name="use"><see cref="RpcTargetUse.Temp"/> will return a cached target, which should not be stored as it will
/// be overwritten in future calls to Not() or Group(). Do not call Dispose() on Temp targets.<br /><br /><see cref="RpcTargetUse.Persistent"/> will
/// return a new target, which can be stored, but should not be done frequently because it results in a GC allocation. You must call Dispose() on Persistent targets when you are done with them.</param>
/// <returns></returns>
public BaseRpcTarget Group(NativeList<ulong> clientIds, RpcTargetUse use)
{
var asArray = clientIds.AsArray();
return Group(asArray, use);
}
/// <summary>
/// Sends to a group of client IDs.
/// Constructing arrays requires garbage collected allocations. This override is only recommended
/// if you either have no strict performance requirements, or have the group of client IDs cached so
/// it is not created each time.
/// </summary>
/// <param name="clientIds"></param>
/// <param name="use"><see cref="RpcTargetUse.Temp"/> will return a cached target, which should not be stored as it will
/// be overwritten in future calls to Not() or Group(). Do not call Dispose() on Temp targets.<br /><br /><see cref="RpcTargetUse.Persistent"/> will
/// return a new target, which can be stored, but should not be done frequently because it results in a GC allocation. You must call Dispose() on Persistent targets when you are done with them.</param>
/// <returns></returns>
public BaseRpcTarget Group(ulong[] clientIds, RpcTargetUse use)
{
return Group(new NativeArray<ulong>(clientIds, Allocator.Temp), use);
}
/// <summary>
/// Sends to a group of client IDs.
/// This accepts any IEnumerable type, such as List&lt;ulong&gt;, but cannot be called without
/// a garbage collected allocation (even if the type itself is a struct type, due to boxing).
/// This override is only recommended if you either have no strict performance requirements,
/// or have the group of client IDs cached so it is not created each time.
/// </summary>
/// <param name="clientIds"></param>
/// <param name="use"><see cref="RpcTargetUse.Temp"/> will return a cached target, which should not be stored as it will
/// be overwritten in future calls to Not() or Group(). Do not call Dispose() on Temp targets.<br /><br /><see cref="RpcTargetUse.Persistent"/> will
/// return a new target, which can be stored, but should not be done frequently because it results in a GC allocation. You must call Dispose() on Persistent targets when you are done with them.</param>
/// <returns></returns>
public BaseRpcTarget Group<T>(T clientIds, RpcTargetUse use) where T : IEnumerable<ulong>
{
IGroupRpcTarget target;
if (m_NetworkManager.IsServer)
{
if (use == RpcTargetUse.Persistent)
{
target = new RpcTargetGroup(m_NetworkManager);
}
else
{
target = m_CachedTargetGroup;
}
}
else
{
if (use == RpcTargetUse.Persistent)
{
target = new ProxyRpcTargetGroup(m_NetworkManager);
}
else
{
target = m_CachedProxyRpcTargetGroup;
}
}
target.Clear();
foreach (var clientId in clientIds)
{
target.Add(clientId);
}
return target.Target;
}
/// <summary>
/// Sends to everyone EXCEPT a group of client IDs.
/// NativeArrays can be trivially constructed using Allocator.Temp, making this an efficient
/// Group method if the group list is dynamically constructed.
/// </summary>
/// <param name="excludedClientIds"></param>
/// <param name="use"><see cref="RpcTargetUse.Temp"/> will return a cached target, which should not be stored as it will
/// be overwritten in future calls to Not() or Group(). Do not call Dispose() on Temp targets.<br /><br /><see cref="RpcTargetUse.Persistent"/> will
/// return a new target, which can be stored, but should not be done frequently because it results in a GC allocation. You must call Dispose() on Persistent targets when you are done with them.</param>
/// <returns></returns>
public BaseRpcTarget Not(NativeArray<ulong> excludedClientIds, RpcTargetUse use)
{
IGroupRpcTarget target;
if (m_NetworkManager.IsServer)
{
if (use == RpcTargetUse.Persistent)
{
target = new RpcTargetGroup(m_NetworkManager);
}
else
{
target = m_CachedTargetGroup;
}
}
else
{
if (use == RpcTargetUse.Persistent)
{
target = new ProxyRpcTargetGroup(m_NetworkManager);
}
else
{
target = m_CachedProxyRpcTargetGroup;
}
}
target.Clear();
using var asASet = new NativeHashSet<ulong>(excludedClientIds.Length, Allocator.Temp);
foreach (var clientId in excludedClientIds)
{
asASet.Add(clientId);
}
foreach (var clientId in m_NetworkManager.ConnectedClientsIds)
{
if (!asASet.Contains(clientId))
{
target.Add(clientId);
}
}
// If ServerIsHost, ConnectedClientIds already contains ServerClientId and this would duplicate it.
if (!m_NetworkManager.ServerIsHost && !asASet.Contains(NetworkManager.ServerClientId))
{
target.Add(NetworkManager.ServerClientId);
}
return target.Target;
}
/// <summary>
/// Sends to everyone EXCEPT a group of client IDs.
/// NativeList can be trivially constructed using Allocator.Temp, making this an efficient
/// Group method if the group list is dynamically constructed.
/// </summary>
/// <param name="excludedClientIds"></param>
/// <param name="use"><see cref="RpcTargetUse.Temp"/> will return a cached target, which should not be stored as it will
/// be overwritten in future calls to Not() or Group(). Do not call Dispose() on Temp targets.<br /><br /><see cref="RpcTargetUse.Persistent"/> will
/// return a new target, which can be stored, but should not be done frequently because it results in a GC allocation. You must call Dispose() on Persistent targets when you are done with them.</param>
/// <returns></returns>
public BaseRpcTarget Not(NativeList<ulong> excludedClientIds, RpcTargetUse use)
{
var asArray = excludedClientIds.AsArray();
return Not(asArray, use);
}
/// <summary>
/// Sends to everyone EXCEPT a group of client IDs.
/// Constructing arrays requires garbage collected allocations. This override is only recommended
/// if you either have no strict performance requirements, or have the group of client IDs cached so
/// it is not created each time.
/// </summary>
/// <param name="excludedClientIds"></param>
/// <param name="use"><see cref="RpcTargetUse.Temp"/> will return a cached target, which should not be stored as it will
/// be overwritten in future calls to Not() or Group(). Do not call Dispose() on Temp targets.<br /><br /><see cref="RpcTargetUse.Persistent"/> will
/// return a new target, which can be stored, but should not be done frequently because it results in a GC allocation. You must call Dispose() on Persistent targets when you are done with them.</param>
/// <returns></returns>
public BaseRpcTarget Not(ulong[] excludedClientIds, RpcTargetUse use)
{
return Not(new NativeArray<ulong>(excludedClientIds, Allocator.Temp), use);
}
/// <summary>
/// Sends to everyone EXCEPT a group of client IDs.
/// This accepts any IEnumerable type, such as List&lt;ulong&gt;, but cannot be called without
/// a garbage collected allocation (even if the type itself is a struct type, due to boxing).
/// This override is only recommended if you either have no strict performance requirements,
/// or have the group of client IDs cached so it is not created each time.
/// </summary>
/// <param name="excludedClientIds"></param>
/// <param name="use"><see cref="RpcTargetUse.Temp"/> will return a cached target, which should not be stored as it will
/// be overwritten in future calls to Not() or Group(). Do not call Dispose() on Temp targets.<br /><br /><see cref="RpcTargetUse.Persistent"/> will
/// return a new target, which can be stored, but should not be done frequently because it results in a GC allocation. You must call Dispose() on Persistent targets when you are done with them.</param>
/// <returns></returns>
public BaseRpcTarget Not<T>(T excludedClientIds, RpcTargetUse use) where T : IEnumerable<ulong>
{
IGroupRpcTarget target;
if (m_NetworkManager.IsServer)
{
if (use == RpcTargetUse.Persistent)
{
target = new RpcTargetGroup(m_NetworkManager);
}
else
{
target = m_CachedTargetGroup;
}
}
else
{
if (use == RpcTargetUse.Persistent)
{
target = new ProxyRpcTargetGroup(m_NetworkManager);
}
else
{
target = m_CachedProxyRpcTargetGroup;
}
}
target.Clear();
using var asASet = new NativeHashSet<ulong>(m_NetworkManager.ConnectedClientsIds.Count, Allocator.Temp);
foreach (var clientId in excludedClientIds)
{
asASet.Add(clientId);
}
foreach (var clientId in m_NetworkManager.ConnectedClientsIds)
{
if (!asASet.Contains(clientId))
{
target.Add(clientId);
}
}
// If ServerIsHost, ConnectedClientIds already contains ServerClientId and this would duplicate it.
if (!m_NetworkManager.ServerIsHost && !asASet.Contains(NetworkManager.ServerClientId))
{
target.Add(NetworkManager.ServerClientId);
}
return target.Target;
}
private ProxyRpcTargetGroup m_CachedProxyRpcTargetGroup;
private RpcTargetGroup m_CachedTargetGroup;
private DirectSendRpcTarget m_CachedDirectSendTarget;
private ProxyRpcTarget m_CachedProxyRpcTarget;
}
}