Add network objects ownership changing with network sync
This commit is contained in:
@@ -12,6 +12,7 @@ enum class NetworkMessageIDs : uint8
|
|||||||
ObjectReplicate,
|
ObjectReplicate,
|
||||||
ObjectSpawn,
|
ObjectSpawn,
|
||||||
ObjectDespawn,
|
ObjectDespawn,
|
||||||
|
ObjectRole,
|
||||||
|
|
||||||
MAX,
|
MAX,
|
||||||
};
|
};
|
||||||
@@ -27,4 +28,5 @@ public:
|
|||||||
static void OnNetworkMessageObjectReplicate(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
static void OnNetworkMessageObjectReplicate(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
||||||
static void OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
static void OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
||||||
static void OnNetworkMessageObjectDespawn(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
static void OnNetworkMessageObjectDespawn(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
||||||
|
static void OnNetworkMessageObjectRole(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ namespace
|
|||||||
NetworkInternal::OnNetworkMessageObjectReplicate,
|
NetworkInternal::OnNetworkMessageObjectReplicate,
|
||||||
NetworkInternal::OnNetworkMessageObjectSpawn,
|
NetworkInternal::OnNetworkMessageObjectSpawn,
|
||||||
NetworkInternal::OnNetworkMessageObjectDespawn,
|
NetworkInternal::OnNetworkMessageObjectDespawn,
|
||||||
|
NetworkInternal::OnNetworkMessageObjectRole,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,13 @@ PACK_STRUCT(struct NetworkMessageObjectDespawn
|
|||||||
Guid ObjectId;
|
Guid ObjectId;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
PACK_STRUCT(struct NetworkMessageObjectRole
|
||||||
|
{
|
||||||
|
NetworkMessageIDs ID = NetworkMessageIDs::ObjectRole;
|
||||||
|
Guid ObjectId;
|
||||||
|
uint32 OwnerClientId;
|
||||||
|
});
|
||||||
|
|
||||||
struct NetworkReplicatedObject
|
struct NetworkReplicatedObject
|
||||||
{
|
{
|
||||||
ScriptingObjectReference<ScriptingObject> Object;
|
ScriptingObjectReference<ScriptingObject> Object;
|
||||||
@@ -180,6 +187,30 @@ NetworkReplicatedObject* ResolveObject(Guid objectId, Guid parentId, char object
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendObjectRoleMessage(const NetworkReplicatedObject& item, const NetworkClient* excludedClient = nullptr)
|
||||||
|
{
|
||||||
|
auto peer = NetworkManager::Peer;
|
||||||
|
NetworkMessageObjectRole msgData;
|
||||||
|
msgData.ObjectId = item.ObjectId;
|
||||||
|
msgData.OwnerClientId = item.OwnerClientId;
|
||||||
|
NetworkMessage msg = peer->BeginSendMessage();
|
||||||
|
msg.WriteStructure(msgData);
|
||||||
|
if (NetworkManager::IsClient())
|
||||||
|
{
|
||||||
|
NetworkManager::Peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CachedTargets.Clear();
|
||||||
|
for (const NetworkClient* client : NetworkManager::Clients)
|
||||||
|
{
|
||||||
|
if (client->State == NetworkConnectionState::Connected && client != excludedClient)
|
||||||
|
CachedTargets.Add(client->Connection);
|
||||||
|
}
|
||||||
|
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg, CachedTargets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if !COMPILE_WITHOUT_CSHARP
|
#if !COMPILE_WITHOUT_CSHARP
|
||||||
|
|
||||||
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
||||||
@@ -321,6 +352,45 @@ NetworkObjectRole NetworkReplicator::GetObjectRole(ScriptingObject* obj)
|
|||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerClientId, NetworkObjectRole localRole)
|
||||||
|
{
|
||||||
|
if (!obj)
|
||||||
|
return;
|
||||||
|
ScopeLock lock(ObjectsLock);
|
||||||
|
const auto it = Objects.Find(obj->GetID());
|
||||||
|
if (it == Objects.End())
|
||||||
|
return;
|
||||||
|
auto& item = it->Item;
|
||||||
|
if (item.Object != obj)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Check if this client is object owner
|
||||||
|
if (item.OwnerClientId == NetworkManager::LocalClientId)
|
||||||
|
{
|
||||||
|
// Check if object owner will change
|
||||||
|
if (item.OwnerClientId != ownerClientId)
|
||||||
|
{
|
||||||
|
// Change role locally
|
||||||
|
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
|
||||||
|
item.OwnerClientId = ownerClientId;
|
||||||
|
item.LastOwnerFrame = 1;
|
||||||
|
item.Role = localRole;
|
||||||
|
SendObjectRoleMessage(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Object is the owner
|
||||||
|
CHECK(localRole == NetworkObjectRole::OwnedAuthoritative);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Allow to change local role of the object (except ownership)
|
||||||
|
CHECK(localRole != NetworkObjectRole::OwnedAuthoritative);
|
||||||
|
item.Role = localRole;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client)
|
void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client)
|
||||||
{
|
{
|
||||||
ScopeLock lock(ObjectsLock);
|
ScopeLock lock(ObjectsLock);
|
||||||
@@ -559,10 +629,13 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo
|
|||||||
if (!obj)
|
if (!obj)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Reject replication from someone who is not an object owner
|
// Reject event from someone who is not an object owner
|
||||||
if (client && e->OwnerClientId != client->ClientId)
|
if (client && item.OwnerClientId != client->ClientId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Skip replication if we own the object (eg. late replication message after ownership change)
|
||||||
|
if (item.Role == NetworkObjectRole::OwnedAuthoritative)
|
||||||
return;
|
return;
|
||||||
ASSERT(e->Role != NetworkObjectRole::OwnedAuthoritative); // Ensure that we don't replicate object that we own
|
|
||||||
|
|
||||||
// Drop object replication if it has old data (eg. newer message was already processed due to unordered channel usage)
|
// Drop object replication if it has old data (eg. newer message was already processed due to unordered channel usage)
|
||||||
if (item.LastOwnerFrame >= msgData.OwnerFrame)
|
if (item.LastOwnerFrame >= msgData.OwnerFrame)
|
||||||
@@ -669,8 +742,8 @@ void NetworkInternal::OnNetworkMessageObjectDespawn(NetworkEvent& event, Network
|
|||||||
if (!obj || !item.Spawned)
|
if (!obj || !item.Spawned)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Reject despawn from someone who is not an object owner
|
// Reject event from someone who is not an object owner
|
||||||
if (client && e->OwnerClientId != client->ClientId)
|
if (client && item.OwnerClientId != client->ClientId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Remove object
|
// Remove object
|
||||||
@@ -684,3 +757,48 @@ void NetworkInternal::OnNetworkMessageObjectDespawn(NetworkEvent& event, Network
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkInternal::OnNetworkMessageObjectRole(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer)
|
||||||
|
{
|
||||||
|
NetworkMessageObjectRole msgData;
|
||||||
|
event.Message.ReadStructure(msgData);
|
||||||
|
ScopeLock lock(ObjectsLock);
|
||||||
|
NetworkReplicatedObject* e = ResolveObject(msgData.ObjectId);
|
||||||
|
if (e)
|
||||||
|
{
|
||||||
|
auto& item = *e;
|
||||||
|
ScriptingObject* obj = item.Object.Get();
|
||||||
|
if (!obj)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Reject event from someone who is not an object owner
|
||||||
|
if (client && item.OwnerClientId != client->ClientId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update
|
||||||
|
item.OwnerClientId = msgData.OwnerClientId;
|
||||||
|
item.LastOwnerFrame = 1;
|
||||||
|
if (item.OwnerClientId == NetworkManager::LocalClientId)
|
||||||
|
{
|
||||||
|
// Upgrade ownership automatically
|
||||||
|
item.Role = NetworkObjectRole::OwnedAuthoritative;
|
||||||
|
item.LastOwnerFrame = 0;
|
||||||
|
}
|
||||||
|
else if (item.Role == NetworkObjectRole::OwnedAuthoritative)
|
||||||
|
{
|
||||||
|
// Downgrade ownership automatically
|
||||||
|
item.Role = NetworkObjectRole::Replicated;
|
||||||
|
}
|
||||||
|
if (!NetworkManager::IsClient())
|
||||||
|
{
|
||||||
|
// Server has to broadcast ownership message to the other clients
|
||||||
|
SendObjectRoleMessage(item, client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if NETWORK_REPLICATOR_DEBUG_LOG
|
||||||
|
LOG(Error, "[NetworkReplicator] Unknown object role update {}", msgData.ObjectId);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -87,6 +87,14 @@ public:
|
|||||||
/// <returns>The object role.</returns>
|
/// <returns>The object role.</returns>
|
||||||
API_FUNCTION() static NetworkObjectRole GetObjectRole(ScriptingObject* obj);
|
API_FUNCTION() static NetworkObjectRole GetObjectRole(ScriptingObject* obj);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the network object ownership - owning client identifier and local role to use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The network object.</param>
|
||||||
|
/// <param name="ownerClientId">The new owner. Set to NetworkManager::LocalClientId for local client to be owner (server might reject it).</param>
|
||||||
|
/// <param name="localRole">The local role to assign for the object.</param>
|
||||||
|
API_FUNCTION() static void SetObjectOwnership(ScriptingObject* obj, uint32 ownerClientId, NetworkObjectRole localRole = NetworkObjectRole::Replicated);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if !COMPILE_WITHOUT_CSHARP
|
#if !COMPILE_WITHOUT_CSHARP
|
||||||
API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& type, const Function<void(void*, void*)>& serialize, const Function<void(void*, void*)>& deserialize);
|
API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& type, const Function<void(void*, void*)>& serialize, const Function<void(void*, void*)>& deserialize);
|
||||||
|
|||||||
Reference in New Issue
Block a user