#include "precompiled.h" #include #include "Network/Server.h" #include "Network/Network.h" #include "Game.h" #include "Player.h" #include "CLogger.h" #include "CConsole.h" #define LOG_CAT_NET "net" extern CConsole *g_Console; CNetServer *g_NetServer=NULL; CNetServerAttributes g_NetServerAttributes; using namespace std; CNetServerSession *CNetServer::CreateSession(CSocketInternal *pInt) { CNetServerSession *pRet=new CNetServerSession(this, pInt); CServerHandshake *pMsg=new CServerHandshake(); pMsg->m_Magic=PS_PROTOCOL_MAGIC; pMsg->m_ProtocolVersion=PS_PROTOCOL_VERSION; pMsg->m_SoftwareVersion=PS_PROTOCOL_VERSION; pRet->Push(pMsg); return pRet; } void CNetServer::OnAccept(const CSocketAddress &addr) { LOG(NORMAL, LOG_CAT_NET, "CNetServer::OnAccept(): Accepted connection from %s port %d", addr.GetString().c_str(), addr.GetPort()); CSocketInternal *pInt=Accept(); CNetServerSession *pSession=CreateSession(pInt); } CNetServerSession::~CNetServerSession() { m_pServer->RemoveSession(this); } void CNetServerSession::StartGame() { if (m_pMessageHandler==PreGameHandler) m_pMessageHandler=InGameHandler; } #define UNHANDLED(_pMsg) return false; #define HANDLED(_pMsg) delete _pMsg; return true; #define TAKEN(_pMsg) return true; bool CNetServerSession::BaseHandler(CNetMessage *pMsg, CNetSession *pNetSession) { CNetServerSession *pSession=(CNetServerSession *)pNetSession; switch (pMsg->GetType()) { case NMT_ERROR: { CNetErrorMessage *msg=(CNetErrorMessage *)pMsg; LOG(WARNING, LOG_CAT_NET, "CNetServerSession::BaseHandler(): NMT_ERROR: %s", msg->GetString().c_str()); if (msg->m_State == SS_UNCONNECTED) { /* We were disconnected... What happens with our session? * * Note that deleting a session also removes it from m_Sessions * in CNetServer. If the session is an observer it is also * removed from m_Observers. * * Sessions that are observers or chatters should perhaps * generate an exit message that is sent to all other sessions. * * Player sessions require more care. In Pre-Game, each player * session has an associated CPlayer object and player session * slot requiring special care when deleting. */ if (!pSession->m_pPlayer) { delete pSession; } else { LOG(ERROR, LOG_CAT_NET, "CNetServerSession::BaseHandler(): Player disconnection not implemented!!"); } } HANDLED(pMsg); } } UNHANDLED(pMsg); } bool CNetServerSession::HandshakeHandler(CNetMessage *pMsg, CNetSession *pNetSession) { CNetServerSession *pSession=(CNetServerSession *)pNetSession; LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::HandshakeHandler(): %s.", pMsg->GetString().c_str()); switch (pMsg->GetType()) { case NMT_ClientHandshake: { CClientHandshake * /*SmartPointer*/ msg=(CClientHandshake *)pMsg/*.GetRawPointer()*/; if (msg->m_ProtocolVersion != PS_PROTOCOL_VERSION) do {} while(0); // This will never happen to us here, but anyways ;-) CServerHandshakeResponse *retmsg=new CServerHandshakeResponse(); retmsg->m_UseProtocolVersion=PS_PROTOCOL_VERSION; retmsg->m_Flags=0; retmsg->m_Message=CStrW(L"Hello and Welcome!"); pSession->Push(retmsg); pSession->m_pMessageHandler=AuthenticateHandler; HANDLED(pMsg); } } return BaseHandler(pMsg, pNetSession); } bool CNetServerSession::AuthenticateHandler(CNetMessage *pMsg, CNetSession *pNetSession) { CNetServerSession *pSession=(CNetServerSession *)pNetSession; CNetServer *pServer=pSession->m_pServer; LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::AuthenticateHandler(): %s.", pMsg->GetString().c_str()); if (pMsg->GetType() == NMT_Authenticate) { CAuthenticate *msg=(CAuthenticate *)pMsg; if (msg->m_Password == pSession->m_pServer->m_Password) { LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::AuthenticateHandler(): Login Successful"); pSession->m_Name=msg->m_Name; pSession->m_pServer->m_Sessions.push_back(pSession); CResult *msg=new CResult(); msg->m_Code=NRC_OK; msg->m_Message=L"Logged in"; pSession->Push(msg); if (pServer->GetServerState() == NSS_PreGame) { // We're in pre-game. The connected client becomes a Player. // Find the first free player slot and assign the player to the // client. // If no free player slots could be found - demote to chatter/ // observer. pSession->m_pMessageHandler=PreGameHandler; if (!pServer->AddNewPlayer(pSession)) pSession->m_pMessageHandler=ObserverHandler; } else // We're not in pre-game. The session becomes a chatter/observer here. { pSession->m_pMessageHandler=ObserverHandler; } } else { LOG(WARNING, LOG_CAT_NET, "CNetServerSession::AuthenticateHandler(): Login Failed"); CResult *msg=new CResult(); msg->m_Code=NRC_PasswordInvalid; msg->m_Message=L"Invalid Password"; pSession->Push(msg); } HANDLED(pMsg); } return BaseHandler(pMsg, pNetSession); } bool CNetServerSession::PreGameHandler(CNetMessage *pMsg, CNetSession *pNetSession) { CNetServerSession *pSession=(CNetServerSession *)pNetSession; LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::PreGameHandler(): %s.", pMsg->GetString().c_str()); return ChatHandler(pMsg, pNetSession); } bool CNetServerSession::ObserverHandler(CNetMessage *pMsg, CNetSession *pNetSession) { printf("CNetServerSession::ObserverHandler(): %s.\n", pMsg->GetString().c_str()); // TODO Implement observers and chatter => observer promotion /* if (pMsg->GetType() == NMT_RequestObserve) */ return ChatHandler(pMsg, pNetSession); } bool CNetServerSession::ChatHandler(CNetMessage *pMsg, CNetSession *pNetSession) { CNetServerSession *pSession=(CNetServerSession *)pNetSession; if (pMsg->GetType() == NMT_ChatMessage) { CChatMessage *msg=(CChatMessage *)pMsg; msg->m_Sender=pSession->m_Name; CStrW wstr=msg->m_Message; g_Console->ReceivedChatMessage(pSession->GetName().c_str(), wstr.c_str()); pSession->m_pServer->Broadcast(msg); TAKEN(pMsg); } return BaseHandler(pMsg, pNetSession); } bool CNetServerSession::InGameHandler(CNetMessage *pMsg, CNetSession *pNetSession) { CNetServerSession *pSession=(CNetServerSession *)pNetSession; if (pMsg->GetType() != NMT_EndCommandBatch) LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::InGameHandler(): %s.", pMsg->GetString().c_str()); if (BaseHandler(pMsg, pNetSession)) return true; if (ChatHandler(pMsg, pNetSession)) return true; switch (pMsg->GetType()) { /* All Command Messages (i.e. simulation turn synchronized messages) */ case NMT_GotoCommand: //pSession->m_pPlayer->ValidateCommand(pMsg); pSession->m_pServer->QueueIncomingCommand(pMsg); TAKEN(pMsg); case NMT_EndCommandBatch: // TODO Update client timing information here. HANDLED(pMsg); default: UNHANDLED(pMsg); } } CNetServerAttributes::CNetServerAttributes() { AddValue("serverPlayerName", L"Noname Server Player"); AddValue("serverName", L"Noname Server"); AddValue("welcomeMessage", L"Noname Server Welcome Message"); } CNetServer::CNetServer(CNetServerAttributes *pServerAttribs, CGame *pGame, CGameAttributes *pGameAttribs): m_pServerAttributes(pServerAttribs), m_pGame(pGame), m_pGameAttributes(pGameAttribs), m_MaxObservers(5) { m_pGameAttributes->SetUpdateCallback(AttributeUpdate, this); m_ServerPlayerName=m_pServerAttributes->GetValue("serverPlayerName"); m_NumPlayers=m_pGameAttributes->GetValue("numPlayers").ToUInt(); m_pGame->GetSimulation()->SetTurnManager(this); } PS_RESULT CNetServer::Bind(const CSocketAddress &address) { PS_RESULT res=CServerSocket::Bind(address); if (res==PS_OK) m_ServerState=NSS_PreGame; return res; } bool CNetServer::AddNewPlayer(CNetServerSession *pSession) { CAttributeMap::MapType &attribs=m_pGameAttributes->GetInternalValueMap(); CAttributeMap::MapType::iterator it=attribs.begin(); CSetGameConfig *pMsg=new CSetGameConfig(); uint n=0; while (it != attribs.end()) { pMsg->m_Values.resize(++n); pMsg->m_Values.back().m_Name=it->first; pMsg->m_Values.back().m_Value=it->second; ++it; } pSession->Push(pMsg); if (m_PlayerSessions.size() < m_NumPlayers) { m_PlayerSessions.push_back(pSession); CPlayerConnect *pMsg=new CPlayerConnect(); pMsg->m_Players.resize(1); pMsg->m_Players[0].m_PlayerID=(u32)m_PlayerSessions.size(); pMsg->m_Players[0].m_Nick=pSession->GetName(); Broadcast(pMsg); pMsg=new CPlayerConnect(); for (uint i=0;im_Players.resize(i+1); pMsg->m_Players.back().m_PlayerID=i; pMsg->m_Players.back().m_Nick=m_PlayerSessions[i]->GetName(); } pSession->Push(pMsg); return true; } else return false; } void CNetServer::AttributeUpdate(CStr name, CStrW newValue, void *userdata) { CNetServer *pServer=(CNetServer *)userdata; if (name == CStr("numPlayers")) { pServer->m_NumPlayers=newValue.ToUInt(); } CSetGameConfig *pMsg=new CSetGameConfig; pMsg->m_Values.resize(1); pMsg->m_Values[0].m_Name=name; pMsg->m_Values[1].m_Value=newValue; pServer->Broadcast(pMsg); } bool CNetServer::AllowObserver(CNetServerSession *pSession) { return m_Observers.size() < m_MaxObservers; } void CNetServer::RemoveSession(CNetServerSession *pSession) { vector::iterator it=find(m_Sessions.begin(), m_Sessions.end(), pSession); if (it != m_Sessions.end()) m_Sessions.erase(it); // TODO Correct handling of player and observers } // Unfortunately, the message queueing model is made so that each message has // to be copied once for each socket its sent over, messages are deleted when // sent by CMessageSocket. We could ref-count, but that requires a lot of // thread safety stuff => hard work void CNetServer::Broadcast(CNetMessage *pMsg) { if (m_Sessions.empty()) return; size_t i=0; for (;iPush(pMsg->Copy()); } m_Sessions[i]->Push(pMsg); } int CNetServer::StartGame() { if (m_pGame->StartGame(m_pGameAttributes) != PSRETURN_OK) return -1; else { CTurnManager::Initialize(m_PlayerSessions.size()); for (uint i=0;i::iterator it=m_Sessions.begin(); while (it != m_Sessions.end()) { (*it)->StartGame(); ++it; } return 0; } } void CNetServer::GetDefaultListenAddress(CSocketAddress &address) { address=CSocketAddress(PS_DEFAULT_PORT, IPv4); } void CNetServer::NewTurn() { RecordBatch(2); RotateBatches(); ClearBatch(2); IterateBatch(1, CSimulation::GetMessageMask, m_pGame->GetSimulation()); SendBatch(1); //SendBatchToList(1, m_Observers); } void CNetServer::QueueLocalCommand(CNetMessage *pMsg) { QueueIncomingCommand(pMsg); } void CNetServer::QueueIncomingCommand(CNetMessage *pMsg) { LOG(NORMAL, LOG_CAT_NET, "CNetServer::QueueIncomingCommand(): %s.", pMsg->GetString().c_str()); QueueMessage(2, pMsg); }