123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "core/dnet.h"
- #include "console/simBase.h"
- #include "sim/netConnection.h"
- #include "core/stream/bitStream.h"
- #include "core/stream/fileStream.h"
- #ifndef TORQUE_TGB_ONLY
- #include "scene/pathManager.h"
- #endif
- #include "console/consoleTypes.h"
- #include "sim/netInterface.h"
- #include "console/engineAPI.h"
- #include <stdarg.h>
- IMPLEMENT_SCOPE( NetAPI, Net,, "Networking functionality." );
- IMPLEMENT_NONINSTANTIABLE_CLASS( NetEvent,
- "An event to be sent over the network." )
- END_IMPLEMENT_CLASS;
- S32 gNetBitsSent = 0;
- extern S32 gNetBitsReceived;
- U32 gGhostUpdates = 0;
- enum NetConnectionConstants {
- PingTimeout = 4500, ///< milliseconds
- DefaultPingRetryCount = 15,
- };
- SimObjectPtr<NetConnection> NetConnection::mServerConnection;
- SimObjectPtr<NetConnection> NetConnection::mLocalClientConnection;
- //----------------------------------------------------------------------
- /// ConnectionMessageEvent
- ///
- /// This event is used inside by the connection and subclasses to message
- /// itself when sequencing events occur. Right now, the message event
- /// only uses 6 bits to transmit the message, so
- class ConnectionMessageEvent : public NetEvent
- {
- U32 sequence;
- U32 message;
- U32 ghostCount;
- public:
- typedef NetEvent Parent;
- ConnectionMessageEvent(U32 msg=0, U32 seq=0, U32 gc=0)
- { message = msg; sequence = seq; ghostCount = gc;}
- void pack(NetConnection *, BitStream *bstream)
- {
- bstream->write(sequence);
- bstream->writeInt(message, 3);
- bstream->writeInt(ghostCount, NetConnection::GhostIdBitSize + 1);
- }
- void write(NetConnection *, BitStream *bstream)
- {
- bstream->write(sequence);
- bstream->writeInt(message, 3);
- bstream->writeInt(ghostCount, NetConnection::GhostIdBitSize + 1);
- }
- void unpack(NetConnection *, BitStream *bstream)
- {
- bstream->read(&sequence);
- message = bstream->readInt(3);
- ghostCount = bstream->readInt(NetConnection::GhostIdBitSize + 1);
- }
- void process(NetConnection *ps)
- {
- ps->handleConnectionMessage(message, sequence, ghostCount);
- }
- DECLARE_CONOBJECT(ConnectionMessageEvent);
- };
- IMPLEMENT_CO_NETEVENT_V1(ConnectionMessageEvent);
- ConsoleDocClass( ConnectionMessageEvent,
- "@brief This event is used inside by the connection and subclasses to message itself when sequencing events occur.\n\n"
- "Not intended for game development, for editors or internal use only.\n\n "
- "@internal");
- void NetConnection::sendConnectionMessage(U32 message, U32 sequence, U32 ghostCount)
- {
- postNetEvent(new ConnectionMessageEvent(message, sequence, ghostCount));
- }
- //--------------------------------------------------------------------
- IMPLEMENT_CONOBJECT(NetConnection);
- ConsoleDocClass( NetConnection,
- "@brief Provides the basis for implementing a multiplayer game protocol.\n\n"
- "NetConnection combines a low-level notify protocol implemented in ConnectionProtocol with a SimGroup, "
- "and implements several distinct subsystems:\n\n"
- "- <b>Event Manager</b> This is responsible for transmitting NetEvents over the wire. "
- "It deals with ensuring that the various types of NetEvents are delivered appropriately, "
- "and with notifying the event of its delivery status.\n\n"
- "- <b>Move Manager</b> This is responsible for transferring a Move to the server 32 "
- "times a second (on the client) and applying it to the control object (on the server).\n\n"
- "- <b>Ghost Manager</b> This is responsible for doing scoping calculations (on the server "
- "side) and transmitting most-recent ghost information to the client.\n\n"
- "- <b>File Transfer</b> It is often the case that clients will lack important files when "
- "connecting to a server which is running a mod or new map. This subsystem allows the "
- "server to transfer such files to the client.\n\n"
- "- <b>Networked String Table</b> String data can easily soak up network bandwidth, so for "
- "efficiency, we implement a networked string table. We can then notify the connection "
- "of strings we will reference often, such as player names, and transmit only a tag, "
- "instead of the whole string.\n\n"
- "- <b>Demo Recording</b> A demo in Torque is a log of the network traffic between client "
- "and server; when a NetConnection records a demo, it simply logs this data to a file. When "
- "it plays a demo back, it replays the logged data.\n\n"
- "- <b>Connection Database</b> This is used to keep track of all the NetConnections; it can "
- "be iterated over (for instance, to send an event to all active connections), or queried "
- "by address.\n\n"
- "The NetConnection is a SimGroup. On the client side, it contains all the objects which have been "
- "ghosted to that client. On the server side, it is empty; it can be used (typically in script) to "
- "hold objects related to the connection. For instance, you might place an observation camera in the "
- "NetConnnection. In both cases, when the connection is destroyed, so are the contained objects.\n\n"
- "The NetConnection also has the concept of local connections. These are used when the client and "
- "server reside in the same process. A local connection is typically required to use the standard "
- "Torque world building tools. A local connection is also required when building a single player "
- "game.\n\n"
- "@see @ref Networking, @ref ghosting_scoping, @ref netconnection_simgroup, @ref local_connections, GameConnection, AIConnection, and AIClient.\n\n"
- "@ingroup Networking\n");
- NetConnection* NetConnection::mConnectionList = NULL;
- NetConnection* NetConnection::mHashTable[NetConnection::HashTableSize] = { NULL, };
- bool NetConnection::mFilesWereDownloaded = false;
- static inline U32 HashNetAddress(const NetAddress *addr)
- {
- return addr->getHash() % NetConnection::HashTableSize;
- }
- NetConnection *NetConnection::lookup(const NetAddress *addr)
- {
- U32 hashIndex = HashNetAddress(addr);
- for(NetConnection *walk = mHashTable[hashIndex]; walk; walk = walk->mNextTableHash)
- if(Net::compareAddresses(addr, walk->getNetAddress()))
- return walk;
- return NULL;
- }
- void NetConnection::netAddressTableInsert()
- {
- U32 hashIndex = HashNetAddress(&mNetAddress);
- mNextTableHash = mHashTable[hashIndex];
- mHashTable[hashIndex] = this;
- }
- void NetConnection::netAddressTableRemove()
- {
- U32 hashIndex = HashNetAddress(&mNetAddress);
- NetConnection **walk = &mHashTable[hashIndex];
- while(*walk)
- {
- if(*walk == this)
- {
- *walk = mNextTableHash;
- mNextTableHash = NULL;
- return;
- }
- walk = &((*walk)->mNextTableHash);
- }
- }
- void NetConnection::setNetAddress(const NetAddress *addr)
- {
- mNetAddress = *addr;
- }
- const NetAddress *NetConnection::getNetAddress()
- {
- return &mNetAddress;
- }
- void NetConnection::setSequence(U32 sequence)
- {
- mConnectSequence = sequence;
- }
- U32 NetConnection::getSequence()
- {
- return mConnectSequence;
- }
- static U32 gPacketRateToServer = 32;
- static U32 gPacketUpdateDelayToServer = 32;
- static U32 gPacketRateToClient = 10;
- static U32 gPacketSize = 508;
- void NetConnection::consoleInit()
- {
- Con::addVariable("$pref::Net::PacketRateToServer", TypeS32, &gPacketRateToServer,
- "@brief Sets how often packets are sent from the client to the server.\n\n"
- "It is possible to control how often packets may be sent to the server. This may be "
- "used to throttle the amount of bandwidth being used, but should be adjusted with "
- "caution.\n\n"
- "The actual formula used to calculate the delay between sending packets to the server is:\n"
- "@code\n"
- "Packet Update Delay To Server = 1024 / $pref::Net::PacketRateToServer"
- "@endcode\n"
- "with the result in ms. A minimum rate of 8 is enforced in the source code. The default "
- "value is 32.\n\n"
- "@note When using a local connection (@ref local_connections) be aware that this variable "
- "is always forced to 128.\n\n"
- "@ingroup Networking");
- Con::addVariable("$pref::Net::PacketRateToClient", TypeS32, &gPacketRateToClient,
- "@brief Sets how often packets are sent from the server to a client.\n\n"
- "It is possible to control how often packets may be sent to the clients. This may be "
- "used to throttle the amount of bandwidth being used, but should be adjusted with "
- "caution.\n\n"
- "The actual formula used to calculate the delay between sending packets to a client is:\n"
- "@code\n"
- "Packet Update Delay To Client = 1024 / $pref::Net::PacketRateToClient"
- "@endcode\n"
- "with the result in ms. A minimum rate of 1 is enforced in the source code. The default "
- "value is 10.\n\n"
- "@note When using a local connection (@ref local_connections) be aware that this variable "
- "is always forced to 128.\n\n"
- "@ingroup Networking");
- Con::addVariable("$pref::Net::PacketSize", TypeS32, &gPacketSize,
- "@brief Sets the maximum size in bytes an individual network packet may be.\n\n"
- "It is possible to control how large each individual network packet may be. Increasing "
- "its size from the default allows for more data to be sent on each network send. "
- "However, this value should be changed with caution as too large a value will cause "
- "packets to be split up by the networking platform or hardware, which is something "
- "Torque cannot handle.\n\n"
- "A minimum packet size of 100 bytes is enforced in the source code. There is no "
- "enforced maximum. The default value is 200 bytes.\n\n"
- "@note When using a local connection (@ref local_connections) be aware that this variable "
- "is always forced to 1024 bytes.\n\n"
- "@ingroup Networking");
- Con::addVariable("$Stats::netBitsSent", TypeS32, &gNetBitsSent,
- "@brief The number of bytes sent during the last packet send operation.\n\n"
- "@note Even though this variable has 'Bits' in it, the value is indeed reported in bytes. This name "
- "is a legacy holdover and remains for compatibility reasons.\n"
- "@ingroup Networking");
- Con::addVariable("$Stats::netBitsReceived", TypeS32, &gNetBitsReceived,
- "@brief The number of bytes received during the last packet process operation.\n\n"
- "@note Even though this variable has 'Bits' in it, the value is indeed reported in bytes. This name "
- "is a legacy holdover and remains for compatibility reasons.\n"
- "@ingroup Networking");
- Con::addVariable("$Stats::netGhostUpdates", TypeS32, &gGhostUpdates,
- "@brief The total number of ghosts added, removed, and/or updated on the client "
- "during the last packet process operation.\n\n"
- "@ingroup Networking");
- }
- void NetConnection::checkMaxRate()
- {
- // Enforce some minimum limits to the network settings.
- gPacketRateToServer = getMax( gPacketRateToServer, (U32)8 );
- gPacketRateToClient = getMax( gPacketRateToClient, (U32)1 );
- gPacketSize = getMax( gPacketSize, (U32)100 );
- U32 packetRateToServer = gPacketRateToServer;
- U32 packetRateToClient = gPacketRateToClient;
- U32 packetSize = gPacketSize;
- if (isLocalConnection())
- {
- packetRateToServer = 128;
- packetRateToClient = 128;
- // These changes introduced in T3D 1.1 Preview reduce the packet headroom which leads
- // to some spells and effects running out of room when dynamic variables are used
- // to send launch-time parameters to clients.
- packetSize = 512;
- }
- gPacketUpdateDelayToServer = 1024 / packetRateToServer;
- U32 toClientUpdateDelay = 1024 / packetRateToClient;
- if(mMaxRate.updateDelay != toClientUpdateDelay || mMaxRate.packetSize != packetSize)
- {
- mMaxRate.updateDelay = toClientUpdateDelay;
- mMaxRate.packetSize = packetSize;
- mMaxRate.changed = true;
- }
- }
- void NetConnection::setSendingEvents(bool sending)
- {
- AssertFatal(!mEstablished, "Error, cannot change event behavior after a connection has been established.");
- mSendingEvents = sending;
- }
- void NetConnection::setTranslatesStrings(bool xl)
- {
- AssertFatal(!mEstablished, "Error, cannot change event behavior after a connection has been established.");
- mTranslateStrings = xl;
- if(mTranslateStrings)
- mStringTable = new ConnectionStringTable(this);
- }
- void NetConnection::setNetClassGroup(U32 grp)
- {
- AssertFatal(!mEstablished, "Error, cannot change net class group after a connection has been established.");
- mNetClassGroup = grp;
- }
- NetConnection::NetConnection()
- : mNetAddress()
- {
- mTranslateStrings = false;
- mConnectSequence = 0;
- mStringTable = NULL;
- mSendingEvents = true;
- mNetClassGroup = NetClassGroupGame;
- AssertFatal(mNetClassGroup >= NetClassGroupGame && mNetClassGroup < NetClassGroupsCount,
- "Invalid net event class type.");
- mSimulatedPing = 0;
- mSimulatedPacketLoss = 0;
- #ifdef TORQUE_DEBUG_NET
- mLogging = false;
- #endif
- mEstablished = false;
- mLastUpdateTime = 0;
- mRoundTripTime = 0;
- mPacketLoss = 0;
- mNextTableHash = NULL;
- mSendDelayCredit = 0;
- mConnectionState = NotConnected;
- mCurrentDownloadingFile = NULL;
- mCurrentFileBuffer = NULL;
- mNextConnection = NULL;
- mPrevConnection = NULL;
- mNotifyQueueHead = NULL;
- mNotifyQueueTail = NULL;
- mCurRate.updateDelay = 102;
- mCurRate.packetSize = 200;
- mCurRate.changed = false;
- mMaxRate.updateDelay = 102;
- mMaxRate.packetSize = 200;
- mMaxRate.changed = false;
- checkMaxRate();
- // event management data:
- mNotifyEventList = NULL;
- mSendEventQueueHead = NULL;
- mSendEventQueueTail = NULL;
- mUnorderedSendEventQueueHead = NULL;
- mUnorderedSendEventQueueTail = NULL;
- mWaitSeqEvents = NULL;
- mNextSendEventSeq = FirstValidSendEventSeq;
- mNextRecvEventSeq = FirstValidSendEventSeq;
- mLastAckedEventSeq = -1;
- // ghost management data:
- mScopeObject = NULL;
- mGhostingSequence = 0;
- mGhosting = false;
- mScoping = false;
- mGhostArray = NULL;
- mGhostRefs = NULL;
- mGhostLookupTable = NULL;
- mLocalGhosts = NULL;
- mGhostsActive = 0;
- mMissionPathsSent = false;
- mDemoWriteStream = NULL;
- mDemoReadStream = NULL;
- mPingSendCount = 0;
- mPingRetryCount = DefaultPingRetryCount;
- mLastPingSendTime = Platform::getVirtualMilliseconds();
- mCurrentDownloadingFile = NULL;
- mCurrentFileBuffer = NULL;
- mCurrentFileBufferSize = 0;
- mCurrentFileBufferOffset = 0;
- mNumDownloadedFiles = 0;
- // Disable starting a new journal recording or playback from here on
- Journal::Disable();
- // Ensure NetAddress is cleared
- dMemset(&mNetAddress, '\0', sizeof(NetAddress));
- }
- NetConnection::~NetConnection()
- {
- AssertFatal(mNotifyQueueHead == NULL, "Uncleared notifies remain.");
- netAddressTableRemove();
- dFree(mCurrentFileBuffer);
- if(mCurrentDownloadingFile)
- delete mCurrentDownloadingFile;
- delete[] mLocalGhosts;
- delete[] mGhostLookupTable;
- delete[] mGhostRefs;
- delete[] mGhostArray;
- delete mStringTable;
- if(mDemoWriteStream)
- delete mDemoWriteStream;
- if(mDemoReadStream)
- delete mDemoReadStream;
- }
- NetConnection::PacketNotify::PacketNotify()
- {
- rateChanged = false;
- maxRateChanged = false;
- sendTime = 0;
- eventList = 0;
- ghostList = 0;
- subList = NULL;
- nextPacket = NULL;
- }
- bool NetConnection::checkTimeout(U32 time)
- {
- if(!isNetworkConnection())
- return false;
- if(time > mLastPingSendTime + PingTimeout)
- {
- if(mPingSendCount >= mPingRetryCount)
- return true;
- mLastPingSendTime = time;
- mPingSendCount++;
- sendPingPacket();
- }
- return false;
- }
- void NetConnection::keepAlive()
- {
- mLastPingSendTime = Platform::getVirtualMilliseconds();
- mPingSendCount = 0;
- }
- void NetConnection::handleConnectionEstablished()
- {
- }
- //--------------------------------------------------------------------------
- #ifndef TORQUE_TGB_ONLY
- DefineEngineMethod( NetConnection, transmitPaths, void, (),,
- "@brief Sent by the server during phase 2 of the mission download to update motion spline paths.\n\n"
- "The server transmits all spline motion paths that are within the mission (Path) separate from "
- "other objects. This is due to the potentially large number of nodes within each path, which may "
- "saturate a packet sent to the client. By managing this step separately, Torque has finer control "
- "over how packets are organised vs. doing it during the ghosting stage.\n\n"
- "Internally a PathManager is used to track all paths defined within a mission on the server, and each "
- "one is transmitted using a PathManagerEvent. The client side collects these events and builds the "
- "given paths within its own PathManager. This is typically done during the standard mission start "
- "phase 2 when following Torque's example mission startup sequence.\n\n"
- "When a mission is ended, all paths need to be cleared from their respective path managers."
- "@tsexample\n"
- "function serverCmdMissionStartPhase2Ack(%client, %seq, %playerDB)\n"
- "{\n"
- " // Make sure to ignore calls from a previous mission load\n"
- " if (%seq != $missionSequence || !$MissionRunning)\n"
- " return;\n"
- " if (%client.currentPhase != 1.5)\n"
- " return;\n"
- " %client.currentPhase = 2;\n"
- "\n"
- " // Set the player datablock choice\n"
- " %client.playerDB = %playerDB;\n"
- "\n"
- " // Update mission paths (SimPath), this needs to get there before the objects.\n"
- " %client.transmitPaths();\n"
- "\n"
- " // Start ghosting objects to the client\n"
- " %client.activateGhosting();\n"
- "}\n"
- "@endtsexample\n"
-
- "@see clearPaths()\n"
- "@see Path\n")
- {
- gServerPathManager->transmitPaths(object);
- object->setMissionPathsSent(true);
- }
- DefineEngineMethod( NetConnection, clearPaths, void, (),,
- "@brief On the server, resets the connection to indicate that motion spline paths have not been transmitted.\n\n"
- "Typically when a mission has ended on the server, all connected clients are informed of this change "
- "and their connections are reset back to a starting state. This method resets a connection on the "
- "server to indicate that motion spline paths have not been transmitted.\n\n"
- "@tsexample\n"
- " // Inform the clients\n"
- " for (%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++)\n"
- " {\n"
- " // clear ghosts and paths from all clients\n"
- " %cl = ClientGroup.getObject(%clientIndex);\n"
- " %cl.endMission();\n"
- " %cl.resetGhosting();\n"
- " %cl.clearPaths();\n"
- " }\n"
- "@endtsexample\n"
-
- "@see transmitPaths()\n"
- "@see Path\n")
- {
- object->setMissionPathsSent(false);
- }
- #endif
- DefineEngineMethod( NetConnection, getAddress, const char *, (),,
- "@brief Returns the far end network address for the connection.\n\n"
- "The address will be in one of the following forms:\n"
- "- <b>IP:Broadcast:<port></b> for broadcast type addresses\n"
- "- <b>IP:<address>:<port></b> for IP addresses\n"
- "- <b>local</b> when connected locally (server and client running in same process\n")
- {
- if(object->isLocalConnection())
- return "local";
- char *buffer = Con::getReturnBuffer(256);
- Net::addressToString(object->getNetAddress(), buffer);
- return buffer;
- }
- DefineEngineMethod( NetConnection, setSimulatedNetParams, void, (F32 packetLoss, S32 delay),,
- "@brief Simulate network issues on the connection for testing.\n\n"
- "@param packetLoss The fraction of packets that will be lost. Ranges from 0.0 (no loss) to 1.0 (complete loss)\n"
- "@param delay Delays packets being transmitted by simulating a particular ping. This is an absolute "
- "integer, measured in ms.\n")
- {
- object->setSimulatedNetParams(packetLoss, delay);
- }
- DefineEngineMethod( NetConnection, getPing, S32, (),,
- "@brief Returns the average round trip time (in ms) for the connection.\n\n"
- "The round trip time is recalculated every time a notify packet is received. Notify "
- "packets are used to information the connection that the far end successfully received "
- "the sent packet.\n")
- {
- return( S32( object->getRoundTripTime() ) );
- }
- DefineEngineMethod( NetConnection, getPacketLoss, S32, (),,
- "@brief Returns the percentage of packets lost per tick.\n\n"
- "@note This method is not yet hooked up.\n")
- {
- return( S32( 100 * object->getPacketLoss() ) );
- }
- DefineEngineMethod( NetConnection, checkMaxRate, void, (),,
- "@brief Ensures that all configured packet rates and sizes meet minimum requirements.\n\n"
- "This method is normally only called when a NetConnection class is first constructed. It need "
- "only be manually called if the global variables that set the packet rate or size have changed.\n\n"
- "@note If @$pref::Net::PacketRateToServer, @$pref::Net::PacketRateToClient or @$pref::Net::PacketSize "
- "have been changed since a NetConnection has been created, this method must be called on "
- "all connections for them to follow the new rates or size.\n")
- {
- object->checkMaxRate();
- }
- #ifdef TORQUE_DEBUG_NET
- DefineEngineMethod( NetConnection, setLogging, void, (bool state),,
- "@brief Sets if debug statements should be written to the console log.\n\n"
- "@note Only valid if the executable has been compiled with TORQUE_DEBUG_NET.\n")
- {
- object->setLogging(state);
- }
- #endif
- //--------------------------------------------------------------------
- void NetConnection::setEstablished()
- {
- AssertFatal(!mEstablished, "NetConnection::setEstablished - Error, this NetConnection has already been established.");
- mEstablished = true;
- mNextConnection = mConnectionList;
- if(mConnectionList)
- mConnectionList->mPrevConnection = this;
- mConnectionList = this;
- if(isNetworkConnection())
- netAddressTableInsert();
- }
- void NetConnection::onRemove()
- {
- // delete any ghosts that may exist for this connection, but aren't added
- while(mGhostAlwaysSaveList.size())
- {
- delete mGhostAlwaysSaveList[0].ghost;
- mGhostAlwaysSaveList.pop_front();
- }
- if(mNextConnection)
- mNextConnection->mPrevConnection = mPrevConnection;
- if(mPrevConnection)
- mPrevConnection->mNextConnection = mNextConnection;
- if(mConnectionList == this)
- mConnectionList = mNextConnection;
- while(mNotifyQueueHead)
- handleNotify(false);
- ghostOnRemove();
- eventOnRemove();
- Parent::onRemove();
- }
- String NetConnection::mErrorBuffer;
- void NetConnection::setLastError(const char *fmt, ...)
- {
- va_list argptr;
- va_start(argptr, fmt);
- mErrorBuffer = String::ToString(fmt, argptr);
- va_end(argptr);
- #ifdef TORQUE_DEBUG_NET
- // setLastErrors assert in net_debug builds
- AssertFatal(false, mErrorBuffer.c_str());
- #endif
- }
- //--------------------------------------------------------------------
- void NetConnection::handleNotify(bool recvd)
- {
- // Con::printf("NET %d: NOTIFY - %d %s", getId(), gPacketId, recvd ? "RECVD" : "DROPPED");
- PacketNotify *note = mNotifyQueueHead;
- AssertFatal(note != NULL, "Error: got a notify with a null notify head.");
- mNotifyQueueHead = mNotifyQueueHead->nextPacket;
- if(note->rateChanged && !recvd)
- mCurRate.changed = true;
- if(note->maxRateChanged && !recvd)
- mMaxRate.changed = true;
- if(recvd)
- {
- // Running average of roundTrip time
- U32 curTime = Platform::getVirtualMilliseconds();
- mRoundTripTime = (mRoundTripTime + (curTime - note->sendTime)) * 0.5;
- packetReceived(note);
- }
- else
- packetDropped(note);
- delete note;
- }
- void NetConnection::processRawPacket(BitStream *bstream)
- {
- if(mDemoWriteStream)
- recordBlock(BlockTypePacket, bstream->getReadByteSize(), bstream->getBuffer());
- ConnectionProtocol::processRawPacket(bstream);
- }
- void NetConnection::handlePacket(BitStream *bstream)
- {
- // Con::printf("NET %d: RECV - %d", getId(), mLastSeqRecvd);
- // clear out any errors
- mErrorBuffer = String();
- if(bstream->readFlag())
- {
- mCurRate.updateDelay = bstream->readInt(12);
- mCurRate.packetSize = bstream->readInt(12);
- }
- if(bstream->readFlag())
- {
- U32 omaxDelay = bstream->readInt(12);
- S32 omaxSize = bstream->readInt(12);
- if(omaxDelay < mMaxRate.updateDelay)
- omaxDelay = mMaxRate.updateDelay;
- if(omaxSize > mMaxRate.packetSize)
- omaxSize = mMaxRate.packetSize;
- if(omaxDelay != mCurRate.updateDelay || omaxSize != mCurRate.packetSize)
- {
- mCurRate.updateDelay = omaxDelay;
- mCurRate.packetSize = omaxSize;
- mCurRate.changed = true;
- }
- }
- readPacket(bstream);
- if(mErrorBuffer.isNotEmpty())
- connectionError(mErrorBuffer);
- }
- void NetConnection::connectionError(const char *errorString)
- {
- TORQUE_UNUSED(errorString);
- }
- //--------------------------------------------------------------------
- NetConnection::PacketNotify *NetConnection::allocNotify()
- {
- return new PacketNotify;
- }
- /// Used when simulating lag.
- ///
- /// We post this SimEvent when we want to send a packet; it delays for a bit, then
- /// sends the actual packet.
- class NetDelayEvent : public SimEvent
- {
- U8 buffer[Net::MaxPacketDataSize];
- BitStream stream;
- public:
- NetDelayEvent(BitStream *inStream) : stream(NULL, 0)
- {
- dMemcpy(buffer, inStream->getBuffer(), inStream->getPosition());
- stream.setBuffer(buffer, inStream->getPosition());
- stream.setPosition(inStream->getPosition());
- }
- void process(SimObject *object)
- {
- ((NetConnection *) object)->sendPacket(&stream);
- }
- };
- void NetConnection::checkPacketSend(bool force)
- {
- U32 curTime = Platform::getVirtualMilliseconds();
- U32 delay = isConnectionToServer() ? gPacketUpdateDelayToServer : mCurRate.updateDelay;
- if(!force)
- {
- if(curTime < mLastUpdateTime + delay - mSendDelayCredit)
- return;
- mSendDelayCredit = curTime - (mLastUpdateTime + delay - mSendDelayCredit);
- if(mSendDelayCredit > 1000)
- mSendDelayCredit = 1000;
- if(mDemoWriteStream)
- recordBlock(BlockTypeSendPacket, 0, 0);
- }
- if(windowFull())
- return;
- BitStream *stream = BitStream::getPacketStream(mCurRate.packetSize);
- buildSendPacketHeader(stream);
- mLastUpdateTime = curTime;
- PacketNotify *note = allocNotify();
- if(!mNotifyQueueHead)
- mNotifyQueueHead = note;
- else
- mNotifyQueueTail->nextPacket = note;
- mNotifyQueueTail = note;
- note->nextPacket = NULL;
- note->sendTime = curTime;
- note->rateChanged = mCurRate.changed;
- note->maxRateChanged = mMaxRate.changed;
- if(stream->writeFlag(mCurRate.changed))
- {
- stream->writeInt(mCurRate.updateDelay, 12);
- stream->writeInt(mCurRate.packetSize, 12);
- mCurRate.changed = false;
- }
- if(stream->writeFlag(mMaxRate.changed))
- {
- stream->writeInt(mMaxRate.updateDelay, 12);
- stream->writeInt(mMaxRate.packetSize, 12);
- mMaxRate.changed = false;
- }
- #ifdef TORQUE_DEBUG_NET
- U32 start = stream->getCurPos();
- #endif
- DEBUG_LOG(("PKLOG %d START", getId()) );
- writePacket(stream, note);
- DEBUG_LOG(("PKLOG %d END - %d", getId(), stream->getCurPos() - start) );
- if(mSimulatedPacketLoss && Platform::getRandom() < mSimulatedPacketLoss)
- {
- //Con::printf("NET %d: SENDDROP - %d", getId(), mLastSendSeq);
- return;
- }
- if(mSimulatedPing)
- {
- Sim::postEvent(getId(), new NetDelayEvent(stream), Sim::getCurrentTime() + mSimulatedPing);
- return;
- }
- sendPacket(stream);
- }
- Net::Error NetConnection::sendPacket(BitStream *stream)
- {
- //Con::printf("NET %d: SEND - %d", getId(), mLastSendSeq);
- // do nothing on send if this is a demo replay.
- if(mDemoReadStream)
- return Net::NoError;
- gNetBitsSent = stream->getPosition();
- if(isLocalConnection())
- {
- // short circuit connection to the other side.
- // handle the packet, then force a notify.
- stream->setBuffer(stream->getBuffer(), stream->getPosition(), stream->getPosition());
- mRemoteConnection->processRawPacket(stream);
- return Net::NoError;
- }
- else
- {
- return Net::sendto(getNetAddress(), stream->getBuffer(), stream->getPosition());
- }
- }
- //--------------------------------------------------------------------
- //--------------------------------------------------------------------
- // these are the virtual function defs for Connection -
- // if your subclass has additional data to read / write / notify, add it in these functions.
- void NetConnection::readPacket(BitStream *bstream)
- {
- eventReadPacket(bstream);
- ghostReadPacket(bstream);
- }
- void NetConnection::writePacket(BitStream *bstream, PacketNotify *note)
- {
- eventWritePacket(bstream, note);
- ghostWritePacket(bstream, note);
- }
- void NetConnection::packetReceived(PacketNotify *note)
- {
- eventPacketReceived(note);
- ghostPacketReceived(note);
- }
- void NetConnection::packetDropped(PacketNotify *note)
- {
- eventPacketDropped(note);
- ghostPacketDropped(note);
- }
- //--------------------------------------------------------------------
- //--------------------------------------------------------------------
- void NetConnection::writeDemoStartBlock(ResizeBitStream* stream)
- {
- ConnectionProtocol::writeDemoStartBlock(stream);
- stream->write(mRoundTripTime);
- stream->write(mPacketLoss);
- #ifndef TORQUE_TGB_ONLY
- // Write all the current paths to the stream...
- gClientPathManager->dumpState(stream);
- #endif
- stream->validate();
- mStringTable->writeDemoStartBlock(stream);
- U32 start = 0;
- PacketNotify *note = mNotifyQueueHead;
- while(note)
- {
- start++;
- note = note->nextPacket;
- }
- stream->write(start);
- eventWriteStartBlock(stream);
- ghostWriteStartBlock(stream);
- }
- bool NetConnection::readDemoStartBlock(BitStream* stream)
- {
- ConnectionProtocol::readDemoStartBlock(stream);
- stream->read(&mRoundTripTime);
- stream->read(&mPacketLoss);
- #ifndef TORQUE_TGB_ONLY
- // Read
- gClientPathManager->readState(stream);
- #endif
- mStringTable->readDemoStartBlock(stream);
- U32 pos;
- stream->read(&pos); // notify count
- for(U32 i = 0; i < pos; i++)
- {
- PacketNotify *note = allocNotify();
- note->nextPacket = NULL;
- if(!mNotifyQueueHead)
- mNotifyQueueHead = note;
- else
- mNotifyQueueTail->nextPacket = note;
- mNotifyQueueTail = note;
- }
- eventReadStartBlock(stream);
- ghostReadStartBlock(stream);
- return true;
- }
- bool NetConnection::startDemoRecord(const char *fileName)
- {
- FileStream *fs = new FileStream;
- if((fs = FileStream::createAndOpen( fileName, Torque::FS::File::Write )) == NULL)
- return false;
- mDemoWriteStream = fs;
- mDemoWriteStream->write(mProtocolVersion);
- ResizeBitStream bs;
- // then write out the start block
- writeDemoStartBlock(&bs);
- U32 size = bs.getPosition() + 1;
- mDemoWriteStream->write(size);
- mDemoWriteStream->write(size, bs.getBuffer());
- return true;
- }
- bool NetConnection::replayDemoRecord(const char *fileName)
- {
- Stream *fs;
- if((fs = FileStream::createAndOpen( fileName, Torque::FS::File::Read )) == NULL)
- return false;
- mDemoReadStream = fs;
- mDemoReadStream->read(&mProtocolVersion);
- U32 size;
- mDemoReadStream->read(&size);
- U8 *block = new U8[size];
- mDemoReadStream->read(size, block);
- BitStream bs(block, size);
- bool res = readDemoStartBlock(&bs);
- delete[] block;
- if(!res)
- return false;
- // prep for first block read
- // type/size stored in U16: [type:4][size:12]
- U16 typeSize;
- mDemoReadStream->read(&typeSize);
- mDemoNextBlockType = typeSize >> 12;
- mDemoNextBlockSize = typeSize & 0xFFF;
- if(mDemoReadStream->getStatus() != Stream::Ok)
- return false;
- return true;
- }
- void NetConnection::stopRecording()
- {
- if(mDemoWriteStream)
- {
- delete mDemoWriteStream;
- mDemoWriteStream = NULL;
- }
- }
- void NetConnection::recordBlock(U32 type, U32 size, void *data)
- {
- AssertFatal(type < MaxNumBlockTypes, "NetConnection::recordBlock: invalid type");
- AssertFatal(size < MaxBlockSize, "NetConnection::recordBlock: invalid size");
- if((type >= MaxNumBlockTypes) || (size >= MaxBlockSize))
- return;
- if(mDemoWriteStream)
- {
- // store type/size in U16: [type:4][size:12]
- U16 typeSize = (type << 12) | size;
- mDemoWriteStream->write(typeSize);
- if(size)
- mDemoWriteStream->write(size, data);
- }
- }
- void NetConnection::handleRecordedBlock(U32 type, U32 size, void *data)
- {
- switch(type)
- {
- case BlockTypePacket: {
- BitStream bs(data, size);
- processRawPacket(&bs);
- break;
- }
- case BlockTypeSendPacket:
- checkPacketSend(true);
- break;
- }
- }
- void NetConnection::demoPlaybackComplete()
- {
- }
- void NetConnection::stopDemoPlayback()
- {
- demoPlaybackComplete();
- deleteObject();
- }
- bool NetConnection::processNextBlock()
- {
- U8 buffer[Net::MaxPacketDataSize];
- // read in and handle
- if(mDemoReadStream->read(mDemoNextBlockSize, buffer))
- handleRecordedBlock(mDemoNextBlockType, mDemoNextBlockSize, buffer);
- U16 typeSize;
- mDemoReadStream->read(&typeSize);
- mDemoNextBlockType = typeSize >> 12;
- mDemoNextBlockSize = typeSize & 0xFFF;
- if(mDemoReadStream->getStatus() != Stream::Ok)
- {
- stopDemoPlayback();
- return false;
- }
- return true;
- }
- //--------------------------------------------------------------------
- //--------------------------------------------------------------------
- // some handy string functions for compressing strings over a connection:
- enum NetStringConstants
- {
- NullString = 0,
- CString,
- TagString,
- Integer
- };
- void NetConnection::validateSendString(const char *str)
- {
- if(U8(*str) == StringTagPrefixByte)
- {
- NetStringHandle strHandle(dAtoi(str + 1));
- checkString(strHandle);
- }
- }
- void NetConnection::packString(BitStream *stream, const char *str)
- {
- if(!*str)
- {
- stream->writeInt(NullString, 2);
- return;
- }
- if(U8(str[0]) == StringTagPrefixByte)
- {
- stream->writeInt(TagString, 2);
- stream->writeInt(dAtoi(str + 1), ConnectionStringTable::EntryBitSize);
- return;
- }
- if(str[0] == '-' || (str[0] >= '0' && str[0] <= '9'))
- {
- char buf[16];
- S32 num = dAtoi(str);
- dSprintf(buf, sizeof(buf), "%d", num);
- if(!String::compare(buf, str))
- {
- stream->writeInt(Integer, 2);
- if(stream->writeFlag(num < 0))
- num = -num;
- if(stream->writeFlag(num < 128))
- {
- stream->writeInt(num, 7);
- return;
- }
- if(stream->writeFlag(num < 32768))
- {
- stream->writeInt(num, 15);
- return;
- }
- else
- {
- stream->writeInt(num, 31);
- return;
- }
- }
- }
- stream->writeInt(CString, 2);
- stream->writeString(str);
- }
- void NetConnection::unpackString(BitStream *stream, char readBuffer[1024])
- {
- U32 code = stream->readInt(2);
- switch(code)
- {
- case NullString:
- readBuffer[0] = 0;
- return;
- case CString:
- {
- stream->readString(readBuffer);
- return;
- }
- case TagString:
- U32 tag;
- tag = stream->readInt(ConnectionStringTable::EntryBitSize);
- readBuffer[0] = StringTagPrefixByte;
- dSprintf(readBuffer+1, 1023, "%d", tag);
- return;
- case Integer:
- bool neg;
- neg = stream->readFlag();
- S32 num;
- if(stream->readFlag())
- num = stream->readInt(7);
- else if(stream->readFlag())
- num = stream->readInt(15);
- else
- num = stream->readInt(31);
- if(neg)
- num = -num;
- dSprintf(readBuffer, 1024, "%d", num);
- }
- }
- void NetConnection::packNetStringHandleU(BitStream *stream, NetStringHandle &h)
- {
- if(stream->writeFlag(h.isValidString() ))
- {
- bool isReceived;
- U32 netIndex = checkString(h, &isReceived);
- if(stream->writeFlag(isReceived))
- stream->writeInt(netIndex, ConnectionStringTable::EntryBitSize);
- else
- stream->writeString(h.getString());
- }
- }
- NetStringHandle NetConnection::unpackNetStringHandleU(BitStream *stream)
- {
- NetStringHandle ret;
- if(stream->readFlag())
- {
- if(stream->readFlag())
- ret = mStringTable->lookupString(stream->readInt(ConnectionStringTable::EntryBitSize));
- else
- {
- char buf[256];
- stream->readString(buf);
- ret = NetStringHandle(buf);
- }
- }
- return ret;
- }
- //----------------------------------------------------------------------------
- //----------------------------------------------------------------------------
- //----------------------------------------------------------------------------
- void NetConnection::setAddressDigest(U32 digest[4])
- {
- mAddressDigest[0] = digest[0];
- mAddressDigest[1] = digest[1];
- mAddressDigest[2] = digest[2];
- mAddressDigest[3] = digest[3];
- }
- void NetConnection::getAddressDigest(U32 digest[4])
- {
- digest[0] = mAddressDigest[0];
- digest[1] = mAddressDigest[1];
- digest[2] = mAddressDigest[2];
- digest[3] = mAddressDigest[3];
- }
- bool NetConnection::canRemoteCreate()
- {
- return false;
- }
- void NetConnection::onTimedOut()
- {
- }
- void NetConnection::connect(const NetAddress *address)
- {
- mNetAddress = *address;
- GNet->startConnection(this);
- }
- void NetConnection::onConnectTimedOut()
- {
- }
- void NetConnection::sendDisconnectPacket(const char *reason)
- {
- GNet->sendDisconnectPacket(this, reason);
- }
- void NetConnection::onDisconnect(const char *reason)
- {
- TORQUE_UNUSED(reason);
- }
- void NetConnection::onConnectionRejected(const char *reason)
- {
- }
- void NetConnection::onConnectionEstablished(bool isInitiator)
- {
- }
- void NetConnection::handleStartupError(const char *errorString)
- {
- }
- void NetConnection::writeConnectRequest(BitStream *stream)
- {
- stream->write(mNetClassGroup);
- stream->write(U32(AbstractClassRep::getClassCRC(mNetClassGroup)));
- }
- bool NetConnection::readConnectRequest(BitStream *stream, const char **errorString)
- {
- U32 classGroup, classCRC;
- stream->read(&classGroup);
- stream->read(&classCRC);
- if(classGroup == mNetClassGroup && classCRC == AbstractClassRep::getClassCRC(mNetClassGroup))
- return true;
- *errorString = "CHR_INVALID";
- return false;
- }
- void NetConnection::writeConnectAccept(BitStream *stream)
- {
- TORQUE_UNUSED(stream);
- }
- bool NetConnection::readConnectAccept(BitStream *stream, const char **errorString)
- {
- TORQUE_UNUSED(stream);
- TORQUE_UNUSED(errorString);
- return true;
- }
- DefineEngineMethod( NetConnection, resolveGhostID, S32, (S32 ghostID),,
- "@brief On the client, convert a ghost ID from this connection to a real SimObject ID.\n\n"
- "Torque's network ghosting system only exchanges ghost ID's between the server and client. Use "
- "this method on the client to discover an object's local SimObject ID when you only have a "
- "ghost ID.\n"
- "@param ghostID The ghost ID of the object as sent by the server.\n"
- "@returns The SimObject ID of the object, or 0 if it could not be resolved.\n\n"
- "@tsexample\n"
- "%object = ServerConnection.resolveGhostID( %ghostId );\n"
- "@endtsexample\n\n"
- "@see @ref ghosting_scoping for a description of the ghosting system.\n\n")
- {
- // Safety check
- if(ghostID < 0 || ghostID > NetConnection::MaxGhostCount) return 0;
- NetObject *foo = object->resolveGhost(ghostID);
- if(foo)
- return foo->getId();
- else
- return 0;
- }
- DefineEngineMethod( NetConnection, resolveObjectFromGhostIndex, S32, (S32 ghostID),,
- "@brief On the server, convert a ghost ID from this connection to a real SimObject ID.\n\n"
- "Torque's network ghosting system only exchanges ghost ID's between the server and client. Use "
- "this method on the server to discover an object's local SimObject ID when you only have a "
- "ghost ID.\n"
- "@param ghostID The ghost ID of the object as sent by the server.\n"
- "@returns The SimObject ID of the object, or 0 if it could not be resolved.\n\n"
- "@tsexample\n"
- "%object = %client.resolveObjectFromGhostIndex( %ghostId );\n"
- "@endtsexample\n\n"
- "@see @ref ghosting_scoping for a description of the ghosting system.\n\n")
- {
- // Safety check
- if(ghostID < 0 || ghostID > NetConnection::MaxGhostCount) return 0;
- NetObject *foo = object->resolveObjectFromGhostIndex(ghostID);
- if(foo)
- return foo->getId();
- else
- return 0;
- }
- DefineEngineMethod( NetConnection, getGhostID, S32, (S32 realID),,
- "@brief On server or client, convert a real id to the ghost id for this connection.\n\n"
- "Torque's network ghosting system only exchanges ghost ID's between the server and client. Use "
- "this method on the server or client to discover an object's ghost ID based on its real SimObject ID.\n"
- "@param realID The real SimObject ID of the object.\n"
- "@returns The ghost ID of the object for this connection, or -1 if it could not be resolved.\n\n"
- "@see @ref ghosting_scoping for a description of the ghosting system.\n\n")
- {
- NetObject * foo;
- if(Sim::findObject(realID, foo))
- {
- return object->getGhostIndex(foo);
- }
- else
- {
- Con::errorf("NetConnection::serverToGhostID - could not find specified object");
- return -1;
- }
- }
- DefineEngineMethod( NetConnection, connect, void, (const char* remoteAddress),,
- "@brief Connects to the remote address.\n\n"
- "Attempts to connect with another NetConnection on the given address. Typically once "
- "connected, a game's information is passed along from the server to the client, followed "
- "by the player entering the game world. The actual procedure is dependent on "
- "the NetConnection subclass that is used. i.e. GameConnection.\n"
- "@param remoteAddress The address to connect to in the form of IP:<address>:<port&rt; "
- "although the <i>IP:</i> portion is optional. The <i>address</i> portion may be in the form "
- "of w.x.y.z or as a host name, in which case a DNS lookup will be performed. You may also "
- "substitue the word <i>broadcast</i> for the address to broadcast the connect request over "
- "the local subnet.\n\n"
- "@see NetConnection::connectLocal() to connect to a server running within the same process "
- "as the client.\n"
- )
- {
- NetAddress addr;
- if (Net::stringToAddress(remoteAddress, &addr) != Net::NoError)
- {
- Con::errorf("NetConnection::connect: invalid address - %s", remoteAddress);
- return;
- }
- object->connect(&addr);
- }
- DefineEngineMethod( NetConnection, connectLocal, const char*, (),,
- "@brief Connects with the server that is running within the same process as the client.\n\n"
- "@returns An error text message upon failure, or an empty string when successful.\n\n"
- "@see See @ref local_connections for a description of local connections and their use. See "
- "NetConnection::connect() to connect to a server running in another process (on the same machine or not).\n")
- {
- ConsoleObject *co = ConsoleObject::create(object->getClassName());
- NetConnection *client = object;
- NetConnection *server = dynamic_cast<NetConnection *>(co);
- BitStream *stream = BitStream::getPacketStream();
- if(!server || !server->canRemoteCreate())
- {
- delete co;
- return "error";
- }
-
- server->registerObject();
- server->setIsLocalClientConnection();
- server->setSequence(0);
- client->setSequence(0);
- client->setRemoteConnectionObject(server);
- server->setRemoteConnectionObject(client);
- //We need to reset the maxrate's here, because we
- // can't test if it is a local connection until RemoteConnectionObject
- // has been set
- server->checkMaxRate();
- client->checkMaxRate();
- stream->setPosition(0);
- client->writeConnectRequest(stream);
- stream->setPosition(0);
-
- const char* error;
- if( !server->readConnectRequest( stream, &error ) )
- {
- client->onConnectionRejected( error );
- server->deleteObject();
- return "error";
- }
- stream->setPosition(0);
- server->writeConnectAccept(stream);
- stream->setPosition(0);
- if( !client->readConnectAccept( stream, &error ) )
- {
- client->handleStartupError( error );
- server->deleteObject();
- return "error";
- }
- client->onConnectionEstablished(true);
- server->onConnectionEstablished(false);
- client->setEstablished();
- server->setEstablished();
- client->setConnectSequence(0);
- server->setConnectSequence(0);
- NetConnection::setLocalClientConnection(server);
- server->assignName("LocalClientConnection");
-
- return "";
- }
|