Add **Network Replication Hierarchy** for robust control over replication in multiplayer games
This commit is contained in:
258
Source/Engine/Networking/NetworkReplicationHierarchy.h
Normal file
258
Source/Engine/Networking/NetworkReplicationHierarchy.h
Normal file
@@ -0,0 +1,258 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
#include "Engine/Core/Math/Vector3.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Engine/Scripting/ScriptingObjectReference.h"
|
||||
|
||||
class Actor;
|
||||
|
||||
/// <summary>
|
||||
/// Network replication hierarchy object data.
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault, Namespace = "FlaxEngine.Networking") struct FLAXENGINE_API NetworkReplicationHierarchyObject
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkReplicationObjectInfo);
|
||||
// The object to replicate.
|
||||
API_FIELD() ScriptingObject* Object;
|
||||
// The target amount of the replication updates per second (frequency of the replication). Constrained by NetworkManager::NetworkFPS.
|
||||
API_FIELD() float ReplicationFPS = 60;
|
||||
// The minimum distance from the player to the object at which it can process replication. For example, players further away won't receive object data.
|
||||
API_FIELD() float CullDistance = 15000;
|
||||
// Runtime value for update frames left for the next replication of this object. Matches NetworkManager::NetworkFPS calculated from ReplicationFPS.
|
||||
API_FIELD(Attributes="HideInEditor") uint16 ReplicationUpdatesLeft = 0;
|
||||
|
||||
FORCE_INLINE NetworkReplicationHierarchyObject(const ScriptingObjectReference<ScriptingObject>& obj)
|
||||
: Object(obj.Get())
|
||||
{
|
||||
}
|
||||
|
||||
FORCE_INLINE NetworkReplicationHierarchyObject(ScriptingObject* obj = nullptr)
|
||||
: Object(obj)
|
||||
{
|
||||
}
|
||||
|
||||
// Gets the actors context (object itself or parent actor).
|
||||
Actor* GetActor() const;
|
||||
|
||||
bool operator==(const NetworkReplicationHierarchyObject& other) const
|
||||
{
|
||||
return Object == other.Object;
|
||||
}
|
||||
|
||||
bool operator==(const ScriptingObject* other) const
|
||||
{
|
||||
return Object == other;
|
||||
}
|
||||
};
|
||||
|
||||
inline uint32 GetHash(const NetworkReplicationHierarchyObject& key)
|
||||
{
|
||||
return GetHash(key.Object);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bit mask for NetworkClient list (eg. to selectively send object replication).
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault, Namespace = "FlaxEngine.Networking") struct FLAXENGINE_API NetworkClientsMask
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkClientsMask);
|
||||
// The first 64 bits (each for one client).
|
||||
API_FIELD() uint64 Word0 = 0;
|
||||
// The second 64 bits (each for one client).
|
||||
API_FIELD() uint64 Word1 = 0;
|
||||
|
||||
// All bits set for all clients.
|
||||
API_FIELD() static NetworkClientsMask All;
|
||||
|
||||
FORCE_INLINE bool HasBit(int32 bitIndex) const
|
||||
{
|
||||
const int32 wordIndex = bitIndex / 64;
|
||||
const uint64 wordMask = 1ull << (uint64)(bitIndex - wordIndex * 64);
|
||||
const uint64 word = *(&Word0 + wordIndex);
|
||||
return (word & wordMask) == wordMask;
|
||||
}
|
||||
|
||||
FORCE_INLINE void SetBit(int32 bitIndex)
|
||||
{
|
||||
const int32 wordIndex = bitIndex / 64;
|
||||
const uint64 wordMask = 1ull << (uint64)(bitIndex - wordIndex * 64);
|
||||
uint64& word = *(&Word0 + wordIndex);
|
||||
word |= wordMask;
|
||||
}
|
||||
|
||||
FORCE_INLINE void UnsetBit(int32 bitIndex)
|
||||
{
|
||||
const int32 wordIndex = bitIndex / 64;
|
||||
const uint64 wordMask = 1ull << (uint64)(bitIndex - wordIndex * 64);
|
||||
uint64& word = *(&Word0 + wordIndex);
|
||||
word &= ~wordMask;
|
||||
}
|
||||
|
||||
FORCE_INLINE operator bool() const
|
||||
{
|
||||
return Word0 + Word1 != 0;
|
||||
}
|
||||
|
||||
bool operator==(const NetworkClientsMask& other) const
|
||||
{
|
||||
return Word0 == other.Word0 && Word1 == other.Word1;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Network replication hierarchy output data to send.
|
||||
/// </summary>
|
||||
API_CLASS(Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkReplicationHierarchyUpdateResult : public ScriptingObject
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(NetworkReplicationHierarchyUpdateResult, ScriptingObject);
|
||||
friend class NetworkInternal;
|
||||
friend class NetworkReplicationNode;
|
||||
friend class NetworkReplicationGridNode;
|
||||
|
||||
private:
|
||||
struct Client
|
||||
{
|
||||
bool HasLocation;
|
||||
Vector3 Location;
|
||||
};
|
||||
|
||||
struct Entry
|
||||
{
|
||||
ScriptingObject* Object;
|
||||
NetworkClientsMask TargetClients;
|
||||
};
|
||||
|
||||
bool _clientsHaveLocation;
|
||||
NetworkClientsMask _clientsMask;
|
||||
Array<Client> _clients;
|
||||
Array<Entry> _entries;
|
||||
|
||||
void Init();
|
||||
|
||||
public:
|
||||
// Scales the ReplicationFPS property of objects in hierarchy. Can be used to slow down or speed up replication rate.
|
||||
API_FIELD() float ReplicationScale = 1.0f;
|
||||
|
||||
// Adds object to the update results.
|
||||
API_FUNCTION() void AddObject(ScriptingObject* obj)
|
||||
{
|
||||
Entry& e = _entries.AddOne();
|
||||
e.Object = obj;
|
||||
e.TargetClients = NetworkClientsMask::All;
|
||||
}
|
||||
|
||||
// Adds object to the update results. Defines specific clients to receive the update (server-only, unused on client). Mask matches NetworkManager::Clients.
|
||||
API_FUNCTION() void AddObject(ScriptingObject* obj, NetworkClientsMask targetClients)
|
||||
{
|
||||
Entry& e = _entries.AddOne();
|
||||
e.Object = obj;
|
||||
e.TargetClients = targetClients;
|
||||
}
|
||||
|
||||
// Gets amount of the clients to use. Matches NetworkManager::Clients.
|
||||
API_PROPERTY() int32 GetClientsCount() const
|
||||
{
|
||||
return _clients.Count();
|
||||
}
|
||||
|
||||
// Gets mask with all client bits set. Matches NetworkManager::Clients.
|
||||
API_PROPERTY() NetworkClientsMask GetClientsMask() const
|
||||
{
|
||||
return _clientsMask;
|
||||
}
|
||||
|
||||
// Sets the viewer location for a certain client. Client index must match NetworkManager::Clients.
|
||||
API_FUNCTION() void SetClientLocation(int32 clientIndex, const Vector3& location);
|
||||
|
||||
// Gets the viewer location for a certain client. Client index must match NetworkManager::Clients. Returns true if got a location set, otherwise false.
|
||||
API_FUNCTION() bool GetClientLocation(int32 clientIndex, API_PARAM(out) Vector3& location) const;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Base class for the network objects replication hierarchy nodes. Contains a list of objects.
|
||||
/// </summary>
|
||||
API_CLASS(Abstract, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkReplicationNode : public ScriptingObject
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(NetworkReplicationNode, ScriptingObject);
|
||||
|
||||
/// <summary>
|
||||
/// List with objects stored in this node.
|
||||
/// </summary>
|
||||
API_FIELD() Array<NetworkReplicationHierarchyObject> Objects;
|
||||
|
||||
/// <summary>
|
||||
/// Adds an object into the hierarchy.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to add.</param>
|
||||
API_FUNCTION() virtual void AddObject(NetworkReplicationHierarchyObject obj);
|
||||
|
||||
/// <summary>
|
||||
/// Removes object from the hierarchy.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to remove.</param>
|
||||
/// <returns>True on successful removal, otherwise false.</returns>
|
||||
API_FUNCTION() virtual bool RemoveObject(ScriptingObject* obj);
|
||||
|
||||
/// <summary>
|
||||
/// Force replicates the object during the next update. Resets any internal tracking state to force the synchronization.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to update.</param>
|
||||
/// <returns>True on successful update, otherwise false.</returns>
|
||||
API_FUNCTION() virtual bool DirtyObject(ScriptingObject* obj);
|
||||
|
||||
/// <summary>
|
||||
/// Iterates over all objects and adds them to the replication work.
|
||||
/// </summary>
|
||||
/// <param name="result">The update results container.</param>
|
||||
API_FUNCTION() virtual void Update(NetworkReplicationHierarchyUpdateResult* result);
|
||||
};
|
||||
|
||||
inline uint32 GetHash(const Int3& key)
|
||||
{
|
||||
uint32 hash = GetHash(key.X);
|
||||
CombineHash(hash, GetHash(key.Y));
|
||||
CombineHash(hash, GetHash(key.Z));
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Network replication hierarchy node with 3D grid spatialization. Organizes static objects into chunks to improve performance in large worlds.
|
||||
/// </summary>
|
||||
API_CLASS(Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkReplicationGridNode : public NetworkReplicationNode
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(NetworkReplicationGridNode, NetworkReplicationNode);
|
||||
~NetworkReplicationGridNode();
|
||||
|
||||
private:
|
||||
struct Cell
|
||||
{
|
||||
NetworkReplicationNode* Node;
|
||||
float MinCullDistance;
|
||||
};
|
||||
|
||||
Dictionary<Int3, Cell> _children;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Size of the grid cell (in world units). Used to chunk the space for separate nodes.
|
||||
/// </summary>
|
||||
API_FIELD() float CellSize = 10000.0f;
|
||||
|
||||
void AddObject(NetworkReplicationHierarchyObject obj) override;
|
||||
bool RemoveObject(ScriptingObject* obj) override;
|
||||
void Update(NetworkReplicationHierarchyUpdateResult* result) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Defines the network objects replication hierarchy (tree structure) that controls chunking and configuration of the game objects replication.
|
||||
/// Contains only 'owned' objects. It's used by the networking system only on a main thread.
|
||||
/// </summary>
|
||||
API_CLASS(Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkReplicationHierarchy : public NetworkReplicationNode
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(NetworkReplicationHierarchy, NetworkReplicationNode);
|
||||
};
|
||||
Reference in New Issue
Block a user