Network objects replication impl progress
This commit is contained in:
28
Source/Engine/Networking/INetworkSerializable.h
Normal file
28
Source/Engine/Networking/INetworkSerializable.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Compiler.h"
|
||||
#include "Engine/Core/Config.h"
|
||||
|
||||
class NetworkStream;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for values and objects that can be serialized/deserialized for network replication.
|
||||
/// </summary>
|
||||
API_INTERFACE(Namespace = "FlaxEngine.Networking") class FLAXENGINE_API INetworkSerializable
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(INetworkSerializable);
|
||||
public:
|
||||
/// <summary>
|
||||
/// Serializes object to the output stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The output stream to write serialized data.</param>
|
||||
API_FUNCTION() virtual void Serialize(NetworkStream* stream) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes object from the input stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The input stream to read serialized data.</param>
|
||||
API_FUNCTION() virtual void Deserialize(NetworkStream* stream) = 0;
|
||||
};
|
||||
@@ -1,7 +1,5 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
|
||||
namespace FlaxEngine.Networking
|
||||
{
|
||||
partial class NetworkClient
|
||||
|
||||
12
Source/Engine/Networking/NetworkInternal.h
Normal file
12
Source/Engine/Networking/NetworkInternal.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
class NetworkInternal
|
||||
{
|
||||
public:
|
||||
static void NetworkReplicatorClear();
|
||||
static void NetworkReplicatorUpdate();
|
||||
};
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "NetworkEvent.h"
|
||||
#include "NetworkChannelType.h"
|
||||
#include "NetworkSettings.h"
|
||||
#include "NetworkInternal.h"
|
||||
#include "FlaxEngine.Gen.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
@@ -15,6 +16,7 @@
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
|
||||
float NetworkManager::NetworkFPS = 60.0f;
|
||||
NetworkPeer* NetworkManager::Peer = nullptr;
|
||||
NetworkManagerMode NetworkManager::Mode = NetworkManagerMode::Offline;
|
||||
NetworkConnectionState NetworkManager::State = NetworkConnectionState::Offline;
|
||||
NetworkClient* NetworkManager::LocalClient = nullptr;
|
||||
@@ -109,7 +111,6 @@ void OnNetworkMessageHandshakeReply(NetworkEvent& event, NetworkClient* client,
|
||||
|
||||
namespace
|
||||
{
|
||||
NetworkPeer* Peer = nullptr;
|
||||
uint32 GameProtocolVersion = 0;
|
||||
double LastUpdateTime = 0;
|
||||
|
||||
@@ -144,7 +145,7 @@ NetworkManagerService NetworkManagerServiceInstance;
|
||||
bool StartPeer()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
ASSERT_LOW_LAYER(!Peer);
|
||||
ASSERT_LOW_LAYER(!NetworkManager::Peer);
|
||||
NetworkManager::State = NetworkConnectionState::Connecting;
|
||||
NetworkManager::StateChanged();
|
||||
const auto& settings = *NetworkSettings::Get();
|
||||
@@ -172,8 +173,8 @@ bool StartPeer()
|
||||
return true;
|
||||
}
|
||||
networkConfig.NetworkDriver = ScriptingObject::NewObject(networkDriverType);
|
||||
Peer = NetworkPeer::CreatePeer(networkConfig);
|
||||
if (!Peer)
|
||||
NetworkManager::Peer = NetworkPeer::CreatePeer(networkConfig);
|
||||
if (!NetworkManager::Peer)
|
||||
{
|
||||
LOG(Error, "Failed to create Network Peer at {0}:{1}", networkConfig.Address, networkConfig.Port);
|
||||
return true;
|
||||
@@ -184,13 +185,13 @@ bool StartPeer()
|
||||
|
||||
void StopPeer()
|
||||
{
|
||||
if (!Peer)
|
||||
if (!NetworkManager::Peer)
|
||||
return;
|
||||
PROFILE_CPU();
|
||||
if (NetworkManager::Mode == NetworkManagerMode::Client)
|
||||
Peer->Disconnect();
|
||||
NetworkPeer::ShutdownPeer(Peer);
|
||||
Peer = nullptr;
|
||||
NetworkManager::Peer->Disconnect();
|
||||
NetworkPeer::ShutdownPeer(NetworkManager::Peer);
|
||||
NetworkManager::Peer = nullptr;
|
||||
}
|
||||
|
||||
void NetworkSettings::Apply()
|
||||
@@ -283,6 +284,7 @@ void NetworkManager::Stop()
|
||||
client->State = NetworkConnectionState::Disconnecting;
|
||||
StateChanged();
|
||||
|
||||
NetworkInternal::NetworkReplicatorClear();
|
||||
for (int32 i = Clients.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
NetworkClient* client = Clients[i];
|
||||
@@ -312,11 +314,12 @@ void NetworkManagerService::Update()
|
||||
return;
|
||||
PROFILE_CPU();
|
||||
LastUpdateTime = currentTime;
|
||||
auto peer = NetworkManager::Peer;
|
||||
// TODO: convert into TaskGraphSystems and use async jobs
|
||||
|
||||
// Process network messages
|
||||
NetworkEvent event;
|
||||
while (Peer->PopEvent(event))
|
||||
while (peer->PopEvent(event))
|
||||
{
|
||||
switch (event.EventType)
|
||||
{
|
||||
@@ -347,10 +350,10 @@ void NetworkManagerService::Update()
|
||||
msgData.Platform = (byte)connectionData.Platform;
|
||||
msgData.Architecture = (byte)connectionData.Architecture;
|
||||
msgData.PayloadDataSize = (uint16)connectionData.PayloadData.Count();
|
||||
NetworkMessage msg = Peer->BeginSendMessage();
|
||||
NetworkMessage msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgData);
|
||||
msg.WriteBytes(connectionData.PayloadData.Get(), connectionData.PayloadData.Count());
|
||||
Peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
|
||||
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -397,15 +400,18 @@ void NetworkManagerService::Update()
|
||||
uint8 id = *event.Message.Buffer;
|
||||
if (id < (uint8)NetworkMessageIDs::MAX)
|
||||
{
|
||||
MessageHandlers[id](event, client, Peer);
|
||||
MessageHandlers[id](event, client, peer);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Warning, "Unknown message id={0} from connection {1}", id, event.Sender.ConnectionId);
|
||||
}
|
||||
}
|
||||
Peer->RecycleMessage(event.Message);
|
||||
peer->RecycleMessage(event.Message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update replication
|
||||
NetworkInternal::NetworkReplicatorUpdate();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,11 @@ public:
|
||||
/// </summary>
|
||||
API_FIELD() static float NetworkFPS;
|
||||
|
||||
/// <summary>
|
||||
/// Current network peer (low-level).
|
||||
/// </summary>
|
||||
API_FIELD(ReadOnly) static NetworkPeer* Peer;
|
||||
|
||||
/// <summary>
|
||||
/// Current manager mode.
|
||||
/// </summary>
|
||||
|
||||
169
Source/Engine/Networking/NetworkReplicator.cpp
Normal file
169
Source/Engine/Networking/NetworkReplicator.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "NetworkReplicator.h"
|
||||
#include "NetworkClient.h"
|
||||
#include "NetworkManager.h"
|
||||
#include "NetworkInternal.h"
|
||||
#include "NetworkStream.h"
|
||||
#include "INetworkSerializable.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Collections/HashSet.h"
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Scripting/ScriptingObjectReference.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
|
||||
// Enables verbose logging for Network Replicator actions (dev-only)
|
||||
#define NETWORK_REPLICATOR_DEBUG_LOG 1
|
||||
|
||||
struct NetworkReplicatedObject
|
||||
{
|
||||
ScriptingObjectReference<ScriptingObject> Object;
|
||||
Guid OwnerId;
|
||||
#if NETWORK_REPLICATOR_DEBUG_LOG
|
||||
Guid ObjectId;
|
||||
bool InvalidTypeWarn = false;
|
||||
#endif
|
||||
|
||||
bool operator==(const NetworkReplicatedObject& other) const
|
||||
{
|
||||
return Object == other.Object;
|
||||
}
|
||||
|
||||
bool operator==(const ScriptingObject* other) const
|
||||
{
|
||||
return Object == other;
|
||||
}
|
||||
};
|
||||
|
||||
#if NETWORK_REPLICATOR_DEBUG_LOG
|
||||
#include "Engine/Core/Formatting.h"
|
||||
DEFINE_DEFAULT_FORMATTING(NetworkReplicatedObject, "{}", v.ObjectId);
|
||||
#endif
|
||||
|
||||
inline uint32 GetHash(const NetworkReplicatedObject& key)
|
||||
{
|
||||
return GetHash(key.Object.Get());
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
CriticalSection ObjectsLock;
|
||||
HashSet<NetworkReplicatedObject> Objects;
|
||||
NetworkStream* CachedStream = nullptr;
|
||||
}
|
||||
|
||||
class NetworkReplicationService : public EngineService
|
||||
{
|
||||
public:
|
||||
NetworkReplicationService()
|
||||
: EngineService(TEXT("Network Replication"), 1100)
|
||||
{
|
||||
}
|
||||
|
||||
void Dispose() override;
|
||||
};
|
||||
|
||||
void NetworkReplicationService::Dispose()
|
||||
{
|
||||
NetworkInternal::NetworkReplicatorClear();
|
||||
}
|
||||
|
||||
NetworkReplicationService NetworkReplicationServiceInstance;
|
||||
|
||||
void NetworkReplicator::AddObject(ScriptingObject* obj, ScriptingObject* owner)
|
||||
{
|
||||
if (!obj || NetworkManager::State == NetworkConnectionState::Offline)
|
||||
return;
|
||||
CHECK(owner && owner != obj);
|
||||
ScopeLock lock(ObjectsLock);
|
||||
if (Objects.Contains(obj))
|
||||
return;
|
||||
|
||||
// Add object to the list
|
||||
NetworkReplicatedObject item;
|
||||
item.Object = obj;
|
||||
item.OwnerId = owner->GetID();
|
||||
#if NETWORK_REPLICATOR_DEBUG_LOG
|
||||
item.ObjectId = obj->GetID();
|
||||
LOG(Info, "[NetworkReplicator] Add new object {}:{}, owned by {}:{}", item, obj->GetType().ToString(), item.OwnerId, owner->GetType().ToString());
|
||||
#endif
|
||||
Objects.Add(MoveTemp(item));
|
||||
}
|
||||
|
||||
void NetworkInternal::NetworkReplicatorClear()
|
||||
{
|
||||
ScopeLock lock(ObjectsLock);
|
||||
|
||||
// Cleanup
|
||||
#if NETWORK_REPLICATOR_DEBUG_LOG
|
||||
LOG(Info, "[NetworkReplicator] Shutdown");
|
||||
#endif
|
||||
Objects.Clear();
|
||||
Objects.SetCapacity(0);
|
||||
SAFE_DELETE(CachedStream);
|
||||
}
|
||||
|
||||
void NetworkInternal::NetworkReplicatorUpdate()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
ScopeLock lock(ObjectsLock);
|
||||
if (Objects.Count() == 0)
|
||||
return;
|
||||
if (CachedStream == nullptr)
|
||||
CachedStream = New<NetworkStream>();
|
||||
// TODO: introduce NetworkReplicationHierarchy to optimize objects replication in large worlds (eg. batched culling networked scene objects that are too far from certain client to be relevant)
|
||||
// TODO: per-object sync interval (in frames) - could be scaled by hierarchy (eg. game could slow down sync rate for objects far from player)
|
||||
// TODO: network authority (eg. object owned by client)
|
||||
|
||||
if (NetworkManager::IsClient())
|
||||
{
|
||||
// TODO: client logic to apply replication changes
|
||||
}
|
||||
else
|
||||
{
|
||||
// Brute force synchronize all networked objects with clients
|
||||
for (auto it = Objects.Begin(); it.IsNotEnd(); ++it)
|
||||
{
|
||||
auto& item = it->Item;
|
||||
ScriptingObject* obj = item.Object.Get();
|
||||
if (!obj)
|
||||
{
|
||||
// Object got deleted
|
||||
#if NETWORK_REPLICATOR_DEBUG_LOG
|
||||
LOG(Info, "[NetworkReplicator] Remove object {}, owned by {}", item.Object, item.OwnerId);
|
||||
#endif
|
||||
Objects.Remove(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Serialize object
|
||||
// TODO: cache per-type serialization thunk to boost CPU performance
|
||||
CachedStream->Initialize(1024);
|
||||
if (auto* serializable = ScriptingObject::ToInterface<INetworkSerializable>(obj))
|
||||
{
|
||||
serializable->Serialize(CachedStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETWORK_REPLICATOR_DEBUG_LOG
|
||||
if (!item.InvalidTypeWarn)
|
||||
{
|
||||
item.InvalidTypeWarn = true;
|
||||
LOG(Error, "[NetworkReplicator] Cannot serialize object {} (missing serialization logic)", item);
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
// TODO: how to serialize object? in memory to MemoryWriteStream? handle both C++ and C# without any memory alloc!
|
||||
|
||||
// Brute force object to all clients
|
||||
for (NetworkClient* client : NetworkManager::Clients)
|
||||
{
|
||||
// TODO: split object data (eg. more messages) if needed
|
||||
// TODO: send message from Peer to client->Connection
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Source/Engine/Networking/NetworkReplicator.h
Normal file
23
Source/Engine/Networking/NetworkReplicator.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Scripting/ScriptingType.h"
|
||||
|
||||
/// <summary>
|
||||
/// High-level networking replication system for game objects.
|
||||
/// </summary>
|
||||
API_CLASS(static, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkReplicator
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkReplicator);
|
||||
public:
|
||||
/// <summary>
|
||||
/// Adds the object to the network replication system.
|
||||
/// </summary>
|
||||
/// <remarks>Does nothing if network is offline.</remarks>
|
||||
/// <param name="obj">The object to replicate.</param>
|
||||
/// <param name="owner">The owner of the object (eg. player that spawned it).</param>
|
||||
API_FUNCTION() static void AddObject(ScriptingObject* obj, ScriptingObject* owner);
|
||||
};
|
||||
113
Source/Engine/Networking/NetworkStream.cpp
Normal file
113
Source/Engine/Networking/NetworkStream.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "NetworkStream.h"
|
||||
|
||||
NetworkStream::NetworkStream(const SpawnParams& params)
|
||||
: ScriptingObject(params)
|
||||
, ReadStream()
|
||||
, WriteStream()
|
||||
{
|
||||
}
|
||||
|
||||
NetworkStream::~NetworkStream()
|
||||
{
|
||||
if (_allocated)
|
||||
Allocator::Free(_buffer);
|
||||
}
|
||||
|
||||
void NetworkStream::Initialize(uint32 minCapacity)
|
||||
{
|
||||
// Unlink buffer if was reading from memory
|
||||
if (!_allocated)
|
||||
_buffer = nullptr;
|
||||
|
||||
// Allocate if buffer is missing or too small
|
||||
if (!_buffer || _length < minCapacity)
|
||||
{
|
||||
// Release previous
|
||||
if (_buffer)
|
||||
Allocator::Free(_buffer);
|
||||
|
||||
// Allocate new one
|
||||
_buffer = (byte*)Allocator::Allocate(minCapacity);
|
||||
_length = minCapacity;
|
||||
_allocated = true;
|
||||
}
|
||||
|
||||
// Reset pointer to the start
|
||||
_position = _buffer;
|
||||
}
|
||||
|
||||
void NetworkStream::Flush()
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
void NetworkStream::Close()
|
||||
{
|
||||
if (_allocated)
|
||||
Allocator::Free(_buffer);
|
||||
_position = _buffer = nullptr;
|
||||
_length = 0;
|
||||
_allocated = false;
|
||||
}
|
||||
|
||||
uint32 NetworkStream::GetLength()
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
uint32 NetworkStream::GetPosition()
|
||||
{
|
||||
return static_cast<uint32>(_position - _buffer);
|
||||
}
|
||||
|
||||
void NetworkStream::SetPosition(uint32 seek)
|
||||
{
|
||||
ASSERT(_length > 0);
|
||||
_position = _buffer + seek;
|
||||
}
|
||||
|
||||
void NetworkStream::ReadBytes(void* data, uint32 bytes)
|
||||
{
|
||||
if (bytes > 0)
|
||||
{
|
||||
ASSERT(data && GetLength() - GetPosition() >= bytes);
|
||||
Platform::MemoryCopy(data, _position, bytes);
|
||||
_position += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkStream::WriteBytes(const void* data, uint32 bytes)
|
||||
{
|
||||
// Calculate current position
|
||||
const uint32 position = GetPosition();
|
||||
|
||||
// Check if there is need to update a buffer size
|
||||
if (_length - position < bytes)
|
||||
{
|
||||
// Perform reallocation
|
||||
uint32 newLength = _length != 0 ? _length * 2 : 256;
|
||||
while (newLength < position + bytes)
|
||||
newLength *= 2;
|
||||
byte* newBuf = (byte*)Allocator::Allocate(newLength);
|
||||
if (newBuf == nullptr)
|
||||
{
|
||||
OUT_OF_MEMORY;
|
||||
}
|
||||
if (_buffer && _length)
|
||||
Platform::MemoryCopy(newBuf, _buffer, _length);
|
||||
if (_allocated)
|
||||
Allocator::Free(_buffer);
|
||||
|
||||
// Update state
|
||||
_buffer = newBuf;
|
||||
_length = newLength;
|
||||
_position = _buffer + position;
|
||||
_allocated = true;
|
||||
}
|
||||
|
||||
// Copy data
|
||||
Platform::MemoryCopy(_position, data, bytes);
|
||||
_position += bytes;
|
||||
}
|
||||
372
Source/Engine/Networking/NetworkStream.cs
Normal file
372
Source/Engine/Networking/NetworkStream.cs
Normal file
@@ -0,0 +1,372 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace FlaxEngine.Networking
|
||||
{
|
||||
unsafe partial class NetworkStream
|
||||
{
|
||||
/// <summary>
|
||||
/// 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>
|
||||
public void WriteBytes(byte* bytes, int length)
|
||||
{
|
||||
WriteData(new IntPtr(bytes), length);
|
||||
}
|
||||
|
||||
/// <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="length">The minimal amount of bytes that the buffer contains.</param>
|
||||
public void ReadBytes(byte* buffer, int length)
|
||||
{
|
||||
ReadData(new IntPtr(buffer), length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 array.</param>
|
||||
public void WriteBytes(byte[] bytes, int length)
|
||||
{
|
||||
fixed (byte* bytesPtr = bytes)
|
||||
WriteData(new IntPtr(bytesPtr), length);
|
||||
}
|
||||
|
||||
/// <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="length">The minimal amount of bytes that the buffer contains.</param>
|
||||
public void ReadBytes(byte[] buffer, int length)
|
||||
{
|
||||
fixed (byte* bufferPtr = buffer)
|
||||
ReadData(new IntPtr(bufferPtr), length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Int64"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteInt64(long value)
|
||||
{
|
||||
WriteBytes((byte*)&value, sizeof(long));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Int64"/> from the message.
|
||||
/// </summary>
|
||||
public long ReadInt64()
|
||||
{
|
||||
long value = 0;
|
||||
ReadBytes((byte*)&value, sizeof(long));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Int32"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteInt32(int value)
|
||||
{
|
||||
WriteBytes((byte*)&value, sizeof(int));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Int32"/> from the message.
|
||||
/// </summary>
|
||||
public int ReadInt32()
|
||||
{
|
||||
int value = 0;
|
||||
ReadBytes((byte*)&value, sizeof(int));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Int16"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteInt16(short value)
|
||||
{
|
||||
WriteBytes((byte*)&value, sizeof(short));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Int16"/> from the message.
|
||||
/// </summary>
|
||||
public short ReadInt16()
|
||||
{
|
||||
short value = 0;
|
||||
ReadBytes((byte*)&value, sizeof(short));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="SByte"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteSByte(sbyte value)
|
||||
{
|
||||
WriteBytes((byte*)&value, sizeof(sbyte));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="SByte"/> from the message.
|
||||
/// </summary>
|
||||
public sbyte ReadSByte()
|
||||
{
|
||||
sbyte value = 0;
|
||||
ReadBytes((byte*)&value, sizeof(sbyte));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="UInt64"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteUInt64(ulong value)
|
||||
{
|
||||
WriteBytes((byte*)&value, sizeof(ulong));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="UInt64"/> from the message.
|
||||
/// </summary>
|
||||
public ulong ReadUInt64()
|
||||
{
|
||||
ulong value = 0;
|
||||
ReadBytes((byte*)&value, sizeof(ulong));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="UInt32"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteUInt32(uint value)
|
||||
{
|
||||
WriteBytes((byte*)&value, sizeof(uint));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="UInt32"/> from the message.
|
||||
/// </summary>
|
||||
public uint ReadUInt32()
|
||||
{
|
||||
uint value = 0;
|
||||
ReadBytes((byte*)&value, sizeof(uint));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="UInt16"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteUInt16(ushort value)
|
||||
{
|
||||
WriteBytes((byte*)&value, sizeof(ushort));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="UInt16"/> from the message.
|
||||
/// </summary>
|
||||
public ushort ReadUInt16()
|
||||
{
|
||||
ushort value = 0;
|
||||
ReadBytes((byte*)&value, sizeof(ushort));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Byte"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteByte(byte value)
|
||||
{
|
||||
WriteBytes(&value, sizeof(byte));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Byte"/> from the message.
|
||||
/// </summary>
|
||||
public byte ReadByte()
|
||||
{
|
||||
byte value = 0;
|
||||
ReadBytes(&value, sizeof(byte));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Single"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteSingle(float value)
|
||||
{
|
||||
WriteBytes((byte*)&value, sizeof(float));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Single"/> from the message.
|
||||
/// </summary>
|
||||
public float ReadSingle()
|
||||
{
|
||||
float value = 0.0f;
|
||||
ReadBytes((byte*)&value, sizeof(float));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Double"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteDouble(double value)
|
||||
{
|
||||
WriteBytes((byte*)&value, sizeof(double));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Double"/> from the message.
|
||||
/// </summary>
|
||||
public double ReadDouble()
|
||||
{
|
||||
double value = 0.0;
|
||||
ReadBytes((byte*)&value, sizeof(double));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="string"/> into the message. UTF-16 encoded.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="string"/> from the message. UTF-16 encoded.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Guid"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteGuid(Guid value)
|
||||
{
|
||||
WriteBytes((byte*)&value, sizeof(Guid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Guid"/> from the message.
|
||||
/// </summary>
|
||||
public Guid ReadGuid()
|
||||
{
|
||||
var guidData = stackalloc Guid[1];
|
||||
ReadBytes((byte*)guidData, sizeof(Guid));
|
||||
return guidData[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Vector2"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteVector2(Vector2 value)
|
||||
{
|
||||
WriteSingle((float)value.X);
|
||||
WriteSingle((float)value.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Vector2"/> from the message.
|
||||
/// </summary>
|
||||
public Vector2 ReadVector2()
|
||||
{
|
||||
return new Vector2(ReadSingle(), ReadSingle());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Vector3"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteVector3(Vector3 value)
|
||||
{
|
||||
WriteSingle((float)value.X);
|
||||
WriteSingle((float)value.Y);
|
||||
WriteSingle((float)value.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Vector3"/> from the message.
|
||||
/// </summary>
|
||||
public Vector3 ReadVector3()
|
||||
{
|
||||
return new Vector3(ReadSingle(), ReadSingle(), ReadSingle());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Vector4"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteVector4(Vector4 value)
|
||||
{
|
||||
WriteSingle((float)value.X);
|
||||
WriteSingle((float)value.Y);
|
||||
WriteSingle((float)value.Z);
|
||||
WriteSingle((float)value.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Vector4"/> from the message.
|
||||
/// </summary>
|
||||
public Vector4 ReadVector4()
|
||||
{
|
||||
return new Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Quaternion"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteQuaternion(Quaternion value)
|
||||
{
|
||||
WriteSingle(value.X);
|
||||
WriteSingle(value.Y);
|
||||
WriteSingle(value.Z);
|
||||
WriteSingle(value.W);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Quaternion"/> from the message.
|
||||
/// </summary>
|
||||
public Quaternion ReadQuaternion()
|
||||
{
|
||||
return new Quaternion(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data of type <see cref="Boolean"/> into the message.
|
||||
/// </summary>
|
||||
public void WriteBoolean(bool value)
|
||||
{
|
||||
WriteBytes((byte*)&value, sizeof(bool));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads and returns data of type <see cref="Boolean"/> from the message.
|
||||
/// </summary>
|
||||
public bool ReadBoolean()
|
||||
{
|
||||
bool value = default;
|
||||
ReadBytes((byte*)&value, sizeof(bool));
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Source/Engine/Networking/NetworkStream.h
Normal file
62
Source/Engine/Networking/NetworkStream.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Serialization/ReadStream.h"
|
||||
#include "Engine/Serialization/WriteStream.h"
|
||||
|
||||
/// <summary>
|
||||
/// Objects and values serialization stream for sending data over network. Uses memory buffer for both read and write operations.
|
||||
/// </summary>
|
||||
API_CLASS(sealed, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkStream final : public ScriptingObject, public ReadStream, public WriteStream
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE(NetworkStream);
|
||||
private:
|
||||
byte* _buffer = nullptr;
|
||||
byte* _position = nullptr;
|
||||
uint32 _length = 0;
|
||||
bool _allocated = false;
|
||||
|
||||
public:
|
||||
~NetworkStream();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the stream for writing. Allocates the memory or reuses already existing memory. Resets the current stream position to beginning.
|
||||
/// </summary>
|
||||
API_FUNCTION() void Initialize(uint32 minCapacity);
|
||||
|
||||
/// <summary>
|
||||
/// Writes bytes to the stream
|
||||
/// </summary>
|
||||
/// <param name="data">Data to write</param>
|
||||
/// <param name="bytes">Amount of bytes to write</param>
|
||||
API_FUNCTION() FORCE_INLINE void WriteData(const void* data, int32 bytes)
|
||||
{
|
||||
WriteBytes(data, bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads bytes from the stream
|
||||
/// </summary>
|
||||
/// <param name="data">Data to write</param>
|
||||
/// <param name="bytes">Amount of bytes to write</param>
|
||||
API_FUNCTION() FORCE_INLINE void ReadData(void* data, int32 bytes)
|
||||
{
|
||||
ReadBytes(data, bytes);
|
||||
}
|
||||
|
||||
public:
|
||||
// [Stream]
|
||||
void Flush() override;
|
||||
void Close() override;
|
||||
uint32 GetLength() override;
|
||||
uint32 GetPosition() override;
|
||||
void SetPosition(uint32 seek) override;
|
||||
|
||||
// [ReadStream]
|
||||
void ReadBytes(void* data, uint32 bytes) override;
|
||||
|
||||
// [WriteStream]
|
||||
void WriteBytes(const void* data, uint32 bytes) override;
|
||||
};
|
||||
@@ -7,8 +7,10 @@ enum class NetworkEventType;
|
||||
enum class NetworkConnectionState;
|
||||
|
||||
class INetworkDriver;
|
||||
class INetworkSerializable;
|
||||
class NetworkPeer;
|
||||
class NetworkClient;
|
||||
class NetworkStream;
|
||||
|
||||
struct NetworkEvent;
|
||||
struct NetworkConnection;
|
||||
|
||||
Reference in New Issue
Block a user