Changes to get client migration partially functional
Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user