// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #pragma once #include "Engine/Core/Math/Vector2.h" #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/Scripting/ScriptingType.h" /// /// Network message structure. Provides raw data writing and reading to the message buffer. /// API_STRUCT(Namespace="FlaxEngine.Networking") struct FLAXENGINE_API NetworkMessage { DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkMessage); public: /// /// The raw message buffer. /// API_FIELD() uint8* Buffer = nullptr; /// /// The unique, internal message identifier. /// API_FIELD() uint32 MessageId = 0; /// /// The size in bytes of the buffer that this message has. /// API_FIELD() uint32 BufferSize = 0; /// /// The length in bytes of this message. /// API_FIELD() uint32 Length = 0; /// /// The position in bytes in buffer where the next read/write will occur. /// API_FIELD() uint32 Position = 0; public: /// /// Initializes default values of the structure. /// NetworkMessage() = default; /// /// Initializes values of the structure. /// NetworkMessage(uint8* buffer, uint32 messageId, uint32 bufferSize, uint32 length, uint32 position) : Buffer(buffer) , MessageId(messageId) , BufferSize(bufferSize) , Length(length) , Position(position) { } ~NetworkMessage() = default; public: /// /// Writes raw bytes into the message. /// /// The bytes that will be written. /// The amount of bytes to write from the bytes pointer. FORCE_INLINE void WriteBytes(const uint8* bytes, const int32 numBytes) { ASSERT(Position + numBytes <= BufferSize); Platform::MemoryCopy(Buffer + Position, bytes, numBytes); Position += numBytes; Length = Position; } /// /// Reads raw bytes from the message into the given byte array. /// /// /// The buffer pointer that will be used to store the bytes. /// Should be of the same length as length or longer. /// /// The minimal amount of bytes that the buffer contains. FORCE_INLINE void ReadBytes(uint8* bytes, const int32 numBytes) { ASSERT(Position + numBytes <= BufferSize); Platform::MemoryCopy(bytes, Buffer + Position, numBytes); Position += numBytes; } /// /// Skips bytes from the message. /// /// Amount of bytes to skip. /// Pointer to skipped data beginning. FORCE_INLINE void* SkipBytes(const int32 numBytes) { ASSERT(Position + numBytes <= BufferSize); byte* result = Buffer + Position; Position += numBytes; return result; } template FORCE_INLINE void WriteStructure(const T& data) { WriteBytes((const uint8*)&data, sizeof(data)); } template FORCE_INLINE void ReadStructure(const T& data) { ReadBytes((uint8*)&data, sizeof(data)); } #define DECL_READWRITE(type, name) \ FORCE_INLINE void Write##name(type value) { WriteBytes(reinterpret_cast(&value), sizeof(type)); } \ FORCE_INLINE type Read##name() { type value = 0; ReadBytes(reinterpret_cast(&value), sizeof(type)); return value; } DECL_READWRITE(int8, Int8) DECL_READWRITE(uint8, UInt8) DECL_READWRITE(int16, Int16) DECL_READWRITE(uint16, UInt16) DECL_READWRITE(int32, Int32) DECL_READWRITE(uint32, UInt32) DECL_READWRITE(int64, Int64) DECL_READWRITE(uint64, UInt64) DECL_READWRITE(float, Single) DECL_READWRITE(double, Double) DECL_READWRITE(bool, Boolean) #undef DECL_READWRITE /// /// Writes data of type Vector2 into the message. /// FORCE_INLINE void WriteVector2(const Vector2& value) { WriteSingle((float)value.X); WriteSingle((float)value.Y); } /// /// Reads and returns data of type Vector2 from the message. /// FORCE_INLINE Vector2 ReadVector2() { return Vector2(ReadSingle(), ReadSingle()); } /// /// Writes data of type Vector3 into the message. /// FORCE_INLINE void WriteVector3(const Vector3& value) { WriteSingle((float)value.X); WriteSingle((float)value.Y); WriteSingle((float)value.Z); } /// /// Reads and returns data of type Vector3 from the message. /// FORCE_INLINE Vector3 ReadVector3() { return Vector3(ReadSingle(), ReadSingle(), ReadSingle()); } /// /// Writes data of type Vector4 into the message. /// FORCE_INLINE void WriteVector4(const Vector4& value) { WriteSingle((float)value.X); WriteSingle((float)value.Y); WriteSingle((float)value.Z); WriteSingle((float)value.W); } /// /// Reads and returns data of type Vector4 from the message. /// FORCE_INLINE Vector4 ReadVector4() { return Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()); } /// /// Writes data of type Quaternion into the message. /// FORCE_INLINE void WriteQuaternion(const Quaternion& value) { WriteSingle(value.X); WriteSingle(value.Y); WriteSingle(value.Z); WriteSingle(value.W); } /// /// Reads and returns data of type Quaternion from the message. /// FORCE_INLINE Quaternion ReadQuaternion() { return Quaternion(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()); } /// /// Writes data of type String into the message. UTF-16 encoded. /// FORCE_INLINE void WriteString(const StringView& value) { WriteUInt16(value.Length()); // TODO: Use 1-byte length when possible WriteBytes((const uint8*)value.Get(), value.Length() * sizeof(Char)); } /// /// Writes data of type String into the message. /// FORCE_INLINE void WriteStringAnsi(const StringAnsiView& value) { WriteUInt16(value.Length()); // TODO: Use 1-byte length when possible WriteBytes((const uint8*)value.Get(), value.Length()); } /// /// Reads and returns data of type String from the message. UTF-16 encoded. Data valid within message lifetime. /// FORCE_INLINE StringView ReadString() { const uint16 length = ReadUInt16(); return StringView(length ? (const Char*)SkipBytes(length * 2) : nullptr, length); } /// /// Reads and returns data of type String from the message. ANSI encoded. Data valid within message lifetime. /// FORCE_INLINE StringAnsiView ReadStringAnsi() { const uint16 length = ReadUInt16(); return StringAnsiView(length ? (const char*)SkipBytes(length) : nullptr, length); } /// /// Writes data of type Guid into the message. /// FORCE_INLINE void WriteGuid(const Guid& value) { WriteBytes((const uint8*)&value, sizeof(Guid)); } /// /// Reads and returns data of type Guid from the message. /// FORCE_INLINE Guid ReadGuid() { Guid value; ReadBytes((uint8*)&value, sizeof(Guid)); return value; } /// /// Writes identifier into the stream that is networked-synced (by a server). If both peers acknowledge a specific id then the data transfer is optimized to 32 bits. /// /// Network-synced identifier. void WriteNetworkId(const Guid& id); /// /// Reads identifier from the stream that is networked-synced (by a server). If both peers acknowledge a specific id then the data transfer is optimized to 32 bits. /// /// Network-synced identifier. void ReadNetworkId(Guid& id); /// /// Writes name into the stream that is networked-synced (by a server). If both peers acknowledge a specific name then the data transfer is optimized to 32 bits. /// /// Network-synced name. void WriteNetworkName(const StringAnsiView& name); /// /// Reads name from the stream that is networked-synced (by a server). If both peers acknowledge a specific name then the data transfer is optimized to 32 bits. /// /// Network-synced name. void ReadNetworkName(StringAnsiView& name); public: /// /// Returns true if the message is valid for reading or writing. /// bool IsValid() const { return Buffer != nullptr && BufferSize > 0; } }; template<> struct TIsPODType { enum { Value = true }; };