2012-09-19 15:15:01 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// 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.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2017-07-26 23:09:36 +00:00
|
|
|
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
|
|
|
|
|
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
|
|
|
|
|
// Copyright (C) 2015 Faust Logic, Inc.
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
|
|
|
|
|
|
2012-09-19 15:15:01 +00:00
|
|
|
#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);
|
2018-03-07 06:13:56 +00:00
|
|
|
/// dStrcpy(message1, "Hello World 1!", bufLen);
|
|
|
|
|
/// dStrcpy(message2, "Hello World 2!", bufLen);
|
2012-09-19 15:15:01 +00:00
|
|
|
/// }
|
|
|
|
|
/// @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);
|
2018-03-07 06:13:56 +00:00
|
|
|
/// dStrcpy(message1, msg, bufLen);
|
2012-09-19 15:15:01 +00:00
|
|
|
/// }
|
|
|
|
|
/// void setMessage2(const char *msg)
|
|
|
|
|
/// {
|
|
|
|
|
/// setMaskBits(Message2Mask);
|
2018-03-07 06:13:56 +00:00
|
|
|
/// dStrcpy(message2, msg, bufLen);
|
2012-09-19 15:15:01 +00:00
|
|
|
/// }
|
|
|
|
|
/// @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
|
2016-05-01 04:34:54 +00:00
|
|
|
class NetObject : public SimGroup
|
2012-09-19 15:15:01 +00:00
|
|
|
{
|
|
|
|
|
// 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:
|
2016-05-01 04:34:54 +00:00
|
|
|
typedef SimGroup Parent;
|
2012-09-19 15:15:01 +00:00
|
|
|
|
|
|
|
|
/// 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();
|
|
|
|
|
|
|
|
|
|
virtual String describeSelf() const;
|
|
|
|
|
|
|
|
|
|
/// @name Miscellaneous
|
|
|
|
|
/// @{
|
|
|
|
|
DECLARE_CONOBJECT(NetObject);
|
|
|
|
|
static void initPersistFields();
|
|
|
|
|
bool onAdd();
|
|
|
|
|
void onRemove();
|
|
|
|
|
/// @}
|
|
|
|
|
|
|
|
|
|
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() ); }
|
|
|
|
|
|
|
|
|
|
/// @}
|
2017-07-26 23:09:36 +00:00
|
|
|
protected:
|
2018-03-14 02:27:31 +00:00
|
|
|
U16 mScope_id;
|
|
|
|
|
U16 mScope_refs;
|
|
|
|
|
bool mScope_registered;
|
2017-07-26 23:09:36 +00:00
|
|
|
virtual void onScopeIdChange() { }
|
|
|
|
|
public:
|
|
|
|
|
enum { SCOPE_ID_BITS = 14 };
|
2018-03-14 02:27:31 +00:00
|
|
|
U16 getScopeId() const { return mScope_id; }
|
2017-07-26 23:09:36 +00:00
|
|
|
U16 addScopeRef();
|
|
|
|
|
void removeScopeRef();
|
2018-03-14 02:27:31 +00:00
|
|
|
void setScopeRegistered(bool flag) { mScope_registered = flag; }
|
|
|
|
|
bool getScopeRegistered() const { return mScope_registered; }
|
2018-01-16 06:47:53 +00:00
|
|
|
|
|
|
|
|
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);
|
2012-09-19 15:15:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
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
|