Daniel Gibson 72edf1c720 Commented out unused functions and variables
many unused functions and variables are now commented out

You'll still get tons of warnings, which should mostly fall in one of
the following categories:
1. Unnecessary variables or values generated from .es scripts
2. Pointers assigned to from functions with side-effects: DO NOT REMOVE!
   Like CEntity *penNew = CreateEntity_t(...); - even if penNew isn't
   used, CreateEntity() must be called there!
2016-05-09 18:51:03 +02:00

1610 lines
49 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
#include <Engine/StdH.h>
#include <Engine/Build.h>
#include <Engine/Network/Network.h>
#include <Engine/Network/Server.h>
#include <Engine/Network/SessionState.h>
#include <Engine/Network/PlayerSource.h>
#include <Engine/Network/PlayerBuffer.h>
#include <Engine/Network/PlayerTarget.h>
#include <Engine/Network/NetworkProfile.h>
#include <Engine/Network/ClientInterface.h>
#include <Engine/Network/CommunicationInterface.h>
#include <Engine/Network/Compression.h>
#include <Engine/Network/Diff.h>
#include <Engine/Network/CPacket.h>
#include <Engine/World/WorldSettings.h>
#include <Engine/Base/Console.h>
#include <Engine/Base/Shell.h>
#include <Engine/Math/Functions.h>
#include <Engine/Entities/InternalClasses.h>
#include <Engine/Base/CRC.h>
#include <Engine/Base/ErrorTable.h>
#include <Engine/GameAgent/GameAgent.h>
#include <Engine/Templates/StaticArray.cpp>
extern INDEX ser_iSyncCheckBuffer;
extern FLOAT net_tmDisconnectTimeout;
extern BOOL con_bCapture;
extern CTString con_strCapture;
extern CTString ser_strIPMask;
extern CTString ser_strNameMask;
extern INDEX ser_bInverseBanning;
extern BOOL MatchesBanMask(const CTString &strString, const CTString &strMask);
extern CClientInterface cm_aciClients[SERVER_CLIENTS];
CSessionSocket::CSessionSocket(void)
{
sso_bActive = FALSE;
sso_bVIP = FALSE;
sso_bSendStream = FALSE;
sso_iDisconnectedState = 0;
sso_iLastSentSequence = -1;
sso_ctBadSyncs = 0;
sso_tvLastMessageSent.Clear();
sso_tvLastPingSent.Clear();
}
CSessionSocket::~CSessionSocket(void)
{
sso_bActive = FALSE;
sso_bVIP = FALSE;
sso_bSendStream = FALSE;
sso_iLastSentSequence = -1;
sso_ctBadSyncs = 0;
sso_tvLastMessageSent.Clear();
sso_tvLastPingSent.Clear();
}
void CSessionSocket::Clear(void)
{
sso_bActive = FALSE;
sso_bVIP = FALSE;
sso_bSendStream = FALSE;
sso_tvMessageReceived.Clear();
sso_tmLastSyncReceived = -1.0f;
sso_iLastSentSequence = -1;
sso_tvLastMessageSent.Clear();
sso_tvLastPingSent.Clear();
sso_nsBuffer.Clear();
sso_iDisconnectedState = 0;
sso_ctBadSyncs = 0;
sso_sspParams.Clear();
}
void CSessionSocket::Activate(void)
{
#if DEBUG_SYNCSTREAMDUMPING
ClearDumpStream();
#endif
ASSERT(!sso_bActive);
sso_bActive = TRUE;
sso_bVIP = FALSE;
sso_bSendStream = FALSE;
sso_tvMessageReceived.Clear();
sso_tmLastSyncReceived = -1.0f;
sso_iLastSentSequence = -1;
sso_tvLastMessageSent.Clear();
sso_tvLastPingSent.Clear();
sso_iDisconnectedState = 0;
sso_ctBadSyncs = 0;
sso_sspParams.Clear();
// sso_nsBuffer.Clear();
}
void CSessionSocket::Deactivate(void)
{
sso_iDisconnectedState = 0;
sso_iLastSentSequence = -1;
sso_tvLastMessageSent.Clear();
sso_tvLastPingSent.Clear();
sso_ctBadSyncs = 0;
sso_bActive = FALSE;
sso_nsBuffer.Clear();
sso_sspParams.Clear();
}
BOOL CSessionSocket::IsActive(void)
{
return sso_bActive;
}
extern INDEX cli_iBufferActions;
extern INDEX cli_iMaxBPS;
extern INDEX cli_iMinBPS;
CSessionSocketParams::CSessionSocketParams(void)
{
Clear();
}
void CSessionSocketParams::Clear(void)
{
ssp_iBufferActions = 2;
ssp_iMaxBPS = 4000;
ssp_iMinBPS = 1000;
}
static void ClampParams(void)
{
cli_iBufferActions = Clamp(cli_iBufferActions, INDEX(1), INDEX(20));
cli_iMaxBPS = Clamp(cli_iMaxBPS, INDEX(100), INDEX(1000000));
cli_iMinBPS = Clamp(cli_iMinBPS, INDEX(100), INDEX(1000000));
}
// check if up to date with current params
BOOL CSessionSocketParams::IsUpToDate(void)
{
ClampParams();
return
ssp_iBufferActions == cli_iBufferActions &&
ssp_iMaxBPS == cli_iMaxBPS &&
ssp_iMinBPS == cli_iMinBPS;
}
// update
void CSessionSocketParams::Update(void)
{
ClampParams();
ssp_iBufferActions = cli_iBufferActions;
ssp_iMaxBPS = cli_iMaxBPS;
ssp_iMinBPS = cli_iMinBPS;
}
// message operations
CNetworkMessage &operator<<(CNetworkMessage &nm, CSessionSocketParams &ssp)
{
nm<<ssp.ssp_iBufferActions<<ssp.ssp_iMaxBPS<<ssp.ssp_iMinBPS;
return nm;
}
CNetworkMessage &operator>>(CNetworkMessage &nm, CSessionSocketParams &ssp)
{
nm>>ssp.ssp_iBufferActions>>ssp.ssp_iMaxBPS>>ssp.ssp_iMinBPS;
return nm;
}
/*
* Constructor.
*/
CServer::CServer(void)
{
srv_bActive = FALSE;
srv_assoSessions.New(NET_MAXGAMECOMPUTERS);
srv_aplbPlayers.New(NET_MAXGAMEPLAYERS);
// initialize player indices
INDEX iPlayer = 0;
FOREACHINSTATICARRAY(srv_aplbPlayers, CPlayerBuffer, itplb) {
itplb->plb_Index = iPlayer;
iPlayer++;
}
}
/*
* Destructor.
*/
CServer::~CServer()
{
srv_bActive = FALSE;
}
/*
* Clear the object.
*/
void CServer::Stop(void)
{
// stop gameagent
GameAgent_ServerEnd();
// tell all clients to disconnect
INDEX ctClients = srv_assoSessions.sa_Count;
INDEX iClient;
for (iClient=0;iClient<ctClients;iClient++) {
if (srv_assoSessions[iClient].sso_bActive == TRUE) {
SendDisconnectMessage(iClient,"Server stopped.");
}
}
// run a few updates, so the message gets sent
for (int ctUpdates=0;ctUpdates<10;ctUpdates++) {
BOOL bFound = FALSE;
for (iClient=0;iClient<ctClients;iClient++) {
if (srv_assoSessions[iClient].sso_bActive == TRUE) {
bFound = TRUE;
break;
}
}
if (bFound == FALSE) {
break;
} else {
_cmiComm.Server_Update();
_pTimer->Sleep(100);
}
}
// stop network driver server
_cmiComm.Server_Close();
// clear all session
srv_assoSessions.Clear();
srv_assoSessions.New(NET_MAXGAMECOMPUTERS);
// clear list of players
srv_aplbPlayers.Clear();
srv_aplbPlayers.New(NET_MAXGAMEPLAYERS);
// initialize player indices
INDEX iPlayer = 0;
{FOREACHINSTATICARRAY(srv_aplbPlayers, CPlayerBuffer, itplb) {
itplb->plb_Index = iPlayer;
iPlayer++;
}}
// init buffer for sync checks
srv_ascChecks.Clear();
srv_bActive = FALSE;
};
/*
* Initialize server and start message handlers.
*/
void CServer::Start_t(void)
{
// init buffer for sync checks
srv_ascChecks.Clear();
// set up structures
srv_tmLastProcessedTick = 0.0f;
srv_iLastProcessedSequence = -1; // -1 so that next one will be 0
srv_bActive = TRUE;
srv_bPause = FALSE;
srv_bGameFinished = FALSE;
srv_fServerStep = 0.0f;
// init network driver server
_cmiComm.Server_Init_t();
// init gameagent
if (_cmiComm.IsNetworkEnabled()) {
GameAgent_ServerInit();
}
}
/*
* Send disconnect message to some node.
*/
void CServer::SendDisconnectMessage(INDEX iClient, const char *strExplanation, BOOL bStream)
{
CSessionSocket &sso = srv_assoSessions[iClient];
if (!bStream) {
CNetworkMessage nmDisconnect(MSG_INF_DISCONNECTED);
// compose message
nmDisconnect<<CTString(strExplanation);
// send it
_pNetwork->SendToClientReliable(iClient, nmDisconnect);
} else {
CTMemoryStream strmDisconnect;
strmDisconnect<<INDEX(MSG_INF_DISCONNECTED);
strmDisconnect<<CTString(strExplanation);
// send the stream to the remote session state
_pNetwork->SendToClientReliable(iClient, strmDisconnect);
}
// report that it has gone away
CPrintF(TRANSV("Client '%s' ordered to disconnect: %s\n"),
(const char *) _cmiComm.Server_GetClientName(iClient), strExplanation);
// if not disconnected before
if (sso.sso_iDisconnectedState==0) {
// mark the disconnection
sso.sso_iDisconnectedState = 1;
// if the client was already kicked before, but is still hanging here
} else {
// force the disconnection
CPrintF(TRANSV("Forcing client '%s' to disconnect\n"),
(const char *) _cmiComm.Server_GetClientName(iClient));
sso.sso_iDisconnectedState = 2;
}
}
// add a new sync check to buffer
void CServer::AddSyncCheck(const CSyncCheck &sc)
{
ser_iSyncCheckBuffer = ClampDn(ser_iSyncCheckBuffer, INDEX(1));
if (srv_ascChecks.Count()!=ser_iSyncCheckBuffer) {
srv_ascChecks.Clear();
srv_ascChecks.New(ser_iSyncCheckBuffer);
}
// find the oldest one
INDEX iOldest = 0;
for (INDEX i=1; i<srv_ascChecks.Count(); i++) {
if (srv_ascChecks[i].sc_tmTick<srv_ascChecks[iOldest].sc_tmTick) {
iOldest=i;
}
}
// overwrite it
srv_ascChecks[iOldest] = sc;
}
// try to find a sync check for given time in the buffer (-1==too old, 0==found, 1==toonew)
INDEX CServer::FindSyncCheck(TIME tmTick, CSyncCheck &sc)
{
BOOL bHasEarlier = FALSE;
BOOL bHasLater = FALSE;
for (INDEX i=0; i<srv_ascChecks.Count(); i++) {
TIME tmInTable = srv_ascChecks[i].sc_tmTick;
if (tmInTable==tmTick) {
sc = srv_ascChecks[i];
return 0;
} else if (tmInTable<tmTick) {
bHasEarlier = TRUE;
} else if (tmInTable>tmTick) {
bHasLater = TRUE;
}
}
if (!bHasEarlier) {
ASSERT(bHasLater);
return -1;
} else if (!bHasLater) {
ASSERT(bHasEarlier);
return +1;
} else {
ASSERT(FALSE); // cannot have both earlier and later and not be found
return +1;
}
}
/* Send one regular batch of sequences to a client. */
void CServer::SendGameStreamBlocks(INDEX iClient)
{
// get corresponding session socket
CSessionSocket &sso = srv_assoSessions[iClient];
// gather needed data to decide what to send
INDEX iLastSent = sso.sso_iLastSentSequence;
INDEX ctMinBytes = sso.sso_sspParams.ssp_iMinBPS/20;
INDEX ctMaxBytes = sso.sso_sspParams.ssp_iMaxBPS/20;
// make sure outgoing message doesn't overflow UDP size
ctMinBytes = Clamp(ctMinBytes, 0, 1000);
ctMaxBytes = Clamp(ctMaxBytes, 0, 1000);
// limit the clients BPS by server's local settings
extern INDEX ser_iMaxAllowedBPS;
ctMinBytes = ClampUp(ctMinBytes, (INDEX) ( ser_iMaxAllowedBPS/20L - MAX_HEADER_SIZE));
ctMaxBytes = ClampUp(ctMaxBytes, (INDEX) (ser_iMaxAllowedBPS/20L - MAX_HEADER_SIZE));
// prevent server/singleplayer from flooding itself
extern INDEX cli_bPredictIfServer;
if (iClient==0 && !cli_bPredictIfServer) {
ctMinBytes = 0;
ctMaxBytes = (INDEX) 1E6;
}
// CPrintF("Send%d(%d, %d, %d): ", iClient, iLastSent, ctMinBytes, ctMaxBytes);
// start after last sequence that was sent and go upwards
INDEX iSequence = iLastSent+1;
INDEX iStep = +1;
// CPrintF("last=%d -- ", iLastSent);
// initialize the message that is to be sent
CNetworkMessage nmGameStreamBlocks(MSG_GAMESTREAMBLOCKS);
// get one message for last compressed message of valid size
CNetworkMessage nmPackedBlocks(MSG_GAMESTREAMBLOCKS);
CNetworkMessage nmPackedBlocksNew(MSG_GAMESTREAMBLOCKS);
// repeat for max 100 sequences
INDEX iBlocksOk = 0;
INDEX iMaxSent = -1;
for(INDEX i=0; i<100; i++) {
if (iStep<0 && iBlocksOk>=3) {
// break;
}
// get the stream block with current sequence
// CPrintF("%d: ", iSequence);
CNetworkStreamBlock *pnsbBlock;
CNetworkStream::Result res = sso.sso_nsBuffer.GetBlockBySequence(iSequence, pnsbBlock);
// if it is not found
if (res!=CNetworkStream::R_OK) {
// if going upward
if (iStep>0 ) {
// // if this block is missing
// && res==CNetworkStream::R_BLOCKMISSING
// if none sent so far
if (iBlocksOk<=0) {
// give up
// CPrintF("giving up\n");
break;
}
// CPrintF("rewind ", iSequence);
// rewind and continue downward
if (iSequence == iLastSent+1) {
iSequence = iLastSent-1;
} else {
iSequence = iLastSent;
}
iStep = -1;
// retry
continue;
// otherwise
} else {
// stop adding more blocks
break;
}
}
// if uncompressed message would overflow
if (nmGameStreamBlocks.nm_slSize+pnsbBlock->nm_slSize+32>MAX_NETWORKMESSAGE_SIZE) {
// CPrintF("overflow ");
break;
}
// add this block to the message and pack it
pnsbBlock->WriteToMessage(nmGameStreamBlocks);
nmPackedBlocksNew.Reinit();
nmGameStreamBlocks.PackDefault(nmPackedBlocksNew);
// if some blocks written already and the batch is too large
if (iBlocksOk>0) {
if (iStep>0 && nmPackedBlocksNew.nm_slSize>=ctMaxBytes ||
iStep<0 && nmPackedBlocksNew.nm_slSize>=ctMinBytes ) {
// stop
// CPrintF("toomuch ");
break;
}
}
// use new pack
// CPrintF("added ");
nmPackedBlocks = nmPackedBlocksNew;
iMaxSent = Max(iMaxSent, iSequence);
iSequence+= iStep;
iBlocksOk++;
}
// if no blocks to write
if (iBlocksOk<=0) {
// if not sent anything for some time
CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
extern FLOAT ser_tmKeepAlive;
if ((tvNow-sso.sso_tvLastMessageSent).GetSeconds()>ser_tmKeepAlive) {
// send keepalive
CNetworkMessage nmKeepalive(MSG_KEEPALIVE);
_pNetwork->SendToClient(iClient, nmKeepalive);
sso.sso_tvLastMessageSent = tvNow;
}
// CPrintF("nothing\n");
return;
}
// send the message to the client
// CPrintF("sent: %d=%dB\n", iBlocksOk, nmPackedBlocks.nm_slSize);
_pNetwork->SendToClient(iClient, nmPackedBlocks);
sso.sso_iLastSentSequence = Max(sso.sso_iLastSentSequence, iMaxSent);
sso.sso_tvLastMessageSent = _pTimer->GetHighPrecisionTimer();
// remove the block(s) that fall out of the buffer
extern INDEX ser_iRememberBehind;
sso.sso_nsBuffer.RemoveOlderBlocksBySequence(srv_iLastProcessedSequence-ser_iRememberBehind);
// if haven't sent pings for some time
CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
extern FLOAT ser_tmPingUpdate;
if ((tvNow-sso.sso_tvLastPingSent).GetSeconds()>ser_tmPingUpdate) {
// send ping info
CNetworkMessage nmPings(MSG_INF_PINGS);
for(INDEX i=0; i<NET_MAXGAMEPLAYERS; i++) {
CPlayerBuffer &plb = srv_aplbPlayers[i];
if (plb.IsActive()) {
BOOL b = 1;
nmPings.WriteBits(&b, 1);
nmPings.WriteBits(&plb.plb_iPing, 10);
} else {
BOOL b = 0;
nmPings.WriteBits(&b, 1);
}
}
_pNetwork->SendToClient(iClient, nmPings);
sso.sso_tvLastPingSent = tvNow;
}
}
/* Resend a batch of game stream blocks to a client. */
void CServer::ResendGameStreamBlocks(INDEX iClient, INDEX iSequence0, INDEX ctSequences)
{
extern INDEX net_bReportMiscErrors;
if (net_bReportMiscErrors) {
CPrintF(TRANSV("Server: Resending sequences %d-%d(%d) to '%s'..."),
iSequence0, iSequence0+ctSequences-1, ctSequences, (const char *) _cmiComm.Server_GetClientName(iClient));
}
// get corresponding session socket
CSessionSocket &sso = srv_assoSessions[iClient];
// create a package message
CNetworkMessage nmGameStreamBlocks(MSG_GAMESTREAMBLOCKS);
CNetworkMessage nmPackedBlocks(MSG_GAMESTREAMBLOCKS);
// for each sequence
INDEX iSequence;
for(iSequence = iSequence0; iSequence<iSequence0+ctSequences; iSequence++) {
// get the stream block with that sequence
CNetworkStreamBlock *pnsbBlock;
CNetworkStream::Result res = sso.sso_nsBuffer.GetBlockBySequence(iSequence, pnsbBlock);
// if it is not found
if (res!=CNetworkStream::R_OK) {
// tell the requesting session state to disconnect
SendDisconnectMessage(iClient, TRANS("Gamestream synchronization lost"));
return;
}
CNetworkMessage nmPackedBlocksNew(MSG_GAMESTREAMBLOCKS);
// pack it in the batch
pnsbBlock->WriteToMessage(nmGameStreamBlocks);
nmGameStreamBlocks.PackDefault(nmPackedBlocksNew);
// if the batch is too large
if (nmPackedBlocksNew.nm_slSize>512) {
// stop
break;
}
// use new pack
nmPackedBlocks = nmPackedBlocksNew;
}
// send the last batch of valid size
_pfNetworkProfile.IncrementCounter(CNetworkProfile::PCI_GAMESTREAMRESENDS);
_pNetwork->SendToClient(iClient, nmPackedBlocks);
extern INDEX net_bReportMiscErrors;
if (net_bReportMiscErrors) {
CPrintF(TRANSV(" sent %d-%d(%d - %db)\n"),
iSequence0, iSequence, iSequence-iSequence0-1, nmPackedBlocks.nm_slSize);
}
}
/* Get number of active players. */
INDEX CServer::GetPlayersCount(void)
{
INDEX ctPlayers = 0;
FOREACHINSTATICARRAY(srv_aplbPlayers, CPlayerBuffer, itplb) {
if (itplb->IsActive()) {
ctPlayers++;
}
}
return ctPlayers;
}
/* Get number of active vip players. */
INDEX CServer::GetVIPPlayersCount(void)
{
INDEX ctPlayers = 0;
FOREACHINSTATICARRAY(srv_aplbPlayers, CPlayerBuffer, itplb) {
if (itplb->IsActive() && srv_assoSessions[itplb->plb_iClient].sso_bVIP) {
ctPlayers++;
}
}
return ctPlayers;
}
/* Get total number of active clients. */
INDEX CServer::GetClientsCount(void)
{
INDEX ctClients = 0;
// for each active session
for(INDEX iSession=0; iSession<srv_assoSessions.Count(); iSession++) {
CSessionSocket &sso = srv_assoSessions[iSession];
if (iSession>0 && !sso.IsActive()) {
continue;
}
ctClients++;
}
return ctClients;
}
/* Get number of active vip clients. */
INDEX CServer::GetVIPClientsCount(void)
{
INDEX ctClients = 0;
// for each active session
for(INDEX iSession=0; iSession<srv_assoSessions.Count(); iSession++) {
CSessionSocket &sso = srv_assoSessions[iSession];
if (iSession>0 && !sso.IsActive()) {
continue;
}
if (sso.sso_bVIP) {
ctClients++;
}
}
return ctClients;
}
/* Get number of active observers. */
INDEX CServer::GetObserversCount(void)
{
INDEX ctClients = 0;
// for each active session
for(INDEX iSession=0; iSession<srv_assoSessions.Count(); iSession++) {
CSessionSocket &sso = srv_assoSessions[iSession];
if (iSession>0 && !sso.IsActive()) {
continue;
}
if (sso.sso_ctLocalPlayers == 0) {
ctClients++;
}
}
return ctClients;
}
/* Get number of active players on one client. */
INDEX CServer::GetPlayersCountForClient(INDEX iClient)
{
INDEX ctPlayers = 0;
FOREACHINSTATICARRAY(srv_aplbPlayers, CPlayerBuffer, itplb) {
if (itplb->IsActive() && itplb->plb_iClient==iClient) {
ctPlayers++;
}
}
return ctPlayers;
}
/*
* Find first inactive client.
*/
CPlayerBuffer *CServer::FirstInactivePlayer(void)
{
// for all players in game
FOREACHINSTATICARRAY(srv_aplbPlayers, CPlayerBuffer, itplb) {
// if player is not active
if (!itplb->IsActive()) {
// return it
return &itplb.Current();
}
}
// if no inactive players found, return error
return NULL;
}
/*
* Check if some character already exists in this session.
*/
BOOL CServer::CharacterNameIsUsed(CPlayerCharacter &pcCharacter)
{
// for all players in game
FOREACHINSTATICARRAY(srv_aplbPlayers, CPlayerBuffer, itplb) {
// if it is active and has same character as this one
if (itplb->IsActive() && itplb->plb_pcCharacter == pcCharacter) {
// it exists
return TRUE;
}
}
// otherwise, it doesn't exist
return FALSE;
}
// find a mask of all players on a certain client
ULONG CServer::MaskOfPlayersOnClient(INDEX iClient)
{
ULONG ulClientPlayers = 0;
for(INDEX ipl=0; ipl<srv_aplbPlayers.Count(); ipl++) {
CPlayerBuffer &plb = srv_aplbPlayers[ipl];
if (plb.IsActive() && plb.plb_iClient==iClient) {
ulClientPlayers |= 1UL<<ipl;
}
}
return ulClientPlayers;
}
/*
* Handle network messages.
*/
void CServer::ServerLoop(void)
{
// if not started
if (!srv_bActive) {
ASSERTALWAYS("Running server loop for before starting server!");
// do not gather/send actions
return;
}
_pfNetworkProfile.StartTimer(CNetworkProfile::PTI_SERVER_LOOP);
// try {
// _cmiComm.Server_Accept_t();
// } catch (char *strError) {
// CPrintF(TRANSV("Accepting failed, no more clients can connect: %s\n"), strError);
// }
// handle all incoming messages
HandleAll();
//INDEX iSpeed = 1;
extern INDEX ser_bWaitFirstPlayer;
// if the local session is keeping up with time and not paused
BOOL bPaused = srv_bPause || _pNetwork->ga_bLocalPause || _pNetwork->IsWaitingForPlayers() ||
srv_bGameFinished || ser_bWaitFirstPlayer;
if (_pNetwork->ga_sesSessionState.ses_bKeepingUpWithTime
&&(_pTimer->GetRealTimeTick()-_pNetwork->ga_sesSessionState.ses_tmLastUpdated<=_pTimer->TickQuantum*2.01f)
&& !bPaused ) {
// advance time
srv_fServerStep += _pNetwork->ga_fGameRealTimeFactor*_pNetwork->ga_sesSessionState.ses_fRealTimeFactor;
// if stepped to next tick
if (srv_fServerStep>=1.0f) {
// find how many ticks were stepped
INDEX iSpeed = ClampDn(INDEX(srv_fServerStep), 1);
srv_fServerStep -= iSpeed;
// for each tick
for( INDEX i=0; i<iSpeed; i++) {
// make allaction messages for one tick
MakeAllActions();
}
}
}
// for each active session
for(INDEX iSession=0; iSession<srv_assoSessions.Count(); iSession++) {
CSessionSocket &sso = srv_assoSessions[iSession];
if (iSession>0 && (!sso.IsActive() || !sso.sso_bSendStream)) {
continue;
}
// send one regular batch of sequences to the client
SendGameStreamBlocks(iSession);
}
_pfNetworkProfile.StopTimer(CNetworkProfile::PTI_SERVER_LOOP);
}
// make allaction messages for one tick
void CServer::MakeAllActions(void)
{
// increment tick counter and processed sequence
srv_tmLastProcessedTick += _pTimer->TickQuantum;
srv_iLastProcessedSequence++;
// for each active session
for(INDEX iSession=0; iSession<srv_assoSessions.Count(); iSession++) {
CSessionSocket &sso = srv_assoSessions[iSession];
if (iSession>0 && !sso.IsActive()) {
continue;
}
// create all-actions message
CNetworkStreamBlock nsbAllActions(MSG_SEQ_ALLACTIONS, srv_iLastProcessedSequence);
// write time there
nsbAllActions<<srv_tmLastProcessedTick;
// for all players in game
INDEX iPlayer = 0;
FOREACHINSTATICARRAY(srv_aplbPlayers, CPlayerBuffer, itplb) {
// if player is active
if (itplb->IsActive()) {
// player indices transmission is unneccessary unless if debugging
// // write its index
// nsbAllActions<<iPlayer;
// write its action
itplb->CreateActionPacket(&nsbAllActions, iSession);
}
iPlayer++;
}
// add the all-actions block to the buffer
sso.sso_nsBuffer.AddBlock(nsbAllActions);
}
// for all players in game
{FOREACHINSTATICARRAY(srv_aplbPlayers, CPlayerBuffer, itplb) {
// if player is active
if (itplb->IsActive()) {
// flush oldest action
itplb->AdvanceActionBuffer();
}
}}
}
// add a block to streams for all sessions
void CServer::AddBlockToAllSessions(CNetworkStreamBlock &nsb)
{
// for each active session
for(INDEX iSession=0; iSession<srv_assoSessions.Count(); iSession++) {
CSessionSocket &sso = srv_assoSessions[iSession];
if (iSession>0 && !sso.IsActive()) {
continue;
}
// add the block to the buffer
sso.sso_nsBuffer.AddBlock(nsb);
}
}
/* Send initialization info to local client. */
void CServer::ConnectLocalSessionState(INDEX iClient, CNetworkMessage &nm)
{
// find session of this client
CSessionSocket &sso = srv_assoSessions[iClient];
// activate it
sso.Activate();
// prepare his initialization message
CNetworkMessage nmInitMainServer(MSG_REP_CONNECTLOCALSESSIONSTATE);
nmInitMainServer<<srv_tmLastProcessedTick;
nmInitMainServer<<srv_iLastProcessedSequence;
sso.sso_ctLocalPlayers = -1;
nm>>sso.sso_sspParams;
// send him server session state initialization message
_pNetwork->SendToClientReliable(iClient, nmInitMainServer);
}
/* Send initialization info to remote client. */
void CServer::ConnectRemoteSessionState(INDEX iClient, CNetworkMessage &nm)
{
ASSERT(iClient>0);
// find session of this client
CSessionSocket &sso = srv_assoSessions[iClient];
// if the IP is banned
if (!MatchesBanMask(_cmiComm.Server_GetClientName(iClient), ser_strIPMask) != !ser_bInverseBanning) {
// disconnect the client
SendDisconnectMessage(iClient, TRANS("You are banned from this server"), /*bStream=*/TRUE);
return;
}
// read version info
INDEX iTag, iMajor, iMinor;
nm>>iTag;
#define VTAG 0x56544147 // Looks like 'VTAG' in ASCII.
if (iTag==VTAG) {
nm>>iMajor>>iMinor;
} else {
iMajor = 109;
iMinor = 1;
}
// if wrong
if (iMajor!=_SE_BUILD_MAJOR || iMinor!=_SE_BUILD_MINOR) {
// disconnect the client
CTString strExplanation;
strExplanation.PrintF(TRANSV(
"This server runs version %d.%d, your version is %d.%d.\n"
"Please visit http://www.croteam.com for information on version updating."),
_SE_BUILD_MAJOR, _SE_BUILD_MINOR, iMajor, iMinor);
SendDisconnectMessage(iClient, strExplanation, /*bStream=*/TRUE);
return;
}
extern CTString net_strConnectPassword;
extern CTString net_strVIPPassword;
extern CTString net_strObserverPassword;
extern INDEX net_iVIPReserve;
extern INDEX net_iMaxObservers;
extern INDEX net_iMaxClients;
CTString strGivenMod;
CTString strGivenPassword;
nm>>strGivenMod>>strGivenPassword;
INDEX ctWantedLocalPlayers;
nm>>ctWantedLocalPlayers;
// if wrong mod
if (_strModName!=strGivenMod) {
// disconnect the client
// NOTE: DO NOT TRANSLATE THIS STRING!
CTString strMod(0, "MOD:%s\\%s", (const char *) _strModName, (const char *) _strModURL);
SendDisconnectMessage(iClient, strMod, /*bStream=*/TRUE);
return;
}
// get counts of allowed players, clients, vips and check for connection allowance
INDEX ctMaxAllowedPlayers = _pNetwork->ga_sesSessionState.ses_ctMaxPlayers;
INDEX ctMaxAllowedClients = ctMaxAllowedPlayers;
if (net_iMaxClients>0) {
ctMaxAllowedClients = ClampUp(net_iMaxClients, (INDEX)NET_MAXGAMECOMPUTERS);
}
INDEX ctMaxAllowedVIPPlayers = 0;
INDEX ctMaxAllowedVIPClients = 0;
if (net_iVIPReserve>0 && net_strVIPPassword!="") {
ctMaxAllowedVIPPlayers = ClampDn(net_iVIPReserve-GetVIPPlayersCount(), 0);
ctMaxAllowedVIPClients = ClampDn(net_iVIPReserve-GetVIPClientsCount(), 0);
}
INDEX ctMaxAllowedObservers = net_iMaxObservers;
// get current counts
INDEX ctCurrentPlayers = GetPlayersCount();
INDEX ctCurrentClients = GetClientsCount();
INDEX ctCurrentObservers = GetObserversCount();
// check which passwords this client can satisfy
BOOL bAutorizedAsVIP = FALSE;
BOOL bAutorizedAsObserver = FALSE;
BOOL bAutorizedAsPlayer = FALSE;
if (net_strVIPPassword!="" && net_strVIPPassword==strGivenPassword) {
bAutorizedAsVIP = TRUE;
bAutorizedAsPlayer = TRUE;
bAutorizedAsObserver = TRUE;
}
if (net_strConnectPassword=="" || net_strConnectPassword==strGivenPassword) {
bAutorizedAsPlayer = TRUE;
}
if ((net_strObserverPassword==""&&bAutorizedAsPlayer) || net_strObserverPassword==strGivenPassword) {
bAutorizedAsObserver = TRUE;
}
// if the user is not authorised as a VIP
if (!bAutorizedAsVIP) {
// artificially decrease allowed number of players and clients
ctMaxAllowedPlayers = ClampDn(ctMaxAllowedPlayers-ctMaxAllowedVIPPlayers, 0);
ctMaxAllowedClients = ClampDn(ctMaxAllowedClients-ctMaxAllowedVIPClients, 0);
}
// if too many clients or players
if (ctCurrentPlayers+ctWantedLocalPlayers>ctMaxAllowedPlayers
||ctCurrentClients+1>ctMaxAllowedClients) {
// disconnect the client
SendDisconnectMessage(iClient, TRANS("Server full!"), /*bStream=*/TRUE);
return;
}
// if the user is trying to connect as observer
if (ctWantedLocalPlayers==0) {
// if too many observers already
if (ctCurrentObservers>=ctMaxAllowedObservers && !bAutorizedAsVIP) {
// disconnect the client
SendDisconnectMessage(iClient, TRANS("Too many observers!"), /*bStream=*/TRUE);
return;
}
// if observer password is wrong
if (!bAutorizedAsObserver) {
// disconnect the client
if (strGivenPassword=="") {
SendDisconnectMessage(iClient, TRANS("This server requires password for observers!"), /*bStream=*/TRUE);
} else {
SendDisconnectMessage(iClient, TRANS("Wrong observer password!"), /*bStream=*/TRUE);
}
}
// if the user is trying to connect as player
} else {
// if player password is wrong
if (!bAutorizedAsPlayer) {
// disconnect the client
if (strGivenPassword=="") {
SendDisconnectMessage(iClient, TRANS("This server requires password to connect!"), /*bStream=*/TRUE);
} else {
SendDisconnectMessage(iClient, TRANS("Wrong password!"), /*bStream=*/TRUE);
}
}
}
// activate it
sso.Activate();
// load parameters for it
sso.sso_ctLocalPlayers = ctWantedLocalPlayers;
sso.sso_bVIP = bAutorizedAsVIP;
nm>>sso.sso_sspParams;
// try to
try {
// create base info to be sent
extern CTString ser_strMOTD;
CTMemoryStream strmInfo;
strmInfo<<INDEX(MSG_REP_CONNECTREMOTESESSIONSTATE);
strmInfo<<ser_strMOTD;
strmInfo<<_pNetwork->ga_World.wo_fnmFileName;
strmInfo<<_pNetwork->ga_sesSessionState.ses_ulSpawnFlags;
strmInfo.Write_t(_pNetwork->ga_aubDefaultProperties, NET_MAXSESSIONPROPERTIES);
SLONG slSize = strmInfo.GetStreamSize();
// send the stream to the remote session state
_pNetwork->SendToClientReliable(iClient, strmInfo);
CPrintF(TRANSV("Server: Sent initialization info to '%s' (%dk)\n"),
(const char*)_cmiComm.Server_GetClientName(iClient), slSize/1024);
// if failed
} catch (char *strError) {
// deactivate it
sso.Deactivate();
// report error
CPrintF(TRANSV("Server: Cannot prepare connection data: %s\n"), strError);
}
}
/* Send session state data to remote client. */
void CServer::SendSessionStateData(INDEX iClient)
{
ASSERT(iClient>0);
// find session of this client
CSessionSocket &sso = srv_assoSessions[iClient];
// copy its buffer from local session state
sso.sso_nsBuffer.Copy(srv_assoSessions[0].sso_nsBuffer);
// try to
try {
// prepare files or memory streams for connection info
CTFileStream strmStateFile; CTMemoryStream strmStateMem;
CTFileStream strmDeltaFile; CTMemoryStream strmDeltaMem;
CTStream *pstrmState; CTMemoryStream *pstrmDelta;
extern INDEX net_bDumpConnectionInfo;
UBYTE* pubSrc = NULL;
/*if (net_bDumpConnectionInfo) {
strmStateFile.Create_t(CTString("Temp\\State.bin"));
strmDeltaFile.Create_t(CTString("Temp\\Delta.bin"));
pstrmState = &strmStateFile;
pstrmDelta = &strmDeltaFile;
} else {*/
pstrmState = &strmStateMem;
pstrmDelta = &strmDeltaMem;
pubSrc = strmDeltaMem.mstrm_pubBuffer + strmDeltaMem.mstrm_slLocation;
//}
ASSERT(pubSrc != NULL);
// write main session state
_pNetwork->ga_sesSessionState.Write_t(pstrmState);
pstrmState->SetPos_t(0);
SLONG slFullSize = pstrmState->GetStreamSize();
CTMemoryStream strmInfo;
strmInfo<<INDEX(MSG_REP_STATEDELTA);
// compress it to another one, using delta from original
CTMemoryStream strmDefaultState;
strmDefaultState.Write_t
(_pNetwork->ga_pubDefaultState, _pNetwork->ga_slDefaultStateSize);
strmDefaultState.SetPos_t(0);
DIFF_Diff_t(&strmDefaultState, pstrmState, pstrmDelta);
pstrmDelta->SetPos_t(0);
SLONG slDeltaSize = pstrmDelta->GetStreamSize();
CzlibCompressor comp;
comp.PackStream_t(*pstrmDelta, strmInfo);
SLONG slSize = strmInfo.GetStreamSize();
// send the stream to the remote session state
_pNetwork->SendToClientReliable(iClient, strmInfo);
CPrintF(TRANSV("Server: Sent connection data to '%s' (%dk->%dk->%dk)\n"),
(const char*)_cmiComm.Server_GetClientName(iClient),
slFullSize/1024, slDeltaSize/1024, slSize/1024);
if (net_bDumpConnectionInfo) {
CPrintF(TRANSV("Server: Connection data dumped.\n"));
}
// if failed
} catch (char *strError) {
// deactivate it
sso.Deactivate();
// report error
CPrintF(TRANSV("Server: Cannot prepare connection data: %s\n"), strError);
}
}
/* Handle incoming network messages. */
void CServer::HandleAll()
{
// clear last accepted client info
/* INDEX iClient = -1;
if (_cmiComm.GetLastAccepted(iClient)) {
CPrintF(TRANSV("Server: Accepted session connection by '%s'\n"),
(const char *) _cmiComm.Server_GetClientName(iClient));
}
*/
// for each active client
{for( INDEX iClient=0; iClient<NET_MAXGAMECOMPUTERS; iClient++) {
// handle all of its messages
HandleAllForAClient(iClient);
}}
}
void CServer::HandleAllForAClient(INDEX iClient)
{
// if the client is not connected
if (!_cmiComm.Server_IsClientUsed(iClient)) {
// skip it
return;
}
// update the client's max BPS limit from the session socket parameters
cm_aciClients[iClient].ci_pbOutputBuffer.pb_pbsLimits.pbs_fBandwidthLimit = srv_assoSessions[iClient].sso_sspParams.ssp_iMaxBPS*8;
// find session of this client
CSessionSocket &sso = srv_assoSessions[iClient];
// if hasn't received sync check for too long
extern INDEX ser_bKickOnSyncLate;
if (ser_bKickOnSyncLate &&sso.sso_bActive &&
sso.sso_tmLastSyncReceived>0 &&
sso.sso_tmLastSyncReceived<_pNetwork->ga_sesSessionState.ses_tmLastSyncCheck -
(2*ser_iSyncCheckBuffer*_pNetwork->ga_sesSessionState.ses_tmSyncCheckFrequency)) {
SendDisconnectMessage(iClient, TRANS("No valid SYNCCHECK received for too long!"));
}
if (iClient>0 && sso.sso_bActive && sso.sso_bSendStream && sso.sso_tvMessageReceived.tv_llValue>0 &&
(_pTimer->GetHighPrecisionTimer()-sso.sso_tvMessageReceived).GetSeconds()>net_tmDisconnectTimeout) {
SendDisconnectMessage(iClient, TRANS("Connection timeout"));
}
// if the client is disconnected
if (!_cmiComm.Server_IsClientUsed(iClient) || sso.sso_iDisconnectedState>1) {
CPrintF(TRANSV("Server: Client '%s' disconnected.\n"), (const char *) _cmiComm.Server_GetClientName(iClient));
// clear it
_cmiComm.Server_ClearClient(iClient);
// free all that data that was allocated for the client
HandleClientDisconected(iClient);
}
CNetworkMessage nmReceived;
// repeat
FOREVER {
// if there is some reliable message
if (_pNetwork->ReceiveFromClientReliable(iClient, nmReceived)) {
// process it
Handle(iClient, nmReceived);
// if there are no more messages
} else {
// skip to receiving unreliable
break;
}
}
// if the client has confirmed disconnect in this loop
if (!_cmiComm.Server_IsClientUsed(iClient) || sso.sso_iDisconnectedState>1) {
CPrintF(TRANSV("Server: Client '%s' disconnected.\n"), (const char *) _cmiComm.Server_GetClientName(iClient));
// clear it
_cmiComm.Server_ClearClient(iClient);
// free all that data that was allocated for the client
HandleClientDisconected(iClient);
}
// repeat
FOREVER {
// if there is some unreliable message
if (_pNetwork->ReceiveFromClient(iClient, nmReceived)) {
// process it
Handle(iClient, nmReceived);
// if there are no more messages
} else {
// finish with this client
break;
}
}
}
void CServer::HandleClientDisconected(INDEX iClient)
{
// find session of this client
CSessionSocket &sso = srv_assoSessions[iClient];
// deactivate it
sso.Deactivate();
INDEX iPlayer = 0;
FOREACHINSTATICARRAY(srv_aplbPlayers, CPlayerBuffer, itplb) {
// if player is on that client
if (itplb->plb_iClient==iClient) {
// create message for removing player from all session
CNetworkStreamBlock nsbRemPlayerData(MSG_SEQ_REMPLAYER, ++srv_iLastProcessedSequence);
nsbRemPlayerData<<iPlayer; // player index
// put the message in buffer to be sent to all sessions
AddBlockToAllSessions(nsbRemPlayerData);
// deactivate it
itplb->Deactivate();
}
iPlayer++;
}
}
// split the rcon response string into lines and send one by one to the client
static void SendAdminResponse(INDEX iClient, const CTString &strResponse)
{
CTString str = strResponse;
while (str!="") {
CTString strLine = str;
strLine.OnlyFirstLine();
str.RemovePrefix(strLine);
str.DeleteChar(0);
if (strLine.Length()>0) {
CNetworkMessage nm(MSG_ADMIN_RESPONSE);
nm<<strLine;
_pNetwork->SendToClientReliable(iClient, nm);
}
}
}
void CServer::Handle(INDEX iClient, CNetworkMessage &nmMessage)
{
CSessionSocket &sso = srv_assoSessions[iClient];
sso.sso_tvMessageReceived = _pTimer->GetHighPrecisionTimer();
switch (nmMessage.GetType()) {
// if if it is just keepalive, ignore it
case MSG_KEEPALIVE: break;
case MSG_REP_DISCONNECTED: {
CSessionSocket &sso = srv_assoSessions[iClient];
sso.sso_iDisconnectedState=2;
} break;
// if local session state asks for registration
case MSG_REQ_CONNECTLOCALSESSIONSTATE: {
ConnectLocalSessionState(iClient, nmMessage);
} break;
// if remote server asks for registration
case MSG_REQ_CONNECTREMOTESESSIONSTATE: {
ConnectRemoteSessionState(iClient, nmMessage);
} break;
// if remote server asks for data
case MSG_REQ_STATEDELTA: {
CPrintF(TRANSV("Sending statedelta response\n"));
SendSessionStateData(iClient);
} break;
// if player asks for registration
case MSG_REQ_CONNECTPLAYER: {
// check that someone doesn't add too many players
if (iClient>0 && GetPlayersCountForClient(iClient)>=sso.sso_ctLocalPlayers) {
CTString strMessage;
strMessage.PrintF(TRANSV("Protocol violation"));
SendDisconnectMessage(iClient, strMessage);
}
// read character data from the message
CPlayerCharacter pcCharacter;
nmMessage>>pcCharacter;
// if the name is banned
if (!MatchesBanMask(pcCharacter.GetName(), ser_strNameMask) != !ser_bInverseBanning) {
// disconnect the client
SendDisconnectMessage(iClient, TRANS("You are banned from this server"), /*bStream=*/TRUE);
return;
}
CPlayerBuffer *pplbNewClient;
// find some inactive player
pplbNewClient = FirstInactivePlayer();
// if there is some active player with same character name
if (CharacterNameIsUsed(pcCharacter)) {
// send refusal message
CTString strMessage;
strMessage.PrintF(TRANSV("Player character '%s' already exists in this session."),
(const char *) pcCharacter.GetName());
SendDisconnectMessage(iClient, strMessage);
// if the max. number of clients is not reached
} else if (pplbNewClient!=NULL) {
// activate it
pplbNewClient->Activate(iClient);
INDEX iNewPlayer = pplbNewClient->plb_Index;
// remember its character
pplbNewClient->plb_pcCharacter = pcCharacter;
// create message for adding player data to sessions
CNetworkStreamBlock nsbAddClientData(MSG_SEQ_ADDPLAYER, ++srv_iLastProcessedSequence);
nsbAddClientData<<iNewPlayer; // client index
nsbAddClientData<<pcCharacter; // client character data
extern INDEX ser_bWaitFirstPlayer;
ser_bWaitFirstPlayer = 0; // player is here don't wait any more
// put the message in buffer to be sent to all servers
AddBlockToAllSessions(nsbAddClientData);
// send him client initialization message
CNetworkMessage nmPlayerRegistered(MSG_REP_CONNECTPLAYER);
nmPlayerRegistered<<iNewPlayer; // player index
_pNetwork->SendToClientReliable(iClient, nmPlayerRegistered);
// notify gameagent
GameAgent_ServerStateChanged();
// if refused
} else {
// send him refusal message
SendDisconnectMessage(iClient, TRANS("Too many players in session."));
}
} break;
// if client source wants to change character
case MSG_REQ_CHARACTERCHANGE: {
// read character data from the message
INDEX iPlayer;
CPlayerCharacter pcCharacter;
nmMessage>>iPlayer>>pcCharacter;
// first check if the request is valid
if (iPlayer<0 || iPlayer>srv_aplbPlayers.Count() ) {
break;
}
CPlayerBuffer &plb = srv_aplbPlayers[iPlayer];
if (plb.plb_iClient!=iClient || !(plb.plb_pcCharacter==pcCharacter) ) {
break;
}
// if all was right, add that as change a sequence
CNetworkStreamBlock nsbChangeChar(MSG_SEQ_CHARACTERCHANGE, ++srv_iLastProcessedSequence);
nsbChangeChar<<iPlayer;
nsbChangeChar<<pcCharacter;
plb.plb_pcCharacter = pcCharacter;
// put the message in buffer to be sent to all clients
AddBlockToAllSessions(nsbChangeChar);
} break;
// if client source sends action packet
case MSG_ACTION: {
CSessionSocket &sso = srv_assoSessions[iClient];
// for each possible player on that client
for(INDEX ipls=0; ipls<NET_MAXLOCALPLAYERS; ipls++) {
// see if saved in the message
BOOL bSaved = 0;
nmMessage.ReadBits(&bSaved, 1);
// if saved
if (bSaved) {
// read client index
INDEX iPlayer = 0;
nmMessage.ReadBits(&iPlayer, 4);
CPlayerBuffer &plb = srv_aplbPlayers[iPlayer];
// if the player is not on that client
if (plb.plb_iClient!=iClient) {
// consider the entire message invalid
CPrintF("Wrong Client!\n");
break;
}
// read ping
plb.plb_iPing = 0;
nmMessage.ReadBits(&plb.plb_iPing, 10);
// let the corresponding client buffer receive the message
INDEX iMaxBuffer = sso.sso_sspParams.ssp_iBufferActions;
extern INDEX cli_bPredictIfServer;
if (iClient==0 && !cli_bPredictIfServer) {
iMaxBuffer = 1;
}
plb.ReceiveActionPacket(&nmMessage, iMaxBuffer);
}
}
} break;
// if client sent a synchronization check
case MSG_SYNCCHECK: {
extern INDEX ser_bReportSyncOK;
extern INDEX ser_bReportSyncBad;
extern INDEX ser_bReportSyncLate;
extern INDEX ser_bReportSyncEarly;
extern INDEX ser_bPauseOnSyncBad;
extern INDEX ser_iKickOnSyncBad;
// read sync check from the packet
CSyncCheck scRemote;
nmMessage.Read(&scRemote, sizeof(scRemote));
// try to find it in buffer
CSyncCheck scLocal;
INDEX iFound = FindSyncCheck(scRemote.sc_tmTick, scLocal);
// if found
if (iFound==0) {
// flush the clients stream buffer up to that sequence
// (the sync is used as piggy-backed acknowledge of packet receival)
CSessionSocket &sso = srv_assoSessions[iClient];
sso.sso_nsBuffer.RemoveOlderBlocksBySequence(scRemote.sc_iSequence);
// if level was changed
if (scLocal.sc_iLevel!=scRemote.sc_iLevel) {
// disconnect the client
SendDisconnectMessage(iClient, TRANS("Level change in progress. Please retry."));
// if it is wrong crc
} else if (scLocal.sc_ulCRC!=scRemote.sc_ulCRC) {
sso.sso_ctBadSyncs++;
if( ser_bReportSyncBad) {
CPrintF( TRANS("SYNCBAD: Client '%s', Sequence %d Tick %.2f - bad %d\n"),
(const char *) _cmiComm.Server_GetClientName(iClient), scRemote.sc_iSequence , scRemote.sc_tmTick, sso.sso_ctBadSyncs);
}
if (ser_iKickOnSyncBad>0) {
if (sso.sso_ctBadSyncs>=ser_iKickOnSyncBad) {
SendDisconnectMessage(iClient, TRANS("Too many bad syncs"));
}
} else if( ser_bPauseOnSyncBad) {
_pNetwork->ga_sesSessionState.ses_bWantPause = TRUE;
}
} else {
sso.sso_ctBadSyncs = 0;
if (ser_bReportSyncOK) {
CPrintF( TRANS("SYNCOK: Client '%s', Tick %.2f\n"),
(const char *) _cmiComm.Server_GetClientName(iClient), scRemote.sc_tmTick);
}
}
// remember that this client has sent sync for that tick
if (srv_assoSessions[iClient].sso_tmLastSyncReceived<scRemote.sc_tmTick) {
srv_assoSessions[iClient].sso_tmLastSyncReceived = scRemote.sc_tmTick;
}
// if too old
} else if (iFound<0) {
// report only if syncs are ok now (so that we don't report a bunch of late syncs on level change
if( ser_bReportSyncLate && srv_assoSessions[iClient].sso_tmLastSyncReceived>0) {
CPrintF( TRANS("SYNCLATE: Client '%s', Tick %.2f\n"),
(const char *) _cmiComm.Server_GetClientName(iClient), scRemote.sc_tmTick);
}
// if too new
} else {
if( ser_bReportSyncEarly) {
CPrintF( TRANS("SYNCEARLY: Client '%s', Tick %.2f\n"),
(const char *) _cmiComm.Server_GetClientName(iClient), scRemote.sc_tmTick);
}
// remember that this client has sent sync for that tick
// (even though we cannot really check that it is valid)
if (srv_assoSessions[iClient].sso_tmLastSyncReceived<scRemote.sc_tmTick) {
srv_assoSessions[iClient].sso_tmLastSyncReceived = scRemote.sc_tmTick;
}
}
} break;
// if a server requests resend of some game stream packets
case MSG_REQUESTGAMESTREAMRESEND: {
// get the sequences from the block
INDEX iSequence0, ctSequences;
nmMessage>>iSequence0;
nmMessage>>ctSequences;
// resend the game stream blocks to the server
ResendGameStreamBlocks(iClient, iSequence0, ctSequences);
} break;
// if a client wants to toggle pause
case MSG_REQ_PAUSE: {
// read the pause state from the message
BOOL bWantPause;
nmMessage>>(INDEX&)bWantPause;
// if state is new
if (!srv_bPause != !bWantPause) {
// if the client may pause
extern INDEX ser_bClientsMayPause;
if (_cmiComm.Server_IsClientLocal(iClient) || ser_bClientsMayPause) {
// change it
srv_bPause = bWantPause;
// add the pause state block to the buffer to be sent to all clients
CNetworkStreamBlock nsbPause(MSG_SEQ_PAUSE, ++srv_iLastProcessedSequence);
nsbPause<<(INDEX&)srv_bPause;
nsbPause<<_cmiComm.Server_GetClientName(iClient);
AddBlockToAllSessions(nsbPause);
}
}
} break;
// if a player wants to change its buffer settings
case MSG_SET_CLIENTSETTINGS: {
// read data
CSessionSocket &sso = srv_assoSessions[iClient];
nmMessage>>sso.sso_sspParams;
} break;
// if a chat message was sent
case MSG_CHAT_IN: {
// get it
ULONG ulFrom, ulTo;
CTString strMessage;
nmMessage>>ulFrom>>ulTo>>strMessage;
// filter the from address by the client's players
ulFrom &= MaskOfPlayersOnClient(iClient);
// if the source has no players
if (ulFrom==0) {
// make it public message
ulTo = (ULONG) -1;
}
// make the outgoing message
CNetworkMessage nmOut(MSG_CHAT_OUT);
nmOut<<ulFrom;
if (ulFrom==0) {
CTString strFrom;
if (iClient==0) {
strFrom = TRANS("Server");
} else {
strFrom.PrintF(TRANSV("Client %d"), iClient);
}
nmOut<<strFrom;
}
nmOut<<strMessage;
// for each active client
for(INDEX iSession=0; iSession<srv_assoSessions.Count(); iSession++) {
CSessionSocket &sso = srv_assoSessions[iSession];
if (iSession>0 && !sso.IsActive()) {
continue;
}
// if message is public or the client has some of destination players
if (ulTo==-1 || ulTo&MaskOfPlayersOnClient(iSession)) {
// send the message to that computer
_pNetwork->SendToClient(iSession, nmOut);
}
}
} break;
// if a crc response is received
case MSG_REQ_CRCLIST: {
CPrintF(TRANSV("Sending CRC response\n"));
// create CRC challenge
CTMemoryStream strmCRC;
strmCRC<<INDEX(MSG_REQ_CRCCHECK);
strmCRC.Write_t(_pNetwork->ga_pubCRCList, _pNetwork->ga_slCRCList);
SLONG slSize = strmCRC.GetStreamSize();
// send the stream to the remote session state
_pNetwork->SendToClientReliable(iClient, strmCRC);
CPrintF(TRANSV("Server: Sent CRC challenge to '%s' (%dk)\n"),
(const char*)_cmiComm.Server_GetClientName(iClient), slSize/1024);
} break;
// if a crc response is received
case MSG_REP_CRCCHECK: {
// get it
ULONG ulCRC;
INDEX iLastSequence;
nmMessage>>ulCRC>>iLastSequence;
// if not same
if (_pNetwork->ga_ulCRC!=ulCRC) {
// disconnect the client
SendDisconnectMessage(iClient, TRANS("Wrong CRC check."));
// if same
} else {
CPrintF(TRANSV("Server: Client '%s', CRC check OK\n"),
(const char*)_cmiComm.Server_GetClientName(iClient));
// use the piggybacked sequence number to initiate sending stream to it
CSessionSocket &sso = srv_assoSessions[iClient];
sso.sso_bSendStream = TRUE;
sso.sso_nsBuffer.RemoveOlderBlocksBySequence(iLastSequence);
sso.sso_iLastSentSequence = iLastSequence;
}
} break;
// if a rcon request is received
case MSG_ADMIN_COMMAND: {
extern CTString net_strAdminPassword;
// get it
CTString strPassword, strCommand;
nmMessage>>strPassword>>strCommand;
if (net_strAdminPassword=="") {
CNetworkMessage nmRes(MSG_ADMIN_RESPONSE);
nmRes<<CTString(TRANS("Remote administration not allowed on this server.\n"));
CPrintF(TRANSV("Server: Client '%s', Tried to use remote administration.\n"),
(const char*)_cmiComm.Server_GetClientName(iClient));
_pNetwork->SendToClientReliable(iClient, nmRes);
} else if (net_strAdminPassword!=strPassword) {
CPrintF(TRANSV("Server: Client '%s', Wrong password for remote administration.\n"),
(const char*)_cmiComm.Server_GetClientName(iClient));
SendDisconnectMessage(iClient, TRANS("Wrong admin password. The attempt was logged."));
break;
} else {
CPrintF(TRANSV("Server: Client '%s', Admin cmd: %s\n"),
(const char*)_cmiComm.Server_GetClientName(iClient), (const char *) strCommand);
con_bCapture = TRUE;
con_strCapture = "";
_pShell->Execute(strCommand+";");
CTString strResponse = CTString(">")+strCommand+"\n"+con_strCapture;
SendAdminResponse(iClient, strResponse);
con_bCapture = FALSE;
con_strCapture = "";
}
} break;
// otherwise
default:
ASSERT(FALSE);
}
}