netObject.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  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 "console/simBase.h"
  24. #include "core/dnet.h"
  25. #include "sim/netConnection.h"
  26. #include "sim/netObject.h"
  27. #include "console/consoleTypes.h"
  28. #include "console/engineAPI.h"
  29. IMPLEMENT_CONOBJECT(NetObject);
  30. // More information can be found in the Torque Manual (CHM)
  31. ConsoleDocClass( NetObject,
  32. "@brief Superclass for all ghostable networked objects.\n\n"
  33. "@ingroup Networking\n");
  34. //----------------------------------------------------------------------------
  35. NetObject *NetObject::mDirtyList = NULL;
  36. NetObject::NetObject()
  37. {
  38. // netFlags will clear itself to 0
  39. mNetIndex = U32(-1);
  40. mFirstObjectRef = NULL;
  41. mPrevDirtyList = NULL;
  42. mNextDirtyList = NULL;
  43. mDirtyMaskBits = 0;
  44. }
  45. NetObject::~NetObject()
  46. {
  47. if(mDirtyMaskBits)
  48. {
  49. if(mPrevDirtyList)
  50. mPrevDirtyList->mNextDirtyList = mNextDirtyList;
  51. else
  52. mDirtyList = mNextDirtyList;
  53. if(mNextDirtyList)
  54. mNextDirtyList->mPrevDirtyList = mPrevDirtyList;
  55. }
  56. }
  57. String NetObject::describeSelf() const
  58. {
  59. String desc = Parent::describeSelf();
  60. if( isClientObject() )
  61. desc += "|net: client";
  62. else
  63. desc += "|net: server";
  64. return desc;
  65. }
  66. void NetObject::setMaskBits(U32 orMask)
  67. {
  68. AssertFatal(orMask != 0, "Invalid net mask bits set.");
  69. AssertFatal(mDirtyMaskBits == 0 || (mPrevDirtyList != NULL || mNextDirtyList != NULL || mDirtyList == this), "Invalid dirty list state.");
  70. if(!mDirtyMaskBits)
  71. {
  72. AssertFatal(mNextDirtyList == NULL && mPrevDirtyList == NULL, "Object with zero mask already in list.");
  73. if(mDirtyList)
  74. {
  75. mNextDirtyList = mDirtyList;
  76. mDirtyList->mPrevDirtyList = this;
  77. }
  78. mDirtyList = this;
  79. }
  80. mDirtyMaskBits |= orMask;
  81. AssertFatal(mDirtyMaskBits == 0 || (mPrevDirtyList != NULL || mNextDirtyList != NULL || mDirtyList == this), "Invalid dirty list state.");
  82. }
  83. void NetObject::clearMaskBits(U32 orMask)
  84. {
  85. if(isDeleted())
  86. return;
  87. if(mDirtyMaskBits)
  88. {
  89. mDirtyMaskBits &= ~orMask;
  90. if(!mDirtyMaskBits)
  91. {
  92. if(mPrevDirtyList)
  93. mPrevDirtyList->mNextDirtyList = mNextDirtyList;
  94. else
  95. mDirtyList = mNextDirtyList;
  96. if(mNextDirtyList)
  97. mNextDirtyList->mPrevDirtyList = mPrevDirtyList;
  98. mNextDirtyList = mPrevDirtyList = NULL;
  99. }
  100. }
  101. for(GhostInfo *walk = mFirstObjectRef; walk; walk = walk->nextObjectRef)
  102. {
  103. if(walk->updateMask && walk->updateMask == orMask)
  104. {
  105. walk->updateMask = 0;
  106. walk->connection->ghostPushToZero(walk);
  107. }
  108. else
  109. walk->updateMask &= ~orMask;
  110. }
  111. }
  112. void NetObject::collapseDirtyList()
  113. {
  114. #ifdef TORQUE_DEBUG
  115. Vector<NetObject *> tempV;
  116. for(NetObject *t = mDirtyList; t; t = t->mNextDirtyList)
  117. tempV.push_back(t);
  118. #endif
  119. for(NetObject *obj = mDirtyList; obj; )
  120. {
  121. NetObject *next = obj->mNextDirtyList;
  122. U32 dirtyMask = obj->mDirtyMaskBits;
  123. obj->mNextDirtyList = NULL;
  124. obj->mPrevDirtyList = NULL;
  125. obj->mDirtyMaskBits = 0;
  126. if(!obj->isDeleted() && dirtyMask)
  127. {
  128. for(GhostInfo *walk = obj->mFirstObjectRef; walk; walk = walk->nextObjectRef)
  129. {
  130. U32 orMask = obj->filterMaskBits(dirtyMask,walk->connection);
  131. if(!walk->updateMask && orMask)
  132. {
  133. walk->updateMask = orMask;
  134. walk->connection->ghostPushNonZero(walk);
  135. }
  136. else
  137. walk->updateMask |= orMask;
  138. }
  139. }
  140. obj = next;
  141. }
  142. mDirtyList = NULL;
  143. #ifdef TORQUE_DEBUG
  144. for(U32 i = 0; i < tempV.size(); i++)
  145. {
  146. AssertFatal(tempV[i]->mNextDirtyList == NULL && tempV[i]->mPrevDirtyList == NULL && tempV[i]->mDirtyMaskBits == 0, "Error in collapse");
  147. }
  148. #endif
  149. }
  150. //-----------------------------------------------------------------------------
  151. DefineEngineMethod( NetObject, scopeToClient, void, ( NetConnection* client),,
  152. "@brief Cause the NetObject to be forced as scoped on the specified NetConnection.\n\n"
  153. "@param client The connection this object will always be scoped to\n\n"
  154. "@tsexample\n"
  155. "// Called to create new cameras in TorqueScript\n"
  156. "// %this - The active GameConnection\n"
  157. "// %spawnPoint - The spawn point location where we creat the camera\n"
  158. "function GameConnection::spawnCamera(%this, %spawnPoint)\n"
  159. "{\n"
  160. " // If this connection's camera exists\n"
  161. " if(isObject(%this.camera))\n"
  162. " {\n"
  163. " // Add it to the mission group to be cleaned up later\n"
  164. " MissionCleanup.add( %this.camera );\n\n"
  165. " // Force it to scope to the client side\n"
  166. " %this.camera.scopeToClient(%this);\n"
  167. " }\n"
  168. "}\n"
  169. "@endtsexample\n\n"
  170. "@see clearScopeToClient()\n")
  171. {
  172. if(!client)
  173. {
  174. Con::errorf(ConsoleLogEntry::General, "NetObject::scopeToClient: Couldn't find connection %s", client);
  175. return;
  176. }
  177. client->objectLocalScopeAlways(object);
  178. }
  179. //ConsoleMethod(NetObject,scopeToClient,void,3,3,"(NetConnection %client)"
  180. // "Cause the NetObject to be forced as scoped on the specified NetConnection.")
  181. //{
  182. // TORQUE_UNUSED(argc);
  183. // NetConnection *conn;
  184. // if(!Sim::findObject(argv[2], conn))
  185. // {
  186. // Con::errorf(ConsoleLogEntry::General, "NetObject::scopeToClient: Couldn't find connection %s", argv[2]);
  187. // return;
  188. // }
  189. // conn->objectLocalScopeAlways(object);
  190. //}
  191. DefineEngineMethod( NetObject, clearScopeToClient, void, ( NetConnection* client),,
  192. "@brief Undo the effects of a scopeToClient() call.\n\n"
  193. "@param client The connection to remove this object's scoping from \n\n"
  194. "@see scopeToClient()\n")
  195. {
  196. if(!client)
  197. {
  198. Con::errorf(ConsoleLogEntry::General, "NetObject::clearScopeToClient: Couldn't find connection %s", client);
  199. return;
  200. }
  201. client->objectLocalClearAlways(object);
  202. }
  203. //ConsoleMethod(NetObject,clearScopeToClient,void,3,3,"clearScopeToClient(%client)"
  204. // "Undo the effects of a scopeToClient() call.")
  205. //{
  206. // TORQUE_UNUSED(argc);
  207. // NetConnection *conn;
  208. // if(!Sim::findObject(argv[2], conn))
  209. // {
  210. // Con::errorf(ConsoleLogEntry::General, "NetObject::clearScopeToClient: Couldn't find connection %s", argv[2]);
  211. // return;
  212. // }
  213. // conn->objectLocalClearAlways(object);
  214. //}
  215. DefineEngineMethod( NetObject, setScopeAlways, void, (),,
  216. "@brief Always scope this object on all connections.\n\n"
  217. "The object is marked as ScopeAlways and is immediately ghosted to "
  218. "all active connections. This function has no effect if the object "
  219. "is not marked as Ghostable.\n\n")
  220. {
  221. object->setScopeAlways();
  222. }
  223. //ConsoleMethod(NetObject,setScopeAlways,void,2,2,"Always scope this object on all connections.")
  224. //{
  225. // TORQUE_UNUSED(argc); TORQUE_UNUSED(argv);
  226. // object->setScopeAlways();
  227. //}
  228. void NetObject::setScopeAlways()
  229. {
  230. if(mNetFlags.test(Ghostable) && !mNetFlags.test(IsGhost))
  231. {
  232. mNetFlags.set(ScopeAlways);
  233. // if it's a ghost always object, add it to the ghost always set
  234. // for ClientReps created later.
  235. Sim::getGhostAlwaysSet()->addObject(this);
  236. // add it to all Connections that already exist.
  237. SimGroup *clientGroup = Sim::getClientGroup();
  238. SimGroup::iterator i;
  239. for(i = clientGroup->begin(); i != clientGroup->end(); i++)
  240. {
  241. NetConnection *con = (NetConnection *) (*i);
  242. if(con->isGhosting())
  243. con->objectInScope(this);
  244. }
  245. }
  246. }
  247. void NetObject::clearScopeAlways()
  248. {
  249. if(!mNetFlags.test(IsGhost))
  250. {
  251. mNetFlags.clear(ScopeAlways);
  252. Sim::getGhostAlwaysSet()->removeObject(this);
  253. // Un ghost this object from all the connections
  254. while(mFirstObjectRef)
  255. mFirstObjectRef->connection->detachObject(mFirstObjectRef);
  256. }
  257. }
  258. bool NetObject::onAdd()
  259. {
  260. if(mNetFlags.test(ScopeAlways))
  261. setScopeAlways();
  262. return Parent::onAdd();
  263. }
  264. void NetObject::onRemove()
  265. {
  266. while(mFirstObjectRef)
  267. mFirstObjectRef->connection->detachObject(mFirstObjectRef);
  268. Parent::onRemove();
  269. }
  270. //-----------------------------------------------------------------------------
  271. F32 NetObject::getUpdatePriority(CameraScopeQuery*, U32, S32 updateSkips)
  272. {
  273. return F32(updateSkips) * 0.1;
  274. }
  275. U32 NetObject::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
  276. {
  277. return 0;
  278. }
  279. void NetObject::unpackUpdate(NetConnection*, BitStream*)
  280. {
  281. }
  282. void NetObject::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery* /*camInfo*/)
  283. {
  284. // default behavior -
  285. // ghost everything that is ghostable
  286. for (SimSetIterator obj(Sim::getRootGroup()); *obj; ++obj)
  287. {
  288. NetObject* nobj = dynamic_cast<NetObject*>(*obj);
  289. if (nobj)
  290. {
  291. AssertFatal(!nobj->mNetFlags.test(NetObject::Ghostable) || !nobj->mNetFlags.test(NetObject::IsGhost),
  292. "NetObject::onCameraScopeQuery: object marked both ghostable and as ghost");
  293. // Some objects don't ever want to be ghosted
  294. if (!nobj->mNetFlags.test(NetObject::Ghostable))
  295. continue;
  296. if (!nobj->mNetFlags.test(NetObject::ScopeAlways))
  297. {
  298. // it's in scope...
  299. cr->objectInScope(nobj);
  300. }
  301. }
  302. }
  303. }
  304. //-----------------------------------------------------------------------------
  305. void NetObject::initPersistFields()
  306. {
  307. Parent::initPersistFields();
  308. }
  309. DefineEngineMethod( NetObject, getGhostID, S32, (),,
  310. "@brief Get the ghost index of this object from the server.\n\n"
  311. "@returns The ghost ID of this NetObject on the server\n"
  312. "@tsexample\n"
  313. "%ghostID = LocalClientConnection.getGhostId( %serverObject );\n"
  314. "@endtsexample\n\n")
  315. {
  316. return object->getNetIndex();
  317. }
  318. //ConsoleMethod( NetObject, getGhostID, S32, 2, 2, "")
  319. //{
  320. // return object->getNetIndex();
  321. //}
  322. DefineEngineMethod( NetObject, getClientObject, S32, (),,
  323. "@brief Returns a pointer to the client object when on a local connection.\n\n"
  324. "Short-Circuit-Networking: this is only valid for a local-client / singleplayer situation.\n\n"
  325. "@returns the SimObject ID of the client object.\n"
  326. "@tsexample\n"
  327. "// Psuedo-code, some values left out for this example\n"
  328. "%node = new ParticleEmitterNode(){};\n"
  329. "%clientObject = %node.getClientObject();\n"
  330. "if(isObject(%clientObject)\n"
  331. " %clientObject.setTransform(\"0 0 0\");\n\n"
  332. "@endtsexample\n\n"
  333. "@see @ref local_connections")
  334. {
  335. NetObject *obj = object->getClientObject();
  336. if ( obj )
  337. return obj->getId();
  338. return NULL;
  339. }
  340. //ConsoleMethod( NetObject, getClientObject, S32, 2, 2, "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation." )
  341. //{
  342. // NetObject *obj = object->getClientObject();
  343. // if ( obj )
  344. // return obj->getId();
  345. //
  346. // return NULL;
  347. //}
  348. DefineEngineMethod( NetObject, getServerObject, S32, (),,
  349. "@brief Returns a pointer to the client object when on a local connection.\n\n"
  350. "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation.\n\n"
  351. "@returns The SimObject ID of the server object.\n"
  352. "@tsexample\n"
  353. "// Psuedo-code, some values left out for this example\n"
  354. "%node = new ParticleEmitterNode(){};\n"
  355. "%serverObject = %node.getServerObject();\n"
  356. "if(isObject(%serverObject)\n"
  357. " %serverObject.setTransform(\"0 0 0\");\n\n"
  358. "@endtsexample\n\n"
  359. "@see @ref local_connections")
  360. {
  361. NetObject *obj = object->getServerObject();
  362. if ( obj )
  363. return obj->getId();
  364. return NULL;
  365. }
  366. //ConsoleMethod( NetObject, getServerObject, S32, 2, 2, "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation." )
  367. //{
  368. // NetObject *obj = object->getServerObject();
  369. // if ( obj )
  370. // return obj->getId();
  371. //
  372. // return NULL;
  373. //}
  374. DefineEngineMethod( NetObject, isClientObject, bool, (),,
  375. "@brief Called to check if an object resides on the clientside.\n\n"
  376. "@return True if the object resides on the client, false otherwise.")
  377. {
  378. return object->isClientObject();
  379. }
  380. //ConsoleMethod( NetObject, isClientObject, bool, 2, 2, "Return true for client-side objects." )
  381. //{
  382. // return object->isClientObject();
  383. //}
  384. DefineEngineMethod( NetObject, isServerObject, bool, (),,
  385. "@brief Checks if an object resides on the server.\n\n"
  386. "@return True if the object resides on the server, false otherwise.")
  387. {
  388. return object->isServerObject();
  389. }
  390. //ConsoleMethod( NetObject, isServerObject, bool, 2, 2, "Return true for client-side objects." )
  391. //{
  392. // return object->isServerObject();
  393. //}