📄 netobject.h
字号:
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _NETOBJECT_H_
#define _NETOBJECT_H_
#ifdef TGE_RPG
#ifndef _RPGNETOBJECT_H_
#include "rpg/RPGNetObject.h"
#endif
#else
#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!");
/// dStrcpy(message2, "Hello World 2!");
/// }
/// @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);
/// }
/// void setMessage2(const char *msg)
/// {
/// setMaskBits(Message2Mask);
/// dStrcpy(message2, msg);
/// }
/// @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.
///
/// @nosubgrouping
class NetObject: public SimObject
{
// The Ghost Manager needs read/write access
friend class NetConnection;
friend struct GhostInfo;
friend class ProcessList;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -