2
0

netObject.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  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. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  23. // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  24. // Copyright (C) 2015 Faust Logic, Inc.
  25. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  26. #include "platform/platform.h"
  27. #include "console/simBase.h"
  28. #include "core/dnet.h"
  29. #include "sim/netConnection.h"
  30. #include "sim/netObject.h"
  31. #include "console/consoleTypes.h"
  32. #include "console/engineAPI.h"
  33. #ifdef TORQUE_AFX_ENABLED
  34. #include "afx/arcaneFX.h"
  35. #endif
  36. IMPLEMENT_CONOBJECT(NetObject);
  37. // More information can be found in the Torque Manual (CHM)
  38. ConsoleDocClass( NetObject,
  39. "@brief Superclass for all ghostable networked objects.\n\n"
  40. "@ingroup Networking\n");
  41. //----------------------------------------------------------------------------
  42. NetObject *NetObject::mDirtyList = NULL;
  43. NetObject::NetObject()
  44. {
  45. // netFlags will clear itself to 0
  46. mNetIndex = U32(-1);
  47. mFirstObjectRef = NULL;
  48. mPrevDirtyList = NULL;
  49. mNextDirtyList = NULL;
  50. mDirtyMaskBits = 0;
  51. #ifdef TORQUE_AFX_ENABLED
  52. mScope_id = 0;
  53. mScope_refs = 0;
  54. mScope_registered = false;
  55. #endif
  56. }
  57. NetObject::~NetObject()
  58. {
  59. if(mDirtyMaskBits)
  60. {
  61. if(mPrevDirtyList)
  62. mPrevDirtyList->mNextDirtyList = mNextDirtyList;
  63. else
  64. mDirtyList = mNextDirtyList;
  65. if(mNextDirtyList)
  66. mNextDirtyList->mPrevDirtyList = mPrevDirtyList;
  67. }
  68. }
  69. String NetObject::describeSelf() const
  70. {
  71. String desc = Parent::describeSelf();
  72. if( isClientObject() )
  73. desc += "|net: client";
  74. else
  75. desc += "|net: server";
  76. return desc;
  77. }
  78. void NetObject::setMaskBits(U32 orMask)
  79. {
  80. AssertFatal(orMask != 0, "Invalid net mask bits set.");
  81. AssertFatal(mDirtyMaskBits == 0 || (mPrevDirtyList != NULL || mNextDirtyList != NULL || mDirtyList == this), "Invalid dirty list state.");
  82. if(!mDirtyMaskBits)
  83. {
  84. AssertFatal(mNextDirtyList == NULL && mPrevDirtyList == NULL, "Object with zero mask already in list.");
  85. if(mDirtyList)
  86. {
  87. mNextDirtyList = mDirtyList;
  88. mDirtyList->mPrevDirtyList = this;
  89. }
  90. mDirtyList = this;
  91. }
  92. mDirtyMaskBits |= orMask;
  93. AssertFatal(mDirtyMaskBits == 0 || (mPrevDirtyList != NULL || mNextDirtyList != NULL || mDirtyList == this), "Invalid dirty list state.");
  94. }
  95. void NetObject::clearMaskBits(U32 orMask)
  96. {
  97. if(isDeleted())
  98. return;
  99. if(mDirtyMaskBits)
  100. {
  101. mDirtyMaskBits &= ~orMask;
  102. if(!mDirtyMaskBits)
  103. {
  104. if(mPrevDirtyList)
  105. mPrevDirtyList->mNextDirtyList = mNextDirtyList;
  106. else
  107. mDirtyList = mNextDirtyList;
  108. if(mNextDirtyList)
  109. mNextDirtyList->mPrevDirtyList = mPrevDirtyList;
  110. mNextDirtyList = mPrevDirtyList = NULL;
  111. }
  112. }
  113. for(GhostInfo *walk = mFirstObjectRef; walk; walk = walk->nextObjectRef)
  114. {
  115. if(walk->updateMask && walk->updateMask == orMask)
  116. {
  117. walk->updateMask = 0;
  118. walk->connection->ghostPushToZero(walk);
  119. }
  120. else
  121. walk->updateMask &= ~orMask;
  122. }
  123. }
  124. void NetObject::collapseDirtyList()
  125. {
  126. #ifdef TORQUE_DEBUG
  127. Vector<NetObject *> tempV;
  128. for(NetObject *t = mDirtyList; t; t = t->mNextDirtyList)
  129. tempV.push_back(t);
  130. #endif
  131. for(NetObject *obj = mDirtyList; obj; )
  132. {
  133. NetObject *next = obj->mNextDirtyList;
  134. U32 dirtyMask = obj->mDirtyMaskBits;
  135. obj->mNextDirtyList = NULL;
  136. obj->mPrevDirtyList = NULL;
  137. obj->mDirtyMaskBits = 0;
  138. if(!obj->isDeleted() && dirtyMask)
  139. {
  140. for(GhostInfo *walk = obj->mFirstObjectRef; walk; walk = walk->nextObjectRef)
  141. {
  142. U32 orMask = obj->filterMaskBits(dirtyMask,walk->connection);
  143. if(!walk->updateMask && orMask)
  144. {
  145. walk->updateMask = orMask;
  146. walk->connection->ghostPushNonZero(walk);
  147. }
  148. else
  149. walk->updateMask |= orMask;
  150. }
  151. }
  152. obj = next;
  153. }
  154. mDirtyList = NULL;
  155. #ifdef TORQUE_DEBUG
  156. for(U32 i = 0; i < tempV.size(); i++)
  157. {
  158. AssertFatal(tempV[i]->mNextDirtyList == NULL && tempV[i]->mPrevDirtyList == NULL && tempV[i]->mDirtyMaskBits == 0, "Error in collapse");
  159. }
  160. #endif
  161. }
  162. //-----------------------------------------------------------------------------
  163. DefineEngineMethod( NetObject, scopeToClient, void, ( NetConnection* client),,
  164. "@brief Cause the NetObject to be forced as scoped on the specified NetConnection.\n\n"
  165. "@param client The connection this object will always be scoped to\n\n"
  166. "@tsexample\n"
  167. "// Called to create new cameras in TorqueScript\n"
  168. "// %this - The active GameConnection\n"
  169. "// %spawnPoint - The spawn point location where we creat the camera\n"
  170. "function GameConnection::spawnCamera(%this, %spawnPoint)\n"
  171. "{\n"
  172. " // If this connection's camera exists\n"
  173. " if(isObject(%this.camera))\n"
  174. " {\n"
  175. " // Add it to the mission group to be cleaned up later\n"
  176. " MissionCleanup.add( %this.camera );\n\n"
  177. " // Force it to scope to the client side\n"
  178. " %this.camera.scopeToClient(%this);\n"
  179. " }\n"
  180. "}\n"
  181. "@endtsexample\n\n"
  182. "@see clearScopeToClient()\n")
  183. {
  184. if(!client)
  185. {
  186. Con::errorf(ConsoleLogEntry::General, "NetObject::scopeToClient: Couldn't find connection %s", client);
  187. return;
  188. }
  189. client->objectLocalScopeAlways(object);
  190. }
  191. //ConsoleMethod(NetObject,scopeToClient,void,3,3,"(NetConnection %client)"
  192. // "Cause the NetObject to be forced as scoped on the specified NetConnection.")
  193. //{
  194. // TORQUE_UNUSED(argc);
  195. // NetConnection *conn;
  196. // if(!Sim::findObject(argv[2], conn))
  197. // {
  198. // Con::errorf(ConsoleLogEntry::General, "NetObject::scopeToClient: Couldn't find connection %s", argv[2]);
  199. // return;
  200. // }
  201. // conn->objectLocalScopeAlways(object);
  202. //}
  203. DefineEngineMethod( NetObject, clearScopeToClient, void, ( NetConnection* client),,
  204. "@brief Undo the effects of a scopeToClient() call.\n\n"
  205. "@param client The connection to remove this object's scoping from \n\n"
  206. "@see scopeToClient()\n")
  207. {
  208. if(!client)
  209. {
  210. Con::errorf(ConsoleLogEntry::General, "NetObject::clearScopeToClient: Couldn't find connection %s", client);
  211. return;
  212. }
  213. client->objectLocalClearAlways(object);
  214. }
  215. //ConsoleMethod(NetObject,clearScopeToClient,void,3,3,"clearScopeToClient(%client)"
  216. // "Undo the effects of a scopeToClient() call.")
  217. //{
  218. // TORQUE_UNUSED(argc);
  219. // NetConnection *conn;
  220. // if(!Sim::findObject(argv[2], conn))
  221. // {
  222. // Con::errorf(ConsoleLogEntry::General, "NetObject::clearScopeToClient: Couldn't find connection %s", argv[2]);
  223. // return;
  224. // }
  225. // conn->objectLocalClearAlways(object);
  226. //}
  227. DefineEngineMethod( NetObject, setScopeAlways, void, (),,
  228. "@brief Always scope this object on all connections.\n\n"
  229. "The object is marked as ScopeAlways and is immediately ghosted to "
  230. "all active connections. This function has no effect if the object "
  231. "is not marked as Ghostable.\n\n")
  232. {
  233. object->setScopeAlways();
  234. }
  235. //ConsoleMethod(NetObject,setScopeAlways,void,2,2,"Always scope this object on all connections.")
  236. //{
  237. // TORQUE_UNUSED(argc); TORQUE_UNUSED(argv);
  238. // object->setScopeAlways();
  239. //}
  240. void NetObject::setScopeAlways()
  241. {
  242. if(mNetFlags.test(Ghostable) && !mNetFlags.test(IsGhost))
  243. {
  244. mNetFlags.set(ScopeAlways);
  245. // if it's a ghost always object, add it to the ghost always set
  246. // for ClientReps created later.
  247. Sim::getGhostAlwaysSet()->addObject(this);
  248. // add it to all Connections that already exist.
  249. SimGroup *clientGroup = Sim::getClientGroup();
  250. SimGroup::iterator i;
  251. for(i = clientGroup->begin(); i != clientGroup->end(); i++)
  252. {
  253. NetConnection *con = (NetConnection *) (*i);
  254. if(con->isGhosting())
  255. con->objectInScope(this);
  256. }
  257. }
  258. }
  259. void NetObject::clearScopeAlways()
  260. {
  261. if(!mNetFlags.test(IsGhost))
  262. {
  263. mNetFlags.clear(ScopeAlways);
  264. Sim::getGhostAlwaysSet()->removeObject(this);
  265. // Un ghost this object from all the connections
  266. while(mFirstObjectRef)
  267. mFirstObjectRef->connection->detachObject(mFirstObjectRef);
  268. }
  269. }
  270. bool NetObject::onAdd()
  271. {
  272. if(mNetFlags.test(ScopeAlways))
  273. setScopeAlways();
  274. return Parent::onAdd();
  275. }
  276. void NetObject::onRemove()
  277. {
  278. while(mFirstObjectRef)
  279. mFirstObjectRef->connection->detachObject(mFirstObjectRef);
  280. Parent::onRemove();
  281. }
  282. //-----------------------------------------------------------------------------
  283. F32 NetObject::getUpdatePriority(CameraScopeQuery*, U32, S32 updateSkips)
  284. {
  285. return F32(updateSkips) * 0.1;
  286. }
  287. U32 NetObject::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
  288. {
  289. return 0;
  290. }
  291. void NetObject::unpackUpdate(NetConnection*, BitStream*)
  292. {
  293. }
  294. void NetObject::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery* /*camInfo*/)
  295. {
  296. // default behavior -
  297. // ghost everything that is ghostable
  298. for (SimSetIterator obj(Sim::getRootGroup()); *obj; ++obj)
  299. {
  300. NetObject* nobj = dynamic_cast<NetObject*>(*obj);
  301. if (nobj)
  302. {
  303. AssertFatal(!nobj->mNetFlags.test(NetObject::Ghostable) || !nobj->mNetFlags.test(NetObject::IsGhost),
  304. "NetObject::onCameraScopeQuery: object marked both ghostable and as ghost");
  305. // Some objects don't ever want to be ghosted
  306. if (!nobj->mNetFlags.test(NetObject::Ghostable))
  307. continue;
  308. if (!nobj->mNetFlags.test(NetObject::ScopeAlways))
  309. {
  310. // it's in scope...
  311. cr->objectInScope(nobj);
  312. }
  313. }
  314. }
  315. }
  316. //-----------------------------------------------------------------------------
  317. void NetObject::initPersistFields()
  318. {
  319. docsURL;
  320. Parent::initPersistFields();
  321. }
  322. DefineEngineMethod( NetObject, getGhostID, S32, (),,
  323. "@brief Get the ghost index of this object from the server.\n\n"
  324. "@returns The ghost ID of this NetObject on the server\n"
  325. "@tsexample\n"
  326. "%ghostID = LocalClientConnection.getGhostId( %serverObject );\n"
  327. "@endtsexample\n\n")
  328. {
  329. return object->getNetIndex();
  330. }
  331. //ConsoleMethod( NetObject, getGhostID, S32, 2, 2, "")
  332. //{
  333. // return object->getNetIndex();
  334. //}
  335. DefineEngineMethod( NetObject, getClientObject, S32, (),,
  336. "@brief Returns a pointer to the client object when on a local connection.\n\n"
  337. "Short-Circuit-Networking: this is only valid for a local-client / singleplayer situation.\n\n"
  338. "@returns the SimObject ID of the client object.\n"
  339. "@tsexample\n"
  340. "// Psuedo-code, some values left out for this example\n"
  341. "%node = new ParticleEmitterNode(){};\n"
  342. "%clientObject = %node.getClientObject();\n"
  343. "if(isObject(%clientObject)\n"
  344. " %clientObject.setTransform(\"0 0 0\");\n\n"
  345. "@endtsexample\n\n"
  346. "@see @ref local_connections")
  347. {
  348. NetObject *obj = object->getClientObject();
  349. if ( obj )
  350. return obj->getId();
  351. return 0;
  352. }
  353. //ConsoleMethod( NetObject, getClientObject, S32, 2, 2, "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation." )
  354. //{
  355. // NetObject *obj = object->getClientObject();
  356. // if ( obj )
  357. // return obj->getId();
  358. //
  359. // return NULL;
  360. //}
  361. DefineEngineMethod( NetObject, getServerObject, S32, (),,
  362. "@brief Returns a pointer to the client object when on a local connection.\n\n"
  363. "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation.\n\n"
  364. "@returns The SimObject ID of the server object.\n"
  365. "@tsexample\n"
  366. "// Psuedo-code, some values left out for this example\n"
  367. "%node = new ParticleEmitterNode(){};\n"
  368. "%serverObject = %node.getServerObject();\n"
  369. "if(isObject(%serverObject)\n"
  370. " %serverObject.setTransform(\"0 0 0\");\n\n"
  371. "@endtsexample\n\n"
  372. "@see @ref local_connections")
  373. {
  374. NetObject *obj = object->getServerObject();
  375. if ( obj )
  376. return obj->getId();
  377. return 0;
  378. }
  379. //ConsoleMethod( NetObject, getServerObject, S32, 2, 2, "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation." )
  380. //{
  381. // NetObject *obj = object->getServerObject();
  382. // if ( obj )
  383. // return obj->getId();
  384. //
  385. // return NULL;
  386. //}
  387. DefineEngineMethod( NetObject, isClientObject, bool, (),,
  388. "@brief Called to check if an object resides on the clientside.\n\n"
  389. "@return True if the object resides on the client, false otherwise.")
  390. {
  391. return object->isClientObject();
  392. }
  393. //ConsoleMethod( NetObject, isClientObject, bool, 2, 2, "Return true for client-side objects." )
  394. //{
  395. // return object->isClientObject();
  396. //}
  397. DefineEngineMethod( NetObject, isServerObject, bool, (),,
  398. "@brief Checks if an object resides on the server.\n\n"
  399. "@return True if the object resides on the server, false otherwise.")
  400. {
  401. return object->isServerObject();
  402. }
  403. //ConsoleMethod( NetObject, isServerObject, bool, 2, 2, "Return true for client-side objects." )
  404. //{
  405. // return object->isServerObject();
  406. //}
  407. #ifdef TORQUE_AFX_ENABLED
  408. U16 NetObject::addScopeRef()
  409. {
  410. if (mScope_refs == 0)
  411. {
  412. mScope_id = arcaneFX::generateScopeId();
  413. onScopeIdChange();
  414. }
  415. mScope_refs++;
  416. return mScope_id;
  417. }
  418. void NetObject::removeScopeRef()
  419. {
  420. if (mScope_refs == 0)
  421. return;
  422. mScope_refs--;
  423. if (mScope_refs == 0)
  424. {
  425. mScope_id = 0;
  426. onScopeIdChange();
  427. }
  428. }
  429. #endif
  430. //Networked fields
  431. //------------------------------------------------------------------
  432. void NetObject::addNetworkedField(const char* in_pFieldname,
  433. const U32 in_fieldType,
  434. const dsize_t in_fieldOffset,
  435. const char* in_pFieldDocs,
  436. U32 flags,
  437. U32 networkMask)
  438. {
  439. addNetworkedField(
  440. in_pFieldname,
  441. in_fieldType,
  442. in_fieldOffset,
  443. 1,
  444. in_pFieldDocs,
  445. flags,
  446. networkMask);
  447. }
  448. void NetObject::addNetworkedField(const char* in_pFieldname,
  449. const U32 in_fieldType,
  450. const dsize_t in_fieldOffset,
  451. AbstractClassRep::WriteDataNotify in_writeDataFn,
  452. const char* in_pFieldDocs,
  453. U32 flags,
  454. U32 networkMask)
  455. {
  456. addNetworkedField(
  457. in_pFieldname,
  458. in_fieldType,
  459. in_fieldOffset,
  460. in_writeDataFn,
  461. 1,
  462. in_pFieldDocs,
  463. flags,
  464. networkMask);
  465. }
  466. void NetObject::addNetworkedField(const char* in_pFieldname,
  467. const U32 in_fieldType,
  468. const dsize_t in_fieldOffset,
  469. const U32 in_elementCount,
  470. const char* in_pFieldDocs,
  471. U32 flags,
  472. U32 networkMask)
  473. {
  474. addNetworkedField(in_pFieldname,
  475. in_fieldType,
  476. in_fieldOffset,
  477. &defaultProtectedWriteFn,
  478. in_elementCount,
  479. in_pFieldDocs,
  480. flags,
  481. networkMask);
  482. }
  483. void NetObject::addNetworkedField(const char* in_pFieldname,
  484. const U32 in_fieldType,
  485. const dsize_t in_fieldOffset,
  486. AbstractClassRep::WriteDataNotify in_writeDataFn,
  487. const U32 in_elementCount,
  488. const char* in_pFieldDocs,
  489. U32 flags,
  490. U32 networkMask)
  491. {
  492. AbstractClassRep::Field f;
  493. f.pFieldname = StringTable->insert(in_pFieldname);
  494. if (in_pFieldDocs)
  495. f.pFieldDocs = in_pFieldDocs;
  496. f.type = in_fieldType;
  497. f.offset = in_fieldOffset;
  498. f.elementCount = in_elementCount;
  499. f.validator = NULL;
  500. f.flag = flags;
  501. f.setDataFn = &defaultProtectedSetFn;
  502. f.getDataFn = &defaultProtectedGetFn;
  503. f.writeDataFn = in_writeDataFn;
  504. f.networkMask = networkMask;
  505. ConsoleBaseType* conType = ConsoleBaseType::getType(in_fieldType);
  506. AssertFatal(conType, "ConsoleObject::addField - invalid console type");
  507. f.table = conType->getEnumTable();
  508. sg_tempFieldList.push_back(f);
  509. }
  510. DefineEngineMethod(NetObject, clearScopeAlways, void, (), ,
  511. "@brief Clears the scope always flag on this object.\n\n")
  512. {
  513. object->clearScopeAlways();
  514. }