netObject.h 20 KB

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