netConnection.h
Engine/source/sim/netConnection.h
Classes:
class
Information about a ghosted object.
class
Torque network connection.
class
Structure to track ghost references in packets.
class
Structure to track ghost-always objects and their ghost indices.
class
Structure to track packets and what we sent over them.
class
An event to be sent over the network.
class
Public Defines
define
DEBUG_LOG(x)
define
IMPLEMENT_CO_CLIENTEVENT(className, groupMask) ( className, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, , , className::getParentStaticClassRep(), &Parent::__description)
define
IMPLEMENT_CO_CLIENTEVENT_V1(className) ( className, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep(#className, "Type" #className, &_smTypeId,, , , className::getParentStaticClassRep(), &Parent::__description)
define
IMPLEMENT_CO_NETEVENT(className, groupMask) ( className, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, , , className::getParentStaticClassRep(), &Parent::__description)
define
IMPLEMENT_CO_NETEVENT_V1(className) ( className, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep( #className, "Type" #className, &_smTypeId, , , , className::getParentStaticClassRep(), &Parent::__description)
define
IMPLEMENT_CO_SERVEREVENT(className, groupMask) ( className, className, __scope, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, , , className::getParentStaticClassRep(), &Parent::__description)
define
IMPLEMENT_CO_SERVEREVENT_V1(className) ( className, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep(#className, "Type" #className, &_smTypeId, , , , className::getParentStaticClassRep(), &Parent::__description)
Public Functions
DECLARE_SCOPE(NetAPI )
Detailed Description
Public Defines
DEBUG_LOG(x)
IMPLEMENT_CO_CLIENTEVENT(className, groupMask) ( className, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, , , className::getParentStaticClassRep(), &Parent::__description)
IMPLEMENT_CO_CLIENTEVENT_V1(className) ( className, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep(#className, "Type" #className, &_smTypeId,, , , className::getParentStaticClassRep(), &Parent::__description)
IMPLEMENT_CO_NETEVENT(className, groupMask) ( className, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, , , className::getParentStaticClassRep(), &Parent::__description)
IMPLEMENT_CO_NETEVENT_V1(className) ( className, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep( #className, "Type" #className, &_smTypeId, , , , className::getParentStaticClassRep(), &Parent::__description)
IMPLEMENT_CO_SERVEREVENT(className, groupMask) ( className, className, __scope, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, , , className::getParentStaticClassRep(), &Parent::__description)
IMPLEMENT_CO_SERVEREVENT_V1(className) ( className, ) \ ; \ className::_smTypeId; \ * className::getClassRep() { return &className::dynClassRep; } \ * className::getStaticClassRep() { return &dynClassRep; } \ * className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ * className::getContainerChildStaticClassRep() { return ; } \ className::getStaticWriteCustomTamlSchema() { return ; } \ <className> className::dynClassRep(#className, "Type" #className, &_smTypeId, , , , className::getParentStaticClassRep(), &Parent::__description)
Public Functions
DECLARE_SCOPE(NetAPI )
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 _NETCONNECTION_H_ 25#define _NETCONNECTION_H_ 26 27#ifndef _MPOINT3_H_ 28#include "math/mPoint3.h" 29#endif 30#ifndef _NETOBJECT_H_ 31#include "sim/netObject.h" 32#endif 33#ifndef _NETSTRINGTABLE_H_ 34#include "sim/netStringTable.h" 35#endif 36#ifndef _DNET_H_ 37#include "core/dnet.h" 38#endif 39 40#ifndef _H_CONNECTIONSTRINGTABLE 41#include "sim/connectionStringTable.h" 42#endif 43 44class NetConnection; 45class NetObject; 46class BitStream; 47class ResizeBitStream; 48class Stream; 49class Point3F; 50 51struct GhostInfo; 52struct SubPacketRef; // defined in NetConnection subclass 53 54//#define DEBUG_NET 55 56#ifdef TORQUE_DEBUG_NET 57#define DEBUG_LOG(x) if(mLogging){Con::printf x;} 58#else 59#define DEBUG_LOG(x) 60#endif 61 62DECLARE_SCOPE( NetAPI ); 63 64//---------------------------------------------------------------------------- 65 66class NetEvent; 67 68struct NetEventNote 69{ 70 NetEvent *mEvent; 71 S32 mSeqCount; 72 NetEventNote *mNextEvent; 73}; 74 75/// An event to be sent over the network. 76/// 77/// @note Torque implements two methods of network data passing; this is one of them. 78/// See NetConnection for details of the other, which is referred to as ghosting. 79/// 80/// Torque's network layer lets you pass events to/from the server. There are three 81/// types of events: 82/// - <b>Unguaranteed events</b> are events which are sent once. If they don't 83/// make it through the link, they are not resent. This is good for quick, 84/// frequent status updates which are of transient interest, like position 85/// updates or voice communication. 86/// - <b>Guaranteed events</b> are events which are guaranteed to be 87/// delivered. If they don't make it through the link, they are sent as 88/// needed. This is good for important, one-time information, 89/// like which team a user wants to play on, or the current weather. 90/// - <b>GuaranteedOrdered events</b> are events which are guaranteed not 91/// only to be delivered, but to be delivered in order. This is good for 92/// information which is not only important, but also order-critical, like 93/// chat messages. 94/// 95/// There are 6 methods that you need to implement if you want to make a 96/// basic NetEvent subclass, and 2 macros you need to call. 97/// 98/// @code 99/// // A simple NetEvent to transmit a string over the network. 100/// // This is based on the code in netTest.cc 101/// class SimpleMessageEvent : public NetEvent 102/// { 103/// typedef NetEvent Parent; 104/// char *msg; 105/// public: 106/// SimpleMessageEvent(const char *message = NULL); 107/// ~SimpleMessageEvent(); 108/// 109/// virtual void pack (NetConnection *conn, BitStream *bstream); 110/// virtual void write (NetConnection *conn, BitStream *bstream); 111/// virtual void unpack (NetConnection *conn, BitStream *bstream); 112/// virtual void process(NetConnection *conn); 113/// 114/// DECLARE_CONOBJECT(SimpleMessageEvent); 115/// }; 116/// 117/// IMPLEMENT_CO_NETEVENT_V1(SimpleMessageEvent); 118/// @endcode 119/// 120/// Notice the two macros which we call. The first, DECLARE_CONOBJECT() is there 121/// because we're a ConsoleObject. The second, IMPLEMENT_CO_NETEVENT_V1(), is there 122/// to register this event type with Torque's networking layer, so that it can be 123/// properly transmitted over the wire. There are three macros which you might use: 124/// - <b>IMPLEMENT_CO_NETEVENT_V1</b>, which indicates an event which may be sent 125/// in either direction, from the client to the server, or from the server to the 126/// client. 127/// - <b>IMPLEMENT_CO_CLIENTEVENT_V1</b>, which indicates an event which may only 128/// be sent to the client. 129/// - <b>IMPLEMENT_CO_SERVEREVENT_V1</b>, which indicates an event which may only 130/// be sent to the server. 131/// 132/// Choosing the right macro is a good way to make your game more resistant to hacking; for instance, 133/// PathManager events are marked as CLIENTEVENTs, because they would cause the server to crash if 134/// a client sent them. 135/// 136/// @note Torque allows you to call NetConnection::setLastError() on the NetConnection passed to 137/// your NetEvent. You can cause the connection to abort if invalid data is received, specifying 138/// a reason to the user. 139/// 140/// Now, the 6 methods which we have above; the constructor and destructor need only do 141/// whatever book-keeping is needed for your specific implementation. In our case, we 142/// just need to allocate/deallocate the space for our string: 143/// 144/// @code 145/// SimpleMessageEvent::SimpleMessageEvent(const char *message = NULL) 146/// { 147/// // If we wanted to make this not be a GuaranteedOrdered event, we'd 148/// // put a line like this in the constructor: 149/// // mGuaranteeType = Guaranteed; 150/// // (or whatever type you wanted.) 151/// if(message) 152/// msg = dStrdup(message); 153/// else 154/// msg = NULL; 155/// } 156/// 157/// SimpleMessageEvent::~SimpleMessageEvent() 158/// { 159/// dFree(msg); 160/// } 161/// @endcode 162/// 163/// Simple as that! Now, onto pack(), write(), unpack(), process(). 164/// 165/// <b>pack()</b> is responsible for packing the event over the wire: 166/// 167/// @code 168/// void SimpleMessageEvent::pack(NetConnection* conn, BitStream *bstream) 169/// { 170/// bstream->writeString(msg); 171/// } 172/// @endcode 173/// 174/// <b>unpack()</b> is responsible for unpacking the event on the other end: 175/// 176/// @code 177/// // The networking layer takes care of instantiating a new 178/// // SimpleMessageEvent, which saves us a bit of effort. 179/// void SimpleMessageEvent::unpack(NetConnection *conn, BitStream *bstream) 180/// { 181/// char buf[256]; 182/// bstream->readString(buf); 183/// msg = dStrdup(buf); 184/// } 185/// @endcode 186/// 187/// <b>process()</b> is called when the network layer is finished with things. 188/// A typical case is that a GuaranteedOrdered event is unpacked and stored, but 189/// not processed until the events preceding it in the sequence have also been 190/// dealt with. 191/// 192/// @code 193/// // This just prints the event in the console. You might 194/// // want to do something more clever here -- BJG 195/// void SimpleMessageEvent::process(NetConnection *conn) 196/// { 197/// Con::printf("RMSG %d %s", mSourceId, msg); 198/// } 199/// @endcode 200/// 201/// <b>write()</b> is called if a demo recording is started, and the event has not yet been 202/// processed, but it has been unpacked. It should be identical in its output to the bitstream 203/// compared to pack(), but since it is called after unpack() some lookups may not need to be 204/// performed. In normal demo recording, whole network packets are recorded, meaning that most 205/// of the time write() will not be called. 206/// 207/// In our case, it's entirely identical to pack(): 208/// 209/// @code 210/// virtual void write(NetConnection*, BitStream *bstream) 211/// { 212/// bstream->writeString(msg); 213/// } 214/// @endcode 215/// 216/// The NetEvent is sent over the wire in a straightforward way (assuming you have a 217/// handle to a NetConnection): 218/// 219/// @code 220/// NetConnection *conn; // We assume you have filled this in. 221/// 222/// con->postNetEvent(new SimpleMessageEvent("This is a test!")); 223/// @endcode 224/// 225/// @see GhostAlwaysObjectEvent for an example of dissimilar write()/pack() methods. 226/// 227/// Finally, for more advanced applications, notifySent() is called whenever the event is 228/// sent over the wire, in NetConnection::eventWritePacket(). notifyDelivered() is called 229/// when the packet is finally received or (in the case of Unguaranteed packets) dropped. 230/// 231/// @note IMPLEMENT_CO_NETEVENT_V1 and co. have sibling macros which allow you to specify a 232/// groupMask; see ConsoleObject for a further discussion of this. 233class NetEvent : public ConsoleObject 234{ 235public: 236 237 DECLARE_ABSTRACT_CLASS( NetEvent, ConsoleObject ); 238 DECLARE_INSCOPE( NetAPI ); 239 240 /// @name Implementation Details 241 /// 242 /// These are internal fields which you won't need to manipulate, except for mGuaranteeType. 243 /// @{ 244 245 /// 246 typedef ConsoleObject Parent; 247 enum { 248 GuaranteedOrdered = 0, 249 Guaranteed = 1, 250 Unguaranteed = 2 251 } mGuaranteeType; 252 NetConnectionId mSourceId; 253 254 void incRef() 255 { 256 incRefCount(); 257 } 258 void decRef() 259 { 260 decRefCount(); 261 } 262 263#ifdef TORQUE_DEBUG_NET 264 virtual const char *getDebugName(); 265#endif 266 /// @} 267 268 /// @name Things To Subclass 269 /// @{ 270 271 /// 272 NetEvent() { mGuaranteeType = GuaranteedOrdered; } 273 virtual ~NetEvent(); 274 275 virtual void write(NetConnection *ps, BitStream *bstream) = 0; 276 virtual void pack(NetConnection *ps, BitStream *bstream) = 0; 277 virtual void unpack(NetConnection *ps, BitStream *bstream) = 0; 278 virtual void process(NetConnection *ps) = 0; 279 virtual void notifySent(NetConnection *ps); 280 virtual void notifyDelivered(NetConnection *ps, bool madeit); 281 /// @} 282}; 283 284#define IMPLEMENT_CO_NETEVENT_V1(className) \ 285 IMPLEMENT_CLASS( className, NULL ) \ 286 END_IMPLEMENT_CLASS; \ 287 S32 className::_smTypeId; \ 288 AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ 289 AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ 290 AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ 291 AbstractClassRep* className::getContainerChildStaticClassRep() { return NULL; } \ 292 AbstractClassRep::WriteCustomTamlSchema className::getStaticWriteCustomTamlSchema() { return NULL; } \ 293 ConcreteClassRep<className> className::dynClassRep( #className, "Type" #className, &_smTypeId, NetClassGroupGameMask, NetClassTypeEvent, NetEventDirAny, className::getParentStaticClassRep(), &Parent::__description) 294 295#define IMPLEMENT_CO_CLIENTEVENT_V1(className) \ 296 IMPLEMENT_CLASS( className, NULL ) \ 297 END_IMPLEMENT_CLASS; \ 298 S32 className::_smTypeId; \ 299 AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ 300 AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ 301 AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ 302 AbstractClassRep* className::getContainerChildStaticClassRep() { return NULL; } \ 303 AbstractClassRep::WriteCustomTamlSchema className::getStaticWriteCustomTamlSchema() { return NULL; } \ 304 ConcreteClassRep<className> className::dynClassRep(#className, "Type" #className, &_smTypeId,NetClassGroupGameMask, NetClassTypeEvent, NetEventDirServerToClient, className::getParentStaticClassRep(), &Parent::__description) 305 306#define IMPLEMENT_CO_SERVEREVENT_V1(className) \ 307 IMPLEMENT_CLASS( className, NULL ) \ 308 END_IMPLEMENT_CLASS; \ 309 S32 className::_smTypeId; \ 310 AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ 311 AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ 312 AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ 313 AbstractClassRep* className::getContainerChildStaticClassRep() { return NULL; } \ 314 AbstractClassRep::WriteCustomTamlSchema className::getStaticWriteCustomTamlSchema() { return NULL; } \ 315 ConcreteClassRep<className> className::dynClassRep(#className, "Type" #className, &_smTypeId, NetClassGroupGameMask, NetClassTypeEvent, NetEventDirClientToServer, className::getParentStaticClassRep(), &Parent::__description) 316 317#define IMPLEMENT_CO_NETEVENT(className,groupMask) \ 318 IMPLEMENT_CLASS( className, NULL ) \ 319 END_IMPLEMENT_CLASS; \ 320 S32 className::_smTypeId; \ 321 AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ 322 AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ 323 AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ 324 AbstractClassRep* className::getContainerChildStaticClassRep() { return NULL; } \ 325 AbstractClassRep::WriteCustomTamlSchema className::getStaticWriteCustomTamlSchema() { return NULL; } \ 326 ConcreteClassRep<className> className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, NetClassTypeEvent, NetEventDirAny, className::getParentStaticClassRep(), &Parent::__description) 327 328#define IMPLEMENT_CO_CLIENTEVENT(className,groupMask) \ 329 IMPLEMENT_CLASS( className, NULL ) \ 330 END_IMPLEMENT_CLASS; \ 331 S32 className::_smTypeId; \ 332 AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ 333 AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ 334 AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ 335 AbstractClassRep* className::getContainerChildStaticClassRep() { return NULL; } \ 336 AbstractClassRep::WriteCustomTamlSchema className::getStaticWriteCustomTamlSchema() { return NULL; } \ 337 ConcreteClassRep<className> className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, NetClassTypeEvent, NetEventDirServerToClient, className::getParentStaticClassRep(), &Parent::__description) 338 339#define IMPLEMENT_CO_SERVEREVENT(className,groupMask) \ 340 IMPLEMENT_CLASS( className, className, __scope, NULL ) \ 341 END_IMPLEMENT_CLASS; \ 342 S32 className::_smTypeId; \ 343 AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ 344 AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ 345 AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ 346 AbstractClassRep* className::getContainerChildStaticClassRep() { return NULL; } \ 347 AbstractClassRep::WriteCustomTamlSchema className::getStaticWriteCustomTamlSchema() { return NULL; } \ 348 ConcreteClassRep<className> className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, NetClassTypeEvent, NetEventDirClientToServer, className::getParentStaticClassRep(), &Parent::__description) 349 350 351//---------------------------------------------------------------------------- 352 353/// Torque network connection. 354/// 355/// @section NetConnection_intro Introduction 356/// 357/// NetConnection is the glue that binds a networked Torque game together. It combines 358/// the low-level notify protocol implemented in ConnectionProtocol with a SimGroup to 359/// provide a powerful basis for implementing a multiplayer game protocol. 360/// 361/// On top of this basis it implements several distinct subsystems: 362/// - <b>Event manager</b>, which is responsible for transmitting NetEvents over the wire. 363/// It deals with ensuring that the various types of NetEvents are delivered appropriately, 364/// and with notifying the event of its delivery status. 365/// - <b>Move manager</b>, which is responsible for transferring a Move to the server 32 366/// times a second (on the client) and applying it to the control object (on the server). 367/// - <b>Ghost manager</b>, which is responsible for doing scoping calculations (on the server 368/// side) and transmitting most-recent ghost information to the client. 369/// - <b>File transfer</b>; it is often the case that clients will lack important files when 370/// connecting to a server which is running a mod or new map. This subsystem allows the 371/// server to transfer such files to the client. 372/// - <b>Networked String Table</b>; string data can easily soak up network bandwidth, so for 373/// efficiency, we implement a networked string table. We can then notify the connection 374/// of strings we will reference often, such as player names, and transmit only a tag, 375/// instead of the whole string. 376/// - <b>Demo Recording</b> is also implemented in NetConnection. A demo in Torque is a log 377/// of the network traffic between client and server; when a NetConnection records a demo, 378/// it simply logs this data to a file. When it plays a demo back, it replays the logged 379/// data. 380/// - The <b>Connection Database</b> is used to keep track of all the NetConnections; it can 381/// be iterated over (for instance, to send an event to all active connections), or queried 382/// by address. 383/// 384/// @section NetConnection_events On Events 385/// 386/// The Event Manager is exposed to the outside world via postNetEvent(), which accepts NetEvents. 387/// 388/// @see NetEvent for a more thorough explanation of how to use events. 389/// 390/// @section NetConnection_ghosting On Ghosting and Scoping 391/// 392/// Ghosting is the most complex, and most powerful, part of Torque's networking capabilities. It 393/// allows the information sent to clients to be very precisely matched to what they need, so that 394/// no excess bandwidth is wasted. The control object's onCameraScopeQuery() is called, to determine 395/// scoping information for the client; then objects which are in scope are then transmitted to the 396/// client, prioritized by the results of their getPriority() method. 397/// 398/// There is a cap on the maximum number of ghosts; ghost IDs are currently sent via a 12-bit field, 399/// ergo, there is a cap of 4096 objects ghosted per client. This can be easily raised; see the 400/// GhostConstants enum. 401/// 402/// Each object ghosted is assigned a ghost ID; the client is _only_ aware of the ghost ID. This acts 403/// to enhance game security, as it becomes difficult to map objects from one connection to another, or 404/// to reliably identify objects from ID alone. IDs are also reassigned based on need, making it hard 405/// to track objects that have fallen out of scope (as any object which the player shouldn't see would). 406/// 407/// resolveGhost() is used on the client side, and resolveObjectFromGhostIndex() on the server side, to 408/// turn ghost IDs into object references. 409/// 410/// The NetConnection is a SimGroup. On the client side, it contains all the objects which have been 411/// ghosted to that client. On the server side, it is empty; it can be used (typically in script) to 412/// hold objects related to the connection. For instance, you might place an observation camera in the 413/// NetConnnection. In both cases, when the connection is destroyed, so are the contained objects. 414/// 415/// @see NetObject, which is the superclass for ghostable objects, and ShapeBase, which is the base 416/// for player and vehicle classes. 417/// 418/// @nosubgrouping 419class NetConnection : public SimGroup, public ConnectionProtocol 420{ 421 friend class NetInterface; 422 423 typedef SimGroup Parent; 424 425public: 426 /// Structure to track ghost references in packets. 427 /// 428 /// Every packet we send out with an update from a ghost causes one of these to be 429 /// allocated. mask is used to track what states were sent; that way if a packet is 430 /// dropped, we can easily manipulate the stored states and figure out what if any data 431 /// we need to resend. 432 /// 433 struct GhostRef 434 { 435 U32 mask; ///< States we transmitted. 436 U32 ghostInfoFlags; ///< Flags from GhostInfo::Flags 437 GhostInfo *ghost; ///< Reference to the GhostInfo we're from. 438 GhostRef *nextRef; ///< Next GhostRef in this packet. 439 GhostRef *nextUpdateChain; ///< Next update we sent for this ghost. 440 }; 441 442 enum Constants 443 { 444 HashTableSize = 127, 445 }; 446 447 void sendDisconnectPacket(const char *reason); 448 449 virtual bool canRemoteCreate(); 450 451 virtual void onTimedOut(); 452 virtual void onConnectTimedOut(); 453 virtual void onDisconnect(const char *reason); 454 virtual void onConnectionRejected(const char *reason); 455 virtual void onConnectionEstablished(bool isInitiator); 456 virtual void handleStartupError(const char *errorString); 457 458 virtual void writeConnectRequest(BitStream *stream); 459 virtual bool readConnectRequest(BitStream *stream, const char **errorString); 460 461 virtual void writeConnectAccept(BitStream *stream); 462 virtual bool readConnectAccept(BitStream *stream, const char **errorString); 463 464 void connect(const NetAddress *address); 465 466 //---------------------------------------------------------------- 467 /// @name Global Connection List 468 /// @{ 469 470private: 471 /// 472 NetConnection *mNextConnection; ///< Next item in list. 473 NetConnection *mPrevConnection; ///< Previous item in list. 474 static NetConnection *mConnectionList; ///< Head of list. 475public: 476 static NetConnection *getConnectionList() { return mConnectionList; } 477 NetConnection *getNext() { return mNextConnection; } 478 /// @} 479 //---------------------------------------------------------------- 480 481 enum NetConnectionFlags 482 { 483 ConnectionToServer = BIT(0), 484 ConnectionToClient = BIT(1), 485 LocalClientConnection = BIT(2), 486 NetworkConnection = BIT(3), 487 }; 488 489private: 490 BitSet32 mTypeFlags; 491 492 U32 mNetClassGroup; ///< The NetClassGroup of this connection. 493 494 /// @name Statistics 495 /// @{ 496 497 /// Last time a packet was sent in milliseconds. 498 /// @see Platform::getVirtualMilliseconds() 499 U32 mLastUpdateTime; 500 501 F32 mRoundTripTime; 502 F32 mPacketLoss; 503 U32 mSimulatedPing; 504 F32 mSimulatedPacketLoss; 505 506 /// @} 507 508 /// @name State 509 /// @{ 510 511 U32 mProtocolVersion; 512 U32 mSendDelayCredit; 513 U32 mConnectSequence; 514 U32 mAddressDigest[4]; 515 516 bool mEstablished; 517 bool mMissionPathsSent; 518 519 struct NetRate 520 { 521 U32 updateDelay; 522 S32 packetSize; 523 bool changed; 524 }; 525 526 NetRate mCurRate; 527 NetRate mMaxRate; 528 529 /// If we're doing a "short circuited" connection, this stores 530 /// a pointer to the other side. 531 SimObjectPtr<NetConnection> mRemoteConnection; 532 533 NetAddress mNetAddress; 534 535 /// @} 536 537 538 /// @name Timeout Management 539 /// @{ 540 541 U32 mPingSendCount; 542 U32 mPingRetryCount; 543 U32 mLastPingSendTime; 544 /// @} 545 546 /// @name Connection Table 547 /// 548 /// We store our connections on a hash table so we can 549 /// quickly find them. 550 /// @{ 551 552 NetConnection *mNextTableHash; 553 static NetConnection *mHashTable[HashTableSize]; 554 555 /// @} 556 557protected: 558 static SimObjectPtr<NetConnection> mServerConnection; 559 static SimObjectPtr<NetConnection> mLocalClientConnection; 560 561 static bool mFilesWereDownloaded; 562 563 U32 mConnectSendCount; 564 U32 mConnectLastSendTime; 565 566 SimObjectPtr<NetConnection> getRemoteConnection() { return mRemoteConnection; } 567 568public: 569 static NetConnection *getConnectionToServer() { return mServerConnection; } 570 571 static NetConnection *getLocalClientConnection() { return mLocalClientConnection; } 572 static void setLocalClientConnection(NetConnection *conn) { mLocalClientConnection = conn; } 573 574 U32 getNetClassGroup() { return mNetClassGroup; } 575 static bool filesWereDownloaded() { return mFilesWereDownloaded; } 576 static String &getErrorBuffer() { return mErrorBuffer; } 577 578#ifdef TORQUE_DEBUG_NET 579 bool mLogging; 580 void setLogging(bool logging) { mLogging = logging; } 581#endif 582 583 void setSimulatedNetParams(F32 packetLoss, U32 ping) 584 { mSimulatedPacketLoss = packetLoss; mSimulatedPing = ping; } 585 586 bool isConnectionToServer() { return mTypeFlags.test(ConnectionToServer); } 587 bool isLocalConnection() { return !mRemoteConnection.isNull() ; } 588 bool isNetworkConnection() { return mTypeFlags.test(NetworkConnection); } 589 590 void setIsConnectionToServer() { mTypeFlags.set(ConnectionToServer); } 591 void setIsLocalClientConnection() { mTypeFlags.set(LocalClientConnection); } 592 void setNetworkConnection(bool net) { mTypeFlags.set(BitSet32(NetworkConnection), net); } 593 594 virtual void setEstablished(); 595 596 /// Call this if the "connection" is local to this app. This short-circuits the protocol layer. 597 void setRemoteConnectionObject(NetConnection *connection) { mRemoteConnection = connection; }; 598 599 void setSequence(U32 connectSequence); 600 601 void setAddressDigest(U32 digest[4]); 602 void getAddressDigest(U32 digest[4]); 603 604 U32 getSequence(); 605 606 void setProtocolVersion(U32 protocolVersion) { mProtocolVersion = protocolVersion; } 607 U32 getProtocolVersion() { return mProtocolVersion; } 608 F32 getRoundTripTime() { return mRoundTripTime; } 609 F32 getPacketLoss() { return( mPacketLoss ); } 610 611 static String mErrorBuffer; 612 static void setLastError(const char *fmt,...); 613 614 void checkMaxRate(); 615 void handlePacket(BitStream *stream); 616 void processRawPacket(BitStream *stream); 617 void handleNotify(bool recvd); 618 void handleConnectionEstablished(); 619 void keepAlive(); 620 621 const NetAddress *getNetAddress(); 622 void setNetAddress(const NetAddress *address); 623 Net::Error sendPacket(BitStream *stream); 624 625private: 626 void netAddressTableInsert(); 627 void netAddressTableRemove(); 628 629public: 630 /// Find a NetConnection, if any, with the specified address. 631 static NetConnection *lookup(const NetAddress *remoteAddress); 632 633 bool checkTimeout(U32 time); ///< returns true if the connection timed out 634 635 void checkPacketSend(bool force); 636 637 bool missionPathsSent() const { return mMissionPathsSent; } 638 void setMissionPathsSent(const bool s) { mMissionPathsSent = s; } 639 640 static void consoleInit(); 641 642 void onRemove(); 643 644 NetConnection(); 645 ~NetConnection(); 646 647public: 648 enum NetConnectionState 649 { 650 NotConnected, 651 AwaitingChallengeResponse, ///< We've sent a challenge request, awaiting the response. 652 AwaitingConnectRequest, ///< We've received a challenge request and sent a challenge response. 653 AwaitingConnectResponse, ///< We've received a challenge response and sent a connect request. 654 Connected, ///< We've accepted a connect request, or we've received a connect response accept. 655 }; 656 657 U32 mConnectionSendCount; ///< number of connection messages we've sent. 658 U32 mConnectionState; ///< State of the connection, from NetConnectionState. 659 660 void setConnectionState(U32 state) { mConnectionState = state; } 661 U32 getConnectionState() { return mConnectionState; } 662 663 664 void setGhostFrom(bool ghostFrom); ///< Sets whether ghosts transmit from this side of the connection. 665 void setGhostTo(bool ghostTo); ///< Sets whether ghosts are allowed from the other side of the connection. 666 void setSendingEvents(bool sending); ///< Sets whether this side actually sends the events that are posted to it. 667 void setTranslatesStrings(bool xl); ///< Sets whether this connection is capable of translating strings. 668 void setNetClassGroup(U32 group); ///< Sets the group of NetClasses this connection traffics in. 669 bool isEstablished() { return mEstablished; } ///< Is the connection established? 670 671 DECLARE_CONOBJECT(NetConnection); 672 DECLARE_INSCOPE( NetAPI ); 673 674 /// Structure to track packets and what we sent over them. 675 /// 676 /// We need to know what is sent in each packet, so that if a packet is 677 /// dropped, we know what to resend. This is the structure we use to track 678 /// this data. 679 struct PacketNotify 680 { 681 bool rateChanged; ///< Did the rate change on this packet? 682 bool maxRateChanged; ///< Did the max rate change on this packet? 683 U32 sendTime; ///< Timestampe, when we sent this packet. 684 685 NetEventNote *eventList; ///< Linked list of events sent over this packet. 686 GhostRef *ghostList; ///< Linked list of ghost updates we sent in this packet. 687 SubPacketRef *subList; ///< Defined by subclass - used as desired. 688 689 PacketNotify *nextPacket; ///< Next packet sent. 690 PacketNotify(); 691 }; 692 virtual PacketNotify *allocNotify(); 693 PacketNotify *mNotifyQueueHead; ///< Head of packet notify list. 694 PacketNotify *mNotifyQueueTail; ///< Tail of packet notify list. 695 696protected: 697 virtual void readPacket(BitStream *bstream); 698 virtual void writePacket(BitStream *bstream, PacketNotify *note); 699 virtual void packetReceived(PacketNotify *note); 700 virtual void packetDropped(PacketNotify *note); 701 virtual void connectionError(const char *errorString); 702 703//---------------------------------------------------------------- 704/// @name Event Manager 705/// @{ 706 707private: 708 NetEventNote *mSendEventQueueHead; 709 NetEventNote *mSendEventQueueTail; 710 NetEventNote *mUnorderedSendEventQueueHead; 711 NetEventNote *mUnorderedSendEventQueueTail; 712 NetEventNote *mWaitSeqEvents; 713 NetEventNote *mNotifyEventList; 714 715 static FreeListChunker<NetEventNote> mEventNoteChunker; 716 717 bool mSendingEvents; 718 719 S32 mNextSendEventSeq; 720 S32 mNextRecvEventSeq; 721 S32 mLastAckedEventSeq; 722 723 enum NetEventConstants { 724 InvalidSendEventSeq = -1, 725 FirstValidSendEventSeq = 0 726 }; 727 728 void eventOnRemove(); 729 730 void eventPacketDropped(PacketNotify *notify); 731 void eventPacketReceived(PacketNotify *notify); 732 733 void eventWritePacket(BitStream *bstream, PacketNotify *notify); 734 void eventReadPacket(BitStream *bstream); 735 736 void eventWriteStartBlock(ResizeBitStream *stream); 737 void eventReadStartBlock(BitStream *stream); 738public: 739 /// Post an event to this connection. 740 bool postNetEvent(NetEvent *event); 741 742/// @} 743 744//---------------------------------------------------------------- 745/// @name Networked string table 746/// @{ 747 748private: 749 bool mTranslateStrings; 750 ConnectionStringTable *mStringTable; 751public: 752 void mapString(U32 netId, NetStringHandle &string) 753 { mStringTable->mapString(netId, string); } 754 U32 checkString(NetStringHandle &string, bool *isOnOtherSide = NULL) 755 { if(mStringTable) return mStringTable->checkString(string, isOnOtherSide); else return 0; } 756 U32 getNetSendId(NetStringHandle &string) 757 { if(mStringTable) return mStringTable->getNetSendId(string); else return 0;} 758 void confirmStringReceived(NetStringHandle &string, U32 index) 759 { if(!isRemoved()) mStringTable->confirmStringReceived(string, index); } 760 761 NetStringHandle translateRemoteStringId(U32 id) { return mStringTable->lookupString(id); } 762 void validateSendString(const char *str); 763 764 void packString(BitStream *stream, const char *str); 765 void unpackString(BitStream *stream, char readBuffer[1024]); 766 767 void packNetStringHandleU(BitStream *stream, NetStringHandle &h); 768 NetStringHandle unpackNetStringHandleU(BitStream *stream); 769/// @} 770 771//---------------------------------------------------------------- 772/// @name Ghost manager 773/// @{ 774 775protected: 776 enum GhostStates 777 { 778 GhostAlwaysDone, 779 ReadyForNormalGhosts, 780 EndGhosting, 781 GhostAlwaysStarting, 782 SendNextDownloadRequest, 783 FileDownloadSizeMessage, 784 NumConnectionMessages, 785 }; 786 GhostInfo **mGhostArray; ///< Linked list of ghostInfos ghosted by this side of the connection 787 788 U32 mGhostZeroUpdateIndex; ///< Index in mGhostArray of first ghost with 0 update mask. 789 U32 mGhostFreeIndex; ///< Index in mGhostArray of first free ghost. 790 791 U32 mGhostsActive; ///- Track actve ghosts on client side 792 793 bool mGhosting; ///< Am I currently ghosting objects? 794 bool mScoping; ///< am I currently scoping objects? 795 U32 mGhostingSequence; ///< Sequence number describing this ghosting session. 796 797 NetObject **mLocalGhosts; ///< Local ghost for remote object. 798 /// 799 /// mLocalGhosts pointer is NULL if mGhostTo is false 800 801 GhostInfo *mGhostRefs; ///< Allocated array of ghostInfos. Null if ghostFrom is false. 802 GhostInfo **mGhostLookupTable; ///< Table indexed by object id to GhostInfo. Null if ghostFrom is false. 803 804 /// The object around which we are scoping this connection. 805 /// 806 /// This is usually the player object, or a related object, like a vehicle 807 /// that the player is driving. 808 SimObjectPtr<NetObject> mScopeObject; 809 810 void clearGhostInfo(); 811 bool validateGhostArray(); 812 813 void ghostPacketDropped(PacketNotify *notify); 814 void ghostPacketReceived(PacketNotify *notify); 815 816 void ghostWritePacket(BitStream *bstream, PacketNotify *notify); 817 void ghostReadPacket(BitStream *bstream); 818 void freeGhostInfo(GhostInfo *); 819 820 void ghostWriteStartBlock(ResizeBitStream *stream); 821 void ghostReadStartBlock(BitStream *stream); 822 823 virtual void ghostWriteExtra(NetObject *,BitStream *) {} 824 virtual void ghostReadExtra(NetObject *,BitStream *, bool newGhost) {} 825 virtual void ghostPreRead(NetObject *, bool newGhost) {} 826 827 /// Called when 'EndGhosting' message is received from server. 828 virtual void onEndGhosting() {} 829 830public: 831 /// Some configuration values. 832 enum GhostConstants 833 { 834 GhostIdBitSize = 18, //262,144 ghosts 835 MaxGhostCount = 1 << GhostIdBitSize, //4096, 836 GhostLookupTableSize = 1 << GhostIdBitSize, //4096 837 GhostIndexBitSize = 4 // number of bits GhostIdBitSize-3 fits into 838 }; 839 840 U32 getGhostsActive() { return mGhostsActive;}; 841 842 /// Are we ghosting to someone? 843 bool isGhostingTo() { return mLocalGhosts != NULL; }; 844 845 /// Are we ghosting from someone? 846 bool isGhostingFrom() { return mGhostArray != NULL; }; 847 848 /// Called by onRemove, to shut down the ghost subsystem. 849 void ghostOnRemove(); 850 851 /// Called when we're done with normal scoping. 852 /// 853 /// This gives subclasses a chance to shove things into scope, such as 854 /// the results of a sensor network calculation, that would otherwise 855 /// be awkward to add. 856 virtual void doneScopingScene() { /* null */ } 857 858 /// Set the object around which we are currently scoping network traffic. 859 void setScopeObject(NetObject *object); 860 861 /// Get the object around which we are currently scoping network traffic. 862 NetObject *getScopeObject(); 863 864 /// Add an object to scope. 865 void objectInScope(NetObject *object); 866 867 /// Add an object to scope, marking that it should always be scoped to this connection. 868 void objectLocalScopeAlways(NetObject *object); 869 870 /// Mark an object that is being ghosted as not always needing to be scoped. 871 /// 872 /// This undoes objectLocalScopeAlways(), but doesn't immediately flush it from scope. 873 /// 874 /// Instead, the standard scoping mechanisms will clear it from scope when it is appropos 875 /// to do so. 876 void objectLocalClearAlways(NetObject *object); 877 878 /// Get a NetObject* from a ghost ID (on client side). 879 NetObject *resolveGhost(S32 id); 880 881 /// Get a NetObject* from a ghost index (on the server side). 882 NetObject *resolveObjectFromGhostIndex(S32 id); 883 884 /// Get the ghost index corresponding to a given NetObject. This is only 885 /// meaningful on the server side. 886 S32 getGhostIndex(NetObject *object); 887 888 /// Move a GhostInfo into the nonzero portion of the list (so that we know to update it). 889 void ghostPushNonZero(GhostInfo *gi); 890 891 /// Move a GhostInfo into the zero portion of the list (so that we know not to update it). 892 void ghostPushToZero(GhostInfo *gi); 893 894 /// Move a GhostInfo from the zero portion of the list to the free portion. 895 void ghostPushZeroToFree(GhostInfo *gi); 896 897 /// Move a GhostInfo from the free portion of the list to the zero portion. 898 inline void ghostPushFreeToZero(GhostInfo *info); 899 900 /// Stop all ghosting activity and inform the other side about this. 901 /// 902 /// Turns off ghosting. 903 void resetGhosting(); 904 905 /// Activate ghosting, once it's enabled. 906 void activateGhosting(); 907 908 /// Are we ghosting? 909 bool isGhosting() { return mGhosting; } 910 911 /// Begin to stop ghosting an object. 912 void detachObject(GhostInfo *info); 913 914 /// Mark an object to be always ghosted. Index is the ghost index of the object. 915 void setGhostAlwaysObject(NetObject *object, U32 index); 916 917 918 /// Send ghost connection handshake message. 919 /// 920 /// As part of the ghost connection process, extensive hand-shaking must be performed. 921 /// 922 /// This is done by passing ConnectionMessageEvents; this is a helper function 923 /// to more effectively perform this task. Messages are dealt with by 924 /// handleConnectionMessage(). 925 /// 926 /// @param message One of GhostStates 927 /// @param sequence A sequence number, if any. 928 /// @param ghostCount A count of ghosts relating to this message. 929 void sendConnectionMessage(U32 message, U32 sequence = 0, U32 ghostCount = 0); 930 931 /// Handle message from sendConnectionMessage(). 932 /// 933 /// This is called to handle messages sent via sendConnectionMessage. 934 /// 935 /// @param message One of GhostStates 936 /// @param sequence A sequence number, if any. 937 /// @param ghostCount A count of ghosts relating to this message. 938 virtual void handleConnectionMessage(U32 message, U32 sequence, U32 ghostCount); 939 940 /// Sends a signal to any object that needs to wait till everything has been ghosted 941 /// before performing an operation. 942 static Signal<void()> smGhostAlwaysDone; 943 944 /// @} 945public: 946//---------------------------------------------------------------- 947/// @name File transfer 948/// @{ 949 950protected: 951 /// List of files missing for this connection. 952 /// 953 /// The currently downloading file is always first in the list (ie, [0]). 954 Vector<char*> mMissingFileList; 955 956 /// Stream for currently uploading file (if any). 957 Stream *mCurrentDownloadingFile; 958 959 /// Storage for currently downloading file. 960 void *mCurrentFileBuffer; 961 962 /// Size of currently downloading file in bytes. 963 U32 mCurrentFileBufferSize; 964 965 /// Our position in the currently downloading file in bytes. 966 U32 mCurrentFileBufferOffset; 967 968 /// Number of files we have downloaded. 969 U32 mNumDownloadedFiles; 970 971 /// Error storage for file transfers. 972 String mLastFileErrorBuffer; 973 974 /// Structure to track ghost-always objects and their ghost indices. 975 struct GhostSave { 976 NetObject *ghost; 977 U32 index; 978 }; 979 980 /// List of objects to ghost-always. 981 Vector<GhostSave> mGhostAlwaysSaveList; 982 983public: 984 /// Start sending the specified file over the link. 985 bool startSendingFile(const char *fileName); 986 987 /// Called when we receive a FileChunkEvent. 988 void chunkReceived(U8 *chunkData, U32 chunkLen); 989 990 /// Get the next file... 991 void sendNextFileDownloadRequest(); 992 993 /// Post the next FileChunkEvent. 994 void sendFileChunk(); 995 996 /// Called when we finish downloading file data. 997 virtual void fileDownloadSegmentComplete(); 998 999 /// This is part of the file transfer logic; basically, we call this 1000 /// every time we finish downloading new files. It attempts to load 1001 /// the GhostAlways objects; if they fail, it marks an error and we 1002 /// have chance to retry. 1003 void loadNextGhostAlwaysObject(bool hadNewFiles); 1004/// @} 1005 1006//---------------------------------------------------------------- 1007/// @name Demo Recording 1008/// @{ 1009 1010private: 1011 Stream *mDemoWriteStream; 1012 Stream *mDemoReadStream; 1013 U32 mDemoNextBlockType; 1014 U32 mDemoNextBlockSize; 1015 1016 U32 mDemoWriteStartTime; 1017 U32 mDemoReadStartTime; 1018 U32 mDemoLastWriteTime; 1019 1020 U32 mDemoRealStartTime; 1021 1022public: 1023 enum DemoBlockTypes { 1024 BlockTypePacket, 1025 BlockTypeSendPacket, 1026 NetConnectionBlockTypeCount 1027 }; 1028 1029 enum DemoConstants { 1030 MaxNumBlockTypes = 16, 1031 MaxBlockSize = 0x1000, 1032 }; 1033 1034 bool isRecording() 1035 { return mDemoWriteStream != NULL; } 1036 bool isPlayingBack() 1037 { return mDemoReadStream != NULL; } 1038 1039 U32 getNextBlockType() { return mDemoNextBlockType; } 1040 void recordBlock(U32 type, U32 size, void *data); 1041 virtual void handleRecordedBlock(U32 type, U32 size, void *data); 1042 bool processNextBlock(); 1043 1044 bool startDemoRecord(const char *fileName); 1045 bool replayDemoRecord(const char *fileName); 1046 void startDemoRead(); 1047 void stopRecording(); 1048 void stopDemoPlayback(); 1049 1050 virtual void writeDemoStartBlock(ResizeBitStream *stream); 1051 virtual bool readDemoStartBlock(BitStream *stream); 1052 virtual void demoPlaybackComplete(); 1053/// @} 1054}; 1055 1056 1057//---------------------------------------------------------------------------- 1058/// Information about a ghosted object. 1059/// 1060/// @note If the size of this structure changes, the 1061/// NetConnection::getGhostIndex function MUST be changed 1062/// to reflect the new size. 1063struct GhostInfo 1064{ 1065public: // required for MSVC 1066 NetObject *obj; ///< The object being ghosted. 1067 U32 updateMask; ///< Flags indicating what state data needs to be transferred. 1068 1069 U32 updateSkipCount; ///< How many updates have we skipped this guy? 1070 U32 flags; ///< Flags from GhostInfo::Flags 1071 F32 priority; ///< A float value indicating the priority of this object for 1072 /// updates. 1073 1074 /// @name References 1075 /// 1076 /// The GhostInfo structure is used in several linked lists; these members are 1077 /// the implementation for this. 1078 /// @{ 1079 1080 NetConnection::GhostRef *updateChain; ///< List of references in NetConnections to us. 1081 1082 GhostInfo *nextObjectRef; ///< Next ghosted object. 1083 GhostInfo *prevObjectRef; ///< Previous ghosted object. 1084 NetConnection *connection; ///< Connection that we're ghosting over. 1085 GhostInfo *nextLookupInfo; ///< GhostInfo references are stored in a hash; this is the bucket 1086 /// implementation. 1087 1088 /// @} 1089 1090 U32 index; 1091 U32 arrayIndex; 1092 1093 /// Flags relating to the state of the object. 1094 enum Flags 1095 { 1096 Valid = BIT(0), 1097 InScope = BIT(1), 1098 ScopeAlways = BIT(2), 1099 NotYetGhosted = BIT(3), 1100 Ghosting = BIT(4), 1101 KillGhost = BIT(5), 1102 KillingGhost = BIT(6), 1103 ScopedEvent = BIT(7), 1104 ScopeLocalAlways = BIT(8), 1105 }; 1106}; 1107 1108inline void NetConnection::ghostPushNonZero(GhostInfo *info) 1109{ 1110 AssertFatal(info->arrayIndex >= mGhostZeroUpdateIndex && info->arrayIndex < mGhostFreeIndex, "Out of range arrayIndex."); 1111 AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object."); 1112 if(info->arrayIndex != mGhostZeroUpdateIndex) 1113 { 1114 mGhostArray[mGhostZeroUpdateIndex]->arrayIndex = info->arrayIndex; 1115 mGhostArray[info->arrayIndex] = mGhostArray[mGhostZeroUpdateIndex]; 1116 mGhostArray[mGhostZeroUpdateIndex] = info; 1117 info->arrayIndex = mGhostZeroUpdateIndex; 1118 } 1119 mGhostZeroUpdateIndex++; 1120 //AssertFatal(validateGhostArray(), "Invalid ghost array!"); 1121} 1122 1123inline void NetConnection::ghostPushToZero(GhostInfo *info) 1124{ 1125 AssertFatal(info->arrayIndex < mGhostZeroUpdateIndex, "Out of range arrayIndex."); 1126 AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object."); 1127 mGhostZeroUpdateIndex--; 1128 if(info->arrayIndex != mGhostZeroUpdateIndex) 1129 { 1130 mGhostArray[mGhostZeroUpdateIndex]->arrayIndex = info->arrayIndex; 1131 mGhostArray[info->arrayIndex] = mGhostArray[mGhostZeroUpdateIndex]; 1132 mGhostArray[mGhostZeroUpdateIndex] = info; 1133 info->arrayIndex = mGhostZeroUpdateIndex; 1134 } 1135 //AssertFatal(validateGhostArray(), "Invalid ghost array!"); 1136} 1137 1138inline void NetConnection::ghostPushZeroToFree(GhostInfo *info) 1139{ 1140 AssertFatal(info->arrayIndex >= mGhostZeroUpdateIndex && info->arrayIndex < mGhostFreeIndex, "Out of range arrayIndex."); 1141 AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object."); 1142 mGhostFreeIndex--; 1143 if(info->arrayIndex != mGhostFreeIndex) 1144 { 1145 mGhostArray[mGhostFreeIndex]->arrayIndex = info->arrayIndex; 1146 mGhostArray[info->arrayIndex] = mGhostArray[mGhostFreeIndex]; 1147 mGhostArray[mGhostFreeIndex] = info; 1148 info->arrayIndex = mGhostFreeIndex; 1149 } 1150 //AssertFatal(validateGhostArray(), "Invalid ghost array!"); 1151} 1152 1153inline void NetConnection::ghostPushFreeToZero(GhostInfo *info) 1154{ 1155 AssertFatal(info->arrayIndex >= mGhostFreeIndex, "Out of range arrayIndex."); 1156 AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object."); 1157 if(info->arrayIndex != mGhostFreeIndex) 1158 { 1159 mGhostArray[mGhostFreeIndex]->arrayIndex = info->arrayIndex; 1160 mGhostArray[info->arrayIndex] = mGhostArray[mGhostFreeIndex]; 1161 mGhostArray[mGhostFreeIndex] = info; 1162 info->arrayIndex = mGhostFreeIndex; 1163 } 1164 mGhostFreeIndex++; 1165 //AssertFatal(validateGhostArray(), "Invalid ghost array!"); 1166} 1167 1168#endif 1169 1170 1171
