Serious-Engine/Sources/Engine/Network/Network.cpp
2016-03-11 15:57:17 +02:00

2510 lines
83 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
#include "StdH.h"
#include <Engine/Build.h>
#include <Engine/Base/Console.h>
#include <Engine/Base/CRCTable.h>
#include <Engine/Base/Shell.h>
#include <Engine/Base/ProgressHook.h>
#include <Engine/Network/Server.h>
#include <Engine/Network/SessionState.h>
#include <Engine/Network/Network.h>
#include <Engine/Network/PlayerSource.h>
#include <Engine/Network/PlayerBuffer.h>
#include <Engine/Network/PlayerTarget.h>
#include <Engine/Entities/InternalClasses.h>
#include <Engine/Entities/Precaching.h>
#include <Engine/Network/CommunicationInterface.h>
#include <Engine/Templates/Stock_CModelData.h>
#include <Engine/Templates/Stock_CAnimData.h>
#include <Engine/Templates/Stock_CTextureData.h>
#include <Engine/Templates/Stock_CSoundData.h>
#include <Engine/Templates/Stock_CEntityClass.h>
#include <Engine/Base/Statistics_internal.h>
#include <Engine/Graphics/DrawPort.h>
#include <Engine/Sound/SoundLibrary.h>
#include <Engine/Sound/SoundListener.h>
#include <Engine/Rendering/Render.h>
#include <Engine/Rendering/Render_internal.h>
#include <Engine/Base/ListIterator.inl>
#include <Engine/Math/Float.h>
#include <Engine/Rendering/RenderProfile.h>
#include <Engine/Network/NetworkProfile.h>
#include <Engine/Network/LevelChange.h>
#include <Engine/Brushes/BrushArchive.h>
#include <Engine/Entities/Entity.h>
#include <Engine/Models/ModelObject.h>
#include <Engine/Ska/ModelInstance.h>
#include <Engine/Entities/ShadingInfo.h>
#include <Engine/Entities/EntityCollision.h>
#include <Engine/Entities/LastPositions.h>
#include <Engine/Templates/DynamicContainer.cpp>
#include <Engine/Templates/DynamicArray.cpp>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/StaticStackArray.cpp>
#include <Engine/Templates/Stock_CAnimData.h>
#include <Engine/Templates/Stock_CTextureData.h>
#include <Engine/Templates/Stock_CSoundData.h>
#include <Engine/Templates/Stock_CEntityClass.h>
#include <Engine/Templates/Stock_CModelData.h>
#include <Engine/Templates/Stock_CAnimSet.h>
#include <Engine/Templates/Stock_CMesh.h>
#include <Engine/Templates/Stock_CShader.h>
#include <Engine/Templates/Stock_CSkeleton.h>
#include <Engine/GameAgent/GameAgent.h>
// pointer to global instance of the only game object in the application
CNetworkLibrary *_pNetwork= NULL;
extern BOOL _bNeedPretouch;
extern BOOL _bMultiPlayer = FALSE;
extern INDEX _ctEntities = 0;
extern INDEX _ctPredictorEntities = 0;
extern LevelChangePhase _lphCurrent = LCP_NOCHANGE;
extern BOOL _bTempNetwork = FALSE; // set while using temporary second network object
extern BOOL con_bCapture;
extern CTString con_strCapture;
static CWorld *_pwoCurrentWorld = NULL;
static FLOAT _bStartDemoRecordingNextTime = FALSE;
static FLOAT _bStopDemoRecordingNextTime = FALSE;
static INDEX dem_iRecordedNumber = 0;
// network control
extern INDEX ser_bReportSyncOK = FALSE;
extern INDEX ser_bReportSyncBad = TRUE;
extern INDEX ser_bReportSyncLate = FALSE;
extern INDEX ser_bReportSyncEarly = FALSE;
extern INDEX ser_bPauseOnSyncBad = FALSE;
extern INDEX ser_iKickOnSyncBad = 10;
extern INDEX ser_bKickOnSyncLate = 1;
extern INDEX ser_iRememberBehind = 3000;
extern INDEX ser_iExtensiveSyncCheck = 0;
extern INDEX ser_bClientsMayPause = TRUE;
extern FLOAT ser_tmSyncCheckFrequency = 1.0f;
extern INDEX ser_iSyncCheckBuffer = 60;
extern INDEX ser_bEnumeration = TRUE;
extern INDEX ser_bPingGameAgent = TRUE;
extern FLOAT ser_tmKeepAlive = 0.1f;
extern FLOAT ser_tmPingUpdate = 3.0f;
extern INDEX ser_bWaitFirstPlayer = 0;
extern INDEX ser_iMaxAllowedBPS = 8000;
extern CTString ser_strIPMask = "";
extern CTString ser_strNameMask = "";
extern INDEX ser_bInverseBanning = FALSE;
extern CTString ser_strMOTD = "";
extern INDEX cli_bEmulateDesync = FALSE;
extern INDEX cli_bDumpSync = FALSE;
extern INDEX cli_bDumpSyncEachTick = FALSE;
extern INDEX cli_bAutoAdjustSettings = FALSE;
extern FLOAT cli_tmAutoAdjustThreshold = 2.0f;
extern INDEX cli_bPrediction = FALSE;
extern INDEX cli_iMaxPredictionSteps = 10;
extern INDEX cli_bPredictIfServer = FALSE;
extern INDEX cli_bPredictLocalPlayers = TRUE;
extern INDEX cli_bPredictRemotePlayers = FALSE;
extern FLOAT cli_fPredictEntitiesRange = 20.0f;
extern INDEX cli_bLerpActions = FALSE;
extern INDEX cli_bReportPredicted = FALSE;
extern INDEX cli_iSendBehind = 3;
extern INDEX cli_iPredictionFlushing = 1;
extern INDEX cli_iBufferActions = 1;
extern INDEX cli_iMaxBPS = 4000;
extern INDEX cli_iMinBPS = 0;
extern INDEX net_iCompression = 1;
extern INDEX net_bLookupHostNames = FALSE;
extern INDEX net_bReportPackets = FALSE;
extern INDEX net_iMaxSendRetries = 10;
extern FLOAT net_fSendRetryWait = 0.5f;
extern INDEX net_bReportTraffic = FALSE;
extern INDEX net_bReportICMPErrors = FALSE;
extern INDEX net_bReportMiscErrors = FALSE;
extern INDEX net_bLerping = TRUE;
extern INDEX net_iGraphBuffer = 100;
extern INDEX net_iExactTimer = 2;
extern INDEX net_bDumpStreamBlocks = 0;
extern INDEX net_bDumpConnectionInfo = 0;
extern INDEX net_iPort = 25600;
extern CTString net_strLocalHost = "";
extern CTString net_strLocationCode = "";
extern CTString net_strConnectPassword = "";
extern CTString net_strAdminPassword = "";
extern CTString net_strVIPPassword = "";
extern CTString net_strObserverPassword = "";
extern INDEX net_iVIPReserve = 0;
extern INDEX net_iMaxObservers = 16;
extern INDEX net_iMaxClients = 0;
extern FLOAT net_tmConnectionTimeout = 30.0f;
extern FLOAT net_tmProblemsTimeout = 5.0f;
extern FLOAT net_tmDisconnectTimeout = 300.0f; // must be higher for level changing
extern INDEX net_bReportCRC = FALSE;
extern FLOAT net_fDropPackets = 0.0f;
extern FLOAT net_tmLatency = 0.0f;
extern INDEX ent_bReportSpawnInWall = FALSE;
extern FLOAT cmd_tmTick = 0.0f;
extern CTString cmd_cmdOnTick = "";
extern CTString cmd_strChatSender = "";
extern CTString cmd_strChatMessage = "";
extern CTString cmd_cmdOnChat = "";
extern INDEX net_ctChatMessages = 0; // counter for incoming chat messages
extern CPacketBufferStats _pbsSend;
extern CPacketBufferStats _pbsRecv;
extern BOOL _bPredictionActive = FALSE;
class CGatherCRC {
public:
BOOL bOld;
CGatherCRC();
~CGatherCRC();
};
CGatherCRC::CGatherCRC() {
bOld = CRCT_bGatherCRCs;
}
CGatherCRC::~CGatherCRC() {
CRCT_bGatherCRCs = bOld;
}
// precache control
extern INDEX _precache_NONE = PRECACHE_NONE;
extern INDEX _precache_SMART = PRECACHE_SMART;
extern INDEX _precache_ALL = PRECACHE_ALL;
extern INDEX _precache_PARANOIA = PRECACHE_PARANOIA;
extern INDEX gam_iPrecachePolicy = _precache_SMART;
extern INDEX _precache_bNowPrecaching = FALSE;
extern INDEX dbg_bBreak = FALSE;
extern INDEX gam_bPretouch = FALSE;
extern FLOAT phy_fCollisionCacheAhead = 5.0f;
extern FLOAT phy_fCollisionCacheAround = 1.5f;
extern FLOAT cli_fPredictionFilter = 0.5f;
extern INDEX shd_bCacheAll;
// input
extern INDEX inp_iKeyboardReadingMethod = 2; // 0=getasynckey, 1=virtkeytrap, 2=scancodetrap
extern INDEX inp_bAllowMouseAcceleration = TRUE;
extern FLOAT inp_fMouseSensitivity = 1.0f;
extern INDEX inp_bMousePrecision = FALSE;
extern FLOAT inp_fMousePrecisionFactor = 4.0f;
extern FLOAT inp_fMousePrecisionThreshold = 10.0f;
extern FLOAT inp_fMousePrecisionTimeout = 0.25f;
extern FLOAT inp_bInvertMouse = FALSE;
extern INDEX inp_bFilterMouse = FALSE;
extern INDEX inp_bAllowPrescan = TRUE;
extern INDEX inp_i2ndMousePort = 0; // COM no (0=disable)
extern FLOAT inp_f2ndMouseSensitivity = 1.0f;
extern INDEX inp_b2ndMousePrecision = FALSE;
extern FLOAT inp_f2ndMousePrecisionFactor = 4.0f;
extern FLOAT inp_f2ndMousePrecisionThreshold = 10.0f;
extern FLOAT inp_f2ndMousePrecisionTimeout = 0.25f;
extern INDEX inp_bInvert2ndMouse = FALSE;
extern INDEX inp_bFilter2ndMouse = FALSE;
extern INDEX inp_iMButton4Up;
extern INDEX inp_iMButton4Dn;
extern INDEX inp_iMButton5Up;
extern INDEX inp_iMButton5Dn;
extern INDEX inp_bMsgDebugger;
extern INDEX inp_ctJoysticksAllowed;
extern INDEX inp_bForceJoystickPolling;
extern INDEX inp_bAutoDisableJoysticks;
extern INDEX wed_bUseGenericTextureReplacement = FALSE;
extern void RendererInfo(void);
extern void ClearRenderer(void);
// cache all shadowmaps now
extern void CacheShadows(void)
{
// mute all sounds
_pSound->Mute();
CWorld *pwo = (CWorld*)_pShell->GetINDEX("pwoCurrentWorld");
if( pwo!=NULL) {
pwo->wo_baBrushes.CacheAllShadowmaps();
CPrintF( TRANS("All shadows recached"));
if( shd_bCacheAll) CPrintF(".\n");
else CPrintF( TRANS(", but not for long.\n(precache all shadows function is disabled)\n"));
}
// mark that we need pretouching
_bNeedPretouch = TRUE;
}
// check if a name or IP matches a mask
extern BOOL MatchesBanMask(const CTString &strString, const CTString &strMask)
{
CTString strRest = strMask;
CTString strLine;
while(strRest!="") {
strLine = strRest;
strLine.OnlyFirstLine();
strRest.RemovePrefix(strLine);
strRest.DeleteChar(0);
if (strString.Matches(strLine)) {
return TRUE;
}
}
return FALSE;
}
extern CTString RemoveSubstring(const CTString &strFull, const CTString &strSub);
static void AddIPMask(void* pArgs)
{
CTString strIP = *NEXTARGUMENT(CTString*);
ser_strIPMask+= strIP+"\n";
}
static void RemIPMask(void* pArgs)
{
CTString strIP = *NEXTARGUMENT(CTString*);
ser_strIPMask = RemoveSubstring(ser_strIPMask, strIP+"\n");
}
static void AddNameMask(void* pArgs)
{
CTString strName = *NEXTARGUMENT(CTString*);
ser_strNameMask += strName+"\n";
}
static void RemNameMask(void* pArgs)
{
CTString strName = *NEXTARGUMENT(CTString*);
ser_strNameMask = RemoveSubstring(ser_strNameMask, strName+"\n");
}
static void StartDemoRecording(void)
{
_bStartDemoRecordingNextTime = TRUE;
}
static void StopDemoRecording(void)
{
_bStopDemoRecordingNextTime = TRUE;
}
static void NetworkInfo(void)
{
CPrintF("*Network library information:\n");
CPrintF("Entities existing: %d\n", _ctEntities);
CPrintF("Predictor entities existing: %d\n", _ctPredictorEntities);
CPrintF("Server:\n");
if (_pNetwork->ga_srvServer.srv_bActive) {
CPrintF(" last processed tick: %g\n", _pNetwork->ga_srvServer.srv_tmLastProcessedTick);
CPrintF(" last processed sequence: %d\n", _pNetwork->ga_srvServer.srv_iLastProcessedSequence);
CPrintF(" players:\n");
for(INDEX iplb=0; iplb<_pNetwork->ga_srvServer.srv_aplbPlayers.Count(); iplb++) {
CPlayerBuffer &plb = _pNetwork->ga_srvServer.srv_aplbPlayers[iplb];
if (plb.plb_Active) {
CPrintF(" %2d(%2d):'%s'@client%2d: (%dact)\n",
iplb, plb.plb_Index, plb.plb_pcCharacter.GetNameForPrinting(),
plb.plb_iClient, plb.plb_abReceived.GetCount());
}
}
CPrintF(" clients:\n");
for(INDEX iSession=0; iSession<_pNetwork->ga_srvServer.srv_assoSessions.Count(); iSession++) {
CSessionSocket &sso = _pNetwork->ga_srvServer.srv_assoSessions[iSession];
if (sso.sso_bActive) {
CPrintF(" %2d:'%s'\n", iSession, _cmiComm.Server_GetClientName(iSession)),
CPrintF(" buffer: %dblk=%dk\n",
sso.sso_nsBuffer.GetUsedBlocks(),
sso.sso_nsBuffer.GetUsedMemory()/1024);
CPrintF(" state:");
if (sso.sso_iDisconnectedState>0) {
CPrintF(" disconnecting");
} else if (sso.sso_bSendStream) {
CPrintF(" connected");
} else {
CPrintF(" connecting");
}
CPrintF("\n");
}
}
} else {
CPrintF(" not a server\n");
}
CPrintF("Session state:\n");
CPrintF(" buffer: (%dblk)%dk\n",
_pNetwork->ga_sesSessionState.ses_nsGameStream.GetUsedBlocks(),
_pNetwork->ga_sesSessionState.ses_nsGameStream.GetUsedMemory()/1024);
CPrintF(" last processed tick: %g\n", _pNetwork->ga_sesSessionState.ses_tmLastProcessedTick);
CPrintF(" last processed sequence: %d\n", _pNetwork->ga_sesSessionState.ses_iLastProcessedSequence);
CPrintF(" level change: %d\n", _pNetwork->ga_sesSessionState.ses_iLevel);
for(INDEX iplt=0; iplt<_pNetwork->ga_sesSessionState.ses_apltPlayers.Count(); iplt++) {
CPlayerTarget &plt = _pNetwork->ga_sesSessionState.ses_apltPlayers[iplt];
if (plt.plt_bActive) {
ULONG ulID = -1;
if (plt.plt_penPlayerEntity!=NULL) {
ulID = plt.plt_penPlayerEntity->en_ulID;
}
CPrintF(" player %2d (ID:%d): (%dact)\n", iplt, ulID, plt.plt_abPrediction.GetCount());
}
}
if (TIMER_PROFILING) {
CTString strNetProfile;
_pfNetworkProfile.Report(strNetProfile);
CPrintF(strNetProfile);
}
}
static void ListPlayers(void)
{
CPrintF("player list:\n");
if (!_pNetwork->ga_srvServer.srv_bActive) {
CPrintF(" <not a server>\n");
return;
}
CPrintF(" client# name\n");
CPrintF(" ----------------------\n");
for(INDEX iplb=0; iplb<_pNetwork->ga_srvServer.srv_aplbPlayers.Count(); iplb++) {
CPlayerBuffer &plb = _pNetwork->ga_srvServer.srv_aplbPlayers[iplb];
if (plb.plb_Active) {
CPrintF(" %-2d %s\n", plb.plb_iClient, plb.plb_pcCharacter.GetNameForPrinting());
}
}
CPrintF(" ----------------------\n");
}
static void KickClient(INDEX iClient, const CTString &strReason)
{
if (!_pNetwork->IsServer()) {
CPrintF( TRANS("Only server can kick people!\n"));
return;
}
iClient = Clamp(iClient, INDEX(0), INDEX(NET_MAXGAMECOMPUTERS));
if (!_pNetwork->ga_srvServer.srv_assoSessions[iClient].IsActive()) {
CPrintF(TRANS("Client not connected!\n"));
return;
}
if (iClient == 0) {
CPrintF(TRANS("Can't kick local client!\n"));
return;
}
CPrintF( TRANS("Kicking %d with explanation '%s'...\n"), iClient, strReason);
_pNetwork->ga_srvServer.SendDisconnectMessage(iClient, "Admin: "+strReason);
}
static void KickClientCfunc(void* pArgs)
{
INDEX iClient = NEXTARGUMENT(INDEX);
CTString strReason = *NEXTARGUMENT(CTString*);
KickClient(iClient, strReason);
}
static void KickByName(const CTString &strName, const CTString &strReason)
{
if (!_pNetwork->IsServer()) {
CPrintF( TRANS("Only server can kick people!\n"));
return;
}
for(INDEX iplb=0; iplb<_pNetwork->ga_srvServer.srv_aplbPlayers.Count(); iplb++) {
CPlayerBuffer &plb = _pNetwork->ga_srvServer.srv_aplbPlayers[iplb];
if (plb.plb_Active && plb.plb_pcCharacter.GetNameForPrinting().Undecorated().Matches(strName)) {
KickClient(plb.plb_iClient, strReason);
}
}
}
static void KickByNameCfunc(void* pArgs)
{
CTString strName = *NEXTARGUMENT(CTString*);
CTString strReason = *NEXTARGUMENT(CTString*);
KickByName(strName, strReason);
}
static void Admin(void* pArgs)
{
CTString strCommand = *NEXTARGUMENT(CTString*);
CNetworkMessage nm(MSG_ADMIN_COMMAND);
nm<<net_strAdminPassword<<strCommand;
_pNetwork->SendToServerReliable(nm);
}
static void StockInfo(void)
{
// find memory used by shadowmap (both cached and uploaded)
INDEX ctCachedShadows=0, ctDynamicShadows=0, ctFlatShadows=0;
SLONG slStaticMemory=0, slDynamicMemory=0, slUploadMemory=0;
SLONG slShdBytes=0, slSlackMemory=0, slFlatMemory=0;
INDEX ct256=0, ct128=0, ct64=0, ct32=0, ct16=0;
SLONG sl256Memory=0, sl128Memory=0, sl64Memory=0, sl32Memory=0, sl16Memory=0;
if( _pGfx!=NULL)
{
FLOAT fSlackRatio;
FOREACHINLIST( CShadowMap, sm_lnInGfx, _pGfx->gl_lhCachedShadows, itsm)
{ // get polygon size in pixels (used portion of shadowmap)
SLONG slStaticSize, slDynamicSize, slUploadSize;
BOOL bIsFlat = itsm->GetUsedMemory( slStaticSize, slDynamicSize, slUploadSize, fSlackRatio);
SLONG slTotalSize = slDynamicSize+slUploadSize;
if( bIsFlat) {
slStaticMemory += 4;
slTotalSize += 4;
slFlatMemory += slStaticSize;
ctFlatShadows++;
} else {
slStaticMemory += slStaticSize;
slTotalSize += slStaticSize;
if( slTotalSize>0) ctCachedShadows++;
}
if( slDynamicSize>0) {
slDynamicMemory += slDynamicSize;
ctDynamicShadows++;
}
slUploadMemory += slUploadSize;
slShdBytes += slTotalSize + sizeof(CShadowMap);
slSlackMemory += slTotalSize*fSlackRatio;
if( !bIsFlat) { // by size ...
if( slStaticSize>128*1024) { ct256++; sl256Memory+=slTotalSize; }
else if( slStaticSize> 64*1024) { ct128++; sl128Memory+=slTotalSize; }
else if( slStaticSize> 32*1024) { ct64++; sl64Memory +=slTotalSize; }
else if( slStaticSize> 16*1024) { ct32++; sl32Memory +=slTotalSize; }
else if( slStaticSize> 0) { ct16++; sl16Memory +=slTotalSize; }
}
}
// report shadowmap memory usage (if any)
if( slShdBytes>0) {
CPrintF( "\nCached shadowmaps:\n");
CPrintF( " Total: %d in %d KB with %d%% (%d KB) of slack space\n", ctCachedShadows, slShdBytes/1024, slSlackMemory*100/slShdBytes, slSlackMemory/1024);
CPrintF( " Static: %d KB\n", slStaticMemory/1024);
CPrintF( " Upload: %d KB\n", slUploadMemory/1024);
CPrintF( " Dynamic: %d in %d KB\n", ctDynamicShadows, slDynamicMemory/1024);
if( ctCachedShadows<1) ctCachedShadows=1; // for percentage calc
CPrintF( " Flats: %d (%d%%) with %d KB saved\n", ctFlatShadows, ctFlatShadows*100/ctCachedShadows, slFlatMemory/1024);
CPrintF("of size:\n");
CPrintF( " >128K: %4d in %d KB\n", ct256, sl256Memory/1024);
CPrintF( " 128-64K: %4d in %d KB\n", ct128, sl128Memory/1024);
CPrintF( " 64-32K: %4d in %d KB\n", ct64, sl64Memory /1024);
CPrintF( " 32-16K: %4d in %d KB\n", ct32, sl32Memory /1024);
CPrintF( " <=16K: %4d in %d KB\n", ct16, sl16Memory /1024);
}
}
// report world stats
INDEX ctEntities=0, ctShadowLayers=0, ctPolys=0, ctPlanes=0, ctEdges=0, ctVertices=0, ctSectors=0;
SLONG slEntBytes=0, slLyrBytes=0, slPlyBytes=0, slPlnBytes=0, slEdgBytes=0, slVtxBytes=0, slSecBytes=0;
SLONG slCgrBytes=0;
CWorld *pwo = (CWorld*)_pShell->GetINDEX("pwoCurrentWorld");
if( pwo!=NULL)
{
// report count of and memory used by entities
FOREACHINDYNAMICCONTAINER( pwo->wo_cenEntities, CEntity, iten) {
ctEntities++;
slEntBytes += iten->GetUsedMemory();
}
// report shadow layers and world geometry memory usage
FOREACHINDYNAMICARRAY( pwo->wo_baBrushes.ba_abrBrushes, CBrush3D, itbr) // for all brush entities in the world
{
// skip brush without entity
if( itbr->br_penEntity==NULL) continue;
// for each mip
FOREACHINLIST( CBrushMip, bm_lnInBrush, itbr->br_lhBrushMips, itbm)
{
// for each sector in the brush mip
FOREACHINDYNAMICARRAY( itbm->bm_abscSectors, CBrushSector, itbsc)
{
// add sector class memory usage to polygons memory
ctSectors++;
slSecBytes += itbsc->GetUsedMemory();
// add each vertex and working vertex in sector
ctVertices += itbsc->bsc_abvxVertices.Count();
FOREACHINSTATICARRAY( itbsc->bsc_abvxVertices, CBrushVertex, itbvx) slVtxBytes += itbvx->GetUsedMemory();
FOREACHINSTATICARRAY( itbsc->bsc_awvxVertices, CWorkingVertex, itwvx) slVtxBytes += 32; // aligned to 32 bytes!
// add each plane and working plane in sector
ctPlanes += itbsc->bsc_abplPlanes.Count();
FOREACHINSTATICARRAY( itbsc->bsc_abplPlanes, CBrushPlane, itbpl) slPlnBytes += itbpl->GetUsedMemory();
FOREACHINSTATICARRAY( itbsc->bsc_awplPlanes, CWorkingPlane, itwpl) slPlnBytes += sizeof(CWorkingPlane);
// add each edge and working edge in sector
ctEdges += itbsc->bsc_abedEdges.Count();
FOREACHINSTATICARRAY( itbsc->bsc_abedEdges, CBrushEdge, itbed) slEdgBytes += itbed->GetUsedMemory();
FOREACHINSTATICARRAY( itbsc->bsc_awedEdges, CWorkingEdge, itwed) slEdgBytes += sizeof(CWorkingEdge);
// for each polygon in sector
ctPolys += itbsc->bsc_abpoPolygons.Count();
FOREACHINSTATICARRAY( itbsc->bsc_abpoPolygons, CBrushPolygon, itbpo) {
CBrushPolygon &bpo = *itbpo;
slPlyBytes += bpo.GetUsedMemory();
// count in the shadow layers (if any)
if( bpo.bpo_smShadowMap.bsm_lhLayers.IsEmpty()) continue; // skip polygon without shadowmap
ctShadowLayers += bpo.bpo_smShadowMap.GetShadowLayersCount();
slLyrBytes += bpo.bpo_smShadowMap.GetUsedMemory();
}
}
}
} // add in memory used by collision grid
extern SLONG GetCollisionGridMemory( CCollisionGrid *pcg);
slCgrBytes += GetCollisionGridMemory( pwo->wo_pcgCollisionGrid);
}
// stock info
const DOUBLE dToMB = 1.0/1024.0/1024.0;
const FLOAT fTexBytes = dToMB * _pTextureStock->CalculateUsedMemory();
const FLOAT fMdlBytes = dToMB * _pModelStock->CalculateUsedMemory();
const FLOAT fSndBytes = dToMB * _pSoundStock->CalculateUsedMemory();
const FLOAT fMshBytes = dToMB * _pMeshStock->CalculateUsedMemory();
const FLOAT fAstBytes = dToMB * _pAnimSetStock->CalculateUsedMemory();
const FLOAT fShaBytes = dToMB * _pShaderStock->CalculateUsedMemory();
const FLOAT fSkaBytes = dToMB * _pSkeletonStock->CalculateUsedMemory();
CPrintF("\nStock information:\n");
CPrintF(" Textures: %5d (%5.2f MB)\n", _pTextureStock->GetTotalCount(), fTexBytes);
CPrintF(" ShadowMaps: %5d (%5.2f MB)\n", ctCachedShadows, slShdBytes*dToMB);
CPrintF(" Entities: %5d (%5.2f MB)\n", ctEntities, slEntBytes*dToMB);
CPrintF(" Sounds: %5d (%5.2f MB)\n", _pSoundStock->GetTotalCount(), fSndBytes);
CPrintF("\n");
CPrintF(" Sectors: %5d (%5.2f MB)\n", ctSectors, slSecBytes*dToMB);
CPrintF(" Planes: %5d (%5.2f MB)\n", ctPlanes, slPlnBytes*dToMB);
CPrintF(" Edges: %5d (%5.2f MB)\n", ctEdges, slEdgBytes*dToMB);
CPrintF(" Polygons: %5d (%5.2f MB)\n", ctPolys, slPlyBytes*dToMB);
CPrintF(" Vertices: %5d (%5.2f MB)\n", ctVertices, slVtxBytes*dToMB);
CPrintF(" ShadowLayers: %5d (%5.2f MB)\n", ctShadowLayers, slLyrBytes*dToMB);
CPrintF("\n");
CPrintF(" Models: %5d (%5.2f MB)\n", _pModelStock->GetTotalCount(), fMdlBytes);
CPrintF(" Meshes: %5d (%5.2f MB)\n", _pMeshStock->GetTotalCount(), fMshBytes);
CPrintF(" Skeletons: %5d (%5.2f MB)\n", _pSkeletonStock->GetTotalCount(), fSkaBytes);
CPrintF(" AnimSets: %5d (%5.2f MB)\n", _pAnimSetStock->GetTotalCount(), fAstBytes);
CPrintF(" Shaders: %5d (%5.2f MB)\n", _pShaderStock->GetTotalCount(), fShaBytes);
CPrintF("\n");
CPrintF("CollisionGrid: %.2f MB\n", slCgrBytes*dToMB);
CPrintF("--------------\n");
CPrintF(" Total: %.2f MB\n", fTexBytes+fSndBytes+fMdlBytes+fMshBytes+fSkaBytes+fAstBytes+fShaBytes
+ (slShdBytes+slEntBytes+slSecBytes+slPlnBytes+slEdgBytes+slPlyBytes+slVtxBytes+slLyrBytes+slCgrBytes)*dToMB);
CPrintF("\n");
}
static void StockDump(void)
{
try {
CTFileStream strm;
CTFileName fnm = CTString("Temp\\StockDump.txt");
strm.Create_t(fnm);
strm.PutLine_t("Animations:");
_pAnimStock->DumpMemoryUsage_t(strm);
strm.PutLine_t("Textures:");
_pTextureStock->DumpMemoryUsage_t(strm);
strm.PutLine_t("Models:");
_pModelStock->DumpMemoryUsage_t(strm);
strm.PutLine_t("Sounds:");
_pSoundStock->DumpMemoryUsage_t(strm);
strm.PutLine_t("Classes:");
_pEntityClassStock->DumpMemoryUsage_t(strm);
CPrintF("Dumped to '%s'\n", CTString(fnm));
} catch (char *strError) {
CPrintF("Error: %s\n", strError);
}
}
// free all unused stocks
extern void FreeUnusedStock(void)
{
// free all unused stocks
_pEntityClassStock->FreeUnused();
_pModelStock->FreeUnused();
_pSoundStock->FreeUnused();
_pTextureStock->FreeUnused();
_pAnimStock->FreeUnused();
}
/*
* This is called every TickQuantum seconds.
*/
void CNetworkTimerHandler::HandleTimer(void)
{
if (this==NULL || _bTempNetwork) {
return; // this can happen during NET_MakeDefaultState_t()!
}
// enable stream handling during timer
CTSTREAM_BEGIN {
// do the timer loop
_pNetwork->TimerLoop();
} CTSTREAM_END;
}
/*
* Default constructor.
*/
CNetworkLibrary::CNetworkLibrary(void) :
ga_IsServer(FALSE), // is not server
ga_bDemoRec(FALSE), // not recording demo
ga_bDemoPlay(FALSE), // not playing demo
ga_bDemoPlayFinished(FALSE), // demo not finished
ga_srvServer(*new CServer),
ga_sesSessionState(*new CSessionState)
{
ga_aplsPlayers.New(NET_MAXLOCALPLAYERS);
// default demo syncronization is real-time, with 1:1 playback speed
ga_fDemoSyncRate = DEMOSYNC_REALTIME;
ga_fDemoRealTimeFactor = 1.0f;
ga_fGameRealTimeFactor = 1.0f;
ga_pubDefaultState = NULL;
ga_slDefaultStateSize = 0;
memset(ga_aubDefaultProperties, 0, sizeof(ga_aubDefaultProperties));
ga_pubCRCList = NULL;
ga_slCRCList = 0;
ga_ulDemoMinorVersion = _SE_BUILD_MINOR;
ga_csNetwork.cs_iIndex = 2000;
ga_ctTimersPending = -1;
ga_fEnumerationProgress = 0;
ga_bEnumerationChange = FALSE;
}
/*
* Destructor.
*/
CNetworkLibrary::~CNetworkLibrary(void)
{
// clear the global world
ga_World.DeletePredictors();
ga_World.Clear();
// free renderer info to free pointers to entities etc.
if (!_bTempNetwork) {
extern void ClearRenderer(void);
ClearRenderer();
}
delete &ga_sesSessionState;
delete &ga_srvServer;
}
/*
* Initialize game management.
*/
void CNetworkLibrary::Init(const CTString &strGameID)
{
// remember the game ID
CMessageDispatcher::Init(strGameID);
// add shell symbols
_pShell->DeclareSymbol("user INDEX dbg_bBreak;", &dbg_bBreak);
_pShell->DeclareSymbol("persistent user INDEX gam_bPretouch;", &gam_bPretouch);
_pShell->DeclareSymbol("user INDEX dem_iRecordedNumber;", &dem_iRecordedNumber);
_pShell->DeclareSymbol("user void StartDemoRecording(void);", &StartDemoRecording);
_pShell->DeclareSymbol("user void StopDemoRecording(void);", &StopDemoRecording);
_pShell->DeclareSymbol("user void NetworkInfo(void);", &NetworkInfo);
_pShell->DeclareSymbol("user void StockInfo(void);", &StockInfo);
_pShell->DeclareSymbol("user void StockDump(void);", &StockDump);
_pShell->DeclareSymbol("user void RendererInfo(void);", &RendererInfo);
_pShell->DeclareSymbol("user void ClearRenderer(void);", &ClearRenderer);
_pShell->DeclareSymbol("user void CacheShadows(void);", &CacheShadows);
_pShell->DeclareSymbol("user void KickClient(INDEX, CTString);", &KickClientCfunc);
_pShell->DeclareSymbol("user void KickByName(CTString, CTString);", &KickByNameCfunc);
_pShell->DeclareSymbol("user void ListPlayers(void);", &ListPlayers);
_pShell->DeclareSymbol("user void Admin(CTString);", &Admin);
_pShell->DeclareSymbol("user void AddIPMask(CTString);", &AddIPMask);
_pShell->DeclareSymbol("user void RemIPMask(CTString);", &RemIPMask);
_pShell->DeclareSymbol("user void AddNameMask(CTString);", &AddNameMask);
_pShell->DeclareSymbol("user void RemNameMask(CTString);", &RemNameMask);
_pShell->DeclareSymbol("user FLOAT dem_tmTimer;", &ga_fDemoTimer);
_pShell->DeclareSymbol("user FLOAT dem_fSyncRate;", &ga_fDemoSyncRate);
_pShell->DeclareSymbol("user FLOAT dem_fRealTimeFactor;", &ga_fDemoRealTimeFactor);
_pShell->DeclareSymbol("user FLOAT gam_fRealTimeFactor;", &ga_fGameRealTimeFactor);
_pShell->DeclareSymbol("user const FLOAT net_tmLatency;", &net_tmLatency);
_pShell->DeclareSymbol("user const FLOAT cmd_tmTick;", &cmd_tmTick);
_pShell->DeclareSymbol("persistent user CTString cmd_cmdOnTick;", &cmd_cmdOnTick);
_pShell->DeclareSymbol("user CTString cmd_strChatSender ;", &cmd_strChatSender );
_pShell->DeclareSymbol("user CTString cmd_strChatMessage;", &cmd_strChatMessage);
_pShell->DeclareSymbol("persistent user CTString cmd_cmdOnChat;", &cmd_cmdOnChat);
_pShell->DeclareSymbol("user INDEX net_ctChatMessages;", &net_ctChatMessages);
_pShell->DeclareSymbol("persistent user INDEX ent_bReportSpawnInWall;", &ent_bReportSpawnInWall);
_pShell->DeclareSymbol("user INDEX ser_bReportSyncOK;", &ser_bReportSyncOK);
_pShell->DeclareSymbol("user INDEX ser_bReportSyncBad;", &ser_bReportSyncBad);
_pShell->DeclareSymbol("user INDEX ser_bReportSyncLate;", &ser_bReportSyncLate);
_pShell->DeclareSymbol("user INDEX ser_bReportSyncEarly;", &ser_bReportSyncEarly);
_pShell->DeclareSymbol("user INDEX ser_bPauseOnSyncBad;", &ser_bPauseOnSyncBad);
_pShell->DeclareSymbol("user INDEX ser_iKickOnSyncBad;", &ser_iKickOnSyncBad);
_pShell->DeclareSymbol("user INDEX ser_bKickOnSyncLate;", &ser_bKickOnSyncLate);
_pShell->DeclareSymbol("persistent user FLOAT ser_tmSyncCheckFrequency;", &ser_tmSyncCheckFrequency);
_pShell->DeclareSymbol("persistent user INDEX ser_iSyncCheckBuffer;", &ser_iSyncCheckBuffer);
_pShell->DeclareSymbol("persistent user INDEX cli_bLerpActions;", &cli_bLerpActions);
_pShell->DeclareSymbol("persistent user INDEX cli_bReportPredicted;", &cli_bReportPredicted);
_pShell->DeclareSymbol("persistent user INDEX net_iExactTimer;", &net_iExactTimer);
_pShell->DeclareSymbol("user INDEX net_bDumpStreamBlocks;", &net_bDumpStreamBlocks);
_pShell->DeclareSymbol("user INDEX net_bDumpConnectionInfo;", &net_bDumpConnectionInfo);
_pShell->DeclareSymbol("user INDEX net_iPort;", &net_iPort);
_pShell->DeclareSymbol("persistent user CTString net_strLocalHost;", &net_strLocalHost);
_pShell->DeclareSymbol("persistent user CTString net_strLocationCode;", &net_strLocationCode);
_pShell->DeclareSymbol("user CTString net_strVIPPassword;", &net_strVIPPassword);
_pShell->DeclareSymbol("user CTString net_strObserverPassword;", &net_strObserverPassword);
_pShell->DeclareSymbol("user INDEX net_iVIPReserve;", &net_iVIPReserve);
_pShell->DeclareSymbol("user INDEX net_iMaxObservers;", &net_iMaxObservers);
_pShell->DeclareSymbol("user INDEX net_iMaxClients;", &net_iMaxClients);
_pShell->DeclareSymbol("user CTString net_strConnectPassword;", &net_strConnectPassword);
_pShell->DeclareSymbol("user CTString net_strAdminPassword;", &net_strAdminPassword);
_pShell->DeclareSymbol("user FLOAT net_tmConnectionTimeout;", &net_tmConnectionTimeout);
_pShell->DeclareSymbol("user FLOAT net_tmProblemsTimeout;", &net_tmProblemsTimeout);
_pShell->DeclareSymbol("user FLOAT net_tmDisconnectTimeout;", &net_tmDisconnectTimeout);
_pShell->DeclareSymbol("user INDEX net_bReportCRC;", &net_bReportCRC);
_pShell->DeclareSymbol("user INDEX ser_iRememberBehind;", &ser_iRememberBehind);
_pShell->DeclareSymbol("user INDEX cli_bEmulateDesync;", &cli_bEmulateDesync);
_pShell->DeclareSymbol("user INDEX cli_bDumpSync;", &cli_bDumpSync);
_pShell->DeclareSymbol("user INDEX cli_bDumpSyncEachTick;",&cli_bDumpSyncEachTick);
_pShell->DeclareSymbol("persistent user INDEX ser_iExtensiveSyncCheck;", &ser_iExtensiveSyncCheck);
_pShell->DeclareSymbol("persistent user INDEX net_bLookupHostNames;", &net_bLookupHostNames);
_pShell->DeclareSymbol("persistent user INDEX net_iCompression ;", &net_iCompression);
_pShell->DeclareSymbol("persistent user INDEX net_bReportPackets;", &net_bReportPackets);
_pShell->DeclareSymbol("persistent user INDEX net_iMaxSendRetries;", &net_iMaxSendRetries);
_pShell->DeclareSymbol("persistent user FLOAT net_fSendRetryWait;", &net_fSendRetryWait);
_pShell->DeclareSymbol("persistent user INDEX net_bReportTraffic;", &net_bReportTraffic);
_pShell->DeclareSymbol("persistent user INDEX net_bReportICMPErrors;", &net_bReportICMPErrors);
_pShell->DeclareSymbol("persistent user INDEX net_bReportMiscErrors;", &net_bReportMiscErrors);
_pShell->DeclareSymbol("persistent user INDEX net_bLerping;", &net_bLerping);
_pShell->DeclareSymbol("persistent user INDEX ser_bClientsMayPause;", &ser_bClientsMayPause);
_pShell->DeclareSymbol("persistent user INDEX ser_bEnumeration;", &ser_bEnumeration);
_pShell->DeclareSymbol("persistent user INDEX ser_bPingGameAgent;", &ser_bPingGameAgent);
_pShell->DeclareSymbol("persistent user FLOAT ser_tmKeepAlive;", &ser_tmKeepAlive);
_pShell->DeclareSymbol("persistent user FLOAT ser_tmPingUpdate;", &ser_tmPingUpdate);
_pShell->DeclareSymbol("persistent user INDEX ser_bWaitFirstPlayer;", &ser_bWaitFirstPlayer);
_pShell->DeclareSymbol("persistent user INDEX ser_iMaxAllowedBPS;", &ser_iMaxAllowedBPS);
_pShell->DeclareSymbol("persistent user INDEX ser_iMaxAllowedBPS;", &ser_iMaxAllowedBPS);
_pShell->DeclareSymbol("persistent user CTString ser_strIPMask;", &ser_strIPMask);
_pShell->DeclareSymbol("persistent user CTString ser_strNameMask;", &ser_strNameMask);
_pShell->DeclareSymbol("persistent user INDEX ser_bInverseBanning;", &ser_bInverseBanning);
_pShell->DeclareSymbol("persistent user CTString ser_strMOTD;", &ser_strMOTD);
_pShell->DeclareSymbol("persistent user INDEX cli_bAutoAdjustSettings;", &cli_bAutoAdjustSettings);
_pShell->DeclareSymbol("persistent user FLOAT cli_tmAutoAdjustThreshold;", &cli_tmAutoAdjustThreshold);
_pShell->DeclareSymbol("persistent user INDEX cli_bPrediction;", &cli_bPrediction);
_pShell->DeclareSymbol("persistent user INDEX cli_iMaxPredictionSteps;", &cli_iMaxPredictionSteps);
_pShell->DeclareSymbol("persistent user INDEX cli_bPredictIfServer;", &cli_bPredictIfServer);
_pShell->DeclareSymbol("persistent user INDEX cli_bPredictLocalPlayers;", &cli_bPredictLocalPlayers);
_pShell->DeclareSymbol("persistent user INDEX cli_bPredictRemotePlayers;", &cli_bPredictRemotePlayers);
_pShell->DeclareSymbol("persistent user FLOAT cli_fPredictEntitiesRange;", &cli_fPredictEntitiesRange);
_pShell->DeclareSymbol("persistent user FLOAT cli_fPredictionFilter;", &cli_fPredictionFilter);
_pShell->DeclareSymbol("persistent user INDEX cli_iSendBehind;", &cli_iSendBehind);
_pShell->DeclareSymbol("persistent user INDEX cli_iPredictionFlushing;", &cli_iPredictionFlushing);
_pShell->DeclareSymbol("persistent user INDEX cli_iBufferActions;", &cli_iBufferActions);
_pShell->DeclareSymbol("persistent user INDEX cli_iMaxBPS;", &cli_iMaxBPS);
_pShell->DeclareSymbol("persistent user INDEX cli_iMinBPS;", &cli_iMinBPS);
_pShell->DeclareSymbol("user FLOAT net_fLimitLatencySend;", &_pbsSend.pbs_fLatencyLimit);
_pShell->DeclareSymbol("user FLOAT net_fLimitLatencyRecv;", &_pbsRecv.pbs_fLatencyLimit);
_pShell->DeclareSymbol("user FLOAT net_fLatencyVariationSend;", &_pbsSend.pbs_fLatencyVariation);
_pShell->DeclareSymbol("user FLOAT net_fLatencyVariationRecv;", &_pbsRecv.pbs_fLatencyVariation);
_pShell->DeclareSymbol("user FLOAT net_fLimitBandwidthSend;", &_pbsSend.pbs_fBandwidthLimit);
_pShell->DeclareSymbol("user FLOAT net_fLimitBandwidthRecv;", &_pbsRecv.pbs_fBandwidthLimit);
_pShell->DeclareSymbol("user FLOAT net_fDropPackets;", &net_fDropPackets);
_pShell->DeclareSymbol("persistent user INDEX net_iGraphBuffer;", &net_iGraphBuffer);
_pShell->DeclareSymbol("user const INDEX precache_NONE;", &_precache_NONE);
_pShell->DeclareSymbol("user const INDEX precache_SMART;", &_precache_SMART);
_pShell->DeclareSymbol("user const INDEX precache_ALL;", &_precache_ALL);
_pShell->DeclareSymbol("user const INDEX precache_PARANOIA;", &_precache_PARANOIA);
_pShell->DeclareSymbol("persistent user INDEX gam_iPrecachePolicy;", &gam_iPrecachePolicy);
_pShell->DeclareSymbol("user FLOAT phy_fCollisionCacheAhead;", &phy_fCollisionCacheAhead);
_pShell->DeclareSymbol("user FLOAT phy_fCollisionCacheAround;", &phy_fCollisionCacheAround);
_pShell->DeclareSymbol("persistent user INDEX inp_iKeyboardReadingMethod;", &inp_iKeyboardReadingMethod);
_pShell->DeclareSymbol("persistent user INDEX inp_bAllowMouseAcceleration;", &inp_bAllowMouseAcceleration);
_pShell->DeclareSymbol("persistent user FLOAT inp_fMouseSensitivity;", &inp_fMouseSensitivity);
_pShell->DeclareSymbol("persistent user INDEX inp_bMousePrecision;", &inp_bMousePrecision);
_pShell->DeclareSymbol("persistent user FLOAT inp_fMousePrecisionFactor;", &inp_fMousePrecisionFactor);
_pShell->DeclareSymbol("persistent user FLOAT inp_fMousePrecisionThreshold;", &inp_fMousePrecisionThreshold);
_pShell->DeclareSymbol("persistent user FLOAT inp_fMousePrecisionTimeout;", &inp_fMousePrecisionTimeout);
_pShell->DeclareSymbol("persistent user INDEX inp_bInvertMouse;", &inp_bInvertMouse);
_pShell->DeclareSymbol("persistent user INDEX inp_bFilterMouse;", &inp_bFilterMouse);
_pShell->DeclareSymbol("persistent user INDEX inp_bAllowPrescan;", &inp_bAllowPrescan);
_pShell->DeclareSymbol("persistent user INDEX inp_i2ndMousePort;", &inp_i2ndMousePort);
_pShell->DeclareSymbol("persistent user INDEX inp_bInvert2ndMouse;", &inp_bInvert2ndMouse);
_pShell->DeclareSymbol("persistent user INDEX inp_bFilter2ndMouse;", &inp_bFilter2ndMouse);
_pShell->DeclareSymbol("persistent user FLOAT inp_f2ndMouseSensitivity;", &inp_f2ndMouseSensitivity);
_pShell->DeclareSymbol("persistent user INDEX inp_b2ndMousePrecision;", &inp_b2ndMousePrecision);
_pShell->DeclareSymbol("persistent user FLOAT inp_f2ndMousePrecisionFactor;", &inp_f2ndMousePrecisionFactor);
_pShell->DeclareSymbol("persistent user FLOAT inp_f2ndMousePrecisionThreshold;", &inp_f2ndMousePrecisionThreshold);
_pShell->DeclareSymbol("persistent user FLOAT inp_f2ndMousePrecisionTimeout;", &inp_f2ndMousePrecisionTimeout);
_pShell->DeclareSymbol("persistent user INDEX inp_bMsgDebugger;", &inp_bMsgDebugger);
_pShell->DeclareSymbol("persistent user INDEX inp_iMButton4Up;", &inp_iMButton4Up);
_pShell->DeclareSymbol("persistent user INDEX inp_iMButton4Dn;", &inp_iMButton4Dn);
_pShell->DeclareSymbol("persistent user INDEX inp_iMButton5Up;", &inp_iMButton5Up);
_pShell->DeclareSymbol("persistent user INDEX inp_iMButton5Dn;", &inp_iMButton5Dn);
_pShell->DeclareSymbol("persistent user INDEX inp_ctJoysticksAllowed;", &inp_ctJoysticksAllowed);
_pShell->DeclareSymbol("persistent user INDEX inp_bForceJoystickPolling;", &inp_bForceJoystickPolling);
_pShell->DeclareSymbol("persistent user INDEX inp_bAutoDisableJoysticks;", &inp_bAutoDisableJoysticks);
_pShell->DeclareSymbol("persistent user INDEX wed_bUseGenericTextureReplacement;", &wed_bUseGenericTextureReplacement);
_pShell->DeclareSymbol("user CTString ga_strServer;", &ga_strServer);
_pShell->DeclareSymbol("INDEX pwoCurrentWorld;", &_pwoCurrentWorld);
}
/*
* Add the timer handler.
*/
void CNetworkLibrary::AddTimerHandler(void)
{
if (this==NULL || _bTempNetwork) {
return; // this can happen during NET_MakeDefaultState_t()!
}
_pTimer->AddHandler(&ga_thTimerHandler);
}
/*
* Remove the timer handler.
*/
void CNetworkLibrary::RemoveTimerHandler(void)
{
if (this==NULL || _bTempNetwork) {
return; // this can happen during NET_MakeDefaultState_t()!
}
_pTimer->RemHandler(&ga_thTimerHandler);
}
/*
// set settings to prediction-off
void AdjustPredictionOff(void)
{
if (!cli_bAutoAdjustSettings) {
return;
}
if (cli_bPrediction) {
CPrintF("AutoAdjustment: prediction off, buffer 1\n");
}
cli_bPrediction = 0;
cli_iBufferActions = 1;
}
// set settings to prediction-on
void AdjustPredictionOn(void)
{
if (!cli_bAutoAdjustSettings) {
return;
}
if (!cli_bPrediction) {
CPrintF("AutoAdjustment: prediction on, buffer 3\n");
}
cli_bPrediction = 1;
cli_iBufferActions = 3;
}
// automatically adjust network settings
void CNetworkLibrary::AutoAdjustSettings(void)
{
// if server and not debugging prediction
if (IsServer() && !cli_bPredictIfServer) {
// just turn it all off
AdjustPredictionOff();
return;
}
static TIME _tmLastTimeNoPredictionSteps = -1;
// get network lag in terms of ticks
INDEX ctLagTicks = ga_sesSessionState.GetPredictionStepsCount()-(cli_iBufferActions-1);
// if no significant lag
if (ctLagTicks<=1) {
// set settings to prediction-off
AdjustPredictionOff();
_tmLastTimeNoPredictionSteps = _pTimer->CurrentTick();
// if there is lag now for some time
} else if (_pTimer->CurrentTick()-_tmLastTimeNoPredictionSteps>=cli_tmAutoAdjustThreshold) {
// set settings to prediction-on
AdjustPredictionOn();
}
}
*/
/*
* Start a peer-to-peer game session.
*
* remember to keep this routine up to date with CNetworkLibrary::Read()
*/
void CNetworkLibrary::StartPeerToPeer_t(const CTString &strSessionName,
const CTFileName &fnmWorld, ULONG ulSpawnFlags,
INDEX ctMaxPlayers, BOOL bWaitAllPlayers,
void *pvSessionProperties) // throw char *
{
// mute all sounds
_pSound->Mute();
// go on
CPrintF( TRANS("Starting session: '%s'\n"), strSessionName);
CPrintF( TRANS(" level: '%s'\n"), (const char*) fnmWorld);
CPrintF( TRANS(" spawnflags: %08x\n"), ulSpawnFlags);
CPrintF( TRANS(" max players: %d\n"), ctMaxPlayers);
CPrintF( TRANS(" waiting: %d\n"), bWaitAllPlayers);
CGatherCRC gc;
// if starting in network
if (_cmiComm.IsNetworkEnabled()) {
CPrintF( TRANS(" network is on\n"));
// start gathering CRCs
InitCRCGather();
// make default state data for creating deltas
MakeDefaultState(fnmWorld, ulSpawnFlags, pvSessionProperties);
} else {
CPrintF( TRANS(" network is off\n"));
}
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
ga_ctTimersPending = -1; // disable timer pending
ga_strSessionName = strSessionName;
ga_bLocalPause = FALSE;
ga_sesSessionState.ses_iLevel+=1;
ga_sesSessionState.ses_ulSpawnFlags = ulSpawnFlags;
ga_sesSessionState.ses_tmSyncCheckFrequency = ser_tmSyncCheckFrequency;
ga_sesSessionState.ses_iExtensiveSyncCheck = ser_iExtensiveSyncCheck;
memcpy(ga_aubProperties, pvSessionProperties, NET_MAXSESSIONPROPERTIES);
// remember the world filename
ga_fnmWorld = fnmWorld;
ga_fnmNextLevel = CTString("");
try {
// load the world
_pTimer->SetCurrentTick(0.0f); // must have timer at 0 while loading
ga_World.Load_t(fnmWorld);
// delete all entities that don't fit given spawn flags
ga_World.FilterEntitiesBySpawnFlags(ga_sesSessionState.ses_ulSpawnFlags);
} catch(char *) {
ga_fnmWorld = CTString("");
_cmiComm.Server_Close();
_cmiComm.Client_Close();
throw;
}
// remember the world pointer
_pShell->SetINDEX("pwoCurrentWorld", (INDEX)&ga_World);
SetProgressDescription(TRANS("starting server"));
CallProgressHook_t(0.0f);
// initialize server
try {
ga_srvServer.Start_t();
} catch (char *) {
ga_World.DeletePredictors();
ga_World.Clear();
throw;
}
ga_IsServer = TRUE;
ga_ulDemoMinorVersion = _SE_BUILD_MINOR;
CallProgressHook_t(1.0f);
// start the timer loop
AddTimerHandler();
SetProgressDescription(TRANS("starting session"));
CallProgressHook_t(0.0f);
// initialize session state
try {
ga_sesSessionState.Start_t(-1);
} catch (char *strError) {
(void)strError;
RemoveTimerHandler();
ga_srvServer.Stop();
ga_World.DeletePredictors();
ga_World.Clear();
throw;
}
CallProgressHook_t(1.0f);
// remember maximum number of players
ga_sesSessionState.ses_ctMaxPlayers = ctMaxPlayers;
ga_sesSessionState.ses_bWaitAllPlayers = bWaitAllPlayers;
// time speed is normal by default
ga_sesSessionState.ses_fRealTimeFactor = 1.0f;
// eventually cache all shadowmaps in world (memory eater!)
if( shd_bCacheAll) ga_World.wo_baBrushes.CacheAllShadowmaps();
// flush stale caches
FreeUnusedStock();
// mark that pretouching is required
_bNeedPretouch = TRUE;
// start timer sync anew
ga_ctTimersPending = 0;
FinishCRCGather();
CPrintF( TRANS(" started.\n"));
}
/*
* Save the game.
*/
void CNetworkLibrary::Save_t(const CTFileName &fnmGame) // throw char *
{
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
// must be server
if (!ga_IsServer) {
throw TRANS("Cannot save game - not a server!\n");
}
// create the file
CTFileStream strmFile;
strmFile.Create_t(fnmGame);
// write game to stream
strmFile.WriteID_t("GAME");
ga_sesSessionState.Write_t(&strmFile);
strmFile.WriteID_t("GEND"); // game end
}
/*
* Load the game.
*
* remember to keep this routine up to date with CNetworkLibrary::StartPeerToPeer()
*/
void CNetworkLibrary::Load_t(const CTFileName &fnmGame) // throw char *
{
// mute all sounds
_pSound->Mute();
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
ga_ctTimersPending = -1; // disable timer pending
CGatherCRC gc;
ga_bLocalPause = FALSE;
// open the file
CTFileStream strmFile;
strmFile.Open_t(fnmGame);
// if starting in network
if (_cmiComm.IsNetworkEnabled()) {
// start gathering CRCs
InitCRCGather();
}
// initialize server
ga_srvServer.Start_t();
ga_IsServer = TRUE;
ga_ulDemoMinorVersion = _SE_BUILD_MINOR;
ga_fnmNextLevel = CTString("");
memset(ga_aubProperties, 0, NET_MAXSESSIONPROPERTIES);
// start the timer loop
AddTimerHandler();
strmFile.ExpectID_t("GAME");
// read session state
try {
ga_sesSessionState.Start_t(-1);
ga_sesSessionState.Read_t(&strmFile);
// if starting in network
if (_cmiComm.IsNetworkEnabled()) {
// make default state data for creating deltas
MakeDefaultState(ga_fnmWorld, ga_sesSessionState.ses_ulSpawnFlags,
ga_aubProperties);
}
// players will be connected later
ga_sesSessionState.ses_apltPlayers.Clear();
ga_sesSessionState.ses_apltPlayers.New(NET_MAXGAMEPLAYERS);
strmFile.ExpectID_t("GEND"); // game end
} catch(char *) {
RemoveTimerHandler();
ga_srvServer.Stop();
ga_IsServer = FALSE;
throw;
}
// set time and pause for server from the saved game
ga_sesSessionState.ses_iLevel+=1;
ga_srvServer.srv_tmLastProcessedTick = ga_sesSessionState.ses_tmLastProcessedTick;
ga_srvServer.srv_iLastProcessedSequence = ga_sesSessionState.ses_iLastProcessedSequence;
ga_srvServer.srv_bPause = ga_sesSessionState.ses_bPause;
ga_srvServer.srv_bGameFinished = ga_sesSessionState.ses_bGameFinished;
ga_sesSessionState.ses_tmPredictionHeadTick = ga_sesSessionState.ses_tmLastProcessedTick;
// start sending stream to local state
ga_srvServer.srv_assoSessions[0].sso_bSendStream = TRUE;
ga_srvServer.srv_assoSessions[0].sso_iLastSentSequence = ga_srvServer.srv_iLastProcessedSequence;
// eventually cache all shadowmaps in world (memory eater!)
if( shd_bCacheAll) ga_World.wo_baBrushes.CacheAllShadowmaps();
// flush stale caches
FreeUnusedStock();
// mark that pretouching is required
_bNeedPretouch = TRUE;
// start timer sync anew
ga_ctTimersPending = 0;
FinishCRCGather();
}
/*
* Save a debugging game.
*/
void CNetworkLibrary::DebugSave(void)
{
// try to save game
try {
Save_t(CTString("Save\\Debug.sav"));
// if not successful
} catch (char *strError){
FatalError("Cannot save debug game:\n%s", strError);
}
}
/* Enumerate existing sessions. */
void CNetworkLibrary::EnumSessions(BOOL bInternet)
{
// clear old list
FORDELETELIST(CNetworkSession, ns_lnNode, ga_lhEnumeratedSessions, itns) {
delete &*itns;
}
// make sure network is on
if (!_cmiComm.IsNetworkEnabled()) {
_cmiComm.PrepareForUse(/*network*/TRUE, /*client*/FALSE); // have to enumerate as server
}
// request enumeration
GameAgent_EnumTrigger(bInternet);
}
/*
* Join a running multi-player game.
*/
void CNetworkLibrary::JoinSession_t(const CNetworkSession &nsSesssion, INDEX ctLocalPlayers) // throw char *
{
// mute all sounds
_pSound->Mute();
// report session addres
CPrintF( TRANS("Joining session at: '%s'\n"), nsSesssion.ns_strAddress);
ga_bLocalPause = FALSE;
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
ga_ctTimersPending = -1; // disable timer pending
// start gathering CRCs
CGatherCRC gc;
InitCRCGather();
// set session name and server address
ga_strSessionName = nsSesssion.ns_strSession;
ga_strServerAddress = nsSesssion.ns_strAddress;
ga_fnmNextLevel = CTString("");
ga_fnmWorld = CTString("");
memset(ga_aubProperties, 0, NET_MAXSESSIONPROPERTIES);
ga_IsServer = FALSE;
ga_ulDemoMinorVersion = _SE_BUILD_MINOR;
// start the timer loop
AddTimerHandler();
SetProgressDescription(TRANS("connecting"));
CallProgressHook_t(0.0f);
// initialize session state
try {
ga_sesSessionState.Start_t(ctLocalPlayers);
} catch(char *) {
RemoveTimerHandler();
throw;
}
// remember the world pointer
_pShell->SetINDEX("pwoCurrentWorld", (INDEX)&ga_World);
// eventually cache all shadowmaps in world (memory eater!)
if( shd_bCacheAll) ga_World.wo_baBrushes.CacheAllShadowmaps();
// flush stale caches
FreeUnusedStock();
// mark that pretouching is required
_bNeedPretouch = TRUE;
// run main loop to let session state process messages from server
MainLoop();
// start timer sync anew
ga_ctTimersPending = 0;
// initially auto adjust prediction on
// AdjustPredictionOn();
CPrintF(" joined\n");
}
/* Start playing a demo. */
void CNetworkLibrary::StartDemoPlay_t(const CTFileName &fnDemo) // throw char *
{
// mute all sounds
_pSound->Mute();
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
ga_ctTimersPending = -1; // disable timer pending
ga_bLocalPause = FALSE;
// open the file
ga_strmDemoPlay.Open_t(fnDemo);
// remember that playing demo
ga_bDemoPlay = TRUE;
ga_bDemoPlayFinished = FALSE;
// create session name from demo name
CTString strSessionName = CTString("Demo: ")+fnDemo;
ga_strSessionName = strSessionName;
ga_IsServer = FALSE;
// start the timer loop
AddTimerHandler();
// initialize server
try {
// read initial info to stream
ga_strmDemoPlay.ExpectID_t("DEMO");
if (ga_strmDemoPlay.PeekID_t()==CChunkID("MVER")) {
ga_strmDemoPlay.ExpectID_t("MVER");
ga_strmDemoPlay>>ga_ulDemoMinorVersion;
} else {
ga_ulDemoMinorVersion = 2;
}
ga_sesSessionState.Read_t(&ga_strmDemoPlay);
} catch(char *) {
RemoveTimerHandler();
ga_strmDemoPlay.Close();
ga_bDemoPlay = FALSE;
throw;
}
// eventually cache all shadowmaps in world (memory eater!)
if( shd_bCacheAll) ga_World.wo_baBrushes.CacheAllShadowmaps();
// flush stale caches
FreeUnusedStock();
// mark that pretouching is required
_bNeedPretouch = TRUE;
// remember the world pointer
_pShell->SetINDEX("pwoCurrentWorld", (INDEX)&ga_World);
// demo synchronization starts at the beginning initially
ga_fDemoTimer = 0.0f;
ga_tvDemoTimerLastTime = _pTimer->GetHighPrecisionTimer();
// demo sync seuqence must be initialized first time in ProcessGameStream()
ga_sesSessionState.ses_tmLastDemoSequence = -1.0f;
// run main loop to let server process messages from host
MainLoop();
// start timer sync anew
ga_ctTimersPending = 0;
}
/* Test if currently playing demo has finished. */
BOOL CNetworkLibrary::IsDemoPlayFinished(void)
{
return ga_bDemoPlay && ga_bDemoPlayFinished;
}
/* Test if currently playing a demo. */
BOOL CNetworkLibrary::IsPlayingDemo(void)
{
return ga_bDemoPlay;
}
/* Test if currently recording a demo. */
BOOL CNetworkLibrary::IsRecordingDemo(void)
{
return ga_bDemoRec;
}
BOOL CNetworkLibrary::IsNetworkEnabled(void)
{
return _cmiComm.IsNetworkEnabled();
}
// pause/unpause game
void CNetworkLibrary::TogglePause(void)
{
ga_sesSessionState.ses_bWantPause = !ga_sesSessionState.ses_bWantPause;
}
// test if game is paused
BOOL CNetworkLibrary::IsPaused(void)
{
if (this==NULL || _bTempNetwork) {
return TRUE; // this can happen during NET_MakeDefaultState_t()!
}
return ga_sesSessionState.ses_bPause;
}
// test if having connnection problems (not getting messages from server regulary)
BOOL CNetworkLibrary::IsConnectionStable(void)
{
// if network is not enabled
if (!_cmiComm.IsNetworkEnabled()) {
// it is always stable
return TRUE;
}
// check when last message was received.
return (_pTimer->GetHighPrecisionTimer()-ga_sesSessionState.ses_tvMessageReceived).GetSeconds()<net_tmProblemsTimeout;
}
// test if completely disconnected and why
BOOL CNetworkLibrary::IsDisconnected(void)
{
return ga_sesSessionState.ses_strDisconnected!="";
}
const CTString &CNetworkLibrary::WhyDisconnected(void)
{
return ga_sesSessionState.ses_strDisconnected;
}
// set/get server side pause (for single player only)
void CNetworkLibrary::SetLocalPause(BOOL bPause)
{
ga_bLocalPause = bPause;
}
BOOL CNetworkLibrary::GetLocalPause(void)
{
if (this==NULL || _bTempNetwork) {
return TRUE; // this can happen during NET_MakeDefaultState_t()!
}
return ga_bLocalPause;
}
// get server/client name and address
void CNetworkLibrary::GetHostName(CTString &strName, CTString &strAddress)
{
_cmiComm.GetHostName(strName, strAddress);
}
// mark that the game has finished -- called from AI
void CNetworkLibrary::SetGameFinished(void)
{
ga_sesSessionState.ses_bGameFinished = TRUE;
if (IsServer()) {
ga_srvServer.srv_bGameFinished = TRUE;
}
}
BOOL CNetworkLibrary::IsGameFinished(void)
{
return ga_sesSessionState.ses_bGameFinished;
}
// manipulation with realtime factor for slower/faster time -- called from AI
void CNetworkLibrary::SetRealTimeFactor(FLOAT fSpeed)
{
ga_sesSessionState.ses_fRealTimeFactor = fSpeed;
}
FLOAT CNetworkLibrary::GetRealTimeFactor(void)
{
return ga_sesSessionState.ses_fRealTimeFactor;
}
// test if game is waiting for more players to connect
BOOL CNetworkLibrary::IsWaitingForPlayers(void)
{
// if game mode does not include waiting for players
if (!ga_sesSessionState.ses_bWaitAllPlayers) {
// not waiting
return FALSE;
}
// if server
if (IsServer()) {
// check number of players on server
return ga_srvServer.GetPlayersCount()<ga_sesSessionState.ses_ctMaxPlayers;
// if not server
} else {
// check number of players in session
return ga_sesSessionState.GetPlayersCount()<ga_sesSessionState.ses_ctMaxPlayers;
}
}
// test if game is waiting for server
BOOL CNetworkLibrary::IsWaitingForServer(void)
{
return ga_sesSessionState.ses_bWaitingForServer;
}
// test if game session is currently doing prediction
BOOL CNetworkLibrary::IsPredicting(void)
{
return ga_sesSessionState.ses_bPredicting;
}
/*
* Stop currently running game.
*/
void CNetworkLibrary::StopGame(void)
{
// mute all sounds
_pSound->Mute();
CPrintF( TRANS("stopping game.\n"));
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
ga_ctTimersPending = -1; // disable timer pending
// stop demo recording if active
StopDemoRec();
// if playing demo
if (ga_bDemoPlay) {
// close the demo file
ga_strmDemoPlay.Close();
// remember that not playing demo
ga_bDemoPlay = FALSE;
ga_bDemoPlayFinished = FALSE;
}
// stop the timer loop
RemoveTimerHandler();
// stop session
ga_sesSessionState.Stop();
// stop server
if (ga_IsServer) {
ga_srvServer.Stop();
ga_IsServer = FALSE;
}
ga_ulDemoMinorVersion = _SE_BUILD_MINOR;
ga_strSessionName = "";
ga_World.DeletePredictors();
ga_World.Clear();
// free default state if existing
if (ga_pubDefaultState!=NULL) {
FreeMemory(ga_pubDefaultState);
ga_pubDefaultState = NULL;
ga_slDefaultStateSize = 0;
memset(ga_aubDefaultProperties, 0, sizeof(ga_aubDefaultProperties));
}
if (ga_pubCRCList!=NULL) {
FreeMemory(ga_pubCRCList);
ga_pubCRCList = NULL;
ga_slCRCList = 0;
}
ga_aplsPlayers.Clear();
ga_aplsPlayers.New(NET_MAXLOCALPLAYERS);
// remember the world pointer
_pShell->SetINDEX("pwoCurrentWorld", (INDEX)NULL);
// rewind the timer
_pTimer->SetCurrentTick(0.0f);
}
// initiate level change
void CNetworkLibrary::ChangeLevel(
const CTFileName &fnmNextLevel, BOOL bRemember, INDEX iUserData)
{
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
ASSERT(!IsPredicting());
// if not currently changing
if (_lphCurrent==LCP_NOCHANGE) {
// initiate change
ga_fnmNextLevel = fnmNextLevel;
ga_bNextRemember = bRemember;
ga_iNextLevelUserData = iUserData;
_lphCurrent = LCP_INITIATED;
}
}
// really do the level change
void CNetworkLibrary::ChangeLevel_internal(void)
{
CSetFPUPrecision FPUPrecision(FPT_24BIT);
extern BOOL _bReinitEntitiesWhileCopying;
_bReinitEntitiesWhileCopying = FALSE;
// mute all sounds
_pSound->Mute();
// cancel all predictions before crossing levels
_pNetwork->ga_World.DeletePredictors();
// find all entities that are to cross to next level
CEntitySelection senToCross;
{FOREACHINDYNAMICCONTAINER(ga_World.wo_cenEntities, CEntity, iten) {
if (iten->en_ulFlags&ENF_CROSSESLEVELS) {
senToCross.Select(*iten);
}
}}
// copy them to a temporary world
CWorld wldTemp;
CEntitySelection senInTemp;
wldTemp.CopyEntities(ga_World, senToCross,
senInTemp, CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0)));
// remember characters for all player targets and disable them
CPlayerCharacter apc[NET_MAXGAMEPLAYERS];
BOOL abWasActive[NET_MAXGAMEPLAYERS];
CPlayerAction apaActions[NET_MAXGAMEPLAYERS][2];
{for(INDEX i=0; i<NET_MAXGAMEPLAYERS; i++) {
CPlayerTarget &plt = ga_sesSessionState.ses_apltPlayers[i];
abWasActive[i] = plt.IsActive();
if (plt.IsActive()) {
apc[i] = plt.plt_penPlayerEntity->en_pcCharacter;
apaActions[i][0] = plt.plt_paLastAction;
apaActions[i][1] = plt.plt_paPreLastAction;
plt.plt_penPlayerEntity = NULL;
plt.Deactivate();
}
}}
// destroy all entities that will cross level
ga_World.DestroyEntities(senToCross);
// if should remember old levels
if (ga_bNextRemember) {
// remember current level
ga_sesSessionState.RememberCurrentLevel(ga_fnmWorld);
}
CGatherCRC gc;
// if starting in network
if (_cmiComm.IsNetworkEnabled()) {
// start gathering CRCs
InitCRCGather();
// make default state data for creating deltas
MakeDefaultState(ga_fnmNextLevel, ga_sesSessionState.ses_ulSpawnFlags, ga_aubProperties);
}
// if the new level is not remembered
if (ga_sesSessionState.FindRememberedLevel(ga_fnmNextLevel)==NULL) {
// remember original world filename
CTFileName fnmOldWorld = ga_fnmWorld;
// try to
try {
// load the new world
_pTimer->SetCurrentTick(0.0f); // must have timer at 0 while loading
ga_World.Load_t(ga_fnmNextLevel);
// delete all entities that don't fit given spawn flags
ga_World.FilterEntitiesBySpawnFlags(ga_sesSessionState.ses_ulSpawnFlags);
// if failed
} catch(char *strError) {
// report error
CPrintF(TRANS("Cannot change level:\n%s"), strError);
// try to
try {
// load the old world
ga_fnmNextLevel = fnmOldWorld;
ga_World.Load_t(ga_fnmNextLevel);
// delete all entities that don't fit given spawn flags
ga_World.FilterEntitiesBySpawnFlags(ga_sesSessionState.ses_ulSpawnFlags);
// if that fails
} catch (char *strError2) {
// fatal error
FatalError(
TRANS("Cannot change level because:\n%s\n"
"and cannot go back to original one because:\n%s"), strError, strError2);
return;
}
}
// remember the world filename
ga_fnmWorld = ga_fnmNextLevel;
// remember the world pointer
_pShell->SetINDEX("pwoCurrentWorld", (INDEX)&ga_World);
// if there is remembered level
} else {
// restore it
ga_sesSessionState.RestoreOldLevel(ga_fnmNextLevel);
}
// set overdue timers in just loaded world to be due in current time
ga_World.AdjustLateTimers(ga_sesSessionState.ses_tmLastProcessedTick);
// copy entities from temporary world into new one
CEntitySelection senCrossed;
ga_World.CopyEntities(wldTemp, senInTemp,
senCrossed, CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0)));
// restore pointers to entities for all active player targets
{for(INDEX i=0; i<NET_MAXGAMEPLAYERS; i++) {
CPlayerTarget &plt = ga_sesSessionState.ses_apltPlayers[i];
if (abWasActive[i]) {
plt.Activate();
plt.plt_paLastAction = apaActions[i][0];
plt.plt_paPreLastAction = apaActions[i][1];
plt.AttachEntity(ga_World.FindEntityWithCharacter(apc[i]));
}
}}
_bReinitEntitiesWhileCopying = TRUE;
// if should not remember old levels
if (!ga_bNextRemember) {
// clear them all
ga_sesSessionState.ForgetOldLevels();
}
// if not server
if (!IsServer()) {
// start waiting for server
ga_sesSessionState.ses_bWaitingForServer = TRUE;
// if server
} else {
// flush sync check buffer
ga_srvServer.srv_ascChecks.Clear();
// for each client
{for( INDEX iClient=0; iClient<NET_MAXGAMECOMPUTERS; iClient++) {
CSessionSocket &sso = ga_srvServer.srv_assoSessions[iClient];
// reset message timer
sso.sso_tvMessageReceived = -1I64;
// reset sync timer
sso.sso_tmLastSyncReceived = -1.0f;
}}
// for each player
{for( INDEX iPlayer=0; iPlayer<NET_MAXGAMEPLAYERS; iPlayer++) {
CPlayerBuffer &plb = _pNetwork->ga_srvServer.srv_aplbPlayers[iPlayer];
if (plb.plb_Active) {
// add one dummy action
CPlayerAction pa;
pa.Clear();
pa.pa_aRotation = plb.plb_paLastAction.pa_aRotation;
pa.pa_aViewRotation = plb.plb_paLastAction.pa_aViewRotation;
plb.plb_abReceived.AddAction(pa);
}
}}
}
ga_sesSessionState.ses_iLevel+=1;
// flush stale caches
FreeUnusedStock();
// mark that pretouching is required
_bNeedPretouch = TRUE;
// start timer sync anew
ga_ctTimersPending = 0;
FinishCRCGather();
}
/* Start recording a demo. */
void CNetworkLibrary::StartDemoRec_t(const CTFileName &fnDemo) // throw char *
{
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
// if already recording
if (ga_bDemoRec) {
// error
throw TRANS("Already recording a demo!");
}
// create the file
ga_strmDemoRec.Create_t(fnDemo);
// write initial info to stream
ga_strmDemoRec.WriteID_t("DEMO");
ga_strmDemoRec.WriteID_t("MVER");
ga_strmDemoRec<<ULONG(_SE_BUILD_MINOR);
ga_sesSessionState.Write_t(&ga_strmDemoRec);
// remember that recording demo
ga_bDemoRec = TRUE;
}
/* Stop recording a demo. */
void CNetworkLibrary::StopDemoRec(void)
{
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
// if not recording
if (!ga_bDemoRec) {
// do nothing
return;
}
// write terminal info to the stream
ga_strmDemoRec.WriteID_t("DEND"); // game end
// close the file
ga_strmDemoRec.Close();
// remember that not recording demo
ga_bDemoRec = FALSE;
}
// split the rcon response string into lines and send one by one to the client
static void SendAdminResponse(ULONG ulAdr, UWORD uwPort, ULONG ulCode, const CTString &strResponse)
{
CTString str = strResponse;
INDEX iLineCt = 0;
while (str!="") {
CTString strLine = str;
strLine.OnlyFirstLine();
str.RemovePrefix(strLine);
str.DeleteChar(0);
if (strLine.Length()>0) {
CNetworkMessage nm(MSG_EXTRA);
nm<<CTString(0, "log %u %d %s\n", ulCode, iLineCt++, strLine);
_pNetwork->SendBroadcast(nm, ulAdr, uwPort);
}
}
}
/*
* Main loop.
*/
void CNetworkLibrary::MainLoop(void)
{
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
// update network state variable (to control usage of some cvars that cannot be altered in mulit-player mode)
_bMultiPlayer = (_pNetwork->ga_sesSessionState.GetPlayersCount() > 1);
// if should change world
if (_lphCurrent==LCP_SIGNALLED) {
// really do the level change here
ChangeLevel_internal();
_lphCurrent=LCP_CHANGED;
}
if (_bStartDemoRecordingNextTime) {
_bStartDemoRecordingNextTime = 0.0f;
if (!ga_bDemoRec) {
try {
CTString strName;
strName.PrintF("Temp\\Recorded%02d.dem", (INDEX)dem_iRecordedNumber);
StartDemoRec_t(strName);
dem_iRecordedNumber+=1;
} catch(char *strError) {
CPrintF(TRANS("Demo recording error: %s\n"), strError);
}
}
}
if (_bStopDemoRecordingNextTime) {
_bStopDemoRecordingNextTime = 0.0f;
if (ga_bDemoRec) {
StopDemoRec();
}
}
_sfStats.StartTimer(CStatForm::STI_MAINLOOP);
_pfNetworkProfile.StartTimer(CNetworkProfile::PTI_MAINLOOP);
// handle messages for session state
if (!ga_bDemoPlay) {
if (_cmiComm.Client_Update() == FALSE) {
ga_sesSessionState.Stop();
return;
}
ga_sesSessionState.SessionStateLoop();
if (_cmiComm.Client_Update() == FALSE) {
ga_sesSessionState.Stop();
return;
}
}
// if this is server computer
if (ga_IsServer) {
// handle server messages
_cmiComm.Server_Update();
}
// let server process game stream
TIME tmBefore = _pTimer->GetRealTimeTick();
_pTimer->SetLerp(0.0f);
/*
// automatically adjust network settings
if (cli_bAutoAdjustSettings) {
AutoAdjustSettings();
}
*/
// determine whether to use prediction
BOOL bUsePrediction = cli_bPrediction && (cli_bPredictIfServer || !IsServer());
_bPredictionActive = bUsePrediction; // memeber this for other misc code
// mark all predictable entities that will be predicted using user-set criterions
if (bUsePrediction) {
ga_World.MarkForPrediction();
}
// process the game stream coming from the server
ga_sesSessionState.ProcessGameStream();
// flush actions that don't need to be predicted any more
ga_sesSessionState.FlushProcessedPredictions();
// process additional prediction steps
if (bUsePrediction) {
// mark all new predictable entities that might have been spawned
ga_World.UnmarkForPrediction();
ga_World.MarkForPrediction();
ga_sesSessionState.ProcessPrediction();
// unmark all predictable entities marked for prediction
ga_World.UnmarkForPrediction();
}
ga_sesSessionState.ses_tmLastUpdated = _pTimer->GetRealTimeTick();
TIME tmAfter = _pTimer->GetRealTimeTick();
ga_sesSessionState.ses_bKeepingUpWithTime = (tmAfter-tmBefore)<=_pTimer->TickQuantum*2.01f;
CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
// set the lerping factor for current frame
if (!ga_bDemoPlay) {
ga_sesSessionState.SetLerpFactor(tvNow);
} else {
ga_sesSessionState.SetLerpFactor(CTimerValue(ga_fDemoTimer));
}
// if playing a demo
if (ga_bDemoPlay) {
// if synchronizing by real time
if (ga_fDemoSyncRate==DEMOSYNC_REALTIME) {
// if server is keeping up
if (ga_sesSessionState.ses_bKeepingUpWithTime) {
// add passed time with slow/fast factor
ga_fDemoTimer += FLOAT((tvNow-ga_tvDemoTimerLastTime).GetSeconds())
*ga_fDemoRealTimeFactor*ga_sesSessionState.ses_fRealTimeFactor;
}
// if synchronizing is stopped
} else if (ga_fDemoSyncRate==DEMOSYNC_STOP) {
// don't step
NOTHING;
// if synchronizing by given steps
} else {
// just add the step
ga_fDemoTimer += 1.0f/ga_fDemoSyncRate;
}
}
// remember the demo timer
ga_tvDemoTimerLastTime = tvNow;
// if network
if (_cmiComm.IsNetworkEnabled()) {
// do services for gameagent querying
GameAgent_ServerUpdate();
// _cmiComm.Broadcast_Update();
// repeat
FOREVER {
CNetworkMessage nmReceived;
// _cmiComm.Broadcast_Update();
ULONG ulFrom;
UWORD uwPort;
BOOL bHasMsg = ReceiveBroadcast(nmReceived, ulFrom, uwPort);
// if there are no more messages
if (!bHasMsg) {
// finish
break;
}
// if this message is not valid rcon message
if (nmReceived.GetType()!=MSG_EXTRA) {
// skip it
continue;
}
// get the string from the message
CTString strMsg;
nmReceived>>strMsg;
// if this is server
if (IsServer()) {
// accept requests
if (!strMsg.RemovePrefix("rcmd ")) {
continue;
}
ULONG ulCode;
char strPass[80];
char strCmd[256];
strMsg.ScanF("%u \"%80[^\"]\"%256[^\n]", &ulCode, strPass, strCmd);
CTString strAdr = AddressToString(ulFrom);
if (net_strAdminPassword=="" || net_strAdminPassword!=strPass) {
CPrintF(TRANS("Server: Client '%s', Wrong password for remote administration.\n"), (const char*)strAdr);
continue;
}
CPrintF(TRANS("Server: Client '%s', Admin cmd: %s\n"), (const char*)strAdr, strCmd);
con_bCapture = TRUE;
con_strCapture = "";
_pShell->Execute(CTString(strCmd)+";");
CTString strResponse = CTString(">")+strCmd+"\n"+con_strCapture;
SendAdminResponse(ulFrom, uwPort, ulCode, strResponse);
con_bCapture = FALSE;
con_strCapture = "";
}
}
}
_pfNetworkProfile.StopTimer(CNetworkProfile::PTI_MAINLOOP);
_sfStats.StopTimer(CStatForm::STI_MAINLOOP);
}
// make actions packet for local players and send to server
void CNetworkLibrary::SendActionsToServer(void)
{
// make the packet
CNetworkMessage nmAction(MSG_ACTION);
// for all local players on this machine
for(INDEX ipls=0; ipls<ga_aplsPlayers.Count(); ipls++) {
CPlayerSource &pls = ga_aplsPlayers[ipls];
// create action packet if the player exists
pls.WriteActionPacket(nmAction);
}
// send the packet
SendToServer(nmAction);
}
/*
* Client loop.
*/
void CNetworkLibrary::TimerLoop(void)
{
if (this==NULL || _bTempNetwork) {
return; // this can happen during NET_MakeDefaultState_t()!
}
_pfNetworkProfile.StartTimer(CNetworkProfile::PTI_TIMERLOOP);
// count number of timer interrupts that happened
if (ga_ctTimersPending>=0) {
ga_ctTimersPending++;
}
// if can synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, FALSE);
// initially, no timer functions needed
INDEX ct = 0;
// if timer exactness level is full
if (net_iExactTimer==2) {
// lock network mutex
slNetwork.Lock();
// execute exactly one
ct = 1;
// if timer exactness level is partial
} else if (net_iExactTimer==1) {
// if network mutex can be locked
if (slNetwork.TryToLock()) {
// execute all pending
ct = ga_ctTimersPending;
}
// if timer exactness level is low
} else if (net_iExactTimer==0) {
// if network mutex can be locked
if (slNetwork.TryToLock()) {
// execute exactly one
ct = 1;
}
}
// for each pending interrupt
while(ct) {
ct--;
ga_ctTimersPending--;
// if not disconnected
// if (!IsDisconnected()) {
if (_cmiComm.cci_bClientInitialized) {
// make actions packet for all local players and send to server
SendActionsToServer();
_cmiComm.Client_Update();
}
// if this is server computer
if (ga_IsServer) {
// handle server messages
_cmiComm.Server_Update();
ga_srvServer.ServerLoop();
_cmiComm.Server_Update();
}
}
_pfNetworkProfile.StopTimer(CNetworkProfile::PTI_TIMERLOOP);
}
/* Get player entity for a given local player. */
CEntity *CNetworkLibrary::GetLocalPlayerEntity(CPlayerSource *ppls)
{
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
// get the index of the player target in game state
INDEX iPlayerTarget = ppls->pls_Index;
// if player is not added
if (iPlayerTarget<0) {
// no entity
return NULL;
// if player is added
} else {
// get the entity from player target
CPlayerTarget &plt = ga_sesSessionState.ses_apltPlayers[iPlayerTarget];
CPlayerEntity *pen = plt.plt_penPlayerEntity;
return pen;
}
}
/* Get player entity for a given player by name. */
CEntity *CNetworkLibrary::GetPlayerEntityByName(const CTString &strName)
{
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
// for each player in game
CStaticArray<CPlayerTarget> &aplt = ga_sesSessionState.ses_apltPlayers;
for(INDEX iplt = 0; iplt<aplt.Count(); iplt++) {
// if it is active and has that name
if (aplt[iplt].IsActive()
&&aplt[iplt].plt_penPlayerEntity->en_pcCharacter.GetName()==strName) {
// return it
return aplt[iplt].plt_penPlayerEntity;
}
}
// else not found
return NULL;
}
/* Get number of entities with given name. */
INDEX CNetworkLibrary::GetNumberOfEntitiesWithName(const CTString &strName)
{
INDEX ctEntities = 0;
{FOREACHINDYNAMICCONTAINER(ga_World.wo_cenEntities, CEntity, iten) {
if (iten->GetName()==strName) {
ctEntities++;
}
}}
return ctEntities;
}
/* Get n-th entity with given name. */
CEntity *CNetworkLibrary::GetEntityWithName(const CTString &strName, INDEX iEntityWithThatName)
{
INDEX ctEntities = 0;
CEntity *pen = NULL;
{FOREACHINDYNAMICCONTAINER(ga_World.wo_cenEntities, CEntity, iten) {
if (iten->GetName()==strName) {
pen = iten;
if (ctEntities==iEntityWithThatName) {
break;
}
ctEntities++;
}
}}
return pen;
}
/* Test if a given player is local to this computer. */
BOOL CNetworkLibrary::IsPlayerLocal(CEntity *pen)
{
return GetPlayerSource(pen)!=NULL;
}
// get player source for a given player if it is local to this computer
CPlayerSource *CNetworkLibrary::GetPlayerSource(CEntity *pen)
{
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
// for all local player on this machine
{FOREACHINSTATICARRAY(ga_aplsPlayers, CPlayerSource, itpls) {
// get the index of the player target in game state
INDEX iPlayerTarget = itpls->pls_Index;
// if player is added
if (iPlayerTarget>=0) {
// get the player target
CPlayerTarget &plt = ga_sesSessionState.ses_apltPlayers[iPlayerTarget];
// if it is that one
if (plt.plt_penPlayerEntity == pen) {
// return it
return itpls;
}
}
}}
// if not found, it is not local
return NULL;
}
// get game time in currently running game
TIME CNetworkLibrary::GetGameTime(void)
{
return ga_sesSessionState.ses_tmLastProcessedTick;
}
/*
* Add a new client to game.
*/
CPlayerSource *CNetworkLibrary::AddPlayer_t(CPlayerCharacter &pcCharacter) // throw char *
{
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
CPrintF( TRANS("Adding player: '%s'\n"), pcCharacter.GetNameForPrinting());
// for all local clients on this machine
FOREACHINSTATICARRAY(ga_aplsPlayers, CPlayerSource, itcls) {
// if client is not active
if (!itcls->IsActive()) {
// activate it
itcls->Start_t(pcCharacter);
CPrintF( TRANS(" done.\n"));
return &itcls.Current();
}
}
// number of local clients is limited with NET_MAXLOCALCLIENTS
ASSERTALWAYS("Adding too much local clients!");
throw TRANS("Cannot add more local clients");
return NULL;
}
/* Get session properties for current game. */
void *CNetworkLibrary::GetSessionProperties(void)
{
// synchronize access to network
CTSingleLock slNetwork(&ga_csNetwork, TRUE);
return ga_aubProperties;
}
/* Send chat message from some players to some other players. */
void CNetworkLibrary::SendChat(ULONG ulFrom, ULONG ulTo, const CTString &strMessage)
{
// if the string is too long and we're not server
if (strlen(strMessage)>256 && !_pNetwork->IsServer()) {
// refuse it
return;
}
// just make the message and send it to server
CNetworkMessage nm(MSG_CHAT_IN);
nm<<ulFrom;
nm<<ulTo;
nm<<strMessage;
SendToServer(nm);
}
// save current version of engine
void CNetworkLibrary::WriteVersion_t(CTStream &strm)
{
strm.WriteID_t("BUIV"); // build version
strm<<INDEX(_SE_BUILD_MAJOR);
}
// load version of engine saved in file and check against current
void CNetworkLibrary::CheckVersion_t(CTStream &strm, BOOL bAllowReinit, BOOL &bNeedsReinit)
{
// if not saved
if (strm.PeekID_t()!=CChunkID("BUIV")) { // build version
// behave as if everything is ok (for old versions)
bNeedsReinit = FALSE;
return;
}
strm.ExpectID_t("BUIV"); // build version
// read the saved one
INDEX iSaved;
strm>>iSaved;
// get current one
INDEX iCurrent = _SE_BUILD_MAJOR;
// if current version is an internal build
if (iCurrent==0) {
// it is never forced to reinit
bNeedsReinit = FALSE;
return;
}
// if current version is older than the saved one
if (iCurrent<iSaved) {
// it cannot be reinitialized
ThrowF_t(TRANS("File '%s' was saved by a newer version of engine, it cannot be loaded"),
strm.GetDescription());
return;
}
// if current version is same as the saved one
if (iCurrent==iSaved) {
// all ok
bNeedsReinit = FALSE;
return;
}
// if current version is newer than the saved one
if (iCurrent>iSaved) {
// it should be reinitialized
bNeedsReinit = TRUE;
// if it may not be reinitialized
if (!bAllowReinit) {
ThrowF_t(TRANS("File '%s' was saved by an older version of engine, it cannot be loaded"),
strm.GetDescription());
}
return;
}
// this should not happen
ASSERT(FALSE);
bNeedsReinit = FALSE;
return;
}
// add a value to the netgraph
void CNetworkLibrary::AddNetGraphValue(enum NetGraphEntryType nget, FLOAT fLatency)
{
net_iGraphBuffer = Clamp(net_iGraphBuffer, INDEX(20), INDEX(1000));
// make sure the netgraph has wanted number of values
if (ga_angeNetGraph.Count()!=net_iGraphBuffer) {
ga_angeNetGraph.Clear();
ga_angeNetGraph.New(net_iGraphBuffer);
memset(&ga_angeNetGraph[0], 0, ga_angeNetGraph.Count()*sizeof(ga_angeNetGraph[0]));
}
// scroll the values in the netgraph by one value
memmove(&ga_angeNetGraph[1], &ga_angeNetGraph[0], (ga_angeNetGraph.Count()-1)*sizeof(ga_angeNetGraph[0]));
// add the new value
ga_angeNetGraph[0].nge_ngetType = nget;
ga_angeNetGraph[0].nge_fLatency = fLatency;
}
// make default state for a network game
extern void NET_MakeDefaultState_t(
const CTFileName &fnmWorld, ULONG ulSpawnFlags, void *pvSessionProperties,
CTStream &strmState) // throw char *
{
// mute all sounds
_pSound->Mute();
// first off - mark that we are in the special state
_bTempNetwork = TRUE;
// make sure that current network object gets locked
CTSingleLock slNetwork(&_pNetwork->ga_csNetwork, TRUE);
// remember original network pointer and clear it
CNetworkLibrary *pnlOld = _pNetwork;
_pNetwork = NULL;
// try to
try {
// create new network object
CNetworkLibrary *pNewNet = new CNetworkLibrary;
// it must have new mutex index since both will be locked
pNewNet->ga_csNetwork.cs_iIndex = 2001;
// lock the new network access
CTSingleLock slNetwork(&pNewNet->ga_csNetwork, TRUE);
pNewNet->ga_ctTimersPending = -1; // disable timer pending
// only after locking it, we may allow the new pointer to be remembered
// otherwise, the other thread can jump in between
_pNetwork = pNewNet;
// remember settings
_pNetwork->ga_sesSessionState.ses_ulSpawnFlags = ulSpawnFlags;
_pNetwork->ga_sesSessionState.ses_tmSyncCheckFrequency = 10.0f;
_pNetwork->ga_sesSessionState.ses_iExtensiveSyncCheck = 0;
memcpy(_pNetwork->ga_aubProperties, pvSessionProperties, NET_MAXSESSIONPROPERTIES);
_pNetwork->ga_fnmWorld = fnmWorld;
_pNetwork->ga_fnmNextLevel = CTString("");
try {
// load the world
_pTimer->SetCurrentTick(0.0f); // must have timer at 0 while loading
_pNetwork->ga_World.Load_t(fnmWorld);
// delete all entities that don't fit given spawn flags
_pNetwork->ga_World.FilterEntitiesBySpawnFlags(_pNetwork->ga_sesSessionState.ses_ulSpawnFlags);
} catch(char *) {
throw;
}
// remember the world filename
_pNetwork->ga_fnmWorld = fnmWorld;
_pNetwork->ga_fnmNextLevel = CTString("");
// remember the world pointer
_pShell->SetINDEX("pwoCurrentWorld", (INDEX)&_pNetwork->ga_World);
// reset random number generator
_pNetwork->ga_sesSessionState.ResetRND();
// flush stale caches
FreeUnusedStock();
// warmup the world
_pNetwork->ga_sesSessionState.WarmUpWorld();
// save the session state to the stream
_pNetwork->ga_sesSessionState.Write_t(&strmState);
// if any error
} catch (char *) {
// restore original network pointer
CNetworkLibrary *pnlTemp = _pNetwork;
_pNetwork = pnlOld;
if (pnlTemp!=NULL) {
delete pnlTemp;
}
_bTempNetwork = FALSE;
// fail
throw;
}
// restore original network pointer
CNetworkLibrary *pnlTemp = _pNetwork;
_pNetwork = pnlOld;
delete pnlTemp;
_bTempNetwork = FALSE;
}
// handle broadcast messages (server enumeration)
void CNetworkLibrary::GameInactive(void)
{
GameAgent_EnumUpdate();
// if no network
if (!_cmiComm.IsNetworkEnabled()) {
// do not handle
return;
}
// _cmiComm.Broadcast_Update();
// repeat
FOREVER {
CNetworkMessage nmReceived;
//_cmiComm.Broadcast_Update();
ULONG ulFrom;
UWORD uwPort;
BOOL bHasMsg = ReceiveBroadcast(nmReceived, ulFrom, uwPort);
// if there are no more messages
if (!bHasMsg) {
// finish
break;
}
/* This is handled by GameAgent.
// if requesting enumeration and this is server and enumeration is allowed
if (nmReceived.GetType()==MSG_REQ_ENUMSERVERS
&& IsServer()
&& (ser_bEnumeration && ga_sesSessionState.ses_ctMaxPlayers>1)) {
// create response
CNetworkMessage nmEnum(MSG_SERVERINFO);
nmEnum<<ga_strSessionName;
nmEnum<<ga_World.wo_strName;
nmEnum<<ga_srvServer.GetPlayersCount();
nmEnum<<ga_sesSessionState.ses_ctMaxPlayers;
// send it
SendBroadcast(nmEnum, ulFrom, uwPort);
// if receiving enumeration
} else if (nmReceived.GetType()==MSG_SERVERINFO) {
// create a new session
CNetworkSession &ns = *new CNetworkSession;
ga_lhEnumeratedSessions.AddTail(ns.ns_lnNode);
// read it
nmReceived>>ns.ns_strSession;
nmReceived>>ns.ns_strWorld;
nmReceived>>ns.ns_ctPlayers;
nmReceived>>ns.ns_ctMaxPlayers;
ns.ns_strAddress = AddressToString(ulFrom);
}*/
}
}
void CNetworkLibrary::InitCRCGather(void)
{
CRCT_ResetActiveList();
CRCT_bGatherCRCs = TRUE;
CRCT_AddFile_t(CTString("Classes\\Player.ecl"));
}
// finish gathering of file CRCs to CRC table (call for server only!)
void CNetworkLibrary::FinishCRCGather(void)
{
try {
// make the list
CTMemoryStream strmCRC;
CRCT_MakeFileList_t(strmCRC);
// remember it
strmCRC.SetPos_t(0);
ga_slCRCList = strmCRC.GetStreamSize();
ga_pubCRCList = (UBYTE*)AllocMemory(ga_slCRCList);
strmCRC.Read_t(ga_pubCRCList, ga_slCRCList);
// remember its CRC
strmCRC.SetPos_t(0);
ga_ulCRC = CRCT_MakeCRCForFiles_t(strmCRC);
} catch (char *strError) {
CPrintF(TRANS("Warning, cannot get CRCs: %s\n"), strError);
}
}
// make default state data for creating deltas
void CNetworkLibrary::MakeDefaultState(const CTFileName &fnmWorld,
ULONG ulSpawnFlags, void *pvSessionProperties)
{
// prepare file or memory stream for state
CTFileStream strmStateFile; CTMemoryStream strmStateMem;
CTStream *pstrmState;
extern INDEX net_bDumpConnectionInfo;
if (net_bDumpConnectionInfo) {
strmStateFile.Create_t(CTString("Temp\\DefaultState.bin"));
pstrmState = &strmStateFile;
} else {
pstrmState = &strmStateMem;
}
// make default state for a network game
NET_MakeDefaultState_t(fnmWorld, ulSpawnFlags, pvSessionProperties, *pstrmState);
pstrmState->SetPos_t(0);
ga_slDefaultStateSize = pstrmState->GetStreamSize();
ga_pubDefaultState = (UBYTE*)AllocMemory(ga_slDefaultStateSize);
pstrmState->Read_t(ga_pubDefaultState, ga_slDefaultStateSize);
memcpy(ga_aubDefaultProperties, pvSessionProperties, sizeof(ga_aubDefaultProperties));
}