Changes to get client migration partially functional
Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
@@ -32,7 +32,7 @@ namespace Multiplayer
|
||||
using EntityStopEvent = AZ::Event<const ConstNetworkEntityHandle&>;
|
||||
using EntityDirtiedEvent = AZ::Event<>;
|
||||
using EntitySyncRewindEvent = AZ::Event<>;
|
||||
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&, AzNetworking::ConnectionId>;
|
||||
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&, const HostId&, AzNetworking::ConnectionId>;
|
||||
using EntityPreRenderEvent = AZ::Event<float>;
|
||||
using EntityCorrectionEvent = AZ::Event<>;
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Multiplayer
|
||||
void MarkDirty();
|
||||
void NotifyLocalChanges();
|
||||
void NotifySyncRewindState();
|
||||
void NotifyServerMigration(const HostId& hostId, AzNetworking::ConnectionId connectionId);
|
||||
void NotifyServerMigration(const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId);
|
||||
void NotifyPreRender(float deltaTime);
|
||||
void NotifyCorrection();
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Multiplayer
|
||||
using ClientMigrationStartEvent = AZ::Event<ClientInputId>;
|
||||
using ClientMigrationEndEvent = AZ::Event<>;
|
||||
using ClientDisconnectedEvent = AZ::Event<>;
|
||||
using NotifyClientMigrationEvent = AZ::Event<const HostId&, uint64_t, ClientInputId>;
|
||||
using NotifyClientMigrationEvent = AZ::Event<const HostId&, uint64_t, ClientInputId, NetEntityId>;
|
||||
using NotifyEntityMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&>;
|
||||
using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>;
|
||||
using SessionInitEvent = AZ::Event<AzNetworking::INetworkInterface*>;
|
||||
@@ -131,10 +131,11 @@ namespace Multiplayer
|
||||
virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0;
|
||||
|
||||
//! Signals a NotifyClientMigrationEvent with the provided parameters.
|
||||
//! @param hostId the host id of the host the client is migrating to
|
||||
//! @param userIdentifier the user identifier the client will provide the new host to validate identity
|
||||
//! @param lastClientInputId the last processed clientInputId by the current host
|
||||
virtual void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) = 0;
|
||||
//! @param hostId the host id of the host the client is migrating to
|
||||
//! @param userIdentifier the user identifier the client will provide the new host to validate identity
|
||||
//! @param lastClientInputId the last processed clientInputId by the current host
|
||||
//! @param controlledEntityId the entityId of the clients autonomous entity
|
||||
virtual void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) = 0;
|
||||
|
||||
//! Signals a NotifyEntityMigrationEvent with the provided parameters.
|
||||
//! @param entityHandle the network entity handle of the entity being migrated
|
||||
@@ -176,6 +177,11 @@ namespace Multiplayer
|
||||
//! @return pointer to the filtered entity manager, or nullptr if not set
|
||||
virtual IFilterEntityManager* GetFilterEntityManager() = 0;
|
||||
|
||||
//! Registers a temp userId to allow a host to look up a players controlled entity in the event of a rejoin or migration event.
|
||||
//! @param temporaryUserIdentifier the temporary user identifier used to identify a player across hosts
|
||||
//! @param controlledEntityId the controlled entityId of the players autonomous entity
|
||||
virtual void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) = 0;
|
||||
|
||||
//! Enables or disables automatic instantiation of netbound entities.
|
||||
//! This setting is controlled by the networking layer and should not be touched
|
||||
//! If enabled, netbound entities will instantiate as spawnables are loaded into the game world, generally true for the server
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace Multiplayer
|
||||
Server, // A simulated proxy on a server
|
||||
Authority // An authoritative proxy on a server (full authority)
|
||||
};
|
||||
const char* GetEnumString(NetEntityRole value);
|
||||
|
||||
enum class ComponentSerializationType : uint8_t
|
||||
{
|
||||
@@ -113,6 +114,24 @@ namespace Multiplayer
|
||||
bool Serialize(AzNetworking::ISerializer& serializer);
|
||||
};
|
||||
|
||||
inline const char* GetEnumString(NetEntityRole value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case NetEntityRole::InvalidRole:
|
||||
return "InvalidRole";
|
||||
case NetEntityRole::Client:
|
||||
return "Client";
|
||||
case NetEntityRole::Autonomous:
|
||||
return "Autonomous";
|
||||
case NetEntityRole::Server:
|
||||
return "Server";
|
||||
case NetEntityRole::Authority:
|
||||
return "Authority";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
inline PrefabEntityId::PrefabEntityId(AZ::Name name, uint32_t entityOffset)
|
||||
: m_prefabName(name)
|
||||
, m_entityOffset(entityOffset)
|
||||
|
||||
+5
@@ -57,6 +57,10 @@ namespace Multiplayer
|
||||
EntityReplicationManager(AzNetworking::IConnection& connection, AzNetworking::IConnectionListener& connectionListener, Mode mode);
|
||||
~EntityReplicationManager() = default;
|
||||
|
||||
//! Used to override during client migration if your host has a specially assigned publically routable address.
|
||||
//! @param remoteHostId the publically routable address to use in place of the remote HostId
|
||||
void SetMigrateHostId(const HostId& remoteHostId);
|
||||
const HostId& GetMigrateHostId() const;
|
||||
const HostId& GetRemoteHostId() const;
|
||||
|
||||
void ActivatePendingEntities();
|
||||
@@ -207,6 +211,7 @@ namespace Multiplayer
|
||||
AZ::TimeMs m_entityPendingRemovalMs = AZ::TimeMs{ 0 };
|
||||
AZ::TimeMs m_frameTimeMs = AZ::TimeMs{ 0 };
|
||||
HostId m_remoteHostId = InvalidHostId;
|
||||
HostId m_migrateHostId = InvalidHostId;
|
||||
uint32_t m_maxRemoteEntitiesPendingCreationCount = AZStd::numeric_limits<uint32_t>::max();
|
||||
uint32_t m_maxPayloadSize = 0;
|
||||
Mode m_updateMode = Mode::Invalid;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
<Packet Name="Connect" HandshakePacket="true" Desc="Client connection packet, on success the server will reply with an Accept">
|
||||
<Member Type="uint16_t" Name="networkProtocolVersion" Init="0" />
|
||||
<Member Type="uint64_t" Name="temporaryUserId" Init="0" />
|
||||
<Member Type="Multiplayer::LongNetworkString" Name="ticket" />
|
||||
</Packet>
|
||||
|
||||
|
||||
@@ -394,9 +394,9 @@ namespace Multiplayer
|
||||
m_syncRewindEvent.Signal();
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyServerMigration(const HostId& hostId, AzNetworking::ConnectionId connectionId)
|
||||
void NetBindComponent::NotifyServerMigration(const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId)
|
||||
{
|
||||
m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId);
|
||||
m_entityServerMigrationEvent.Signal(m_netEntityHandle, remoteHostId, migrateHostId, connectionId);
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyPreRender(float deltaTime)
|
||||
|
||||
@@ -23,22 +23,16 @@ namespace Multiplayer
|
||||
ServerToClientConnectionData::ServerToClientConnectionData
|
||||
(
|
||||
AzNetworking::IConnection* connection,
|
||||
AzNetworking::IConnectionListener& connectionListener,
|
||||
NetworkEntityHandle controlledEntity
|
||||
AzNetworking::IConnectionListener& connectionListener
|
||||
)
|
||||
: m_connection(connection)
|
||||
, m_controlledEntityRemovedHandler([this](const ConstNetworkEntityHandle&) { OnControlledEntityRemove(); })
|
||||
, m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId) { OnControlledEntityMigration(entityHandle, remoteHostId, connectionId); })
|
||||
, m_controlledEntity(controlledEntity)
|
||||
, m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId)
|
||||
{
|
||||
OnControlledEntityMigration(entityHandle, remoteHostId, migrateHostId, connectionId);
|
||||
})
|
||||
, m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalServerToRemoteClient)
|
||||
{
|
||||
NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent();
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
netBindComponent->AddEntityStopEventHandler(m_controlledEntityRemovedHandler);
|
||||
netBindComponent->AddEntityServerMigrationEventHandler(m_controlledEntityMigrationHandler);
|
||||
}
|
||||
|
||||
m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(sv_ClientMaxRemoteEntitiesPendingCreationCount);
|
||||
m_entityReplicationManager.SetEntityPendingRemovalMs(sv_ClientEntityReplicatorPendingRemovalTimeMs);
|
||||
}
|
||||
@@ -54,6 +48,20 @@ namespace Multiplayer
|
||||
m_controlledEntityRemovedHandler.Disconnect();
|
||||
}
|
||||
|
||||
void ServerToClientConnectionData::SetControlledEntity(NetworkEntityHandle primaryPlayerEntity)
|
||||
{
|
||||
m_controlledEntityRemovedHandler.Disconnect();
|
||||
m_controlledEntityMigrationHandler.Disconnect();
|
||||
|
||||
m_controlledEntity = primaryPlayerEntity;
|
||||
NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent();
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
netBindComponent->AddEntityStopEventHandler(m_controlledEntityRemovedHandler);
|
||||
netBindComponent->AddEntityServerMigrationEventHandler(m_controlledEntityMigrationHandler);
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionDataType ServerToClientConnectionData::GetConnectionDataType() const
|
||||
{
|
||||
return ConnectionDataType::ServerToClient;
|
||||
@@ -94,7 +102,8 @@ namespace Multiplayer
|
||||
void ServerToClientConnectionData::OnControlledEntityMigration
|
||||
(
|
||||
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
|
||||
[[maybe_unused]] const HostId& remoteHostId,
|
||||
const HostId& remoteHostId,
|
||||
const HostId& migrateHostId,
|
||||
[[maybe_unused]] AzNetworking::ConnectionId connectionId
|
||||
)
|
||||
{
|
||||
@@ -109,13 +118,13 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
// Generate crypto-rand user identifier, send to both server and client so they can negotiate the autonomous entity to assume predictive control over after migration
|
||||
const uint64_t randomUserIdentifier = AzNetworking::CryptoRand64();
|
||||
const uint64_t temporaryUserIdentifier = AzNetworking::CryptoRand64();
|
||||
|
||||
// Tell the new host that a client is about to (re)join
|
||||
GetMultiplayer()->SendNotifyClientMigrationEvent(remoteHostId, randomUserIdentifier, migratedClientInputId);
|
||||
GetMultiplayer()->SendNotifyClientMigrationEvent(remoteHostId, temporaryUserIdentifier, migratedClientInputId, m_controlledEntity.GetNetEntityId());
|
||||
|
||||
// Tell the client who to join
|
||||
MultiplayerPackets::ClientMigration clientMigration(remoteHostId, randomUserIdentifier, migratedClientInputId);
|
||||
MultiplayerPackets::ClientMigration clientMigration(migrateHostId, temporaryUserIdentifier, migratedClientInputId);
|
||||
GetConnection()->SendReliablePacket(clientMigration);
|
||||
|
||||
m_controlledEntity = NetworkEntityHandle();
|
||||
|
||||
@@ -20,11 +20,12 @@ namespace Multiplayer
|
||||
ServerToClientConnectionData
|
||||
(
|
||||
AzNetworking::IConnection* connection,
|
||||
AzNetworking::IConnectionListener& connectionListener,
|
||||
NetworkEntityHandle controlledEntity
|
||||
AzNetworking::IConnectionListener& connectionListener
|
||||
);
|
||||
~ServerToClientConnectionData() override;
|
||||
|
||||
void SetControlledEntity(NetworkEntityHandle primaryPlayerEntity);
|
||||
|
||||
//! IConnectionData interface
|
||||
//! @{
|
||||
ConnectionDataType GetConnectionDataType() const override;
|
||||
@@ -42,7 +43,7 @@ namespace Multiplayer
|
||||
|
||||
private:
|
||||
void OnControlledEntityRemove();
|
||||
void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId);
|
||||
void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, const HostId& migrateHostId, AzNetworking::ConnectionId connectionId);
|
||||
void OnGameplayStarted();
|
||||
|
||||
EntityReplicationManager m_entityReplicationManager;
|
||||
|
||||
@@ -76,6 +76,7 @@ namespace Multiplayer
|
||||
"The address of the remote server or host to connect to");
|
||||
AZ_CVAR(uint16_t, cl_serverport, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port of the remote host to connect to for game traffic");
|
||||
AZ_CVAR(uint16_t, sv_port, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that this multiplayer gem will bind to for game traffic");
|
||||
AZ_CVAR(uint16_t, sv_portRange, 999, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The range of ports the host will incrementally attempt to bind to when initializing");
|
||||
AZ_CVAR(AZ::CVarFixedString, sv_map, "nolevel", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The map the server should load");
|
||||
AZ_CVAR(ProtocolType, sv_protocol, ProtocolType::Udp, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "This flag controls whether we use TCP or UDP for game networking");
|
||||
AZ_CVAR(bool, sv_isDedicated, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether the host command creates an independent or client hosted server");
|
||||
@@ -168,6 +169,7 @@ namespace Multiplayer
|
||||
AZ::ConsoleFunctorFlags flags,
|
||||
AZ::ConsoleInvokedFrom invokedFrom
|
||||
) { OnConsoleCommandInvoked(command, args, flags, invokedFrom); })
|
||||
, m_autonomousEntityReplicatorCreatedHandler([this]([[maybe_unused]] NetEntityId netEntityId) { OnAutonomousEntityReplicatorCreated(); })
|
||||
{
|
||||
AZ::Interface<IMultiplayer>::Register(this);
|
||||
}
|
||||
@@ -205,8 +207,23 @@ namespace Multiplayer
|
||||
|
||||
bool MultiplayerSystemComponent::StartHosting(uint16_t port, bool isDedicated)
|
||||
{
|
||||
if (port != sv_port)
|
||||
{
|
||||
sv_port = port;
|
||||
}
|
||||
|
||||
InitializeMultiplayer(isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer);
|
||||
return m_networkInterface->Listen(port);
|
||||
const uint16_t maxPort = sv_port + sv_portRange;
|
||||
while (sv_port <= maxPort)
|
||||
{
|
||||
if (m_networkInterface->Listen(sv_port))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
AZLOG_WARN("Failed to start listening on port %u, port is in use?", static_cast<uint32_t>(sv_port));
|
||||
sv_port = sv_port + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MultiplayerSystemComponent::Connect(const AZStd::string& remoteAddress, uint16_t port)
|
||||
@@ -310,6 +327,11 @@ namespace Multiplayer
|
||||
|
||||
void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
if (bg_multiplayerDebugDraw)
|
||||
{
|
||||
m_networkEntityManager.DebugDraw();
|
||||
}
|
||||
|
||||
const AZ::TimeMs deltaTimeMs = aznumeric_cast<AZ::TimeMs>(static_cast<int32_t>(deltaTime * 1000.0f));
|
||||
const AZ::TimeMs serverRateMs = static_cast<AZ::TimeMs>(sv_serverSendRateMs);
|
||||
const float serverRateSeconds = static_cast<float>(serverRateMs) / 1000.0f;
|
||||
@@ -394,11 +416,6 @@ namespace Multiplayer
|
||||
{
|
||||
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
|
||||
}
|
||||
|
||||
if (bg_multiplayerDebugDraw)
|
||||
{
|
||||
m_networkEntityManager.DebugDraw();
|
||||
}
|
||||
}
|
||||
|
||||
int MultiplayerSystemComponent::GetTickOrder()
|
||||
@@ -469,17 +486,41 @@ namespace Multiplayer
|
||||
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); };
|
||||
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str());
|
||||
|
||||
// Hosts will spawn a new default player prefab for the user that just connected
|
||||
if (GetAgentType() == MultiplayerAgentType::ClientServer
|
||||
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
|
||||
{
|
||||
// We use a temporary userId so we can maintain client lookups even in the event of wifi handoff
|
||||
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(packet.GetTemporaryUserId());
|
||||
if (controlledEntity.Exists())
|
||||
{
|
||||
controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId());
|
||||
}
|
||||
// Activate the entity if necessary
|
||||
if (controlledEntity.GetEntity()->GetState() == AZ::Entity::State::Init)
|
||||
{
|
||||
controlledEntity.Activate();
|
||||
}
|
||||
|
||||
ServerToClientConnectionData* connectionData = reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData());
|
||||
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
|
||||
connectionData->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
|
||||
connectionData->SetControlledEntity(controlledEntity);
|
||||
}
|
||||
|
||||
if (connection->SendReliablePacket(MultiplayerPackets::Accept(sv_map)))
|
||||
{
|
||||
m_didHandshake = true;
|
||||
|
||||
// Sync our console
|
||||
ConsoleReplicator consoleReplicator(connection);
|
||||
AZ::Interface<AZ::IConsole>::Get()->VisitRegisteredFunctors([&consoleReplicator](AZ::ConsoleFunctorBase* functor) { consoleReplicator.Visit(functor); });
|
||||
if (packet.GetTemporaryUserId() == 0)
|
||||
{
|
||||
// Sync our console
|
||||
ConsoleReplicator consoleReplicator(connection);
|
||||
AZ::Interface<AZ::IConsole>::Get()->VisitRegisteredFunctors([&consoleReplicator](AZ::ConsoleFunctorBase* functor) { consoleReplicator.Visit(functor); });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -493,10 +534,24 @@ namespace Multiplayer
|
||||
)
|
||||
{
|
||||
m_didHandshake = true;
|
||||
AZ::CVarFixedString commandString = "sv_map " + packet.GetMap();
|
||||
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(commandString.c_str());
|
||||
AZ::CVarFixedString loadLevelString = "LoadLevel " + packet.GetMap();
|
||||
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(loadLevelString.c_str());
|
||||
if (m_temporaryUserIdentifier == 0)
|
||||
{
|
||||
AZ::CVarFixedString commandString = "sv_map " + packet.GetMap();
|
||||
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(commandString.c_str());
|
||||
AZ::CVarFixedString loadLevelString = "LoadLevel " + packet.GetMap();
|
||||
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(loadLevelString.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
OnAutonomousEntityReplicatorCreated();
|
||||
// IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection->GetUserData());
|
||||
// if (connectionData)
|
||||
// {
|
||||
// // @nt: TODO - delete once dropped RPC problem fixed
|
||||
// // Connection has migrated, we are now waiting for the autonomous entity replicator to be created
|
||||
// connectionData->GetReplicationManager().AddAutonomousEntityReplicatorCreatedHandler(m_autonomousEntityReplicatorCreatedHandler);
|
||||
// }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -617,13 +672,17 @@ namespace Multiplayer
|
||||
|
||||
// Store the temporary user identifier so we can transmit it with our next Connect packet
|
||||
// The new server will use this to re-attach our set of autonomous entities
|
||||
m_temporaryUserIdentifier = packet.GetTemporaryUserIdentifier();
|
||||
|
||||
// Disconnect our existing server connection
|
||||
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::ClientMigrated, TerminationEndpoint::Local); };
|
||||
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
|
||||
AZLOG_INFO("Migrating to new server shard");
|
||||
m_clientMigrationStartEvent.Signal(packet.GetLastClientInputId());
|
||||
m_networkInterface->Connect(packet.GetRemoteServerAddress());
|
||||
if (m_networkInterface->Connect(packet.GetRemoteServerAddress()) == AzNetworking::InvalidConnectionId)
|
||||
{
|
||||
AZLOG_ERROR("Failed to connect to new host during client migration event");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -653,7 +712,7 @@ namespace Multiplayer
|
||||
providerTicket = m_pendingConnectionTickets.front();
|
||||
m_pendingConnectionTickets.pop();
|
||||
}
|
||||
connection->SendReliablePacket(MultiplayerPackets::Connect(0, providerTicket.c_str()));
|
||||
connection->SendReliablePacket(MultiplayerPackets::Connect(0, m_temporaryUserIdentifier, providerTicket.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -661,20 +720,10 @@ namespace Multiplayer
|
||||
m_connectionAcquiredEvent.Signal(datum);
|
||||
}
|
||||
|
||||
// Hosts will spawn a new default player prefab for the user that just connected
|
||||
if (GetAgentType() == MultiplayerAgentType::ClientServer
|
||||
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
|
||||
{
|
||||
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab();
|
||||
if (controlledEntity.Exists())
|
||||
{
|
||||
controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId());
|
||||
}
|
||||
controlledEntity.Activate();
|
||||
|
||||
connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity));
|
||||
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
|
||||
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
|
||||
connection->SetUserData(new ServerToClientConnectionData(connection, *this));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -696,9 +745,9 @@ namespace Multiplayer
|
||||
|
||||
void MultiplayerSystemComponent::OnDisconnect(AzNetworking::IConnection* connection, DisconnectReason reason, TerminationEndpoint endpoint)
|
||||
{
|
||||
const char* endpointString = (endpoint == TerminationEndpoint::Local) ? "Disconnecting" : "Remote host disconnected";
|
||||
const char* endpointString = (endpoint == TerminationEndpoint::Local) ? "Disconnecting" : "Remotely disconnected";
|
||||
AZStd::string reasonString = ToString(reason);
|
||||
AZLOG_INFO("%s due to %s from remote address: %s", endpointString, reasonString.c_str(), connection->GetRemoteAddress().GetString().c_str());
|
||||
AZLOG_INFO("%s from remote address %s due to %s", endpointString, connection->GetRemoteAddress().GetString().c_str(), reasonString.c_str());
|
||||
|
||||
// The client is disconnecting
|
||||
if (GetAgentType() == MultiplayerAgentType::Client)
|
||||
@@ -780,7 +829,7 @@ namespace Multiplayer
|
||||
// Spawn the default player for this host since the host is also a player (not a dedicated server)
|
||||
if (m_agentType == MultiplayerAgentType::ClientServer)
|
||||
{
|
||||
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab();
|
||||
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(0);
|
||||
if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent())
|
||||
{
|
||||
controlledEntityNetBindComponent->SetAllowAutonomy(true);
|
||||
@@ -831,9 +880,9 @@ namespace Multiplayer
|
||||
handler.Connect(m_shutdownEvent);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId)
|
||||
void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId)
|
||||
{
|
||||
m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId);
|
||||
m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId, controlledEntityId);
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
|
||||
@@ -887,6 +936,11 @@ namespace Multiplayer
|
||||
return m_filterEntityManager;
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId)
|
||||
{
|
||||
m_playerRejoinData[temporaryUserIdentifier] = controlledEntityId;
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::SetShouldSpawnNetworkEntities(bool value)
|
||||
{
|
||||
m_spawnNetboundEntities = value;
|
||||
@@ -1017,6 +1071,13 @@ namespace Multiplayer
|
||||
m_cvarCommands.PushBackItem(AZStd::move(replicateString));
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::OnAutonomousEntityReplicatorCreated()
|
||||
{
|
||||
m_autonomousEntityReplicatorCreatedHandler.Disconnect();
|
||||
//m_networkEntityManager.GetNetworkEntityAuthorityTracker()->ResetTimeoutTime(AZ::TimeMs{ 2000 });
|
||||
m_clientMigrationEndEvent.Signal();
|
||||
}
|
||||
|
||||
void MultiplayerSystemComponent::ExecuteConsoleCommandList(IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands)
|
||||
{
|
||||
AZ::IConsole* console = AZ::Interface<AZ::IConsole>::Get();
|
||||
@@ -1028,8 +1089,14 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab()
|
||||
NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab(uint64_t temporaryUserIdentifier)
|
||||
{
|
||||
const auto node = m_playerRejoinData.find(temporaryUserIdentifier);
|
||||
if (node != m_playerRejoinData.end())
|
||||
{
|
||||
return m_networkEntityManager.GetNetworkEntityTracker()->Get(node->second);
|
||||
}
|
||||
|
||||
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str()));
|
||||
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate);
|
||||
|
||||
@@ -1045,7 +1112,7 @@ namespace Multiplayer
|
||||
{
|
||||
if (!AZ::Interface<IMultiplayer>::Get()->StartHosting(sv_port, sv_isDedicated))
|
||||
{
|
||||
AZLOG_ERROR("Failed to start listening on port %u, port is in use?", static_cast<uint32_t>(sv_port));
|
||||
AZLOG_ERROR("Failed to start listening on any allocated port");
|
||||
}
|
||||
}
|
||||
AZ_CONSOLEFREEFUNC(host, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection as a host for other clients to connect to");
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace Multiplayer
|
||||
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
|
||||
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
|
||||
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
|
||||
void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) override;
|
||||
void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) override;
|
||||
void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override;
|
||||
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
|
||||
AZ::TimeMs GetCurrentHostTimeMs() const override;
|
||||
@@ -125,6 +125,7 @@ namespace Multiplayer
|
||||
INetworkEntityManager* GetNetworkEntityManager() override;
|
||||
void SetFilterEntityManager(IFilterEntityManager* entityFilter) override;
|
||||
IFilterEntityManager* GetFilterEntityManager() override;
|
||||
void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) override;
|
||||
void SetShouldSpawnNetworkEntities(bool value) override;
|
||||
bool GetShouldSpawnNetworkEntities() const override;
|
||||
//! @}
|
||||
@@ -138,8 +139,9 @@ namespace Multiplayer
|
||||
|
||||
void TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds);
|
||||
void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom);
|
||||
void OnAutonomousEntityReplicatorCreated();
|
||||
void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands);
|
||||
NetworkEntityHandle SpawnDefaultPlayerPrefab();
|
||||
NetworkEntityHandle SpawnDefaultPlayerPrefab(uint64_t temporaryUserIdentifier);
|
||||
|
||||
AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session");
|
||||
|
||||
@@ -162,12 +164,16 @@ namespace Multiplayer
|
||||
ClientMigrationEndEvent m_clientMigrationEndEvent;
|
||||
NotifyClientMigrationEvent m_notifyClientMigrationEvent;
|
||||
NotifyEntityMigrationEvent m_notifyEntityMigrationEvent;
|
||||
AZ::Event<NetEntityId>::Handler m_autonomousEntityReplicatorCreatedHandler;
|
||||
|
||||
AZStd::queue<AZStd::string> m_pendingConnectionTickets;
|
||||
AZStd::unordered_map<uint64_t, NetEntityId> m_playerRejoinData;
|
||||
|
||||
AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 };
|
||||
HostFrameId m_lastReplicatedHostFrameId = HostFrameId(0);
|
||||
|
||||
uint64_t m_temporaryUserIdentifier = 0; // Used in the event of a migration or rejoin
|
||||
|
||||
double m_serverSendAccumulator = 0.0;
|
||||
float m_renderBlendFactor = 0.0f;
|
||||
float m_tickFactor = 0.0f;
|
||||
|
||||
+34
-6
@@ -47,8 +47,9 @@ namespace Multiplayer
|
||||
, m_entityExitDomainEventHandler([this](const ConstNetworkEntityHandle& entityHandle) { OnEntityExitDomain(entityHandle); })
|
||||
, m_notifyEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) { OnPostEntityMigration(entityHandle, remoteHostId); })
|
||||
{
|
||||
// Set up our remote host identifier, we use the IP address of the host
|
||||
// Set up our remote host identifier, by default we use the IP address of the remote host
|
||||
m_remoteHostId = connection.GetRemoteAddress();
|
||||
m_migrateHostId = m_remoteHostId;
|
||||
|
||||
// Our max payload size is whatever is passed in, minus room for a udp packetheader
|
||||
m_maxPayloadSize = connection.GetConnectionMtu() - UdpPacketHeaderSerializeSize - ReplicationManagerPacketOverhead;
|
||||
@@ -65,7 +66,21 @@ namespace Multiplayer
|
||||
networkEntityManager->AddEntityExitDomainHandler(m_entityExitDomainEventHandler);
|
||||
}
|
||||
|
||||
GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler);
|
||||
if (m_updateMode == Mode::LocalServerToRemoteServer)
|
||||
{
|
||||
GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityReplicationManager::SetMigrateHostId(const HostId& remoteHostId)
|
||||
{
|
||||
// Allows overriding the remote HostId
|
||||
m_migrateHostId = remoteHostId;
|
||||
}
|
||||
|
||||
const HostId& EntityReplicationManager::GetMigrateHostId() const
|
||||
{
|
||||
return m_migrateHostId;
|
||||
}
|
||||
|
||||
const HostId& EntityReplicationManager::GetRemoteHostId() const
|
||||
@@ -362,15 +377,28 @@ namespace Multiplayer
|
||||
const bool changedRemoteRole = (remoteNetworkRole != entityReplicator->GetRemoteNetworkRole());
|
||||
// Check if we've changed our bound local role - this can occur when we gain Autonomous or lose Autonomous on a client
|
||||
bool changedLocalRole(false);
|
||||
if (AZ::Entity* localEnt = entityReplicator->GetEntityHandle().GetEntity())
|
||||
NetBindComponent* netBindComponent = entityReplicator->GetEntityHandle().GetNetBindComponent();
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
NetBindComponent* netBindComponent = entityReplicator->GetEntityHandle().GetNetBindComponent();
|
||||
AZ_Assert(netBindComponent != nullptr, "No NetBindComponent");
|
||||
changedLocalRole = (netBindComponent->GetNetEntityRole() != entityReplicator->GetBoundLocalNetworkRole());
|
||||
}
|
||||
|
||||
if (changedRemoteRole || changedLocalRole)
|
||||
{
|
||||
const uint32_t intEntityId = static_cast<uint32_t>(netBindComponent->GetNetEntityId());
|
||||
if (changedLocalRole)
|
||||
{
|
||||
const char* oldRoleString = GetEnumString(entityReplicator->GetRemoteNetworkRole());
|
||||
const char* newRoleString = GetEnumString(remoteNetworkRole);
|
||||
AZLOG(NET_ReplicatorRoles, "Replicator %u changed local role, old role = %s, new role = %s", intEntityId, oldRoleString, newRoleString);
|
||||
}
|
||||
if (changedRemoteRole)
|
||||
{
|
||||
const char* oldRoleString = GetEnumString(entityReplicator->GetBoundLocalNetworkRole());
|
||||
const char* newRoleString = GetEnumString(netBindComponent->GetNetEntityRole());
|
||||
AZLOG(NET_ReplicatorRoles, "Replicator %u changed remote role, old role = %s, new role = %s", intEntityId, oldRoleString, newRoleString);
|
||||
}
|
||||
|
||||
// If we changed roles, we need to reset everything
|
||||
if (!entityReplicator->IsMarkedForRemoval())
|
||||
{
|
||||
@@ -1107,7 +1135,7 @@ namespace Multiplayer
|
||||
|
||||
if (m_updateMode == EntityReplicationManager::Mode::LocalServerToRemoteServer)
|
||||
{
|
||||
netBindComponent->NotifyServerMigration(GetRemoteHostId(), GetConnection().GetConnectionId());
|
||||
netBindComponent->NotifyServerMigration(GetRemoteHostId(), GetMigrateHostId(), GetConnection().GetConnectionId());
|
||||
}
|
||||
|
||||
bool didSucceed = true;
|
||||
|
||||
@@ -176,7 +176,6 @@ namespace Multiplayer
|
||||
switch (GetBoundLocalNetworkRole())
|
||||
{
|
||||
case NetEntityRole::Authority:
|
||||
{
|
||||
if (GetRemoteNetworkRole() == NetEntityRole::Client || GetRemoteNetworkRole() == NetEntityRole::Autonomous)
|
||||
{
|
||||
m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
|
||||
@@ -189,10 +188,8 @@ namespace Multiplayer
|
||||
{
|
||||
m_onForwardRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case NetEntityRole::Server:
|
||||
{
|
||||
if (GetRemoteNetworkRole() == NetEntityRole::Authority)
|
||||
{
|
||||
m_onSendRpcHandler.Connect(netBindComponent->GetSendServerToAuthorityRpcEvent());
|
||||
@@ -204,23 +201,21 @@ namespace Multiplayer
|
||||
// Listen for these to forward the rpc along to the other Client replicators
|
||||
m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
|
||||
}
|
||||
// NOTE: e_Autonomous is not connected to e_ServerProxy, it is always connected to an e_Authority
|
||||
AZ_Assert(GetRemoteNetworkRole() != NetEntityRole::Autonomous, "Unexpected autonomous remote role")
|
||||
}
|
||||
break;
|
||||
else if (GetRemoteNetworkRole() == NetEntityRole::Autonomous)
|
||||
{
|
||||
// NOTE: Autonomous is not connected to ServerProxy, it is always connected to an Authority
|
||||
AZ_Assert(false, "Unexpected autonomous remote role")
|
||||
}
|
||||
break;
|
||||
case NetEntityRole::Client:
|
||||
{
|
||||
// Nothing allowed, no Client to Server communication
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case NetEntityRole::Autonomous:
|
||||
{
|
||||
if (GetRemoteNetworkRole() == NetEntityRole::Authority)
|
||||
{
|
||||
m_onSendRpcHandler.Connect(netBindComponent->GetSendAutonomousToAuthorityRpcEvent());
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
AZ_Assert(false, "Unexpected network role");
|
||||
}
|
||||
@@ -255,19 +250,6 @@ namespace Multiplayer
|
||||
AZLOG_WARN("Trying to activate an entity that is not in the Init state (%u)", GetEntityHandle().GetNetEntityId());
|
||||
}
|
||||
|
||||
// First we need to make sure the transform component has been updated with the correct value prior to activation
|
||||
// This is because vanilla az components may only depend on the transform component, not the multiplayer transform component
|
||||
//if (auto* locationComponent = FindCommonComponent<LocationComponent::Common>(GetEntityHandle()))
|
||||
//{
|
||||
// AZ::Transform newTransform = locationComponent->GetTransform();
|
||||
// auto* transformComponent = entity->FindComponent<AzFramework::TransformComponent>();
|
||||
// if (transformComponent)
|
||||
// {
|
||||
// // We can't use EBus here since the TransFormBus does not get connected until the activate call below
|
||||
// transformComponent->SetWorldTM(newTransform);
|
||||
// }
|
||||
//}
|
||||
// Ugly, but this is the only time we need to call a non-const function on this entity
|
||||
entity->Activate();
|
||||
|
||||
m_replicationManager.m_orphanedEntityRpcs.DispatchOrphanedRpcs(*this);
|
||||
@@ -281,8 +263,7 @@ namespace Multiplayer
|
||||
NetBindComponent* netBindComponent = m_netBindComponent;
|
||||
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
|
||||
|
||||
bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority)
|
||||
&& (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
|
||||
bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
|
||||
bool isClient = GetRemoteNetworkRole() == NetEntityRole::Client;
|
||||
bool isAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::Autonomous;
|
||||
if (isAuthority || isClient || isAutonomous)
|
||||
@@ -428,10 +409,8 @@ namespace Multiplayer
|
||||
if (const NetworkTransformComponent* networkTransform = entity->FindComponent<NetworkTransformComponent>())
|
||||
{
|
||||
const NetEntityId parentId = networkTransform->GetParentEntityId();
|
||||
/*
|
||||
* For root entities attached to a level, a network parent won't be set.
|
||||
* In this case, this entity is the root entity of the hierarchy and it will be activated first.
|
||||
*/
|
||||
// For root entities attached to a level, a network parent won't be set.
|
||||
// In this case, this entity is the root entity of the hierarchy and it will be activated first.
|
||||
if (parentId != InvalidNetEntityId)
|
||||
{
|
||||
ConstNetworkEntityHandle parentHandle = GetNetworkEntityManager()->GetEntity(parentId);
|
||||
@@ -552,42 +531,33 @@ namespace Multiplayer
|
||||
switch (entityRpcMessage.GetRpcDeliveryType())
|
||||
{
|
||||
case RpcDeliveryType::AuthorityToClient:
|
||||
{
|
||||
if (((GetBoundLocalNetworkRole() == NetEntityRole::Client) || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous))
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Authority))
|
||||
{
|
||||
// We are a local client, and we are connected to server, aka AuthorityToClient
|
||||
result = RpcValidationResult::HandleRpc;
|
||||
}
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Server)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Authority))
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Server) && (GetRemoteNetworkRole() == NetEntityRole::Authority))
|
||||
{
|
||||
// We are on a server, and we received this message from another server, therefore we should forward this to any connected clients
|
||||
result = RpcValidationResult::ForwardToClient;
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case RpcDeliveryType::AuthorityToAutonomous:
|
||||
{
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Autonomous)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Authority))
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Autonomous) && (GetRemoteNetworkRole() == NetEntityRole::Authority))
|
||||
{
|
||||
// We are an autonomous client, and we are connected to server, aka AuthorityToAutonomous
|
||||
result = RpcValidationResult::HandleRpc;
|
||||
}
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Server))
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Server))
|
||||
{
|
||||
// We are on a server, and we received this message from another server, therefore we should forward this to our autonomous player
|
||||
// This can occur if we've recently migrated
|
||||
result = RpcValidationResult::ForwardToAutonomous;
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case RpcDeliveryType::AutonomousToAuthority:
|
||||
{
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Autonomous))
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Autonomous))
|
||||
{
|
||||
if (IsMarkedForRemoval())
|
||||
{
|
||||
@@ -609,12 +579,9 @@ namespace Multiplayer
|
||||
result = RpcValidationResult::HandleRpc;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case RpcDeliveryType::ServerToAuthority:
|
||||
{
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority)
|
||||
&& (GetRemoteNetworkRole() == NetEntityRole::Server))
|
||||
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Server))
|
||||
{
|
||||
// if we're marked for removal, then we should forward to whomever now owns this entity
|
||||
if (IsMarkedForRemoval())
|
||||
@@ -637,9 +604,9 @@ namespace Multiplayer
|
||||
result = RpcValidationResult::HandleRpc;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (result == RpcValidationResult::DropRpcAndDisconnect)
|
||||
{
|
||||
bool isLocalServer = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) || (GetBoundLocalNetworkRole() == NetEntityRole::Server);
|
||||
@@ -653,30 +620,29 @@ namespace Multiplayer
|
||||
{
|
||||
AZLOG_ERROR
|
||||
(
|
||||
"Dropping RPC and Connection EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s",
|
||||
"Dropping RPC and Connection EntityId=%u LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
|
||||
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
|
||||
GetEnumString(GetBoundLocalNetworkRole()),
|
||||
GetEnumString(GetRemoteNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
|
||||
GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
|
||||
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
|
||||
IsMarkedForRemoval() ? "true" : "false"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == RpcValidationResult::DropRpc)
|
||||
{
|
||||
AZLOG
|
||||
(
|
||||
NET_Rpc,
|
||||
"Dropping RPC EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s",
|
||||
"Dropping RPC EntityId=%u LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
|
||||
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
|
||||
GetEnumString(GetBoundLocalNetworkRole()),
|
||||
GetEnumString(GetRemoteNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
|
||||
GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
|
||||
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
|
||||
IsMarkedForRemoval() ? "true" : "false"
|
||||
);
|
||||
@@ -695,13 +661,12 @@ namespace Multiplayer
|
||||
{
|
||||
AZLOG_WARN
|
||||
(
|
||||
"Dropping RPC since entity deleted EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s",
|
||||
"Dropping RPC since entity deleted EntityId=%u LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
|
||||
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()),
|
||||
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()),
|
||||
GetEnumString(GetBoundLocalNetworkRole()),
|
||||
GetEnumString(GetRemoteNetworkRole()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()),
|
||||
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
|
||||
GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
|
||||
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
|
||||
IsMarkedForRemoval() ? "true" : "false"
|
||||
);
|
||||
@@ -739,23 +704,23 @@ namespace Multiplayer
|
||||
case RpcValidationResult::DropRpcAndDisconnect:
|
||||
return false;
|
||||
case RpcValidationResult::ForwardToClient:
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendAuthorityToClientRpcEvent().Signal(entityRpcMessage);
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendAuthorityToClientRpcEvent().Signal(entityRpcMessage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case RpcValidationResult::ForwardToAutonomous:
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendAuthorityToAutonomousRpcEvent().Signal(entityRpcMessage);
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendAuthorityToAutonomousRpcEvent().Signal(entityRpcMessage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case RpcValidationResult::ForwardToAuthority:
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendServerToAuthorityRpcEvent().Signal(entityRpcMessage);
|
||||
{
|
||||
ScopedForwardingMessage forwarding(*this);
|
||||
m_netBindComponent->GetSendServerToAuthorityRpcEvent().Signal(entityRpcMessage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -225,11 +225,19 @@ namespace Multiplayer
|
||||
{
|
||||
AZ::Entity* entity = it->second;
|
||||
NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity);
|
||||
AZ::Aabb entityBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityWorldBoundsUnion(entity->GetId());
|
||||
entityBounds.Expand(AZ::Vector3(0.01f));
|
||||
if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority)
|
||||
{
|
||||
const AZ::Aabb entityBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityWorldBoundsUnion(entity->GetId());
|
||||
debugDisplay->DrawWireBox(entityBounds.GetMin(), entityBounds.GetMax());
|
||||
debugDisplay->SetColor(AZ::Colors::Black);
|
||||
debugDisplay->SetAlpha(0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
debugDisplay->SetColor(AZ::Colors::DeepSkyBlue);
|
||||
debugDisplay->SetAlpha(0.25f);
|
||||
}
|
||||
debugDisplay->DrawWireBox(entityBounds.GetMin(), entityBounds.GetMax());
|
||||
}
|
||||
|
||||
if (m_entityDomain != nullptr)
|
||||
|
||||
+4
-12
@@ -91,17 +91,10 @@ namespace Multiplayer
|
||||
return m_isPoorConnection ? sv_MinEntitiesToReplicate : sv_MaxEntitiesToReplicate;
|
||||
}
|
||||
|
||||
bool ServerToClientReplicationWindow::IsInWindow(const ConstNetworkEntityHandle& entityHandle, NetEntityRole& outNetworkRole) const
|
||||
bool ServerToClientReplicationWindow::IsInWindow([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, NetEntityRole& outNetworkRole) const
|
||||
{
|
||||
// TODO: Clean up this interface, this function is used for server->server migrations, and probably shouldn't be exposed in it's current setup
|
||||
AZ_Assert(false, "IsInWindow should not be called on the ServerToClientReplicationWindow");
|
||||
outNetworkRole = NetEntityRole::InvalidRole;
|
||||
auto iter = m_replicationSet.find(entityHandle);
|
||||
if (iter != m_replicationSet.end())
|
||||
{
|
||||
outNetworkRole = iter->second.m_netEntityRole;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -145,7 +138,7 @@ namespace Multiplayer
|
||||
NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
|
||||
IFilterEntityManager* filterEntityManager = GetMultiplayer()->GetFilterEntityManager();
|
||||
|
||||
// Add all the neighbors
|
||||
// Add all the neighbours
|
||||
for (AzFramework::VisibilityEntry* visEntry : gatheredEntries)
|
||||
{
|
||||
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
|
||||
@@ -300,7 +293,6 @@ namespace Multiplayer
|
||||
void ServerToClientReplicationWindow::AddEntityToReplicationSet(ConstNetworkEntityHandle& entityHandle, float priority, [[maybe_unused]] float distanceSquared)
|
||||
{
|
||||
// Assumption: the entity has been checked for filtering prior to this call.
|
||||
|
||||
if (!sv_ReplicateServerProxies)
|
||||
{
|
||||
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
|
||||
@@ -311,11 +303,11 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
const bool isQueueFull = (m_candidateQueue.size() >= sv_MaxEntitiesToTrackReplication); // See if have the maximum number of entities in our set
|
||||
const bool isQueueFull = (m_candidateQueue.size() >= sv_MaxEntitiesToTrackReplication); // See if have the maximum number of entities in our set
|
||||
const bool isInReplicationSet = m_replicationSet.find(entityHandle) != m_replicationSet.end();
|
||||
if (!isInReplicationSet)
|
||||
{
|
||||
if (isQueueFull) // if our set is full, then we need to remove the worst priority in our set
|
||||
if (isQueueFull) // If our set is full, then we need to remove the worst priority in our set
|
||||
{
|
||||
ConstNetworkEntityHandle removeEnt = m_candidateQueue.top().m_entityHandle;
|
||||
m_candidateQueue.pop();
|
||||
|
||||
@@ -75,8 +75,6 @@ namespace Multiplayer
|
||||
AZ::EntityActivatedEvent::Handler m_entityActivatedEventHandler;
|
||||
AZ::EntityDeactivatedEvent::Handler m_entityDeactivatedEventHandler;
|
||||
|
||||
//NetBindComponent* m_controlledNetBindComponent = nullptr;
|
||||
|
||||
AzNetworking::IConnection* m_connection = nullptr;
|
||||
|
||||
// Cached values to detect a poor network connection
|
||||
|
||||
Reference in New Issue
Block a user