Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h (133 lines of code) (raw):

/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #pragma once #include <aws/gamelift/server/GameLiftServerAPI.h> #include <aws/gamelift/server/model/GameSession.h> #include <AzCore/JSON/rapidjson.h> #include <AzCore/JSON/document.h> #include <AzCore/std/containers/vector.h> #include <AzCore/std/string/string.h> #include <AzCore/std/smart_ptr/unique_ptr.h> #include <Multiplayer/Session/ISessionHandlingRequests.h> #include <Multiplayer/Session/SessionConfig.h> #include <AWSGameLiftPlayer.h> #include <Request/AWSGameLiftServerRequestBus.h> namespace AWSGameLift { class GameLiftServerSDKWrapper; //! GameLift server process settings. struct GameLiftServerProcessDesc { AZStd::vector<AZStd::string> m_logPaths; //!< Log paths the servers will write to. Both relative to the game root folder and absolute paths supported. uint16_t m_port = 0; //!< The port the server will be listening on. }; //! Manage the server process for hosting game sessions via GameLiftServerSDK. class AWSGameLiftServerManager : public AWSGameLiftServerRequestBus::Handler , public Multiplayer::ISessionHandlingProviderRequests { public: static constexpr const char AWSGameLiftServerManagerName[] = "AWSGameLiftServerManager"; static constexpr const char AWSGameLiftServerSDKNotInitErrorMessage[] = "Amazon GameLift Server SDK is not initialized yet."; static constexpr const char AWSGameLiftServerSDKAlreadyInitErrorMessage[] = "Amazon GameLift Server SDK has already been initialized."; static constexpr const char AWSGameLiftServerTempPortErrorMessage[] = "No server port specified, server will be listening on ephemeral port."; static constexpr const char AWSGameLiftServerGameInitErrorMessage[] = "Failed to process game dependent initialization during OnStartGameSession."; static constexpr const char AWSGameLiftServerGameSessionDestroyErrorMessage[] = "Failed to destroy game session during OnProcessTerminate."; static constexpr const char AWSGameLiftServerPlayerConnectionRegisteredErrorMessage[] = "Player connection id %d is already registered to player session id %s. Remove connected player first."; static constexpr const char AWSGameLiftServerPlayerConnectionMissingErrorMessage[] = "Player connection id %d does not exist."; static constexpr const char AWSGameLiftServerInstanceCertificateErrorMessage[] = "Failed to locate Amazon GameLift TLS certificate file."; static constexpr const char AWSGameLiftServerInstancePrivateKeyErrorMessage[] = "Failed to locate Amazon GameLift TLS private key file."; static constexpr const char AWSGameLiftServerInitSDKErrorMessage[] = "Failed to initialize Amazon GameLift Server SDK. ErrorMessage: %s"; static constexpr const char AWSGameLiftServerProcessReadyErrorMessage[] = "Failed to notify GameLift server process ready. ErrorMessage: %s"; static constexpr const char AWSGameLiftServerActivateGameSessionErrorMessage[] = "Failed to activate GameLift game session. ErrorMessage: %s"; static constexpr const char AWSGameLiftServerProcessEndingErrorMessage[] = "Failed to notify GameLift server process ending. ErrorMessage: %s"; static constexpr const char AWSGameLiftServerAcceptPlayerSessionErrorMessage[] = "Failed to validate player session connection with id %s. ErrorMessage: %s"; static constexpr const char AWSGameLiftServerInvalidConnectionConfigErrorMessage[] = "Invalid player connection config, player connection id: %d, player session id: %s"; static constexpr const char AWSGameLiftServerRemovePlayerSessionErrorMessage[] = "Failed to notify GameLift that the player with the player session id %s has disconnected from the server process. ErrorMessage: %s"; static constexpr const char AWSGameLiftMatchmakingDataInvalidErrorMessage[] = "Failed to parse GameLift matchmaking data. ErrorMessage: %s"; static constexpr const char AWSGameLiftMatchmakingDataMissingErrorMessage[] = "GameLift matchmaking data is missing or invalid to parse."; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeInvalidErrorMessage[] = "Failed to build player %s attributes. ErrorMessage: %s"; static constexpr const char AWSGameLiftDescribePlayerSessionsErrorMessage[] = "Failed to describe player sessions. ErrorMessage: %s"; static constexpr const char AWSGameLiftStartMatchBackfillErrorMessage[] = "Failed to start match backfill. ErrorMessage: %s"; static constexpr const char AWSGameLiftStopMatchBackfillErrorMessage[] = "Failed to stop match backfill. ErrorMessage: %s"; static constexpr const char AWSGameLiftMatchmakingConfigurationKeyName[] = "matchmakingConfigurationArn"; static constexpr const char AWSGameLiftMatchmakingTeamsKeyName[] = "teams"; static constexpr const char AWSGameLiftMatchmakingTeamNameKeyName[] = "name"; static constexpr const char AWSGameLiftMatchmakingPlayersKeyName[] = "players"; static constexpr const char AWSGameLiftMatchmakingPlayerIdKeyName[] = "playerId"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributesKeyName[] = "attributes"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeTypeKeyName[] = "attributeType"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeValueKeyName[] = "valueAttribute"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSTypeName[] = "S"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSServerTypeName[] = "STRING"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeNTypeName[] = "N"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeNServerTypeName[] = "DOUBLE"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSLTypeName[] = "SL"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSLServerTypeName[] = "STRING_LIST"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSDMTypeName[] = "SDM"; static constexpr const char AWSGameLiftMatchmakingPlayerAttributeSDMServerTypeName[] = "STRING_DOUBLE_MAP"; static constexpr const uint16_t AWSGameLiftDescribePlayerSessionsPageSize = 30; AWSGameLiftServerManager(); virtual ~AWSGameLiftServerManager(); void ActivateManager(); void DeactivateManager(); //! Initialize GameLift API client by calling InitSDK(). void InitializeGameLiftServerSDK(); // AWSGameLiftServerRequestBus interface implementation bool NotifyGameLiftProcessReady() override; bool StartMatchBackfill(const AZStd::string& ticketId, const AZStd::vector<AWSGameLiftPlayer>& players) override; bool StopMatchBackfill(const AZStd::string& ticketId) override; // ISessionHandlingProviderRequests interface implementation void HandleDestroySession() override; bool ValidatePlayerJoinSession(const Multiplayer::PlayerConnectionConfig& playerConnectionConfig) override; void HandlePlayerLeaveSession(const Multiplayer::PlayerConnectionConfig& playerConnectionConfig) override; AZ::IO::Path GetInternalSessionCertificate() override; AZ::IO::Path GetExternalSessionCertificate() override; protected: void SetGameLiftServerSDKWrapper(AZStd::unique_ptr<GameLiftServerSDKWrapper> gameLiftServerSDKWrapper); //! Add connected player session id. bool AddConnectedPlayer(const Multiplayer::PlayerConnectionConfig& playerConnectionConfig); //! Get active server player data from lazy loaded game session for server match backfill AZStd::vector<AWSGameLiftPlayer> GetActiveServerMatchBackfillPlayers(); //! Update local game session data to latest one void UpdateGameSessionData(const Aws::GameLift::Server::Model::GameSession& gameSession); private: //! Build the serverProcessDesc with appropriate server port number and log paths. GameLiftServerProcessDesc BuildGameLiftServerProcessDesc(); //! Build active server player data from lazy loaded game session based on player id bool BuildActiveServerMatchBackfillPlayer(const AZStd::string& playerId, AWSGameLiftPlayer& outPlayer); //! Build server player attribute data from lazy load matchmaking data void BuildServerMatchBackfillPlayerAttributes(const rapidjson::Value& playerAttributes, AWSGameLiftPlayer& outPlayer); //! Build server player data for server match backfill bool BuildServerMatchBackfillPlayer(const AWSGameLiftPlayer& player, Aws::GameLift::Server::Model::Player& outBackfillPlayer); //! Build start match backfill request for StartMatchBackfill operation bool BuildStartMatchBackfillRequest( const AZStd::string& ticketId, const AZStd::vector<AWSGameLiftPlayer>& players, Aws::GameLift::Server::Model::StartMatchBackfillRequest& outRequest); //! Build stop match backfill request for StopMatchBackfill operation void BuildStopMatchBackfillRequest(const AZStd::string& ticketId, Aws::GameLift::Server::Model::StopMatchBackfillRequest& outRequest); //! Build session config by using AWS GameLift Server GameSession Model. Multiplayer::SessionConfig BuildSessionConfig(const Aws::GameLift::Server::Model::GameSession& gameSession); //! Check whether matchmaking data is in proper format bool IsMatchmakingDataValid(); //! Fetch active player sessions in game session. AZStd::vector<Aws::GameLift::Server::Model::PlayerSession> GetActivePlayerSessions(); //! Callback function that the GameLift service invokes to activate a new game session. void OnStartGameSession(const Aws::GameLift::Server::Model::GameSession& gameSession); //! Callback function that the GameLift service invokes to pass an updated game session object to the server process. void OnUpdateGameSession(const Aws::GameLift::Server::Model::UpdateGameSession& updateGameSession); //! Callback function that the server process or GameLift service invokes to force the server process to shut down. void OnProcessTerminate(); //! Callback function that the GameLift service invokes to request a health status report from the server process. //! @return Whether the server process is healthy. bool OnHealthCheck(); //! Remove connected player session id. //! @param playerConnectionId Connection id of the player to remove. //! @param outPlayerSessionId Session id of the removed player. Empty if the player cannot be removed. //! @return Whether the player is removed successfully. bool RemoveConnectedPlayer(uint32_t playerConnectionId, AZStd::string& outPlayerSessionId); AZStd::unique_ptr<GameLiftServerSDKWrapper> m_gameLiftServerSDKWrapper; bool m_serverSDKInitialized; AZStd::mutex m_gameliftMutex; using PlayerConnectionId = uint32_t; using PlayerSessionId = AZStd::string; AZStd::unordered_map<PlayerConnectionId, PlayerSessionId> m_connectedPlayers; // Lazy loaded game session and matchmaking data Aws::GameLift::Server::Model::GameSession m_gameSession; // Matchmaking data contains a unique match ID, it identifies the matchmaker that created the match // and describes the teams, team assignments, and players. // Reference https://docs.aws.amazon.com/gamelift/latest/flexmatchguide/match-server.html#match-server-data rapidjson::Document m_matchmakingData; }; } // namespace AWSGameLift