Files
o3de/Gems/Multiplayer/Code/Source/BehaviorContext/GridSystemContext.cpp
T
2021-03-08 14:30:57 -08:00

428 lines
18 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 "Multiplayer_precompiled.h"
#include "Multiplayer/BehaviorContext/GridSystemContext.h"
#include "Multiplayer/BehaviorContext/GridSearchContext.h"
#include <GridMate/NetworkGridMate.h>
#include "Multiplayer/GridMateServiceWrapper/GridMateLANServiceWrapper.h"
#include <Multiplayer_Traits_Platform.h>
#include <Multiplayer_GridMateServiceWrapper_Platform.h>
// Forward declarations for platform operations
namespace Platform
{
bool FetchParam(const char* key, const Multiplayer::SessionDesc& sessionDesc, GridMate::GridSessionParam& p);
}
#include "Multiplayer/MultiplayerUtils.h"
namespace Multiplayer
{
/**
* used to capture SessionEventBus and MultiplayerLobbyBus bus events for this behavior to forward onto the SessionManagerHandler
*/
struct SessionManagerHandlerImpl
: public GridMate::SessionEventBus::Handler
{
SessionManagerHandlerImpl()
: m_gridMate(nullptr)
{
if (gEnv->pNetwork)
{
m_gridMate = gEnv->pNetwork->GetGridMate();
}
}
~SessionManagerHandlerImpl()
{
Disconnect();
}
void Connect(SessionDesc sessionDesc)
{
m_sessionDesc = sessionDesc;
if (m_gridMate)
{
GridMate::SessionEventBus::Handler::BusConnect(m_gridMate);
}
}
void Disconnect()
{
GridMate::SessionEventBus::Handler::BusDisconnect();
}
//
// GridMate::SessionEventBus::Handler
//
void OnSessionCreated(GridMate::GridSession* session) override
{
(void)session;
}
void OnSessionStart(GridMate::GridSession* session) override
{
EBUS_EVENT(SessionManagerBus, OnHostSessionStarted, session);
}
void OnSessionEnd(GridMate::GridSession* session) override
{
(void)session;
}
void OnSessionDelete(GridMate::GridSession* session) override
{
(void)session;
}
void OnSessionError(GridMate::GridSession* session, const GridMate::string& errorMsg) override
{
(void)session;
(void)errorMsg;
}
//
// shared resource(s)
//
GridMate::IGridMate* GetGridMate() const
{
AZ_Assert(m_gridMate, "IGridMate missing.");
return m_gridMate;
}
private:
GridMate::IGridMate* m_gridMate;
SessionDesc m_sessionDesc;
};
/**
* The high level bus manager for GridMate Lua behaviors
*/
class SessionManagerHandler
: public SessionManagerBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
AZ_EBUS_BEHAVIOR_BINDER(SessionManagerHandler, "{97F049D6-1C49-4661-A88C-1AE63E0554B3}", AZ::SystemAllocator
, StartHost
, Close
, OnHostSessionStarted
);
//
// SessionManagerBus::Handler
//
bool StartHost(const SessionDesc& sessionDesc) override
{
m_sessionDesc = sessionDesc;
Close();
m_gridMateSessionHandler.Connect(m_sessionDesc);
m_gridMateServiceWrapper.reset(GridMateSystemContext::RegisterServiceWrapper(m_sessionDesc.m_serviceType));
if (m_gridMateServiceWrapper)
{
if (m_gridMateServiceWrapper->StartSessionService(m_gridMateSessionHandler.GetGridMate()))
{
return CreateServerForWrappedService(sessionDesc);
}
}
return false;
}
bool Close() override
{
// disable further EBus communications with GridMate::SessionEventBus::Handler or MultiplayerLobbyBus::Handler
m_gridMateSessionHandler.Disconnect();
// handle GridMate resources
if (m_gridMateServiceWrapper)
{
m_gridMateServiceWrapper->StopSessionService(m_gridMateSessionHandler.GetGridMate());
m_gridMateServiceWrapper.reset(nullptr);
return true;
}
return false;
}
void OnHostSessionStarted(GridMate::GridSession* session) override
{
Call(FN_OnHostSessionStarted, session);
}
protected:
void InitCarrierDesc(GridMate::CarrierDesc& carrierDesc, const GridMateServiceParams& gridMateServiceParams)
{
if (!carrierDesc.m_simulator)
{
EBUS_EVENT_RESULT(carrierDesc.m_simulator, Multiplayer::MultiplayerRequestBus, GetSimulator);
}
m_securityString = gridMateServiceParams.FetchString("gm_securityData");
carrierDesc.m_port = gridMateServiceParams.FetchValueOrDefault<int>("cl_clientport", 0);
carrierDesc.m_connectionTimeoutMS = 10000;
carrierDesc.m_threadUpdateTimeMS = 30;
carrierDesc.m_threadInstantResponse = true;
carrierDesc.m_driverIsCrossPlatform = true;
carrierDesc.m_securityData = m_securityString.c_str();
carrierDesc.m_familyType = gridMateServiceParams.FetchValueOrDefault<int>("gm_ipversion", 1);
carrierDesc.m_version = gridMateServiceParams.m_version;
carrierDesc.m_enableDisconnectDetection = !!gridMateServiceParams.FetchValueOrDefault<int>("gm_disconnectDetection", 1);
carrierDesc.m_disconnectDetectionRttThreshold = gridMateServiceParams.FetchValueOrDefault<float>("gm_disconnectDetectionRttThreshold", 500.0f);
carrierDesc.m_disconnectDetectionPacketLossThreshold = gridMateServiceParams.FetchValueOrDefault<float>("gm_disconnectDetectionPacketLossThreshold", 0.3f);
#if AZ_TRAIT_MULTIPLAYER_ASSIGN_NETWORK_FAMILY
#if AZ_TRAIT_MULTIPLAYER_GRID_SYSTEM_CHECK_SECURITY_DATA_ENABLE
AZ_TRAIT_MULTIPLAYER_GRID_SYSTEM_CHECK_SECURITY_DATA(AZ_TRAIT_MULTIPLAYER_SESSION_NAME, AZ_TRAIT_MULTIPLAYER_GRID_SYSTEM_CHECK_SECURITY_DATA_MESSAGE);
#endif
AZ_Error(AZ_TRAIT_MULTIPLAYER_SESSION_NAME, carrierDesc.m_familyType == AZ_TRAIT_MULTIPLAYER_ADDRESS_TYPE, AZ_TRAIT_MULTIPLAYER_DRIVER_MESSAGE);
carrierDesc.m_familyType = AZ_TRAIT_MULTIPLAYER_ADDRESS_TYPE;
#endif
}
bool CreateServerForWrappedService([[maybe_unused]] const SessionDesc& sessionDesc)
{
GridMate::GridSession* gridSession = nullptr;
EBUS_EVENT_RESULT(gridSession, Multiplayer::MultiplayerRequestBus, GetSession);
if (!gridSession)
{
GridMate::IGridMate* gridMate = gEnv->pNetwork->GetGridMate();
GridMate::SessionParams sessionParams;
sessionParams.m_topology = GridMate::ST_CLIENT_SERVER;
sessionParams.m_numPublicSlots = m_sessionDesc.m_maxPlayerSlots + (gEnv->IsDedicated() ? 1 : 0); // One slot for server member.
sessionParams.m_numPrivateSlots = 0;
sessionParams.m_peerToPeerTimeout = 60000;
sessionParams.m_flags = 0;
sessionParams.m_numParams = 0;
sessionParams.m_params[sessionParams.m_numParams].m_id = "sv_name";
sessionParams.m_params[sessionParams.m_numParams].SetValue(m_sessionDesc.m_serverName.c_str());
sessionParams.m_numParams++;
sessionParams.m_params[sessionParams.m_numParams].m_id = "sv_map";
sessionParams.m_params[sessionParams.m_numParams].SetValue(m_sessionDesc.m_mapName.c_str());
sessionParams.m_numParams++;
std::string securityString;
auto fetchParams = [&](const char* param)
{
GridMate::GridSessionParam p;
if (!strcmp(param, "cl_clientport"))
{
p.SetValue(m_sessionDesc.m_gamePort);
}
else if (!strcmp(param, "gm_ipversion"))
{
p.SetValue(AZ_TRAIT_MULTIPLAYER_ADDRESS_TYPE);
}
return p;
};
GridMateServiceParams gridMateServiceParams(sessionParams, fetchParams);
GridMate::CarrierDesc carrierDesc;
InitCarrierDesc(carrierDesc, gridMateServiceParams);
Multiplayer::NetSec::ConfigureCarrierDescForHost(carrierDesc);
carrierDesc.m_port = m_sessionDesc.m_gamePort;
carrierDesc.m_enableDisconnectDetection = m_sessionDesc.m_enableDisconnectDetection;
carrierDesc.m_connectionTimeoutMS = m_sessionDesc.m_connectionTimeoutMS;
carrierDesc.m_threadUpdateTimeMS = m_sessionDesc.m_threadUpdateTimeMS;
GridMate::GridSession* session = m_gridMateServiceWrapper->CreateServer(gridMate, carrierDesc, gridMateServiceParams);
if (session == nullptr)
{
Multiplayer::NetSec::OnSessionFailedToCreate(carrierDesc);
EBUS_EVENT(GridMate::SessionEventBus, OnSessionError, gridSession, "Error while hosting Session.");
}
else
{
EBUS_EVENT(Multiplayer::MultiplayerRequestBus, RegisterSession, session);
}
}
else
{
EBUS_EVENT(GridMate::SessionEventBus, OnSessionError, gridSession, "Invalid Gem Session");
return false;
}
return true;
}
SessionDesc m_sessionDesc;
SessionManagerHandlerImpl m_gridMateSessionHandler;
AZStd::unique_ptr<GridMateServiceWrapper> m_gridMateServiceWrapper;
AZStd::string m_securityString;
};
//////////////////////////////////////////////////////////////////////////
// GridMateSystemContext
namespace GridMateSystemContext
{
void Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<SessionDesc>()
->Version(1)
->Field("Port", &SessionDesc::m_gamePort)
->Field("MaxPlayerSlots", &SessionDesc::m_maxPlayerSlots)
->Field("EnableDisconnectDetection", &SessionDesc::m_enableDisconnectDetection)
->Field("ConnectionTimeoutMS", &SessionDesc::m_connectionTimeoutMS)
->Field("ThreadUpdateTimeMS", &SessionDesc::m_threadUpdateTimeMS)
->Field("MapName", &SessionDesc::m_mapName)
->Field("ServerName", &SessionDesc::m_serverName)
->Field("ServiceType", &SessionDesc::m_serviceType)
;
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(reflectContext);
if (behaviorContext)
{
behaviorContext->Class<GridMate::ServiceType>("GridServiceType")
->Enum<(int)GridMate::ST_LAN>("LAN")
#if defined(AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS)
#define AZ_RESTRICTED_PLATFORM_EXPANSION(CodeName, CODENAME, codename, PrivateName, PRIVATENAME, privatename, PublicName, PUBLICNAME, publicname, PublicAuxName1, PublicAuxName2, PublicAuxName3)\
->Enum<GridMate::ST_##CODENAME>(PublicAuxName2)
AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS
#undef AZ_RESTRICTED_PLATFORM_EXPANSION
#endif
->Enum<(int)GridMate::ST_STEAM>("Steam")
;
// expose the parameters for a GridSession
behaviorContext->Class<SessionDesc>("SessionDesc")
->Property("gamePort", BehaviorValueProperty(&SessionDesc::m_gamePort))
->Property("mapName", BehaviorValueProperty(&SessionDesc::m_mapName))
->Property("maxPlayerSlots", BehaviorValueProperty(&SessionDesc::m_maxPlayerSlots))
->Property("serverName", BehaviorValueProperty(&SessionDesc::m_serverName))
->Property("enableDisconnectDetection", BehaviorValueProperty(&SessionDesc::m_enableDisconnectDetection))
->Property("connectionTimeoutMS", BehaviorValueProperty(&SessionDesc::m_connectionTimeoutMS))
->Property("threadUpdateTimeMS", BehaviorValueProperty(&SessionDesc::m_threadUpdateTimeMS))
->Property("serviceType", [](SessionDesc* that) -> int { return that->m_serviceType; }, [](SessionDesc* that, int value) { that->m_serviceType = static_cast<GridMate::ServiceType>(value); });
;
behaviorContext->EBus<SessionManagerBus>("SessionManagerBus")
->Handler<SessionManagerHandler>()
->Event("StartHost", &SessionManagerBus::Events::StartHost)
->Event("Close", &SessionManagerBus::Events::Close)
;
GridSearchBehavior::Reflect(behaviorContext);
}
}
// STATIC
bool FetchParam(const char * key, SessionDesc sessionDesc, GridMate::GridSessionParam& p)
{
if (!strcmp(key, "cl_clientport"))
{
if (sessionDesc.m_gamePort == 0)
{
if (gEnv && gEnv->pConsole && gEnv->pConsole->GetCVar("cl_clientport"))
{
p.SetValue(gEnv->pConsole->GetCVar("cl_clientport")->GetIVal());
}
}
else
{
p.SetValue(sessionDesc.m_gamePort);
}
}
else if (!strcmp(key, "gm_ipversion"))
{
p.SetValue(AZ_TRAIT_MULTIPLAYER_ADDRESS_TYPE);
}
else if (!strcmp(key, "gm_disconnectDetection"))
{
p.SetValue(sessionDesc.m_enableDisconnectDetection);
}
else if (!strcmp(key, "gm_disconnectDetectionRttThreshold"))
{
if (gEnv && gEnv->pConsole && gEnv->pConsole->GetCVar("gm_disconnectDetectionRttThreshold"))
{
p.SetValue(gEnv->pConsole->GetCVar("gm_disconnectDetectionRttThreshold")->GetFVal());
}
}
else if (!strcmp(key, "gm_disconnectDetectionPacketLossThreshold"))
{
if (gEnv && gEnv->pConsole && gEnv->pConsole->GetCVar("gm_disconnectDetectionPacketLossThreshold"))
{
p.SetValue(gEnv->pConsole->GetCVar("gm_disconnectDetectionPacketLossThreshold")->GetFVal());
}
}
else if (Platform::FetchParam(key, sessionDesc, p))
{
return true;
}
return !p.m_value.empty();
}
void InitCarrierDesc(const GridMateServiceParams& gridMateServiceParams, GridMate::CarrierDesc& carrierDesc)
{
if (!carrierDesc.m_simulator)
{
EBUS_EVENT_RESULT(carrierDesc.m_simulator, Multiplayer::MultiplayerRequestBus, GetSimulator);
}
carrierDesc.m_port = gridMateServiceParams.FetchValueOrDefault<int>("cl_clientport", 0);
carrierDesc.m_connectionTimeoutMS = 10000;
carrierDesc.m_threadUpdateTimeMS = 30;
carrierDesc.m_threadInstantResponse = true;
carrierDesc.m_driverIsCrossPlatform = true;
carrierDesc.m_securityData = gridMateServiceParams.FetchString("gm_securityData").c_str();
carrierDesc.m_familyType = gridMateServiceParams.FetchValueOrDefault<int>("gm_ipversion", 1);
carrierDesc.m_version = gridMateServiceParams.m_version;
carrierDesc.m_enableDisconnectDetection = !!gridMateServiceParams.FetchValueOrDefault<int>("gm_disconnectDetection", 1);
if (carrierDesc.m_enableDisconnectDetection)
{
carrierDesc.m_disconnectDetectionRttThreshold = gridMateServiceParams.FetchValueOrDefault<float>("gm_disconnectDetectionRttThreshold", 500.0f);
carrierDesc.m_disconnectDetectionPacketLossThreshold = gridMateServiceParams.FetchValueOrDefault<float>("gm_disconnectDetectionPacketLossThreshold", 0.3f);
}
#if AZ_TRAIT_MULTIPLAYER_ASSIGN_NETWORK_FAMILY
#if AZ_TRAIT_MULTIPLAYER_GRID_SYSTEM_CHECK_SECURITY_DATA_ENABLE
AZ_TRAIT_MULTIPLAYER_GRID_SYSTEM_CHECK_SECURITY_DATA(AZ_TRAIT_MULTIPLAYER_SESSION_NAME, AZ_TRAIT_MULTIPLAYER_GRID_SYSTEM_CHECK_SECURITY_DATA_MESSAGE);
#endif
AZ_Error(AZ_TRAIT_MULTIPLAYER_SESSION_NAME, carrierDesc.m_familyType == AZ_TRAIT_MULTIPLAYER_ADDRESS_TYPE, AZ_TRAIT_MULTIPLAYER_DRIVER_MESSAGE);
carrierDesc.m_familyType = AZ_TRAIT_MULTIPLAYER_ADDRESS_TYPE;
#endif
}
/**
* Helper function to translate GridMate::ServiceType to the proper MultiplayerLobbyServiceWrapper
*/
GridMateServiceWrapper* RegisterServiceWrapper(GridMate::ServiceType gridServiceType)
{
switch (gridServiceType)
{
case GridMate::ST_LAN:
return aznew GridMateLANServiceWrapper();
#if AZ_TRAIT_MULTIPLAYER_GRID_SYSTEM_HAS_PLATFORM_SERVICE_WRAPPER
case GridMate::AZ_TRAIT_MULTIPLAYER_GRIDMATE_SERVICE_TYPE_ENUM:
return aznew AZ_TRAIT_MULTIPLAYER_GRID_SYSTEM_HAS_PLATFORM_SERVICE_TYPE_CLASS();
#endif
default:
AZ_Assert(false, "Unsupported GridMate::ServiceType of %d", gridServiceType);
}
return nullptr;
}
} // namespace GridMateSystemContext
};