// Copyright (c) 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/StringView.h" #include "Engine/Scripting/ScriptingType.h" /// /// Message flags. /// 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); /// /// Network message structure. Provides raw data writing and reading to the message buffer. /// API_STRUCT(Namespace="FlaxEngine.Networking", NoDefault) struct FLAXENGINE_API NetworkMessage { DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkMessage); public: /// /// The raw message buffer. /// API_FIELD() uint8* Buffer = nullptr; /// /// The size in bytes of the buffer that this message has. /// API_FIELD() uint32 BufferSize = 0; /// /// The position in bytes in buffer where the next read/write will occur. /// API_FIELD() uint32 Position = 0; /// /// The unique, internal message identifier. /// API_FIELD() uint32 MessageId = 0; /// /// Set of flags that describe the message state. /// API_FIELD() NetworkMessageFlags Flags = NetworkMessageFlags::None; public: /// /// Initializes default values of the structure. /// NetworkMessage() = default; /// /// Initializes values of the structure. /// NetworkMessage(uint8* buffer, uint32 bufferSize, uint32 messageId = 0) : Buffer(buffer) , BufferSize(bufferSize) , MessageId(messageId) { } ~NetworkMessage() = default; public: /// /// Writes raw bytes into the message. /// /// The bytes that will be written. /// The amount of bytes to write from the pointer. void WriteBytes(const void* bytes, const int32 length) { if (Position + length > BufferSize) { Flags |= NetworkMessageFlags::HasError; return; } Platform::MemoryCopy(Buffer + Position, bytes, length); Position += length; } /// /// 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. void ReadBytes(void* bytes, const int32 length) { if (Position + length > BufferSize) { Flags |= NetworkMessageFlags::HasError; return; } Platform::MemoryCopy(bytes, Buffer + Position, length); Position += length; } /// /// Skips bytes from the message. /// /// Amount of bytes to skip. /// Pointer to skipped data beginning. void* SkipBytes(const int32 length) { if (Position + length > BufferSize) { Flags |= NetworkMessageFlags::HasError; return nullptr; } byte* result = Buffer + Position; Position += length; return result; } template FORCE_INLINE void WriteStructure(const T& data) { WriteBytes(&data, sizeof(data)); } template FORCE_INLINE void ReadStructure(const T& data) { ReadBytes((T*)&data, sizeof(data)); } #define DECL_READWRITE(type, name) \ 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) 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. /// void WriteVector2(const Vector2& value) { WriteSingle((float)value.X); WriteSingle((float)value.Y); } /// /// Reads and returns data of type Vector2 from the message. /// Vector2 ReadVector2() { return Vector2(ReadSingle(), ReadSingle()); } /// /// Writes data of type Vector3 into the message. /// 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. /// Vector3 ReadVector3() { return Vector3(ReadSingle(), ReadSingle(), ReadSingle()); } /// /// Writes data of type Vector4 into the message. /// 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. /// Vector4 ReadVector4() { return Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()); } /// /// Writes data of type Quaternion into the message. /// void WriteQuaternion(const Quaternion& value) { WriteBytes(&value, sizeof(Quaternion)); } /// /// Reads and returns data of type Quaternion from the message. /// Quaternion ReadQuaternion() { Quaternion result = Quaternion::Identity; ReadBytes(&result, sizeof(Quaternion)); return result; } /// /// Writes data of type String into the message. UTF-16 encoded. /// void WriteString(const StringView& value) { WriteUInt16(value.Length()); // TODO: Use 1-byte length when possible WriteBytes(value.Get(), value.Length() * sizeof(Char)); } /// /// Writes data of type String into the message. /// void WriteStringAnsi(const StringAnsiView& value) { WriteUInt16(value.Length()); // TODO: Use 1-byte length when possible WriteBytes(value.Get(), value.Length()); } /// /// Reads and returns data of type String from the message. UTF-16 encoded. Data valid within message lifetime. /// StringView ReadString() { const uint16 length = ReadUInt16(); if (length) { auto str = SkipBytes(length * 2); if (str) return StringView((const Char*)str, length); } return StringView::Empty; } /// /// Reads and returns data of type String from the message. ANSI encoded. Data valid within message lifetime. /// StringAnsiView ReadStringAnsi() { const uint16 length = ReadUInt16(); if (length) { auto str = SkipBytes(length); if (str) return StringAnsiView((const char*)str, length); } return StringAnsiView::Empty; } /// /// Writes data of type Guid into the message. /// void WriteGuid(const Guid& value) { WriteBytes((const uint8*)&value, sizeof(Guid)); } /// /// Reads and returns data of type Guid from the message. /// Guid ReadGuid() { Guid value = Guid::Empty; 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 }; };