/* * 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 #include "Multiplayer/GridMateServiceWrapper/GridMateLANServiceWrapper.h" #include #include // 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("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("gm_ipversion", 1); carrierDesc.m_version = gridMateServiceParams.m_version; carrierDesc.m_enableDisconnectDetection = !!gridMateServiceParams.FetchValueOrDefault("gm_disconnectDetection", 1); carrierDesc.m_disconnectDetectionRttThreshold = gridMateServiceParams.FetchValueOrDefault("gm_disconnectDetectionRttThreshold", 500.0f); carrierDesc.m_disconnectDetectionPacketLossThreshold = gridMateServiceParams.FetchValueOrDefault("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(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 m_gridMateServiceWrapper; AZStd::string m_securityString; }; ////////////////////////////////////////////////////////////////////////// // GridMateSystemContext namespace GridMateSystemContext { void Reflect(AZ::ReflectContext* reflectContext) { AZ::SerializeContext* serializeContext = azrtti_cast(reflectContext); if (serializeContext) { serializeContext->Class() ->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(reflectContext); if (behaviorContext) { behaviorContext->Class("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(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") ->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(value); }); ; behaviorContext->EBus("SessionManagerBus") ->Handler() ->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("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("gm_ipversion", 1); carrierDesc.m_version = gridMateServiceParams.m_version; carrierDesc.m_enableDisconnectDetection = !!gridMateServiceParams.FetchValueOrDefault("gm_disconnectDetection", 1); if (carrierDesc.m_enableDisconnectDetection) { carrierDesc.m_disconnectDetectionRttThreshold = gridMateServiceParams.FetchValueOrDefault("gm_disconnectDetectionRttThreshold", 500.0f); carrierDesc.m_disconnectDetectionPacketLossThreshold = gridMateServiceParams.FetchValueOrDefault("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 };