#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; 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); } CNetServer::CNetServer(CGame *pGame, CGameAttributes *pGameAttribs): m_pGame(pGame), m_pGameAttributes(pGameAttribs), m_MaxObservers(5), m_ServerPlayerName(L"Noname Server Player"), m_ServerName(L"Noname Server"), m_WelcomeMessage(L"Noname Server Welcome Message"), m_Port(-1) { ONCE( (AddMethod("open", 0)); CJSObject::ScriptingInit("NetServer"); ); AddProperty(L"serverPlayerName", &m_ServerPlayerName); AddProperty(L"serverName", &m_ServerName); AddProperty(L"welcomeMessage", &m_WelcomeMessage); AddProperty(L"port", &m_Port); m_pGameAttributes->SetUpdateCallback(AttributeUpdate, this); m_pGameAttributes->SetPlayerUpdateCallback(PlayerAttributeUpdate, this); m_NumPlayers=m_pGameAttributes->m_NumPlayers; m_pGame->GetSimulation()->SetTurnManager(this); // Set an incredibly long turn length - less command batch spam that way for (int i=0;i<3;i++) CTurnManager::SetTurnLength(i, 3000); g_ScriptingHost.SetGlobal("g_NetServer", OBJECT_TO_JSVAL(GetScript())); } bool CNetServer::JSI_Open(JSContext *cx, uintN argc, jsval *argv) { CSocketAddress addr; if (m_Port == -1) GetDefaultListenAddress(addr); else addr=CSocketAddress(m_Port, /*m_UseIPv6?IPv6:*/IPv4); PS_RESULT res=Bind(addr); if (res != PS_OK) { LOG(ERROR, LOG_CAT_NET, "CNetServer::JSI_Open(): Bind error: %s", res); return false; } return true; } PS_RESULT CNetServer::Bind(const CSocketAddress &address) { PS_RESULT res=CServerSocket::Bind(address); if (res==PS_OK) m_ServerState=NSS_PreGame; return res; } void FillSetGameConfigCB(CStrW name, ISynchedJSProperty *prop, void *userdata) { CSetGameConfig *pMsg=(CSetGameConfig *)userdata; size_t size=pMsg->m_Values.size(); pMsg->m_Values.resize(size+1); pMsg->m_Values[size].m_Name=name; pMsg->m_Values[size].m_Value=prop->ToString(); } void CNetServer::FillSetGameConfig(CSetGameConfig *pMsg) { m_pGameAttributes->IterateSynchedProperties(FillSetGameConfigCB, pMsg); } bool CNetServer::AddNewPlayer(CNetServerSession *pSession) { CSetGameConfig *pMsg=new CSetGameConfig(); FillSetGameConfig(pMsg); pSession->Push(pMsg); if (m_PlayerSessions.size() < m_NumPlayers-1) { m_PlayerSessions.push_back(pSession); // Broadcast a message for the newly added player session CPlayerConnect *pMsg=new CPlayerConnect(); pMsg->m_Players.resize(1); pMsg->m_Players[0].m_PlayerID=pSession->m_pPlayer->GetPlayerID(); pMsg->m_Players[0].m_Nick=pSession->GetName(); Broadcast(pMsg); pMsg=new CPlayerConnect(); // Server Player pMsg->m_Players.resize(1); pMsg->m_Players.back().m_PlayerID=m_pServerPlayer->GetPlayerID(); pMsg->m_Players.back().m_Nick=m_ServerPlayerName; // All the other players for (uint i=0;im_Players.resize(i+2); pMsg->m_Players.back().m_PlayerID=i+1; pMsg->m_Players.back().m_Nick=m_PlayerSessions[i]->GetName(); } pSession->Push(pMsg); return true; } else return false; } void CNetServer::AttributeUpdate(CStrW name, CStrW newValue, void *userdata) { CNetServer *pServer=(CNetServer *)userdata; g_Console->InsertMessage(L"AttributeUpdate: %ls = \"%ls\"", name.c_str(), newValue.c_str()); if (name == CStrW(L"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[0].m_Value=newValue; pServer->Broadcast(pMsg); } void CNetServer::PlayerAttributeUpdate(CStrW name, CStrW newValue, CPlayer *pPlayer, void *userdata) { CNetServer *pServer=(CNetServer *)userdata; g_Console->InsertMessage(L"PlayerAttributeUpdate(%d): %ls = \"%ls\"", pPlayer->GetPlayerID(), name.c_str(), newValue.c_str()); CSetPlayerConfig *pMsg=new CSetPlayerConfig; pMsg->m_PlayerID=pPlayer->GetPlayerID(); pMsg->m_Values.resize(1); pMsg->m_Values[0].m_Name=name; pMsg->m_Values[0].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 players 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() { // TODO Check for the case where we haven't yet filled all player slots // CGame expects to have numPlayer players when it starts the game... Broadcast(new CStartGame()); if (m_pGame->StartGame(m_pGameAttributes) != PSRETURN_OK) return -1; else { for (uint i=0;iSetPlayer(m_pGame->GetPlayer(i+1)); } 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); }