// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #pragma once #include "Types.h" #include "Engine/Core/Types/Span.h" #include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/ScriptingType.h" template class DataContainer; /// /// The high-level network object role and authority. Used to define who owns the object and when it can be simulated or just replicated. /// API_ENUM(Namespace="FlaxEngine.Networking") enum class NetworkObjectRole : byte { // Not replicated object. None = 0, // Server/client owns the object and replicates it to others. Only owning client can simulate object and provides current state. OwnedAuthoritative, // Server/client gets replicated object from other server/client who owns it. Object cannot be simulated locally (any changes will be overriden by replication). Replicated, // Client gets replicated object from server but still can locally autonomously simulate it too. For example, client can control local pawn with real human input but will validate with server proper state (eg. to prevent cheats). ReplicatedSimulated, }; /// /// High-level networking replication system for game objects. /// API_CLASS(static, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkReplicator { DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkReplicator); friend class NetworkReplicatorInternal; typedef void (*SerializeFunc)(void* instance, NetworkStream* stream, void* tag); public: #if !BUILD_RELEASE /// /// Enables verbose logging of the networking runtime. Can be used to debug problems of missing RPC invoke or object replication issues. /// API_FIELD() static bool EnableLog; #endif /// /// Gets the network replication hierarchy. /// API_PROPERTY() static NetworkReplicationHierarchy* GetHierarchy(); /// /// Sets the network replication hierarchy. /// API_PROPERTY() static void SetHierarchy(NetworkReplicationHierarchy* value); public: /// /// Adds the network replication serializer for a given type. /// /// The scripting type to serialize. /// Serialization callback method. /// Deserialization callback method. /// Serialization callback method tag value. /// Deserialization callback method tag value. static void AddSerializer(const ScriptingTypeHandle& typeHandle, SerializeFunc serialize, SerializeFunc deserialize, void* serializeTag = nullptr, void* deserializeTag = nullptr); /// /// Invokes the network replication serializer for a given type. /// /// The scripting type to serialize. /// The value instance to serialize. /// The input/output stream to use for serialization. /// True if serialize, otherwise deserialize mode. /// True if failed, otherwise false. API_FUNCTION(NoProxy) static bool InvokeSerializer(const ScriptingTypeHandle& typeHandle, void* instance, NetworkStream* stream, bool serialize); /// /// Adds the object to the network replication system. /// /// Does nothing if network is offline. /// The object to replicate. /// The parent of the object (eg. player that spawned it). API_FUNCTION() static void AddObject(ScriptingObject* obj, const ScriptingObject* parent = nullptr); /// /// Removes the object from the network replication system. /// /// Does nothing if network is offline. /// The object to don't replicate. API_FUNCTION() static void RemoveObject(ScriptingObject* obj); /// /// Spawns the object to the other clients. Can be spawned by the owner who locally created it (eg. from prefab). /// /// Does nothing if network is offline. Doesn't spawn actor in a level - but in network replication system. /// The object to spawn on other clients. API_FUNCTION() static void SpawnObject(ScriptingObject* obj); /// /// Spawns the object to the other clients. Can be spawned by the owner who locally created it (eg. from prefab). /// /// Does nothing if network is offline. Doesn't spawn actor in a level - but in network replication system. /// The object to spawn on other clients. /// List with network client IDs that should receive network spawn event. Empty to spawn on all clients. API_FUNCTION() static void SpawnObject(ScriptingObject* obj, const DataContainer& clientIds); /// /// Despawns the object from the other clients. Deletes object from remove clients. /// /// Does nothing if network is offline. /// The object to despawn on other clients. API_FUNCTION() static void DespawnObject(ScriptingObject* obj); /// /// Checks if the network object is spawned or added to the network replication system. /// /// The network object. /// True if object exists in networking, otherwise false. API_FUNCTION() static bool HasObject(const ScriptingObject* obj); /// /// Maps object ID into server or client ID (depending on the source ID). Leaves source value unchanged if that specific ID is unused. /// /// The network object identifier to map. Contains result ID once the method completes. API_FUNCTION() static void MapObjectId(API_PARAM(Ref) Guid& objectId); /// /// Resolves foreign Guid into a local ScriptingObject /// /// The Guid of a foreign object. /// Object if managed to resolve, otherwise null. API_FUNCTION() static ScriptingObject* ResolveForeignObject(Guid objectId); /// /// Gets the Client Id of the network object owner. /// /// The network object. /// The Client Id. API_FUNCTION() static uint32 GetObjectOwnerClientId(const ScriptingObject* obj); /// /// Gets the role of the network object used locally (eg. to check if can simulate object). /// /// The network object. /// The object role. API_FUNCTION() static NetworkObjectRole GetObjectRole(const ScriptingObject* obj); /// /// Checks if the network object is owned locally (thus current client has authority to manage it). /// /// Equivalent to GetObjectRole == OwnedAuthoritative. /// The network object. /// True if object is owned by this client, otherwise false. API_FUNCTION() FORCE_INLINE static bool IsObjectOwned(const ScriptingObject* obj) { return GetObjectRole(obj) == NetworkObjectRole::OwnedAuthoritative; } /// /// Checks if the network object is simulated locally (thus current client has can modify it - changed might be overriden by other client who owns this object). /// /// Equivalent to GetObjectRole != Replicated. /// The network object. /// True if object is simulated on this client, otherwise false. API_FUNCTION() FORCE_INLINE static bool IsObjectSimulated(const ScriptingObject* obj) { return GetObjectRole(obj) != NetworkObjectRole::Replicated; } /// /// Checks if the network object is replicated locally (any local changes might be overriden by other client who owns this object). /// /// Equivalent to (GetObjectRole == Replicated or GetObjectRole == ReplicatedAutonomous). /// The network object. /// True if object is simulated on this client, otherwise false. API_FUNCTION() FORCE_INLINE static bool IsObjectReplicated(const ScriptingObject* obj) { const NetworkObjectRole role = GetObjectRole(obj); return role == NetworkObjectRole::Replicated || role == NetworkObjectRole::ReplicatedSimulated; } /// /// Sets the network object ownership - owning client identifier and local role to use. /// /// The network object. /// The new owner. Set to NetworkManager::LocalClientId for local client to be owner (server might reject it). /// The local role to assign for the object. /// True if apply the ownership to all child objects of this object (eg. all child actors and scripts attached to the networked actor). API_FUNCTION() static void SetObjectOwnership(ScriptingObject* obj, uint32 ownerClientId, NetworkObjectRole localRole = NetworkObjectRole::Replicated, bool hierarchical = true); /// /// Marks the object dirty to perform immediate replication to the other clients. /// /// The network object. API_FUNCTION() static void DirtyObject(ScriptingObject* obj); public: /// /// Begins invoking the RPC and returns the Network Stream to serialize parameters to. /// /// Network Stream to write RPC parameters to. API_FUNCTION() static NetworkStream* BeginInvokeRPC(); /// /// Ends invoking the RPC. /// /// The target object to invoke RPC. /// The RPC type. /// The RPC name. /// The RPC serialized arguments stream returned from BeginInvokeRPC. /// Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs. /// True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids). static bool EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span targetIds = Span()); private: #if !COMPILE_WITHOUT_CSHARP API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& typeHandle, const Function& serialize, const Function& deserialize); API_FUNCTION(NoProxy) static void AddRPC(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, const Function& execute, bool isServer, bool isClient, NetworkChannelType channel); API_FUNCTION(NoProxy) static bool CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds); static StringAnsiView GetCSharpCachedName(const StringAnsiView& name); #endif };