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