gameConnection.cpp 73 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "T3D/gameBase/gameConnection.h"
  24. #include "platform/profiler.h"
  25. #include "core/dnet.h"
  26. #include "core/util/safeDelete.h"
  27. #include "core/stream/bitStream.h"
  28. #include "console/consoleTypes.h"
  29. #include "console/simBase.h"
  30. #include "sfx/sfxProfile.h"
  31. #include "sfx/sfxDescription.h"
  32. #include "app/game.h"
  33. #include "app/auth.h"
  34. #include "T3D/camera.h"
  35. #include "T3D/gameBase/gameProcess.h"
  36. #include "T3D/gameBase/gameConnectionEvents.h"
  37. #include "console/engineAPI.h"
  38. #include "math/mTransform.h"
  39. #ifdef TORQUE_HIFI_NET
  40. #include "T3D/gameBase/hifi/hifiMoveList.h"
  41. #elif defined TORQUE_EXTENDED_MOVE
  42. #include "T3D/gameBase/extended/extendedMoveList.h"
  43. #else
  44. #include "T3D/gameBase/std/stdMoveList.h"
  45. #endif
  46. //----------------------------------------------------------------------------
  47. #define MAX_MOVE_PACKET_SENDS 4
  48. #define ControlRequestTime 5000
  49. const U32 GameConnection::CurrentProtocolVersion = 12;
  50. const U32 GameConnection::MinRequiredProtocolVersion = 12;
  51. //----------------------------------------------------------------------------
  52. IMPLEMENT_CONOBJECT(GameConnection);
  53. S32 GameConnection::mLagThresholdMS = 0;
  54. Signal<void(F32)> GameConnection::smFovUpdate;
  55. Signal<void()> GameConnection::smPlayingDemo;
  56. ConsoleDocClass( GameConnection,
  57. "@brief The game-specific subclass of NetConnection.\n\n"
  58. "The GameConnection introduces the concept of the control object. The control object "
  59. "is simply the object that the client is associated with that network connection controls. By "
  60. "default the control object is an instance of the Player class, but can also be an instance "
  61. "of Camera (when editing the mission, for example), or any other ShapeBase derived class as "
  62. "appropriate for the game.\n\n"
  63. "Torque uses a model in which the server is the authoritative master of the simulation. To "
  64. "prevent clients from cheating, the server simulates all player moves and then tells the "
  65. "client where his player is in the world. This model, while secure, can have problems. If "
  66. "the network latency is high, this round-trip time can give the player a very noticeable sense "
  67. "of movement lag. To correct this problem, the game uses a form of prediction - it simulates "
  68. "the movement of the control object on the client and on the server both. This way the client "
  69. "doesn't need to wait for round-trip verification of his moves. Only in the case of a force "
  70. "acting on the control object on the server that doesn't exist on the client does the client's "
  71. "position need to be forcefully changed.\n\n"
  72. "To support this, all control objects (derivative of ShapeBase) must supply a writePacketData() "
  73. "and readPacketData() function that send enough data to accurately simulate the object on the "
  74. "client. These functions are only called for the current control object, and only when the "
  75. "server can determine that the client's simulation is somehow out of sync with the server. This "
  76. "occurs usually if the client is affected by a force not present on the server (like an "
  77. "interpolating object) or if the server object is affected by a server only force (such as the "
  78. "impulse from an explosion).\n\n"
  79. "The Move structure is a 32 millisecond snapshot of player input, containing x, y, and z "
  80. "positional and rotational changes as well as trigger state changes. When time passes in the "
  81. "simulation moves are collected (depending on how much time passes), and applied to the current "
  82. "control object on the client. The same moves are then packed over to the server in "
  83. "GameConnection::writePacket(), for processing on the server's version of the control object.\n\n"
  84. "@see @ref Networking, NetConnection, ShapeBase\n\n"
  85. "@ingroup Networking\n");
  86. //----------------------------------------------------------------------------
  87. IMPLEMENT_CALLBACK( GameConnection, onConnectionTimedOut, void, (), (),
  88. "@brief Called on the client when the connection to the server times out.\n\n");
  89. IMPLEMENT_CALLBACK( GameConnection, onConnectionAccepted, void, (), (),
  90. "@brief Called on the client when the connection to the server has been established.\n\n");
  91. IMPLEMENT_CALLBACK( GameConnection, onConnectRequestTimedOut, void, (), (),
  92. "@brief Called when connection attempts have timed out.\n\n");
  93. IMPLEMENT_CALLBACK( GameConnection, onConnectionDropped, void, (const char* reason), (reason),
  94. "@brief Called on the client when the connection to the server has been dropped.\n\n"
  95. "@param reason The reason why the connection was dropped.\n\n");
  96. IMPLEMENT_CALLBACK( GameConnection, onConnectRequestRejected, void, (const char* reason), (reason),
  97. "@brief Called on the client when the connection to the server has been rejected.\n\n"
  98. "@param reason The reason why the connection request was rejected.\n\n");
  99. IMPLEMENT_CALLBACK( GameConnection, onConnectionError, void, (const char* errorString), (errorString),
  100. "@brief Called on the client when there is an error with the connection to the server.\n\n"
  101. "@param errorString The connection error text.\n\n");
  102. IMPLEMENT_CALLBACK( GameConnection, onDrop, void, (const char* disconnectReason), (disconnectReason),
  103. "@brief Called on the server when the client's connection has been dropped.\n\n"
  104. "@param disconnectReason The reason why the connection was dropped.\n\n");
  105. IMPLEMENT_CALLBACK( GameConnection, initialControlSet, void, (), (),
  106. "@brief Called on the client when the first control object has been set by the "
  107. "server and we are now ready to go.\n\n"
  108. "A common action to perform when this callback is called is to switch the GUI "
  109. "canvas from the loading screen and over to the 3D game GUI.");
  110. IMPLEMENT_CALLBACK( GameConnection, onControlObjectChange, void, (), (),
  111. "@brief Called on the client when the control object has been changed by the "
  112. "server.\n\n");
  113. IMPLEMENT_CALLBACK( GameConnection, setLagIcon, void, (bool state), (state),
  114. "@brief Called on the client to display the lag icon.\n\n"
  115. "When the connection with the server is lagging, this callback is called to "
  116. "allow the game GUI to display some indicator to the player.\n\n"
  117. "@param state Set to true if the lag icon should be displayed.\n\n");
  118. IMPLEMENT_CALLBACK( GameConnection, onDataBlocksDone, void, (U32 sequence), (sequence),
  119. "@brief Called on the server when all datablocks has been sent to the client.\n\n"
  120. "During phase 1 of the mission download, all datablocks are sent from the server "
  121. "to the client. Once all datablocks have been sent, this callback is called and "
  122. "the mission download procedure may move on to the next phase.\n\n"
  123. "@param sequence The sequence is common between the server and client and ensures "
  124. "that the client is acting on the most recent mission start process. If an errant "
  125. "network packet (one that was lost but has now been found) is received by the client "
  126. "with an incorrect sequence, it is just ignored. This sequence number is updated on "
  127. "the server every time a mission is loaded.\n\n"
  128. "@see GameConnection::transmitDataBlocks()\n\n");
  129. IMPLEMENT_GLOBAL_CALLBACK( onDataBlockObjectReceived, void, (U32 index, U32 total), (index, total),
  130. "@brief Called on the client each time a datablock has been received.\n\n"
  131. "This callback is typically used to notify the player of how far along "
  132. "in the datablock download process they are.\n\n"
  133. "@param index The index of the datablock just received.\n"
  134. "@param total The total number of datablocks to be received.\n\n"
  135. "@see GameConnection, GameConnection::transmitDataBlocks(), GameConnection::onDataBlocksDone()\n\n"
  136. "@ingroup Networking\n");
  137. IMPLEMENT_CALLBACK( GameConnection, onFlash, void, (bool state), (state),
  138. "@brief Called on the client when the damage flash or white out states change.\n\n"
  139. "When the server changes the damage flash or white out values, this callback is called "
  140. "either is on or both are off. Typically this is used to enable the flash postFx.\n\n"
  141. "@param state Set to true if either the damage flash or white out conditions are active.\n\n");
  142. //----------------------------------------------------------------------------
  143. GameConnection::GameConnection()
  144. {
  145. mLagging = false;
  146. mControlObject = NULL;
  147. mCameraObject = NULL;
  148. #ifdef TORQUE_HIFI_NET
  149. mMoveList = new HifiMoveList();
  150. #elif defined TORQUE_EXTENDED_MOVE
  151. mMoveList = new ExtendedMoveList();
  152. #else
  153. mMoveList = new StdMoveList();
  154. #endif
  155. mMoveList->setConnection( this );
  156. mDataBlockModifiedKey = 0;
  157. mMaxDataBlockModifiedKey = 0;
  158. mAuthInfo = NULL;
  159. mControlForceMismatch = false;
  160. mConnectArgc = 0;
  161. for(U32 i = 0; i < MaxConnectArgs; i++)
  162. mConnectArgv[i] = 0;
  163. mJoinPassword = NULL;
  164. mMissionCRC = 0xffffffff;
  165. mDamageFlash = mWhiteOut = 0;
  166. mCameraPos = 0;
  167. mCameraSpeed = 10;
  168. mCameraFov = 90.f;
  169. mUpdateCameraFov = false;
  170. mAIControlled = false;
  171. mLastPacketTime = 0;
  172. mDisconnectReason[0] = 0;
  173. //blackout vars
  174. mBlackOut = 0.0f;
  175. mBlackOutTimeMS = 0;
  176. mBlackOutStartTimeMS = 0;
  177. mFadeToBlack = false;
  178. // first person
  179. mFirstPerson = true;
  180. mUpdateFirstPerson = false;
  181. // Control scheme
  182. mUpdateControlScheme = false;
  183. mAbsoluteRotation = false;
  184. mAddYawToAbsRot = false;
  185. mAddPitchToAbsRot = false;
  186. mVisibleGhostDistance = 0.0f;
  187. clearDisplayDevice();
  188. }
  189. GameConnection::~GameConnection()
  190. {
  191. setDisplayDevice(NULL);
  192. delete mAuthInfo;
  193. for(U32 i = 0; i < mConnectArgc; i++)
  194. dFree(mConnectArgv[i]);
  195. dFree(mJoinPassword);
  196. delete mMoveList;
  197. }
  198. //----------------------------------------------------------------------------
  199. void GameConnection::setVisibleGhostDistance(F32 dist)
  200. {
  201. mVisibleGhostDistance = dist;
  202. }
  203. F32 GameConnection::getVisibleGhostDistance()
  204. {
  205. return mVisibleGhostDistance;
  206. }
  207. bool GameConnection::canRemoteCreate()
  208. {
  209. return true;
  210. }
  211. void GameConnection::setConnectArgs(U32 argc, const char **argv)
  212. {
  213. if(argc > MaxConnectArgs)
  214. argc = MaxConnectArgs;
  215. mConnectArgc = argc;
  216. for(U32 i = 0; i < mConnectArgc; i++)
  217. mConnectArgv[i] = dStrdup(argv[i]);
  218. }
  219. void GameConnection::setJoinPassword(const char *password)
  220. {
  221. mJoinPassword = dStrdup(password);
  222. }
  223. DefineEngineMethod( GameConnection, setJoinPassword, void, (const char* password),,
  224. "@brief On the client, set the password that will be passed to the server.\n\n"
  225. "On the server, this password is compared with what is stored in $pref::Server::Password. "
  226. "If $pref::Server::Password is empty then the client's sent password is ignored. Otherwise, "
  227. "if the passed in client password and the server password do not match, the CHR_PASSWORD "
  228. "error string is sent back to the client and the connection is immediately terminated.\n\n"
  229. "This password checking is performed quite early on in the connection request process so as "
  230. "to minimize the impact of multiple failed attempts -- also known as hacking.")
  231. {
  232. object->setJoinPassword(password);
  233. }
  234. ConsoleMethod(GameConnection, setConnectArgs, void, 3, 17,
  235. "(const char* args) @brief On the client, pass along a variable set of parameters to the server.\n\n"
  236. "Once the connection is established with the server, the server calls its onConnect() method "
  237. "with the client's passed in parameters as aruments.\n\n"
  238. "@see GameConnection::onConnect()\n\n")
  239. {
  240. StringStackWrapper args(argc - 2, argv + 2);
  241. object->setConnectArgs(args.count(), args);
  242. }
  243. void GameConnection::onTimedOut()
  244. {
  245. if(isConnectionToServer())
  246. {
  247. Con::printf("Connection to server timed out");
  248. onConnectionTimedOut_callback();
  249. }
  250. else
  251. {
  252. Con::printf("Client %d timed out.", getId());
  253. setDisconnectReason("TimedOut");
  254. }
  255. }
  256. void GameConnection::onConnectionEstablished(bool isInitiator)
  257. {
  258. if(isInitiator)
  259. {
  260. setGhostFrom(false);
  261. setGhostTo(true);
  262. setSendingEvents(true);
  263. setTranslatesStrings(true);
  264. setIsConnectionToServer();
  265. mServerConnection = this;
  266. Con::printf("Connection established %d", getId());
  267. onConnectionAccepted_callback();
  268. }
  269. else
  270. {
  271. setGhostFrom(true);
  272. setGhostTo(false);
  273. setSendingEvents(true);
  274. setTranslatesStrings(true);
  275. Sim::getClientGroup()->addObject(this);
  276. mMoveList->init();
  277. const char *argv[MaxConnectArgs + 2];
  278. argv[0] = "onConnect";
  279. argv[1] = NULL; // Filled in later
  280. for(U32 i = 0; i < mConnectArgc; i++)
  281. argv[i + 2] = mConnectArgv[i];
  282. // NOTE: Need to fallback to Con::execute() as IMPLEMENT_CALLBACK does not
  283. // support variadic functions.
  284. Con::execute(this, mConnectArgc + 2, argv);
  285. }
  286. }
  287. void GameConnection::onConnectTimedOut()
  288. {
  289. onConnectRequestTimedOut_callback();
  290. }
  291. void GameConnection::onDisconnect(const char *reason)
  292. {
  293. if(isConnectionToServer())
  294. {
  295. Con::printf("Connection with server lost.");
  296. onConnectionDropped_callback(reason);
  297. mMoveList->init();
  298. }
  299. else
  300. {
  301. Con::printf("Client %d disconnected.", getId());
  302. setDisconnectReason(reason);
  303. }
  304. }
  305. void GameConnection::onConnectionRejected(const char *reason)
  306. {
  307. onConnectRequestRejected_callback(reason);
  308. }
  309. void GameConnection::handleStartupError(const char *errorString)
  310. {
  311. onConnectRequestRejected_callback(errorString);
  312. }
  313. void GameConnection::writeConnectAccept(BitStream *stream)
  314. {
  315. Parent::writeConnectAccept(stream);
  316. stream->write(getProtocolVersion());
  317. }
  318. bool GameConnection::readConnectAccept(BitStream *stream, const char **errorString)
  319. {
  320. if(!Parent::readConnectAccept(stream, errorString))
  321. return false;
  322. U32 protocolVersion;
  323. stream->read(&protocolVersion);
  324. if(protocolVersion < MinRequiredProtocolVersion || protocolVersion > CurrentProtocolVersion)
  325. {
  326. *errorString = "CHR_PROTOCOL"; // this should never happen unless someone is faking us out.
  327. return false;
  328. }
  329. return true;
  330. }
  331. void GameConnection::writeConnectRequest(BitStream *stream)
  332. {
  333. Parent::writeConnectRequest(stream);
  334. stream->writeString(GameString);
  335. stream->write(CurrentProtocolVersion);
  336. stream->write(MinRequiredProtocolVersion);
  337. stream->writeString(mJoinPassword);
  338. stream->write(mConnectArgc);
  339. for(U32 i = 0; i < mConnectArgc; i++)
  340. stream->writeString(mConnectArgv[i]);
  341. }
  342. bool GameConnection::readConnectRequest(BitStream *stream, const char **errorString)
  343. {
  344. if(!Parent::readConnectRequest(stream, errorString))
  345. return false;
  346. U32 currentProtocol, minProtocol;
  347. char gameString[256];
  348. stream->readString(gameString);
  349. if(dStrcmp(gameString, GameString))
  350. {
  351. *errorString = "CHR_GAME";
  352. return false;
  353. }
  354. stream->read(&currentProtocol);
  355. stream->read(&minProtocol);
  356. char joinPassword[256];
  357. stream->readString(joinPassword);
  358. if(currentProtocol < MinRequiredProtocolVersion)
  359. {
  360. *errorString = "CHR_PROTOCOL_LESS";
  361. return false;
  362. }
  363. if(minProtocol > CurrentProtocolVersion)
  364. {
  365. *errorString = "CHR_PROTOCOL_GREATER";
  366. return false;
  367. }
  368. setProtocolVersion(currentProtocol < CurrentProtocolVersion ? currentProtocol : CurrentProtocolVersion);
  369. const char *serverPassword = Con::getVariable("pref::Server::Password");
  370. if(serverPassword[0])
  371. {
  372. if(dStrcmp(joinPassword, serverPassword))
  373. {
  374. *errorString = "CHR_PASSWORD";
  375. return false;
  376. }
  377. }
  378. stream->read(&mConnectArgc);
  379. if(mConnectArgc > MaxConnectArgs)
  380. {
  381. *errorString = "CR_INVALID_ARGS";
  382. return false;
  383. }
  384. ConsoleValueRef connectArgv[MaxConnectArgs + 3];
  385. ConsoleValue connectArgvValue[MaxConnectArgs + 3];
  386. for(U32 i = 0; i < mConnectArgc+3; i++)
  387. {
  388. connectArgv[i].value = &connectArgvValue[i];
  389. connectArgvValue[i].init();
  390. }
  391. for(U32 i = 0; i < mConnectArgc; i++)
  392. {
  393. char argString[256];
  394. stream->readString(argString);
  395. mConnectArgv[i] = dStrdup(argString);
  396. connectArgv[i + 3] = mConnectArgv[i];
  397. }
  398. connectArgvValue[0].setStackStringValue("onConnectRequest");
  399. connectArgvValue[1].setIntValue(0);
  400. char buffer[256];
  401. Net::addressToString(getNetAddress(), buffer);
  402. connectArgvValue[2].setStackStringValue(buffer);
  403. // NOTE: Cannot convert over to IMPLEMENT_CALLBACK as it has variable args.
  404. const char *ret = Con::execute(this, mConnectArgc + 3, connectArgv);
  405. if(ret[0])
  406. {
  407. *errorString = ret;
  408. return false;
  409. }
  410. return true;
  411. }
  412. //----------------------------------------------------------------------------
  413. //----------------------------------------------------------------------------
  414. void GameConnection::connectionError(const char *errorString)
  415. {
  416. if(isConnectionToServer())
  417. {
  418. Con::printf("Connection error: %s.", errorString);
  419. onConnectionError_callback(errorString);
  420. }
  421. else
  422. {
  423. Con::printf("Client %d packet error: %s.", getId(), errorString);
  424. setDisconnectReason("Packet Error.");
  425. }
  426. deleteObject();
  427. }
  428. void GameConnection::setAuthInfo(const AuthInfo *info)
  429. {
  430. mAuthInfo = new AuthInfo;
  431. *mAuthInfo = *info;
  432. }
  433. const AuthInfo *GameConnection::getAuthInfo()
  434. {
  435. return mAuthInfo;
  436. }
  437. //----------------------------------------------------------------------------
  438. void GameConnection::setControlObject(GameBase *obj)
  439. {
  440. if(mControlObject == obj)
  441. return;
  442. if(mControlObject && mControlObject != mCameraObject)
  443. mControlObject->setControllingClient(0);
  444. if(obj)
  445. {
  446. // Nothing else is permitted to control this object.
  447. if (GameBase* coo = obj->getControllingObject())
  448. coo->setControlObject(0);
  449. if (GameConnection *con = obj->getControllingClient())
  450. {
  451. if(this != con)
  452. {
  453. // was it controlled via camera or control
  454. if(con->getControlObject() == obj)
  455. con->setControlObject(0);
  456. else
  457. con->setCameraObject(0);
  458. }
  459. }
  460. // We are now the controlling client of this object.
  461. obj->setControllingClient(this);
  462. // Update the camera's FOV to match the new control object
  463. setControlCameraFov( obj->getCameraFov() );
  464. }
  465. // Okay, set our control object.
  466. mControlObject = obj;
  467. mControlForceMismatch = true;
  468. if(mCameraObject.isNull())
  469. setScopeObject(mControlObject);
  470. }
  471. void GameConnection::setCameraObject(GameBase *obj)
  472. {
  473. if(mCameraObject == obj)
  474. return;
  475. if(mCameraObject && mCameraObject != mControlObject)
  476. mCameraObject->setControllingClient(0);
  477. if(obj)
  478. {
  479. // nothing else is permitted to control this object
  480. if(GameBase *coo = obj->getControllingObject())
  481. coo->setControlObject(0);
  482. if(GameConnection *con = obj->getControllingClient())
  483. {
  484. if(this != con)
  485. {
  486. // was it controlled via camera or control
  487. if(con->getControlObject() == obj)
  488. con->setControlObject(0);
  489. else
  490. con->setCameraObject(0);
  491. }
  492. }
  493. // we are now the controlling client of this object
  494. obj->setControllingClient(this);
  495. }
  496. // Okay, set our camera object.
  497. mCameraObject = obj;
  498. if(mCameraObject.isNull())
  499. setScopeObject(mControlObject);
  500. else
  501. {
  502. setScopeObject(mCameraObject);
  503. // if this is a client then set the fov and active image
  504. if(isConnectionToServer())
  505. {
  506. F32 fov = mCameraObject->getDefaultCameraFov();
  507. //GameSetCameraFov(fov);
  508. smFovUpdate.trigger(fov);
  509. }
  510. }
  511. }
  512. GameBase* GameConnection::getCameraObject()
  513. {
  514. // If there is no camera object, or if we're first person, return
  515. // the control object.
  516. if( !mControlObject.isNull() && (mCameraObject.isNull() || mFirstPerson))
  517. return mControlObject;
  518. return mCameraObject;
  519. }
  520. static S32 sChaseQueueSize = 0;
  521. static MatrixF* sChaseQueue = 0;
  522. static S32 sChaseQueueHead = 0;
  523. static S32 sChaseQueueTail = 0;
  524. bool GameConnection::getControlCameraTransform(F32 dt, MatrixF* mat)
  525. {
  526. GameBase* obj = getCameraObject();
  527. if(!obj)
  528. return false;
  529. GameBase* cObj = obj;
  530. while((cObj = cObj->getControlObject()) != 0)
  531. {
  532. if(cObj->useObjsEyePoint())
  533. obj = cObj;
  534. }
  535. if (dt)
  536. {
  537. if (mFirstPerson || obj->onlyFirstPerson())
  538. {
  539. if (mCameraPos > 0)
  540. if ((mCameraPos -= mCameraSpeed * dt) <= 0)
  541. mCameraPos = 0;
  542. }
  543. else
  544. {
  545. if (mCameraPos < 1)
  546. if ((mCameraPos += mCameraSpeed * dt) > 1)
  547. mCameraPos = 1;
  548. }
  549. }
  550. if (!sChaseQueueSize || mFirstPerson || obj->onlyFirstPerson())
  551. obj->getCameraTransform(&mCameraPos,mat);
  552. else
  553. {
  554. MatrixF& hm = sChaseQueue[sChaseQueueHead];
  555. MatrixF& tm = sChaseQueue[sChaseQueueTail];
  556. obj->getCameraTransform(&mCameraPos,&hm);
  557. *mat = tm;
  558. if (dt)
  559. {
  560. if ((sChaseQueueHead += 1) >= sChaseQueueSize)
  561. sChaseQueueHead = 0;
  562. if (sChaseQueueHead == sChaseQueueTail)
  563. if ((sChaseQueueTail += 1) >= sChaseQueueSize)
  564. sChaseQueueTail = 0;
  565. }
  566. }
  567. return true;
  568. }
  569. bool GameConnection::getControlCameraEyeTransforms(IDisplayDevice *display, MatrixF *transforms)
  570. {
  571. GameBase* obj = getCameraObject();
  572. if(!obj)
  573. return false;
  574. GameBase* cObj = obj;
  575. while((cObj = cObj->getControlObject()) != 0)
  576. {
  577. if(cObj->useObjsEyePoint())
  578. obj = cObj;
  579. }
  580. // Perform operation on left & right eyes. For each we need to calculate the world space
  581. // of the rotated eye offset and add that onto the camera world space.
  582. for (U32 i=0; i<2; i++)
  583. {
  584. obj->getEyeCameraTransform(display, i, &transforms[i]);
  585. }
  586. return true;
  587. }
  588. bool GameConnection::getControlCameraDefaultFov(F32 * fov)
  589. {
  590. //find the last control object in the chain (client->player->turret->whatever...)
  591. GameBase *obj = getCameraObject();
  592. GameBase *cObj = NULL;
  593. while (obj)
  594. {
  595. cObj = obj;
  596. obj = obj->getControlObject();
  597. }
  598. if (cObj)
  599. {
  600. *fov = cObj->getDefaultCameraFov();
  601. return(true);
  602. }
  603. return(false);
  604. }
  605. bool GameConnection::getControlCameraFov(F32 * fov)
  606. {
  607. //find the last control object in the chain (client->player->turret->whatever...)
  608. GameBase *obj = getCameraObject();
  609. GameBase *cObj = NULL;
  610. while (obj)
  611. {
  612. cObj = obj;
  613. obj = obj->getControlObject();
  614. }
  615. if (cObj)
  616. {
  617. *fov = cObj->getCameraFov();
  618. return(true);
  619. }
  620. return(false);
  621. }
  622. bool GameConnection::isValidControlCameraFov(F32 fov)
  623. {
  624. //find the last control object in the chain (client->player->turret->whatever...)
  625. GameBase *obj = getCameraObject();
  626. GameBase *cObj = NULL;
  627. while (obj)
  628. {
  629. cObj = obj;
  630. obj = obj->getControlObject();
  631. }
  632. return cObj ? cObj->isValidCameraFov(fov) : NULL;
  633. }
  634. bool GameConnection::setControlCameraFov(F32 fov)
  635. {
  636. //find the last control object in the chain (client->player->turret->whatever...)
  637. GameBase *obj = getCameraObject();
  638. GameBase *cObj = NULL;
  639. while (obj)
  640. {
  641. cObj = obj;
  642. obj = obj->getControlObject();
  643. }
  644. if (cObj)
  645. {
  646. // allow shapebase to clamp fov to its datablock values
  647. cObj->setCameraFov(mClampF(fov, MinCameraFov, MaxCameraFov));
  648. F32 newFov = cObj->getCameraFov();
  649. // server fov of client has 1degree resolution
  650. if( S32(newFov) != S32(mCameraFov) || newFov != fov )
  651. mUpdateCameraFov = true;
  652. mCameraFov = newFov;
  653. return(true);
  654. }
  655. return(false);
  656. }
  657. bool GameConnection::getControlCameraVelocity(Point3F *vel)
  658. {
  659. if (GameBase* obj = getCameraObject()) {
  660. *vel = obj->getVelocity();
  661. return true;
  662. }
  663. return false;
  664. }
  665. bool GameConnection::isControlObjectRotDampedCamera()
  666. {
  667. if (Camera* cam = dynamic_cast<Camera*>(getCameraObject())) {
  668. if(cam->isRotationDamped())
  669. return true;
  670. }
  671. return false;
  672. }
  673. void GameConnection::setFirstPerson(bool firstPerson)
  674. {
  675. mFirstPerson = firstPerson;
  676. mUpdateFirstPerson = true;
  677. }
  678. //----------------------------------------------------------------------------
  679. void GameConnection::setControlSchemeParameters(bool absoluteRotation, bool addYawToAbsRot, bool addPitchToAbsRot)
  680. {
  681. mAbsoluteRotation = absoluteRotation;
  682. mAddYawToAbsRot = addYawToAbsRot;
  683. mAddPitchToAbsRot = addPitchToAbsRot;
  684. mUpdateControlScheme = true;
  685. }
  686. //----------------------------------------------------------------------------
  687. bool GameConnection::onAdd()
  688. {
  689. if (!Parent::onAdd())
  690. return false;
  691. return true;
  692. }
  693. void GameConnection::onRemove()
  694. {
  695. if(isNetworkConnection())
  696. {
  697. sendDisconnectPacket(mDisconnectReason);
  698. }
  699. else if (isLocalConnection() && isConnectionToServer())
  700. {
  701. // We're a client-side but local connection
  702. // delete the server side of the connection on our local server so that it updates
  703. // clientgroup and what not (this is so that we can disconnect from a local server
  704. // without needing to destroy and recreate the server before we can connect to it
  705. // again).
  706. // Safe-delete as we don't know whether the server connection is currently being
  707. // worked on.
  708. getRemoteConnection()->safeDeleteObject();
  709. setRemoteConnectionObject(NULL);
  710. }
  711. if(!isConnectionToServer())
  712. {
  713. onDrop_callback(mDisconnectReason);
  714. }
  715. if (mControlObject)
  716. mControlObject->setControllingClient(0);
  717. Parent::onRemove();
  718. }
  719. void GameConnection::setDisconnectReason(const char *str)
  720. {
  721. dStrncpy(mDisconnectReason, str, sizeof(mDisconnectReason) - 1);
  722. mDisconnectReason[sizeof(mDisconnectReason) - 1] = 0;
  723. }
  724. //----------------------------------------------------------------------------
  725. void GameConnection::handleRecordedBlock(U32 type, U32 size, void *data)
  726. {
  727. switch(type)
  728. {
  729. case BlockTypeMove:
  730. mMoveList->pushMove(*((Move *) data));
  731. if(isRecording()) // put it back into the stream
  732. recordBlock(type, size, data);
  733. break;
  734. default:
  735. Parent::handleRecordedBlock(type, size, data);
  736. break;
  737. }
  738. }
  739. void GameConnection::writeDemoStartBlock(ResizeBitStream *stream)
  740. {
  741. // write all the data blocks to the stream:
  742. for(SimObjectId i = DataBlockObjectIdFirst; i <= DataBlockObjectIdLast; i++)
  743. {
  744. SimDataBlock *data;
  745. if(Sim::findObject(i, data))
  746. {
  747. stream->writeFlag(true);
  748. SimDataBlockEvent evt(data);
  749. evt.pack(this, stream);
  750. stream->validate();
  751. }
  752. }
  753. stream->writeFlag(false);
  754. stream->write(mFirstPerson);
  755. stream->write(mCameraPos);
  756. stream->write(mCameraSpeed);
  757. // Control scheme
  758. stream->write(mAbsoluteRotation);
  759. stream->write(mAddYawToAbsRot);
  760. stream->write(mAddPitchToAbsRot);
  761. stream->writeString(Con::getVariable("$Client::MissionFile"));
  762. mMoveList->writeDemoStartBlock(stream);
  763. // dump all the "demo" vars associated with this connection:
  764. SimFieldDictionaryIterator itr(getFieldDictionary());
  765. SimFieldDictionary::Entry *entry;
  766. while((entry = *itr) != NULL)
  767. {
  768. if(!dStrnicmp(entry->slotName, "demo", 4))
  769. {
  770. stream->writeFlag(true);
  771. stream->writeString(entry->slotName + 4);
  772. stream->writeString(entry->value);
  773. stream->validate();
  774. }
  775. ++itr;
  776. }
  777. stream->writeFlag(false);
  778. Parent::writeDemoStartBlock(stream);
  779. stream->validate();
  780. // dump out the control object ghost id
  781. S32 idx = mControlObject ? getGhostIndex(mControlObject) : -1;
  782. stream->write(idx);
  783. if(mControlObject)
  784. {
  785. #ifdef TORQUE_NET_STATS
  786. U32 beginPos = stream->getBitPosition();
  787. #endif
  788. mControlObject->writePacketData(this, stream);
  789. #ifdef TORQUE_NET_STATS
  790. // TYPEOF( mControlObject )->getNetInfo().updateNetStatWriteData( stream->getBitPosition() - beginPos );
  791. mControlObject->getClassRep()->updateNetStatWriteData( stream->getBitPosition() - beginPos);
  792. #endif
  793. }
  794. idx = mCameraObject ? getGhostIndex(mCameraObject) : -1;
  795. stream->write(idx);
  796. if(mCameraObject && mCameraObject != mControlObject)
  797. {
  798. #ifdef TORQUE_NET_STATS
  799. U32 beginPos = stream->getBitPosition();
  800. #endif
  801. mCameraObject->writePacketData(this, stream);
  802. #ifdef TORQUE_NET_STATS
  803. mCameraObject->getClassRep()->updateNetStatWriteData( stream->getBitPosition() - beginPos);
  804. #endif
  805. }
  806. mLastControlRequestTime = Platform::getVirtualMilliseconds();
  807. }
  808. bool GameConnection::readDemoStartBlock(BitStream *stream)
  809. {
  810. while(stream->readFlag())
  811. {
  812. SimDataBlockEvent evt;
  813. evt.unpack(this, stream);
  814. evt.process(this);
  815. }
  816. while(mDataBlockLoadList.size())
  817. {
  818. preloadNextDataBlock(false);
  819. if(mErrorBuffer.isNotEmpty())
  820. return false;
  821. }
  822. stream->read(&mFirstPerson);
  823. stream->read(&mCameraPos);
  824. stream->read(&mCameraSpeed);
  825. // Control scheme
  826. stream->read(&mAbsoluteRotation);
  827. stream->read(&mAddYawToAbsRot);
  828. stream->read(&mAddPitchToAbsRot);
  829. char buf[256];
  830. stream->readString(buf);
  831. Con::setVariable("$Client::MissionFile",buf);
  832. mMoveList->readDemoStartBlock(stream);
  833. // read in all the demo vars associated with this recording
  834. // they are all tagged on to the object and start with the
  835. // string "demo"
  836. while(stream->readFlag())
  837. {
  838. StringTableEntry slotName = StringTable->insert("demo");
  839. char array[256];
  840. char value[256];
  841. stream->readString(array);
  842. stream->readString(value);
  843. setDataField(slotName, array, value);
  844. }
  845. bool ret = Parent::readDemoStartBlock(stream);
  846. // grab the control object
  847. S32 idx;
  848. stream->read(&idx);
  849. GameBase * obj = 0;
  850. if(idx != -1)
  851. {
  852. obj = dynamic_cast<GameBase*>(resolveGhost(idx));
  853. setControlObject(obj);
  854. obj->readPacketData(this, stream);
  855. }
  856. // Get the camera object, and read it in if it's different
  857. S32 idx2;
  858. stream->read(&idx2);
  859. obj = 0;
  860. if(idx2 != -1 && idx2 != idx)
  861. {
  862. obj = dynamic_cast<GameBase*>(resolveGhost(idx2));
  863. setCameraObject(obj);
  864. obj->readPacketData(this, stream);
  865. }
  866. return ret;
  867. }
  868. void GameConnection::demoPlaybackComplete()
  869. {
  870. static const char* demoPlaybackArgv[1] = { "demoPlaybackComplete" };
  871. static StringStackConsoleWrapper demoPlaybackCmd(1, demoPlaybackArgv);
  872. Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(demoPlaybackCmd.argc, demoPlaybackCmd.argv, false));
  873. Parent::demoPlaybackComplete();
  874. }
  875. void GameConnection::ghostPreRead(NetObject * nobj, bool newGhost)
  876. {
  877. Parent::ghostPreRead( nobj, newGhost );
  878. mMoveList->ghostPreRead(nobj,newGhost);
  879. }
  880. void GameConnection::ghostReadExtra(NetObject * nobj, BitStream * bstream, bool newGhost)
  881. {
  882. Parent::ghostReadExtra( nobj, bstream, newGhost );
  883. mMoveList->ghostReadExtra(nobj, bstream, newGhost);
  884. }
  885. void GameConnection::ghostWriteExtra(NetObject * nobj, BitStream * bstream)
  886. {
  887. Parent::ghostWriteExtra( nobj, bstream);
  888. mMoveList->ghostWriteExtra(nobj, bstream);
  889. }
  890. //----------------------------------------------------------------------------
  891. void GameConnection::readPacket(BitStream *bstream)
  892. {
  893. bstream->clearStringBuffer();
  894. bstream->clearCompressionPoint();
  895. if (isConnectionToServer())
  896. {
  897. mMoveList->clientReadMovePacket(bstream);
  898. bool hadFlash = mDamageFlash > 0 || mWhiteOut > 0;
  899. mDamageFlash = 0;
  900. mWhiteOut = 0;
  901. if(bstream->readFlag())
  902. {
  903. if(bstream->readFlag())
  904. mDamageFlash = bstream->readFloat(7);
  905. if(bstream->readFlag())
  906. mWhiteOut = bstream->readFloat(7) * 1.5;
  907. if(!hadFlash)
  908. {
  909. // Started a damage flash or white out
  910. onFlash_callback(true);
  911. }
  912. else
  913. {
  914. if(!(mDamageFlash > 0 || mWhiteOut > 0))
  915. {
  916. // Received a zero damage flash and white out.
  917. onFlash_callback(false);
  918. }
  919. }
  920. }
  921. else if(hadFlash)
  922. {
  923. // Catch those cases where both the damage flash and white out are at zero
  924. // on the server but we did not receive an exact zero packet due to
  925. // precision over the network issues. No problem as the flag we just
  926. // read (which is false if we're here) has also told us about the change.
  927. onFlash_callback(false);
  928. }
  929. if ( bstream->readFlag() ) // gIndex != -1
  930. {
  931. if ( bstream->readFlag() ) // mMoveList->isMismatch() || mControlForceMismatch
  932. {
  933. // the control object is dirty...so we get an update:
  934. bool callScript = false;
  935. bool controlObjChange = false;
  936. if(mControlObject.isNull())
  937. callScript = true;
  938. S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize);
  939. GameBase* obj = dynamic_cast<GameBase*>(resolveGhost(gIndex));
  940. if (mControlObject != obj)
  941. {
  942. setControlObject(obj);
  943. controlObjChange = true;
  944. }
  945. #ifdef TORQUE_NET_STATS
  946. U32 beginSize = bstream->getBitPosition();
  947. #endif
  948. obj->readPacketData(this, bstream);
  949. #ifdef TORQUE_NET_STATS
  950. obj->getClassRep()->updateNetStatReadData(bstream->getBitPosition() - beginSize);
  951. #endif
  952. // let move list know that control object is dirty
  953. mMoveList->markControlDirty();
  954. if(callScript)
  955. {
  956. initialControlSet_callback();
  957. }
  958. if(controlObjChange)
  959. {
  960. onControlObjectChange_callback();
  961. }
  962. }
  963. else
  964. {
  965. // read out the compression point
  966. Point3F pos;
  967. bstream->read(&pos.x);
  968. bstream->read(&pos.y);
  969. bstream->read(&pos.z);
  970. bstream->setCompressionPoint(pos);
  971. }
  972. }
  973. if (bstream->readFlag())
  974. {
  975. S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize);
  976. GameBase* obj = dynamic_cast<GameBase*>(resolveGhost(gIndex));
  977. setCameraObject(obj);
  978. obj->readPacketData(this, bstream);
  979. }
  980. else
  981. setCameraObject(0);
  982. // server changed control scheme
  983. if(bstream->readFlag())
  984. {
  985. bool absoluteRotation = bstream->readFlag();
  986. bool addYawToAbsRot = bstream->readFlag();
  987. bool addPitchToAbsRot = bstream->readFlag();
  988. setControlSchemeParameters(absoluteRotation, addYawToAbsRot, addPitchToAbsRot);
  989. mUpdateControlScheme = false;
  990. }
  991. // server changed first person
  992. if(bstream->readFlag())
  993. {
  994. setFirstPerson(bstream->readFlag());
  995. mUpdateFirstPerson = false;
  996. }
  997. // server forcing a fov change?
  998. if(bstream->readFlag())
  999. {
  1000. S32 fov = bstream->readInt(8);
  1001. setControlCameraFov((F32)fov);
  1002. // don't bother telling the server if we were able to set the fov
  1003. F32 setFov;
  1004. if(getControlCameraFov(&setFov) && (S32(setFov) == fov))
  1005. mUpdateCameraFov = false;
  1006. // update the games fov info
  1007. smFovUpdate.trigger((F32)fov);
  1008. }
  1009. }
  1010. else
  1011. {
  1012. mMoveList->serverReadMovePacket(bstream);
  1013. mCameraPos = bstream->readFlag() ? 1.0f : 0.0f;
  1014. if (bstream->readFlag())
  1015. mControlForceMismatch = true;
  1016. // client changed control scheme
  1017. if(bstream->readFlag())
  1018. {
  1019. bool absoluteRotation = bstream->readFlag();
  1020. bool addYawToAbsRot = bstream->readFlag();
  1021. bool addPitchToAbsRot = bstream->readFlag();
  1022. setControlSchemeParameters(absoluteRotation, addYawToAbsRot, addPitchToAbsRot);
  1023. mUpdateControlScheme = false;
  1024. }
  1025. // client changed first person
  1026. if(bstream->readFlag())
  1027. {
  1028. setFirstPerson(bstream->readFlag());
  1029. mUpdateFirstPerson = false;
  1030. }
  1031. // check fov change.. 1degree granularity on server
  1032. if(bstream->readFlag())
  1033. {
  1034. S32 fov = mClamp(bstream->readInt(8), S32(MinCameraFov), S32(MaxCameraFov));
  1035. setControlCameraFov((F32)fov);
  1036. // may need to force client back to a valid fov
  1037. F32 setFov;
  1038. if(getControlCameraFov(&setFov) && (S32(setFov) == fov))
  1039. mUpdateCameraFov = false;
  1040. }
  1041. }
  1042. Parent::readPacket(bstream);
  1043. bstream->clearCompressionPoint();
  1044. bstream->clearStringBuffer();
  1045. if (isConnectionToServer())
  1046. {
  1047. PROFILE_START(ClientCatchup);
  1048. ClientProcessList::get()->clientCatchup(this);
  1049. PROFILE_END();
  1050. }
  1051. }
  1052. void GameConnection::writePacket(BitStream *bstream, PacketNotify *note)
  1053. {
  1054. bstream->clearCompressionPoint();
  1055. bstream->clearStringBuffer();
  1056. GamePacketNotify *gnote = (GamePacketNotify *) note;
  1057. U32 startPos = bstream->getBitPosition();
  1058. if (isConnectionToServer())
  1059. {
  1060. mMoveList->clientWriteMovePacket(bstream);
  1061. bstream->writeFlag(mCameraPos == 1);
  1062. // if we're recording, we want to make sure that we get periodic updates of the
  1063. // control object "just in case" - ie if the math copro is different between the
  1064. // recording machine (SIMD vs FPU), we get periodic corrections
  1065. bool forceUpdate = false;
  1066. if(isRecording())
  1067. {
  1068. U32 currentTime = Platform::getVirtualMilliseconds();
  1069. if(currentTime - mLastControlRequestTime > ControlRequestTime)
  1070. {
  1071. mLastControlRequestTime = currentTime;
  1072. forceUpdate=true;;
  1073. }
  1074. }
  1075. bstream->writeFlag(forceUpdate);
  1076. // Control scheme changed?
  1077. if(bstream->writeFlag(mUpdateControlScheme))
  1078. {
  1079. bstream->writeFlag(mAbsoluteRotation);
  1080. bstream->writeFlag(mAddYawToAbsRot);
  1081. bstream->writeFlag(mAddPitchToAbsRot);
  1082. mUpdateControlScheme = false;
  1083. }
  1084. // first person changed?
  1085. if(bstream->writeFlag(mUpdateFirstPerson))
  1086. {
  1087. bstream->writeFlag(mFirstPerson);
  1088. mUpdateFirstPerson = false;
  1089. }
  1090. // camera fov changed? (server fov resolution is 1 degree)
  1091. if(bstream->writeFlag(mUpdateCameraFov))
  1092. {
  1093. bstream->writeInt(mClamp(S32(mCameraFov), S32(MinCameraFov), S32(MaxCameraFov)), 8);
  1094. mUpdateCameraFov = false;
  1095. }
  1096. DEBUG_LOG(("PKLOG %d CLIENTMOVES: %d", getId(), bstream->getCurPos() - startPos));
  1097. }
  1098. else
  1099. {
  1100. mMoveList->serverWriteMovePacket(bstream);
  1101. // get the ghost index of the control object, and write out
  1102. // all the damage flash & white out
  1103. S32 gIndex = -1;
  1104. if (!mControlObject.isNull())
  1105. {
  1106. gIndex = getGhostIndex(mControlObject);
  1107. F32 flash = mControlObject->getDamageFlash();
  1108. F32 whiteOut = mControlObject->getWhiteOut();
  1109. if(bstream->writeFlag(flash != 0 || whiteOut != 0))
  1110. {
  1111. if(bstream->writeFlag(flash != 0))
  1112. bstream->writeFloat(flash, 7);
  1113. if(bstream->writeFlag(whiteOut != 0))
  1114. bstream->writeFloat(whiteOut/1.5, 7);
  1115. }
  1116. }
  1117. else
  1118. bstream->writeFlag(false);
  1119. if (bstream->writeFlag(gIndex != -1))
  1120. {
  1121. // assume that the control object will write in a compression point
  1122. if(bstream->writeFlag(mMoveList->isMismatch() || mControlForceMismatch))
  1123. {
  1124. #ifdef TORQUE_DEBUG_NET
  1125. if (mMoveList->isMismatch())
  1126. Con::printf("packetDataChecksum disagree!");
  1127. else
  1128. Con::printf("packetDataChecksum disagree! (force)");
  1129. #endif
  1130. bstream->writeInt(gIndex, NetConnection::GhostIdBitSize);
  1131. #ifdef TORQUE_NET_STATS
  1132. U32 beginSize = bstream->getBitPosition();
  1133. #endif
  1134. mControlObject->writePacketData(this, bstream);
  1135. #ifdef TORQUE_NET_STATS
  1136. mControlObject->getClassRep()->updateNetStatWriteData(bstream->getBitPosition() - beginSize);
  1137. #endif
  1138. mControlForceMismatch = false;
  1139. }
  1140. else
  1141. {
  1142. // we'll have to use the control object's position as the compression point
  1143. // should make this lower res for better space usage:
  1144. Point3F coPos = mControlObject->getPosition();
  1145. bstream->write(coPos.x);
  1146. bstream->write(coPos.y);
  1147. bstream->write(coPos.z);
  1148. bstream->setCompressionPoint(coPos);
  1149. }
  1150. }
  1151. DEBUG_LOG(("PKLOG %d CONTROLOBJECTSTATE: %d", getId(), bstream->getCurPos() - startPos));
  1152. startPos = bstream->getBitPosition();
  1153. if (!mCameraObject.isNull() && mCameraObject != mControlObject)
  1154. {
  1155. gIndex = getGhostIndex(mCameraObject);
  1156. if (bstream->writeFlag(gIndex != -1))
  1157. {
  1158. bstream->writeInt(gIndex, NetConnection::GhostIdBitSize);
  1159. mCameraObject->writePacketData(this, bstream);
  1160. }
  1161. }
  1162. else
  1163. bstream->writeFlag( false );
  1164. // Control scheme changed?
  1165. if(bstream->writeFlag(mUpdateControlScheme))
  1166. {
  1167. bstream->writeFlag(mAbsoluteRotation);
  1168. bstream->writeFlag(mAddYawToAbsRot);
  1169. bstream->writeFlag(mAddPitchToAbsRot);
  1170. mUpdateControlScheme = false;
  1171. }
  1172. // first person changed?
  1173. if(bstream->writeFlag(mUpdateFirstPerson))
  1174. {
  1175. bstream->writeFlag(mFirstPerson);
  1176. mUpdateFirstPerson = false;
  1177. }
  1178. // server forcing client fov?
  1179. gnote->cameraFov = -1;
  1180. if(bstream->writeFlag(mUpdateCameraFov))
  1181. {
  1182. gnote->cameraFov = mClamp(S32(mCameraFov), S32(MinCameraFov), S32(MaxCameraFov));
  1183. bstream->writeInt(gnote->cameraFov, 8);
  1184. mUpdateCameraFov = false;
  1185. }
  1186. DEBUG_LOG(("PKLOG %d PINGCAMSTATE: %d", getId(), bstream->getCurPos() - startPos));
  1187. }
  1188. Parent::writePacket(bstream, note);
  1189. bstream->clearCompressionPoint();
  1190. bstream->clearStringBuffer();
  1191. }
  1192. void GameConnection::detectLag()
  1193. {
  1194. //see if we're lagging...
  1195. S32 curTime = Sim::getCurrentTime();
  1196. if (curTime - mLastPacketTime > mLagThresholdMS)
  1197. {
  1198. if (!mLagging)
  1199. {
  1200. mLagging = true;
  1201. setLagIcon_callback(true);
  1202. }
  1203. }
  1204. else if (mLagging)
  1205. {
  1206. mLagging = false;
  1207. setLagIcon_callback(false);
  1208. }
  1209. }
  1210. GameConnection::GamePacketNotify::GamePacketNotify()
  1211. {
  1212. // need to fill in empty notifes for demo start block
  1213. cameraFov = 0;
  1214. }
  1215. NetConnection::PacketNotify *GameConnection::allocNotify()
  1216. {
  1217. return new GamePacketNotify;
  1218. }
  1219. void GameConnection::packetReceived(PacketNotify *note)
  1220. {
  1221. //record the time so we can tell if we're lagging...
  1222. mLastPacketTime = Sim::getCurrentTime();
  1223. // If we wanted to do something special, we grab our note like this:
  1224. //GamePacketNotify *gnote = (GamePacketNotify *) note;
  1225. Parent::packetReceived(note);
  1226. }
  1227. void GameConnection::packetDropped(PacketNotify *note)
  1228. {
  1229. Parent::packetDropped(note);
  1230. GamePacketNotify *gnote = (GamePacketNotify *) note;
  1231. if(gnote->cameraFov != -1)
  1232. mUpdateCameraFov = true;
  1233. }
  1234. //----------------------------------------------------------------------------
  1235. void GameConnection::play2D(SFXProfile* profile)
  1236. {
  1237. postNetEvent(new Sim2DAudioEvent(profile));
  1238. }
  1239. void GameConnection::play3D(SFXProfile* profile, const MatrixF *transform)
  1240. {
  1241. if ( !transform )
  1242. play2D(profile);
  1243. else if ( !mControlObject )
  1244. postNetEvent(new Sim3DAudioEvent(profile,transform));
  1245. else
  1246. {
  1247. // TODO: Maybe improve this to account for the duration
  1248. // of the sound effect and if the control object can get
  1249. // into hearing range within time?
  1250. // Only post the event if it's within audible range
  1251. // of the control object.
  1252. Point3F ear,pos;
  1253. transform->getColumn(3,&pos);
  1254. mControlObject->getTransform().getColumn(3,&ear);
  1255. if ((ear - pos).len() < profile->getDescription()->mMaxDistance)
  1256. postNetEvent(new Sim3DAudioEvent(profile,transform));
  1257. }
  1258. }
  1259. void GameConnection::doneScopingScene()
  1260. {
  1261. // Could add special post-scene scoping here, such as scoping
  1262. // objects not visible to the camera, but visible to sensors.
  1263. }
  1264. void GameConnection::preloadDataBlock(SimDataBlock *db)
  1265. {
  1266. mDataBlockLoadList.push_back(db);
  1267. if(mDataBlockLoadList.size() == 1)
  1268. preloadNextDataBlock(false);
  1269. }
  1270. void GameConnection::fileDownloadSegmentComplete()
  1271. {
  1272. // this is called when a the file list has finished processing...
  1273. // at this point we can try again to add the object
  1274. // subclasses can override this to do, for example, datablock redos.
  1275. if(mDataBlockLoadList.size())
  1276. preloadNextDataBlock(mNumDownloadedFiles != 0);
  1277. Parent::fileDownloadSegmentComplete();
  1278. }
  1279. void GameConnection::preloadNextDataBlock(bool hadNewFiles)
  1280. {
  1281. if(!mDataBlockLoadList.size())
  1282. return;
  1283. while(mDataBlockLoadList.size())
  1284. {
  1285. // only check for new files if this is the first load, or if new
  1286. // files were downloaded from the server.
  1287. // if(hadNewFiles)
  1288. // gResourceManager->setMissingFileLogging(true);
  1289. // gResourceManager->clearMissingFileList();
  1290. SimDataBlock *object = mDataBlockLoadList[0];
  1291. if(!object)
  1292. {
  1293. // a null object is used to signify that the last ghost in the list is down
  1294. mDataBlockLoadList.pop_front();
  1295. AssertFatal(mDataBlockLoadList.size() == 0, "Error! Datablock save list should be empty!");
  1296. sendConnectionMessage(DataBlocksDownloadDone, mDataBlockSequence);
  1297. // gResourceManager->setMissingFileLogging(false);
  1298. return;
  1299. }
  1300. mFilesWereDownloaded = hadNewFiles;
  1301. if(!object->preload(false, mErrorBuffer))
  1302. {
  1303. mFilesWereDownloaded = false;
  1304. // make sure there's an error message if necessary
  1305. if(mErrorBuffer.isEmpty())
  1306. setLastError("Invalid packet. (object preload)");
  1307. // if there were new files, make sure the error message
  1308. // is the one from the last time we tried to add this object
  1309. if(hadNewFiles)
  1310. {
  1311. mErrorBuffer = mLastFileErrorBuffer;
  1312. // gResourceManager->setMissingFileLogging(false);
  1313. return;
  1314. }
  1315. // object failed to load, let's see if it had any missing files
  1316. if(isLocalConnection() /*|| !gResourceManager->getMissingFileList(mMissingFileList)*/)
  1317. {
  1318. // no missing files, must be an error
  1319. // connection will automagically delete the ghost always list
  1320. // when this error is reported.
  1321. // gResourceManager->setMissingFileLogging(false);
  1322. return;
  1323. }
  1324. // ok, copy the error buffer out to a scratch pad for now
  1325. mLastFileErrorBuffer = mErrorBuffer;
  1326. //mErrorBuffer = String();
  1327. // request the missing files...
  1328. mNumDownloadedFiles = 0;
  1329. sendNextFileDownloadRequest();
  1330. break;
  1331. }
  1332. mFilesWereDownloaded = false;
  1333. // gResourceManager->setMissingFileLogging(false);
  1334. mDataBlockLoadList.pop_front();
  1335. hadNewFiles = true;
  1336. }
  1337. }
  1338. void GameConnection::onEndGhosting()
  1339. {
  1340. Parent::onEndGhosting();
  1341. // Reset move list. All the moves are obsolete now and furthermore,
  1342. // if we don't clear out the list now we might run in danger of
  1343. // getting backlogged later on what is a list full of obsolete moves.
  1344. mMoveList->reset();
  1345. }
  1346. //----------------------------------------------------------------------------
  1347. //localconnection only blackout functions
  1348. void GameConnection::setBlackOut(bool fadeToBlack, S32 timeMS)
  1349. {
  1350. mFadeToBlack = fadeToBlack;
  1351. mBlackOutStartTimeMS = Sim::getCurrentTime();
  1352. mBlackOutTimeMS = timeMS;
  1353. //if timeMS <= 0 set the value instantly
  1354. if (mBlackOutTimeMS <= 0)
  1355. mBlackOut = (mFadeToBlack ? 1.0f : 0.0f);
  1356. }
  1357. F32 GameConnection::getBlackOut()
  1358. {
  1359. S32 curTime = Sim::getCurrentTime();
  1360. //see if we're in the middle of a black out
  1361. if (curTime < mBlackOutStartTimeMS + mBlackOutTimeMS)
  1362. {
  1363. S32 elapsedTime = curTime - mBlackOutStartTimeMS;
  1364. F32 timePercent = F32(elapsedTime) / F32(mBlackOutTimeMS);
  1365. mBlackOut = (mFadeToBlack ? timePercent : 1.0f - timePercent);
  1366. }
  1367. else
  1368. mBlackOut = (mFadeToBlack ? 1.0f : 0.0f);
  1369. //return the blackout time
  1370. return mBlackOut;
  1371. }
  1372. void GameConnection::handleConnectionMessage(U32 message, U32 sequence, U32 ghostCount)
  1373. {
  1374. if(isConnectionToServer())
  1375. {
  1376. if(message == DataBlocksDone)
  1377. {
  1378. mDataBlockLoadList.push_back(NULL);
  1379. mDataBlockSequence = sequence;
  1380. if(mDataBlockLoadList.size() == 1)
  1381. preloadNextDataBlock(true);
  1382. }
  1383. }
  1384. else
  1385. {
  1386. if(message == DataBlocksDownloadDone)
  1387. {
  1388. if(getDataBlockSequence() == sequence)
  1389. {
  1390. onDataBlocksDone_callback( getDataBlockSequence() );
  1391. }
  1392. }
  1393. }
  1394. Parent::handleConnectionMessage(message, sequence, ghostCount);
  1395. }
  1396. //----------------------------------------------------------------------------
  1397. DefineEngineMethod( GameConnection, transmitDataBlocks, void, (S32 sequence),,
  1398. "@brief Sent by the server during phase 1 of the mission download to send the datablocks to the client.\n\n"
  1399. "SimDataBlocks, also known as just datablocks, need to be transmitted to the client "
  1400. "prior to the client entering the game world. These represent the static data that "
  1401. "most objects in the world reference. This is typically done during the standard "
  1402. "mission start phase 1 when following Torque's example mission startup sequence.\n\n"
  1403. "When the datablocks have all been transmitted, onDataBlocksDone() is called to move "
  1404. "the mission start process to the next phase."
  1405. "@param sequence The sequence is common between the server and client and ensures "
  1406. "that the client is acting on the most recent mission start process. If an errant "
  1407. "network packet (one that was lost but has now been found) is received by the client "
  1408. "with an incorrect sequence, it is just ignored. This sequence number is updated on "
  1409. "the server every time a mission is loaded.\n\n"
  1410. "@tsexample\n"
  1411. "function serverCmdMissionStartPhase1Ack(%client, %seq)\n"
  1412. "{\n"
  1413. " // Make sure to ignore calls from a previous mission load\n"
  1414. " if (%seq != $missionSequence || !$MissionRunning)\n"
  1415. " return;\n"
  1416. " if (%client.currentPhase != 0)\n"
  1417. " return;\n"
  1418. " %client.currentPhase = 1;\n"
  1419. "\n"
  1420. " // Start with the CRC\n"
  1421. " %client.setMissionCRC( $missionCRC );\n"
  1422. "\n"
  1423. " // Send over the datablocks...\n"
  1424. " // OnDataBlocksDone will get called when have confirmation\n"
  1425. " // that they've all been received.\n"
  1426. " %client.transmitDataBlocks($missionSequence);\n"
  1427. "}\n"
  1428. "@endtsexample\n\n"
  1429. "@see GameConnection::onDataBlocksDone()\n\n")
  1430. {
  1431. // Set the datablock sequence.
  1432. object->setDataBlockSequence(sequence);
  1433. // Store a pointer to the datablock group.
  1434. SimDataBlockGroup* pGroup = Sim::getDataBlockGroup();
  1435. // Determine the size of the datablock group.
  1436. const U32 iCount = pGroup->size();
  1437. // If this is the local client...
  1438. if (GameConnection::getLocalClientConnection() == object)
  1439. {
  1440. // Set up a pointer to the datablock.
  1441. SimDataBlock* pDataBlock = 0;
  1442. // Set up a buffer for the datablock send.
  1443. U8 iBuffer[16384];
  1444. BitStream mStream(iBuffer, 16384);
  1445. // Iterate through all the datablocks...
  1446. for (U32 i = 0; i < iCount; i++)
  1447. {
  1448. // Get a pointer to the datablock in question...
  1449. pDataBlock = (SimDataBlock*)(*pGroup)[i];
  1450. // Set the client's new modified key.
  1451. object->setMaxDataBlockModifiedKey(pDataBlock->getModifiedKey());
  1452. // Pack the datablock stream.
  1453. mStream.setPosition(0);
  1454. mStream.clearCompressionPoint();
  1455. pDataBlock->packData(&mStream);
  1456. // Unpack the datablock stream.
  1457. mStream.setPosition(0);
  1458. mStream.clearCompressionPoint();
  1459. pDataBlock->unpackData(&mStream);
  1460. // Call the console function to set the number of blocks to be sent.
  1461. onDataBlockObjectReceived_callback(i, iCount);
  1462. // Preload the datablock on the dummy client.
  1463. pDataBlock->preload(false, NetConnection::getErrorBuffer());
  1464. }
  1465. // Get the last datablock (if any)...
  1466. if (pDataBlock)
  1467. {
  1468. // Ensure the datablock modified key is set.
  1469. object->setDataBlockModifiedKey(object->getMaxDataBlockModifiedKey());
  1470. // Ensure that the client knows that the datablock send is done...
  1471. object->sendConnectionMessage(GameConnection::DataBlocksDone, object->getDataBlockSequence());
  1472. }
  1473. }
  1474. else
  1475. {
  1476. // Otherwise, store the current datablock modified key.
  1477. const S32 iKey = object->getDataBlockModifiedKey();
  1478. // Iterate through the datablock group...
  1479. U32 i = 0;
  1480. for (; i < iCount; i++)
  1481. {
  1482. // If the datablock's modified key has already been set, break out of the loop...
  1483. if (((SimDataBlock*)(*pGroup)[i])->getModifiedKey() > iKey)
  1484. {
  1485. break;
  1486. }
  1487. }
  1488. // If this is the last datablock in the group...
  1489. if (i == iCount)
  1490. {
  1491. // Ensure that the client knows that the datablock send is done...
  1492. object->sendConnectionMessage(GameConnection::DataBlocksDone, object->getDataBlockSequence());
  1493. // Then exit out since nothing else needs to be done.
  1494. return;
  1495. }
  1496. // Set the maximum datablock modified key value.
  1497. object->setMaxDataBlockModifiedKey(iKey);
  1498. // Get the minimum number of datablocks...
  1499. const U32 iMax = getMin(i + DataBlockQueueCount, iCount);
  1500. // Iterate through the remaining datablocks...
  1501. for (;i < iMax; i++)
  1502. {
  1503. // Get a pointer to the datablock in question...
  1504. SimDataBlock* pDataBlock = (SimDataBlock*)(*pGroup)[i];
  1505. // Post the datablock event to the client.
  1506. object->postNetEvent(new SimDataBlockEvent(pDataBlock, i, iCount, object->getDataBlockSequence()));
  1507. }
  1508. }
  1509. }
  1510. DefineEngineMethod( GameConnection, activateGhosting, void, (),,
  1511. "@brief Called by the server during phase 2 of the mission download to start sending ghosts to the client.\n\n"
  1512. "Ghosts represent objects on the server that are in scope for the client. These need "
  1513. "to be synchronized with the client in order for the client to see and interact with them. "
  1514. "This is typically done during the standard mission start phase 2 when following Torque's "
  1515. "example mission startup sequence.\n\n"
  1516. "@tsexample\n"
  1517. "function serverCmdMissionStartPhase2Ack(%client, %seq, %playerDB)\n"
  1518. "{\n"
  1519. " // Make sure to ignore calls from a previous mission load\n"
  1520. " if (%seq != $missionSequence || !$MissionRunning)\n"
  1521. " return;\n"
  1522. " if (%client.currentPhase != 1.5)\n"
  1523. " return;\n"
  1524. " %client.currentPhase = 2;\n"
  1525. "\n"
  1526. " // Set the player datablock choice\n"
  1527. " %client.playerDB = %playerDB;\n"
  1528. "\n"
  1529. " // Update mod paths, this needs to get there before the objects.\n"
  1530. " %client.transmitPaths();\n"
  1531. "\n"
  1532. " // Start ghosting objects to the client\n"
  1533. " %client.activateGhosting();\n"
  1534. "}\n"
  1535. "@endtsexample\n\n"
  1536. "@see @ref ghosting_scoping for a description of the ghosting system.\n\n")
  1537. {
  1538. object->activateGhosting();
  1539. }
  1540. DefineEngineMethod( GameConnection, resetGhosting, void, (),,
  1541. "@brief On the server, resets the connection to indicate that ghosting has been disabled.\n\n"
  1542. "Typically when a mission has ended on the server, all connected clients are informed of this change "
  1543. "and their connections are reset back to a starting state. This method resets a connection on the "
  1544. "server to indicate that ghosts are no longer being transmitted. On the client end, all ghost "
  1545. "information will be deleted.\n\n"
  1546. "@tsexample\n"
  1547. " // Inform the clients\n"
  1548. " for (%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++)\n"
  1549. " {\n"
  1550. " // clear ghosts and paths from all clients\n"
  1551. " %cl = ClientGroup.getObject(%clientIndex);\n"
  1552. " %cl.endMission();\n"
  1553. " %cl.resetGhosting();\n"
  1554. " %cl.clearPaths();\n"
  1555. " }\n"
  1556. "@endtsexample\n\n"
  1557. "@see @ref ghosting_scoping for a description of the ghosting system.\n\n")
  1558. {
  1559. object->resetGhosting();
  1560. }
  1561. DefineEngineMethod( GameConnection, setControlObject, bool, (GameBase* ctrlObj),,
  1562. "@brief On the server, sets the object that the client will control.\n\n"
  1563. "By default the control object is an instance of the Player class, but can also be an instance "
  1564. "of Camera (when editing the mission, for example), or any other ShapeBase derived class as "
  1565. "appropriate for the game.\n\n"
  1566. "@param ctrlObj The GameBase object on the server to control.")
  1567. {
  1568. if(!ctrlObj)
  1569. return false;
  1570. object->setControlObject(ctrlObj);
  1571. return true;
  1572. }
  1573. DefineEngineMethod( GameConnection, clearDisplayDevice, void, (),,
  1574. "@brief Clear any display device.\n\n"
  1575. "A display device may define a number of properties that are used during rendering.\n\n")
  1576. {
  1577. object->clearDisplayDevice();
  1578. }
  1579. DefineEngineMethod( GameConnection, getControlObject, GameBase*, (),,
  1580. "@brief On the server, returns the object that the client is controlling."
  1581. "By default the control object is an instance of the Player class, but can also be an instance "
  1582. "of Camera (when editing the mission, for example), or any other ShapeBase derived class as "
  1583. "appropriate for the game.\n\n"
  1584. "@see GameConnection::setControlObject()\n\n")
  1585. {
  1586. return object->getControlObject();
  1587. }
  1588. DefineEngineMethod( GameConnection, isAIControlled, bool, (),,
  1589. "@brief Returns true if this connection is AI controlled.\n\n"
  1590. "@see AIConnection")
  1591. {
  1592. return object->isAIControlled();
  1593. }
  1594. DefineEngineMethod( GameConnection, isControlObjectRotDampedCamera, bool, (),,
  1595. "@brief Returns true if the object being controlled by the client is making use "
  1596. "of a rotation damped camera.\n\n"
  1597. "@see Camera")
  1598. {
  1599. return object->isControlObjectRotDampedCamera();
  1600. }
  1601. DefineEngineMethod( GameConnection, play2D, bool, (SFXProfile* profile),,
  1602. "@brief Used on the server to play a 2D sound that is not attached to any object.\n\n"
  1603. "@param profile The SFXProfile that defines the sound to play.\n\n"
  1604. "@tsexample\n"
  1605. "function ServerPlay2D(%profile)\n"
  1606. "{\n"
  1607. " // Play the given sound profile on every client.\n"
  1608. " // The sounds will be transmitted as an event, not attached to any object.\n"
  1609. " for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)\n"
  1610. " ClientGroup.getObject(%idx).play2D(%profile);\n"
  1611. "}\n"
  1612. "@endtsexample\n\n")
  1613. {
  1614. if(!profile)
  1615. return false;
  1616. object->play2D(profile);
  1617. return true;
  1618. }
  1619. DefineEngineMethod( GameConnection, play3D, bool, (SFXProfile* profile, TransformF location),,
  1620. "@brief Used on the server to play a 3D sound that is not attached to any object.\n\n"
  1621. "@param profile The SFXProfile that defines the sound to play.\n"
  1622. "@param location The position and orientation of the 3D sound given in the form of \"x y z ax ay az aa\".\n\n"
  1623. "@tsexample\n"
  1624. "function ServerPlay3D(%profile,%transform)\n"
  1625. "{\n"
  1626. " // Play the given sound profile at the given position on every client\n"
  1627. " // The sound will be transmitted as an event, not attached to any object.\n"
  1628. " for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)\n"
  1629. " ClientGroup.getObject(%idx).play3D(%profile,%transform);\n"
  1630. "}\n"
  1631. "@endtsexample\n\n")
  1632. {
  1633. if(!profile)
  1634. return false;
  1635. MatrixF mat = location.getMatrix();
  1636. object->play3D(profile,&mat);
  1637. return true;
  1638. }
  1639. DefineEngineMethod( GameConnection, chaseCam, bool, (S32 size),,
  1640. "@brief Sets the size of the chase camera's matrix queue.\n\n"
  1641. "@note This sets the queue size across all GameConnections.\n\n"
  1642. "@note This is not currently hooked up.\n\n")
  1643. {
  1644. if (size != sChaseQueueSize)
  1645. {
  1646. SAFE_DELETE_ARRAY(sChaseQueue);
  1647. sChaseQueueSize = size;
  1648. sChaseQueueHead = sChaseQueueTail = 0;
  1649. if (size)
  1650. {
  1651. sChaseQueue = new MatrixF[size];
  1652. return true;
  1653. }
  1654. }
  1655. return false;
  1656. }
  1657. DefineEngineMethod( GameConnection, getControlCameraDefaultFov, F32, (),,
  1658. "@brief Returns the default field of view as used by the control object's camera.\n\n")
  1659. {
  1660. F32 fov = 0.0f;
  1661. if(!object->getControlCameraDefaultFov(&fov))
  1662. return(0.0f);
  1663. return(fov);
  1664. }
  1665. DefineEngineMethod( GameConnection, setControlCameraFov, void, (F32 newFOV),,
  1666. "@brief On the server, sets the control object's camera's field of view.\n\n"
  1667. "@param newFOV New field of view (in degrees) to force the control object's camera to use. This value "
  1668. "is clamped to be within the range of 1 to 179 degrees.\n\n"
  1669. "@note When transmitted over the network to the client, the resolution is limited to "
  1670. "one degree. Any fraction is dropped.")
  1671. {
  1672. object->setControlCameraFov(newFOV);
  1673. }
  1674. DefineEngineMethod( GameConnection, getControlCameraFov, F32, (),,
  1675. "@brief Returns the field of view as used by the control object's camera.\n\n")
  1676. {
  1677. F32 fov = 0.0f;
  1678. if(!object->getControlCameraFov(&fov))
  1679. return(0.0f);
  1680. return(fov);
  1681. }
  1682. DefineEngineMethod( GameConnection, getDamageFlash, F32, (),,
  1683. "@brief On the client, get the control object's damage flash level.\n\n"
  1684. "@return flash level\n")
  1685. {
  1686. return object->getDamageFlash();
  1687. }
  1688. DefineEngineMethod( GameConnection, getWhiteOut, F32, (),,
  1689. "@brief On the client, get the control object's white-out level.\n\n"
  1690. "@return white-out level\n")
  1691. {
  1692. return object->getWhiteOut();
  1693. }
  1694. DefineEngineMethod( GameConnection, setBlackOut, void, (bool doFade, S32 timeMS),,
  1695. "@brief On the server, sets the client's 3D display to fade to black.\n\n"
  1696. "@param doFade Set to true to fade to black, and false to fade from black.\n"
  1697. "@param timeMS Time it takes to perform the fade as measured in ms.\n\n"
  1698. "@note Not currently hooked up, and is not synchronized over the network.")
  1699. {
  1700. object->setBlackOut(doFade, timeMS);
  1701. }
  1702. DefineEngineMethod( GameConnection, setMissionCRC, void, (S32 CRC),,
  1703. "@brief On the server, transmits the mission file's CRC value to the client.\n\n"
  1704. "Typically, during the standard mission start phase 1, the mission file's CRC value "
  1705. "on the server is send to the client. This allows the client to determine if the mission "
  1706. "has changed since the last time it downloaded this mission and act appropriately, such as "
  1707. "rebuilt cached lightmaps.\n\n"
  1708. "@param CRC The mission file's CRC value on the server.\n\n"
  1709. "@tsexample\n"
  1710. "function serverCmdMissionStartPhase1Ack(%client, %seq)\n"
  1711. "{\n"
  1712. " // Make sure to ignore calls from a previous mission load\n"
  1713. " if (%seq != $missionSequence || !$MissionRunning)\n"
  1714. " return;\n"
  1715. " if (%client.currentPhase != 0)\n"
  1716. " return;\n"
  1717. " %client.currentPhase = 1;\n"
  1718. "\n"
  1719. " // Start with the CRC\n"
  1720. " %client.setMissionCRC( $missionCRC );\n"
  1721. "\n"
  1722. " // Send over the datablocks...\n"
  1723. " // OnDataBlocksDone will get called when have confirmation\n"
  1724. " // that they've all been received.\n"
  1725. " %client.transmitDataBlocks($missionSequence);\n"
  1726. "}\n"
  1727. "@endtsexample\n\n")
  1728. {
  1729. if(object->isConnectionToServer())
  1730. return;
  1731. object->postNetEvent(new SetMissionCRCEvent(CRC));
  1732. }
  1733. DefineEngineMethod( GameConnection, delete, void, (const char* reason), (""),
  1734. "@brief On the server, disconnect a client and pass along an optional reason why.\n\n"
  1735. "This method performs two operations: it disconnects a client connection from the server, "
  1736. "and it deletes the connection object. The optional reason is sent in the disconnect packet "
  1737. "and is often displayed to the user so they know why they've been disconnected.\n\n"
  1738. "@param reason [optional] The reason why the user has been disconnected from the server.\n\n"
  1739. "@tsexample\n"
  1740. "function kick(%client)\n"
  1741. "{\n"
  1742. " messageAll( 'MsgAdminForce', '\\c2The Admin has kicked %1.', %client.playerName);\n"
  1743. "\n"
  1744. " if (!%client.isAIControlled())\n"
  1745. " BanList::add(%client.guid, %client.getAddress(), $Pref::Server::KickBanTime);\n"
  1746. " %client.delete(\"You have been kicked from this server\");\n"
  1747. "}\n"
  1748. "@endtsexample\n\n")
  1749. {
  1750. object->setDisconnectReason(reason);
  1751. object->deleteObject();
  1752. }
  1753. //--------------------------------------------------------------------------
  1754. void GameConnection::consoleInit()
  1755. {
  1756. Con::addVariable("$pref::Net::LagThreshold", TypeS32, &mLagThresholdMS,
  1757. "@brief How long between received packets before the client is considered as lagging (in ms).\n\n"
  1758. "This is used by GameConnection to determine if the client is lagging. "
  1759. "If the client is indeed lagging, setLagIcon() is called to inform the user in some way. i.e. "
  1760. "display an icon on screen.\n\n"
  1761. "@see GameConnection, GameConnection::setLagIcon()\n\n"
  1762. "@ingroup Networking\n");
  1763. // Con::addVariable("specialFog", TypeBool, &SceneGraph::useSpecial);
  1764. }
  1765. DefineEngineMethod( GameConnection, startRecording, void, (const char* fileName),,
  1766. "@brief On the client, starts recording the network connection's traffic to a demo file.\n\n"
  1767. "It is often useful to play back a game session. This could be for producing a "
  1768. "demo of the game that will be shown at a later time, or for debugging a game. "
  1769. "By recording the entire network stream it is possible to later play game the game "
  1770. "exactly as it unfolded during the actual play session. This is because all user "
  1771. "control and server results pass through the connection.\n\n"
  1772. "@param fileName The file name to use for the demo recording.\n\n"
  1773. "@see GameConnection::stopRecording(), GameConnection::playDemo()")
  1774. {
  1775. char expFileName[1024];
  1776. Con::expandScriptFilename(expFileName, sizeof(expFileName), fileName);
  1777. object->startDemoRecord(expFileName);
  1778. }
  1779. DefineEngineMethod( GameConnection, stopRecording, void, (),,
  1780. "@brief On the client, stops the recording of a connection's network traffic to a file.\n\n"
  1781. "@see GameConnection::startRecording(), GameConnection::playDemo()")
  1782. {
  1783. object->stopRecording();
  1784. }
  1785. DefineEngineMethod( GameConnection, playDemo, bool, (const char* demoFileName),,
  1786. "@brief On the client, play back a previously recorded game session.\n\n"
  1787. "It is often useful to play back a game session. This could be for producing a "
  1788. "demo of the game that will be shown at a later time, or for debugging a game. "
  1789. "By recording the entire network stream it is possible to later play game the game "
  1790. "exactly as it unfolded during the actual play session. This is because all user "
  1791. "control and server results pass through the connection.\n\n"
  1792. "@returns True if the playback was successful. False if there was an issue, such as "
  1793. "not being able to open the demo file for playback.\n\n"
  1794. "@see GameConnection::startRecording(), GameConnection::stopRecording()")
  1795. {
  1796. char filename[1024];
  1797. Con::expandScriptFilename(filename, sizeof(filename), demoFileName);
  1798. // Note that calling onConnectionEstablished will change the values in argv!
  1799. object->onConnectionEstablished(true);
  1800. object->setEstablished();
  1801. if(!object->replayDemoRecord(filename))
  1802. {
  1803. Con::printf("Unable to open demo file %s.", filename);
  1804. object->deleteObject();
  1805. return false;
  1806. }
  1807. // After demo has loaded, execute the scene re-light the scene
  1808. //SceneLighting::lightScene(0, 0);
  1809. GameConnection::smPlayingDemo.trigger();
  1810. return true;
  1811. }
  1812. DefineEngineMethod( GameConnection, isDemoPlaying, bool, (),,
  1813. "@brief Returns true if a previously recorded demo file is now playing.\n\n"
  1814. "@see GameConnection::playDemo()")
  1815. {
  1816. return object->isPlayingBack();
  1817. }
  1818. DefineEngineMethod( GameConnection, isDemoRecording, bool, (),,
  1819. "@brief Returns true if a demo file is now being recorded.\n\n"
  1820. "@see GameConnection::startRecording(), GameConnection::stopRecording()")
  1821. {
  1822. return object->isRecording();
  1823. }
  1824. DefineEngineMethod( GameConnection, listClassIDs, void, (),,
  1825. "@brief List all of the classes that this connection knows about, and what their IDs are. Useful for debugging network problems.\n\n"
  1826. "@note The list is sent to the console.\n\n")
  1827. {
  1828. Con::printf("--------------- Class ID Listing ----------------");
  1829. Con::printf(" id | name");
  1830. for(AbstractClassRep *rep = AbstractClassRep::getClassList();
  1831. rep;
  1832. rep = rep->getNextClass())
  1833. {
  1834. ConsoleObject *obj = rep->create();
  1835. if(obj && rep->getClassId(object->getNetClassGroup()) >= 0)
  1836. Con::printf("%7.7d| %s", rep->getClassId(object->getNetClassGroup()), rep->getClassName());
  1837. delete obj;
  1838. }
  1839. }
  1840. DefineEngineStaticMethod( GameConnection, getServerConnection, S32, (),,
  1841. "@brief On the client, this static mehtod will return the connection to the server, if any.\n\n"
  1842. "@returns The SimObject ID of the server connection, or -1 if none is found.\n\n")
  1843. {
  1844. if(GameConnection::getConnectionToServer())
  1845. return GameConnection::getConnectionToServer()->getId();
  1846. else
  1847. {
  1848. Con::errorf("GameConnection::getServerConnection - no connection available.");
  1849. return -1;
  1850. }
  1851. }
  1852. DefineEngineMethod( GameConnection, setCameraObject, bool, (GameBase* camera),,
  1853. "@brief On the server, set the connection's camera object used when not viewing "
  1854. "through the control object.\n\n"
  1855. "@see GameConnection::getCameraObject() and GameConnection::clearCameraObject()\n\n")
  1856. {
  1857. if(!camera)
  1858. return false;
  1859. object->setCameraObject(camera);
  1860. return true;
  1861. }
  1862. DefineEngineMethod( GameConnection, getCameraObject, SimObject*, (),,
  1863. "@brief Returns the connection's camera object used when not viewing through the control object.\n\n"
  1864. "@see GameConnection::setCameraObject() and GameConnection::clearCameraObject()\n\n")
  1865. {
  1866. SimObject *obj = dynamic_cast<SimObject*>(object->getCameraObject());
  1867. return obj;
  1868. }
  1869. DefineEngineMethod( GameConnection, clearCameraObject, void, (),,
  1870. "@brief Clear the connection's camera object reference.\n\n"
  1871. "@see GameConnection::setCameraObject() and GameConnection::getCameraObject()\n\n")
  1872. {
  1873. object->setCameraObject(NULL);
  1874. }
  1875. DefineEngineMethod( GameConnection, isFirstPerson, bool, (),,
  1876. "@brief Returns true if this connection is in first person mode.\n\n"
  1877. "@note Transition to first person occurs over time via mCameraPos, so this "
  1878. "won't immediately return true after a set.\n\n")
  1879. {
  1880. // Note: Transition to first person occurs over time via mCameraPos, so this
  1881. // won't immediately return true after a set.
  1882. return object->isFirstPerson();
  1883. }
  1884. DefineEngineMethod( GameConnection, setFirstPerson, void, (bool firstPerson),,
  1885. "@brief On the server, sets this connection into or out of first person mode.\n\n"
  1886. "@param firstPerson Set to true to put the connection into first person mode.\n\n")
  1887. {
  1888. object->setFirstPerson(firstPerson);
  1889. }
  1890. DefineEngineMethod( GameConnection, setControlSchemeParameters, void, (bool absoluteRotation, bool addYawToAbsRot, bool addPitchToAbsRot),,
  1891. "@brief Set the control scheme that may be used by a connection's control object.\n\n"
  1892. "@param absoluteRotation Use absolute rotation values from client, likely through ExtendedMove.\n"
  1893. "@param addYawToAbsRot Add relative yaw control to the absolute rotation calculation. Only useful when absoluteRotation is true.\n\n" )
  1894. {
  1895. object->setControlSchemeParameters(absoluteRotation, addYawToAbsRot, addPitchToAbsRot);
  1896. }
  1897. DefineEngineMethod( GameConnection, getControlSchemeAbsoluteRotation, bool, (),,
  1898. "@brief Get the connection's control scheme absolute rotation property.\n\n"
  1899. "@return True if the connection's control object should use an absolute rotation control scheme.\n\n"
  1900. "@see GameConnection::setControlSchemeParameters()\n\n")
  1901. {
  1902. return object->getControlSchemeAbsoluteRotation();
  1903. }
  1904. DefineEngineMethod( GameConnection, setVisibleGhostDistance, void, (F32 dist),,
  1905. "@brief Sets the distance that objects around it will be ghosted. If set to 0, "
  1906. "it may be defined by the LevelInfo.\n\n"
  1907. "@dist - is the max distance\n\n"
  1908. )
  1909. {
  1910. object->setVisibleGhostDistance(dist);
  1911. }
  1912. DefineEngineMethod( GameConnection, getVisibleGhostDistance, F32, (),,
  1913. "@brief Gets the distance that objects around the connection will be ghosted.\n\n"
  1914. "@return S32 of distance.\n\n"
  1915. )
  1916. {
  1917. return object->getVisibleGhostDistance();
  1918. }