2004-08-16 08:19:17 -07:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
2006-06-01 20:56:24 -07:00
|
|
|
#include "Server.h"
|
|
|
|
|
#include "ServerSession.h"
|
|
|
|
|
#include "Network.h"
|
|
|
|
|
#include "JSEvents.h"
|
2004-08-16 08:19:17 -07:00
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
#include "scripting/ScriptableObject.h"
|
|
|
|
|
|
2006-06-01 19:10:27 -07:00
|
|
|
#include "ps/Game.h"
|
|
|
|
|
#include "simulation/Simulation.h"
|
|
|
|
|
#include "ps/Player.h"
|
|
|
|
|
#include "ps/CLogger.h"
|
|
|
|
|
#include "ps/CConsole.h"
|
2007-11-18 01:09:06 -08:00
|
|
|
#include "ps/ThreadUtil.h"
|
2004-08-16 08:19:17 -07:00
|
|
|
|
|
|
|
|
#define LOG_CAT_NET "net"
|
|
|
|
|
|
|
|
|
|
CNetServer *g_NetServer=NULL;
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
// NOTE: Called in network thread
|
2004-08-16 08:19:17 -07:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
// NOTE: Called in network thread
|
2004-08-16 08:19:17 -07:00
|
|
|
void CNetServer::OnAccept(const CSocketAddress &addr)
|
|
|
|
|
{
|
2007-12-29 08:22:23 -08:00
|
|
|
LOG(CLogger::Normal, LOG_CAT_NET, "CNetServer::OnAccept(): Accepted connection from %s port %d", addr.GetString().c_str(), addr.GetPort());
|
2004-08-16 08:19:17 -07:00
|
|
|
|
|
|
|
|
CSocketInternal *pInt=Accept();
|
|
|
|
|
CNetServerSession *pSession=CreateSession(pInt);
|
2005-08-09 08:55:44 -07:00
|
|
|
UNUSED2(pSession);
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
|
|
|
|
|
2005-01-19 14:32:25 -08:00
|
|
|
CNetServer::CNetServer(CGame *pGame, CGameAttributes *pGameAttribs):
|
2005-02-21 09:13:31 -08:00
|
|
|
m_JSI_Sessions(&m_Sessions),
|
2005-01-19 14:32:25 -08:00
|
|
|
m_pGame(pGame),
|
|
|
|
|
m_pGameAttributes(pGameAttribs),
|
|
|
|
|
m_MaxObservers(5),
|
2005-02-21 09:13:31 -08:00
|
|
|
m_LastSessionID(1),
|
2005-01-19 14:32:25 -08:00
|
|
|
m_ServerPlayerName(L"Noname Server Player"),
|
|
|
|
|
m_ServerName(L"Noname Server"),
|
|
|
|
|
m_WelcomeMessage(L"Noname Server Welcome Message"),
|
|
|
|
|
m_Port(-1)
|
|
|
|
|
{
|
|
|
|
|
m_pGameAttributes->SetUpdateCallback(AttributeUpdate, this);
|
|
|
|
|
m_pGameAttributes->SetPlayerUpdateCallback(PlayerAttributeUpdate, this);
|
2005-02-21 09:13:31 -08:00
|
|
|
m_pGameAttributes->SetPlayerSlotAssignmentCallback(PlayerSlotAssignmentCallback, this);
|
2004-08-16 08:19:17 -07:00
|
|
|
|
2005-01-19 14:32:25 -08:00
|
|
|
m_pGame->GetSimulation()->SetTurnManager(this);
|
2007-06-04 00:41:05 -07:00
|
|
|
|
|
|
|
|
// Set an incredibly long turn length for debugging - less command batch spam that way
|
2007-07-05 00:33:43 -07:00
|
|
|
for (int i=0; i<3; i++)
|
2007-10-22 23:52:23 -07:00
|
|
|
CTurnManager::SetTurnLength(i, CTurnManager::DEFAULT_TURN_LENGTH);
|
2004-08-16 08:19:17 -07:00
|
|
|
|
2005-01-19 14:32:25 -08:00
|
|
|
g_ScriptingHost.SetGlobal("g_NetServer", OBJECT_TO_JSVAL(GetScript()));
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
|
|
|
|
|
2005-01-22 17:36:47 -08:00
|
|
|
CNetServer::~CNetServer()
|
|
|
|
|
{
|
|
|
|
|
g_ScriptingHost.SetGlobal("g_NetServer", JSVAL_NULL);
|
2005-02-21 09:13:31 -08:00
|
|
|
|
|
|
|
|
while (m_Sessions.size() > 0)
|
|
|
|
|
{
|
|
|
|
|
SessionMap::iterator it=m_Sessions.begin();
|
|
|
|
|
delete it->second;
|
|
|
|
|
m_Sessions.erase(it);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CNetServer::ScriptingInit()
|
|
|
|
|
{
|
|
|
|
|
CJSMap<SessionMap>::ScriptingInit("NetServer_SessionMap");
|
|
|
|
|
|
|
|
|
|
AddMethod<bool, &CNetServer::JSI_Open>("open", 0);
|
|
|
|
|
|
2005-04-22 00:12:55 -07:00
|
|
|
AddProperty(L"sessions", &CNetServer::m_JSI_Sessions);
|
|
|
|
|
|
|
|
|
|
AddProperty(L"serverPlayerName", &CNetServer::m_ServerPlayerName);
|
|
|
|
|
AddProperty(L"serverName", &CNetServer::m_ServerName);
|
|
|
|
|
AddProperty(L"welcomeMessage", &CNetServer::m_WelcomeMessage);
|
|
|
|
|
|
|
|
|
|
AddProperty(L"port", &CNetServer::m_Port);
|
|
|
|
|
|
|
|
|
|
AddProperty(L"onChat", &CNetServer::m_OnChat);
|
|
|
|
|
AddProperty(L"onClientConnect", &CNetServer::m_OnClientConnect);
|
|
|
|
|
AddProperty(L"onClientDisconnect", &CNetServer::m_OnClientDisconnect);
|
|
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
CJSObject<CNetServer>::ScriptingInit("NetServer");
|
2005-01-22 17:36:47 -08:00
|
|
|
}
|
|
|
|
|
|
2005-08-09 08:55:44 -07:00
|
|
|
bool CNetServer::JSI_Open(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
2005-01-19 14:32:25 -08:00
|
|
|
CSocketAddress addr;
|
|
|
|
|
if (m_Port == -1)
|
|
|
|
|
GetDefaultListenAddress(addr);
|
|
|
|
|
else
|
2005-02-21 09:13:31 -08:00
|
|
|
addr=CSocketAddress(m_Port, /* m_UseIPv6 ? IPv6 : */ IPv4);
|
2004-09-21 07:40:43 -07:00
|
|
|
|
2005-01-19 14:32:25 -08:00
|
|
|
PS_RESULT res=Bind(addr);
|
|
|
|
|
if (res != PS_OK)
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
2007-12-29 08:22:23 -08:00
|
|
|
LOG(CLogger::Error, LOG_CAT_NET, "CNetServer::JSI_Open(): Bind error: %s", res);
|
2005-01-19 14:32:25 -08:00
|
|
|
return false;
|
2005-01-16 20:52:02 -08:00
|
|
|
}
|
2004-08-16 08:19:17 -07:00
|
|
|
|
2005-01-19 14:32:25 -08:00
|
|
|
return true;
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
|
|
|
|
|
2005-01-19 14:32:25 -08:00
|
|
|
PS_RESULT CNetServer::Bind(const CSocketAddress &address)
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
2005-01-19 14:32:25 -08:00
|
|
|
PS_RESULT res=CServerSocket::Bind(address);
|
|
|
|
|
if (res==PS_OK)
|
|
|
|
|
m_ServerState=NSS_PreGame;
|
|
|
|
|
return res;
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
|
|
|
|
|
2006-07-20 07:37:58 -07:00
|
|
|
void FillSetGameConfigCB(const CStrW& name, ISynchedJSProperty *prop, void *userdata)
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
2005-01-19 14:32:25 -08:00
|
|
|
CSetGameConfig *pMsg=(CSetGameConfig *)userdata;
|
2005-01-22 10:38:58 -08:00
|
|
|
size_t size=pMsg->m_Values.size();
|
2005-01-19 14:32:25 -08:00
|
|
|
pMsg->m_Values.resize(size+1);
|
|
|
|
|
pMsg->m_Values[size].m_Name=name;
|
|
|
|
|
pMsg->m_Values[size].m_Value=prop->ToString();
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
|
|
|
|
|
2005-01-19 14:32:25 -08:00
|
|
|
void CNetServer::FillSetGameConfig(CSetGameConfig *pMsg)
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
2005-01-19 14:32:25 -08:00
|
|
|
m_pGameAttributes->IterateSynchedProperties(FillSetGameConfigCB, pMsg);
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
|
|
|
|
|
2006-07-20 07:37:58 -07:00
|
|
|
void FillSetPlayerConfigCB(const CStrW& name, ISynchedJSProperty *prop, void *userdata)
|
2005-01-29 08:51:13 -08:00
|
|
|
{
|
|
|
|
|
CSetPlayerConfig *pMsg=(CSetPlayerConfig *)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::FillSetPlayerConfig(CSetPlayerConfig *pMsg, CPlayer *pPlayer)
|
|
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
pMsg->m_PlayerID=(u32)pPlayer->GetPlayerID();
|
2005-01-29 08:51:13 -08:00
|
|
|
pPlayer->IterateSynchedProperties(FillSetPlayerConfigCB, pMsg);
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
void CNetServer::AssignSessionID(CNetServerSession *pSession)
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
2005-02-21 09:13:31 -08:00
|
|
|
int newID=++m_LastSessionID;
|
|
|
|
|
pSession->SetID(newID);
|
|
|
|
|
m_Sessions[newID]=pSession;
|
|
|
|
|
}
|
2004-08-16 08:19:17 -07:00
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
void CNetServer::AddSession(CNetServerSession *pSession)
|
|
|
|
|
{
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
2005-02-21 09:13:31 -08:00
|
|
|
CSetGameConfig *pMsg=new CSetGameConfig();
|
|
|
|
|
FillSetGameConfig(pMsg);
|
2004-08-16 08:19:17 -07:00
|
|
|
pSession->Push(pMsg);
|
2005-02-21 09:13:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Broadcast a message for the newly added player session
|
|
|
|
|
CClientConnect *pMsg=new CClientConnect();
|
|
|
|
|
pMsg->m_Clients.resize(1);
|
|
|
|
|
pMsg->m_Clients[0].m_SessionID=pSession->GetID();
|
|
|
|
|
pMsg->m_Clients[0].m_Name=pSession->GetName();
|
|
|
|
|
Broadcast(pMsg);
|
|
|
|
|
|
|
|
|
|
pMsg=new CClientConnect();
|
2004-08-16 08:19:17 -07:00
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
// Server "client"
|
|
|
|
|
pMsg->m_Clients.resize(1);
|
|
|
|
|
pMsg->m_Clients.back().m_SessionID=1; // Server is always 1
|
|
|
|
|
pMsg->m_Clients.back().m_Name=m_ServerPlayerName;
|
|
|
|
|
|
|
|
|
|
// All the other clients
|
|
|
|
|
SessionMap::iterator it=m_Sessions.begin();
|
|
|
|
|
for (;it!=m_Sessions.end();++it)
|
|
|
|
|
{
|
|
|
|
|
pMsg->m_Clients.push_back(CClientConnect::S_m_Clients());
|
|
|
|
|
pMsg->m_Clients.back().m_SessionID=it->second->GetID();
|
|
|
|
|
pMsg->m_Clients.back().m_Name=it->second->GetName();
|
|
|
|
|
}
|
|
|
|
|
pSession->Push(pMsg);
|
|
|
|
|
|
|
|
|
|
// Sync player slot assignments and player attributes
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for (size_t i=0;i<m_pGameAttributes->GetSlotCount();i++)
|
2005-02-21 09:13:31 -08:00
|
|
|
{
|
|
|
|
|
CPlayerSlot *pSlot=m_pGameAttributes->GetSlot(i);
|
|
|
|
|
|
|
|
|
|
pSession->Push(CreatePlayerSlotAssignmentMessage(pSlot));
|
|
|
|
|
|
|
|
|
|
if (pSlot->GetAssignment() == SLOT_SESSION)
|
2005-01-29 08:51:13 -08:00
|
|
|
{
|
2005-02-21 09:13:31 -08:00
|
|
|
CSetPlayerConfig *pMsg=new CSetPlayerConfig();
|
|
|
|
|
FillSetPlayerConfig(pMsg, pSlot->GetPlayer());
|
|
|
|
|
pSession->Push(pMsg);
|
2005-01-29 08:51:13 -08:00
|
|
|
}
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
2005-02-21 09:13:31 -08:00
|
|
|
|
|
|
|
|
OnClientConnect(pSession);
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
|
|
|
|
|
2006-07-20 07:37:58 -07:00
|
|
|
void CNetServer::AttributeUpdate(const CStrW& name, const CStrW& newValue, void *userdata)
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
|
|
|
|
CNetServer *pServer=(CNetServer *)userdata;
|
2005-01-19 14:32:25 -08:00
|
|
|
g_Console->InsertMessage(L"AttributeUpdate: %ls = \"%ls\"", name.c_str(), newValue.c_str());
|
2004-08-16 08:19:17 -07:00
|
|
|
|
|
|
|
|
CSetGameConfig *pMsg=new CSetGameConfig;
|
|
|
|
|
pMsg->m_Values.resize(1);
|
|
|
|
|
pMsg->m_Values[0].m_Name=name;
|
2005-01-16 20:52:02 -08:00
|
|
|
pMsg->m_Values[0].m_Value=newValue;
|
2004-08-16 08:19:17 -07:00
|
|
|
|
|
|
|
|
pServer->Broadcast(pMsg);
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-20 07:37:58 -07:00
|
|
|
void CNetServer::PlayerAttributeUpdate(const CStrW& name, const CStrW& newValue, CPlayer *pPlayer, void *userdata)
|
2005-01-19 14:32:25 -08:00
|
|
|
{
|
|
|
|
|
CNetServer *pServer=(CNetServer *)userdata;
|
|
|
|
|
g_Console->InsertMessage(L"PlayerAttributeUpdate(%d): %ls = \"%ls\"", pPlayer->GetPlayerID(), name.c_str(), newValue.c_str());
|
|
|
|
|
|
|
|
|
|
CSetPlayerConfig *pMsg=new CSetPlayerConfig;
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
pMsg->m_PlayerID=(u32)pPlayer->GetPlayerID();
|
2005-01-19 14:32:25 -08:00
|
|
|
pMsg->m_Values.resize(1);
|
|
|
|
|
pMsg->m_Values[0].m_Name=name;
|
|
|
|
|
pMsg->m_Values[0].m_Value=newValue;
|
|
|
|
|
|
|
|
|
|
pServer->Broadcast(pMsg);
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
CNetMessage *CNetServer::CreatePlayerSlotAssignmentMessage(CPlayerSlot *pSlot)
|
|
|
|
|
{
|
|
|
|
|
CAssignPlayerSlot *pMsg=new CAssignPlayerSlot();
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
pMsg->m_SlotID=(u32)pSlot->GetSlotID();
|
2005-02-21 09:13:31 -08:00
|
|
|
pMsg->m_SessionID=pSlot->GetSessionID();
|
|
|
|
|
switch (pSlot->GetAssignment())
|
|
|
|
|
{
|
|
|
|
|
#define CASE(_a, _b) case _a: pMsg->m_Assignment=_b; break;
|
|
|
|
|
CASE(SLOT_CLOSED, PS_ASSIGN_CLOSED)
|
|
|
|
|
CASE(SLOT_OPEN, PS_ASSIGN_OPEN)
|
|
|
|
|
CASE(SLOT_SESSION, PS_ASSIGN_SESSION)
|
|
|
|
|
//CASE(SLOT_AI, PS_ASSIGN_AI)
|
|
|
|
|
}
|
|
|
|
|
return pMsg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CNetServer::PlayerSlotAssignmentCallback(void *userdata, CPlayerSlot *pSlot)
|
|
|
|
|
{
|
|
|
|
|
CNetServer *pInstance=(CNetServer *)userdata;
|
|
|
|
|
if (pSlot->GetAssignment() == SLOT_SESSION)
|
|
|
|
|
pSlot->GetSession()->SetPlayerSlot(pSlot);
|
|
|
|
|
CNetMessage *pMsg=CreatePlayerSlotAssignmentMessage(pSlot);
|
2005-06-20 08:14:36 -07:00
|
|
|
g_Console->InsertMessage(L"Player Slot Assignment: %hs\n", pMsg->GetString().c_str());
|
2005-02-21 09:13:31 -08:00
|
|
|
pInstance->Broadcast(pMsg);
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-09 08:55:44 -07:00
|
|
|
bool CNetServer::AllowObserver(CNetServerSession* UNUSED(pSession))
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
|
|
|
|
return m_Observers.size() < m_MaxObservers;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-21 07:40:43 -07:00
|
|
|
void CNetServer::RemoveSession(CNetServerSession *pSession)
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
2005-02-21 09:13:31 -08:00
|
|
|
SessionMap::iterator it=m_Sessions.find(pSession->GetID());
|
2004-09-21 07:40:43 -07:00
|
|
|
if (it != m_Sessions.end())
|
|
|
|
|
m_Sessions.erase(it);
|
2004-08-16 08:19:17 -07:00
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
/*
|
|
|
|
|
* Player sessions require some extra care:
|
|
|
|
|
*
|
|
|
|
|
* Pre-Game: dissociate the slot that was used by the session and
|
|
|
|
|
* synchronize the disconnection of the client.
|
|
|
|
|
*
|
|
|
|
|
* In-Game: Revert all player's entities to Gaia control, awaiting the
|
|
|
|
|
* client's reconnect attempts [if/when we implement that]
|
|
|
|
|
*
|
|
|
|
|
* Post-Game: Just sync disconnection - we don't have any players anymore
|
|
|
|
|
* and all is fine.
|
|
|
|
|
*
|
|
|
|
|
* After this is done, call the JS callback if it's been set.
|
|
|
|
|
*/
|
2005-01-29 08:51:13 -08:00
|
|
|
if (pSession->GetPlayer())
|
|
|
|
|
{
|
2005-02-21 09:13:31 -08:00
|
|
|
if (m_ServerState == NSS_PreGame)
|
|
|
|
|
{
|
|
|
|
|
pSession->GetPlayerSlot()->AssignClosed();
|
|
|
|
|
}
|
|
|
|
|
else if (m_ServerState == NSS_InGame)
|
|
|
|
|
{
|
2005-06-20 08:14:36 -07:00
|
|
|
// TODO Reassign entities to Gaia control
|
|
|
|
|
// TODO Set everything up for re-connect and resume
|
|
|
|
|
SetClientPipe(pSession->GetPlayerSlot()->GetSlotID(), NULL);
|
|
|
|
|
pSession->GetPlayerSlot()->AssignClosed();
|
2005-02-21 09:13:31 -08:00
|
|
|
}
|
2005-01-29 08:51:13 -08:00
|
|
|
}
|
2005-02-21 09:13:31 -08:00
|
|
|
|
|
|
|
|
CClientDisconnect *pMsg=new CClientDisconnect();
|
|
|
|
|
pMsg->m_SessionID=pSession->GetID();
|
|
|
|
|
Broadcast(pMsg);
|
|
|
|
|
|
|
|
|
|
OnClientDisconnect(pSession);
|
|
|
|
|
|
2005-01-29 08:51:13 -08:00
|
|
|
// TODO Correct handling of observers
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
|
|
|
|
|
2004-09-21 07:40:43 -07:00
|
|
|
// 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)
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
|
|
|
|
if (m_Sessions.empty())
|
2005-01-22 17:36:47 -08:00
|
|
|
{
|
|
|
|
|
delete pMsg;
|
2004-08-16 08:19:17 -07:00
|
|
|
return;
|
2005-01-22 17:36:47 -08:00
|
|
|
}
|
2004-08-16 08:19:17 -07:00
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
SessionMap::iterator it=m_Sessions.begin();
|
|
|
|
|
// Skip one session
|
|
|
|
|
++it;
|
|
|
|
|
// Send a *copy* to all remaining sessions
|
|
|
|
|
for (;it != m_Sessions.end();++it)
|
2004-08-16 08:19:17 -07:00
|
|
|
{
|
2005-02-21 09:13:31 -08:00
|
|
|
it->second->Push(pMsg->Copy());
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
2005-02-21 09:13:31 -08:00
|
|
|
// Now send to the first session, *not* copying the message
|
|
|
|
|
m_Sessions.begin()->second->Push(pMsg);
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CNetServer::StartGame()
|
|
|
|
|
{
|
|
|
|
|
if (m_pGame->StartGame(m_pGameAttributes) != PSRETURN_OK)
|
2007-06-04 00:41:05 -07:00
|
|
|
{
|
2004-08-16 08:19:17 -07:00
|
|
|
return -1;
|
2007-06-04 00:41:05 -07:00
|
|
|
}
|
2004-08-16 08:19:17 -07:00
|
|
|
else
|
|
|
|
|
{
|
2005-02-21 09:13:31 -08:00
|
|
|
CTurnManager::Initialize(m_pGameAttributes->GetSlotCount());
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for (size_t i=0;i<m_pGameAttributes->GetSlotCount();i++)
|
2004-11-23 15:46:15 -08:00
|
|
|
{
|
2005-02-21 09:13:31 -08:00
|
|
|
CPlayerSlot *pSlot=m_pGameAttributes->GetSlot(i);
|
|
|
|
|
if (pSlot->GetAssignment() == SLOT_SESSION)
|
|
|
|
|
CTurnManager::SetClientPipe(i, pSlot->GetSession());
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
|
|
|
|
m_ServerState=NSS_InGame;
|
|
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
SessionMap::iterator it=m_Sessions.begin();
|
2004-08-16 08:19:17 -07:00
|
|
|
while (it != m_Sessions.end())
|
|
|
|
|
{
|
2005-02-21 09:13:31 -08:00
|
|
|
it->second->StartGame();
|
2004-08-16 08:19:17 -07:00
|
|
|
++it;
|
|
|
|
|
}
|
2005-02-21 09:13:31 -08:00
|
|
|
|
2007-11-18 01:09:06 -08:00
|
|
|
debug_printf("Server StartGame\n");
|
|
|
|
|
Broadcast(new CStartGame());
|
|
|
|
|
|
2005-02-21 09:13:31 -08:00
|
|
|
// This is the signal for everyone to start their simulations.
|
2007-11-18 01:09:06 -08:00
|
|
|
//SendBatch(1);
|
2004-08-16 08:19:17 -07:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CNetServer::GetDefaultListenAddress(CSocketAddress &address)
|
|
|
|
|
{
|
|
|
|
|
address=CSocketAddress(PS_DEFAULT_PORT, IPv4);
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-18 01:09:06 -08:00
|
|
|
bool CNetServer::NewTurnReady()
|
|
|
|
|
{
|
|
|
|
|
// Wait for all clients to check in
|
|
|
|
|
SessionMap::iterator it = m_Sessions.begin();
|
|
|
|
|
for (; it != m_Sessions.end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
if (!it->second->IsReadyForTurn())
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2004-08-16 08:19:17 -07:00
|
|
|
void CNetServer::NewTurn()
|
|
|
|
|
{
|
2007-11-18 01:09:06 -08:00
|
|
|
CScopeLock lock(m_Mutex);
|
|
|
|
|
|
|
|
|
|
// Clear ready flags on clients
|
|
|
|
|
SessionMap::iterator it = m_Sessions.begin();
|
|
|
|
|
for (; it != m_Sessions.end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
it->second->SetReadyForTurn(false);
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-21 07:40:43 -07:00
|
|
|
RecordBatch(2);
|
|
|
|
|
|
2004-08-16 08:19:17 -07:00
|
|
|
RotateBatches();
|
|
|
|
|
ClearBatch(2);
|
|
|
|
|
|
|
|
|
|
IterateBatch(1, CSimulation::GetMessageMask, m_pGame->GetSimulation());
|
2007-11-18 01:09:06 -08:00
|
|
|
//debug_printf("In NewTurn - sending batch\n");
|
2004-08-16 08:19:17 -07:00
|
|
|
SendBatch(1);
|
2005-01-29 08:51:13 -08:00
|
|
|
//IterateBatch(1, SendToObservers, this);
|
2004-08-16 08:19:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CNetServer::QueueLocalCommand(CNetMessage *pMsg)
|
|
|
|
|
{
|
2007-11-18 01:09:06 -08:00
|
|
|
//debug_printf("Queueing command from server\n");
|
2004-08-16 08:19:17 -07:00
|
|
|
QueueIncomingCommand(pMsg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CNetServer::QueueIncomingCommand(CNetMessage *pMsg)
|
|
|
|
|
{
|
2007-11-18 01:09:06 -08:00
|
|
|
CScopeLock lock(m_Mutex);
|
2007-12-29 08:22:23 -08:00
|
|
|
LOG(CLogger::Normal, LOG_CAT_NET, "CNetServer::QueueIncomingCommand(): %s.", pMsg->GetString().c_str());
|
2007-11-18 01:09:06 -08:00
|
|
|
debug_printf("Got a command! queueing it to 2 turns from now\n");
|
2004-08-16 08:19:17 -07:00
|
|
|
QueueMessage(2, pMsg);
|
|
|
|
|
}
|
2005-02-02 08:57:15 -08:00
|
|
|
|
2006-07-20 07:37:58 -07:00
|
|
|
void CNetServer::OnChat(const CStrW& from, const CStrW& message)
|
2005-02-02 08:57:15 -08:00
|
|
|
{
|
|
|
|
|
if (m_OnChat.Defined())
|
|
|
|
|
{
|
|
|
|
|
CChatEvent evt(from, message);
|
|
|
|
|
m_OnChat.DispatchEvent(GetScript(), &evt);
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-02-21 09:13:31 -08:00
|
|
|
|
|
|
|
|
void CNetServer::OnClientConnect(CNetServerSession *pSession)
|
|
|
|
|
{
|
|
|
|
|
if (m_OnClientConnect.Defined())
|
|
|
|
|
{
|
|
|
|
|
CClientConnectEvent evt(pSession);
|
|
|
|
|
m_OnClientConnect.DispatchEvent(GetScript(), &evt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CNetServer::OnClientDisconnect(CNetServerSession *pSession)
|
|
|
|
|
{
|
|
|
|
|
if (m_OnClientDisconnect.Defined())
|
|
|
|
|
{
|
|
|
|
|
CClientDisconnectEvent evt(pSession->GetID(), pSession->GetName());
|
|
|
|
|
m_OnClientDisconnect.DispatchEvent(GetScript(), &evt);
|
|
|
|
|
}
|
|
|
|
|
}
|