SessionState.cpp 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. #include "stdh.h"
  13. #include <Engine/Build.h>
  14. #include <Engine/Network/Network.h>
  15. #include <Engine/Network/Server.h>
  16. #include <Engine/Network/NetworkMessage.h>
  17. #include <Engine/Network/Diff.h>
  18. #include <Engine/Base/ErrorTable.h>
  19. #include <Engine/Base/Translation.h>
  20. #include <Engine/Base/ProgressHook.h>
  21. #include <Engine/Base/CRCTable.h>
  22. #include <Engine/Base/Shell.h>
  23. #include <Engine/Network/SessionState.h>
  24. #include <Engine/Network/PlayerSource.h>
  25. #include <Engine/Entities/EntityClass.h>
  26. #include <Engine/Math/Float.h>
  27. #include <Engine/Network/PlayerTarget.h>
  28. #include <Engine/Network/NetworkProfile.h>
  29. #include <Engine/World/PhysicsProfile.h>
  30. #include <Engine/Network/CommunicationInterface.h>
  31. #include <Engine/Network/Compression.h>
  32. #include <Engine/Entities/InternalClasses.h>
  33. #include <Engine/Base/Console.h>
  34. #include <Engine/Entities/EntityProperties.h>
  35. #include <Engine/Network/LevelChange.h>
  36. #include <Engine/Templates/DynamicContainer.cpp>
  37. #include <Engine/Templates/StaticArray.cpp>
  38. #include <Engine/Base/ListIterator.inl>
  39. #include <Engine/Base/CRC.h>
  40. #define SESSIONSTATEVERSION_OLD 1
  41. #define SESSIONSTATEVERSION_WITHBULLETTIME 2
  42. #define SESSIONSTATEVERSION_CURRENT SESSIONSTATEVERSION_WITHBULLETTIME
  43. //#define DEBUG_LERPING 1
  44. extern INDEX net_bLerping;
  45. extern FLOAT net_tmConnectionTimeout;
  46. extern FLOAT net_tmProblemsTimeOut;
  47. extern FLOAT net_tmDisconnectTimeout;
  48. // this is from ProgresHook.cpp - so we tell the progresshook to run client/srever updates
  49. extern BOOL _bRunNetUpdates;
  50. #if DEBUG_LERPING
  51. FLOAT avfStats[1000][4];
  52. INDEX ctCounter=0;
  53. INDEX ctMax = sizeof(avfStats)/sizeof(avfStats[0]);
  54. #endif // DEBUG_LERPING
  55. // get a pseudo-random number (safe for network gaming)
  56. ULONG CSessionState::Rnd(void) {
  57. ASSERTMSG(ses_bAllowRandom, "Use RND only in entity AI!");
  58. // NOTE:
  59. // using multiplicative congruent method with Greanberger's lambda = 2^18+3
  60. ses_ulRandomSeed = ses_ulRandomSeed*262147;
  61. ASSERT(ses_ulRandomSeed!=0);
  62. return ses_ulRandomSeed;
  63. }
  64. // reset random number generator (always randomizes to same sequence!)
  65. void CSessionState::ResetRND(void)
  66. {
  67. BOOL bOldAllow = ses_bAllowRandom;
  68. ses_bAllowRandom = TRUE;
  69. // random must start at a number different than zero!
  70. ses_ulRandomSeed = 0x87654321;
  71. // run rnd a few times to make it go random
  72. for(INDEX i=0; i<32; i++) {
  73. Rnd();
  74. }
  75. ses_bAllowRandom = bOldAllow;
  76. }
  77. /*
  78. * Constructor.
  79. */
  80. CSessionState::CSessionState(void)
  81. {
  82. ses_bKeepingUpWithTime = TRUE;
  83. ses_tmLastUpdated = -100;
  84. ses_bAllowRandom = TRUE; // random allowed when not in game
  85. ses_bPredicting = FALSE;
  86. ses_tmPredictionHeadTick = -2.0f;
  87. ses_tmLastSyncCheck = 0;
  88. ses_tmLastPredictionProcessed = -200;
  89. ses_bPause = FALSE;
  90. ses_bWantPause = FALSE;
  91. ses_bGameFinished = FALSE;
  92. ses_bWaitingForServer = FALSE;
  93. ses_strDisconnected = "";
  94. ses_ctMaxPlayers = 1;
  95. ses_bWaitAllPlayers = FALSE;
  96. ses_iLevel = 0;
  97. ses_fRealTimeFactor = 1.0f;
  98. ses_pstrm = NULL;
  99. // reset random number generator
  100. ResetRND();
  101. ses_apltPlayers.New(NET_MAXGAMEPLAYERS);
  102. }
  103. /*
  104. * Destructor.
  105. */
  106. CSessionState::~CSessionState()
  107. {
  108. }
  109. /*
  110. * Clear the object.
  111. */
  112. void CSessionState::Stop(void)
  113. {
  114. #if DEBUG_SYNCSTREAMDUMPING
  115. ClearDumpStream();
  116. #endif
  117. #if DEBUG_SYNCSTREAMDUMPING
  118. #endif
  119. ses_bKeepingUpWithTime = TRUE;
  120. ses_tmLastUpdated = -100;
  121. ses_bAllowRandom = TRUE; // random allowed when not in game
  122. ses_bPredicting = FALSE;
  123. ses_tmPredictionHeadTick = -2.0f;
  124. ses_tmLastSyncCheck = 0;
  125. ses_bPause = FALSE;
  126. ses_bWantPause = FALSE;
  127. ses_bGameFinished = FALSE;
  128. ses_bWaitingForServer = FALSE;
  129. ses_strDisconnected = "";
  130. ses_ctMaxPlayers = 1;
  131. ses_fRealTimeFactor = 1.0f;
  132. ses_bWaitAllPlayers = FALSE;
  133. ses_apeEvents.PopAll();
  134. // disable lerping
  135. _pTimer->DisableLerp();
  136. #if DEBUG_LERPING
  137. CTFileStream f;
  138. f.Create_t(CTFILENAME("Temp\\Lerp.stats"), CTStream::CM_TEXT);
  139. for (INDEX i=0; i<ctCounter; i++) {
  140. char strBuffer[256];
  141. sprintf(strBuffer, "%6.2f %6.2f %6.2f %6.2f",
  142. avfStats[i][0],
  143. avfStats[i][1],
  144. avfStats[i][2],
  145. avfStats[i][3]);
  146. f.PutLine_t(strBuffer);
  147. }
  148. f.Close();
  149. #endif // DEBUG_LERPING
  150. CNetworkMessage nmConfirmDisconnect(MSG_REP_DISCONNECTED);
  151. if (_cmiComm.cci_bClientInitialized) {
  152. _pNetwork->SendToServerReliable(nmConfirmDisconnect);
  153. }
  154. _cmiComm.Client_Close();
  155. // clear all old levels
  156. ForgetOldLevels();
  157. ses_apltPlayers.Clear();
  158. ses_apltPlayers.New(NET_MAXGAMEPLAYERS);
  159. }
  160. /*
  161. * Initialize session state and wait for game to be started.
  162. */
  163. void CSessionState::Start_t(INDEX ctLocalPlayers)
  164. {
  165. ses_bKeepingUpWithTime = TRUE;
  166. ses_tmLastUpdated = -100;
  167. // clear message stream
  168. ses_nsGameStream.Clear();
  169. ses_bAllowRandom = FALSE; // random not allowed in game
  170. ses_bPredicting = FALSE;
  171. ses_tmPredictionHeadTick = -2.0f;
  172. ses_tmLastSyncCheck = 0;
  173. ses_bPause = FALSE;
  174. ses_bWantPause = FALSE;
  175. ses_bWaitingForServer = FALSE;
  176. ses_bGameFinished = FALSE;
  177. ses_strDisconnected = "";
  178. ses_ctMaxPlayers = 1;
  179. ses_fRealTimeFactor = 1.0f;
  180. ses_bWaitAllPlayers = FALSE;
  181. ses_iMissingSequence = -1;
  182. ses_apeEvents.PopAll();
  183. ses_tvMessageReceived.Clear();
  184. _pNetwork->ga_strRequiredMod = "";
  185. // reset random number generator
  186. ResetRND();
  187. // clear all old levels
  188. ForgetOldLevels();
  189. #if DEBUG_LERPING
  190. // clear lerp stats
  191. ctCounter = 0;
  192. #endif // DEBUG_LERPING
  193. // ses_LastProcessedTick and ses_LastReceivedTick tick counters are
  194. // irrelevant now, will be initialized when initialization message
  195. // from server is received, no need to set them here
  196. // if this computer is server
  197. if (_pNetwork->IsServer()) {
  198. // initialize local client
  199. _cmiComm.Client_Init_t(0UL);
  200. // connect as main session state
  201. try {
  202. Start_AtServer_t();
  203. } catch(char *) {
  204. _cmiComm.Client_Close();
  205. throw;
  206. }
  207. // if this computer is client
  208. } else {
  209. // connect client to server computer
  210. _cmiComm.Client_Init_t((char*)(const char*)_pNetwork->ga_strServerAddress);
  211. // connect as remote session state
  212. try {
  213. Start_AtClient_t(ctLocalPlayers);
  214. } catch(char *) {
  215. // if failed due to wrong mod
  216. if (strncmp(ses_strDisconnected, "MOD:", 4)==0) {
  217. // remember the mod
  218. _pNetwork->ga_strRequiredMod = ses_strDisconnected+4;
  219. // make sure that the string is never empty
  220. if (_pNetwork->ga_strRequiredMod=="") {
  221. _pNetwork->ga_strRequiredMod=" ";
  222. }
  223. }
  224. _cmiComm.Client_Close();
  225. throw;
  226. }
  227. }
  228. }
  229. void CSessionState::Start_AtServer_t(void) // throw char *
  230. {
  231. // send registration request
  232. CNetworkMessage nmRegisterMainSessionState(MSG_REQ_CONNECTLOCALSESSIONSTATE);
  233. ses_sspParams.Update();
  234. nmRegisterMainSessionState<<ses_sspParams;
  235. _pNetwork->SendToServerReliable(nmRegisterMainSessionState);
  236. for(TIME tmWait=0; tmWait<net_tmConnectionTimeout*1000;
  237. Sleep(NET_WAITMESSAGE_DELAY), tmWait+=NET_WAITMESSAGE_DELAY) {
  238. _pNetwork->TimerLoop();
  239. if (_cmiComm.Client_Update() == FALSE) {
  240. break;
  241. }
  242. // wait for message to come
  243. CNetworkMessage nmReceived;
  244. if (!_pNetwork->ReceiveFromServerReliable(nmReceived)) {
  245. continue;
  246. }
  247. // if this is the init message
  248. if (nmReceived.GetType() == MSG_REP_CONNECTLOCALSESSIONSTATE) {
  249. // just adjust your tick counters
  250. nmReceived>>ses_tmLastProcessedTick;
  251. nmReceived>>ses_iLastProcessedSequence;
  252. ses_tmInitializationTick = -1.0f;
  253. ses_tmInitializationTick2 = -1.0f;
  254. // finish waiting
  255. return;
  256. // otherwise
  257. } else {
  258. // it is invalid message
  259. ThrowF_t(TRANS("Invalid message while waiting for server session registration"));
  260. }
  261. // if client is disconnected
  262. if (!_cmiComm.Client_IsConnected()) {
  263. // quit
  264. ThrowF_t(TRANS("Client disconnected"));
  265. }
  266. }
  267. ThrowF_t(TRANS("Timeout while waiting for server session registration"));
  268. }
  269. void CSessionState::Start_AtClient_t(INDEX ctLocalPlayers) // throw char *
  270. {
  271. // send one unreliable packet to server to make the connection up and running
  272. CNetworkMessage nmKeepAlive(MSG_KEEPALIVE);
  273. _pNetwork->SendToServer(nmKeepAlive);
  274. // send registration request
  275. CNetworkMessage nmRegisterSessionState(MSG_REQ_CONNECTREMOTESESSIONSTATE);
  276. nmRegisterSessionState<<INDEX('VTAG')<<INDEX(_SE_BUILD_MAJOR)<<INDEX(_SE_BUILD_MINOR);
  277. nmRegisterSessionState<<_strModName;
  278. extern CTString net_strConnectPassword;
  279. extern CTString net_strVIPPassword;
  280. CTString strPasw = net_strConnectPassword;
  281. if (strPasw=="") {
  282. strPasw = net_strVIPPassword;
  283. }
  284. nmRegisterSessionState<<strPasw;
  285. nmRegisterSessionState<<ctLocalPlayers;
  286. ses_sspParams.Update();
  287. nmRegisterSessionState<<ses_sspParams;
  288. _pNetwork->SendToServerReliable(nmRegisterSessionState);
  289. // prepare file or memory stream for state
  290. CTFileStream strmStateFile; CTMemoryStream strmStateMem;
  291. CTStream *pstrmState;
  292. extern INDEX net_bDumpConnectionInfo;
  293. if (net_bDumpConnectionInfo) {
  294. strmStateFile.Create_t(CTString("Temp\\DefaultState.bin"));
  295. pstrmState = &strmStateFile;
  296. } else {
  297. pstrmState = &strmStateMem;
  298. }
  299. {
  300. // wait for server's response
  301. CTMemoryStream strmMessage;
  302. WaitStream_t(strmMessage, "reply", MSG_REP_CONNECTREMOTESESSIONSTATE);
  303. // get motd
  304. strmMessage>>ses_strMOTD;
  305. // get info for creating default state
  306. CTFileName fnmWorld;
  307. strmMessage>>fnmWorld;
  308. ULONG ulSpawnFlags;
  309. strmMessage>>ulSpawnFlags;
  310. UBYTE aubProperties[NET_MAXSESSIONPROPERTIES];
  311. strmMessage.Read_t(aubProperties, NET_MAXSESSIONPROPERTIES);
  312. // create default state
  313. NET_MakeDefaultState_t(fnmWorld, ulSpawnFlags, aubProperties, *pstrmState);
  314. pstrmState->SetPos_t(0);
  315. }
  316. // send one unreliable packet to server to make the connection up and running
  317. {CNetworkMessage nmKeepAlive(MSG_KEEPALIVE);
  318. _pNetwork->SendToServer(nmKeepAlive); }
  319. // send data request
  320. CPrintF(TRANS("Sending statedelta request\n"));
  321. CNetworkMessage nmRequestDelta(MSG_REQ_STATEDELTA);
  322. _pNetwork->SendToServerReliable(nmRequestDelta);
  323. {
  324. // wait for server's response
  325. CTMemoryStream strmMessage;
  326. WaitStream_t(strmMessage, "data", MSG_REP_STATEDELTA);
  327. // decompress saved session state
  328. CTMemoryStream strmDelta;
  329. CzlibCompressor comp;
  330. comp.UnpackStream_t(strmMessage, strmDelta);
  331. CTMemoryStream strmNew;
  332. DIFF_Undiff_t(pstrmState, &strmDelta, &strmNew);
  333. strmNew.SetPos_t(0);
  334. // read the initialization information from the stream
  335. Read_t(&strmNew);
  336. ses_tmInitializationTick = -1.0f;
  337. ses_tmInitializationTick2 = -1.0f;
  338. }
  339. // send one unreliable packet to server to make the connection up and running
  340. {CNetworkMessage nmKeepAlive(MSG_KEEPALIVE);
  341. _pNetwork->SendToServer(nmKeepAlive); }
  342. CPrintF(TRANS("Sending CRC request\n"));
  343. // send data request
  344. CNetworkMessage nmRequestCRC(MSG_REQ_CRCLIST);
  345. _pNetwork->SendToServerReliable(nmRequestCRC);
  346. {
  347. // wait for CRC challenge
  348. CTMemoryStream strmMessage;
  349. WaitStream_t(strmMessage, "CRC", MSG_REQ_CRCCHECK);
  350. // make response
  351. CNetworkMessage nmCRC(MSG_REP_CRCCHECK);
  352. nmCRC<<CRCT_MakeCRCForFiles_t(strmMessage)<<ses_iLastProcessedSequence;
  353. _pNetwork->SendToServerReliable(nmCRC);
  354. }
  355. // send one unreliable packet to server to make the connection up and running
  356. {CNetworkMessage nmKeepAlive(MSG_KEEPALIVE);
  357. _pNetwork->SendToServer(nmKeepAlive); }
  358. }
  359. // notify entities of level change
  360. void CSessionState::SendLevelChangeNotification(CEntityEvent &ee)
  361. {
  362. // for each entity in the world
  363. {FOREACHINDYNAMICCONTAINER(_pNetwork->ga_World.wo_cenEntities, CEntity, iten) {
  364. // if it should be notified
  365. if (iten->en_ulFlags&ENF_NOTIFYLEVELCHANGE) {
  366. // send the event
  367. iten->SendEvent(ee);
  368. }
  369. }}
  370. }
  371. // wait for a stream to come from server
  372. void CSessionState::WaitStream_t(CTMemoryStream &strmMessage, const CTString &strName,
  373. INDEX iMsgCode)
  374. {
  375. // start waiting for server's response
  376. SetProgressDescription(TRANS("waiting for ")+strName);
  377. CallProgressHook_t(0.0f);
  378. SLONG slReceivedLast = 0;
  379. // yes, we need the client/server updates in the progres hook
  380. _bRunNetUpdates = TRUE;
  381. // repeat until timed out
  382. for(TIME tmWait=0; tmWait<net_tmConnectionTimeout*1000;
  383. Sleep(NET_WAITMESSAGE_DELAY), tmWait+=NET_WAITMESSAGE_DELAY) {
  384. // update network connection sockets
  385. if (_cmiComm.Client_Update() == FALSE) {
  386. break;
  387. }
  388. // check how much is received so far
  389. SLONG slExpectedSize; // slReceivedSoFar;
  390. SLONG slReceivedSize;
  391. _cmiComm.Client_PeekSize_Reliable(slExpectedSize,slReceivedSize);
  392. // if nothing received yet
  393. if (slExpectedSize==0) {
  394. // progress with waiting
  395. CallProgressHook_t(tmWait/(net_tmConnectionTimeout*1000));
  396. // if something received
  397. } else {
  398. // if some new data received
  399. if (slReceivedSize!=slReceivedLast) {
  400. slReceivedLast = slReceivedSize;
  401. // reset timeout
  402. tmWait=0;
  403. }
  404. // progress with receiving
  405. SetProgressDescription(TRANS("receiving ")+strName+" ");
  406. CallProgressHook_t((float)slReceivedSize/slExpectedSize);
  407. }
  408. // if not everything received yet
  409. if (!_pNetwork->ReceiveFromServerReliable(strmMessage)) {
  410. // continue waiting
  411. continue;
  412. }
  413. // read message identifier
  414. strmMessage.SetPos_t(0);
  415. INDEX iID;
  416. strmMessage>>iID;
  417. // if this is the message
  418. if (iID == iMsgCode) {
  419. // all ok
  420. CallProgressHook_t(1.0f);
  421. // no more client/server updates in the progres hook
  422. _bRunNetUpdates = TRUE;
  423. return;
  424. // if disconnected
  425. } else if (iID == MSG_INF_DISCONNECTED) {
  426. // confirm disconnect
  427. CNetworkMessage nmConfirmDisconnect(MSG_REP_DISCONNECTED);
  428. _pNetwork->SendToServerReliable(nmConfirmDisconnect);
  429. // report the reason
  430. CTString strReason;
  431. strmMessage>>strReason;
  432. ses_strDisconnected = strReason;
  433. // no more client/server updates in the progres hook
  434. _bRunNetUpdates = FALSE;
  435. ThrowF_t(TRANS("Disconnected: %s\n"), strReason);
  436. // otherwise
  437. } else {
  438. // no more client/server updates in the progres hook
  439. _bRunNetUpdates = FALSE;
  440. // it is invalid message
  441. ThrowF_t(TRANS("Invalid stream while waiting for %s"), strName);
  442. }
  443. // if client is disconnected
  444. if (!_cmiComm.Client_IsConnected()) {
  445. // no more client/server updates in the progres hook
  446. _bRunNetUpdates = FALSE;
  447. // quit
  448. ThrowF_t(TRANS("Client disconnected"));
  449. }
  450. }
  451. // no more client/server updates in the progres hook
  452. _bRunNetUpdates = FALSE;
  453. // CNetworkMessage nmConfirmDisconnect(MSG_REP_DISCONNECTED);
  454. // _pNetwork->SendToServerReliable(nmConfirmDisconnect);
  455. ThrowF_t(TRANS("Timeout while waiting for %s"), strName);
  456. }
  457. // check if disconnected
  458. BOOL CSessionState::IsDisconnected(void)
  459. {
  460. return ses_strDisconnected!="";
  461. }
  462. // print an incoming chat message to console
  463. void CSessionState::PrintChatMessage(ULONG ulFrom, const CTString &strFrom, const CTString &strMessage)
  464. {
  465. CTString strSender;
  466. // if no sender players
  467. if (ulFrom==0) {
  468. // take symbolic sender string
  469. strSender = strFrom;
  470. // if there are sender players
  471. } else {
  472. // for each sender player
  473. for(INDEX ipl=0; ipl<ses_apltPlayers.Count(); ipl++) {
  474. CPlayerTarget &plt = ses_apltPlayers[ipl];
  475. if (plt.IsActive() && (ulFrom & (1UL<<ipl)) ) {
  476. // add its name to the sender list
  477. if (strSender!="") {
  478. strSender+=", ";
  479. }
  480. strSender+=plt.plt_penPlayerEntity->GetPlayerName();
  481. }
  482. }
  483. }
  484. // let eventual script addon process the message
  485. extern CTString cmd_strChatSender ;
  486. extern CTString cmd_strChatMessage;
  487. extern CTString cmd_cmdOnChat;
  488. cmd_strChatSender = strSender;
  489. cmd_strChatMessage = strMessage;
  490. if (cmd_cmdOnChat!="") {
  491. _pShell->Execute(cmd_cmdOnChat);
  492. }
  493. // if proccessing didn't kill it
  494. if (cmd_strChatSender!="" && cmd_strChatMessage!="") {
  495. // print the message
  496. CPrintF("%s: ^o^cFFFFFF%s^r\n", (const char*)cmd_strChatSender, (const char*)cmd_strChatMessage);
  497. }
  498. extern INDEX net_ctChatMessages;
  499. net_ctChatMessages++;
  500. }
  501. /* NOTES:
  502. 1) New thinkers might be added by current ones, but it doesn't matter,
  503. since they must be added forward in time and the list is sorted, so they
  504. cannot be processed in this tick.
  505. 2) Thinkers/Movers can be removed during iteration, but the CEntityPointer
  506. guarantee that they are not freed from memory.
  507. */
  508. // do physics for a game tick
  509. void CSessionState::HandleMovers(void)
  510. {
  511. _pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_HANDLEMOVERS);
  512. // CPrintF("---- tick %g\n", _pTimer->CurrentTick());
  513. // put all movers in active list, pushing ones first
  514. CListHead lhActiveMovers, lhDoneMovers, lhDummyMovers;
  515. {FORDELETELIST(CMovableEntity, en_lnInMovers, _pNetwork->ga_World.wo_lhMovers, itenMover) {
  516. CMovableEntity *pen = itenMover;
  517. pen->en_lnInMovers.Remove();
  518. // if predicting, and it is not a predictor
  519. if (ses_bPredicting && !pen->IsPredictor()) {
  520. // skip it
  521. lhDummyMovers.AddTail(pen->en_lnInMovers);
  522. continue;
  523. }
  524. if (!(pen->en_ulFlags&ENF_DELETED)) {
  525. if ((pen->en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_PUSH) {
  526. lhActiveMovers.AddHead(pen->en_lnInMovers);
  527. } else {
  528. lhActiveMovers.AddTail(pen->en_lnInMovers);
  529. }
  530. }
  531. }}
  532. // for each active mover
  533. {FORDELETELIST(CMovableEntity, en_lnInMovers, lhActiveMovers, itenMover) {
  534. // let it clear its temporary variables to prevent bad syncs
  535. itenMover->ClearMovingTemp();
  536. }}
  537. // for each active mover
  538. {FORDELETELIST(CMovableEntity, en_lnInMovers, lhActiveMovers, itenMover) {
  539. // let it calculate its wanted parameters for this tick
  540. itenMover->PreMoving();
  541. }}
  542. // while there are some active movers
  543. while(!lhActiveMovers.IsEmpty()) {
  544. // get first one
  545. CMovableEntity *penMoving = LIST_HEAD(lhActiveMovers, CMovableEntity, en_lnInMovers);
  546. CEntityPointer penCurrent = penMoving; // just to keep it alive around the loop
  547. // first move it to done list (if not done, it will move back to world's movers)
  548. penMoving->en_lnInMovers.Remove();
  549. lhDoneMovers.AddTail(penMoving->en_lnInMovers);
  550. /*
  551. CPrintF("**%s(%08x)",
  552. penMoving->en_pecClass->ec_pdecDLLClass->dec_strName,
  553. penMoving->en_ulID);
  554. if (penMoving->IsPredictable()) {
  555. CPrintF(" predictable");
  556. }
  557. if (penMoving->IsPredictor()) {
  558. CPrintF(" predictor");
  559. }
  560. if (penMoving->IsPredicted()) {
  561. CPrintF(" predicted");
  562. }
  563. if (penMoving->en_penReference!=NULL) {
  564. CPrintF("reference id%08x", penMoving->en_penReference->en_ulID);
  565. }
  566. CPrintF("\n");
  567. */
  568. // let it do its own physics
  569. penMoving->DoMoving();
  570. // CPrintF("\n");
  571. // if any mover is re-added, put it to the end of active list
  572. lhActiveMovers.MoveList(_pNetwork->ga_World.wo_lhMovers);
  573. }
  574. // for each done mover
  575. {FORDELETELIST(CMovableEntity, en_lnInMovers, lhDoneMovers, itenMover) {
  576. // if predicting, and it is not a predictor
  577. if (ses_bPredicting && !itenMover->IsPredictor()) {
  578. // skip it
  579. continue;
  580. }
  581. // let it calculate its parameters after all movement has been resolved
  582. itenMover->PostMoving();
  583. }}
  584. // for each done mover
  585. {FORDELETELIST(CMovableEntity, en_lnInMovers, lhDoneMovers, itenMover) {
  586. CMovableEntity *pen = itenMover;
  587. // if predicting, and it is not a predictor
  588. if (ses_bPredicting && !itenMover->IsPredictor()) {
  589. // skip it
  590. continue;
  591. }
  592. // if marked for removing from list of movers
  593. if (pen->en_ulFlags&ENF_INRENDERING) {
  594. // remove it
  595. pen->en_ulFlags&=~ENF_INRENDERING;
  596. pen->en_lnInMovers.Remove();
  597. }
  598. // let it clear its temporary variables to prevent bad syncs
  599. pen->ClearMovingTemp();
  600. }}
  601. // return all done movers to the world's list
  602. _pNetwork->ga_World.wo_lhMovers.MoveList(lhDummyMovers);
  603. _pNetwork->ga_World.wo_lhMovers.MoveList(lhDoneMovers);
  604. // handle all the sent events
  605. CEntity::HandleSentEvents();
  606. _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_HANDLEMOVERS);
  607. }
  608. // do thinking for a game tick
  609. void CSessionState::HandleTimers(TIME tmCurrentTick)
  610. {
  611. #define TIME_EPSILON 0.0001f
  612. IFDEBUG(TIME tmLast = 0.0f);
  613. _pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_HANDLETIMERS);
  614. // repeat
  615. CListHead &lhTimers = _pNetwork->ga_World.wo_lhTimers;
  616. FOREVER {
  617. // no entity found initially
  618. CRationalEntity *penTimer = NULL;
  619. // for each entity in list of timers
  620. FOREACHINLIST(CRationalEntity, en_lnInTimers, lhTimers, iten) {
  621. // if due after current time
  622. if(iten->en_timeTimer>tmCurrentTick+TIME_EPSILON) {
  623. // stop searching
  624. break;
  625. }
  626. // if now predicting and it is not a predictor
  627. if (ses_bPredicting && !iten->IsPredictor()) {
  628. // skip it
  629. continue;
  630. }
  631. // remember found entity, and stop searching
  632. penTimer = iten;
  633. break;
  634. }
  635. // if no entity is found
  636. if (penTimer==NULL) {
  637. // stop
  638. break;
  639. }
  640. // check that timers are propertly handled
  641. ASSERT(penTimer->en_timeTimer>tmCurrentTick-_pTimer->TickQuantum-TIME_EPSILON);
  642. //ASSERT(penTimer->en_timeTimer>=tmLast);
  643. IFDEBUG(tmLast=penTimer->en_timeTimer);
  644. // remove the timer from the list
  645. penTimer->en_timeTimer = THINKTIME_NEVER;
  646. penTimer->en_lnInTimers.Remove();
  647. // send timer event to the entity
  648. penTimer->SendEvent(ETimer());
  649. }
  650. // handle all the sent events
  651. CEntity::HandleSentEvents();
  652. _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_HANDLETIMERS);
  653. }
  654. // do a warm-up run of the world for a few ticks
  655. void CSessionState::WarmUpWorld(void)
  656. {
  657. #define WORLD_WORMUP_COUNT 20 // run 20 ticks to get stable
  658. ses_tmLastProcessedTick = _pNetwork->ga_srvServer.srv_tmLastProcessedTick = 0;
  659. ses_iLastProcessedSequence = _pNetwork->ga_srvServer.srv_iLastProcessedSequence = -1;
  660. // add a few empty all-action messages to the game stream
  661. for (INDEX iTick=0; iTick<WORLD_WORMUP_COUNT; iTick++) {
  662. _pNetwork->ga_srvServer.srv_tmLastProcessedTick += _pTimer->TickQuantum;
  663. _pNetwork->ga_srvServer.srv_iLastProcessedSequence++;
  664. CNetworkStreamBlock nsbAllActions(MSG_SEQ_ALLACTIONS, _pNetwork->ga_srvServer.srv_iLastProcessedSequence);
  665. nsbAllActions<<_pNetwork->ga_srvServer.srv_tmLastProcessedTick;
  666. nsbAllActions.Rewind();
  667. ses_nsGameStream.AddBlock(nsbAllActions);
  668. }
  669. // process the blocks
  670. ProcessGameStream();
  671. }
  672. // create a checksum value for sync-check
  673. void CSessionState::ChecksumForSync(ULONG &ulCRC, INDEX iExtensiveSyncCheck)
  674. {
  675. CRC_AddLONG(ulCRC, ses_iLastProcessedSequence);
  676. CRC_AddLONG(ulCRC, ses_iLevel);
  677. CRC_AddLONG(ulCRC, ses_bPause);
  678. if (iExtensiveSyncCheck>0) {
  679. CRC_AddLONG(ulCRC, ses_bGameFinished);
  680. CRC_AddLONG(ulCRC, ses_ulRandomSeed);
  681. }
  682. // if all entities should be synced
  683. if (iExtensiveSyncCheck>1) {
  684. // for each active entity in the world
  685. {FOREACHINDYNAMICCONTAINER(_pNetwork->ga_World.wo_cenEntities, CEntity, iten) {
  686. if (iten->IsPredictor()) {
  687. continue;
  688. }
  689. iten->ChecksumForSync(ulCRC, iExtensiveSyncCheck);
  690. }}
  691. // for each entity in the world
  692. {FOREACHINDYNAMICCONTAINER(_pNetwork->ga_World.wo_cenAllEntities, CEntity, iten) {
  693. if (iten->IsPredictor()) {
  694. continue;
  695. }
  696. iten->ChecksumForSync(ulCRC, iExtensiveSyncCheck);
  697. }}
  698. }
  699. if (iExtensiveSyncCheck>0) {
  700. // checksum all movers
  701. {FOREACHINLIST(CMovableEntity, en_lnInMovers, _pNetwork->ga_World.wo_lhMovers, iten) {
  702. if (iten->IsPredictor()) {
  703. continue;
  704. }
  705. iten->ChecksumForSync(ulCRC, iExtensiveSyncCheck);
  706. }}
  707. }
  708. // checksum all active players
  709. {FOREACHINSTATICARRAY(ses_apltPlayers, CPlayerTarget, itclt) {
  710. CPlayerTarget &clt = *itclt;
  711. if (clt.IsActive()) {
  712. clt.plt_paPreLastAction.ChecksumForSync(ulCRC);
  713. clt.plt_paLastAction.ChecksumForSync(ulCRC);
  714. clt.plt_penPlayerEntity->ChecksumForSync(ulCRC, iExtensiveSyncCheck);
  715. }
  716. }}
  717. }
  718. // dump sync data to text file
  719. void CSessionState::DumpSync_t(CTStream &strm, INDEX iExtensiveSyncCheck) // throw char *
  720. {
  721. strm.FPrintF_t("Level: %d\n", ses_iLevel);
  722. strm.FPrintF_t("Sequence: %d\n", ses_iLastProcessedSequence);
  723. strm.FPrintF_t("Tick: %g\n", ses_tmLastProcessedTick);
  724. strm.FPrintF_t("Paused: %d\n", ses_bPause);
  725. if (iExtensiveSyncCheck>0) {
  726. strm.FPrintF_t("Finished: %d\n", ses_bGameFinished);
  727. strm.FPrintF_t("Random seed: 0x%08x\n", ses_ulRandomSeed);
  728. }
  729. _pNetwork->ga_World.LockAll();
  730. strm.FPrintF_t("\n\n======================== players:\n");
  731. // dump all active players
  732. {FOREACHINSTATICARRAY(ses_apltPlayers, CPlayerTarget, itclt) {
  733. CPlayerTarget &clt = *itclt;
  734. if (clt.IsActive()) {
  735. clt.plt_penPlayerEntity->DumpSync_t(strm, iExtensiveSyncCheck);
  736. strm.FPrintF_t("\n -- action:\n");
  737. clt.plt_paPreLastAction.DumpSync_t(strm);
  738. clt.plt_paLastAction.DumpSync_t(strm);
  739. }
  740. }}
  741. if (iExtensiveSyncCheck>0) {
  742. strm.FPrintF_t("\n\n======================== movers:\n");
  743. // dump all movers
  744. {FOREACHINLIST(CMovableEntity, en_lnInMovers, _pNetwork->ga_World.wo_lhMovers, iten) {
  745. if (iten->IsPredictor()) {
  746. continue;
  747. }
  748. iten->DumpSync_t(strm, iExtensiveSyncCheck);
  749. }}
  750. }
  751. // if all entities should be synced
  752. if (iExtensiveSyncCheck>1) {
  753. strm.FPrintF_t("\n\n======================== active entities (%d):\n",
  754. _pNetwork->ga_World.wo_cenEntities.Count());
  755. // for each entity in the world
  756. {FOREACHINDYNAMICCONTAINER(_pNetwork->ga_World.wo_cenEntities, CEntity, iten) {
  757. if (iten->IsPredictor()) {
  758. continue;
  759. }
  760. iten->DumpSync_t(strm, iExtensiveSyncCheck);
  761. }}
  762. strm.FPrintF_t("\n\n======================== all entities (%d):\n",
  763. _pNetwork->ga_World.wo_cenEntities.Count());
  764. // for each entity in the world
  765. {FOREACHINDYNAMICCONTAINER(_pNetwork->ga_World.wo_cenAllEntities, CEntity, iten) {
  766. if (iten->IsPredictor()) {
  767. continue;
  768. }
  769. iten->DumpSync_t(strm, iExtensiveSyncCheck);
  770. }}
  771. }
  772. _pNetwork->ga_World.UnlockAll();
  773. }
  774. #if DEBUG_SYNCSTREAMDUMPING
  775. /*
  776. * Obtain valid session dump memory stream
  777. */
  778. CTMemoryStream *GetDumpStream(void)
  779. {
  780. if( _pNetwork->ga_sesSessionState.ses_pstrm == NULL)
  781. {
  782. _pNetwork->ga_sesSessionState.ses_pstrm = new CTMemoryStream;
  783. }
  784. return _pNetwork->ga_sesSessionState.ses_pstrm;
  785. }
  786. void ClearDumpStream(void)
  787. {
  788. if( _pNetwork->ga_sesSessionState.ses_pstrm != NULL)
  789. {
  790. delete _pNetwork->ga_sesSessionState.ses_pstrm;
  791. _pNetwork->ga_sesSessionState.ses_pstrm = NULL;
  792. }
  793. }
  794. #endif
  795. /*
  796. * Process a game tick.
  797. */
  798. extern INDEX cli_bEmulateDesync;
  799. void CSessionState::ProcessGameTick(CNetworkMessage &nmMessage, TIME tmCurrentTick)
  800. {
  801. ses_tmLastPredictionProcessed = -1;
  802. _pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_PROCESSGAMETICK);
  803. ASSERT(this!=NULL);
  804. #if DEBUG_SYNCSTREAMDUMPING
  805. try
  806. {
  807. CTMemoryStream *pstrm = GetDumpStream();
  808. pstrm->FPrintF_t("Time tick: %.2f\n", tmCurrentTick);
  809. }
  810. catch (char *strError)
  811. {
  812. CPrintF("Cannot dump sync data: %s\n", strError);
  813. }
  814. #endif
  815. //CPrintF("normal: %.2f\n", tmCurrentTick);
  816. // FPU must be in 24-bit mode
  817. CSetFPUPrecision FPUPrecision(FPT_24BIT);
  818. // copy the tick to process into tick used for all tasks
  819. _pTimer->SetCurrentTick(tmCurrentTick);
  820. _pfNetworkProfile.IncrementAveragingCounter();
  821. _pfPhysicsProfile.IncrementAveragingCounter();
  822. // random is allowed only here, during entity ai
  823. ses_bAllowRandom = TRUE;
  824. _pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_APPLYACTIONS);
  825. // for all clients
  826. INDEX iClient = 0;
  827. FOREACHINSTATICARRAY(ses_apltPlayers, CPlayerTarget, itplt) {
  828. // if client is active
  829. if (itplt->IsActive()) {
  830. // player indices transmission is unneccessary unless if debugging
  831. // // check that it is present in message
  832. // INDEX iClientInMessage;
  833. // nmMessage>>iClientInMessage; // client index
  834. // ASSERT(iClient==iClientInMessage);
  835. // read the action
  836. CPlayerAction paAction;
  837. nmMessage>>paAction;
  838. // apply the action
  839. itplt->ApplyActionPacket(paAction);
  840. // desync emulation!
  841. if( cli_bEmulateDesync) {
  842. itplt->plt_penPlayerEntity->SetHealth(1.0f);
  843. }
  844. }
  845. iClient++;
  846. }
  847. cli_bEmulateDesync = FALSE;
  848. // handle all the sent events
  849. CEntity::HandleSentEvents();
  850. _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_APPLYACTIONS);
  851. // do thinking
  852. HandleTimers(tmCurrentTick);
  853. // do physics
  854. HandleMovers();
  855. // notify all entities of level change as needed
  856. if (_lphCurrent==LCP_INITIATED) {
  857. EPreLevelChange ePreChange;
  858. ePreChange.iUserData = _pNetwork->ga_iNextLevelUserData;
  859. SendLevelChangeNotification(ePreChange);
  860. CEntity::HandleSentEvents();
  861. _lphCurrent=LCP_SIGNALLED;
  862. }
  863. if (_lphCurrent==LCP_CHANGED) {
  864. EPostLevelChange ePostChange;
  865. ePostChange.iUserData = _pNetwork->ga_iNextLevelUserData;
  866. SendLevelChangeNotification(ePostChange);
  867. CEntity::HandleSentEvents();
  868. _lphCurrent=LCP_NOCHANGE;
  869. }
  870. _pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_WORLDBASETICK);
  871. // let the worldbase execute its tick function
  872. if (_pNetwork->ga_World.wo_pecWorldBaseClass!=NULL
  873. &&_pNetwork->ga_World.wo_pecWorldBaseClass->ec_pdecDLLClass!=NULL
  874. &&_pNetwork->ga_World.wo_pecWorldBaseClass->ec_pdecDLLClass->dec_OnWorldTick!=NULL) {
  875. _pNetwork->ga_World.wo_pecWorldBaseClass->ec_pdecDLLClass->
  876. dec_OnWorldTick(&_pNetwork->ga_World);
  877. }
  878. // handle all the sent events
  879. CEntity::HandleSentEvents();
  880. _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_WORLDBASETICK);
  881. // make sync-check and send to server if needed
  882. MakeSynchronisationCheck();
  883. #if DEBUG_SYNCSTREAMDUMPING
  884. extern INDEX cli_bDumpSyncEachTick;
  885. if( cli_bDumpSyncEachTick)
  886. {
  887. DumpSyncToMemory();
  888. }
  889. #endif
  890. ses_bAllowRandom = FALSE;
  891. ses_tmPredictionHeadTick = Max(ses_tmPredictionHeadTick, tmCurrentTick);
  892. _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_PROCESSGAMETICK);
  893. // assure that FPU precision was low all the rendering time
  894. ASSERT( GetFPUPrecision()==FPT_24BIT);
  895. // let eventual script do something on each tick
  896. extern FLOAT cmd_tmTick;
  897. extern CTString cmd_cmdOnTick;
  898. if (cmd_cmdOnTick!="") {
  899. cmd_tmTick = tmCurrentTick;
  900. _pShell->Execute(cmd_cmdOnTick);
  901. }
  902. }
  903. /* Process a predicted game tick. */
  904. void CSessionState::ProcessPredictedGameTick(INDEX iPredictionStep, FLOAT fFactor, TIME tmCurrentTick)
  905. {
  906. _pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_PROCESSGAMETICK);
  907. ASSERT(this!=NULL);
  908. //CPrintF("predicted: %.2f\n", tmCurrentTick);
  909. // FPU must be in 24-bit mode
  910. CSetFPUPrecision FPUPrecision(FPT_24BIT);
  911. // now predicting
  912. ses_bPredicting = TRUE;
  913. // copy the tick to process into tick used for all tasks
  914. _pTimer->SetCurrentTick(tmCurrentTick);
  915. _pfNetworkProfile.IncrementAveragingCounter();
  916. _pfPhysicsProfile.IncrementAveragingCounter();
  917. // random is allowed only here, during entity ai
  918. ses_bAllowRandom = TRUE;
  919. _pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_APPLYACTIONS);
  920. // for all clients
  921. FOREACHINSTATICARRAY(ses_apltPlayers, CPlayerTarget, itplt) {
  922. // if client is active
  923. if (itplt->IsActive()) {
  924. // apply the predicted action
  925. itplt->ApplyPredictedAction(iPredictionStep, fFactor);
  926. }
  927. }
  928. // handle all the sent events
  929. CEntity::HandleSentEvents();
  930. _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_APPLYACTIONS);
  931. // do thinking
  932. HandleTimers(tmCurrentTick);
  933. // do physics
  934. HandleMovers();
  935. _pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_WORLDBASETICK);
  936. // handle all the sent events
  937. CEntity::HandleSentEvents();
  938. _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_WORLDBASETICK);
  939. ses_bAllowRandom = FALSE;
  940. // not predicting any more
  941. ses_bPredicting = FALSE;
  942. ses_tmPredictionHeadTick = Max(ses_tmPredictionHeadTick, tmCurrentTick);
  943. _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_PROCESSGAMETICK);
  944. // assure that FPU precision was low all the rendering time
  945. ASSERT( GetFPUPrecision()==FPT_24BIT);
  946. }
  947. /*
  948. * Process all eventual available gamestream blocks.
  949. */
  950. void CSessionState::ProcessGameStream(void)
  951. {
  952. _pfNetworkProfile.StartTimer(CNetworkProfile::PTI_SESSIONSTATE_PROCESSGAMESTREAM);
  953. // must be in 24bit mode when managing entities
  954. CSetFPUPrecision FPUPrecision(FPT_24BIT);
  955. TIME tmDemoNow = _pNetwork->ga_fDemoTimer;
  956. // if playing a demo
  957. if (_pNetwork->ga_bDemoPlay) {
  958. // find how much ticks to step by now
  959. if (ses_tmLastDemoSequence<0.0f) {
  960. ses_tmLastDemoSequence=tmDemoNow;
  961. }
  962. // if it is finished
  963. if (_pNetwork->ga_bDemoPlayFinished) {
  964. _pfNetworkProfile.StopTimer(CNetworkProfile::PTI_SESSIONSTATE_PROCESSGAMESTREAM);
  965. // do nothing
  966. return;
  967. }
  968. }
  969. // repeat
  970. FOREVER {
  971. // if playing a demo
  972. if (_pNetwork->ga_bDemoPlay) {
  973. // if finished for this pass
  974. if (ses_tmLastDemoSequence>=tmDemoNow) {
  975. _pfNetworkProfile.StopTimer(CNetworkProfile::PTI_SESSIONSTATE_PROCESSGAMESTREAM);
  976. // end the loop
  977. return;
  978. }
  979. // try to
  980. try {
  981. // read a stream block from the demo
  982. CChunkID cid = _pNetwork->ga_strmDemoPlay.PeekID_t();
  983. if (cid == CChunkID("DTCK")) {
  984. _pNetwork->ga_strmDemoPlay.ExpectID_t("DTCK");// demo tick
  985. CNetworkStreamBlock nsbBlock;
  986. nsbBlock.Read_t(_pNetwork->ga_strmDemoPlay);
  987. ses_nsGameStream.AddBlock(nsbBlock);
  988. _pNetwork->ga_bDemoPlayFinished = FALSE;
  989. } else {
  990. _pNetwork->ga_strmDemoPlay.ExpectID_t("DEND"); // demo end
  991. _pNetwork->ga_bDemoPlayFinished = TRUE;
  992. _pfNetworkProfile.StopTimer(CNetworkProfile::PTI_SESSIONSTATE_PROCESSGAMESTREAM);
  993. return;
  994. }
  995. // if not successful
  996. } catch(char *strError) {
  997. // report error
  998. CPrintF(TRANS("Error while playing demo: %s"), strError);
  999. _pfNetworkProfile.StopTimer(CNetworkProfile::PTI_SESSIONSTATE_PROCESSGAMESTREAM);
  1000. return;
  1001. }
  1002. }
  1003. // calculate index of next expected sequence
  1004. INDEX iSequence = ses_iLastProcessedSequence+1;
  1005. // get the stream block with that sequence
  1006. CNetworkStreamBlock *pnsbBlock;
  1007. CNetworkStream::Result res = ses_nsGameStream.GetBlockBySequence(iSequence, pnsbBlock);
  1008. // if it is found
  1009. if (res==CNetworkStream::R_OK) {
  1010. // if recording a demo
  1011. if (_pNetwork->ga_bDemoRec) {
  1012. // try to
  1013. try {
  1014. // write the stream block to the demo
  1015. _pNetwork->ga_strmDemoRec.WriteID_t("DTCK");
  1016. pnsbBlock->Write_t(_pNetwork->ga_strmDemoRec);
  1017. // if not successful
  1018. } catch(char *strError) {
  1019. // report error
  1020. CPrintF(TRANS("Error while recording demo: %s"), strError);
  1021. // stop recording
  1022. _pNetwork->StopDemoRec();
  1023. }
  1024. }
  1025. // remember the message type
  1026. int iMsgType=pnsbBlock->GetType();
  1027. // remember the processed sequence
  1028. ses_iLastProcessedSequence = iSequence;
  1029. // process the stream block
  1030. ProcessGameStreamBlock(*pnsbBlock);
  1031. // remove the block from the stream
  1032. pnsbBlock->RemoveFromStream();
  1033. delete pnsbBlock;
  1034. // remove eventual resent blocks that have already been processed
  1035. ses_nsGameStream.RemoveOlderBlocksBySequence(ses_iLastProcessedSequence-2);
  1036. // if the message is all actions
  1037. if (iMsgType==MSG_SEQ_ALLACTIONS) {
  1038. // if playing a demo
  1039. if (_pNetwork->ga_bDemoPlay) {
  1040. // step demo sequence
  1041. ses_tmLastDemoSequence+=_pTimer->TickQuantum;
  1042. }
  1043. }
  1044. // if it is not avaliable yet
  1045. } else if (res==CNetworkStream::R_BLOCKNOTRECEIVEDYET) {
  1046. // finish
  1047. _pfNetworkProfile.StopTimer(CNetworkProfile::PTI_SESSIONSTATE_PROCESSGAMESTREAM);
  1048. return;
  1049. // if it is missing
  1050. } else if (res==CNetworkStream::R_BLOCKMISSING) {
  1051. // if it is a new sequence
  1052. if (iSequence>ses_iMissingSequence) {
  1053. ses_iMissingSequence = iSequence;
  1054. // setup timeout
  1055. ses_tvResendTime = _pTimer->GetHighPrecisionTimer();
  1056. ses_tmResendTimeout = 0.1f;
  1057. }
  1058. // if timeout occured
  1059. if (_pTimer->GetHighPrecisionTimer()>ses_tvResendTime+CTimerValue(ses_tmResendTimeout)) {
  1060. _pNetwork->AddNetGraphValue(NGET_MISSING, 1.0f); // missing sequence
  1061. // find how many are missing
  1062. INDEX iNextValid = ses_nsGameStream.GetOldestSequenceAfter(iSequence);
  1063. INDEX ctSequences = Max(iNextValid-iSequence, INDEX(1));
  1064. // create a request for resending the missing packet
  1065. CNetworkMessage nmResendRequest(MSG_REQUESTGAMESTREAMRESEND);
  1066. nmResendRequest<<iSequence;
  1067. nmResendRequest<<ctSequences;
  1068. // send the request to the server
  1069. _pNetwork->SendToServer(nmResendRequest);
  1070. extern INDEX net_bReportMiscErrors;
  1071. if (net_bReportMiscErrors) {
  1072. CPrintF(TRANS("Session State: Missing sequences %d-%d(%d) timeout %g\n"),
  1073. iSequence, iSequence+ctSequences-1, ctSequences, ses_tmResendTimeout);
  1074. }
  1075. // increase the timeout
  1076. ses_tvResendTime = _pTimer->GetHighPrecisionTimer();
  1077. ses_tmResendTimeout *= 2.0f;
  1078. }
  1079. // finish
  1080. _pfNetworkProfile.StopTimer(CNetworkProfile::PTI_SESSIONSTATE_PROCESSGAMESTREAM);
  1081. return;
  1082. }
  1083. }
  1084. }
  1085. // flush prediction actions that were already processed
  1086. void CSessionState::FlushProcessedPredictions(void)
  1087. {
  1088. // for all clients
  1089. FOREACHINSTATICARRAY(ses_apltPlayers, CPlayerTarget, itplt) {
  1090. // flush
  1091. itplt->FlushProcessedPredictions();
  1092. }
  1093. }
  1094. /* Find out how many prediction steps are currently pending. */
  1095. INDEX CSessionState::GetPredictionStepsCount(void)
  1096. {
  1097. // start with no prediction
  1098. INDEX ctPredictionSteps = 0;
  1099. // for all clients
  1100. FOREACHINSTATICARRAY(ses_apltPlayers, CPlayerTarget, itplt) {
  1101. // update maximum number of possible predictions
  1102. ctPredictionSteps = Max(ctPredictionSteps, itplt->GetNumberOfPredictions());
  1103. }
  1104. return ctPredictionSteps;
  1105. }
  1106. /* Process all eventual available prediction actions. */
  1107. void CSessionState::ProcessPrediction(void)
  1108. {
  1109. // FPU must be in 24-bit mode
  1110. CSetFPUPrecision FPUPrecision(FPT_24BIT);
  1111. // get number of steps that could be predicted
  1112. INDEX ctSteps = GetPredictionStepsCount();
  1113. // limit prediction
  1114. extern INDEX cli_iMaxPredictionSteps;
  1115. ctSteps = ClampUp(ctSteps, cli_iMaxPredictionSteps);
  1116. // if none
  1117. if(ctSteps<=0) {
  1118. // do nothing
  1119. return;
  1120. }
  1121. // if this would not result in any new tick predicted
  1122. TIME tmNow = ses_tmLastProcessedTick+ctSteps*_pTimer->TickQuantum;
  1123. if (Abs(ses_tmLastPredictionProcessed-tmNow)<_pTimer->TickQuantum/10.0f) {
  1124. // do nothing
  1125. return;
  1126. }
  1127. // remeber what was predicted now
  1128. ses_tmLastPredictionProcessed = tmNow;
  1129. // remember random seed and entity ID
  1130. ULONG ulOldRandom = ses_ulRandomSeed;
  1131. ULONG ulEntityID = _pNetwork->ga_World.wo_ulNextEntityID;
  1132. // delete all predictors (if any left from last time)
  1133. _pNetwork->ga_World.DeletePredictors();
  1134. // create new predictors
  1135. _pNetwork->ga_World.CreatePredictors();
  1136. // for each step
  1137. TIME tmPredictedTick = ses_tmLastProcessedTick;
  1138. for(INDEX iPredictionStep=0; iPredictionStep<ctSteps; iPredictionStep++) {
  1139. tmPredictedTick+=_pTimer->TickQuantum;
  1140. //ses_tmPredictionHeadTick = Max(ses_tmPredictionHeadTick, tmPredictedTick);
  1141. // predict it
  1142. ProcessPredictedGameTick(iPredictionStep, FLOAT(iPredictionStep)/ctSteps, tmPredictedTick);
  1143. }
  1144. // restore random seed and entity ID
  1145. ses_ulRandomSeed = ulOldRandom;
  1146. _pNetwork->ga_World.wo_ulNextEntityID = ulEntityID;
  1147. }
  1148. /*
  1149. * Process a gamestream block.
  1150. */
  1151. void CSessionState::ProcessGameStreamBlock(CNetworkMessage &nmMessage)
  1152. {
  1153. // copy the tick to process into tick used for all tasks
  1154. _pTimer->SetCurrentTick(ses_tmLastProcessedTick);
  1155. // check message type
  1156. switch (nmMessage.GetType()) {
  1157. // if adding a new player
  1158. case MSG_SEQ_ADDPLAYER: {
  1159. _pNetwork->AddNetGraphValue(NGET_NONACTION, 1.0f); // non-action sequence
  1160. INDEX iNewPlayer;
  1161. CPlayerCharacter pcCharacter;
  1162. nmMessage>>iNewPlayer; // player index
  1163. nmMessage>>pcCharacter; // player character
  1164. // delete all predictors
  1165. _pNetwork->ga_World.DeletePredictors();
  1166. // activate the player
  1167. ses_apltPlayers[iNewPlayer].Activate();
  1168. // if there is no entity with that character in the world
  1169. CPlayerEntity *penNewPlayer = _pNetwork->ga_World.FindEntityWithCharacter(pcCharacter);
  1170. if (penNewPlayer==NULL) {
  1171. // create an entity for it
  1172. CPlacement3D pl(FLOAT3D(0.0f,0.0f,0.0f), ANGLE3D(0,0,0));
  1173. try {
  1174. CTFileName fnmPlayer = CTString("Classes\\Player.ecl"); // this must not be a dependency!
  1175. penNewPlayer = (CPlayerEntity*)(_pNetwork->ga_World.CreateEntity_t(pl, fnmPlayer));
  1176. // attach entity to client data
  1177. ses_apltPlayers[iNewPlayer].AttachEntity(penNewPlayer);
  1178. // attach the character to it
  1179. penNewPlayer->en_pcCharacter = pcCharacter;
  1180. // prepare the entity
  1181. penNewPlayer->Initialize();
  1182. } catch (char *strError) {
  1183. (void)strError;
  1184. FatalError(TRANS("Cannot load Player class:\n%s"), strError);
  1185. }
  1186. if (!_pNetwork->IsPlayerLocal(penNewPlayer)) {
  1187. CPrintF(TRANS("%s joined\n"), penNewPlayer->GetPlayerName());
  1188. }
  1189. } else {
  1190. // attach entity to client data
  1191. ses_apltPlayers[iNewPlayer].AttachEntity(penNewPlayer);
  1192. // make it update its character
  1193. penNewPlayer->CharacterChanged(pcCharacter);
  1194. if (!_pNetwork->IsPlayerLocal(penNewPlayer)) {
  1195. CPrintF(TRANS("%s rejoined\n"), penNewPlayer->GetPlayerName());
  1196. }
  1197. }
  1198. } break;
  1199. // if removing a player
  1200. case MSG_SEQ_REMPLAYER: {
  1201. _pNetwork->AddNetGraphValue(NGET_NONACTION, 1.0f); // non-action sequence
  1202. INDEX iPlayer;
  1203. nmMessage>>iPlayer; // player index
  1204. // delete all predictors
  1205. _pNetwork->ga_World.DeletePredictors();
  1206. // inform entity of disconnnection
  1207. CPrintF(TRANS("%s left\n"), ses_apltPlayers[iPlayer].plt_penPlayerEntity->GetPlayerName());
  1208. ses_apltPlayers[iPlayer].plt_penPlayerEntity->Disconnect();
  1209. // deactivate the player
  1210. ses_apltPlayers[iPlayer].Deactivate();
  1211. // handle all the sent events
  1212. ses_bAllowRandom = TRUE;
  1213. CEntity::HandleSentEvents();
  1214. ses_bAllowRandom = FALSE;
  1215. } break;
  1216. // if changing character
  1217. case MSG_SEQ_CHARACTERCHANGE: {
  1218. _pNetwork->AddNetGraphValue(NGET_NONACTION, 1.0f); // non-action sequence
  1219. INDEX iPlayer;
  1220. CPlayerCharacter pcCharacter;
  1221. nmMessage>>iPlayer>>pcCharacter;
  1222. // delete all predictors
  1223. _pNetwork->ga_World.DeletePredictors();
  1224. // change the character
  1225. ses_apltPlayers[iPlayer].plt_penPlayerEntity->CharacterChanged(pcCharacter);
  1226. // handle all the sent events
  1227. ses_bAllowRandom = TRUE;
  1228. CEntity::HandleSentEvents();
  1229. ses_bAllowRandom = FALSE;
  1230. } break;
  1231. // if receiving client actions
  1232. case MSG_SEQ_ALLACTIONS: {
  1233. // read time from packet
  1234. TIME tmPacket;
  1235. nmMessage>>tmPacket; // packet time
  1236. // time must be greater by one than that on the last packet received
  1237. TIME tmTickQuantum = _pTimer->TickQuantum;
  1238. TIME tmPacketDelta = tmPacket-ses_tmLastProcessedTick;
  1239. if(! (Abs(tmPacketDelta-tmTickQuantum) < (tmTickQuantum/10.0f)) ) {
  1240. // report debug info
  1241. CPrintF(
  1242. TRANS("Session state: Mistimed MSG_ALLACTIONS: Last received tick %g, this tick %g\n"),
  1243. ses_tmLastProcessedTick, tmPacket);
  1244. }
  1245. // remember the received tick
  1246. ses_tmLastProcessedTick = tmPacket;
  1247. // NOTE: if we got a tick, it means that all players have joined
  1248. // don't wait for new players any more
  1249. ses_bWaitAllPlayers = FALSE;
  1250. // delete all predictors
  1251. _pNetwork->ga_World.DeletePredictors();
  1252. // process the tick
  1253. ProcessGameTick(nmMessage, tmPacket);
  1254. } break;
  1255. // if receiving pause message
  1256. case MSG_SEQ_PAUSE: {
  1257. _pNetwork->AddNetGraphValue(NGET_NONACTION, 1.0f); // non-action sequence
  1258. // delete all predictors
  1259. _pNetwork->ga_World.DeletePredictors();
  1260. BOOL bPauseBefore = ses_bPause;
  1261. // read the pause state and pauser from it
  1262. nmMessage>>(INDEX&)ses_bPause;
  1263. CTString strPauser;
  1264. nmMessage>>strPauser;
  1265. // if paused by some other machine
  1266. if (strPauser!=TRANS("Local machine")) {
  1267. // report who paused
  1268. if (ses_bPause!=bPauseBefore) {
  1269. if (ses_bPause) {
  1270. CPrintF(TRANS("Paused by '%s'\n"), strPauser);
  1271. } else {
  1272. CPrintF(TRANS("Unpaused by '%s'\n"), strPauser);
  1273. }
  1274. }
  1275. }
  1276. // must keep wanting current state
  1277. ses_bWantPause = ses_bPause;
  1278. } break;
  1279. // otherwise
  1280. default:
  1281. // error
  1282. ASSERT(FALSE);
  1283. }
  1284. }
  1285. // Set lerping factor for current frame.
  1286. void CSessionState::SetLerpFactor(CTimerValue tvNow)
  1287. {
  1288. // if no lerping
  1289. if (!net_bLerping) {
  1290. // set lerping factor without lerping
  1291. _pTimer->SetLerp(1.0f);
  1292. _pTimer->SetCurrentTick(ses_tmPredictionHeadTick);
  1293. return;
  1294. }
  1295. FLOAT fFactor = 0.0f;
  1296. FLOAT fFactor2 = 0.0f;
  1297. // ---- primary factor - used for prediction
  1298. {
  1299. TIME tmLastTick = ses_tmPredictionHeadTick;
  1300. // if lerping was never set before
  1301. if (ses_tmInitializationTick<0) {
  1302. // initialize it
  1303. ses_tvInitialization = tvNow;
  1304. ses_tmInitializationTick = tmLastTick;
  1305. }
  1306. // get passed time from session state starting in precise time and in ticks
  1307. FLOAT tmRealDelta = FLOAT((tvNow-ses_tvInitialization).GetSeconds())
  1308. *_pNetwork->ga_fGameRealTimeFactor*_pNetwork->ga_sesSessionState.ses_fRealTimeFactor;
  1309. FLOAT tmTickDelta = tmLastTick-ses_tmInitializationTick;
  1310. // calculate factor
  1311. fFactor = 1.0f-(tmTickDelta-tmRealDelta)/_pTimer->TickQuantum;
  1312. // if the factor starts getting below zero
  1313. if (fFactor<0) {
  1314. //CPrintF("Lerp=%.2f <0 @ %.2fs\n", fFactor, tmLastTick);
  1315. // clamp it
  1316. fFactor = 0.0f;
  1317. // readjust timers so that it gets better
  1318. ses_tvInitialization = tvNow;
  1319. ses_tmInitializationTick = tmLastTick-_pTimer->TickQuantum;
  1320. }
  1321. if (fFactor>1) {
  1322. //CPrintF("Lerp=%.2f >1 @ %.2fs\n", fFactor, tmLastTick);
  1323. // clamp it
  1324. fFactor = 1.0f;
  1325. // readjust timers so that it gets better
  1326. ses_tvInitialization = tvNow;
  1327. ses_tmInitializationTick = tmLastTick;
  1328. }
  1329. #if DEBUG_LERPING
  1330. avfStats[ctCounter][0] = tmRealDelta/_pTimer->TickQuantum;
  1331. avfStats[ctCounter][1] = tmTickDelta/_pTimer->TickQuantum;
  1332. avfStats[ctCounter][2] = fFactor;
  1333. avfStats[ctCounter][3] = (tmLastTick/_pTimer->TickQuantum-1.0f)+fFactor;
  1334. ctCounter++;
  1335. if (ctCounter>=ctMax) {
  1336. ctCounter = 0;
  1337. }
  1338. #endif // DEBUG_LERPING
  1339. }
  1340. // ---- secondary factor - used for non-predicted movement
  1341. {
  1342. TIME tmLastTick = ses_tmLastProcessedTick;
  1343. // if lerping was never set before
  1344. if (ses_tmInitializationTick2<0) {
  1345. // initialize it
  1346. ses_tvInitialization2 = tvNow;
  1347. ses_tmInitializationTick2 = tmLastTick;
  1348. }
  1349. // get passed time from session state starting in precise time and in ticks
  1350. FLOAT tmRealDelta = FLOAT((tvNow-ses_tvInitialization2).GetSeconds())
  1351. *_pNetwork->ga_fGameRealTimeFactor*_pNetwork->ga_sesSessionState.ses_fRealTimeFactor;
  1352. FLOAT tmTickDelta = tmLastTick-ses_tmInitializationTick2;
  1353. // calculate factor
  1354. fFactor2 = 1.0f-(tmTickDelta-tmRealDelta)/_pTimer->TickQuantum;
  1355. // if the factor starts getting below zero
  1356. if (fFactor2<0) {
  1357. //CPrintF("Lerp2=%.2f <0 @ %.2fs\n", fFactor2, tmLastTick);
  1358. // clamp it
  1359. fFactor2 = 0.0f;
  1360. // readjust timers so that it gets better
  1361. ses_tvInitialization2 = tvNow;
  1362. ses_tmInitializationTick2 = tmLastTick-_pTimer->TickQuantum;
  1363. }
  1364. if (fFactor2>1) {
  1365. //CPrintF("Lerp2=%.2f >1 @ %.2fs\n", fFactor2, tmLastTick);
  1366. // clamp it
  1367. fFactor2 = 1.0f;
  1368. // readjust timers so that it gets better
  1369. ses_tvInitialization2 = tvNow;
  1370. ses_tmInitializationTick2 = tmLastTick;
  1371. }
  1372. }
  1373. // set lerping factor2
  1374. _pTimer->SetLerp(fFactor);
  1375. _pTimer->SetLerp2(fFactor2);
  1376. _pTimer->SetCurrentTick(ses_tmPredictionHeadTick);
  1377. }
  1378. /*
  1379. * Read session state state from a stream.
  1380. */
  1381. void CSessionState::Read_t(CTStream *pstr) // throw char *
  1382. {
  1383. #if DEBUG_SYNCSTREAMDUMPING
  1384. CPrintF( "Session state read: Sequence %d, Time %g\n", ses_iLastProcessedSequence, ses_tmLastProcessedTick);
  1385. #endif
  1386. // read time information and random seed
  1387. INDEX iVersion = SESSIONSTATEVERSION_OLD;
  1388. if (pstr->PeekID_t()==CChunkID("SESV")) {
  1389. pstr->ExpectID_t("SESV");
  1390. (*pstr)>>iVersion;
  1391. }
  1392. (*pstr)>>ses_tmLastProcessedTick;
  1393. (*pstr)>>ses_iLastProcessedSequence;
  1394. (*pstr)>>ses_iLevel;
  1395. (*pstr)>>ses_ulRandomSeed;
  1396. (*pstr)>>ses_ulSpawnFlags;
  1397. (*pstr)>>ses_tmSyncCheckFrequency;
  1398. (*pstr)>>ses_iExtensiveSyncCheck;
  1399. (*pstr)>>ses_tmLastSyncCheck;
  1400. (*pstr)>>ses_ctMaxPlayers;
  1401. (*pstr)>>ses_bWaitAllPlayers;
  1402. (*pstr)>>ses_bPause;
  1403. (*pstr)>>ses_bGameFinished;
  1404. if (iVersion>=SESSIONSTATEVERSION_WITHBULLETTIME) {
  1405. (*pstr)>>ses_fRealTimeFactor;
  1406. }
  1407. ses_bWaitingForServer = FALSE;
  1408. ses_bWantPause = ses_bPause;
  1409. ses_strDisconnected = "";
  1410. _pTimer->SetCurrentTick(ses_tmLastProcessedTick);
  1411. // read session properties from stream
  1412. (*pstr)>>_pNetwork->ga_strSessionName;
  1413. pstr->Read_t(_pNetwork->ga_aubProperties, NET_MAXSESSIONPROPERTIES);
  1414. // read world and its state
  1415. ReadWorldAndState_t(pstr);
  1416. #if DEBUG_SYNCSTREAMDUMPING
  1417. ClearDumpStream();
  1418. // dump sync data if needed
  1419. extern INDEX cli_bDumpSyncEachTick;
  1420. if( cli_bDumpSyncEachTick)
  1421. {
  1422. DumpSyncToMemory();
  1423. }
  1424. #endif
  1425. }
  1426. void CSessionState::ReadWorldAndState_t(CTStream *pstr) // throw char *
  1427. {
  1428. // check engine build disallowing reinit
  1429. BOOL bNeedsReinit;
  1430. _pNetwork->CheckVersion_t(*pstr, FALSE, bNeedsReinit);
  1431. ASSERT(!bNeedsReinit);
  1432. // read world filename from stream
  1433. (*pstr)>>_pNetwork->ga_fnmWorld;
  1434. if (CTFileName(pstr->GetDescription()).FileExt()==".dem" &&
  1435. GetFileTimeStamp_t(pstr->GetDescription())<=GetFileTimeStamp_t(_pNetwork->ga_fnmWorld)) {
  1436. ThrowF_t(
  1437. TRANS("Cannot play demo because file '%s'\n"
  1438. "is older than file '%s'!\n"),
  1439. CTString(pstr->GetDescription()),
  1440. CTString(_pNetwork->ga_fnmWorld));
  1441. }
  1442. // prepare the world for loading
  1443. _pNetwork->ga_World.DeletePredictors();
  1444. _pNetwork->ga_World.Clear();
  1445. _pNetwork->ga_World.LockAll();
  1446. // load the world brushes from the world file
  1447. _pNetwork->ga_World.LoadBrushes_t(_pNetwork->ga_fnmWorld);
  1448. // read world situation
  1449. _pNetwork->ga_World.ReadState_t(pstr);
  1450. // create an empty list for relinking timers
  1451. CListHead lhNewTimers;
  1452. // read number of entities in timer list
  1453. pstr->ExpectID_t("TMRS"); // timers
  1454. INDEX ctTimers;
  1455. *pstr>>ctTimers;
  1456. // ASSERT(ctTimers == _pNetwork->ga_World.wo_lhTimers.Count());
  1457. // for each entity in the timer list
  1458. {for(INDEX ienTimer=0; ienTimer<ctTimers; ienTimer++) {
  1459. // read its index in container of all entities
  1460. INDEX ien;
  1461. *pstr>>ien;
  1462. // get the entity
  1463. CRationalEntity *pen = (CRationalEntity*)_pNetwork->ga_World.EntityFromID(ien);
  1464. // remove it from the timer list and add it at the end of the new timer list
  1465. if (pen->en_lnInTimers.IsLinked()) {
  1466. pen->en_lnInTimers.Remove();
  1467. lhNewTimers.AddTail(pen->en_lnInTimers);
  1468. }
  1469. }}
  1470. // use the new timer list instead the old one
  1471. ASSERT(_pNetwork->ga_World.wo_lhTimers.IsEmpty());
  1472. _pNetwork->ga_World.wo_lhTimers.MoveList(lhNewTimers);
  1473. // create an empty list for relinking movers
  1474. CListHead lhNewMovers;
  1475. // read number of entities in mover list
  1476. pstr->ExpectID_t("MVRS"); // movers
  1477. INDEX ctMovers;
  1478. *pstr>>ctMovers;
  1479. ASSERT(ctMovers == _pNetwork->ga_World.wo_lhMovers.Count());
  1480. // for each entity in the mover list
  1481. {for(INDEX ienMover=0; ienMover<ctMovers; ienMover++) {
  1482. // read its index in container of all entities
  1483. INDEX ien;
  1484. *pstr>>ien;
  1485. // get the entity
  1486. CMovableEntity *pen = (CMovableEntity*)_pNetwork->ga_World.EntityFromID(ien);
  1487. // remove it from the mover list and add it at the end of the new mover list
  1488. if (pen->en_lnInMovers.IsLinked()) {
  1489. pen->en_lnInMovers.Remove();
  1490. }
  1491. lhNewMovers.AddTail(pen->en_lnInMovers);
  1492. }}
  1493. // use the new mover list instead the old one
  1494. ASSERT(_pNetwork->ga_World.wo_lhMovers.IsEmpty());
  1495. _pNetwork->ga_World.wo_lhMovers.MoveList(lhNewMovers);
  1496. // read number of players
  1497. INDEX ctPlayers;
  1498. (*pstr)>>ctPlayers;
  1499. ASSERT(ctPlayers==ses_apltPlayers.Count());
  1500. // for all clients
  1501. FOREACHINSTATICARRAY(ses_apltPlayers, CPlayerTarget, itclt) {
  1502. // read from stream
  1503. itclt->Read_t(pstr);
  1504. }
  1505. _pNetwork->ga_World.UnlockAll();
  1506. }
  1507. void CSessionState::ReadRememberedLevels_t(CTStream *pstr)
  1508. {
  1509. pstr->ExpectID_t("RLEV"); // remembered levels
  1510. // read count of remembered levels
  1511. INDEX ctLevels;
  1512. (*pstr)>>ctLevels;
  1513. // for each level
  1514. for(INDEX iLevel=0; iLevel<ctLevels; iLevel++) {
  1515. // create it
  1516. CRememberedLevel *prl = new CRememberedLevel;
  1517. // read it
  1518. (*pstr)>>prl->rl_strFileName;
  1519. //prl->rl_strmSessionState.
  1520. // use readstream() !!!! @@@@
  1521. }
  1522. };
  1523. /*
  1524. * Write session state state into a stream.
  1525. */
  1526. void CSessionState::Write_t(CTStream *pstr) // throw char *
  1527. {
  1528. #if DEBUG_SYNCSTREAMDUMPING
  1529. CPrintF( "Session state write: Sequence %d, Time %.2f\n", ses_iLastProcessedSequence, ses_tmLastProcessedTick);
  1530. #endif
  1531. pstr->WriteID_t("SESV");
  1532. (*pstr)<<INDEX(SESSIONSTATEVERSION_WITHBULLETTIME);
  1533. // write time information and random seed
  1534. (*pstr)<<ses_tmLastProcessedTick;
  1535. (*pstr)<<ses_iLastProcessedSequence;
  1536. (*pstr)<<ses_iLevel;
  1537. (*pstr)<<ses_ulRandomSeed;
  1538. (*pstr)<<ses_ulSpawnFlags;
  1539. (*pstr)<<ses_tmSyncCheckFrequency;
  1540. (*pstr)<<ses_iExtensiveSyncCheck;
  1541. (*pstr)<<ses_tmLastSyncCheck;
  1542. (*pstr)<<ses_ctMaxPlayers;
  1543. (*pstr)<<ses_bWaitAllPlayers;
  1544. (*pstr)<<ses_bPause;
  1545. (*pstr)<<ses_bGameFinished;
  1546. (*pstr)<<ses_fRealTimeFactor;
  1547. // write session properties to stream
  1548. (*pstr)<<_pNetwork->ga_strSessionName;
  1549. pstr->Write_t(_pNetwork->ga_aubProperties, NET_MAXSESSIONPROPERTIES);
  1550. // write world and its state
  1551. WriteWorldAndState_t(pstr);
  1552. }
  1553. void CSessionState::WriteWorldAndState_t(CTStream *pstr) // throw char *
  1554. {
  1555. // delete all predictor entities before saving
  1556. _pNetwork->ga_World.UnmarkForPrediction();
  1557. _pNetwork->ga_World.DeletePredictors();
  1558. // save engine build
  1559. _pNetwork->WriteVersion_t(*pstr);
  1560. // write world filename to stream
  1561. (*pstr)<<_pNetwork->ga_fnmWorld;
  1562. // write world situation
  1563. _pNetwork->ga_World.LockAll();
  1564. _pNetwork->ga_World.WriteState_t(pstr, TRUE);
  1565. // write number of entities in timer list
  1566. pstr->WriteID_t("TMRS"); // timers
  1567. CListHead &lhTimers = _pNetwork->ga_World.wo_lhTimers;
  1568. *pstr<<lhTimers.Count();
  1569. // for each entity in the timer list
  1570. {FOREACHINLIST(CRationalEntity, en_lnInTimers, lhTimers, iten) {
  1571. // save its index in container
  1572. *pstr<<iten->en_ulID;
  1573. }}
  1574. // write number of entities in mover list
  1575. pstr->WriteID_t("MVRS"); // movers
  1576. CListHead &lhMovers = _pNetwork->ga_World.wo_lhMovers;
  1577. *pstr<<lhMovers.Count();
  1578. // for each entity in the mover list
  1579. {FOREACHINLIST(CMovableEntity, en_lnInMovers, lhMovers, iten) {
  1580. // save its index in container
  1581. *pstr<<iten->en_ulID;
  1582. }}
  1583. // write number of clients
  1584. (*pstr)<<ses_apltPlayers.Count();
  1585. // for all clients
  1586. FOREACHINSTATICARRAY(ses_apltPlayers, CPlayerTarget, itclt) {
  1587. // write to stream
  1588. itclt->Write_t(pstr);
  1589. }
  1590. _pNetwork->ga_World.UnlockAll();
  1591. }
  1592. void CSessionState::WriteRememberedLevels_t(CTStream *pstr)
  1593. {
  1594. // use writestream() !!!! @@@@
  1595. };
  1596. // remember current level
  1597. void CSessionState::RememberCurrentLevel(const CTString &strFileName)
  1598. {
  1599. // if level is already remembered
  1600. for(;;) {
  1601. CRememberedLevel *prlOld = FindRememberedLevel(strFileName);
  1602. if (prlOld==NULL) {
  1603. break;
  1604. }
  1605. // remove it
  1606. prlOld->rl_lnInSessionState.Remove();
  1607. delete prlOld;
  1608. }
  1609. // create new remembered level
  1610. CRememberedLevel *prlNew = new CRememberedLevel;
  1611. ses_lhRememberedLevels.AddTail(prlNew->rl_lnInSessionState);
  1612. // remember it
  1613. prlNew->rl_strFileName = strFileName;
  1614. WriteWorldAndState_t(&prlNew->rl_strmSessionState);
  1615. }
  1616. // find a level if it is remembered
  1617. CRememberedLevel *CSessionState::FindRememberedLevel(const CTString &strFileName)
  1618. {
  1619. {FOREACHINLIST(CRememberedLevel, rl_lnInSessionState, ses_lhRememberedLevels, itrl) {
  1620. CRememberedLevel &rl = *itrl;
  1621. if (rl.rl_strFileName==strFileName) {
  1622. return &rl;
  1623. }
  1624. }}
  1625. return NULL;
  1626. }
  1627. // restore some old level
  1628. void CSessionState::RestoreOldLevel(const CTString &strFileName)
  1629. {
  1630. // find the level
  1631. CRememberedLevel *prlOld = FindRememberedLevel(strFileName);
  1632. // it must exist
  1633. ASSERT(prlOld!=NULL);
  1634. // restore it
  1635. try {
  1636. prlOld->rl_strmSessionState.SetPos_t(0);
  1637. _pTimer->SetCurrentTick(0.0f);
  1638. ReadWorldAndState_t(&prlOld->rl_strmSessionState);
  1639. _pTimer->SetCurrentTick(ses_tmLastProcessedTick);
  1640. } catch (char *strError) {
  1641. FatalError(TRANS("Cannot restore old level '%s':\n%s"), prlOld->rl_strFileName, strError);
  1642. }
  1643. // delete it
  1644. delete prlOld;
  1645. }
  1646. // make synchronization test message and send it to server (if client), or add to buffer (if server)
  1647. void CSessionState::MakeSynchronisationCheck(void)
  1648. {
  1649. if (!_cmiComm.cci_bClientInitialized) return;
  1650. // not yet time
  1651. if(ses_tmLastSyncCheck+ses_tmSyncCheckFrequency > ses_tmLastProcessedTick) {
  1652. // don't check yet
  1653. return;
  1654. }
  1655. // make local checksum
  1656. ULONG ulLocalCRC;
  1657. CRC_Start(ulLocalCRC);
  1658. ChecksumForSync(ulLocalCRC, ses_iExtensiveSyncCheck);
  1659. CRC_Finish(ulLocalCRC);
  1660. // create sync-check
  1661. CSyncCheck sc;
  1662. ses_tmLastSyncCheck = ses_tmLastProcessedTick;
  1663. sc.sc_tmTick = ses_tmLastSyncCheck;
  1664. sc.sc_iSequence = ses_iLastProcessedSequence;
  1665. sc.sc_ulCRC = ulLocalCRC;
  1666. sc.sc_iLevel = ses_iLevel;
  1667. // NOTE: If local client, here we buffer the sync and send it to ourselves.
  1668. // It's because synccheck is piggybacking session message buffer, so we acknowledge
  1669. // the server to flush the buffer up to that sequence.
  1670. // if on server
  1671. if (_pNetwork->IsServer()) {
  1672. // buffer the sync-check
  1673. _pNetwork->ga_srvServer.AddSyncCheck(sc);
  1674. }
  1675. // create a message with sync check
  1676. CNetworkMessage nmSyncCheck(MSG_SYNCCHECK);
  1677. nmSyncCheck.Write(&sc, sizeof(sc));
  1678. // send it to server
  1679. _pNetwork->SendToServer(nmSyncCheck);
  1680. }
  1681. // forget all remembered levels
  1682. void CSessionState::ForgetOldLevels(void)
  1683. {
  1684. {FORDELETELIST(CRememberedLevel, rl_lnInSessionState, ses_lhRememberedLevels, itrl) {
  1685. delete &*itrl;
  1686. }}
  1687. }
  1688. extern INDEX cli_bDumpSync;
  1689. extern INDEX cli_bDumpSyncEachTick;
  1690. void CSessionState::DumpSyncToFile_t(CTStream &strm, INDEX iExtensiveSyncCheck) // throw char *
  1691. {
  1692. ULONG ulLocalCRC;
  1693. CRC_Start(ulLocalCRC);
  1694. ChecksumForSync(ulLocalCRC, iExtensiveSyncCheck);
  1695. CRC_Finish(ulLocalCRC);
  1696. strm.FPrintF_t("__________________________________________________________________________________\n", ulLocalCRC);
  1697. strm.FPrintF_t("CRC: 0x%08X\n", ulLocalCRC);
  1698. DumpSync_t(strm, iExtensiveSyncCheck);
  1699. }
  1700. #if DEBUG_SYNCSTREAMDUMPING
  1701. void CSessionState::DumpSyncToMemory(void)
  1702. {
  1703. try
  1704. {
  1705. CTMemoryStream *pstrm = GetDumpStream();
  1706. DumpSyncToFile_t(*pstrm);
  1707. }
  1708. catch (char *strError)
  1709. {
  1710. CPrintF("Cannot dump sync data: %s\n", strError);
  1711. }
  1712. }
  1713. #endif
  1714. /* Session state loop. */
  1715. void CSessionState::SessionStateLoop(void)
  1716. {
  1717. _pfNetworkProfile.StartTimer(CNetworkProfile::PTI_SESSIONSTATE_LOOP);
  1718. // while there is something to do
  1719. BOOL bSomethingToDo = TRUE;
  1720. while (bSomethingToDo && !IsDisconnected()) {
  1721. bSomethingToDo = FALSE;
  1722. // if client was disconnected without a notice
  1723. if (!_cmiComm.Client_IsConnected()) {
  1724. // quit
  1725. ses_strDisconnected = TRANS("Link or server is down");
  1726. }
  1727. CNetworkMessage nmMessage;
  1728. // if there is some unreliable message
  1729. if (_pNetwork->ReceiveFromServer(nmMessage)) {
  1730. bSomethingToDo = TRUE;
  1731. // if it is a gamestream message
  1732. if (nmMessage.GetType() == MSG_GAMESTREAMBLOCKS) {
  1733. ses_tvMessageReceived = _pTimer->GetHighPrecisionTimer();
  1734. ses_bWaitingForServer = FALSE;
  1735. // unpack the message
  1736. CNetworkMessage nmUnpackedBlocks(MSG_GAMESTREAMBLOCKS);
  1737. nmMessage.UnpackDefault(nmUnpackedBlocks);
  1738. // while there are some more blocks in the message
  1739. while (!nmUnpackedBlocks.EndOfMessage()) {
  1740. // read a block to the gamestream
  1741. ses_nsGameStream.ReadBlock(nmUnpackedBlocks);
  1742. }
  1743. // if it is a keepalive
  1744. } else if (nmMessage.GetType() == MSG_KEEPALIVE) {
  1745. // just remember time
  1746. ses_tvMessageReceived = _pTimer->GetHighPrecisionTimer();
  1747. _pNetwork->AddNetGraphValue(NGET_NONACTION, 0.5f); // non-action sequence
  1748. // if it is pings message
  1749. } else if (nmMessage.GetType() == MSG_INF_PINGS) {
  1750. for(INDEX i=0; i<NET_MAXGAMEPLAYERS; i++) {
  1751. CPlayerTarget &plt = ses_apltPlayers[i];
  1752. BOOL bHas = 0;
  1753. nmMessage.ReadBits(&bHas, 1);
  1754. if (bHas) {
  1755. if (plt.IsActive() && plt.plt_penPlayerEntity!=NULL) {
  1756. INDEX iPing = 0;
  1757. nmMessage.ReadBits(&iPing, 10);
  1758. plt.plt_penPlayerEntity->en_tmPing = iPing/1000.0f;
  1759. }
  1760. }
  1761. }
  1762. // if it is chat message
  1763. } else if (nmMessage.GetType() == MSG_CHAT_OUT) {
  1764. // read the message
  1765. ULONG ulFrom;
  1766. CTString strFrom;
  1767. nmMessage>>ulFrom;
  1768. if (ulFrom==0) {
  1769. nmMessage>>strFrom;
  1770. }
  1771. CTString strMessage;
  1772. nmMessage>>strMessage;
  1773. // print it
  1774. PrintChatMessage(ulFrom, strFrom, strMessage);
  1775. // otherwise
  1776. } else {
  1777. CPrintF(TRANS("Session state: Unexpected message during game: %s(%d)\n"),
  1778. ErrorDescription(&MessageTypes, nmMessage.GetType()), nmMessage.GetType());
  1779. }
  1780. }
  1781. CNetworkMessage nmReliable;
  1782. // if there is some reliable message
  1783. if (_pNetwork->ReceiveFromServerReliable(nmReliable)) {
  1784. bSomethingToDo = TRUE;
  1785. // if this is disconnect message
  1786. if (nmReliable.GetType() == MSG_INF_DISCONNECTED) {
  1787. // confirm disconnect
  1788. CNetworkMessage nmConfirmDisconnect(MSG_REP_DISCONNECTED);
  1789. _pNetwork->SendToServerReliable(nmConfirmDisconnect);
  1790. // report the reason
  1791. CTString strReason;
  1792. nmReliable>>strReason;
  1793. ses_strDisconnected = strReason;
  1794. CPrintF(TRANS("Disconnected: %s\n"), strReason);
  1795. // disconnect
  1796. _cmiComm.Client_Close();
  1797. // if this is recon response
  1798. } else if (nmReliable.GetType() == MSG_ADMIN_RESPONSE) {
  1799. // just print it
  1800. CTString strResponse;
  1801. nmReliable>>strResponse;
  1802. CPrintF("%s", "|"+strResponse+"\n");
  1803. // otherwise
  1804. } else {
  1805. CPrintF(TRANS("Session state: Unexpected reliable message during game: %s(%d)\n"),
  1806. ErrorDescription(&MessageTypes, nmReliable.GetType()), nmReliable.GetType());
  1807. }
  1808. }
  1809. }
  1810. // if network client and not waiting for server
  1811. if (_pNetwork->IsNetworkEnabled() && !_pNetwork->IsServer() && !ses_bWaitingForServer) {
  1812. // check when last message was received.
  1813. if (ses_tvMessageReceived.tv_llValue>0 &&
  1814. (_pTimer->GetHighPrecisionTimer()-ses_tvMessageReceived).GetSeconds()>net_tmDisconnectTimeout &&
  1815. ses_strDisconnected=="") {
  1816. ses_strDisconnected = TRANS("Connection timeout");
  1817. CPrintF(TRANS("Disconnected: %s\n"), (const char*)ses_strDisconnected);
  1818. }
  1819. }
  1820. // if pause state should be changed
  1821. if (ses_bPause!=ses_bWantPause) {
  1822. // send appropriate packet to server
  1823. CNetworkMessage nmReqPause(MSG_REQ_PAUSE);
  1824. nmReqPause<<(INDEX&)ses_bWantPause;
  1825. _pNetwork->SendToServer(nmReqPause);
  1826. }
  1827. // dump sync data if needed
  1828. if( cli_bDumpSync)
  1829. {
  1830. cli_bDumpSync = FALSE;
  1831. try
  1832. {
  1833. CTFileStream strmFile;
  1834. CTString strFileName = CTString("temp\\syncdump.txt");
  1835. strmFile.Create_t(CTString("temp\\syncdump.txt"), CTStream::CM_TEXT);
  1836. #if DEBUG_SYNCSTREAMDUMPING
  1837. if( cli_bDumpSyncEachTick)
  1838. {
  1839. // get size and buffer from the stream
  1840. void *pvBuffer;
  1841. SLONG slSize;
  1842. ses_pstrm->LockBuffer(&pvBuffer, &slSize);
  1843. strmFile.Write_t(pvBuffer, slSize);
  1844. ses_pstrm->UnlockBuffer();
  1845. }
  1846. else
  1847. #endif
  1848. {
  1849. DumpSyncToFile_t(strmFile, ses_iExtensiveSyncCheck);
  1850. }
  1851. // inform user
  1852. CPrintF("Sync data dumped to '%s'\n", strFileName);
  1853. }
  1854. catch (char *strError)
  1855. {
  1856. CPrintF("Cannot dump sync data: %s\n", strError);
  1857. }
  1858. }
  1859. // if some client settings changed
  1860. if (!ses_sspParams.IsUpToDate()) {
  1861. // remember new settings
  1862. ses_sspParams.Update();
  1863. // send message to server
  1864. CNetworkMessage nmSet(MSG_SET_CLIENTSETTINGS);
  1865. nmSet<<ses_sspParams;
  1866. _pNetwork->SendToServerReliable(nmSet);
  1867. }
  1868. _pfNetworkProfile.StopTimer(CNetworkProfile::PTI_SESSIONSTATE_LOOP);
  1869. }
  1870. /* Get number of active players. */
  1871. INDEX CSessionState::GetPlayersCount(void)
  1872. {
  1873. INDEX ctPlayers = 0;
  1874. FOREACHINSTATICARRAY(ses_apltPlayers, CPlayerTarget, itplt) {
  1875. if (itplt->IsActive()) {
  1876. ctPlayers++;
  1877. }
  1878. }
  1879. return ctPlayers;
  1880. }
  1881. /* Remember predictor positions of all players. */
  1882. void CSessionState::RememberPlayerPredictorPositions(void)
  1883. {
  1884. // for each active player
  1885. FOREACHINSTATICARRAY(ses_apltPlayers, CPlayerTarget, itplt) {
  1886. CPlayerTarget &plt = *itplt;
  1887. if (plt.IsActive()) {
  1888. // remember its current, or predictor position
  1889. CEntity *pen = plt.plt_penPlayerEntity;
  1890. if (pen->IsPredicted()) {
  1891. pen = pen->GetPredictor();
  1892. }
  1893. plt.plt_vPredictorPos = pen->GetPlacement().pl_PositionVector;
  1894. }
  1895. }
  1896. }
  1897. /* Get player position. */
  1898. const FLOAT3D &CSessionState::GetPlayerPredictorPosition(INDEX iPlayer)
  1899. {
  1900. return ses_apltPlayers[iPlayer].plt_vPredictorPos;
  1901. }
  1902. CPredictedEvent::CPredictedEvent(void)
  1903. {
  1904. }
  1905. // check an event for prediction, returns true if already predicted
  1906. BOOL CSessionState::CheckEventPrediction(CEntity *pen, ULONG ulTypeID, ULONG ulEventID)
  1907. {
  1908. // if prediction is not involved
  1909. if ( !( pen->GetFlags() & (ENF_PREDICTOR|ENF_PREDICTED|ENF_WILLBEPREDICTED) ) ){
  1910. // not predicted
  1911. return FALSE;
  1912. }
  1913. // find eventual prediction tail
  1914. if (pen->IsPredictor()) {
  1915. pen = pen->GetPredictionTail();
  1916. }
  1917. // gather all event relevant data
  1918. ULONG ulEntityID = pen->en_ulID;
  1919. TIME tmNow = _pTimer->CurrentTick();
  1920. BOOL bPredicted = FALSE;
  1921. // for each active event
  1922. INDEX ctpe = ses_apeEvents.Count();
  1923. {for(INDEX ipe=0; ipe<ctpe; ipe++) {
  1924. CPredictedEvent &pe = ses_apeEvents[ipe];
  1925. // if the event is too old
  1926. if (pe.pe_tmTick<tmNow-5.0f) {
  1927. // delete it from list
  1928. pe = ses_apeEvents[ctpe-1];
  1929. ctpe--;
  1930. ipe--;
  1931. continue;
  1932. }
  1933. // if the event is same as the new one
  1934. if (pe.pe_tmTick==tmNow &&
  1935. pe.pe_ulEntityID==ulEntityID &&
  1936. pe.pe_ulTypeID==ulTypeID &&
  1937. pe.pe_ulEventID==ulEventID) {
  1938. // it was already predicted
  1939. bPredicted = TRUE;
  1940. break;
  1941. }
  1942. }}
  1943. // remove unused events at the end
  1944. if (ctpe==0) {
  1945. ses_apeEvents.PopAll();
  1946. } else {
  1947. ses_apeEvents.PopUntil(ctpe-1);
  1948. }
  1949. // if not predicted
  1950. if (!bPredicted) {
  1951. // add the new one
  1952. CPredictedEvent &pe = ses_apeEvents.Push();
  1953. pe.pe_tmTick = tmNow;
  1954. pe.pe_ulEntityID = ulEntityID;
  1955. pe.pe_ulTypeID = ulTypeID;
  1956. pe.pe_ulEventID = ulEventID;
  1957. }
  1958. return bPredicted;
  1959. }