/** *----------------------------------------------------------------------------- * FILE : NetServer.h * PROJECT : 0 A.D. * DESCRIPTION : Network server class interface file *----------------------------------------------------------------------------- */ #ifndef NETSERVER_H #define NETSERVER_H // INCLUDES #include "Network.h" #include "NetSession.h" #include "simulation/TurnManager.h" #include "scripting/ScriptableObject.h" #include "ps/GameAttributes.h" #include "ps/scripting/JSMap.h" #include "ps/Player.h" #include "ps/Game.h" #include "simulation/ScriptObject.h" #include #include // DECLARATIONS #define SERVER_SESSIONID 1 #define CLIENT_MIN_SESSIONID 100 #define MAX_CLIENTS 8 #define MAX_OBSERVERS 5 #define DEFAULT_SERVER_SESSION_ID 1 #define DEFAULT_SERVER_NAME L"Noname Server" #define DEFAULT_PLAYER_NAME L"Noname Player" #define DEFAULT_WELCOME_MESSAGE L"Noname Server Welcome Message" #define DEFAULT_HOST_PORT 0x5073 enum NetServerState { // We haven't opened the port yet, we're just setting some stuff up. // This is probably equivalent to the first "Start Network Game" screen SERVER_STATE_PREBIND, // The server is open and accepting connections. This is the screen where // rules are set up by the operator and where players join and select civs // and stuff. SERVER_STATE_PREGAME, // The one with all the killing ;-) SERVER_STATE_INGAME, // The game is over and someone has won. Players might linger to chat or // download the replay log. SERVER_STATE_POSTGAME }; enum { NSS_HANDSHAKE = 1300, NSS_AUTHENTICATE = 1400, NSS_PREGAME = 1500, NSS_INGAME = 1600 }; enum { NMT_APP_PLAYER_LEAVE = NMT_LAST + 100, NMT_APP_PREGAME = NMT_LAST + 200, NMT_APP_OBSERVER = NMT_LAST + 300 }; typedef std::map< uint, CNetSession* > IDSessionMap; typedef std::vector< CNetSession* > SessionList; /* CLASS : CNetServer DESCRIPTION : CNetServer implements a network server for the game. It receives data and connection requests from clients. Under the hood, it uses ENet library to manage connected peers and bandwidth among these. NOTES : */ class CNetServer : public CNetHost, public CJSObject, public CTurnManager { public: CNetServer( CGame* pGame, CGameAttributes* pGameAttributes ); virtual ~CNetServer( void ); bool Start ( JSContext *pContext, uintN argc, jsval *argv ); // void Shutdown ( void ); /** * Returns true indicating the host acts as a server * * @return Always true */ virtual bool IsServer( void ) const { return true; } /** * Adds a new session to the list of sessions * * @param pSession New session to add */ void AddSession( CNetSession* pSession ); /** * Removes the specified session from the list of sessions. If the session * isn't found it returns NULL otherwise it returns the session object found. * * @param pSession Session to remove * @return The session object if found, NULL otherwise */ CNetSession* RemoveSession( CNetSession* pSession ); /** * Removes all the sessions managed by the network server * */ //void RemoveAllSessions( void ); /** * Returns the number of session the server manages * * @return The number of sessions */ //uint GetSessionCount( void ) const; /** * Returns the session object for the specified ID * * @param sessionID The session ID * @return A pointer to session for the specified ID or * NULL if not found */ CNetSession* GetSessionByID( uint sessionID ); protected: virtual bool SetupSession ( CNetSession* pSession ); virtual bool HandleConnect ( CNetSession* pSession ); virtual bool HandleDisconnect ( CNetSession *pSession ); private: // Not implemented CNetServer( const CNetServer& ); CNetServer& operator=( const CNetServer& ); //void ClientConnect ( ENetPeer* pPeer ); //void ClientDisconnect ( ENetPeer* pPeer ); //void ClientReceive ( ENetPeer* pPeer, ENetPacket* pPacket ); /** * Returns the session associated with the specified ENet peer * * @param pPeer ENet peer * @return The session object if found or NULL */ //CNetSession* GetSessionByPeer( const ENetPeer* pPeer ); /** * Setup client game by sending the apropiate network messages. It also * inform the client about the other connected clients as well as player * slot assignment and attributes. * */ //void SetupNewSession( CNetSession* pSession ); /** * Loads the player properties into the specified message * * @param pMessage Message where to load player properties * @param pPlayer Player for which we load the properties */ void BuildPlayerConfigMessage( CPlayerConfigMessage* pMessage, CPlayer* pPlayer ); /** * Callback function used by the BuildPlayerSetupMessage to iterate over * the player properties. It will be called for each property of the player * * @param name Property name * @param pProperty Pointer to player property * @param pData Context pointer passed on iteration startup */ static void PlayerConfigMessageCallback( const CStrW& name, ISynchedJSProperty* pProperty, void* pData ); /** * Loads game properties into the specified message * * @param pMessage Message where to load game properties */ void BuildGameSetupMessage( CGameSetupMessage* pMessage ); /** * Loads player slot properties into the specified message * * @param pMessage Message where to load player properties * @param pPlayerSlot Player slot properties */ void BuildPlayerSlotAssignmentMessage( CAssignPlayerSlotMessage* pMessage, CPlayerSlot* pPlayerSlot ); /** * Callback function used by the BuildGameSetupMessage to iterate over the * game properties. It will be called for each property of the game * * @param name Property name * @param pProperty Pointer to game property * @param pData Context pointer passed on iteration startup */ // IterateCB GameSetupMessageCallbak; static void GameSetupMessageCallback( const CStrW& name, ISynchedJSProperty *pProperty, void *pData ); /** * Retrieves a free session ID from the recycled sessions list * * @return Free session ID */ uint GetFreeSessionID( void ) const; IDSessionMap m_IDSessions; // List of connected ID and session pairs //CScriptObject m_ScriptConnect; // Script client connect dispatch //CScriptObject m_ScriptDisconnect; // Script client disconnect dispatch //CScriptObject m_ScriptChat; // Script client chat dispatch CPlayer* m_Player; // Server player public: /** * * * @param addr Address where to bind * @return PS_OK if bind successfully, error code otherwise */ //PS_RESULT Bind( const CSocketAddress& addr ); void SetPlayerPassword ( const CStr& password ); CStrW GetPlayerName ( void ) const { return m_PlayerName; } NetServerState GetState ( void ) const { return m_State; } int StartGame ( void ); static void ScriptingInit ( void ); protected: // Assign a session ID to the session. Do this just before calling AddSession void AssignSessionID( CNetSession* pSession ); // Add the session. This will be called after the session passes the // handshake and authentication stages. AssignSessionID should've been called // on the session prior to calling this method. //void AddSession( CNetServerSession* pSession ); // Remove the session from the server //void RemoveSession( CNetServerSession* pSession ); // Queue a command coming in from the wire. The command has been validated // by the caller. void QueueIncomingCommand( CNetMessage* pMessage ); // Call the JS callback for incoming events void OnPlayerChat ( const CStrW& from, const CStrW& message ); void OnPlayerJoin ( CNetSession* pSession ); void OnPlayerLeave ( CNetSession* pSession ); void SetupPlayer ( CNetSession* pSession ); //static bool OnPlayerJoin ( void* pContext, CFsmEvent* pEvent ); static bool OnError ( void* pContext, CFsmEvent* pEvent ); static bool OnHandshake ( void* pContext, CFsmEvent* pEvent ); static bool OnAuthenticate ( void* pContext, CFsmEvent* pEvent ); static bool OnPreGame ( void* pContext, CFsmEvent* pEvent ); static bool OnInGame ( void* pContext, CFsmEvent* pEvent ); static bool OnChat ( void* pContext, CFsmEvent* pEvent ); // OVERRIDES FROM CServerSocket //virtual void OnAccept( const CSocketAddress& address ); // OVERRIDES FROM CTurnManager virtual void NewTurn( void ); virtual bool NewTurnReady( void ); virtual void QueueLocalCommand( CNetMessage* pMessage ); // Will only be called from the Network Thread, by the OnAccept handler //virtual CNetServerSession* CreateSession( CSocketInternal* pSocketInternal); // Ask the server if the session is allowed to start observing. // // Returns: // true if the session should be made an observer // false otherwise virtual bool AllowObserver( CNetSession* pSession ); private: CGameAttributes* m_GameAttributes; // Stores game attributes //int m_LastSessionID; // Stores the last session ID //SessionMap m_Sessions; // Managed sessions CJSMap< IDSessionMap > m_JsSessions; CMutex m_Mutex; // Synchronization object for batches /* All sessions that have observer status (observer as in watcher - simple chatters don't have an entry here, only in m_Sessions). Sessions are added here after they have successfully requested observer status. */ SessionList m_Observers; uint m_MaxObservers; // Maximum number of observers CGame* m_Game; // Pointer to actual game NetServerState m_State; // Holds server state CStrW m_Name; // Server name CStrW m_WelcomeMessage; // Nice welcome message //CPlayer* m_Player; // Pointer to 'server' player CStrW m_PlayerName; // Player name CStrW m_PlayerPassword; // Player password int m_Port; // The listening port CScriptObject m_OnChat; CScriptObject m_OnClientConnect; CScriptObject m_OnClientDisconnect; // static CGameAttributes::UpdateCallback AttributeUpdate; // static CPlayer::UpdateCallback PlayerAttributeUpdate; // static PlayerSlotAssignmentCB PlayerSlotAssignmentCallback; static void AttributeUpdate ( const CStrW& name, const CStrW& newValue, void* pData); static void PlayerAttributeUpdate ( const CStrW& name, const CStrW& value, CPlayer* pPlayer, void* pData ); static void PlayerSlotAssignment ( void* pData, CPlayerSlot* pPlayerSlot ); }; extern CNetServer *g_NetServer; #endif // NETSERVER_H