You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2143 lines
93 KiB
C++
2143 lines
93 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
#include <AzCore/PlatformIncl.h>
|
|
|
|
#include "Tests.h"
|
|
|
|
#include <GridMate/Session/LANSession.h>
|
|
#include <GridMate/Carrier/DefaultSimulator.h>
|
|
#include <GridMate/Carrier/Driver.h>
|
|
|
|
#include <GridMateTests_Traits_Platform.h>
|
|
|
|
#include <AzCore/std/parallel/thread.h>
|
|
#include <AzCore/std/string/conversions.h>
|
|
|
|
using namespace GridMate;
|
|
|
|
//#define AZ_LAN_TEST_MAIN_THREAD_BLOCKED
|
|
|
|
#define AZ_TEST_LANLATENCY_ENABLE_MONSTER_BUFFER
|
|
|
|
namespace UnitTest
|
|
{
|
|
/*
|
|
* Utility function to tick the replica manager
|
|
*/
|
|
void UpdateReplicaManager(ReplicaManager* replicaManager)
|
|
{
|
|
if (replicaManager)
|
|
{
|
|
replicaManager->Unmarshal();
|
|
replicaManager->UpdateFromReplicas();
|
|
replicaManager->UpdateReplicas();
|
|
replicaManager->Marshal();
|
|
}
|
|
}
|
|
|
|
class Integ_LANSessionMatchmakingParamsTest
|
|
: public GridMateMPTestFixture
|
|
, public SessionEventBus::MultiHandler
|
|
{
|
|
void OnSessionCreated(GridSession* gridSession) override
|
|
{
|
|
AZ_TEST_ASSERT(m_hostSession == nullptr);
|
|
AZ_TEST_ASSERT(gridSession->IsHost());
|
|
m_hostSession = gridSession;
|
|
}
|
|
|
|
public:
|
|
Integ_LANSessionMatchmakingParamsTest(bool useIPv6 = false)
|
|
: m_hostSession(nullptr)
|
|
, m_clientGridMate(nullptr)
|
|
{
|
|
m_driverType = useIPv6 ? Driver::BSD_AF_INET6 : Driver::BSD_AF_INET;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Create all grid mates
|
|
SessionEventBus::MultiHandler::BusConnect(m_gridMate);
|
|
StartGridMateService<LANSessionService>(m_gridMate, SessionServiceDesc());
|
|
AZ_TEST_ASSERT(GridMate::LANSessionServiceBus::FindFirstHandler(m_gridMate) != nullptr);
|
|
|
|
m_clientGridMate = GridMateCreate(GridMateDesc());
|
|
AZ_TEST_ASSERT(m_clientGridMate);
|
|
SessionEventBus::MultiHandler::BusConnect(m_clientGridMate);
|
|
StartGridMateService<LANSessionService>(m_clientGridMate, SessionServiceDesc());
|
|
AZ_TEST_ASSERT(GridMate::LANSessionServiceBus::FindFirstHandler(m_clientGridMate) != nullptr);
|
|
//////////////////////////////////////////////////////////////////////////
|
|
}
|
|
virtual ~Integ_LANSessionMatchmakingParamsTest()
|
|
{
|
|
SessionEventBus::MultiHandler::BusDisconnect(m_gridMate);
|
|
SessionEventBus::MultiHandler::BusDisconnect(m_clientGridMate);
|
|
StopGridMateService<LANSessionService>(m_gridMate);
|
|
|
|
GridMateDestroy(m_clientGridMate);
|
|
}
|
|
|
|
void run()
|
|
{
|
|
TestCarrierDesc carrierDesc;
|
|
carrierDesc.m_enableDisconnectDetection = true;
|
|
carrierDesc.m_threadUpdateTimeMS = 10;
|
|
carrierDesc.m_familyType = m_driverType;
|
|
|
|
// Start the host with one parameter of each type
|
|
LANSessionParams sp;
|
|
sp.m_topology = ST_PEER_TO_PEER;
|
|
sp.m_numPublicSlots = 2;
|
|
sp.m_port = k_hostPort;
|
|
sp.m_params[sp.m_numParams].m_id = "VT_INT32";
|
|
sp.m_params[sp.m_numParams].SetValue(static_cast<AZ::s32>(32));
|
|
sp.m_numParams++;
|
|
sp.m_params[sp.m_numParams].m_id = "VT_INT64";
|
|
sp.m_params[sp.m_numParams].SetValue(static_cast<AZ::s64>(64));
|
|
sp.m_numParams++;
|
|
sp.m_params[sp.m_numParams].m_id = "VT_FLOAT";
|
|
sp.m_params[sp.m_numParams].SetValue(32.f);
|
|
sp.m_numParams++;
|
|
sp.m_params[sp.m_numParams].m_id = "VT_DOUBLE";
|
|
sp.m_params[sp.m_numParams].SetValue(static_cast<double>(64.0));
|
|
sp.m_numParams++;
|
|
sp.m_params[sp.m_numParams].m_id = "VT_STRING";
|
|
sp.m_params[sp.m_numParams].SetValue("string");
|
|
sp.m_numParams++;
|
|
GridSession* hostSession = nullptr;
|
|
EBUS_EVENT_ID_RESULT(hostSession,m_gridMate,LANSessionServiceBus,HostSession,sp,carrierDesc);
|
|
AZ_TEST_ASSERT(hostSession);
|
|
|
|
// Wait for session to be hosted
|
|
while (m_hostSession != hostSession)
|
|
{
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
|
|
m_gridMate->Update();
|
|
UpdateReplicaManager(hostSession->GetReplicaMgr());
|
|
}
|
|
|
|
// Perfrom searches
|
|
GridSearch* searchHandle = nullptr;
|
|
LANSearchParams searchParams;
|
|
searchParams.m_serverPort = k_hostPort;
|
|
searchParams.m_listenPort = 0;
|
|
searchParams.m_numParams = sp.m_numParams;
|
|
for (unsigned int iParam = 0; iParam < searchParams.m_numParams; ++iParam)
|
|
{
|
|
static_cast<GridSessionParam&>(searchParams.m_params[iParam]) = sp.m_params[iParam];
|
|
searchParams.m_params[iParam].m_op = GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
|
|
}
|
|
|
|
for (unsigned int iParam = 0; iParam < searchParams.m_numParams; ++iParam)
|
|
{
|
|
// Change parameter iParam to force a mismatch
|
|
searchParams.m_params[iParam].SetValue(0);
|
|
|
|
EBUS_EVENT_ID_RESULT(searchHandle,m_clientGridMate,LANSessionServiceBus,StartGridSearch,searchParams);
|
|
while (!searchHandle->IsDone())
|
|
{
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
|
|
|
|
m_gridMate->Update();
|
|
UpdateReplicaManager(hostSession->GetReplicaMgr());
|
|
|
|
m_clientGridMate->Update();
|
|
}
|
|
AZ_TEST_ASSERT(searchHandle->GetNumResults() == 0);
|
|
searchHandle->Release();
|
|
|
|
// Restore the parameter
|
|
static_cast<GridSessionParam&>(searchParams.m_params[iParam]) = sp.m_params[iParam];
|
|
}
|
|
|
|
// Perform search with all matching parameters
|
|
EBUS_EVENT_ID_RESULT(searchHandle,m_clientGridMate,LANSessionServiceBus,StartGridSearch,searchParams);
|
|
while (!searchHandle->IsDone())
|
|
{
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
|
|
|
|
m_gridMate->Update();
|
|
UpdateReplicaManager(hostSession->GetReplicaMgr());
|
|
|
|
m_clientGridMate->Update();
|
|
}
|
|
AZ_TEST_ASSERT(searchHandle->GetNumResults() == 1);
|
|
AZ_TEST_ASSERT(searchHandle->GetResult(0)->m_sessionId == m_hostSession->GetId());
|
|
searchHandle->Release();
|
|
|
|
// Perform search with no parameters
|
|
searchParams.m_numParams = 0;
|
|
EBUS_EVENT_ID_RESULT(searchHandle,m_clientGridMate,LANSessionServiceBus,StartGridSearch,searchParams);
|
|
while (!searchHandle->IsDone())
|
|
{
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
|
|
|
|
m_gridMate->Update();
|
|
UpdateReplicaManager(hostSession->GetReplicaMgr());
|
|
|
|
m_clientGridMate->Update();
|
|
}
|
|
AZ_TEST_ASSERT(searchHandle->GetNumResults() == 1);
|
|
AZ_TEST_ASSERT(searchHandle->GetResult(0)->m_sessionId == m_hostSession->GetId());
|
|
searchHandle->Release();
|
|
}
|
|
|
|
static const int k_hostPort = 5450;
|
|
|
|
Driver::BSDSocketFamilyType m_driverType;
|
|
GridSession* m_hostSession;
|
|
IGridMate* m_clientGridMate;
|
|
};
|
|
|
|
class Integ_LANSessionTest
|
|
: public GridMateMPTestFixture
|
|
{
|
|
class TestPeerInfo
|
|
: public SessionEventBus::Handler
|
|
{
|
|
public:
|
|
TestPeerInfo()
|
|
: m_gridMate(nullptr)
|
|
, m_lanSearch(nullptr)
|
|
, m_session(nullptr)
|
|
, m_connections(0) {}
|
|
|
|
// Callback that notifies the title when a game search's query have completed.
|
|
void OnGridSearchComplete(GridSearch* gridSearch) override
|
|
{
|
|
AZ_TEST_ASSERT(gridSearch->IsDone() == true);
|
|
}
|
|
|
|
// Callback that notifies the title when a new player joins the game session.
|
|
void OnMemberJoined(GridSession* session, GridMember* member) override
|
|
{
|
|
if (session == m_session)
|
|
{
|
|
if (member != m_session->GetMyMember())
|
|
{
|
|
m_connections++;
|
|
}
|
|
}
|
|
}
|
|
// Callback that notifies the title that a player is leaving the game session. member pointer is NOT valid after the callback returns.
|
|
void OnMemberLeaving(GridSession* session, GridMember* member) override
|
|
{
|
|
if (session == m_session)
|
|
{
|
|
if (member != m_session->GetMyMember())
|
|
{
|
|
m_connections--;
|
|
}
|
|
}
|
|
}
|
|
/// Callback that host decided to kick a member. You will receive a OnMemberLeaving when the actual member leaves the session.
|
|
void OnMemberKicked(GridSession* session, GridMember* member, AZ::u8 reason) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
(void)reason;
|
|
}
|
|
|
|
void OnSessionError(GridSession* session, const string& /*errorMsg*/) override
|
|
{
|
|
(void)session;
|
|
#ifndef AZ_LAN_TEST_MAIN_THREAD_BLOCKED // we will receive an error is we block for a long time
|
|
AZ_TEST_ASSERT(false);
|
|
#endif
|
|
}
|
|
// Callback that notifies the title when a session will be left. session pointer is NOT valid after the callback returns.
|
|
void OnSessionDelete(GridSession* session) override
|
|
{
|
|
if (session == m_session)
|
|
{
|
|
m_session = nullptr;
|
|
}
|
|
}
|
|
|
|
IGridMate* m_gridMate;
|
|
GridSearch* m_lanSearch;
|
|
GridSession* m_session;
|
|
int m_connections;
|
|
};
|
|
|
|
public:
|
|
Integ_LANSessionTest(bool useIPv6 = false)
|
|
{
|
|
m_driverType = useIPv6 ? Driver::BSD_AF_INET6 : Driver::BSD_AF_INET;
|
|
m_doSessionParamsTest = k_numMachines > 1;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Create all grid mates
|
|
m_peers[0].m_gridMate = m_gridMate;
|
|
m_peers[0].SessionEventBus::Handler::BusConnect(m_peers[0].m_gridMate);
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
GridMateDesc desc;
|
|
m_peers[i].m_gridMate = GridMateCreate(desc);
|
|
AZ_TEST_ASSERT(m_peers[i].m_gridMate);
|
|
|
|
m_peers[i].SessionEventBus::Handler::BusConnect(m_peers[i].m_gridMate);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
// start the multiplayer service (session mgr, extra allocator, etc.)
|
|
StartGridMateService<LANSessionService>(m_peers[i].m_gridMate, SessionServiceDesc());
|
|
AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_peers[i].m_gridMate) != nullptr);
|
|
}
|
|
}
|
|
virtual ~Integ_LANSessionTest()
|
|
{
|
|
StopGridMateService<LANSessionService>(m_peers[0].m_gridMate);
|
|
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
if (m_peers[i].m_gridMate)
|
|
{
|
|
m_peers[i].SessionEventBus::Handler::BusDisconnect();
|
|
GridMateDestroy(m_peers[i].m_gridMate);
|
|
}
|
|
}
|
|
|
|
//StopDrilling();
|
|
|
|
// this will stop the first IGridMate which owns the memory allocators.
|
|
m_peers[0].SessionEventBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
void run()
|
|
{
|
|
//StartDrilling("lansession");
|
|
|
|
TestCarrierDesc carrierDesc;
|
|
carrierDesc.m_enableDisconnectDetection = /*false*/ true;
|
|
carrierDesc.m_threadUpdateTimeMS = 10;
|
|
carrierDesc.m_familyType = m_driverType;
|
|
|
|
// On platforms without loopback, first search for an existing session and if none is found, host one.
|
|
// Otherwise the first gridmate instance will host and the rest will join.
|
|
int numMachines = k_numMachines;
|
|
if (numMachines == 1)
|
|
{
|
|
LANSearchParams searchParams;
|
|
searchParams.m_serverPort = k_hostPort;
|
|
searchParams.m_listenPort = k_hostPort;
|
|
searchParams.m_numParams = 1;
|
|
searchParams.m_params[0].m_id = "Param2";
|
|
searchParams.m_params[0].SetValue(25);
|
|
searchParams.m_params[0].m_op = GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
|
|
searchParams.m_familyType = m_driverType;
|
|
|
|
EBUS_EVENT_ID_RESULT(m_peers[0].m_lanSearch,m_peers[0].m_gridMate,LANSessionServiceBus,StartGridSearch,searchParams);
|
|
while (!m_peers[0].m_lanSearch->IsDone())
|
|
{
|
|
m_peers[0].m_gridMate->Update();
|
|
Update();
|
|
}
|
|
int numResults = m_peers[0].m_lanSearch->GetNumResults();
|
|
if (numResults == 0)
|
|
{
|
|
// We will host a session... no result
|
|
LANSessionParams sp;
|
|
sp.m_topology = ST_PEER_TO_PEER;
|
|
sp.m_numPublicSlots = 64;
|
|
sp.m_port = k_hostPort;
|
|
sp.m_numParams = 2;
|
|
sp.m_params[0].m_id = "Param1";
|
|
sp.m_params[0].SetValue(15);
|
|
sp.m_params[1].m_id = "Param2";
|
|
sp.m_params[1].SetValue(25);
|
|
sp.m_flags = LANSessionParams::SF_HOST_MIGRATION_NO_EMPTY_SESSIONS;
|
|
|
|
EBUS_EVENT_ID_RESULT(m_peers[0].m_session,m_peers[0].m_gridMate,LANSessionServiceBus,HostSession,sp,carrierDesc);
|
|
m_peers[0].m_lanSearch->Release();
|
|
}
|
|
else
|
|
{
|
|
// we found a session join it
|
|
EBUS_EVENT_ID_RESULT(m_peers[0].m_session,m_peers[0].m_gridMate,LANSessionServiceBus,JoinSessionBySearchInfo,static_cast<const LANSearchInfo&>(*m_peers[0].m_lanSearch->GetResult(0)),JoinParams(),carrierDesc);
|
|
m_peers[0].m_lanSearch->Release();
|
|
}
|
|
m_peers[0].m_lanSearch = nullptr;
|
|
}
|
|
else
|
|
{
|
|
LANSessionParams sp;
|
|
sp.m_topology = ST_PEER_TO_PEER;
|
|
sp.m_numPublicSlots = 64;
|
|
sp.m_port = k_hostPort;
|
|
sp.m_numParams = 2;
|
|
sp.m_params[0].m_id = "Param1";
|
|
sp.m_params[0].SetValue(15);
|
|
sp.m_params[1].m_id = "Param2";
|
|
sp.m_params[1].SetValue(25);
|
|
sp.m_flags = LANSessionParams::SF_HOST_MIGRATION_NO_EMPTY_SESSIONS;
|
|
EBUS_EVENT_ID_RESULT(m_peers[k_host].m_session,m_peers[k_host].m_gridMate,LANSessionServiceBus,HostSession,sp,carrierDesc);
|
|
|
|
int listenPort = k_hostPort;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (i == k_host)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LANSearchParams searchParams;
|
|
searchParams.m_serverPort = k_hostPort;
|
|
searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port
|
|
// the rest specify return ports
|
|
searchParams.m_numParams = 1;
|
|
searchParams.m_params[0].m_id = "Param2";
|
|
searchParams.m_params[0].SetValue(25);
|
|
searchParams.m_params[0].m_op = GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
|
|
searchParams.m_familyType = m_driverType;
|
|
EBUS_EVENT_ID_RESULT(m_peers[i].m_lanSearch,m_peers[i].m_gridMate,LANSessionServiceBus,StartGridSearch,searchParams);
|
|
}
|
|
}
|
|
|
|
int maxNumUpdates = 500;
|
|
int numUpdates = 0;
|
|
TimeStamp time = AZStd::chrono::system_clock::now();
|
|
bool sessionParamsTestDone = false;
|
|
while (numUpdates <= maxNumUpdates)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_peers[i].m_gridMate)
|
|
{
|
|
m_peers[i].m_gridMate->Update();
|
|
if (m_peers[i].m_session)
|
|
{
|
|
UpdateReplicaManager(m_peers[i].m_session->GetReplicaMgr());
|
|
}
|
|
}
|
|
}
|
|
Update();
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_peers[i].m_lanSearch && m_peers[i].m_lanSearch->IsDone())
|
|
{
|
|
AZ_TEST_ASSERT(m_peers[i].m_lanSearch->GetNumResults() == 1);
|
|
EBUS_EVENT_ID_RESULT(m_peers[i].m_session,m_peers[i].m_gridMate,LANSessionServiceBus,JoinSessionBySearchInfo,static_cast<const LANSearchInfo&>(*m_peers[i].m_lanSearch->GetResult(0)),JoinParams(),carrierDesc);
|
|
|
|
m_peers[i].m_lanSearch->Release();
|
|
m_peers[i].m_lanSearch = nullptr;
|
|
}
|
|
}
|
|
|
|
#if defined(AZ_LAN_TEST_MAIN_THREAD_BLOCKED)
|
|
if (numUpdates == 200)
|
|
{
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::seconds(7));
|
|
}
|
|
int numOfNullSessions = 0;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_peers[i].session == nullptr)
|
|
{
|
|
++numOfNullSessions;
|
|
}
|
|
}
|
|
if (numOfNullSessions == k_numMachines)
|
|
{
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (m_doSessionParamsTest)
|
|
{
|
|
if (!sessionParamsTestDone)
|
|
{
|
|
if (m_peers[k_host].m_connections == k_numMachines - 1)
|
|
{
|
|
// Set param1 to 16
|
|
GridSessionParam param1;
|
|
param1.m_id = "Param1";
|
|
param1.SetValue(16);
|
|
m_peers[k_host].m_session->SetParam(param1);
|
|
|
|
// Remove param2
|
|
m_peers[k_host].m_session->RemoveParam("Param2");
|
|
|
|
// Add a param
|
|
GridSessionParam param3;
|
|
param3.m_id = "Param3";
|
|
param3.SetValue("val3");
|
|
m_peers[k_host].m_session->SetParam(param3);
|
|
|
|
sessionParamsTestDone = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Debug Info
|
|
TimeStamp now = AZStd::chrono::system_clock::now();
|
|
if (AZStd::chrono::milliseconds(now - time).count() > 1000)
|
|
{
|
|
time = now;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_peers[i].m_session == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (m_peers[i].m_session->IsHost())
|
|
{
|
|
AZ_Printf("GridMate", "------ Host %d ------\n", i);
|
|
}
|
|
else
|
|
{
|
|
AZ_Printf("GridMate", "------ Client %d ------\n", i);
|
|
}
|
|
|
|
AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_peers[i].m_session->GetId().c_str(), m_peers[i].m_session->GetNumberOfMembers(), m_peers[i].m_session->IsHost() ? "yes" : "no", m_peers[i].m_session->GetTime());
|
|
for (unsigned int iMember = 0; iMember < m_peers[i].m_session->GetNumberOfMembers(); ++iMember)
|
|
{
|
|
GridMember* member = m_peers[i].m_session->GetMemberByIndex(iMember);
|
|
AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no");
|
|
}
|
|
AZ_Printf("GridMate", "\n");
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
|
|
numUpdates++;
|
|
}
|
|
|
|
// This check only applies to local tests
|
|
if (numMachines > 1)
|
|
{
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
AZ_TEST_ASSERT(m_peers[i].m_connections == k_numMachines - 1);
|
|
|
|
if (m_doSessionParamsTest)
|
|
{
|
|
unsigned int nParams = m_peers[i].m_session->GetNumParams();
|
|
AZ_TEST_ASSERT(nParams == 2);
|
|
bool hasParam3 = false;
|
|
for (unsigned int iParam = 0; iParam < nParams; ++iParam)
|
|
{
|
|
const GridSessionParam& param = m_peers[i].m_session->GetParam(iParam);
|
|
AZ_TEST_ASSERT(param.m_id != "Param2");
|
|
if (param.m_id == "Param1")
|
|
{
|
|
AZ_TEST_ASSERT(param.m_value == "16");
|
|
}
|
|
else if (param.m_id == "Param3")
|
|
{
|
|
hasParam3 = true;
|
|
AZ_TEST_ASSERT(param.m_value == "val3");
|
|
}
|
|
}
|
|
AZ_TEST_ASSERT(hasParam3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static const int k_numMachines = AZ_TRAIT_GRIDMATE_TEST_NUM_MACHINES;
|
|
static const int k_host = 0;
|
|
static const int k_hostPort = 5450;
|
|
|
|
TestPeerInfo m_peers[k_numMachines];
|
|
Driver::BSDSocketFamilyType m_driverType;
|
|
bool m_doSessionParamsTest;
|
|
};
|
|
|
|
class Integ_LANSessionTestIPv6
|
|
: public Integ_LANSessionTest
|
|
{
|
|
public:
|
|
Integ_LANSessionTestIPv6()
|
|
: Integ_LANSessionTest(true) {}
|
|
};
|
|
|
|
class Integ_LANMultipleSessionTest
|
|
: public GridMateMPTestFixture
|
|
, public SessionEventBus::Handler
|
|
{
|
|
static const int k_numMachines = 3;
|
|
static const int k_host = 0;
|
|
static const int k_numSessions = 2;
|
|
static const int k_hostPort = 5450;
|
|
GridSession* m_sessions[k_numMachines * k_numSessions];
|
|
GridSearch* m_lanSearch[k_numMachines * k_numSessions];
|
|
IGridMate* m_gridMates[k_numMachines];
|
|
public:
|
|
// Callback that notifies the title when a game search's query have completed.
|
|
void OnGridSearchComplete(GridSearch* gridSearch) override
|
|
{
|
|
AZ_TEST_ASSERT(gridSearch->IsDone() == true);
|
|
}
|
|
// Callback that notifies the title when a new player joins the game session.
|
|
void OnMemberJoined(GridSession* session, GridMember* member) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
}
|
|
// Callback that notifies the title that a player is leaving the game session. member pointer is NOT valid after the callback returns.
|
|
void OnMemberLeaving(GridSession* session, GridMember* member) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
}
|
|
/// Callback that host decided to kick a member. You will receive a OnMemberLeaving when the actual member leaves the session.
|
|
void OnMemberKicked(GridSession* session, GridMember* member, AZ::u8 reason) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
(void)reason;
|
|
}
|
|
void OnSessionError(GridSession* session, const string& /*errorMsg*/) override
|
|
{
|
|
(void)session;
|
|
AZ_TEST_ASSERT(false);
|
|
}
|
|
// Callback that notifies the title when a session will be left. session pointer is NOT valid after the callback returns.
|
|
void OnSessionDelete(GridSession* session) override
|
|
{
|
|
int i;
|
|
for (i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == session)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
AZ_TEST_ASSERT(i < k_numMachines * k_numSessions);
|
|
m_sessions[i] = nullptr;
|
|
}
|
|
|
|
Integ_LANMultipleSessionTest()
|
|
: GridMateMPTestFixture(200 * 1024 * 1024)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Create all grid mates
|
|
m_gridMates[0] = m_gridMate;
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
GridMateDesc desc;
|
|
m_gridMates[i] = GridMateCreate(desc);
|
|
AZ_TEST_ASSERT(m_gridMates[i]);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Hook to session event bus.
|
|
SessionEventBus::Handler::BusConnect(m_gridMate);
|
|
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
// start the multiplayer service (session mgr, extra allocator, etc.)
|
|
StartGridMateService<LANSessionService>(m_gridMates[i], SessionServiceDesc());
|
|
AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_gridMates[i]) != nullptr);
|
|
}
|
|
}
|
|
|
|
virtual ~Integ_LANMultipleSessionTest()
|
|
{
|
|
GridMate::StopGridMateService<GridMate::LANSessionService>(m_gridMates[0]);
|
|
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
if (m_gridMates[i])
|
|
{
|
|
GridMateDestroy(m_gridMates[i]);
|
|
}
|
|
}
|
|
|
|
// Unhook from session event bus.
|
|
SessionEventBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
void run()
|
|
{
|
|
TestCarrierDesc carrierDesc;
|
|
carrierDesc.m_enableDisconnectDetection = /*false*/ true;
|
|
carrierDesc.m_threadUpdateTimeMS = 10;
|
|
carrierDesc.m_connectionTimeoutMS = 15000;
|
|
|
|
memset(m_sessions, 0, sizeof(m_sessions));
|
|
memset(m_lanSearch, 0, sizeof(m_lanSearch));
|
|
for (int iSession = 0; iSession < k_numSessions; ++iSession)
|
|
{
|
|
int hostId = k_host + iSession * k_numMachines;
|
|
int hostPort = k_hostPort + iSession * 20; // space them out so we can easily check the data
|
|
LANSessionParams sp;
|
|
sp.m_topology = ST_PEER_TO_PEER;
|
|
sp.m_numPublicSlots = 64;
|
|
sp.m_port = hostPort;
|
|
sp.m_numParams = 2;
|
|
sp.m_params[0].m_id = "Param1";
|
|
sp.m_params[0].SetValue(15);
|
|
sp.m_params[1].m_id = "Param2";
|
|
sp.m_params[1].SetValue(25);
|
|
sp.m_flags = LANSessionParams::SF_HOST_MIGRATION_NO_EMPTY_SESSIONS;
|
|
EBUS_EVENT_ID_RESULT(m_sessions[hostId],m_gridMates[k_host],LANSessionServiceBus,HostSession,sp,carrierDesc);
|
|
|
|
int listenPort = hostPort;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (i == k_host)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LANSearchParams searchParams;
|
|
searchParams.m_serverPort = hostPort;
|
|
searchParams.m_listenPort = listenPort == hostPort ? 0 : ++listenPort; // first client will use ephemeral port
|
|
// the rest specify return ports
|
|
searchParams.m_numParams = 1;
|
|
searchParams.m_params[0].m_id = "Param2";
|
|
searchParams.m_params[0].SetValue(25);
|
|
searchParams.m_params[0].m_op = GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
|
|
int fi = i + iSession * k_numMachines;
|
|
EBUS_EVENT_ID_RESULT(m_lanSearch[fi],m_gridMates[i],LANSessionServiceBus,StartGridSearch,searchParams);
|
|
}
|
|
}
|
|
|
|
int maxNumUpdates = 500;
|
|
int numUpdates = 0;
|
|
TimeStamp time = AZStd::chrono::system_clock::now();
|
|
while (numUpdates <= maxNumUpdates)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_gridMates[i])
|
|
{
|
|
m_gridMates[i]->Update();
|
|
}
|
|
}
|
|
Update();
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
for (int iSession = 0; iSession < k_numSessions; ++iSession)
|
|
{
|
|
for (int iMachine = 0; iMachine < k_numMachines; ++iMachine)
|
|
{
|
|
// Update searches
|
|
int i = iMachine + iSession * k_numMachines;
|
|
if (m_lanSearch[i] && m_lanSearch[i]->IsDone())
|
|
{
|
|
if (m_lanSearch[i]->GetNumResults() == 1)
|
|
{
|
|
EBUS_EVENT_ID_RESULT(m_sessions[i],m_gridMates[iMachine],LANSessionServiceBus,JoinSessionBySearchInfo,static_cast<const LANSearchInfo&>(*m_lanSearch[i]->GetResult(0)),JoinParams(),carrierDesc);
|
|
}
|
|
|
|
m_lanSearch[i]->Release();
|
|
m_lanSearch[i] = nullptr;
|
|
}
|
|
|
|
// Update replica managers
|
|
if (m_sessions[i])
|
|
{
|
|
UpdateReplicaManager(m_sessions[i]->GetReplicaMgr());
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Debug Info
|
|
TimeStamp now = AZStd::chrono::system_clock::now();
|
|
if (AZStd::chrono::milliseconds(now - time).count() > 1000)
|
|
{
|
|
time = now;
|
|
for (int iSession = 0; iSession < k_numSessions; ++iSession)
|
|
{
|
|
AZ_Printf("GridMate", "------ Session %d ------\n", iSession);
|
|
|
|
for (int iMachine = 0; iMachine < k_numMachines; ++iMachine)
|
|
{
|
|
int i = iSession * k_numMachines + iMachine;
|
|
|
|
if (m_sessions[i] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (k_host == i)
|
|
{
|
|
AZ_Printf("GridMate", " ------ Host %d ------\n", i);
|
|
}
|
|
else
|
|
{
|
|
AZ_Printf("GridMate", " ------ Client %d ------\n", i);
|
|
}
|
|
|
|
AZ_Printf("GridMate", " Session %s Members: %d Host: %s Clock: %d\n", m_sessions[i]->GetId().c_str(), m_sessions[i]->GetNumberOfMembers(), m_sessions[i]->IsHost() ? "yes" : "no", m_sessions[i]->GetTime());
|
|
for (unsigned int iMember = 0; iMember < m_sessions[i]->GetNumberOfMembers(); ++iMember)
|
|
{
|
|
GridMember* member = m_sessions[i]->GetMemberByIndex(iMember);
|
|
AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no");
|
|
}
|
|
AZ_Printf("GridMate", "\n");
|
|
}
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
|
|
numUpdates++;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Testing session with low latency. This is special mode usually used by tools and communication channels
|
|
* where we try to response instantly on messages.
|
|
*/
|
|
class Integ_LANLatencySessionTest
|
|
: public GridMateMPTestFixture
|
|
, public SessionEventBus::Handler
|
|
{
|
|
static const int k_numMachines = 2;
|
|
static const int k_host = 0;
|
|
static const int k_hostPort = 5450;
|
|
GridSession* m_sessions[k_numMachines];
|
|
GridSearch* m_lanSearch[k_numMachines];
|
|
IGridMate* m_gridMates[k_numMachines];
|
|
public:
|
|
// Callback that notifies the title when a game search's query have completed.
|
|
void OnGridSearchComplete(GridSearch* gridSearch) override
|
|
{
|
|
AZ_TEST_ASSERT(gridSearch->IsDone() == true);
|
|
}
|
|
// Callback that notifies the title when a new player joins the game session.
|
|
void OnMemberJoined(GridSession* session, GridMember* member) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
}
|
|
// Callback that notifies the title that a player is leaving the game session. member pointer is NOT valid after the callback returns.
|
|
void OnMemberLeaving(GridSession* session, GridMember* member) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
}
|
|
/// Callback that host decided to kick a member. You will receive a OnMemberLeaving when the actual member leaves the session.
|
|
void OnMemberKicked(GridSession* session, GridMember* member, AZ::u8 reason) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
(void)reason;
|
|
}
|
|
void OnSessionError(GridSession* session, const string& /*errorMsg*/) override
|
|
{
|
|
(void)session;
|
|
#ifndef AZ_LAN_TEST_MAIN_THREAD_BLOCKED // we will receive an error is we block for a long time
|
|
AZ_TEST_ASSERT(false);
|
|
#endif
|
|
}
|
|
// Callback that notifies the title when a session will be left. session pointer is NOT valid after the callback returns.
|
|
void OnSessionDelete(GridSession* session) override
|
|
{
|
|
int i;
|
|
for (i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == session)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
AZ_TEST_ASSERT(i < k_numMachines);
|
|
m_sessions[i] = nullptr;
|
|
}
|
|
|
|
Integ_LANLatencySessionTest()
|
|
#ifdef AZ_TEST_LANLATENCY_ENABLE_MONSTER_BUFFER
|
|
: GridMateMPTestFixture(50 * 1024 * 1024)
|
|
#endif
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Create all grid mates
|
|
m_gridMates[0] = m_gridMate;
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
GridMateDesc desc;
|
|
m_gridMates[i] = GridMateCreate(desc);
|
|
AZ_TEST_ASSERT(m_gridMates[i]);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Hook to session event bus.
|
|
SessionEventBus::Handler::BusConnect(m_gridMate);
|
|
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
// start the multiplayer service (session mgr, extra allocator, etc.)
|
|
StartGridMateService<LANSessionService>(m_gridMates[i], SessionServiceDesc());
|
|
AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_gridMates[i]) != nullptr);
|
|
}
|
|
}
|
|
|
|
virtual ~Integ_LANLatencySessionTest()
|
|
{
|
|
StopGridMateService<LANSessionService>(m_gridMates[0]);
|
|
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
if (m_gridMates[i])
|
|
{
|
|
GridMateDestroy(m_gridMates[i]);
|
|
}
|
|
}
|
|
|
|
//StopDrilling();
|
|
|
|
// Unhook from session event bus.
|
|
SessionEventBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
void run()
|
|
{
|
|
TestCarrierDesc carrierDesc;
|
|
carrierDesc.m_enableDisconnectDetection = true;
|
|
carrierDesc.m_threadUpdateTimeMS = 10;
|
|
carrierDesc.m_threadInstantResponse = true; // enable low latency mode
|
|
carrierDesc.m_driverIsFullPackets = true; // test sending 64k packets (this is useful for LAN only)
|
|
//carrierDesc.m_driverSendBufferSize = 1 * 1024 * 1024;
|
|
carrierDesc.m_driverReceiveBufferSize = 1 * 1024 * 1024;
|
|
|
|
//StartDrilling("lansession");
|
|
|
|
memset(m_sessions, 0, sizeof(m_sessions));
|
|
LANSessionParams sp;
|
|
sp.m_topology = ST_PEER_TO_PEER;
|
|
sp.m_numPublicSlots = 64;
|
|
sp.m_port = k_hostPort;
|
|
sp.m_numParams = 2;
|
|
sp.m_params[0].m_id = "Param1";
|
|
sp.m_params[0].SetValue(15);
|
|
sp.m_params[1].m_id = "Param2";
|
|
sp.m_params[1].SetValue(25);
|
|
sp.m_flags = LANSessionParams::SF_HOST_MIGRATION_NO_EMPTY_SESSIONS;
|
|
EBUS_EVENT_ID_RESULT(m_sessions[k_host],m_gridMates[k_host],LANSessionServiceBus,HostSession,sp,carrierDesc);
|
|
|
|
memset(m_lanSearch, 0, sizeof(m_lanSearch));
|
|
int listenPort = k_hostPort;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (i == k_host)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LANSearchParams searchParams;
|
|
searchParams.m_serverPort = k_hostPort;
|
|
searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port
|
|
// the rest specify return ports
|
|
searchParams.m_numParams = 1;
|
|
searchParams.m_params[0].m_id = "Param2";
|
|
searchParams.m_params[0].SetValue(25);
|
|
searchParams.m_params[0].m_op = GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
|
|
EBUS_EVENT_ID_RESULT(m_lanSearch[i],m_gridMates[i],LANSessionServiceBus,StartGridSearch,searchParams);
|
|
}
|
|
|
|
int maxNumUpdates = 500 /*25000*/;
|
|
int numUpdates = 0;
|
|
TimeStamp time = AZStd::chrono::system_clock::now();
|
|
#ifdef AZ_TEST_LANLATENCY_ENABLE_MONSTER_BUFFER
|
|
const int monsterBufferSize = /*10*/ 1 * 1024 * 1024;
|
|
char* monsterBufferSend = (char*)azmalloc(monsterBufferSize);
|
|
char* value = monsterBufferSend;
|
|
for (int i = 0; i < monsterBufferSize; ++i, ++value)
|
|
{
|
|
*value = (char)i;
|
|
}
|
|
char* monsterBufferReceive = (char*)azmalloc(monsterBufferSize);
|
|
int numLastSend = 0;
|
|
#endif
|
|
int numMStoSleep = /*30*/ 16;
|
|
while (numUpdates <= maxNumUpdates)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_gridMates[i])
|
|
{
|
|
m_gridMates[i]->Update();
|
|
|
|
if (m_sessions[i])
|
|
{
|
|
UpdateReplicaManager(m_sessions[i]->GetReplicaMgr());
|
|
}
|
|
}
|
|
}
|
|
Update();
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_lanSearch[i] && m_lanSearch[i]->IsDone())
|
|
{
|
|
if (m_lanSearch[i]->GetNumResults() == 1)
|
|
{
|
|
EBUS_EVENT_ID_RESULT(m_sessions[i],m_gridMates[i],LANSessionServiceBus,JoinSessionBySearchInfo,static_cast<const LANSearchInfo&>(*m_lanSearch[i]->GetResult(0)),JoinParams(),carrierDesc);
|
|
}
|
|
|
|
m_lanSearch[i]->Release();
|
|
m_lanSearch[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
if (numUpdates >= 150)
|
|
{
|
|
#ifdef AZ_TEST_LANLATENCY_ENABLE_MONSTER_BUFFER
|
|
if (numUpdates % /*150*/ 50 == /*149*/ 49)
|
|
{
|
|
if (m_sessions[0] != nullptr)
|
|
{
|
|
numLastSend = numUpdates;
|
|
for (unsigned int iMember = 0; iMember < m_sessions[0]->GetNumberOfMembers(); ++iMember)
|
|
{
|
|
GridMember* member = m_sessions[0]->GetMemberByIndex(iMember);
|
|
if (!member->IsLocal())
|
|
{
|
|
member->SendBinary(monsterBufferSend, monsterBufferSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_sessions[1] != nullptr)
|
|
{
|
|
for (unsigned int iMember = 0; iMember < m_sessions[1]->GetNumberOfMembers(); ++iMember)
|
|
{
|
|
GridMember* member = m_sessions[1]->GetMemberByIndex(iMember);
|
|
if (!member->IsLocal())
|
|
{
|
|
Carrier::ReceiveResult result = member->ReceiveBinary(monsterBufferReceive, monsterBufferSize);
|
|
if (result.m_state == Carrier::ReceiveResult::RECEIVED)
|
|
{
|
|
AZ_TEST_ASSERT(memcmp(monsterBufferReceive, monsterBufferSend, monsterBufferSize) == 0);
|
|
memset(monsterBufferReceive, 0, monsterBufferSize);
|
|
AZ_Printf("GridMate", "Monster buffer process time ~%d ms\n", (numUpdates - numLastSend) * numMStoSleep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else // !AZ_TEST_LANLATENCY_ENABLE_MONSTER_BUFFER
|
|
const char* session0SendData = "Hello";
|
|
const char* session1SendData = "Bye";
|
|
char dataBuffer[64];
|
|
if (sessions[0] != nullptr)
|
|
{
|
|
for (unsigned int iMember = 0; iMember < sessions[0]->GetNumberOfMembers(); ++iMember)
|
|
{
|
|
GridMember* member = sessions[0]->GetMember(iMember);
|
|
if (!member->IsLocal())
|
|
{
|
|
member->SendBinary(session0SendData, static_cast<unsigned int>(strlen(session0SendData)));
|
|
Carrier::ReceiveResult receiveResult = member->ReceiveBinary(dataBuffer, AZ_ARRAY_SIZE(dataBuffer));
|
|
if (result.m_state == Carrier::ReceiveResult::RECEIVED)
|
|
{
|
|
AZ_TEST_ASSERT(strncmp(session1SendData, dataBuffer, strlen(session1SendData)) == 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (sessions[1] != nullptr)
|
|
{
|
|
for (unsigned int iMember = 0; iMember < sessions[1]->GetNumberOfMembers(); ++iMember)
|
|
{
|
|
GridMember* member = sessions[1]->GetMember(iMember);
|
|
if (!member->IsLocal())
|
|
{
|
|
member->SendBinary(session1SendData, static_cast<unsigned int>(strlen(session1SendData)));
|
|
Carrier::ReceiveResult receiveResult = member->ReceiveBinary(dataBuffer, AZ_ARRAY_SIZE(dataBuffer));
|
|
if (result.m_state == Carrier::ReceiveResult::RECEIVED)
|
|
{
|
|
AZ_TEST_ASSERT(strncmp(session0SendData, dataBuffer, static_cast<unsigned int>(strlen(session0SendData))) == 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // AZ_TEST_LANLATENCY_ENABLE_MONSTER_BUFFER
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Debug Info
|
|
TimeStamp now = AZStd::chrono::system_clock::now();
|
|
if (AZStd::chrono::milliseconds(now - time).count() > 1000)
|
|
{
|
|
time = now;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (k_host == i)
|
|
{
|
|
AZ_Printf("GridMate", "------ Host %d ------\n", i);
|
|
}
|
|
else
|
|
{
|
|
AZ_Printf("GridMate", "------ Client %d ------\n", i);
|
|
}
|
|
|
|
AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_sessions[i]->GetId().c_str(), m_sessions[i]->GetNumberOfMembers(), m_sessions[i]->IsHost() ? "yes" : "no", m_sessions[i]->GetTime());
|
|
for (unsigned int iMember = 0; iMember < m_sessions[i]->GetNumberOfMembers(); ++iMember)
|
|
{
|
|
GridMember* member = m_sessions[i]->GetMemberByIndex(iMember);
|
|
|
|
// check statistics
|
|
if (member->IsLocal())
|
|
{
|
|
AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no");
|
|
}
|
|
else
|
|
{
|
|
Carrier* carrier = m_sessions[i]->GetCarrier();
|
|
ConnectionID connId = member->GetConnectionId();
|
|
Carrier::Statistics statsLifeTime, statsLastSecond;
|
|
Carrier::Statistics effectiveStatsLifeTime, effectiveStatsLastSecond;
|
|
statsLifeTime.m_rtt = 9999.99f;
|
|
statsLifeTime.m_dataReceived = statsLifeTime.m_dataSend = 0;
|
|
statsLifeTime.m_packetLost = 0;
|
|
effectiveStatsLifeTime.m_rtt = 9999.99f;
|
|
effectiveStatsLifeTime.m_dataReceived = effectiveStatsLifeTime.m_dataSend = 0;
|
|
effectiveStatsLifeTime.m_packetLost = 0;
|
|
if (connId != InvalidConnectionID)
|
|
{
|
|
/// Check the effective data
|
|
carrier->QueryStatistics(connId, &statsLastSecond, &statsLifeTime, &effectiveStatsLastSecond, &effectiveStatsLifeTime);
|
|
|
|
//statsLifeTime.rtt = (statsLifeTime.rtt + statsLastSecond.rtt) * .5f;
|
|
statsLifeTime.m_packetSend += statsLastSecond.m_packetSend;
|
|
statsLifeTime.m_dataSend += statsLastSecond.m_dataSend;
|
|
|
|
//effectiveStatsLifeTime.rtt = (effectiveStatsLifeTime.rtt + effectiveStatsLastSecond.rtt) * .5f;
|
|
effectiveStatsLifeTime.m_packetSend += effectiveStatsLastSecond.m_packetSend;
|
|
effectiveStatsLifeTime.m_dataSend += effectiveStatsLastSecond.m_dataSend;
|
|
|
|
// the first spike (while we allocate is more otherwise it seems correct)
|
|
//AZ_TEST_ASSERT(effectiveStatsLifeTime.rtt<=10.0f); // Check if our low latency mode is actually low latency
|
|
}
|
|
|
|
AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s Rtt:%.2f Send:%u Received:%u Loss:%d eRtt:%.2f eSend:%u eReceived:%u eLoss:%d\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no", statsLifeTime.m_rtt, statsLifeTime.m_dataSend, statsLifeTime.m_dataReceived, statsLifeTime.m_packetLost, effectiveStatsLifeTime.m_rtt, effectiveStatsLifeTime.m_dataSend, effectiveStatsLifeTime.m_dataReceived, effectiveStatsLifeTime.m_packetLost);
|
|
}
|
|
}
|
|
|
|
AZ_Printf("GridMate", "\n");
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(numMStoSleep));
|
|
numUpdates++;
|
|
}
|
|
|
|
#ifdef AZ_TEST_LANLATENCY_ENABLE_MONSTER_BUFFER
|
|
azfree(monsterBufferSend);
|
|
azfree(monsterBufferReceive);
|
|
#endif //
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Simulating most common host migration scenarios.
|
|
* 1. We start a session with 3 members.
|
|
* 2. We drop the host (by blocking it's connection). (we are left with 2 members after migration)
|
|
* 3. After host migration has completed we add additional 3 members. (after join we have 5 members)
|
|
* 4. We drop the new host. (after migration we have 4 members)
|
|
* 5. After host migration we drop the new host again. (after migration we have 3 members).
|
|
* Session should be fully operational at the end with 3 members left.
|
|
*/
|
|
class Integ_LANSessionMigarationTestTest
|
|
: public SessionEventBus::Handler
|
|
, public GridMateMPTestFixture
|
|
{
|
|
static const int k_numInitialMembers = 3;
|
|
static const int k_numSecondMembers = 3;
|
|
static const int k_numMachines = k_numInitialMembers + k_numSecondMembers;
|
|
|
|
static const int k_hostPort = 5450;
|
|
|
|
GridSession* m_sessions[k_numMachines];
|
|
GridSearch* m_lanSearch[k_numMachines];
|
|
IGridMate* m_gridMates[k_numMachines];
|
|
DefaultSimulator m_simulators[k_numMachines];
|
|
|
|
int m_host;
|
|
int m_numUpdates;
|
|
public:
|
|
// Callback that notifies the title when a game search's query have completed.
|
|
void OnGridSearchComplete(GridSearch* gridSearch) override
|
|
{
|
|
AZ_TEST_ASSERT(gridSearch->IsDone() == true);
|
|
}
|
|
// Callback that notifies the title when a new player joins the game session.
|
|
void OnMemberJoined(GridSession* session, GridMember* member) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
}
|
|
// Callback that notifies the title that a player is leaving the game session. member pointer is NOT valid after the callback returns.
|
|
void OnMemberLeaving(GridSession* session, GridMember* member) override
|
|
{
|
|
(void)member;
|
|
if (session->GetNumberOfMembers() == 2) // if the last member (not us) is leaving kill the session!
|
|
{
|
|
session->Leave(false);
|
|
}
|
|
}
|
|
/// Callback that host decided to kick a member. You will receive a OnMemberLeaving when the actual member leaves the session.
|
|
void OnMemberKicked(GridSession* session, GridMember* member, AZ::u8 reason) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
(void)reason;
|
|
}
|
|
void OnSessionError(GridSession* session, const string& /*errorMsg*/) override
|
|
{
|
|
(void)session;
|
|
AZ_TEST_ASSERT(false);
|
|
}
|
|
// Callback that notifies the title when a session will be left. session pointer is NOT valid after the callback returns.
|
|
void OnSessionDelete(GridSession* session) override
|
|
{
|
|
int i;
|
|
for (i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == session)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
AZ_TEST_ASSERT(i < k_numMachines);
|
|
m_sessions[i] = nullptr;
|
|
}
|
|
|
|
void OnMigrationStart(GridSession* session) override
|
|
{
|
|
(void)session;
|
|
AZ_TracePrintf("GridMate", "Migration start on %s at frame %d\n", session->GetMyMember()->GetId().ToAddress().c_str(), m_numUpdates);
|
|
}
|
|
void OnMigrationElectHost(GridSession* session, GridMember*& newHost) override
|
|
{
|
|
(void)session;
|
|
(void)newHost;
|
|
AZ_TracePrintf("GridMate", "Migration elect host on %s at frame %d\n", session->GetMyMember()->GetId().ToAddress().c_str(), m_numUpdates);
|
|
}
|
|
void OnMigrationEnd(GridSession* session, GridMember* newHost) override
|
|
{
|
|
(void)session;
|
|
(void)newHost;
|
|
AZ_TracePrintf("GridMate", "Migration end on %s, new host %s at frame %d\n", session->GetMyMember()->GetId().ToAddress().c_str(), session->GetHost()->GetId().ToAddress().c_str(), m_numUpdates);
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == session)
|
|
{
|
|
if (session->GetMyMember() == newHost)
|
|
{
|
|
m_host = i;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Integ_LANSessionMigarationTestTest()
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Create all grid mates
|
|
m_gridMates[0] = m_gridMate;
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
GridMateDesc desc;
|
|
m_gridMates[i] = GridMateCreate(desc);
|
|
AZ_TEST_ASSERT(m_gridMates[i]);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Hook to session event bus.
|
|
SessionEventBus::Handler::BusConnect(m_gridMate);
|
|
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
// start the multiplayer service (session mgr, extra allocator, etc.)
|
|
StartGridMateService<LANSessionService>(m_gridMates[i], SessionServiceDesc());
|
|
AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_gridMates[i]) != nullptr);
|
|
}
|
|
|
|
//StartDrilling("lanmigration");
|
|
}
|
|
|
|
virtual ~Integ_LANSessionMigarationTestTest()
|
|
{
|
|
StopGridMateService<LANSessionService>(m_gridMates[0]);
|
|
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
if (m_gridMates[i])
|
|
{
|
|
GridMateDestroy(m_gridMates[i]);
|
|
}
|
|
}
|
|
|
|
// Unhook from session event bus.
|
|
SessionEventBus::Handler::BusDisconnect();
|
|
|
|
//StopDrilling();
|
|
}
|
|
|
|
void run()
|
|
{
|
|
TestCarrierDesc carrierDesc;
|
|
carrierDesc.m_enableDisconnectDetection = /*false*/ true;
|
|
carrierDesc.m_threadUpdateTimeMS = 10;
|
|
carrierDesc.m_simulator = &m_simulators[0];
|
|
|
|
m_host = 1; // first we set the second session/machine to be the host (so we can test non zero host)
|
|
|
|
memset(m_sessions, 0, sizeof(m_sessions));
|
|
LANSessionParams sp;
|
|
sp.m_topology = ST_PEER_TO_PEER;
|
|
sp.m_numPublicSlots = 64;
|
|
sp.m_port = k_hostPort;
|
|
sp.m_numParams = 2;
|
|
sp.m_params[0].m_id = "Param1";
|
|
sp.m_params[0].SetValue(15);
|
|
sp.m_params[1].m_id = "Param2";
|
|
sp.m_params[1].SetValue(25);
|
|
EBUS_EVENT_ID_RESULT(m_sessions[m_host],m_gridMates[m_host],LANSessionServiceBus,HostSession,sp,carrierDesc);
|
|
|
|
memset(m_lanSearch, 0, sizeof(m_lanSearch));
|
|
int listenPort = k_hostPort;
|
|
int numSessionsUsed = 0;
|
|
// 1. We start a session with 3 members. (1 host 2 joins)
|
|
for (; numSessionsUsed < k_numInitialMembers; ++numSessionsUsed)
|
|
{
|
|
if (numSessionsUsed == m_host)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LANSearchParams searchParams;
|
|
searchParams.m_serverPort = k_hostPort;
|
|
searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port
|
|
// the rest specify return ports
|
|
searchParams.m_numParams = 1;
|
|
searchParams.m_params[0].m_id = "Param2";
|
|
searchParams.m_params[0].SetValue(25);
|
|
searchParams.m_params[0].m_op = GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
|
|
EBUS_EVENT_ID_RESULT(m_lanSearch[numSessionsUsed],m_gridMates[numSessionsUsed],LANSessionServiceBus,StartGridSearch,searchParams);
|
|
}
|
|
|
|
int maxNumUpdates = 1000;
|
|
m_numUpdates = 0;
|
|
TimeStamp time = AZStd::chrono::system_clock::now();
|
|
while (m_numUpdates <= maxNumUpdates)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_gridMates[i])
|
|
{
|
|
m_gridMates[i]->Update();
|
|
|
|
if (m_sessions[i])
|
|
{
|
|
UpdateReplicaManager(m_sessions[i]->GetReplicaMgr());
|
|
}
|
|
}
|
|
}
|
|
Update();
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_lanSearch[i] && m_lanSearch[i]->IsDone())
|
|
{
|
|
if (m_lanSearch[i]->GetNumResults() == 1)
|
|
{
|
|
carrierDesc.m_simulator = &m_simulators[i];
|
|
EBUS_EVENT_ID_RESULT(m_sessions[i],m_gridMates[i],LANSessionServiceBus,JoinSessionBySearchInfo,static_cast<const LANSearchInfo&>(*m_lanSearch[i]->GetResult(0)),JoinParams(),carrierDesc);
|
|
}
|
|
|
|
m_lanSearch[i]->Release();
|
|
m_lanSearch[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
// 2. We drop the host (by blocking it's connection). (we are left with 2 members after migration)
|
|
if (m_numUpdates == 150)
|
|
{
|
|
// Block 100% the host (simulate connection drop)
|
|
m_simulators[m_host].SetOutgoingPacketLoss(1, 1);
|
|
m_simulators[m_host].SetIncomingPacketLoss(1, 1);
|
|
m_simulators[m_host].Enable();
|
|
}
|
|
|
|
// 3. After host migration has completed we add additional 3 members. (after join we have 5 members)
|
|
if (m_numUpdates == 400)
|
|
{
|
|
for (; numSessionsUsed < k_numMachines; ++numSessionsUsed)
|
|
{
|
|
//if(numSessionsUsed==host) continue;
|
|
|
|
LANSearchParams searchParams;
|
|
searchParams.m_serverPort = k_hostPort;
|
|
searchParams.m_listenPort = listenPort++;
|
|
searchParams.m_numParams = 1;
|
|
searchParams.m_params[0].m_id = "Param2";
|
|
searchParams.m_params[0].SetValue(25);
|
|
searchParams.m_params[0].m_op = GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
|
|
EBUS_EVENT_ID_RESULT(m_lanSearch[numSessionsUsed],m_gridMates[numSessionsUsed],LANSessionServiceBus,StartGridSearch,searchParams);
|
|
}
|
|
}
|
|
|
|
// 4. We drop the new host. (after migration we have 4 members)
|
|
if (m_numUpdates == 600)
|
|
{
|
|
// Block 100% the host (simulate connection drop)
|
|
m_simulators[m_host].SetOutgoingPacketLoss(1, 1);
|
|
m_simulators[m_host].SetIncomingPacketLoss(1, 1);
|
|
m_simulators[m_host].Enable();
|
|
}
|
|
|
|
// 5. After host migration we drop the new host again. (after migration we have 3 members).
|
|
if (m_numUpdates == 800)
|
|
{
|
|
// Block 100% the host (simulate connection drop)
|
|
m_simulators[m_host].SetOutgoingPacketLoss(1, 1);
|
|
m_simulators[m_host].SetIncomingPacketLoss(1, 1);
|
|
m_simulators[m_host].Enable();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Debug Info
|
|
TimeStamp now = AZStd::chrono::system_clock::now();
|
|
if (AZStd::chrono::milliseconds(now - time).count() > 1000)
|
|
{
|
|
time = now;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (m_host == i)
|
|
{
|
|
AZ_Printf("GridMate", "------ Host %d ------\n", i);
|
|
}
|
|
else
|
|
{
|
|
AZ_Printf("GridMate", "------ Client %d ------\n", i);
|
|
}
|
|
|
|
AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_sessions[i]->GetId().c_str(), m_sessions[i]->GetNumberOfMembers(), m_sessions[i]->IsHost() ? "yes" : "no", m_sessions[i]->GetTime());
|
|
for (unsigned int iMember = 0; iMember < m_sessions[i]->GetNumberOfMembers(); ++iMember)
|
|
{
|
|
GridMember* member = m_sessions[i]->GetMemberByIndex(iMember);
|
|
AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no");
|
|
}
|
|
AZ_Printf("GridMate", "\n");
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
|
|
m_numUpdates++;
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Simulating less common conditions (peer drops during host migration).
|
|
* 1. We start a session with 5 members.
|
|
* 2. We drop the host.
|
|
* 3. Shortly after the host (before host election has completed) we drop the next
|
|
* best host candidate (that everybody vote for) - this should trigger a re vote for the next it line.
|
|
* 4. Shortly after (while still in host election) we drop the next best host candidate,
|
|
* which should trigger another re vote.
|
|
* 4.1. The old host and the 2 dropped peers, became the hosts of their own sessions (in this test just remove all of those sessions)
|
|
* 4.2. After host migration has completed we have 2 members and in the session left.
|
|
* 5. We join a 2 new members to the session.
|
|
* Session should be fully operational at the end with 4 members in it.
|
|
*/
|
|
class Integ_LANSessionMigarationTestTest2
|
|
: public SessionEventBus::Handler
|
|
, public GridMateMPTestFixture
|
|
{
|
|
static const int k_numInitialMembers = 5;
|
|
static const int k_numSecondMembers = 2;
|
|
static const int k_numMachines = k_numInitialMembers + k_numSecondMembers;
|
|
|
|
static const int k_hostPort = 5450;
|
|
|
|
GridSession* m_sessions[k_numMachines];
|
|
GridSearch* m_lanSearch[k_numMachines];
|
|
IGridMate* m_gridMates[k_numMachines];
|
|
DefaultSimulator m_simulators[k_numMachines];
|
|
|
|
int m_host;
|
|
int m_numUpdates;
|
|
public:
|
|
// Callback that notifies the title when a game search's query have completed.
|
|
void OnGridSearchComplete(GridSearch* gridSearch) override
|
|
{
|
|
AZ_TEST_ASSERT(gridSearch->IsDone() == true);
|
|
}
|
|
// Callback that notifies the title when a new player joins the game session.
|
|
void OnMemberJoined(GridSession* session, GridMember* member) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
}
|
|
// Callback that notifies the title that a player is leaving the game session. member pointer is NOT valid after the callback returns.
|
|
void OnMemberLeaving(GridSession* session, GridMember* member) override
|
|
{
|
|
(void)member;
|
|
if (session->GetNumberOfMembers() == 2) // if the last member (not us) is leaving kill the session!
|
|
{
|
|
session->Leave(false);
|
|
}
|
|
}
|
|
/// Callback that host decided to kick a member. You will receive a OnMemberLeaving when the actual member leaves the session.
|
|
void OnMemberKicked(GridSession* session, GridMember* member, AZ::u8 reason) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
(void)reason;
|
|
}
|
|
void OnSessionError(GridSession* session, const string& /*errorMsg*/) override
|
|
{
|
|
(void)session;
|
|
// On this test we will get a open port error because we have multiple hosts. This is ok, since we test migration here!
|
|
//AZ_TEST_ASSERT(false);
|
|
}
|
|
// Callback that notifies the title when a session will be left. session pointer is NOT valid after the callback returns.
|
|
void OnSessionDelete(GridSession* session) override
|
|
{
|
|
int i;
|
|
for (i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == session)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
AZ_TEST_ASSERT(i < k_numMachines);
|
|
m_sessions[i] = nullptr;
|
|
}
|
|
|
|
void OnMigrationStart(GridSession* session) override
|
|
{
|
|
(void)session;
|
|
AZ_TracePrintf("GridMate", "Migration start on %s at frame %d\n", session->GetMyMember()->GetId().ToAddress().c_str(), m_numUpdates);
|
|
}
|
|
void OnMigrationElectHost(GridSession* session, GridMember*& newHost) override
|
|
{
|
|
(void)session;
|
|
(void)newHost;
|
|
AZ_TracePrintf("GridMate", "Migration elect host on %s at frame %d\n", session->GetMyMember()->GetId().ToAddress().c_str(), m_numUpdates);
|
|
}
|
|
void OnMigrationEnd(GridSession* session, GridMember* newHost) override
|
|
{
|
|
(void)session;
|
|
(void)newHost;
|
|
AZ_TracePrintf("GridMate", "Migration end on %s, new host %s at frame %d\n", session->GetMyMember()->GetId().ToAddress().c_str(), session->GetHost()->GetId().ToAddress().c_str(), m_numUpdates);
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == session)
|
|
{
|
|
if (session->GetMyMember() == newHost)
|
|
{
|
|
m_host = i;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Integ_LANSessionMigarationTestTest2()
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Create all grid mates
|
|
m_gridMates[0] = m_gridMate;
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
GridMateDesc desc;
|
|
m_gridMates[i] = GridMateCreate(desc);
|
|
AZ_TEST_ASSERT(m_gridMates[i]);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Hook to session event bus.
|
|
SessionEventBus::Handler::BusConnect(m_gridMate);
|
|
|
|
// 1. We start a session with 5 members. (1 host 4 peers)
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
// start the multiplayer service (session mgr, extra allocator, etc.)
|
|
StartGridMateService<LANSessionService>(m_gridMates[i], SessionServiceDesc());
|
|
AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_gridMates[i]) != nullptr);
|
|
}
|
|
|
|
//StartDrilling("lanmigration2");
|
|
}
|
|
virtual ~Integ_LANSessionMigarationTestTest2()
|
|
{
|
|
StopGridMateService<LANSessionService>(m_gridMates[0]);
|
|
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
if (m_gridMates[i])
|
|
{
|
|
GridMateDestroy(m_gridMates[i]);
|
|
}
|
|
}
|
|
|
|
// Unhook from session event bus.
|
|
SessionEventBus::Handler::BusDisconnect();
|
|
|
|
//StopDrilling();
|
|
}
|
|
void run()
|
|
{
|
|
TestCarrierDesc carrierDesc;
|
|
carrierDesc.m_enableDisconnectDetection = /*false*/ true;
|
|
carrierDesc.m_threadUpdateTimeMS = 10;
|
|
carrierDesc.m_simulator = &m_simulators[0];
|
|
|
|
m_host = 1; // first we set the second session/machine to be the host (so we can test non zero host)
|
|
|
|
memset(m_sessions, 0, sizeof(m_sessions));
|
|
LANSessionParams sp;
|
|
sp.m_topology = ST_PEER_TO_PEER;
|
|
sp.m_numPublicSlots = 64;
|
|
sp.m_port = k_hostPort;
|
|
sp.m_numParams = 2;
|
|
sp.m_params[0].m_id = "Param1";
|
|
sp.m_params[0].SetValue(15);
|
|
sp.m_params[1].m_id = "Param2";
|
|
sp.m_params[1].SetValue(25);
|
|
EBUS_EVENT_ID_RESULT(m_sessions[m_host],m_gridMates[m_host],LANSessionServiceBus,HostSession,sp,carrierDesc);
|
|
|
|
memset(m_lanSearch, 0, sizeof(m_lanSearch));
|
|
int listenPort = k_hostPort;
|
|
int numSessionsUsed = 0;
|
|
for (; numSessionsUsed < k_numInitialMembers; ++numSessionsUsed)
|
|
{
|
|
if (numSessionsUsed == m_host)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LANSearchParams searchParams;
|
|
searchParams.m_serverPort = k_hostPort;
|
|
searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port
|
|
// the rest specify return ports
|
|
searchParams.m_numParams = 1;
|
|
searchParams.m_params[0].m_id = "Param2";
|
|
searchParams.m_params[0].SetValue(25);
|
|
searchParams.m_params[0].m_op = GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
|
|
EBUS_EVENT_ID_RESULT(m_lanSearch[numSessionsUsed],m_gridMates[numSessionsUsed],LANSessionServiceBus,StartGridSearch,searchParams);
|
|
}
|
|
|
|
int maxNumUpdates = 800;
|
|
m_numUpdates = 0;
|
|
TimeStamp time = AZStd::chrono::system_clock::now();
|
|
while (m_numUpdates <= maxNumUpdates)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_gridMates[i])
|
|
{
|
|
m_gridMates[i]->Update();
|
|
|
|
if (m_sessions[i])
|
|
{
|
|
UpdateReplicaManager(m_sessions[i]->GetReplicaMgr());
|
|
}
|
|
}
|
|
}
|
|
Update();
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_lanSearch[i] && m_lanSearch[i]->IsDone())
|
|
{
|
|
if (m_lanSearch[i]->GetNumResults() == 1)
|
|
{
|
|
carrierDesc.m_simulator = &m_simulators[i];
|
|
EBUS_EVENT_ID_RESULT(m_sessions[i],m_gridMates[i],LANSessionServiceBus,JoinSessionBySearchInfo,static_cast<const LANSearchInfo&>(*m_lanSearch[i]->GetResult(0)),JoinParams(),carrierDesc);
|
|
}
|
|
|
|
m_lanSearch[i]->Release();
|
|
m_lanSearch[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
// 2. We drop the host.
|
|
if (m_numUpdates == 150)
|
|
{
|
|
// Block 100% the host (simulate connection drop)
|
|
m_simulators[m_host].SetOutgoingPacketLoss(1, 1);
|
|
m_simulators[m_host].SetIncomingPacketLoss(1, 1);
|
|
m_simulators[m_host].Enable();
|
|
}
|
|
|
|
// 3. Shortly after the host (before host election has completed) we drop the next
|
|
// best host candidate (that everybody vote for) - this should trigger a re vote for the next it line.
|
|
if (m_numUpdates == 155)
|
|
{
|
|
GridMember* memberToDisconnect = m_sessions[m_host]->GetMemberByIndex(1);
|
|
int memberSession = -1;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i])
|
|
{
|
|
if (m_sessions[i]->GetMyMember()->GetId() == memberToDisconnect->GetId())
|
|
{
|
|
memberSession = i;
|
|
// Block 100% the host (simulate connection drop)
|
|
m_simulators[memberSession].SetOutgoingPacketLoss(1, 1);
|
|
m_simulators[memberSession].SetIncomingPacketLoss(1, 1);
|
|
m_simulators[memberSession].Enable();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
AZ_TEST_ASSERT(memberSession != -1);
|
|
}
|
|
|
|
// 4. Shortly after (while still in host election) we drop the next best host candidate,
|
|
// which should trigger another re vote.
|
|
if (m_numUpdates == 160)
|
|
{
|
|
GridMember* memberToDisconnect = m_sessions[m_host]->GetMemberByIndex(2);
|
|
int memberSession = -1;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i])
|
|
{
|
|
if (m_sessions[i]->GetMyMember()->GetId() == memberToDisconnect->GetId())
|
|
{
|
|
memberSession = i;
|
|
// Block 100% the host (simulate connection drop)
|
|
m_simulators[memberSession].SetOutgoingPacketLoss(1, 1);
|
|
m_simulators[memberSession].SetIncomingPacketLoss(1, 1);
|
|
m_simulators[memberSession].Enable();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
AZ_TEST_ASSERT(memberSession != -1);
|
|
}
|
|
|
|
// 5. We join a 2 new members to the session.
|
|
if (m_numUpdates == 600)
|
|
{
|
|
for (; numSessionsUsed < k_numMachines; ++numSessionsUsed)
|
|
{
|
|
//if(numSessionsUsed==host) continue;
|
|
|
|
LANSearchParams searchParams;
|
|
searchParams.m_serverPort = k_hostPort;
|
|
searchParams.m_listenPort = listenPort++;
|
|
searchParams.m_numParams = 1;
|
|
searchParams.m_params[0].m_id = "Param2";
|
|
searchParams.m_params[0].SetValue(25);
|
|
searchParams.m_params[0].m_op = GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
|
|
EBUS_EVENT_ID_RESULT(m_lanSearch[numSessionsUsed],m_gridMates[numSessionsUsed],LANSessionServiceBus,StartGridSearch,searchParams);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Debug Info
|
|
TimeStamp now = AZStd::chrono::system_clock::now();
|
|
if (AZStd::chrono::milliseconds(now - time).count() > 1000)
|
|
{
|
|
time = now;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (m_host == i)
|
|
{
|
|
AZ_Printf("GridMate", "------ Host %d ------\n", i);
|
|
}
|
|
else
|
|
{
|
|
AZ_Printf("GridMate", "------ Client %d ------\n", i);
|
|
}
|
|
|
|
AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_sessions[i]->GetId().c_str(), m_sessions[i]->GetNumberOfMembers(), m_sessions[i]->IsHost() ? "yes" : "no", m_sessions[i]->GetTime());
|
|
for (unsigned int iMember = 0; iMember < m_sessions[i]->GetNumberOfMembers(); ++iMember)
|
|
{
|
|
GridMember* member = m_sessions[i]->GetMemberByIndex(iMember);
|
|
AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no");
|
|
}
|
|
AZ_Printf("GridMate", "\n");
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
|
|
m_numUpdates++;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Simulating less common conditions (make one peer detect host disconnection and announce
|
|
* itself as the new host so quickly that other peer(s) reject the host migration).
|
|
* 1. We start a session with 3 members.
|
|
* 2. Terminate connection on next host candidate to the host.
|
|
* 2.1. Shortly after client 2 should receive new host announcement and reject the migration (disconnect from alleged new host)
|
|
* 2.2. New host terminates because nobody else followed it.
|
|
* 3. Add 2 new joins to the original session.
|
|
* Original session should remain fully operational with 4 members in it.
|
|
*/
|
|
class Integ_LANSessionMigarationTestTest3
|
|
: public SessionEventBus::Handler
|
|
, public GridMateMPTestFixture
|
|
{
|
|
static const int k_numInitialMembers = 3;
|
|
static const int k_numSecondMembers = 2;
|
|
static const int k_numMachines = k_numInitialMembers + k_numSecondMembers;
|
|
|
|
static const int k_hostPort = 5450;
|
|
|
|
GridSession* m_sessions[k_numMachines];
|
|
GridSearch* m_lanSearch[k_numMachines];
|
|
IGridMate* m_gridMates[k_numMachines];
|
|
DefaultSimulator m_simulators[k_numMachines];
|
|
|
|
int m_host;
|
|
int m_numUpdates;
|
|
public:
|
|
// Callback that notifies the title when a game search's query have completed.
|
|
void OnGridSearchComplete(GridSearch* gridSearch) override
|
|
{
|
|
AZ_TEST_ASSERT(gridSearch->IsDone() == true);
|
|
}
|
|
// Callback that notifies the title when a new player joins the game session.
|
|
void OnMemberJoined(GridSession* session, GridMember* member) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
}
|
|
// Callback that notifies the title that a player is leaving the game session. member pointer is NOT valid after the callback returns.
|
|
void OnMemberLeaving(GridSession* session, GridMember* member) override
|
|
{
|
|
(void)member;
|
|
if (session->GetNumberOfMembers() == 2) // if the last member (not us) is leaving kill the session!
|
|
{
|
|
session->Leave(false);
|
|
}
|
|
}
|
|
/// Callback that host decided to kick a member. You will receive a OnMemberLeaving when the actual member leaves the session.
|
|
void OnMemberKicked(GridSession* session, GridMember* member, AZ::u8 reason) override
|
|
{
|
|
(void)session;
|
|
(void)member;
|
|
(void)reason;
|
|
}
|
|
void OnSessionError(GridSession* session, const string& /*errorMsg*/) override
|
|
{
|
|
(void)session;
|
|
// On this test we will get a open port error because we have multiple hosts. This is ok, since we test migration here!
|
|
//AZ_TEST_ASSERT(false);
|
|
}
|
|
// Callback that notifies the title when a session will be left. session pointer is NOT valid after the callback returns.
|
|
void OnSessionDelete(GridSession* session) override
|
|
{
|
|
int i;
|
|
for (i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == session)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
AZ_TEST_ASSERT(i < k_numMachines);
|
|
m_sessions[i] = nullptr;
|
|
}
|
|
void OnMigrationStart(GridSession* session) override
|
|
{
|
|
(void)session;
|
|
AZ_TracePrintf("GridMate", "Migration start on %s at frame %d\n", session->GetMyMember()->GetId().ToAddress().c_str(), m_numUpdates);
|
|
}
|
|
void OnMigrationElectHost(GridSession* session, GridMember*& newHost) override
|
|
{
|
|
(void)session;
|
|
(void)newHost;
|
|
AZ_TracePrintf("GridMate", "Migration elect host on %s at frame %d\n", session->GetMyMember()->GetId().ToAddress().c_str(), m_numUpdates);
|
|
}
|
|
void OnMigrationEnd(GridSession* session, GridMember* newHost) override
|
|
{
|
|
(void)session;
|
|
(void)newHost;
|
|
AZ_TracePrintf("GridMate", "Migration end on %s, new host %s at frame %d\n", session->GetMyMember()->GetId().ToAddress().c_str(), session->GetHost()->GetId().ToAddress().c_str(), m_numUpdates);
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == session)
|
|
{
|
|
if (session->GetMyMember() == newHost)
|
|
{
|
|
m_host = i;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Integ_LANSessionMigarationTestTest3()
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Create all grid mates
|
|
m_gridMates[0] = m_gridMate;
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
GridMateDesc desc;
|
|
m_gridMates[i] = GridMateCreate(desc);
|
|
AZ_TEST_ASSERT(m_gridMates[i]);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Hook to session event bus.
|
|
SessionEventBus::Handler::BusConnect(m_gridMate);
|
|
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
// start the multiplayer service (session mgr, extra allocator, etc.)
|
|
StartGridMateService<LANSessionService>(m_gridMates[i], SessionServiceDesc());
|
|
AZ_TEST_ASSERT(LANSessionServiceBus::FindFirstHandler(m_gridMates[i]) != nullptr);
|
|
}
|
|
|
|
//StartDrilling("lanmigration2");
|
|
}
|
|
|
|
~Integ_LANSessionMigarationTestTest3() override
|
|
{
|
|
StopGridMateService<LANSessionService>(m_gridMates[0]);
|
|
|
|
for (int i = 1; i < k_numMachines; ++i)
|
|
{
|
|
if (m_gridMates[i])
|
|
{
|
|
GridMateDestroy(m_gridMates[i]);
|
|
}
|
|
}
|
|
|
|
// Unhook from session event bus.
|
|
SessionEventBus::Handler::BusDisconnect();
|
|
|
|
//StopDrilling();
|
|
}
|
|
|
|
void run()
|
|
{
|
|
TestCarrierDesc carrierDesc;
|
|
carrierDesc.m_enableDisconnectDetection = /*false*/ true;
|
|
carrierDesc.m_threadUpdateTimeMS = 10;
|
|
carrierDesc.m_simulator = &m_simulators[0];
|
|
|
|
m_host = 1; // first we set the second session/machine to be the host (so we can test non zero host)
|
|
|
|
// 1. We start a session with 3 members.
|
|
memset(m_sessions, 0, sizeof(m_sessions));
|
|
LANSessionParams sp;
|
|
sp.m_topology = ST_PEER_TO_PEER;
|
|
sp.m_numPublicSlots = 64;
|
|
sp.m_port = k_hostPort;
|
|
sp.m_numParams = 2;
|
|
sp.m_params[0].m_id = "Param1";
|
|
sp.m_params[0].SetValue(15);
|
|
sp.m_params[1].m_id = "Param2";
|
|
sp.m_params[1].SetValue(25);
|
|
EBUS_EVENT_ID_RESULT(m_sessions[m_host],m_gridMates[m_host],LANSessionServiceBus,HostSession,sp,carrierDesc);
|
|
|
|
memset(m_lanSearch, 0, sizeof(m_lanSearch));
|
|
int listenPort = k_hostPort;
|
|
int numSessionsUsed = 0;
|
|
for (; numSessionsUsed < k_numInitialMembers; ++numSessionsUsed)
|
|
{
|
|
if (numSessionsUsed == m_host)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LANSearchParams searchParams;
|
|
searchParams.m_serverPort = k_hostPort;
|
|
searchParams.m_listenPort = listenPort == k_hostPort ? 0 : ++listenPort; // first client will use ephemeral port
|
|
// the rest specify return ports
|
|
searchParams.m_numParams = 1;
|
|
searchParams.m_params[0].m_id = "Param2";
|
|
searchParams.m_params[0].SetValue(25);
|
|
searchParams.m_params[0].m_op = GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
|
|
EBUS_EVENT_ID_RESULT(m_lanSearch[numSessionsUsed],m_gridMates[numSessionsUsed],LANSessionServiceBus,StartGridSearch,searchParams);
|
|
}
|
|
|
|
int maxNumUpdates = 600;
|
|
m_numUpdates = 0;
|
|
TimeStamp time = AZStd::chrono::system_clock::now();
|
|
while (m_numUpdates <= maxNumUpdates)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_gridMates[i])
|
|
{
|
|
m_gridMates[i]->Update();
|
|
|
|
if (m_sessions[i])
|
|
{
|
|
UpdateReplicaManager(m_sessions[i]->GetReplicaMgr());
|
|
}
|
|
}
|
|
}
|
|
Update();
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_lanSearch[i] && m_lanSearch[i]->IsDone())
|
|
{
|
|
if (m_lanSearch[i]->GetNumResults() == 1)
|
|
{
|
|
carrierDesc.m_simulator = &m_simulators[i];
|
|
EBUS_EVENT_ID_RESULT(m_sessions[i],m_gridMates[i],LANSessionServiceBus,JoinSessionBySearchInfo,static_cast<const LANSearchInfo&>(*m_lanSearch[i]->GetResult(0)),JoinParams(),carrierDesc);
|
|
}
|
|
|
|
m_lanSearch[i]->Release();
|
|
m_lanSearch[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
// 2. Terminate connection on next host candidate to the host.
|
|
if (m_numUpdates == 150)
|
|
{
|
|
GridMember* memberToDisconnect = m_sessions[m_host]->GetMemberByIndex(1);
|
|
int memberSession = -1;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i])
|
|
{
|
|
if (m_sessions[i]->GetMyMember()->GetId() == memberToDisconnect->GetId())
|
|
{
|
|
memberSession = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
AZ_TEST_ASSERT(memberSession != -1);
|
|
for (unsigned int i = 0; i < m_sessions[memberSession]->GetNumberOfMembers(); i++)
|
|
{
|
|
GridMember* member = m_sessions[memberSession]->GetMemberByIndex(i);
|
|
if (member->GetId() == m_sessions[m_host]->GetMyMember()->GetId())
|
|
{
|
|
// we found the host member, kill the connection to it! (so we detect it first)
|
|
m_sessions[memberSession]->GetCarrier()->DebugDeleteConnection(member->GetConnectionId());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Add 2 new joins to the original session.
|
|
if (m_numUpdates == 400)
|
|
{
|
|
for (; numSessionsUsed < k_numMachines; ++numSessionsUsed)
|
|
{
|
|
//if(numSessionsUsed==host) continue;
|
|
|
|
LANSearchParams searchParams;
|
|
searchParams.m_serverPort = k_hostPort;
|
|
searchParams.m_listenPort = listenPort++;
|
|
searchParams.m_numParams = 1;
|
|
searchParams.m_params[0].m_id = "Param2";
|
|
searchParams.m_params[0].SetValue(25);
|
|
searchParams.m_params[0].m_op = GridSessionSearchOperators::SSO_OPERATOR_EQUAL;
|
|
EBUS_EVENT_ID_RESULT(m_lanSearch[numSessionsUsed],m_gridMates[numSessionsUsed],LANSessionServiceBus,StartGridSearch,searchParams);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Debug Info
|
|
TimeStamp now = AZStd::chrono::system_clock::now();
|
|
if (AZStd::chrono::milliseconds(now - time).count() > 1000)
|
|
{
|
|
time = now;
|
|
for (int i = 0; i < k_numMachines; ++i)
|
|
{
|
|
if (m_sessions[i] == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (m_host == i)
|
|
{
|
|
AZ_Printf("GridMate", "------ Host %d ------\n", i);
|
|
}
|
|
else
|
|
{
|
|
AZ_Printf("GridMate", "------ Client %d ------\n", i);
|
|
}
|
|
|
|
AZ_Printf("GridMate", "Session %s Members: %d Host: %s Clock: %d\n", m_sessions[i]->GetId().c_str(), m_sessions[i]->GetNumberOfMembers(), m_sessions[i]->IsHost() ? "yes" : "no", m_sessions[i]->GetTime());
|
|
for (unsigned int iMember = 0; iMember < m_sessions[i]->GetNumberOfMembers(); ++iMember)
|
|
{
|
|
GridMember* member = m_sessions[i]->GetMemberByIndex(iMember);
|
|
AZ_Printf("GridMate", " Member: %s(%s) Host: %s Local: %s\n", member->GetName().c_str(), member->GetId().ToString().c_str(), member->IsHost() ? "yes" : "no", member->IsLocal() ? "yes" : "no");
|
|
}
|
|
AZ_Printf("GridMate", "\n");
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(30));
|
|
m_numUpdates++;
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
GM_TEST_SUITE(SessionSuite)
|
|
GM_TEST(Integ_LANSessionMatchmakingParamsTest)
|
|
GM_TEST(Integ_LANSessionTest)
|
|
#if (AZ_TRAIT_GRIDMATE_TEST_SOCKET_IPV6_SUPPORT_ENABLED)
|
|
GM_TEST(Integ_LANSessionTestIPv6)
|
|
#endif
|
|
GM_TEST(Integ_LANMultipleSessionTest)
|
|
GM_TEST(Integ_LANLatencySessionTest)
|
|
|
|
// Manually enabled tests (require 2+ machines and online services)
|
|
//GM_TEST(LANSessionMigarationTestTest)
|
|
//GM_TEST(LANSessionMigarationTestTest2)
|
|
//GM_TEST(LANSessionMigarationTestTest3)
|
|
|
|
GM_TEST_SUITE_END()
|