Refactor NetworkMessage and NetworkStream to handle errors gracefully, including in Release builds

This commit is contained in:
2026-09-04 11:41:11 +02:00
parent df229983fd
commit ba231ed56d
12 changed files with 239 additions and 170 deletions
@@ -42,7 +42,7 @@ void SendPacketToPeer(ENetPeer* peer, const NetworkChannelType channelType, cons
// Tho, we cannot use it, because we're releasing the message right after the send - and the packet might not
// be sent, yet. To avoid data corruption, we're just using the copy method. We might fix that later, but I'll take
// the smaller risk.
ENetPacket* packet = enet_packet_create(message.Buffer, message.Length, flag);
ENetPacket* packet = enet_packet_create(message.Buffer, message.Position, flag);
// And send it!
enet_peer_send(peer, 0, packet);
@@ -195,7 +195,7 @@ bool ENetDriver::PopEvent(NetworkEvent& eventPtr)
case ENET_EVENT_TYPE_RECEIVE:
eventPtr.EventType = NetworkEventType::Message;
eventPtr.Message = _networkHost->CreateMessage();
eventPtr.Message.Length = event.packet->dataLength;
eventPtr.Message.BufferSize = event.packet->dataLength;
Platform::MemoryCopy(eventPtr.Message.Buffer, event.packet->data, event.packet->dataLength);
break;
default:
@@ -134,8 +134,8 @@ void NetworkLagDriver::SendMessage(const NetworkChannelType channelType, const N
msg.Lag = (double)Lag;
msg.ChannelType = channelType;
msg.Type = 0;
msg.MessageData.Set(message.Buffer, message.Length);
msg.MessageLength = message.Length;
msg.MessageData.Set(message.Buffer, message.Position);
msg.MessageLength = message.Position;
}
void NetworkLagDriver::SendMessage(NetworkChannelType channelType, const NetworkMessage& message, NetworkConnection target)
@@ -151,8 +151,8 @@ void NetworkLagDriver::SendMessage(NetworkChannelType channelType, const Network
msg.ChannelType = channelType;
msg.Type = 1;
msg.Target = target;
msg.MessageData.Set(message.Buffer, message.Length);
msg.MessageLength = message.Length;
msg.MessageData.Set(message.Buffer, message.Position);
msg.MessageLength = message.Position;
}
void NetworkLagDriver::SendMessage(const NetworkChannelType channelType, const NetworkMessage& message, const Array<NetworkConnection, HeapAllocation>& targets)
@@ -168,8 +168,8 @@ void NetworkLagDriver::SendMessage(const NetworkChannelType channelType, const N
msg.ChannelType = channelType;
msg.Type = 2;
msg.Targets = targets;
msg.MessageData.Set(message.Buffer, message.Length);
msg.MessageLength = message.Length;
msg.MessageData.Set(message.Buffer, message.Position);
msg.MessageLength = message.Position;
}
NetworkDriverStats NetworkLagDriver::GetStats()
@@ -203,7 +203,7 @@ void NetworkLagDriver::OnUpdate()
// Use this helper message as a container to send the stored data and length to the ENet driver
NetworkMessage message;
message.Buffer = msg.MessageData.Get();
message.Length = msg.MessageLength;
message.BufferSize = msg.MessageLength;
switch (msg.Type)
{
+1 -1
View File
@@ -8,7 +8,7 @@
#endif
// Internal version number of networking implementation. Updated once engine changes serialization or connection rules.
#define NETWORK_PROTOCOL_VERSION 5
#define NETWORK_PROTOCOL_VERSION 6
// Enables encoding object ids and typenames via uint32 keys rather than full data send.
#define USE_NETWORK_KEYS 1
+5 -1
View File
@@ -819,14 +819,18 @@ void NetworkManagerService::Update()
if (id < (uint8)NetworkMessageIDs::MAX)
{
MessageHandlers[id](event, client, peer);
if (EnumHasAnyFlags(event.Message.Flags, NetworkMessageFlags::HasError))
{
LOG(Warning, "Error occurred while processing message id={0} from connection {1}", id, event.Sender.ConnectionId);
}
}
else
{
LOG(Warning, "Unknown message id={0} from connection {1}", id, event.Sender.ConnectionId);
}
}
peer->RecycleMessage(event.Message);
break;
}
default:
eventIsValid = false;
break;
+38 -33
View File
@@ -1,8 +1,6 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using System.Text;
using FlaxEngine.Assertions;
namespace FlaxEngine.Networking
{
@@ -12,26 +10,30 @@ namespace FlaxEngine.Networking
/// Writes raw bytes into the message.
/// </summary>
/// <param name="bytes">The bytes that will be written.</param>
/// <param name="length">The amount of bytes to write from the bytes pointer.</param>
/// <param name="length">The amount of bytes to write from the pointer.</param>
public void WriteBytes(byte* bytes, int length)
{
Assert.IsTrue(Position + length <= BufferSize, $"Could not write data of length {length} into message with id={MessageId}! Current write position={Position}");
if (Position + length > BufferSize)
{
Flags |= NetworkMessageFlags.HasError;
return;
}
Utils.MemoryCopy(new IntPtr(Buffer + Position), new IntPtr(bytes), (ulong)length);
Position += (uint)length;
Length = Position;
}
/// <summary>
/// Reads raw bytes from the message into the given byte array.
/// </summary>
/// <param name="buffer">
/// The buffer pointer that will be used to store the bytes.
/// Should be of the same length as length or longer.
/// </param>
/// <param name="buffer">The buffer pointer that will be used to store the bytes. Should be of the same length as length or longer.</param>
/// <param name="length">The minimal amount of bytes that the buffer contains.</param>
public void ReadBytes(byte* buffer, int length)
{
Assert.IsTrue(Position + length <= Length, $"Could not read data of length {length} from message with id={MessageId} and size of {Length}B! Current read position={Position}");
if (Position + length > BufferSize)
{
Flags |= NetworkMessageFlags.HasError;
return;
}
Utils.MemoryCopy(new IntPtr(buffer), new IntPtr(Buffer + Position), (ulong)length);
Position += (uint)length;
}
@@ -52,10 +54,7 @@ namespace FlaxEngine.Networking
/// <summary>
/// Reads raw bytes from the message into the given byte array.
/// </summary>
/// <param name="buffer">
/// The buffer that will be used to store the bytes.
/// Should be of the same length as length or longer.
/// </param>
/// <param name="buffer">The buffer that will be used to store the bytes. Should be of the same length as length or longer.</param>
/// <param name="length">The minimal amount of bytes that the buffer contains.</param>
public void ReadBytes(byte[] buffer, int length)
{
@@ -251,13 +250,9 @@ namespace FlaxEngine.Networking
public void WriteString(string value)
{
// Note: Make sure that this is consistent with the C++ message API!
var data = Encoding.Unicode.GetBytes(value);
var dataLength = data.Length;
var stringLength = value.Length;
WriteUInt16((ushort)stringLength); // TODO: Use 1-byte length when possible
WriteBytes(data, dataLength);
WriteUInt16((ushort)value.Length); // TODO: Use 1-byte length when possible
fixed (char* ptr = value)
WriteBytes((byte*)ptr, value.Length * 2);
}
/// <summary>
@@ -266,13 +261,24 @@ namespace FlaxEngine.Networking
public string ReadString()
{
// Note: Make sure that this is consistent with the C++ message API!
var stringLength = ReadUInt16(); // In chars
var dataLength = stringLength * sizeof(char); // In bytes
var bytes = stackalloc char[stringLength];
ReadBytes((byte*)bytes, dataLength);
return new string(bytes, 0, stringLength);
var stringLength = ReadUInt16();
if (stringLength < 200)
{
var bytes = stackalloc char[stringLength];
ReadBytes((byte*)bytes, stringLength * sizeof(char));
if ((Flags & NetworkMessageFlags.HasError) != 0)
return null;
return new string(bytes, 0, stringLength);
}
else
{
var bytes = new char[stringLength];
fixed (char* bytesPtr = bytes)
ReadBytes((byte*)bytesPtr, stringLength * sizeof(char));
if ((Flags & NetworkMessageFlags.HasError) != 0)
return null;
return new string(bytes, 0, stringLength);
}
}
/// <summary>
@@ -352,10 +358,7 @@ namespace FlaxEngine.Networking
/// </summary>
public void WriteQuaternion(Quaternion value)
{
WriteSingle(value.X);
WriteSingle(value.Y);
WriteSingle(value.Z);
WriteSingle(value.W);
WriteBytes((byte*)&value, sizeof(Quaternion));
}
/// <summary>
@@ -363,7 +366,9 @@ namespace FlaxEngine.Networking
/// </summary>
public Quaternion ReadQuaternion()
{
return new Quaternion(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
Quaternion result = Quaternion.Identity;
ReadBytes((byte*)&result, sizeof(Quaternion));
return result;
}
/// <summary>
+94 -63
View File
@@ -6,41 +6,55 @@
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Vector4.h"
#include "Engine/Core/Math/Quaternion.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/StringView.h"
#include "Engine/Scripting/ScriptingType.h"
/// <summary>
/// Message flags.
/// </summary>
API_ENUM(Attributes="Flags") enum class NetworkMessageFlags : uint32
{
// No flags.
None = 0,
// Indicates an error occurred during reading/writing to the message buffer.
HasError = 1,
};
DECLARE_ENUM_OPERATORS(NetworkMessageFlags);
/// <summary>
/// Network message structure. Provides raw data writing and reading to the message buffer.
/// </summary>
API_STRUCT(Namespace="FlaxEngine.Networking") struct FLAXENGINE_API NetworkMessage
API_STRUCT(Namespace="FlaxEngine.Networking", NoDefault) struct FLAXENGINE_API NetworkMessage
{
DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkMessage);
public:
/// <summary>
/// The raw message buffer.
/// </summary>
API_FIELD() uint8* Buffer = nullptr;
/// <summary>
/// The unique, internal message identifier.
/// </summary>
API_FIELD() uint32 MessageId = 0;
/// <summary>
/// The size in bytes of the buffer that this message has.
/// </summary>
API_FIELD() uint32 BufferSize = 0;
/// <summary>
/// The length in bytes of this message.
/// </summary>
API_FIELD() uint32 Length = 0;
/// <summary>
/// The position in bytes in buffer where the next read/write will occur.
/// </summary>
API_FIELD() uint32 Position = 0;
/// <summary>
/// The unique, internal message identifier.
/// </summary>
API_FIELD() uint32 MessageId = 0;
/// <summary>
/// Set of flags that describe the message state.
/// </summary>
API_FIELD() NetworkMessageFlags Flags = NetworkMessageFlags::None;
public:
/// <summary>
/// Initializes default values of the <seealso cref="NetworkMessage"/> structure.
@@ -50,12 +64,10 @@ public:
/// <summary>
/// Initializes values of the <seealso cref="NetworkMessage"/> structure.
/// </summary>
NetworkMessage(uint8* buffer, uint32 messageId, uint32 bufferSize, uint32 length, uint32 position)
NetworkMessage(uint8* buffer, uint32 bufferSize, uint32 messageId = 0)
: Buffer(buffer)
, MessageId(messageId)
, BufferSize(bufferSize)
, Length(length)
, Position(position)
, MessageId(messageId)
{
}
@@ -66,58 +78,66 @@ public:
/// Writes raw bytes into the message.
/// </summary>
/// <param name="bytes">The bytes that will be written.</param>
/// <param name="numBytes">The amount of bytes to write from the bytes pointer.</param>
FORCE_INLINE void WriteBytes(const uint8* bytes, const int32 numBytes)
/// <param name="length">The amount of bytes to write from the pointer.</param>
void WriteBytes(const void* bytes, const int32 length)
{
ASSERT(Position + numBytes <= BufferSize);
Platform::MemoryCopy(Buffer + Position, bytes, numBytes);
Position += numBytes;
Length = Position;
if (Position + length > BufferSize)
{
Flags |= NetworkMessageFlags::HasError;
return;
}
Platform::MemoryCopy(Buffer + Position, bytes, length);
Position += length;
}
/// <summary>
/// Reads raw bytes from the message into the given byte array.
/// </summary>
/// <param name="bytes">
/// The buffer pointer that will be used to store the bytes.
/// Should be of the same length as length or longer.
/// </param>
/// <param name="numBytes">The minimal amount of bytes that the buffer contains.</param>
FORCE_INLINE void ReadBytes(uint8* bytes, const int32 numBytes)
/// <param name="bytes">The buffer pointer that will be used to store the bytes. Should be of the same length as length or longer.</param>
/// <param name="length">The minimal amount of bytes that the buffer contains.</param>
void ReadBytes(void* bytes, const int32 length)
{
ASSERT(Position + numBytes <= BufferSize);
Platform::MemoryCopy(bytes, Buffer + Position, numBytes);
Position += numBytes;
if (Position + length > BufferSize)
{
Flags |= NetworkMessageFlags::HasError;
return;
}
Platform::MemoryCopy(bytes, Buffer + Position, length);
Position += length;
}
/// <summary>
/// Skips bytes from the message.
/// </summary>
/// <param name="numBytes">Amount of bytes to skip.</param>
/// <param name="length">Amount of bytes to skip.</param>
/// <returns>Pointer to skipped data beginning.</returns>
FORCE_INLINE void* SkipBytes(const int32 numBytes)
void* SkipBytes(const int32 length)
{
ASSERT(Position + numBytes <= BufferSize);
if (Position + length > BufferSize)
{
Flags |= NetworkMessageFlags::HasError;
return nullptr;
}
byte* result = Buffer + Position;
Position += numBytes;
Position += length;
return result;
}
template<typename T>
FORCE_INLINE void WriteStructure(const T& data)
{
WriteBytes((const uint8*)&data, sizeof(data));
WriteBytes(&data, sizeof(data));
}
template<typename T>
FORCE_INLINE void ReadStructure(const T& data)
{
ReadBytes((uint8*)&data, sizeof(data));
ReadBytes((T*)&data, sizeof(data));
}
#define DECL_READWRITE(type, name) \
FORCE_INLINE void Write##name(type value) { WriteBytes(reinterpret_cast<const uint8*>(&value), sizeof(type)); } \
FORCE_INLINE type Read##name() { type value = 0; ReadBytes(reinterpret_cast<uint8*>(&value), sizeof(type)); return value; }
void Write##name(type value) { WriteBytes(&value, sizeof(type)); } \
type Read##name() { type value = 0; ReadBytes(&value, sizeof(type)); return value; }
DECL_READWRITE(int8, Int8)
DECL_READWRITE(uint8, UInt8)
DECL_READWRITE(int16, Int16)
@@ -134,7 +154,7 @@ public:
/// <summary>
/// Writes data of type Vector2 into the message.
/// </summary>
FORCE_INLINE void WriteVector2(const Vector2& value)
void WriteVector2(const Vector2& value)
{
WriteSingle((float)value.X);
WriteSingle((float)value.Y);
@@ -143,7 +163,7 @@ public:
/// <summary>
/// Reads and returns data of type Vector2 from the message.
/// </summary>
FORCE_INLINE Vector2 ReadVector2()
Vector2 ReadVector2()
{
return Vector2(ReadSingle(), ReadSingle());
}
@@ -151,7 +171,7 @@ public:
/// <summary>
/// Writes data of type Vector3 into the message.
/// </summary>
FORCE_INLINE void WriteVector3(const Vector3& value)
void WriteVector3(const Vector3& value)
{
WriteSingle((float)value.X);
WriteSingle((float)value.Y);
@@ -161,7 +181,7 @@ public:
/// <summary>
/// Reads and returns data of type Vector3 from the message.
/// </summary>
FORCE_INLINE Vector3 ReadVector3()
Vector3 ReadVector3()
{
return Vector3(ReadSingle(), ReadSingle(), ReadSingle());
}
@@ -169,7 +189,7 @@ public:
/// <summary>
/// Writes data of type Vector4 into the message.
/// </summary>
FORCE_INLINE void WriteVector4(const Vector4& value)
void WriteVector4(const Vector4& value)
{
WriteSingle((float)value.X);
WriteSingle((float)value.Y);
@@ -180,7 +200,7 @@ public:
/// <summary>
/// Reads and returns data of type Vector4 from the message.
/// </summary>
FORCE_INLINE Vector4 ReadVector4()
Vector4 ReadVector4()
{
return Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
}
@@ -188,62 +208,73 @@ public:
/// <summary>
/// Writes data of type Quaternion into the message.
/// </summary>
FORCE_INLINE void WriteQuaternion(const Quaternion& value)
void WriteQuaternion(const Quaternion& value)
{
WriteSingle(value.X);
WriteSingle(value.Y);
WriteSingle(value.Z);
WriteSingle(value.W);
WriteBytes(&value, sizeof(Quaternion));
}
/// <summary>
/// Reads and returns data of type Quaternion from the message.
/// </summary>
FORCE_INLINE Quaternion ReadQuaternion()
Quaternion ReadQuaternion()
{
return Quaternion(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
Quaternion result = Quaternion::Identity;
ReadBytes(&result, sizeof(Quaternion));
return result;
}
/// <summary>
/// Writes data of type String into the message. UTF-16 encoded.
/// </summary>
FORCE_INLINE void WriteString(const StringView& value)
void WriteString(const StringView& value)
{
WriteUInt16(value.Length()); // TODO: Use 1-byte length when possible
WriteBytes((const uint8*)value.Get(), value.Length() * sizeof(Char));
WriteBytes(value.Get(), value.Length() * sizeof(Char));
}
/// <summary>
/// Writes data of type String into the message.
/// </summary>
FORCE_INLINE void WriteStringAnsi(const StringAnsiView& value)
void WriteStringAnsi(const StringAnsiView& value)
{
WriteUInt16(value.Length()); // TODO: Use 1-byte length when possible
WriteBytes((const uint8*)value.Get(), value.Length());
WriteBytes(value.Get(), value.Length());
}
/// <summary>
/// Reads and returns data of type String from the message. UTF-16 encoded. Data valid within message lifetime.
/// </summary>
FORCE_INLINE StringView ReadString()
StringView ReadString()
{
const uint16 length = ReadUInt16();
return StringView(length ? (const Char*)SkipBytes(length * 2) : nullptr, length);
if (length)
{
auto str = SkipBytes(length * 2);
if (str)
return StringView((const Char*)str, length);
}
return StringView::Empty;
}
/// <summary>
/// Reads and returns data of type String from the message. ANSI encoded. Data valid within message lifetime.
/// </summary>
FORCE_INLINE StringAnsiView ReadStringAnsi()
StringAnsiView ReadStringAnsi()
{
const uint16 length = ReadUInt16();
return StringAnsiView(length ? (const char*)SkipBytes(length) : nullptr, length);
if (length)
{
auto str = SkipBytes(length);
if (str)
return StringAnsiView((const char*)str, length);
}
return StringAnsiView::Empty;
}
/// <summary>
/// Writes data of type Guid into the message.
/// </summary>
FORCE_INLINE void WriteGuid(const Guid& value)
void WriteGuid(const Guid& value)
{
WriteBytes((const uint8*)&value, sizeof(Guid));
}
@@ -251,9 +282,9 @@ public:
/// <summary>
/// Reads and returns data of type Guid from the message.
/// </summary>
FORCE_INLINE Guid ReadGuid()
Guid ReadGuid()
{
Guid value;
Guid value = Guid::Empty;
ReadBytes((uint8*)&value, sizeof(Guid));
return value;
}
+18 -14
View File
@@ -82,8 +82,9 @@ void NetworkPeer::Shutdown()
void NetworkPeer::CreateMessageBuffers()
{
if (MessageBuffer)
return;
PROFILE_MEM(Networking);
ASSERT(MessageBuffer == nullptr);
const uint32 pageSize = Platform::GetCPUInfo().PageSize;
@@ -99,10 +100,11 @@ void NetworkPeer::CreateMessageBuffers()
void NetworkPeer::DisposeMessageBuffers()
{
ASSERT(MessageBuffer != nullptr);
Platform::FreePages(MessageBuffer);
MessageBuffer = nullptr;
if (MessageBuffer)
{
Platform::FreePages(MessageBuffer);
MessageBuffer = nullptr;
}
}
bool NetworkPeer::Listen()
@@ -137,17 +139,16 @@ bool NetworkPeer::PopEvent(NetworkEvent& eventRef)
NetworkMessage NetworkPeer::CreateMessage()
{
CHECK_RETURN(MessagePool.HasItems(), NetworkMessage());
const uint32 messageId = MessagePool.Pop();
uint8* messageBuffer = GetMessageBuffer(messageId);
return NetworkMessage(messageBuffer, messageId, Config.MessageSize, 0, 0);
return NetworkMessage(messageBuffer, Config.MessageSize, messageId);
}
void NetworkPeer::RecycleMessage(const NetworkMessage& message)
{
ASSERT(message.IsValid());
#ifdef BUILD_DEBUG
ASSERT(MessagePool.Contains(message.MessageId) == false);
#endif
CHECK(message.IsValid());
ASSERT_LOW_LAYER(MessagePool.Contains(message.MessageId) == false);
// Return the message id
MessagePool.Push(message.MessageId);
@@ -160,13 +161,14 @@ NetworkMessage NetworkPeer::BeginSendMessage()
void NetworkPeer::AbortSendMessage(const NetworkMessage& message)
{
ASSERT(message.IsValid());
CHECK(message.IsValid());
RecycleMessage(message);
}
bool NetworkPeer::EndSendMessage(const NetworkChannelType channelType, const NetworkMessage& message)
{
ASSERT(message.IsValid());
CHECK_RETURN(message.IsValid(), true);
CHECK_RETURN(EnumHasNoneFlags(message.Flags, NetworkMessageFlags::HasError), true);
NetworkDriver->SendMessage(channelType, message);
@@ -176,7 +178,8 @@ bool NetworkPeer::EndSendMessage(const NetworkChannelType channelType, const Net
bool NetworkPeer::EndSendMessage(const NetworkChannelType channelType, const NetworkMessage& message, const NetworkConnection& target)
{
ASSERT(message.IsValid());
CHECK_RETURN(message.IsValid(), true);
CHECK_RETURN(EnumHasNoneFlags(message.Flags, NetworkMessageFlags::HasError), true);
NetworkDriver->SendMessage(channelType, message, target);
@@ -186,7 +189,8 @@ bool NetworkPeer::EndSendMessage(const NetworkChannelType channelType, const Net
bool NetworkPeer::EndSendMessage(const NetworkChannelType channelType, const NetworkMessage& message, const Array<NetworkConnection>& targets)
{
ASSERT(message.IsValid());
CHECK_RETURN(message.IsValid(), true);
CHECK_RETURN(EnumHasNoneFlags(message.Flags, NetworkMessageFlags::HasError), true);
NetworkDriver->SendMessage(channelType, message, targets);
+6 -12
View File
@@ -98,10 +98,8 @@ public:
/// <param name="channelType">The channel to send the message over.</param>
/// <param name="message">The message.</param>
/// <remarks>Can be used only by the client!</remarks>
/// <remarks>
/// Do not recycle the message after calling this.
/// This function automatically recycles the message.
/// </remarks>
/// <remarks>Do not recycle the message after calling this. This function automatically recycles the message.</remarks>
/// <returns>True if failed to send a message, otherwise false.</returns>
API_FUNCTION() bool EndSendMessage(NetworkChannelType channelType, const NetworkMessage& message);
/// <summary>
@@ -111,10 +109,8 @@ public:
/// <param name="message">The message.</param>
/// <param name="target">The client connection to send the message to.</param>
/// <remarks>Can be used only by the server!</remarks>
/// <remarks>
/// Do not recycle the message after calling this.
/// This function automatically recycles the message.
/// </remarks>
/// <remarks>Do not recycle the message after calling this. This function automatically recycles the message.</remarks>
/// <returns>True if failed to send a message, otherwise false.</returns>
API_FUNCTION() bool EndSendMessage(NetworkChannelType channelType, const NetworkMessage& message, const NetworkConnection& target);
/// <summary>
@@ -124,10 +120,8 @@ public:
/// <param name="message">The message.</param>
/// <param name="targets">The connections list to send the message to.</param>
/// <remarks>Can be used only by the server!</remarks>
/// <remarks>
/// Do not recycle the message after calling this.
/// This function automatically recycles the message.
/// </remarks>
/// <remarks>Do not recycle the message after calling this. This function automatically recycles the message.</remarks>
/// <returns>True if failed to send a message, otherwise false.</returns>
API_FUNCTION() bool EndSendMessage(NetworkChannelType channelType, const NetworkMessage& message, const Array<NetworkConnection, HeapAllocation>& targets);
/// <summary>
+29 -10
View File
@@ -34,6 +34,8 @@
#include "FlaxEngine.Gen.h"
#endif
#define NETWORK_STREAM_SIZE_LIMIT 65535
#if !BUILD_RELEASE
bool NetworkReplicator::EnableLog = false;
#include "Engine/Core/Log.h"
@@ -541,7 +543,7 @@ void SendInParts(NetworkPeer* peer, NetworkChannelType channel, const byte* data
msgDataPayload.PartSize = msgDataSize;
msg.WriteStructure(msgDataPayload);
msg.WriteBytes(data, msgDataSize);
uint32 messageSize = msg.Length;
uint32 messageSize = msg.Position;
if (toServer)
peer->EndSendMessage(channel, msg);
else
@@ -561,7 +563,7 @@ void SendInParts(NetworkPeer* peer, NetworkChannelType channel, const byte* data
msg.WriteStructure(msgDataPart);
msg.WriteNetworkId(objectId);
msg.WriteBytes(data + msgDataPart.PartStart, msgDataPart.PartSize);
messageSize += msg.Length;
messageSize += msg.Position;
dataStart += msgDataPart.PartSize;
if (toServer)
peer->EndSendMessage(channel, msg);
@@ -718,13 +720,16 @@ void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients)
const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true);
if (failed)
{
//NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString());
if (stream->HasError())
{
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to serialize object {} of type {}", item.ToString(), obj->GetType().ToString());
}
return;
}
const uint32 size = stream->GetPosition();
if (size > MAX_uint16)
if (size > NETWORK_STREAM_SIZE_LIMIT)
{
LOG(Error, "Too much data for object {} replication ({} bytes provided while limit is {}).", item.ToString(), size, MAX_uint16);
LOG(Error, "Too much data for object {} replication ({} bytes provided while limit is {}).", item.ToString(), size, NETWORK_STREAM_SIZE_LIMIT);
return;
}
@@ -788,9 +793,9 @@ void SendRpc(RpcSendItem& e)
return;
}
auto& item = it->Item;
if (e.ArgsData.Length() > MAX_uint16)
if (e.ArgsData.Length() > NETWORK_STREAM_SIZE_LIMIT)
{
LOG(Error, "Too much data for object RPC method '{}.{}' on object '{}' ({} bytes provided while limit is {}).", e.Name.First.ToString(), e.Name.Second.ToString(), obj->GetID(), e.ArgsData.Length(), MAX_uint16);
LOG(Error, "Too much data for object RPC method '{}.{}' on object '{}' ({} bytes provided while limit is {}).", e.Name.First.ToString(), e.Name.Second.ToString(), obj->GetID(), e.ArgsData.Length(), NETWORK_STREAM_SIZE_LIMIT);
return;
}
const NetworkManagerMode mode = NetworkManager::Mode;
@@ -1015,7 +1020,11 @@ void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, b
const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, false);
if (failed)
{
//NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString());
if (stream->HasError())
{
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to deserialize object {} of type {}", item.ToString(), obj->GetType().ToString());
}
return;
}
if (item.AsNetworkObject)
@@ -1049,6 +1058,10 @@ void InvokeObjectRpc(const NetworkRpcInfo* info, byte* data, uint32 dataSize, ui
// Execute RPC
info->Execute(obj, stream, info->Tag);
if (stream->HasError())
{
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to read stream with arguments of RPC for object {}", obj->GetType().ToString());
}
}
void InvokeObjectSpawn(const NetworkMessageObjectSpawn& msgData, const Guid& prefabId, const NetworkMessageObjectSpawnItem* msgDataItems)
@@ -1426,7 +1439,7 @@ bool NetworkReplicator::InvokeSerializer(const ScriptingTypeHandle& typeHandle,
// Invoke serializer
const byte idx = serialize ? 0 : 1;
serializer.Methods[idx](instance, stream, serializer.Tags[idx]);
return false;
return stream->HasError();
}
void NetworkReplicator::AddObject(ScriptingObject* obj, const ScriptingObject* parent)
@@ -1825,6 +1838,11 @@ bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
if (!info || !obj || NetworkManager::IsOffline())
return false;
if (argsStream && argsStream->HasError())
{
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to write stream with RPC arguments '{}::{}'", type.ToString(), name.ToString());
return true;
}
PROFILE_MEM(Networking);
ObjectsLock.Lock();
auto& rpc = RpcQueue.AddOne();
@@ -1832,7 +1850,8 @@ bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
rpc.Name.First = type;
rpc.Name.Second = name;
rpc.Info = *info;
rpc.ArgsData.Copy(Span<byte>(argsStream->GetBuffer(), argsStream->GetPosition()));
if (argsStream)
rpc.ArgsData.Copy(Span<byte>(argsStream->GetBuffer(), argsStream->GetPosition()));
rpc.Targets.Copy(targetIds);
ObjectsLock.Unlock();
+9 -1
View File
@@ -126,8 +126,9 @@ void NetworkStream::Initialize(uint32 minCapacity)
_allocated = true;
}
// Reset pointer to the start
// Reset state
_position = _buffer;
ReadStream::_hasError = false;
}
void NetworkStream::Initialize(byte* buffer, uint32 length)
@@ -137,6 +138,7 @@ void NetworkStream::Initialize(byte* buffer, uint32 length)
_position = _buffer = buffer;
_length = length;
_allocated = false;
ReadStream::_hasError = false;
}
void NetworkStream::Read(INetworkSerializable& obj)
@@ -196,6 +198,11 @@ void NetworkStream::Write(const Transform& data, bool useDouble)
NetworkQuaternion::Write(this, data.Orientation);
}
bool NetworkStream::HasError() const
{
return ReadStream::_hasError;
}
void NetworkStream::Flush()
{
// Nothing to do
@@ -208,6 +215,7 @@ void NetworkStream::Close()
_position = _buffer = nullptr;
_length = 0;
_allocated = false;
ReadStream::_hasError = false;
}
uint32 NetworkStream::GetLength()
+29 -26
View File
@@ -11,7 +11,7 @@ namespace FlaxEngine.Networking
/// Writes raw bytes into the message.
/// </summary>
/// <param name="bytes">The bytes that will be written.</param>
/// <param name="length">The amount of bytes to write from the bytes pointer.</param>
/// <param name="length">The amount of bytes to write from the pointer.</param>
public void WriteBytes(byte* bytes, int length)
{
WriteData(new IntPtr(bytes), length);
@@ -20,8 +20,7 @@ namespace FlaxEngine.Networking
/// <summary>
/// Reads raw bytes from the message into the given byte array.
/// </summary>
/// <param name="buffer">The buffer pointer that will be used to store the bytes. Should be of the same length as length or longer.
/// </param>
/// <param name="buffer">The buffer pointer that will be used to store the bytes. Should be of the same length as length or longer.</param>
/// <param name="length">The minimal amount of bytes that the buffer contains.</param>
public void ReadBytes(byte* buffer, int length)
{
@@ -285,8 +284,12 @@ namespace FlaxEngine.Networking
/// </summary>
public void WriteVector2(Vector2 value)
{
WriteSingle((float)value.X);
WriteSingle((float)value.Y);
#if USE_LARGE_WORLDS
var tmp = new Float2(value);
WriteBytes((byte*)&tmp, sizeof(Float2));
#else
WriteBytes((byte*)&value, sizeof(Float2));
#endif
}
/// <summary>
@@ -302,9 +305,12 @@ namespace FlaxEngine.Networking
/// </summary>
public void WriteVector3(Vector3 value)
{
WriteSingle((float)value.X);
WriteSingle((float)value.Y);
WriteSingle((float)value.Z);
#if USE_LARGE_WORLDS
var tmp = new Vector3(value);
WriteBytes((byte*)&tmp, sizeof(Vector3));
#else
WriteBytes((byte*)&value, sizeof(Vector3));
#endif
}
/// <summary>
@@ -320,10 +326,12 @@ namespace FlaxEngine.Networking
/// </summary>
public void WriteVector4(Vector4 value)
{
WriteSingle((float)value.X);
WriteSingle((float)value.Y);
WriteSingle((float)value.Z);
WriteSingle((float)value.W);
#if USE_LARGE_WORLDS
var tmp = new Vector4(value);
WriteBytes((byte*)&tmp, sizeof(Vector4));
#else
WriteBytes((byte*)&value, sizeof(Vector4));
#endif
}
/// <summary>
@@ -339,8 +347,7 @@ namespace FlaxEngine.Networking
/// </summary>
public void WriteFloat2(Float2 value)
{
WriteSingle(value.X);
WriteSingle(value.Y);
WriteBytes((byte*)&value, sizeof(Float2));
}
/// <summary>
@@ -356,9 +363,7 @@ namespace FlaxEngine.Networking
/// </summary>
public void WriteFloat3(Float3 value)
{
WriteSingle(value.X);
WriteSingle(value.Y);
WriteSingle(value.Z);
WriteBytes((byte*)&value, sizeof(Float3));
}
/// <summary>
@@ -374,10 +379,7 @@ namespace FlaxEngine.Networking
/// </summary>
public void WriteFloat4(Float4 value)
{
WriteSingle(value.X);
WriteSingle(value.Y);
WriteSingle(value.Z);
WriteSingle(value.W);
WriteBytes((byte*)&value, sizeof(Float4));
}
/// <summary>
@@ -393,10 +395,8 @@ namespace FlaxEngine.Networking
/// </summary>
public void WriteQuaternion(Quaternion value)
{
WriteSingle(value.X);
WriteSingle(value.Y);
WriteSingle(value.Z);
WriteSingle(value.W);
// TODO: use NetworkQuaternion to match C++
WriteBytes((byte*)&value, sizeof(Quaternion));
}
/// <summary>
@@ -404,7 +404,10 @@ namespace FlaxEngine.Networking
/// </summary>
public Quaternion ReadQuaternion()
{
return new Quaternion(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
// TODO: use NetworkQuaternion to match C++
Quaternion result = Quaternion.Identity;
ReadBytes((byte*)&result, sizeof(Quaternion));
return result;
}
/// <summary>
+1
View File
@@ -83,6 +83,7 @@ public:
public:
// [Stream]
bool HasError() const override;
void Flush() override;
void Close() override;
uint32 GetLength() override;