netObject.h

Engine/source/sim/netObject.h

More...

Classes:

class

Superclass for ghostable networked objects.

Detailed Description

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