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//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames 26// Copyright (C) 2015 Faust Logic, Inc. 27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 28 29#ifndef _NETOBJECT_H_ 30#define _NETOBJECT_H_ 31 32#ifndef _SIMBASE_H_ 33#include "console/simBase.h" 34#endif 35#ifndef _MMATH_H_ 36#include "math/mMath.h" 37#endif 38 39 40//----------------------------------------------------------------------------- 41class NetConnection; 42class NetObject; 43 44//----------------------------------------------------------------------------- 45 46struct CameraScopeQuery 47{ 48 NetObject *camera; ///< Pointer to the viewing object. 49 Point3F pos; ///< Position in world space 50 Point3F orientation; ///< Viewing vector in world space 51 F32 fov; ///< Viewing angle/2 52 F32 sinFov; ///< sin(fov/2); 53 F32 cosFov; ///< cos(fov/2); 54 F32 visibleDistance; ///< Visible distance. 55}; 56 57struct GhostInfo; 58 59 60//----------------------------------------------------------------------------- 61/// Superclass for ghostable networked objects. 62/// 63/// @section NetObject_intro Introduction To NetObject And Ghosting 64/// 65/// One of the most powerful aspects of Torque's networking code is its support 66/// for ghosting and prioritized, most-recent-state network updates. The way 67/// this works is a bit complex, but it is immensely efficient. Let's run 68/// through the steps that the server goes through for each client in this part 69/// of Torque's networking: 70/// - First, the server determines what objects are in-scope for the client. 71/// This is done by calling onCameraScopeQuery() on the object which is 72/// considered the "scope" object. This is usually the player object, but 73/// it can be something else. (For instance, the current vehicle, or a 74/// object we're remote controlling.) 75/// - Second, it ghosts them to the client; this is implemented in netGhost.cc. 76/// - Finally, it sends updates as needed, by checking the dirty list and packing 77/// updates. 78/// 79/// There several significant advantages to using this networking system: 80/// - Efficient network usage, since we only send data that has changed. In addition, 81/// since we only care about most-recent data, if a packet is dropped, we don't waste 82/// effort trying to deliver stale data. 83/// - Cheating protection; since we don't deliver information about game objects which 84/// aren't in scope, we dramatically reduce the ability of clients to hack the game and 85/// gain a meaningful advantage. (For instance, they can't find out about things behind 86/// them, since objects behind them don't fall in scope.) In addition, since ghost IDs are 87/// assigned per-client, it's difficult for any sort of co-ordination between cheaters to 88/// occur. 89/// 90/// NetConnection contains the Ghost Manager implementation, which deals with transferring data to 91/// the appropriate clients and keeping state in synch. 92/// 93/// @section NetObject_Implementation An Example Implementation 94/// 95/// The basis of the ghost implementation in Torque is NetObject. It tracks the dirty flags for the 96/// various states that the object trackers, and does some other book-keeping to allow more efficient 97/// operation of the networking layer. 98/// 99/// Using a NetObject is very simple; let's go through a simple example implementation: 100/// 101/// @code 102/// class SimpleNetObject : public NetObject 103/// { 104/// public: 105/// typedef NetObject Parent; 106/// DECLARE_CONOBJECT(SimpleNetObject); 107/// @endcode 108/// 109/// Above is the standard boilerplate code for a Torque class. You can find out more about this in SimObject. 110/// 111/// @code 112/// char message1[256]; 113/// char message2[256]; 114/// enum States { 115/// Message1Mask = BIT(0), 116/// Message2Mask = BIT(1), 117/// }; 118/// @endcode 119/// 120/// For our example, we're having two "states" that we keep track of, message1 and message2. In a real 121/// object, we might map our states to health and position, or some other set of fields. You have 32 122/// bits to work with, so it's possible to be very specific when defining states. In general, you 123/// should try to use as few states as possible (you never know when you'll need to expand your object's 124/// functionality!), and in fact, most of your fields will end up changing all at once, so it's not worth 125/// it to be too fine-grained. (As an example, position and velocity on Player are controlled by the same 126/// bit, as one rarely changes without the other changing, too.) 127/// 128/// @code 129/// SimpleNetObject() 130/// { 131/// // in order for an object to be considered by the network system, 132/// // the Ghostable net flag must be set. 133/// // the ScopeAlways flag indicates that the object is always scoped 134/// // on all active connections. 135/// mNetFlags.set(ScopeAlways | Ghostable); 136/// dStrcpy(message1, "Hello World 1!", bufLen); 137/// dStrcpy(message2, "Hello World 2!", bufLen); 138/// } 139/// @endcode 140/// 141/// Here is the constructor. Here, you see that we initialize our net flags to show that 142/// we should always be scoped, and that we're to be taken into consideration for ghosting. We 143/// also provide some initial values for the message fields. 144/// 145/// @code 146/// U32 packUpdate(NetConnection *, U32 mask, BitStream *stream) 147/// { 148/// // check which states need to be updated, and update them 149/// if(stream->writeFlag(mask & Message1Mask)) 150/// stream->writeString(message1); 151/// if(stream->writeFlag(mask & Message2Mask)) 152/// stream->writeString(message2); 153/// 154/// // the return value from packUpdate can set which states still 155/// // need to be updated for this object. 156/// return 0; 157/// } 158/// @endcode 159/// 160/// Here's half of the meat of the networking code, the packUpdate() function. (The other half, unpackUpdate(), 161/// we'll get to in a second.) The comments in the code pretty much explain everything, however, notice that the 162/// code follows a pattern of if(writeFlag(mask & StateMask)) { ... write data ... }. The packUpdate()/unpackUpdate() 163/// functions are responsible for reading and writing the dirty bits to the bitstream by themselves. 164/// 165/// @code 166/// void unpackUpdate(NetConnection *, BitStream *stream) 167/// { 168/// // the unpackUpdate function must be symmetrical to packUpdate 169/// if(stream->readFlag()) 170/// { 171/// stream->readString(message1); 172/// Con::printf("Got message1: %s", message1); 173/// } 174/// if(stream->readFlag()) 175/// { 176/// stream->readString(message2); 177/// Con::printf("Got message2: %s", message2); 178/// } 179/// } 180/// @endcode 181/// 182/// The other half of the networking code in any NetObject, unpackUpdate(). In our simple example, all that 183/// the code does is print the new messages to the console; however, in a more advanced object, you might 184/// trigger animations, update complex object properties, or even spawn new objects, based on what packet 185/// data you unpack. 186/// 187/// @code 188/// void setMessage1(const char *msg) 189/// { 190/// setMaskBits(Message1Mask); 191/// dStrcpy(message1, msg, bufLen); 192/// } 193/// void setMessage2(const char *msg) 194/// { 195/// setMaskBits(Message2Mask); 196/// dStrcpy(message2, msg, bufLen); 197/// } 198/// @endcode 199/// 200/// Here are the accessors for the two properties. It is good to encapsulate your state 201/// variables, so that you don't have to remember to make a call to setMaskBits every time you change 202/// anything; the accessors can do it for you. In a more complex object, you might need to set 203/// multiple mask bits when you change something; this can be done using the | operator, for instance, 204/// setMaskBits( Message1Mask | Message2Mask ); if you changed both messages. 205/// 206/// @code 207/// IMPLEMENT_CO_NETOBJECT_V1(SimpleNetObject); 208/// 209/// ConsoleMethod(SimpleNetObject, setMessage1, void, 3, 3, "(string msg) Set message 1.") 210/// { 211/// object->setMessage1(argv[2]); 212/// } 213/// 214/// ConsoleMethod(SimpleNetObject, setMessage2, void, 3, 3, "(string msg) Set message 2.") 215/// { 216/// object->setMessage2(argv[2]); 217/// } 218/// @endcode 219/// 220/// Finally, we use the NetObject implementation macro, IMPLEMENT_CO_NETOBJECT_V1(), to implement our 221/// NetObject. It is important that we use this, as it makes Torque perform certain initialization tasks 222/// that allow us to send the object over the network. IMPLEMENT_CONOBJECT() doesn't perform these tasks, see 223/// the documentation on AbstractClassRep for more details. 224/// 225/// @nosubgrouping 226class NetObject : public SimGroup 227{ 228 // The Ghost Manager needs read/write access 229 friend class NetConnection; 230 friend struct GhostInfo; 231 friend class ProcessList; 232 233 // Not the best way to do this, but the event needs access to mNetFlags 234 friend class GhostAlwaysObjectEvent; 235 236private: 237 typedef SimGroup Parent; 238 239 /// Mask indicating which states are dirty and need to be retransmitted on this 240 /// object. 241 U32 mDirtyMaskBits; 242 243 /// @name Dirty List 244 /// 245 /// Whenever a NetObject becomes "dirty", we add it to the dirty list. 246 /// We also remove ourselves on the destructor. 247 /// 248 /// This is done so that when we want to send updates (in NetConnection), 249 /// it's very fast to find the objects that need to be updated. 250 /// @{ 251 252 /// Static pointer to the head of the dirty NetObject list. 253 static NetObject *mDirtyList; 254 255 /// Next item in the dirty list... 256 NetObject *mPrevDirtyList; 257 258 /// Previous item in the dirty list... 259 NetObject *mNextDirtyList; 260 261 /// @} 262protected: 263 264 /// Pointer to the server object on a local connection. 265 /// @see getServerObject 266 SimObjectPtr<NetObject> mServerObject; 267 268 /// Pointer to the client object on a local connection. 269 /// @see getClientObject 270 SimObjectPtr<NetObject> mClientObject; 271 272 enum NetFlags 273 { 274 IsGhost = BIT(1), ///< This is a ghost. 275 ScopeAlways = BIT(6), ///< Object always ghosts to clients. 276 ScopeLocal = BIT(7), ///< Ghost only to local client. 277 Ghostable = BIT(8), ///< Set if this object CAN ghost. 278 279 MaxNetFlagBit = 15 280 }; 281 282 BitSet32 mNetFlags; ///< Flag values from NetFlags 283 U32 mNetIndex; ///< The index of this ghost in the GhostManager on the server. 284 285 GhostInfo *mFirstObjectRef; ///< Head of a linked list storing GhostInfos referencing this NetObject. 286 287public: 288 NetObject(); 289 ~NetObject(); 290 291 virtual String describeSelf() const; 292 293 /// @name Miscellaneous 294 /// @{ 295 DECLARE_CONOBJECT(NetObject); 296 static void initPersistFields(); 297 bool onAdd(); 298 void onRemove(); 299 /// @} 300 301 static void collapseDirtyList(); 302 303 /// Used to mark a bit as dirty; ie, that its corresponding set of fields need to be transmitted next update. 304 /// 305 /// @param orMask Bit(s) to set 306 virtual void setMaskBits(U32 orMask); 307 308 /// Clear the specified bits from the dirty mask. 309 /// 310 /// @param orMask Bits to clear 311 virtual void clearMaskBits(U32 orMask); 312 virtual U32 filterMaskBits(U32 mask, NetConnection * connection) { return mask; } 313 314 /// Scope the object to all connections. 315 /// 316 /// The object is marked as ScopeAlways and is immediately ghosted to 317 /// all active connections. This function has no effect if the object 318 /// is not marked as Ghostable. 319 void setScopeAlways(); 320 321 /// Stop scoping the object to all connections. 322 /// 323 /// The object's ScopeAlways flag is cleared and the object is removed from 324 /// all current active connections. 325 void clearScopeAlways(); 326 327 /// This returns a value which is used to prioritize which objects need to be updated. 328 /// 329 /// In NetObject, our returned priority is 0.1 * updateSkips, so that less recently 330 /// updated objects are more likely to be updated. 331 /// 332 /// In subclasses, this can be adjusted. For instance, ShapeBase provides priority 333 /// based on proximity to the camera. 334 /// 335 /// @param focusObject Information from a previous call to onCameraScopeQuery. 336 /// @param updateMask Current update mask. 337 /// @param updateSkips Number of ticks we haven't been updated for. 338 /// @returns A floating point value indicating priority. These are typically < 5.0. 339 virtual F32 getUpdatePriority(CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips); 340 341 /// Instructs this object to pack its state for transfer over the network. 342 /// 343 /// @param conn Net connection being used 344 /// @param mask Mask indicating fields to transmit. 345 /// @param stream Bitstream to pack data to 346 /// 347 /// @returns Any bits which were not dealt with. The value is stored by the networking 348 /// system. Don't set bits you weren't passed. 349 virtual U32 packUpdate(NetConnection * conn, U32 mask, BitStream *stream); 350 351 /// Instructs this object to read state data previously packed with packUpdate. 352 /// 353 /// @param conn Net connection being used 354 /// @param stream stream to read from 355 virtual void unpackUpdate(NetConnection * conn, BitStream *stream); 356 357 /// Queries the object about information used to determine scope. 358 /// 359 /// Something that is 'in scope' is somehow interesting to the client. 360 /// 361 /// If we are a NetConnection's scope object, it calls this method to determine 362 /// how things should be scoped; basically, we tell it our field of view with camInfo, 363 /// and have the opportunity to manually mark items as "in scope" as we see fit. 364 /// 365 /// By default, we just mark all ghostable objects as in scope. 366 /// 367 /// @param cr Net connection requesting scope information. 368 /// @param camInfo Information about what this object can see. 369 virtual void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *camInfo); 370 371 /// Get the ghost index of this object. 372 U32 getNetIndex() { return mNetIndex; } 373 374 bool isServerObject() const; ///< Is this a server object? 375 bool isClientObject() const; ///< Is this a client object? 376 377 bool isGhost() const; ///< Is this is a ghost? 378 bool isScopeLocal() const; ///< Should this object only be visible to the client which created it? 379 bool isScopeable() const; ///< Is this object subject to scoping? 380 bool isGhostable() const; ///< Is this object ghostable? 381 bool isGhostAlways() const; ///< Should this object always be ghosted? 382 383 384 /// @name Short-Circuited Networking 385 /// 386 /// When we are running with client and server on the same system (which can happen be either 387 /// when we are doing a single player game, or if we're hosting a multiplayer game and having 388 /// someone playing on the same instance), we can do some short circuited code to enhance 389 /// performance. 390 /// 391 /// These variables are used to make it simpler; if we are running in short-circuited mode, 392 /// the ghosted client gets the server object while the server gets the client object. 393 /// 394 /// @note "Premature optimization is the root of all evil" - Donald Knuth. The current codebase 395 /// uses this feature in three small places, mostly for non-speed-related purposes. 396 /// 397 /// @{ 398 399 /// Returns a pointer to the server object when on a local connection. 400 NetObject* getServerObject() const { return mServerObject; } 401 402 /// Returns a pointer to the client object when on a local connection. 403 NetObject* getClientObject() const { return mClientObject; } 404 405 /// Template form for the callers convenience. 406 template < class T > 407 static T* getServerObject( T *netObj ) { return static_cast<T*>( netObj->getServerObject() ); } 408 409 /// Template form for the callers convenience. 410 template < class T > 411 static T* getClientObject( T *netObj ) { return static_cast<T*>( netObj->getClientObject() ); } 412 413 /// @} 414protected: 415 U16 mScope_id; 416 U16 mScope_refs; 417 bool mScope_registered; 418 virtual void onScopeIdChange() { } 419public: 420 enum { SCOPE_ID_BITS = 14 }; 421 U16 getScopeId() const { return mScope_id; } 422 U16 addScopeRef(); 423 void removeScopeRef(); 424 void setScopeRegistered(bool flag) { mScope_registered = flag; } 425 bool getScopeRegistered() const { return mScope_registered; } 426 427protected: 428 /// Add a networked field 429 /// 430 /// A networked field is a regular field but with a bitmask flag associated to it. 431 /// When the field is set, it automatically triggers a call to setMaskBits with the mask associated to the field 432 /// in order to streamline simple networking code 433 /// Register a complex field. 434 /// 435 /// @param in_pFieldname Name of the field. 436 /// @param in_fieldType Type of the field. @see ConsoleDynamicTypes 437 /// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro. 438 /// @param in_elementCount Number of elements in this field. Arrays of elements are assumed to be contiguous in memory. 439 /// @param in_pFieldDocs Usage string for this field. @see console_autodoc 440 static void addNetworkedField(const char* in_pFieldname, 441 const U32 in_fieldType, 442 const dsize_t in_fieldOffset, 443 const U32 in_elementCount = 1, 444 const char* in_pFieldDocs = NULL, 445 U32 flags = 0, 446 U32 networkMask = 0); 447 448 static void addNetworkedField(const char* in_pFieldname, 449 const U32 in_fieldType, 450 const dsize_t in_fieldOffset, 451 AbstractClassRep::WriteDataNotify in_writeDataFn, 452 const U32 in_elementCount = 1, 453 const char* in_pFieldDocs = NULL, 454 U32 flags = 0, 455 U32 networkMask = 0); 456 457 /// Register a simple field. 458 /// 459 /// @param in_pFieldname Name of the field. 460 /// @param in_fieldType Type of the field. @see ConsoleDynamicTypes 461 /// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro. 462 /// @param in_pFieldDocs Usage string for this field. @see console_autodoc 463 static void addNetworkedField(const char* in_pFieldname, 464 const U32 in_fieldType, 465 const dsize_t in_fieldOffset, 466 const char* in_pFieldDocs, 467 U32 flags = 0, 468 U32 networkMask = 0); 469 470 static void addNetworkedField(const char* in_pFieldname, 471 const U32 in_fieldType, 472 const dsize_t in_fieldOffset, 473 AbstractClassRep::WriteDataNotify in_writeDataFn, 474 const char* in_pFieldDocs, 475 U32 flags = 0, 476 U32 networkMask = 0); 477}; 478 479//----------------------------------------------------------------------------- 480 481inline bool NetObject::isGhost() const 482{ 483 return mNetFlags.test(IsGhost); 484} 485 486inline bool NetObject::isClientObject() const 487{ 488 return mNetFlags.test(IsGhost); 489} 490 491inline bool NetObject::isServerObject() const 492{ 493 return !mNetFlags.test(IsGhost); 494} 495 496inline bool NetObject::isScopeLocal() const 497{ 498 return mNetFlags.test(ScopeLocal); 499} 500 501inline bool NetObject::isScopeable() const 502{ 503 return mNetFlags.test(Ghostable) && !mNetFlags.test(ScopeAlways); 504} 505 506inline bool NetObject::isGhostable() const 507{ 508 return mNetFlags.test(Ghostable); 509} 510 511inline bool NetObject::isGhostAlways() const 512{ 513 AssertFatal(mNetFlags.test(Ghostable) || mNetFlags.test(ScopeAlways) == false, 514 "That's strange, a ScopeAlways non-ghostable object? Something wrong here"); 515 return mNetFlags.test(Ghostable) && mNetFlags.test(ScopeAlways); 516} 517 518#endif 519