netObject.h
Classes:
class
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
