Main Page Modules Namespaces C++ Classes Compound List File List Index
TNL: TNL::NetObject Class Reference

# TNL::NetObject Class Reference

`#include <tnlNetObject.h>`

Inheritance diagram for TNL::NetObject:

## Detailed Description

Superclass for ghostable networked objects.

## Introduction To NetObject And Ghosting

One of the most powerful aspects of the Torque Network Library 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 TNL's architecture:

- First, the server determines what objects are in-scope for the client. This is done by calling performScopeQuery() on the object which is considered the "scope" object. This could be a simulation avatar of the character, a flyby camera, a vehicle the user is controlling, or something else.
- Second, it instructs the client to create "ghost" instances of those objects to represent the source objects on the server. Finally, it sends updates to the ghosts of those objects whose state has been updated on the server, prioritizing the updates based on how relevant the object is to that particular client.

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 TNL doesn't deliver information about game objects which aren't "in scope", the ability for clients to learn about objects outside their immediate perceptions can be curtailed by an agressive scoping function.

## An Example Implementation

The basis of the ghost implementation in TNL is NetObject. Each NetObject maintains an updateMask, a 32 bit word representing up to 32 independent states for the object. When a NetObject's state changes it calls the setMaskBits method to notify the network layer that the state has changed and needs to be updated on all clients that have that NetObject in scope.

Using a NetObject is very simple; let's go through a simple example implementation:

     class SimpleNetObject : public NetObject
     {
     public:
       typedef NetObject Parent;
       TNL_DECLARE_CLASS(SimpleNetObject);

Above is the standard boilerplate code for a NetObject subclass.

        char message1[256];
        char message2[256];
        enum States {
           Message1Mask = BIT(0),
           Message2Mask = BIT(1),
        };

The example class has two object "states" that each instance keeps track of, message1 and message2. A real game object might have states for health, velocity and position, or some other set of fields. Each class has 32 bits to work with, so it's possible to be very specific when defining states. In general, individual state bits should be assigned only to things that are updated independently - so if you update the position field and the velocity at the same time always, you could use a single bit to represent that state change.

        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);
           strcpy(message1, "Hello World 1!");
           strcpy(message2, "Hello World 2!");
        }

Here is the constructor. The code initializes the net flags, indicating that the SimpleNetObject should always be scoped, and that it can be ghosted to remote hosts

        U32 packUpdate(GhostConnection *, U32 mask, BitStream *stream)
        {
           // check which states need to be updated, and write updates
           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;
        }

Here's half of the meat of the networking code, the packUpdate() function. (The other half, unpackUpdate(), is shown below.) 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 update flags to the BitStream. This means the GhostConnection doesn't have to send the 32 bit updateMask with every packet.

        void unpackUpdate(GhostConnection *, BitStream *stream)
        {
           // the unpackUpdate function must be symmetrical to packUpdate
           if(stream->readFlag())
           {
              stream->readString(message1);
              logprintf("Got message1: %s", message1);
           }
           if(stream->readFlag())
           {
              stream->readString(message2);
              logprintf("Got message2: %s", message2);
           }
        }

The other half of the networking code in any NetObject, unpackUpdate(). In SimpleNetObject, all the code does is print the new messages to the log; however, in a more advanced object, the code might trigger animations, update complex object properties, or even spawn new objects, based on what packet data is unpacked.

        void setMessage1(const char *msg)
        {
           setMaskBits(Message1Mask);
           strcpy(message1, msg);
        }
        void setMessage2(const char *msg)
        {
           setMaskBits(Message2Mask);
           strcpy(message2, msg);
        }

Here are the accessors for the two properties. It is good to encapsulate 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.

     TNL_IMPLEMENT_NETOBJECT(SimpleNetObject);

Finally, we use the NetObject implementation macro, TNL_IMPLEMENT_NETOBJECT(), to implement our NetObject. It is important that we use this, as it makes TNL perform certain initialization tasks that allow us to send the object over the network. TNL_IMPLEMENT_CLASS() doesn't perform these tasks, see the documentation on NetClassRep for more details.

## Public Member Functions

| NetObject ()
| ~NetObject ()
virtual bool | onGhostAdd (GhostConnection *theConnection)
| onGhostAdd is called on the client side of a connection after the constructor and after the first call to unpackUpdate (the initial call).
virtual void | onGhostRemove ()
| onGhostRemove is called on the client side before the destructor when ghost has gone out of scope and is about to be deleted from the client.
virtual void | onGhostAvailable (GhostConnection *theConnection)
| onGhostAvailable is called on the server side after the server knows that the ghost is available and addressable via the getGhostIndex().
void | setMaskBits (U32 orMask)
| Notify the network system that one or more of this object's states have been changed.
void | clearMaskBits (U32 orMask)
| Notify the network system that one or more of its states does not need to be updated.
virtual F32 | getUpdatePriority (NetObject *scopeObject, U32 updateMask, S32 updateSkips)
| Called to determine the relative update priority of an object.
virtual U32 | packUpdate (GhostConnection *connection, U32 updateMask, BitStream *stream)
| Write the object's state to a packet.
virtual void | unpackUpdate (GhostConnection *connection, BitStream *stream)
| Unpack data written by packUpdate().
virtual void | performScopeQuery (GhostConnection *connection)
| For a scope object, determine what is in scope.
U32 | getNetIndex ()
| getNetIndex returns the index tag used to identify the server copy of a client object.
bool | isGhost () const
| isGhost returns true if this object is a ghost of a server object.
bool | isScopeLocal () const
| isScopeLocal returns true if this object is scoped always to the local client.
bool | isGhostable () const
| isGhostable returns true if this object can be ghosted to any clients.
U32 | getHashId () const
| Return a hash for this object.
void | postRPCEvent (NetObjectRPCEvent *theEvent)
| Internal method called by NetObject RPC events when they are packed.

## Static Public Member Functions

static void | collapseDirtyList ()
| collapseDirtyList pushes all the mDirtyMaskBits down into the GhostInfo's for each object, and clears out the dirty list.
static GhostConnection * | getRPCSourceConnection ()
| Returns the connection from which the current RPC method originated, or NULL if not currently within the processing of an RPC method call.
static void | setRPCDestConnection (GhostConnection *destConnection)
| Sets the connection to which all NetObject RPCs will be destined.
static GhostConnection * | getRPCDestConnection ()
| Returns the connection that serves as the destination of NetObject RPC method calls.

## Protected Types

enum | NetFlag { IsGhost = BIT(1), ScopeLocal = BIT(2), Ghostable = BIT(3), MaxNetFlagBit = 15
}

## Protected Member Functions

bool | isInitialUpdate ()
| Returns true if this pack/unpackUpdate is the initial one for the object.

## Protected Attributes

BitSet32 | mNetFlags
| Flags field describing this object, from NetFlag.

## Static Protected Attributes

static GhostConnection * | mRPCSourceConnection
| RPC method source connection.
static GhostConnection * | mRPCDestConnection
| NetObject RPC method destination connection.

## Private Types

typedef Object | Parent

## Private Attributes

NetObject * | mPrevDirtyList
NetObject * | mNextDirtyList
U32 | mDirtyMaskBits
U32 | mNetIndex
| The index of this ghost on the other side of the connection.
GhostInfo * | mFirstObjectRef
| Head of the linked list of GhostInfos for this object.
SafePtr< NetObject > | mServerObject
| Direct pointer to the parent object on the server if it is a local connection.
GhostConnection * | mOwningConnection
| The connection that owns this ghost, if it's a ghost.

## Static Private Attributes

static NetObject * | mDirtyList
static bool | mIsInitialUpdate
| Managed by GhostConnection - set to true when this is an initial update.

## Friends

class | GhostConnection
class | GhostAlwaysObjectEvent
class | NetObjectRPCEvent

## Member Typedef Documentation

typedef Object TNL::NetObject::Parent`[private]`

Reimplemented in TNLTest::Player, TNLTest::Building, Zap::CTFGameType, Zap::EngineeredObject, Zap::ForceFieldProjector, Zap::Turret, Zap::FlagItem, Zap::GameObject, Zap::GoalZone, Zap::HuntersGameType, Zap::HuntersFlagItem, Zap::HuntersNexusObject, Zap::PickupItem, Zap::GrenadeProjectile, Zap::Mine, Zap::RabbitGameType, Zap::Ship, Zap::SoccerGameType, and Zap::SoccerBallItem.

## Member Enumeration Documentation

enum TNL::NetObject::NetFlag`[protected]`

Enumerator:

  IsGhost | Set if this is a ghost.
  ScopeLocal | If set, this object ghosts only to the local client.
  Ghostable | Set if this object can ghost at all.
  MaxNetFlagBit |

## Constructor & Destructor Documentation

TNL::NetObject::NetObject | ( |  | ) |

TNL::NetObject::~NetObject | ( |  | ) |

## Member Function Documentation

bool TNL::NetObject::isInitialUpdate | ( |  | ) | `[inline, protected]`

Returns true if this pack/unpackUpdate is the initial one for the object.

static void TNL::NetObject::collapseDirtyList | ( |  | ) | `[static]`

collapseDirtyList pushes all the mDirtyMaskBits down into the GhostInfo's for each object, and clears out the dirty list.

static GhostConnection* TNL::NetObject::getRPCSourceConnection | ( |  | ) | `[inline, static]`

Returns the connection from which the current RPC method originated, or NULL if not currently within the processing of an RPC method call.

static void TNL::NetObject::setRPCDestConnection | ( | GhostConnection * | destConnection | ) | `[inline, static]`

Sets the connection to which all NetObject RPCs will be destined.

Calling this function with a NULL value will target NetObject RPCs to every connection for which that object is currently ghosted.

static GhostConnection* TNL::NetObject::getRPCDestConnection | ( |  | ) | `[inline, static]`

Returns the connection that serves as the destination of NetObject RPC method calls.

virtual bool TNL::NetObject::onGhostAdd | ( | GhostConnection * | theConnection | ) | `[virtual]`

onGhostAdd is called on the client side of a connection after the constructor and after the first call to unpackUpdate (the initial call).

Returning true signifies no error - returning false causes the connection to abort.

Reimplemented in TNLTest::Player, TNLTest::Building, and Zap::GameObject.

virtual void TNL::NetObject::onGhostRemove | ( |  | ) | `[virtual]`

onGhostRemove is called on the client side before the destructor when ghost has gone out of scope and is about to be deleted from the client.

Reimplemented in Zap::Ship.

virtual void TNL::NetObject::onGhostAvailable | ( | GhostConnection * | theConnection | ) | `[virtual]`

onGhostAvailable is called on the server side after the server knows that the ghost is available and addressable via the getGhostIndex().

Reimplemented in TNLTest::Player, Zap::GameType, and Zap::HuntersGameType.

void TNL::NetObject::setMaskBits | ( | U32 | orMask | ) |

Notify the network system that one or more of this object's states have been changed.

Note:
  This is a server side call. It has no meaning for ghosts.

void TNL::NetObject::clearMaskBits | ( | U32 | orMask | ) |

Notify the network system that one or more of its states does not need to be updated.

Note:
  This is a server side call. It has no meaning for ghosts.

virtual F32 TNL::NetObject::getUpdatePriority | ( | NetObject * | scopeObject,
|  | U32 | updateMask,
|  | S32 | updateSkips |
| ) |  |  | `[virtual]`

Called to determine the relative update priority of an object.

All objects that are in scope and that have out of date states are queried and sorted by priority before being updated. If there is not enough room in a single packet for all out of date objects, the skipped objects will have an incremented updateSkips the next time that connection prepares to send a packet. Typically the update priority is scaled by updateSkips so that as data becomes stale, it becomes more of a priority to update.

Reimplemented in Zap::GameObject.

virtual U32 TNL::NetObject::packUpdate | ( | GhostConnection * | connection,
|  | U32 | updateMask,
|  | BitStream * | stream |
| ) |  |  | `[virtual]`

Write the object's state to a packet.

packUpdate is called on an object when it is to be written into a packet stream for transmission to the client. The updateMask parameter contains the out-of-date state mask for the object. The initial update mask for any object to a client will always be 0xFFFFFFFF, signifying that all states are out of date. It is often useful to check for this mask, to write one-time initialization information for that object.

Reimplemented in TNLTest::Player, TNLTest::Building, Zap::EngineeredObject, Zap::ForceField, Zap::Turret, Zap::FlagItem, Zap::GoalZone, Zap::HuntersFlagItem, Zap::HuntersNexusObject, Zap::Item, Zap::PickupItem, Zap::Projectile, Zap::GrenadeProjectile, Zap::Mine, Zap::Ship, and Zap::Teleporter.

virtual void TNL::NetObject::unpackUpdate | ( | GhostConnection * | connection,
|  | BitStream * | stream |
| ) |  |  | `[virtual]`

Unpack data written by packUpdate().

unpackUpdate is called on the client to read an update out of a packet's bit stream. Because the update mask is not encoded by the network system directly, each unpack update function will have to determine from the bit stream which states are being updated.

Reimplemented in TNLTest::Player, TNLTest::Building, Zap::EngineeredObject, Zap::ForceField, Zap::Turret, Zap::FlagItem, Zap::GoalZone, Zap::HuntersFlagItem, Zap::HuntersNexusObject, Zap::Item, Zap::PickupItem, Zap::Projectile, Zap::GrenadeProjectile, Zap::Mine, Zap::Ship, and Zap::Teleporter.

virtual void TNL::NetObject::performScopeQuery | ( | GhostConnection * | connection | ) | `[virtual]`

For a scope object, determine what is in scope.

performScopeQuery is called on a NetConnection's scope object to determine which objects in the world are in scope for that connection.

Reimplemented in TNLTest::Player, and Zap::GameType.

U32 TNL::NetObject::getNetIndex | ( |  | ) | `[inline]`

getNetIndex returns the index tag used to identify the server copy of a client object.

bool TNL::NetObject::isGhost | ( |  | ) | const`[inline]`

isGhost returns true if this object is a ghost of a server object.

bool TNL::NetObject::isScopeLocal | ( |  | ) | const`[inline]`

isScopeLocal returns true if this object is scoped always to the local client.

bool TNL::NetObject::isGhostable | ( |  | ) | const`[inline]`

isGhostable returns true if this object can be ghosted to any clients.

U32 TNL::NetObject::getHashId | ( |  | ) | const`[inline]`

Return a hash for this object.

Note:
  This is based on its location in memory.

void TNL::NetObject::postRPCEvent | ( | NetObjectRPCEvent * | theEvent | ) |

Internal method called by NetObject RPC events when they are packed.

## Friends And Related Function Documentation

friend class GhostConnection`[friend]`

friend class GhostAlwaysObjectEvent`[friend]`

friend class NetObjectRPCEvent`[friend]`

## Field Documentation

NetObject* TNL::NetObject::mPrevDirtyList`[private]`

NetObject* TNL::NetObject::mNextDirtyList`[private]`

U32 TNL::NetObject::mDirtyMaskBits`[private]`

NetObject* TNL::NetObject::mDirtyList`[static, private]`

U32 TNL::NetObject::mNetIndex`[private]`

The index of this ghost on the other side of the connection.

GhostInfo* TNL::NetObject::mFirstObjectRef`[private]`

Head of the linked list of GhostInfos for this object.

Reimplemented from TNL::Object.

bool TNL::NetObject::mIsInitialUpdate`[static, private]`

Managed by GhostConnection - set to true when this is an initial update.

SafePtr<NetObject> TNL::NetObject::mServerObject`[private]`

Direct pointer to the parent object on the server if it is a local connection.

GhostConnection* TNL::NetObject::mOwningConnection`[private]`

The connection that owns this ghost, if it's a ghost.

BitSet32 TNL::NetObject::mNetFlags`[protected]`

Flags field describing this object, from NetFlag.

GhostConnection* TNL::NetObject::mRPCSourceConnection`[static, protected]`

RPC method source connection.

GhostConnection* TNL::NetObject::mRPCDestConnection`[static, protected]`

NetObject RPC method destination connection.