netObject.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  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. #ifndef _NETOBJECT_H_
  23. #define _NETOBJECT_H_
  24. #ifndef _SIMBASE_H_
  25. #include "console/simBase.h"
  26. #endif
  27. #ifndef _MMATH_H_
  28. #include "math/mMath.h"
  29. #endif
  30. //-----------------------------------------------------------------------------
  31. class NetConnection;
  32. class NetObject;
  33. //-----------------------------------------------------------------------------
  34. struct CameraScopeQuery
  35. {
  36. NetObject *camera; ///< Pointer to the viewing object.
  37. Point3F pos; ///< Position in world space
  38. Point3F orientation; ///< Viewing vector in world space
  39. F32 fov; ///< Viewing angle/2
  40. F32 sinFov; ///< sin(fov/2);
  41. F32 cosFov; ///< cos(fov/2);
  42. F32 visibleDistance; ///< Visible distance.
  43. };
  44. struct GhostInfo;
  45. //-----------------------------------------------------------------------------
  46. /// Superclass for ghostable networked objects.
  47. ///
  48. /// @section NetObject_intro Introduction To NetObject And Ghosting
  49. ///
  50. /// One of the most powerful aspects of Torque's networking code is its support
  51. /// for ghosting and prioritized, most-recent-state network updates. The way
  52. /// this works is a bit complex, but it is immensely efficient. Let's run
  53. /// through the steps that the server goes through for each client in this part
  54. /// of Torque's networking:
  55. /// - First, the server determines what objects are in-scope for the client.
  56. /// This is done by calling onCameraScopeQuery() on the object which is
  57. /// considered the "scope" object. This is usually the player object, but
  58. /// it can be something else. (For instance, the current vehicle, or a
  59. /// object we're remote controlling.)
  60. /// - Second, it ghosts them to the client; this is implemented in netGhost.cc.
  61. /// - Finally, it sends updates as needed, by checking the dirty list and packing
  62. /// updates.
  63. ///
  64. /// There several significant advantages to using this networking system:
  65. /// - Efficient network usage, since we only send data that has changed. In addition,
  66. /// since we only care about most-recent data, if a packet is dropped, we don't waste
  67. /// effort trying to deliver stale data.
  68. /// - Cheating protection; since we don't deliver information about game objects which
  69. /// aren't in scope, we dramatically reduce the ability of clients to hack the game and
  70. /// gain a meaningful advantage. (For instance, they can't find out about things behind
  71. /// them, since objects behind them don't fall in scope.) In addition, since ghost IDs are
  72. /// assigned per-client, it's difficult for any sort of co-ordination between cheaters to
  73. /// occur.
  74. ///
  75. /// NetConnection contains the Ghost Manager implementation, which deals with transferring data to
  76. /// the appropriate clients and keeping state in synch.
  77. ///
  78. /// @section NetObject_Implementation An Example Implementation
  79. ///
  80. /// The basis of the ghost implementation in Torque is NetObject. It tracks the dirty flags for the
  81. /// various states that the object trackers, and does some other book-keeping to allow more efficient
  82. /// operation of the networking layer.
  83. ///
  84. /// Using a NetObject is very simple; let's go through a simple example implementation:
  85. ///
  86. /// @code
  87. /// class SimpleNetObject : public NetObject
  88. /// {
  89. /// public:
  90. /// typedef NetObject Parent;
  91. /// DECLARE_CONOBJECT(SimpleNetObject);
  92. /// @endcode
  93. ///
  94. /// Above is the standard boilerplate code for a Torque class. You can find out more about this in SimObject.
  95. ///
  96. /// @code
  97. /// char message1[256];
  98. /// char message2[256];
  99. /// enum States {
  100. /// Message1Mask = BIT(0),
  101. /// Message2Mask = BIT(1),
  102. /// };
  103. /// @endcode
  104. ///
  105. /// For our example, we're having two "states" that we keep track of, message1 and message2. In a real
  106. /// object, we might map our states to health and position, or some other set of fields. You have 32
  107. /// bits to work with, so it's possible to be very specific when defining states. In general, you
  108. /// should try to use as few states as possible (you never know when you'll need to expand your object's
  109. /// functionality!), and in fact, most of your fields will end up changing all at once, so it's not worth
  110. /// it to be too fine-grained. (As an example, position and velocity on Player are controlled by the same
  111. /// bit, as one rarely changes without the other changing, too.)
  112. ///
  113. /// @code
  114. /// SimpleNetObject()
  115. /// {
  116. /// // in order for an object to be considered by the network system,
  117. /// // the Ghostable net flag must be set.
  118. /// // the ScopeAlways flag indicates that the object is always scoped
  119. /// // on all active connections.
  120. /// mNetFlags.set(ScopeAlways | Ghostable);
  121. /// dStrcpy(message1, "Hello World 1!");
  122. /// dStrcpy(message2, "Hello World 2!");
  123. /// }
  124. /// @endcode
  125. ///
  126. /// Here is the constructor. Here, you see that we initialize our net flags to show that
  127. /// we should always be scoped, and that we're to be taken into consideration for ghosting. We
  128. /// also provide some initial values for the message fields.
  129. ///
  130. /// @code
  131. /// U32 packUpdate(NetConnection *, U32 mask, BitStream *stream)
  132. /// {
  133. /// // check which states need to be updated, and update them
  134. /// if(stream->writeFlag(mask & Message1Mask))
  135. /// stream->writeString(message1);
  136. /// if(stream->writeFlag(mask & Message2Mask))
  137. /// stream->writeString(message2);
  138. ///
  139. /// // the return value from packUpdate can set which states still
  140. /// // need to be updated for this object.
  141. /// return 0;
  142. /// }
  143. /// @endcode
  144. ///
  145. /// Here's half of the meat of the networking code, the packUpdate() function. (The other half, unpackUpdate(),
  146. /// we'll get to in a second.) The comments in the code pretty much explain everything, however, notice that the
  147. /// code follows a pattern of if(writeFlag(mask & StateMask)) { ... write data ... }. The packUpdate()/unpackUpdate()
  148. /// functions are responsible for reading and writing the dirty bits to the bitstream by themselves.
  149. ///
  150. /// @code
  151. /// void unpackUpdate(NetConnection *, BitStream *stream)
  152. /// {
  153. /// // the unpackUpdate function must be symmetrical to packUpdate
  154. /// if(stream->readFlag())
  155. /// {
  156. /// stream->readString(message1);
  157. /// Con::printf("Got message1: %s", message1);
  158. /// }
  159. /// if(stream->readFlag())
  160. /// {
  161. /// stream->readString(message2);
  162. /// Con::printf("Got message2: %s", message2);
  163. /// }
  164. /// }
  165. /// @endcode
  166. ///
  167. /// The other half of the networking code in any NetObject, unpackUpdate(). In our simple example, all that
  168. /// the code does is print the new messages to the console; however, in a more advanced object, you might
  169. /// trigger animations, update complex object properties, or even spawn new objects, based on what packet
  170. /// data you unpack.
  171. ///
  172. /// @code
  173. /// void setMessage1(const char *msg)
  174. /// {
  175. /// setMaskBits(Message1Mask);
  176. /// dStrcpy(message1, msg);
  177. /// }
  178. /// void setMessage2(const char *msg)
  179. /// {
  180. /// setMaskBits(Message2Mask);
  181. /// dStrcpy(message2, msg);
  182. /// }
  183. /// @endcode
  184. ///
  185. /// Here are the accessors for the two properties. It is good to encapsulate your state
  186. /// variables, so that you don't have to remember to make a call to setMaskBits every time you change
  187. /// anything; the accessors can do it for you. In a more complex object, you might need to set
  188. /// multiple mask bits when you change something; this can be done using the | operator, for instance,
  189. /// setMaskBits( Message1Mask | Message2Mask ); if you changed both messages.
  190. ///
  191. /// @code
  192. /// IMPLEMENT_CO_NETOBJECT_V1(SimpleNetObject);
  193. ///
  194. /// ConsoleMethod(SimpleNetObject, setMessage1, void, 3, 3, "(string msg) Set message 1.")
  195. /// {
  196. /// object->setMessage1(argv[2]);
  197. /// }
  198. ///
  199. /// ConsoleMethod(SimpleNetObject, setMessage2, void, 3, 3, "(string msg) Set message 2.")
  200. /// {
  201. /// object->setMessage2(argv[2]);
  202. /// }
  203. /// @endcode
  204. ///
  205. /// Finally, we use the NetObject implementation macro, IMPLEMENT_CO_NETOBJECT_V1(), to implement our
  206. /// NetObject. It is important that we use this, as it makes Torque perform certain initialization tasks
  207. /// that allow us to send the object over the network. IMPLEMENT_CONOBJECT() doesn't perform these tasks, see
  208. /// the documentation on AbstractClassRep for more details.
  209. ///
  210. /// @nosubgrouping
  211. class NetObject : public SimGroup
  212. {
  213. // The Ghost Manager needs read/write access
  214. friend class NetConnection;
  215. friend struct GhostInfo;
  216. friend class ProcessList;
  217. // Not the best way to do this, but the event needs access to mNetFlags
  218. friend class GhostAlwaysObjectEvent;
  219. private:
  220. typedef SimGroup Parent;
  221. /// Mask indicating which states are dirty and need to be retransmitted on this
  222. /// object.
  223. U32 mDirtyMaskBits;
  224. /// @name Dirty List
  225. ///
  226. /// Whenever a NetObject becomes "dirty", we add it to the dirty list.
  227. /// We also remove ourselves on the destructor.
  228. ///
  229. /// This is done so that when we want to send updates (in NetConnection),
  230. /// it's very fast to find the objects that need to be updated.
  231. /// @{
  232. /// Static pointer to the head of the dirty NetObject list.
  233. static NetObject *mDirtyList;
  234. /// Next item in the dirty list...
  235. NetObject *mPrevDirtyList;
  236. /// Previous item in the dirty list...
  237. NetObject *mNextDirtyList;
  238. /// @}
  239. protected:
  240. /// Pointer to the server object on a local connection.
  241. /// @see getServerObject
  242. SimObjectPtr<NetObject> mServerObject;
  243. /// Pointer to the client object on a local connection.
  244. /// @see getClientObject
  245. SimObjectPtr<NetObject> mClientObject;
  246. enum NetFlags
  247. {
  248. IsGhost = BIT(1), ///< This is a ghost.
  249. ScopeAlways = BIT(6), ///< Object always ghosts to clients.
  250. ScopeLocal = BIT(7), ///< Ghost only to local client.
  251. Ghostable = BIT(8), ///< Set if this object CAN ghost.
  252. MaxNetFlagBit = 15
  253. };
  254. BitSet32 mNetFlags; ///< Flag values from NetFlags
  255. U32 mNetIndex; ///< The index of this ghost in the GhostManager on the server.
  256. GhostInfo *mFirstObjectRef; ///< Head of a linked list storing GhostInfos referencing this NetObject.
  257. public:
  258. NetObject();
  259. ~NetObject();
  260. virtual String describeSelf() const;
  261. /// @name Miscellaneous
  262. /// @{
  263. DECLARE_CONOBJECT(NetObject);
  264. static void initPersistFields();
  265. bool onAdd();
  266. void onRemove();
  267. /// @}
  268. static void collapseDirtyList();
  269. /// Used to mark a bit as dirty; ie, that its corresponding set of fields need to be transmitted next update.
  270. ///
  271. /// @param orMask Bit(s) to set
  272. virtual void setMaskBits(U32 orMask);
  273. /// Clear the specified bits from the dirty mask.
  274. ///
  275. /// @param orMask Bits to clear
  276. virtual void clearMaskBits(U32 orMask);
  277. virtual U32 filterMaskBits(U32 mask, NetConnection * connection) { return mask; }
  278. /// Scope the object to all connections.
  279. ///
  280. /// The object is marked as ScopeAlways and is immediately ghosted to
  281. /// all active connections. This function has no effect if the object
  282. /// is not marked as Ghostable.
  283. void setScopeAlways();
  284. /// Stop scoping the object to all connections.
  285. ///
  286. /// The object's ScopeAlways flag is cleared and the object is removed from
  287. /// all current active connections.
  288. void clearScopeAlways();
  289. /// This returns a value which is used to prioritize which objects need to be updated.
  290. ///
  291. /// In NetObject, our returned priority is 0.1 * updateSkips, so that less recently
  292. /// updated objects are more likely to be updated.
  293. ///
  294. /// In subclasses, this can be adjusted. For instance, ShapeBase provides priority
  295. /// based on proximity to the camera.
  296. ///
  297. /// @param focusObject Information from a previous call to onCameraScopeQuery.
  298. /// @param updateMask Current update mask.
  299. /// @param updateSkips Number of ticks we haven't been updated for.
  300. /// @returns A floating point value indicating priority. These are typically < 5.0.
  301. virtual F32 getUpdatePriority(CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips);
  302. /// Instructs this object to pack its state for transfer over the network.
  303. ///
  304. /// @param conn Net connection being used
  305. /// @param mask Mask indicating fields to transmit.
  306. /// @param stream Bitstream to pack data to
  307. ///
  308. /// @returns Any bits which were not dealt with. The value is stored by the networking
  309. /// system. Don't set bits you weren't passed.
  310. virtual U32 packUpdate(NetConnection * conn, U32 mask, BitStream *stream);
  311. /// Instructs this object to read state data previously packed with packUpdate.
  312. ///
  313. /// @param conn Net connection being used
  314. /// @param stream stream to read from
  315. virtual void unpackUpdate(NetConnection * conn, BitStream *stream);
  316. /// Queries the object about information used to determine scope.
  317. ///
  318. /// Something that is 'in scope' is somehow interesting to the client.
  319. ///
  320. /// If we are a NetConnection's scope object, it calls this method to determine
  321. /// how things should be scoped; basically, we tell it our field of view with camInfo,
  322. /// and have the opportunity to manually mark items as "in scope" as we see fit.
  323. ///
  324. /// By default, we just mark all ghostable objects as in scope.
  325. ///
  326. /// @param cr Net connection requesting scope information.
  327. /// @param camInfo Information about what this object can see.
  328. virtual void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *camInfo);
  329. /// Get the ghost index of this object.
  330. U32 getNetIndex() { return mNetIndex; }
  331. bool isServerObject() const; ///< Is this a server object?
  332. bool isClientObject() const; ///< Is this a client object?
  333. bool isGhost() const; ///< Is this is a ghost?
  334. bool isScopeLocal() const; ///< Should this object only be visible to the client which created it?
  335. bool isScopeable() const; ///< Is this object subject to scoping?
  336. bool isGhostable() const; ///< Is this object ghostable?
  337. bool isGhostAlways() const; ///< Should this object always be ghosted?
  338. /// @name Short-Circuited Networking
  339. ///
  340. /// When we are running with client and server on the same system (which can happen be either
  341. /// when we are doing a single player game, or if we're hosting a multiplayer game and having
  342. /// someone playing on the same instance), we can do some short circuited code to enhance
  343. /// performance.
  344. ///
  345. /// These variables are used to make it simpler; if we are running in short-circuited mode,
  346. /// the ghosted client gets the server object while the server gets the client object.
  347. ///
  348. /// @note "Premature optimization is the root of all evil" - Donald Knuth. The current codebase
  349. /// uses this feature in three small places, mostly for non-speed-related purposes.
  350. ///
  351. /// @{
  352. /// Returns a pointer to the server object when on a local connection.
  353. NetObject* getServerObject() const { return mServerObject; }
  354. /// Returns a pointer to the client object when on a local connection.
  355. NetObject* getClientObject() const { return mClientObject; }
  356. /// Template form for the callers convenience.
  357. template < class T >
  358. static T* getServerObject( T *netObj ) { return static_cast<T*>( netObj->getServerObject() ); }
  359. /// Template form for the callers convenience.
  360. template < class T >
  361. static T* getClientObject( T *netObj ) { return static_cast<T*>( netObj->getClientObject() ); }
  362. /// @}
  363. };
  364. //-----------------------------------------------------------------------------
  365. inline bool NetObject::isGhost() const
  366. {
  367. return mNetFlags.test(IsGhost);
  368. }
  369. inline bool NetObject::isClientObject() const
  370. {
  371. return mNetFlags.test(IsGhost);
  372. }
  373. inline bool NetObject::isServerObject() const
  374. {
  375. return !mNetFlags.test(IsGhost);
  376. }
  377. inline bool NetObject::isScopeLocal() const
  378. {
  379. return mNetFlags.test(ScopeLocal);
  380. }
  381. inline bool NetObject::isScopeable() const
  382. {
  383. return mNetFlags.test(Ghostable) && !mNetFlags.test(ScopeAlways);
  384. }
  385. inline bool NetObject::isGhostable() const
  386. {
  387. return mNetFlags.test(Ghostable);
  388. }
  389. inline bool NetObject::isGhostAlways() const
  390. {
  391. AssertFatal(mNetFlags.test(Ghostable) || mNetFlags.test(ScopeAlways) == false,
  392. "That's strange, a ScopeAlways non-ghostable object? Something wrong here");
  393. return mNetFlags.test(Ghostable) && mNetFlags.test(ScopeAlways);
  394. }
  395. #endif