| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 | //-----------------------------------------------------------------------------// Copyright (c) 2012 GarageGames, LLC//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to// deal in the Software without restriction, including without limitation the// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or// sell copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS// IN THE SOFTWARE.//-----------------------------------------------------------------------------//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames// Copyright (C) 2015 Faust Logic, Inc.//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//#ifndef _NETOBJECT_H_#define _NETOBJECT_H_#ifndef _SIMBASE_H_#include "console/simBase.h"#endif#ifndef _MMATH_H_#include "math/mMath.h"#endif//-----------------------------------------------------------------------------class NetConnection;class NetObject;//-----------------------------------------------------------------------------struct CameraScopeQuery{   NetObject *camera;       ///< Pointer to the viewing object.   Point3F pos;             ///< Position in world space   Point3F orientation;     ///< Viewing vector in world space   F32 fov;                 ///< Viewing angle/2   F32 sinFov;              ///< sin(fov/2);   F32 cosFov;              ///< cos(fov/2);   F32 visibleDistance;     ///< Visible distance.};struct GhostInfo;//-----------------------------------------------------------------------------/// Superclass for ghostable networked objects.////// @section NetObject_intro Introduction To NetObject And Ghosting////// One of the most powerful aspects of Torque's networking code is its support/// for ghosting and prioritized, most-recent-state network updates. The way/// this works is a bit complex, but it is immensely efficient. Let's run/// through the steps that the server goes through for each client in this part/// of Torque's networking:///      - First, the server determines what objects are in-scope for the client.///        This is done by calling onCameraScopeQuery() on the object which is///        considered the "scope" object. This is usually the player object, but///        it can be something else. (For instance, the current vehicle, or a///        object we're remote controlling.)///      - Second, it ghosts them to the client; this is implemented in netGhost.cc.///      - Finally, it sends updates as needed, by checking the dirty list and packing///        updates.////// There several significant advantages to using this networking system:///      - Efficient network usage, since we only send data that has changed. In addition,///        since we only care about most-recent data, if a packet is dropped, we don't waste///        effort trying to deliver stale data.///      - Cheating protection; since we don't deliver information about game objects which///        aren't in scope, we dramatically reduce the ability of clients to hack the game and///        gain a meaningful advantage. (For instance, they can't find out about things behind///        them, since objects behind them don't fall in scope.) In addition, since ghost IDs are///        assigned per-client, it's difficult for any sort of co-ordination between cheaters to///        occur.////// NetConnection contains the Ghost Manager implementation, which deals with transferring data to/// the appropriate clients and keeping state in synch.////// @section NetObject_Implementation An Example Implementation////// The basis of the ghost implementation in Torque is NetObject. It tracks the dirty flags for the/// various states that the object trackers, and does some other book-keeping to allow more efficient/// operation of the networking layer.////// Using a NetObject is very simple; let's go through a simple example implementation:////// @code/// class SimpleNetObject : public NetObject/// {/// public:///   typedef NetObject Parent;///   DECLARE_CONOBJECT(SimpleNetObject);/// @endcode////// Above is the standard boilerplate code for a Torque class. You can find out more about this in SimObject.////// @code///    char message1[256];///    char message2[256];///    enum States {///       Message1Mask = BIT(0),///       Message2Mask = BIT(1),///    };/// @endcode////// For our example, we're having two "states" that we keep track of, message1 and message2. In a real/// object, we might map our states to health and position, or some other set of fields. You have 32/// bits to work with, so it's possible to be very specific when defining states. In general, you/// should try to use as few states as possible (you never know when you'll need to expand your object's/// functionality!), and in fact, most of your fields will end up changing all at once, so it's not worth/// it to be too fine-grained. (As an example, position and velocity on Player are controlled by the same/// bit, as one rarely changes without the other changing, too.)////// @code///    SimpleNetObject()///    {///       // in order for an object to be considered by the network system,///       // the Ghostable net flag must be set.///       // the ScopeAlways flag indicates that the object is always scoped///       // on all active connections.///       mNetFlags.set(ScopeAlways | Ghostable);///       dStrcpy(message1, "Hello World 1!", bufLen);///       dStrcpy(message2, "Hello World 2!", bufLen);///    }/// @endcode////// Here is the constructor. Here, you see that we initialize our net flags to show that/// we should always be scoped, and that we're to be taken into consideration for ghosting. We/// also provide some initial values for the message fields.////// @code///    U32 packUpdate(NetConnection *, U32 mask, BitStream *stream)///    {///       // check which states need to be updated, and update them///       if(stream->writeFlag(mask & Message1Mask))///          stream->writeString(message1);///       if(stream->writeFlag(mask & Message2Mask))///          stream->writeString(message2);//////       // the return value from packUpdate can set which states still///       // need to be updated for this object.///       return 0;///    }/// @endcode////// Here's half of the meat of the networking code, the packUpdate() function. (The other half, unpackUpdate(),/// we'll get to in a second.) The comments in the code pretty much explain everything, however, notice that the/// code follows a pattern of if(writeFlag(mask & StateMask)) { ... write data ... }. The packUpdate()/unpackUpdate()/// functions are responsible for reading and writing the dirty bits to the bitstream by themselves.////// @code///    void unpackUpdate(NetConnection *, BitStream *stream)///    {///       // the unpackUpdate function must be symmetrical to packUpdate///       if(stream->readFlag())///       {///          stream->readString(message1);///          Con::printf("Got message1: %s", message1);///       }///       if(stream->readFlag())///       {///          stream->readString(message2);///          Con::printf("Got message2: %s", message2);///       }///    }/// @endcode////// The other half of the networking code in any NetObject, unpackUpdate(). In our simple example, all that/// the code does is print the new messages to the console; however, in a more advanced object, you might/// trigger animations, update complex object properties, or even spawn new objects, based on what packet/// data you unpack.////// @code///    void setMessage1(const char *msg)///    {///       setMaskBits(Message1Mask);///       dStrcpy(message1, msg, bufLen);///    }///    void setMessage2(const char *msg)///    {///       setMaskBits(Message2Mask);///       dStrcpy(message2, msg, bufLen);///    }/// @endcode////// Here are the accessors for the two properties. It is good to encapsulate your state/// variables, so that you don't have to remember to make a call to setMaskBits every time you change/// anything; the accessors can do it for you. In a more complex object, you might need to set/// multiple mask bits when you change something; this can be done using the | operator, for instance,/// setMaskBits( Message1Mask | Message2Mask ); if you changed both messages.////// @code/// IMPLEMENT_CO_NETOBJECT_V1(SimpleNetObject);////// ConsoleMethod(SimpleNetObject, setMessage1, void, 3, 3, "(string msg) Set message 1.")/// {///    object->setMessage1(argv[2]);/// }////// ConsoleMethod(SimpleNetObject, setMessage2, void, 3, 3, "(string msg) Set message 2.")/// {///    object->setMessage2(argv[2]);/// }/// @endcode////// Finally, we use the NetObject implementation macro, IMPLEMENT_CO_NETOBJECT_V1(), to implement our/// NetObject. It is important that we use this, as it makes Torque perform certain initialization tasks/// that allow us to send the object over the network. IMPLEMENT_CONOBJECT() doesn't perform these tasks, see/// the documentation on AbstractClassRep for more details.////// @nosubgroupingclass NetObject : public SimGroup{   // The Ghost Manager needs read/write access   friend class  NetConnection;   friend struct GhostInfo;   friend class  ProcessList;   // Not the best way to do this, but the event needs access to mNetFlags   friend class GhostAlwaysObjectEvent;private:   typedef SimGroup Parent;   /// Mask indicating which states are dirty and need to be retransmitted on this   /// object.   U32 mDirtyMaskBits;   /// @name Dirty List   ///   /// Whenever a NetObject becomes "dirty", we add it to the dirty list.   /// We also remove ourselves on the destructor.   ///   /// This is done so that when we want to send updates (in NetConnection),   /// it's very fast to find the objects that need to be updated.   /// @{   /// Static pointer to the head of the dirty NetObject list.   static NetObject *mDirtyList;   /// Next item in the dirty list...   NetObject *mPrevDirtyList;   /// Previous item in the dirty list...   NetObject *mNextDirtyList;   /// @}protected:   /// Pointer to the server object on a local connection.   /// @see getServerObject   SimObjectPtr<NetObject> mServerObject;   /// Pointer to the client object on a local connection.   /// @see getClientObject   SimObjectPtr<NetObject> mClientObject;   enum NetFlags   {      IsGhost           =  BIT(1),   ///< This is a ghost.      ScopeAlways       =  BIT(6),  ///< Object always ghosts to clients.      ScopeLocal        =  BIT(7),  ///< Ghost only to local client.      Ghostable         =  BIT(8),  ///< Set if this object CAN ghost.      MaxNetFlagBit     =  15   };   BitSet32 mNetFlags;              ///< Flag values from NetFlags   U32 mNetIndex;                   ///< The index of this ghost in the GhostManager on the server.   GhostInfo *mFirstObjectRef;      ///< Head of a linked list storing GhostInfos referencing this NetObject.public:   NetObject();   ~NetObject();   String describeSelf() const override;   /// @name Miscellaneous   /// @{   DECLARE_CONOBJECT(NetObject);   static void initPersistFields();   bool onAdd() override;   void onRemove() override;   /// @}   static void collapseDirtyList();   /// Used to mark a bit as dirty; ie, that its corresponding set of fields need to be transmitted next update.   ///   /// @param   orMask   Bit(s) to set   virtual void setMaskBits(U32 orMask);   /// Clear the specified bits from the dirty mask.   ///   /// @param   orMask   Bits to clear   virtual void clearMaskBits(U32 orMask);   virtual U32 filterMaskBits(U32 mask, NetConnection * connection) { return mask; }   ///  Scope the object to all connections.   ///   ///  The object is marked as ScopeAlways and is immediately ghosted to   ///  all active connections.  This function has no effect if the object   ///  is not marked as Ghostable.   void setScopeAlways();   /// Stop scoping the object to all connections.   ///   /// The object's ScopeAlways flag is cleared and the object is removed from   /// all current active connections.   void clearScopeAlways();   /// This returns a value which is used to prioritize which objects need to be updated.   ///   /// In NetObject, our returned priority is 0.1 * updateSkips, so that less recently   /// updated objects are more likely to be updated.   ///   /// In subclasses, this can be adjusted. For instance, ShapeBase provides priority   /// based on proximity to the camera.   ///   /// @param  focusObject    Information from a previous call to onCameraScopeQuery.   /// @param  updateMask     Current update mask.   /// @param  updateSkips    Number of ticks we haven't been updated for.   /// @returns A floating point value indicating priority. These are typically < 5.0.   virtual F32 getUpdatePriority(CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips);   /// Instructs this object to pack its state for transfer over the network.   ///   /// @param   conn    Net connection being used   /// @param   mask    Mask indicating fields to transmit.   /// @param   stream  Bitstream to pack data to   ///   /// @returns Any bits which were not dealt with. The value is stored by the networking   ///          system. Don't set bits you weren't passed.   virtual U32  packUpdate(NetConnection * conn, U32 mask, BitStream *stream);   /// Instructs this object to read state data previously packed with packUpdate.   ///   /// @param   conn    Net connection being used   /// @param   stream  stream to read from   virtual void unpackUpdate(NetConnection * conn, BitStream *stream);   /// Queries the object about information used to determine scope.   ///   /// Something that is 'in scope' is somehow interesting to the client.   ///   /// If we are a NetConnection's scope object, it calls this method to determine   /// how things should be scoped; basically, we tell it our field of view with camInfo,   /// and have the opportunity to manually mark items as "in scope" as we see fit.   ///   /// By default, we just mark all ghostable objects as in scope.   ///   /// @param   cr         Net connection requesting scope information.   /// @param   camInfo    Information about what this object can see.   virtual void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *camInfo);   /// Get the ghost index of this object.   U32 getNetIndex() { return mNetIndex; }   bool isServerObject() const;  ///< Is this a server object?   bool isClientObject() const;  ///< Is this a client object?   bool isGhost() const;         ///< Is this is a ghost?   bool isScopeLocal() const;    ///< Should this object only be visible to the client which created it?   bool isScopeable() const;     ///< Is this object subject to scoping?   bool isGhostable() const;     ///< Is this object ghostable?   bool isGhostAlways() const;   ///< Should this object always be ghosted?   /// @name Short-Circuited Networking   ///   /// When we are running with client and server on the same system (which can happen be either   /// when we are doing a single player game, or if we're hosting a multiplayer game and having   /// someone playing on the same instance), we can do some short circuited code to enhance   /// performance.   ///   /// These variables are used to make it simpler; if we are running in short-circuited mode,    /// the ghosted client gets the server object while the server gets the client object.   ///   /// @note "Premature optimization is the root of all evil" - Donald Knuth. The current codebase   ///       uses this feature in three small places, mostly for non-speed-related purposes.   ///   /// @{      /// Returns a pointer to the server object when on a local connection.   NetObject* getServerObject() const { return mServerObject; }   /// Returns a pointer to the client object when on a local connection.   NetObject* getClientObject() const { return mClientObject; }      /// Template form for the callers convenience.   template < class T >   static T* getServerObject( T *netObj ) { return static_cast<T*>( netObj->getServerObject() ); }      /// Template form for the callers convenience.   template < class T >   static T* getClientObject( T *netObj ) { return static_cast<T*>( netObj->getClientObject() ); }   /// @}protected:   U16           mScope_id;   U16           mScope_refs;   bool          mScope_registered;   virtual void  onScopeIdChange() { }public:   enum { SCOPE_ID_BITS = 14 };   U16           getScopeId() const { return mScope_id; }   U16           addScopeRef();   void          removeScopeRef();   void          setScopeRegistered(bool flag) { mScope_registered = flag; }   bool          getScopeRegistered() const { return mScope_registered; }protected:   /// Add a networked field   ///   /// A networked field is a regular field but with a bitmask flag associated to it.   /// When the field is set, it automatically triggers a call to setMaskBits with the mask associated to the field   /// in order to streamline simple networking code   /// Register a complex field.   ///   /// @param  in_pFieldname     Name of the field.   /// @param  in_fieldType      Type of the field. @see ConsoleDynamicTypes   /// @param  in_fieldOffset    Offset to  the field from the start of the class; calculated using the Offset() macro.   /// @param  in_elementCount   Number of elements in this field. Arrays of elements are assumed to be contiguous in memory.   /// @param  in_pFieldDocs     Usage string for this field. @see console_autodoc   static void addNetworkedField(const char*   in_pFieldname,      const U32     in_fieldType,      const dsize_t in_fieldOffset,      const U32     in_elementCount = 1,      const char*   in_pFieldDocs = NULL,      U32 flags = 0,      U32 networkMask = 0);   static void addNetworkedField(const char*   in_pFieldname,      const U32     in_fieldType,      const dsize_t in_fieldOffset,      AbstractClassRep::WriteDataNotify in_writeDataFn,      const U32     in_elementCount = 1,      const char*   in_pFieldDocs = NULL,      U32 flags = 0,      U32 networkMask = 0);   /// Register a simple field.   ///   /// @param  in_pFieldname  Name of the field.   /// @param  in_fieldType   Type of the field. @see ConsoleDynamicTypes   /// @param  in_fieldOffset Offset to  the field from the start of the class; calculated using the Offset() macro.   /// @param  in_pFieldDocs  Usage string for this field. @see console_autodoc   static void addNetworkedField(const char*   in_pFieldname,      const U32     in_fieldType,      const dsize_t in_fieldOffset,      const char*   in_pFieldDocs,      U32 flags = 0,      U32 networkMask = 0);   static void addNetworkedField(const char*   in_pFieldname,      const U32     in_fieldType,      const dsize_t in_fieldOffset,      AbstractClassRep::WriteDataNotify in_writeDataFn,      const char*   in_pFieldDocs,      U32 flags = 0,      U32 networkMask = 0);};//-----------------------------------------------------------------------------inline bool NetObject::isGhost() const{   return mNetFlags.test(IsGhost);}inline bool NetObject::isClientObject() const{   return mNetFlags.test(IsGhost);}inline bool NetObject::isServerObject() const{   return !mNetFlags.test(IsGhost);}inline bool NetObject::isScopeLocal() const{   return mNetFlags.test(ScopeLocal);}inline bool NetObject::isScopeable() const{   return mNetFlags.test(Ghostable) && !mNetFlags.test(ScopeAlways);}inline bool NetObject::isGhostable() const{   return mNetFlags.test(Ghostable);}inline bool NetObject::isGhostAlways() const{   AssertFatal(mNetFlags.test(Ghostable) || mNetFlags.test(ScopeAlways) == false,               "That's strange, a ScopeAlways non-ghostable object?  Something wrong here");   return mNetFlags.test(Ghostable) && mNetFlags.test(ScopeAlways);}#endif
 |