netObject.cpp 16 KB

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