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/CustomMessageManager.cs
Unity Technologies fe02ca682e com.unity.netcode.gameobjects@1.2.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.2.0] - 2022-11-21

### Added

- Added protected method `NetworkBehaviour.OnSynchronize` which is invoked during the initial `NetworkObject` synchronization process. This provides users the ability to include custom serialization information that will be applied to the `NetworkBehaviour` prior to the `NetworkObject` being spawned. (#2298)
- Added support for different versions of the SDK to talk to each other in circumstances where changes permit it. Starting with this version and into future versions, patch versions should be compatible as long as the minor version is the same. (#2290)
- Added `NetworkObject` auto-add helper and Multiplayer Tools install reminder settings to Project Settings. (#2285)
- Added `public string DisconnectReason` getter to `NetworkManager` and `string Reason` to `ConnectionApprovalResponse`. Allows connection approval to communicate back a reason. Also added `public void DisconnectClient(ulong clientId, string reason)` allowing setting a disconnection reason, when explicitly disconnecting a client. (#2280)

### Changed

- Changed 3rd-party `XXHash` (32 & 64) implementation with an in-house reimplementation (#2310)
- When `NetworkConfig.EnsureNetworkVariableLengthSafety` is disabled `NetworkVariable` fields do not write the additional `ushort` size value (_which helps to reduce the total synchronization message size_), but when enabled it still writes the additional `ushort` value. (#2298)
- Optimized bandwidth usage by encoding most integer fields using variable-length encoding. (#2276)

### Fixed

- Fixed issue where `NetworkTransform` components nested under a parent with a `NetworkObject` component  (i.e. network prefab) would not have their associated `GameObject`'s transform synchronized. (#2298)
- Fixed issue where `NetworkObject`s that failed to instantiate could cause the entire synchronization pipeline to be disrupted/halted for a connecting client. (#2298)
- Fixed issue where in-scene placed `NetworkObject`s nested under a `GameObject` would be added to the orphaned children list causing continual console warning log messages. (#2298)
- Custom messages are now properly received by the local client when they're sent while running in host mode. (#2296)
- Fixed issue where the host would receive more than one event completed notification when loading or unloading a scene only when no clients were connected. (#2292)
- Fixed an issue in `UnityTransport` where an error would be logged if the 'Use Encryption' flag was enabled with a Relay configuration that used a secure protocol. (#2289)
- Fixed issue where in-scene placed `NetworkObjects` were not honoring the `AutoObjectParentSync` property. (#2281)
- Fixed the issue where `NetworkManager.OnClientConnectedCallback` was being invoked before in-scene placed `NetworkObject`s had been spawned when starting `NetworkManager` as a host. (#2277)
- Creating a `FastBufferReader` with `Allocator.None` will not result in extra memory being allocated for the buffer (since it's owned externally in that scenario). (#2265)

### Removed

- Removed the `NetworkObject` auto-add and Multiplayer Tools install reminder settings from the Menu interface. (#2285)
2022-11-21 00:00:00 +00:00

338 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using Unity.Collections;
namespace Unity.Netcode
{
/// <summary>
/// The manager class to manage custom messages, note that this is different from the NetworkManager custom messages.
/// These are named and are much easier to use.
/// </summary>
public class CustomMessagingManager
{
private readonly NetworkManager m_NetworkManager;
internal CustomMessagingManager(NetworkManager networkManager)
{
m_NetworkManager = networkManager;
}
/// <summary>
/// Delegate used for incoming unnamed messages
/// </summary>
/// <param name="clientId">The clientId that sent the message</param>
/// <param name="reader">The stream containing the message data</param>
public delegate void UnnamedMessageDelegate(ulong clientId, FastBufferReader reader);
/// <summary>
/// Event invoked when unnamed messages arrive
/// </summary>
public event UnnamedMessageDelegate OnUnnamedMessage;
internal void InvokeUnnamedMessage(ulong clientId, FastBufferReader reader, int serializedHeaderSize)
{
if (OnUnnamedMessage != null)
{
var pos = reader.Position;
var delegates = OnUnnamedMessage.GetInvocationList();
foreach (var handler in delegates)
{
reader.Seek(pos);
((UnnamedMessageDelegate)handler).Invoke(clientId, reader);
}
}
m_NetworkManager.NetworkMetrics.TrackUnnamedMessageReceived(clientId, reader.Length + serializedHeaderSize);
}
/// <summary>
/// Sends unnamed message to all clients
/// </summary>
/// <param name="messageBuffer">The message stream containing the data</param>
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
public void SendUnnamedMessageToAll(FastBufferWriter messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
{
SendUnnamedMessage(m_NetworkManager.ConnectedClientsIds, messageBuffer, networkDelivery);
}
/// <summary>
/// Sends unnamed message to a list of clients
/// </summary>
/// <param name="clientIds">The clients to send to, sends to everyone if null</param>
/// <param name="messageBuffer">The message stream containing the data</param>
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
public void SendUnnamedMessage(IReadOnlyList<ulong> clientIds, FastBufferWriter messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
{
if (!m_NetworkManager.IsServer)
{
throw new InvalidOperationException("Can not send unnamed messages to multiple users as a client");
}
if (clientIds == null)
{
throw new ArgumentNullException(nameof(clientIds), "You must pass in a valid clientId List");
}
if (m_NetworkManager.IsHost)
{
for (var i = 0; i < clientIds.Count; ++i)
{
if (clientIds[i] == m_NetworkManager.LocalClientId)
{
InvokeUnnamedMessage(
m_NetworkManager.LocalClientId,
new FastBufferReader(messageBuffer, Allocator.None),
0
);
}
}
}
var message = new UnnamedMessage
{
SendData = messageBuffer
};
var size = m_NetworkManager.SendMessage(ref message, networkDelivery, clientIds);
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
if (size != 0)
{
m_NetworkManager.NetworkMetrics.TrackUnnamedMessageSent(clientIds, size);
}
}
/// <summary>
/// Sends a unnamed message to a specific client
/// </summary>
/// <param name="clientId">The client to send the message to</param>
/// <param name="messageBuffer">The message stream containing the data</param>
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
public void SendUnnamedMessage(ulong clientId, FastBufferWriter messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
{
if (m_NetworkManager.IsHost)
{
if (clientId == m_NetworkManager.LocalClientId)
{
InvokeUnnamedMessage(
m_NetworkManager.LocalClientId,
new FastBufferReader(messageBuffer, Allocator.None),
0
);
return;
}
}
var message = new UnnamedMessage
{
SendData = messageBuffer
};
var size = m_NetworkManager.SendMessage(ref message, networkDelivery, clientId);
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
if (size != 0)
{
m_NetworkManager.NetworkMetrics.TrackUnnamedMessageSent(clientId, size);
}
}
/// <summary>
/// Delegate used to handle named messages
/// </summary>
public delegate void HandleNamedMessageDelegate(ulong senderClientId, FastBufferReader messagePayload);
private Dictionary<ulong, HandleNamedMessageDelegate> m_NamedMessageHandlers32 = new Dictionary<ulong, HandleNamedMessageDelegate>();
private Dictionary<ulong, HandleNamedMessageDelegate> m_NamedMessageHandlers64 = new Dictionary<ulong, HandleNamedMessageDelegate>();
private Dictionary<ulong, string> m_MessageHandlerNameLookup32 = new Dictionary<ulong, string>();
private Dictionary<ulong, string> m_MessageHandlerNameLookup64 = new Dictionary<ulong, string>();
internal void InvokeNamedMessage(ulong hash, ulong sender, FastBufferReader reader, int serializedHeaderSize)
{
var bytesCount = reader.Length + serializedHeaderSize;
if (m_NetworkManager == null)
{
// We dont know what size to use. Try every (more collision prone)
if (m_NamedMessageHandlers32.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler32))
{
messageHandler32(sender, reader);
m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup32[hash], bytesCount);
}
if (m_NamedMessageHandlers64.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler64))
{
messageHandler64(sender, reader);
m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup64[hash], bytesCount);
}
}
else
{
// Only check the right size.
switch (m_NetworkManager.NetworkConfig.RpcHashSize)
{
case HashSize.VarIntFourBytes:
if (m_NamedMessageHandlers32.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler32))
{
messageHandler32(sender, reader);
m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup32[hash], bytesCount);
}
break;
case HashSize.VarIntEightBytes:
if (m_NamedMessageHandlers64.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler64))
{
messageHandler64(sender, reader);
m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup64[hash], bytesCount);
}
break;
}
}
}
/// <summary>
/// Registers a named message handler delegate.
/// </summary>
/// <param name="name">Name of the message.</param>
/// <param name="callback">The callback to run when a named message is received.</param>
public void RegisterNamedMessageHandler(string name, HandleNamedMessageDelegate callback)
{
var hash32 = XXHash.Hash32(name);
var hash64 = XXHash.Hash64(name);
m_NamedMessageHandlers32[hash32] = callback;
m_NamedMessageHandlers64[hash64] = callback;
m_MessageHandlerNameLookup32[hash32] = name;
m_MessageHandlerNameLookup64[hash64] = name;
}
/// <summary>
/// Unregisters a named message handler.
/// </summary>
/// <param name="name">The name of the message.</param>
public void UnregisterNamedMessageHandler(string name)
{
var hash32 = XXHash.Hash32(name);
var hash64 = XXHash.Hash64(name);
m_NamedMessageHandlers32.Remove(hash32);
m_NamedMessageHandlers64.Remove(hash64);
m_MessageHandlerNameLookup32.Remove(hash32);
m_MessageHandlerNameLookup64.Remove(hash64);
}
/// <summary>
/// Sends a named message to all clients
/// </summary>
/// <param name="messageName">The message name to send</param>
/// <param name="messageStream">The message stream containing the data</param>
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
public void SendNamedMessageToAll(string messageName, FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
{
SendNamedMessage(messageName, m_NetworkManager.ConnectedClientsIds, messageStream, networkDelivery);
}
/// <summary>
/// Sends a named message
/// </summary>
/// <param name="messageName">The message name to send</param>
/// <param name="clientId">The client to send the message to</param>
/// <param name="messageStream">The message stream containing the data</param>
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
public void SendNamedMessage(string messageName, ulong clientId, FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
{
ulong hash = 0;
switch (m_NetworkManager.NetworkConfig.RpcHashSize)
{
case HashSize.VarIntFourBytes:
hash = XXHash.Hash32(messageName);
break;
case HashSize.VarIntEightBytes:
hash = XXHash.Hash64(messageName);
break;
}
if (m_NetworkManager.IsHost)
{
if (clientId == m_NetworkManager.LocalClientId)
{
InvokeNamedMessage(
hash,
m_NetworkManager.LocalClientId,
new FastBufferReader(messageStream, Allocator.None),
0
);
return;
}
}
var message = new NamedMessage
{
Hash = hash,
SendData = messageStream,
};
var size = m_NetworkManager.SendMessage(ref message, networkDelivery, clientId);
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
if (size != 0)
{
m_NetworkManager.NetworkMetrics.TrackNamedMessageSent(clientId, messageName, size);
}
}
/// <summary>
/// Sends the named message
/// </summary>
/// <param name="messageName">The message name to send</param>
/// <param name="clientIds">The clients to send to</param>
/// <param name="messageStream">The message stream containing the data</param>
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
public void SendNamedMessage(string messageName, IReadOnlyList<ulong> clientIds, FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
{
if (!m_NetworkManager.IsServer)
{
throw new InvalidOperationException("Can not send unnamed messages to multiple users as a client");
}
if (clientIds == null)
{
throw new ArgumentNullException(nameof(clientIds), "You must pass in a valid clientId List");
}
ulong hash = 0;
switch (m_NetworkManager.NetworkConfig.RpcHashSize)
{
case HashSize.VarIntFourBytes:
hash = XXHash.Hash32(messageName);
break;
case HashSize.VarIntEightBytes:
hash = XXHash.Hash64(messageName);
break;
}
if (m_NetworkManager.IsHost)
{
for (var i = 0; i < clientIds.Count; ++i)
{
if (clientIds[i] == m_NetworkManager.LocalClientId)
{
InvokeNamedMessage(
hash,
m_NetworkManager.LocalClientId,
new FastBufferReader(messageStream, Allocator.None),
0
);
}
}
}
var message = new NamedMessage
{
Hash = hash,
SendData = messageStream
};
var size = m_NetworkManager.SendMessage(ref message, networkDelivery, clientIds);
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
if (size != 0)
{
m_NetworkManager.NetworkMetrics.TrackNamedMessageSent(clientIds, messageName, size);
}
}
}
}