| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Compression.h"
- #include "strtok_r.h"
- #include "Common/AudioEventRTS.h"
- #include "Common/CRCDebug.h"
- #include "Common/Debug.h"
- #include "Common/File.h"
- #include "Common/GameAudio.h"
- #include "Common/LocalFileSystem.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/RandomValue.h"
- #include "Common/Recorder.h"
- #include "GameClient/Diplomacy.h"
- #include "GameClient/GameText.h"
- #include "GameClient/MessageBox.h"
- #include "GameNetwork/ConnectionManager.h"
- #include "GameNetwork/LANAPICallbacks.h"
- #include "GameNetwork/NAT.h"
- #include "GameNetwork/NetCommandWrapperList.h"
- #include "GameNetwork/NetworkUtil.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/ScriptActions.h"
- #include "GameLogic/ScriptEngine.h"
- #include "GameLogic/VictoryConditions.h"
- #include "GameClient/DisconnectMenu.h"
- #include "GameClient/InGameUI.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- /**
- * Le destructor.
- */
- ConnectionManager::~ConnectionManager(void)
- {
- if (m_localUser != NULL) {
- m_localUser->deleteInstance();
- m_localUser = NULL;
- }
- // m_transport = NULL; // Network will delete transports; we just forget them
- if (m_transport != NULL) {
- delete m_transport;
- m_transport = NULL;
- }
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if (m_frameData[i] != NULL) {
- m_frameData[i]->deleteInstance();
- m_frameData[i] = NULL;
- }
- }
- for (i = 0; i < NUM_CONNECTIONS; ++i) {
- if (m_connections[i] != NULL) {
- m_connections[i]->deleteInstance();
- m_connections[i] = NULL;
- }
- }
- if (TheDisconnectMenu != NULL) {
- // This is done here since TheDisconnectMenu should only be there if we are in a network game.
- delete TheDisconnectMenu;
- TheDisconnectMenu = NULL;
- }
- if (m_disconnectManager != NULL) {
- delete m_disconnectManager;
- m_disconnectManager = NULL;
- }
- if (m_pendingCommands != NULL) {
- m_pendingCommands->deleteInstance();
- m_pendingCommands = NULL;
- }
- if (m_relayedCommands != NULL) {
- m_relayedCommands->deleteInstance();
- m_relayedCommands = NULL;
- }
- if (m_netCommandWrapperList != NULL) {
- m_netCommandWrapperList->deleteInstance();
- m_netCommandWrapperList = NULL;
- }
- s_fileCommandMap.clear();
- s_fileRecipientMaskMap.clear();
- for (i = 0; i < MAX_SLOTS; ++i) {
- s_fileProgressMap[i].clear();
- }
- }
- /**
- * Le constructor
- */
- ConnectionManager::ConnectionManager(void)
- {
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- m_frameData[i] = NULL;
- }
- m_transport = NULL;
- m_disconnectManager = NULL;
- m_pendingCommands = NULL;
- m_relayedCommands = NULL;
- m_localAddr = 0;
- m_localPort = 0;
- m_netCommandWrapperList = NULL;
- m_localUser = NULL;
- m_localUser = newInstance(User);
- }
- /**
- * Initialize the connection manager and any subsystems.
- */
- void ConnectionManager::init()
- {
- // if (m_transport == NULL) {
- // m_transport = new Transport;
- // }
- // m_transport->reset();
- for (UnsignedInt i = 0; i < NUM_CONNECTIONS; ++i) {
- m_connections[i] = NULL;
- }
- if (m_pendingCommands == NULL) {
- m_pendingCommands = newInstance(NetCommandList);
- m_pendingCommands->init();
- }
- m_pendingCommands->reset();
- if (m_relayedCommands == NULL) {
- m_relayedCommands = newInstance(NetCommandList);
- m_relayedCommands->init();
- }
- m_relayedCommands->reset();
- m_localSlot = -1;
- #ifdef MEMORYPOOL_DEBUG
- TheMemoryPoolFactory->debugSetInitFillerIndex(m_localSlot);
- #endif
- m_packetRouterSlot = 0; /// @todo The LAN/WOL interface should be telling us who the packet router is based on machine specs passed around through game options.
- for (i = 0; i < MAX_SLOTS; ++i) {
- m_packetRouterFallback[i] = -1;
- }
- for (i = 0; i < MAX_SLOTS; ++i) {
- if (m_frameData[i] != NULL) {
- m_frameData[i]->deleteInstance();
- m_frameData[i] = NULL;
- }
- }
- // m_averageFps = 30; // since 30 fps is the desired rate, we'll start off at that.
- // m_averageLatency = (Real)0.2; // 200ms seems like a good starting point.
- for (i = 0; i < MAX_SLOTS; ++i) {
- m_fpsAverages[i] = -1;
- }
- for (i = 0; i < MAX_SLOTS; ++i) {
- m_latencyAverages[i] = 0.0; // using zero since all floating point standards should be able to specify 0.0 accurately.
- }
- m_smallestPacketArrivalCushion = -1;
- m_frameMetrics.init();
- TheDisconnectMenu = NEW DisconnectMenu;
- TheDisconnectMenu->init();
- m_disconnectManager = NEW DisconnectManager;
- m_disconnectManager->init();
- TheDisconnectMenu->attachDisconnectManager(m_disconnectManager);
- TheDisconnectMenu->hideScreen();
- m_netCommandWrapperList = newInstance(NetCommandWrapperList);
- m_netCommandWrapperList->init();
- s_fileCommandMap.clear();
- s_fileRecipientMaskMap.clear();
- for (i = 0; i < MAX_SLOTS; ++i) {
- s_fileProgressMap[i].clear();
- }
- }
- /**
- * Reset the connection manager and any subsystems.
- */
- void ConnectionManager::reset()
- {
- // if (m_transport == NULL) {
- // m_transport = new Transport;
- // }
- // m_transport->reset();
- if (m_transport != NULL) {
- delete m_transport;
- m_transport = NULL;
- }
- for (Int i = 0; i < NUM_CONNECTIONS; ++i) {
- if (m_connections[i] != NULL) {
- m_connections[i]->deleteInstance();
- m_connections[i] = NULL;
- }
- }
- for (i=0; i<MAX_SLOTS; ++i)
- {
- if (m_frameData[i] != NULL) {
- m_frameData[i]->deleteInstance();
- m_frameData[i] = NULL;
- }
- }
- if (m_pendingCommands == NULL) {
- m_pendingCommands = newInstance(NetCommandList);
- m_pendingCommands->init();
- }
- m_pendingCommands->reset();
- if (m_relayedCommands == NULL) {
- m_relayedCommands = newInstance(NetCommandList);
- m_relayedCommands->init();
- }
- m_relayedCommands->reset();
- if (m_netCommandWrapperList == NULL) {
- m_netCommandWrapperList = newInstance(NetCommandWrapperList);
- m_netCommandWrapperList->init();
- }
- m_netCommandWrapperList->reset();
- m_localSlot = -1;
- #ifdef MEMORYPOOL_DEBUG
- TheMemoryPoolFactory->debugSetInitFillerIndex(m_localSlot);
- #endif
- m_packetRouterSlot = -1;
- for (i = 0; i < TheGlobalData->m_networkFPSHistoryLength; ++i) {
- m_fpsAverages[i] = -1;
- }
- for (i = 0; i < TheGlobalData->m_networkLatencyHistoryLength; ++i) {
- m_latencyAverages[i] = 0.0;
- }
- for (i = 0; i < MAX_SLOTS; ++i) {
- m_packetRouterFallback[i] = -1;
- }
- m_frameMetrics.reset();
- }
- UnsignedInt ConnectionManager::getPingFrame()
- {
- return (m_disconnectManager)?m_disconnectManager->getPingFrame():0;
- }
- Int ConnectionManager::getPingsSent()
- {
- return (m_disconnectManager)?m_disconnectManager->getPingsSent():0;
- }
- Int ConnectionManager::getPingsRecieved()
- {
- return (m_disconnectManager)?m_disconnectManager->getPingsRecieved():0;
- }
- Bool ConnectionManager::isPlayerConnected( Int playerID )
- {
- return ( playerID == m_localSlot || (m_connections[playerID] && !m_connections[playerID]->isQuitting()) );
- }
- void ConnectionManager::attachTransport(Transport *transport) {
- if (m_transport != NULL) {
- delete m_transport;
- m_transport = NULL;
- }
- m_transport = transport;
- }
- /**
- * zero out the command counts for the given frames. Presently this is used for
- * the start of a game since there won't be any commands for the first few frames due to runahead.
- */
- void ConnectionManager::zeroFrames(UnsignedInt startingFrame, UnsignedInt numFrames) {
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if (m_frameData[i] != NULL) {
- // DEBUG_LOG(("Calling zeroFrames on player %d, starting frame %d, numFrames %d\n", i, startingFrame, numFrames));
- m_frameData[i]->zeroFrames(startingFrame, numFrames);
- }
- }
- }
- /**
- * Destroy any game messages that are left over due to the run ahead.
- */
- void ConnectionManager::destroyGameMessages() {
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- // Need to destroy these game messages because when the game ends, there are
- // still some game messages left over because of the run ahead aspect of
- // network play.
- if (m_frameData[i] != NULL) {
- m_frameData[i]->destroyGameMessages();
- }
- }
- }
- /**
- * ConnectionManager::doRelay()
- * Queries the transport for commands that need to be relayed to another client.
- * Get those commands and relay them to the appropriate Connection(s). We make the
- * assumption that a command will only be relayed once.
- */
- void ConnectionManager::doRelay() {
- static Int numPackets = 0;
- static Int numCommands = 0;
- NetPacket *packet = NULL;
- for (Int i = 0; i < MAX_MESSAGES; ++i) {
- if (m_transport->m_inBuffer[i].length != 0) {
- // This transport buffer has yet to be processed.
- // make a NetPacket out of this data so it can be broken up into individual commands.
- packet = newInstance(NetPacket)(&(m_transport->m_inBuffer[i]));
- //DEBUG_LOG(("ConnectionManager::doRelay() - got a packet with %d commands\n", packet->getNumCommands()));
- //LOGBUFFER( packet->getData(), packet->getLength() );
- // Get the command list from the packet.
- NetCommandList *cmdList = packet->getCommandList();
- NetCommandRef *cmd = cmdList->getFirstMessage();
- // Iterate through the commands in this packet and send them to the proper connections.
- while (cmd != NULL) {
- //DEBUG_LOG(("ConnectionManager::doRelay() - Looking at a command of type %s\n",
- //GetAsciiNetCommandType(cmd->getCommand()->getNetCommandType()).str()));
- if (CommandRequiresAck(cmd->getCommand())) {
- ackCommand(cmd, m_localSlot);
- }
- if (!processNetCommand(cmd)) {
- sendRemoteCommand(cmd);
- }
- cmd = cmd->getNext();
- ++numCommands;
- }
- ++numPackets;
- // Delete this packet since we won't be needing it anymore.
- packet->deleteInstance();
- packet = NULL;
- cmdList->deleteInstance();
- cmdList = NULL;
- // signal that this has been processed.
- m_transport->m_inBuffer[i].length = 0;
- }
- }
- NetCommandList *cmdList = m_netCommandWrapperList->getReadyCommands();
- NetCommandRef *cmd = cmdList->getFirstMessage();
- while (cmd != NULL) {
- if (CommandRequiresAck(cmd->getCommand())) {
- ackCommand(cmd, m_localSlot);
- }
- if (!processNetCommand(cmd)) {
- sendRemoteCommand(cmd);
- }
- cmd = cmd->getNext();
- ++numCommands;
- }
- ++numPackets;
- // Delete this packet since we won't be needing it anymore.
- packet->deleteInstance();
- packet = NULL;
- cmdList->deleteInstance();
- cmdList = NULL;
- }
- /**
- * This is where the non-synchronized network commands should be processed.
- * Return TRUE if the command should not be relayed. Return FALSE if it should be relayed.
- */
- Bool ConnectionManager::processNetCommand(NetCommandRef *ref) {
- NetCommandMsg *msg = ref->getCommand();
- if ((msg->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE1) ||
- (msg->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE2) ||
- (msg->getNetCommandType() == NETCOMMANDTYPE_ACKBOTH)) {
- processAck(msg);
- return FALSE;
- }
- if ((m_connections[msg->getPlayerID()] == NULL) && (msg->getPlayerID() != m_localSlot)) {
- // if this is from a player that is no longer in the game, then ignore them.
- return TRUE;
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_WRAPPER) {
- processWrapper(ref); // need to send the NetCommandRef since we have to construct the relay for the wrapped command.
- return FALSE;
- }
- if ((msg->getPlayerID() >= 0) && (msg->getPlayerID() < MAX_SLOTS) && (msg->getPlayerID() != m_localSlot)) {
- if (m_connections[msg->getPlayerID()] == NULL) {
- return TRUE;
- }
- }
- // Don't allow an out of date command to be sent through.
- // Its unnecessary traffic and it could cause problems.
- //
- // This was a fix for a command count bug where a command would be
- // executed, then a command for that old frame would be added to the
- // FrameData for that frame + 256, and would screw up the command count.
- if (IsCommandSynchronized(msg->getNetCommandType())) {
- if (ref->getCommand()->getExecutionFrame() < TheGameLogic->getFrame()) {
- return TRUE;
- }
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_FRAMEINFO) {
- processFrameInfo((NetFrameCommandMsg *)msg);
-
- // need to set the relay so we don't send it to ourselves.
- UnsignedByte relay = ref->getRelay();
- relay = relay & (0xff ^ (1 << m_localSlot));
- ref->setRelay(relay);
- return FALSE;
- }
-
- if (msg->getNetCommandType() == NETCOMMANDTYPE_PROGRESS)
- {
- //DEBUG_LOG(("ConnectionManager::processNetCommand - got a progress net command from player %d\n", msg->getPlayerID()));
- processProgress((NetProgressCommandMsg *) msg);
- // need to set the relay so we don't send it to ourselves.
- UnsignedByte relay = ref->getRelay();
- relay = relay & (0xff ^ (1 << m_localSlot));
- ref->setRelay(relay);
- return FALSE;
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_TIMEOUTSTART)
- {
- DEBUG_LOG(("ConnectionManager::processNetCommand - got a TimeOut GameStart net command from player %d\n", msg->getPlayerID()));
- processTimeOutGameStart(msg);
- return FALSE;
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_RUNAHEADMETRICS) {
- processRunAheadMetrics((NetRunAheadMetricsCommandMsg *)msg);
- return TRUE;
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_KEEPALIVE) {
- return TRUE;
- }
- if ((msg->getNetCommandType() > NETCOMMANDTYPE_DISCONNECTSTART) && (msg->getNetCommandType() < NETCOMMANDTYPE_DISCONNECTEND)) {
- m_disconnectManager->processDisconnectCommand(ref, this);
- return TRUE;
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTCHAT) {
- processDisconnectChat((NetDisconnectChatCommandMsg *)msg);
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_LOADCOMPLETE)
- {
- DEBUG_LOG(("ConnectionManager::processNetCommand - got a Load Complete net command from player %d\n", msg->getPlayerID()));
- processLoadComplete(msg);
- return FALSE;
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_CHAT) {
- processChat((NetChatCommandMsg *)msg);
- return FALSE;
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_FILE) {
- processFile((NetFileCommandMsg *)msg);
- return FALSE;
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_FILEANNOUNCE) {
- processFileAnnounce((NetFileAnnounceCommandMsg *)msg);
- return FALSE;
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_FILEPROGRESS) {
- processFileProgress((NetFileProgressCommandMsg *)msg);
- return FALSE;
- }
- if (msg->getNetCommandType() == NETCOMMANDTYPE_FRAMERESENDREQUEST) {
- processFrameResendRequest((NetFrameResendRequestCommandMsg *)msg);
- return TRUE;
- }
- return FALSE;
- }
- void ConnectionManager::processFrameResendRequest(NetFrameResendRequestCommandMsg *msg) {
- // first make sure this is a valid slot
- Int playerID = msg->getPlayerID();
- if ((playerID < 0) || (playerID >= MAX_SLOTS)) {
- return;
- }
- // make sure this player is still in our game.
- if ((m_connections[playerID] == NULL) || (m_connections[playerID]->isQuitting() == TRUE)) {
- return;
- }
- sendFrameDataToPlayer(playerID, msg->getFrameToResend());
- }
- /**
- * We have received a wrapper for a command too big to fit in a packet.
- */
- void ConnectionManager::processWrapper(NetCommandRef *ref)
- {
- NetWrapperCommandMsg *wrapperMsg = (NetWrapperCommandMsg *)(ref->getCommand());
- UnsignedShort commandID = wrapperMsg->getWrappedCommandID();
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::processWrapper() - wrapped commandID is %d, commandID is %d\n",
- commandID, wrapperMsg->getID()));
- Int origProgress = 0;
- FileCommandMap::iterator fcIt = s_fileCommandMap.find(commandID);
- if (fcIt != s_fileCommandMap.end())
- {
- origProgress = s_fileProgressMap[m_localSlot][commandID];
- }
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::processWrapper() - origProgress[%d] == %d for command %d\n",
- m_localSlot, origProgress, commandID));
- m_netCommandWrapperList->processWrapper(ref);
- if (fcIt != s_fileCommandMap.end())
- {
- Int newProgress = m_netCommandWrapperList->getPercentComplete(commandID);
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::processWrapper() - newProgress[%d] == %d for command %d\n",
- m_localSlot, newProgress, commandID));
- if (newProgress > origProgress && newProgress < 100)
- {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::processWrapper() - sending a NetFileProgressCommandMsg\n"));
- s_fileProgressMap[m_localSlot][commandID] = newProgress;
- Int progressMask = 0xff ^ (1 << m_localSlot);
- NetFileProgressCommandMsg *msg = newInstance(NetFileProgressCommandMsg);
- msg->setPlayerID(m_localSlot);
- msg->setID(0);
- if (DoesCommandRequireACommandID(msg->getNetCommandType()))
- {
- msg->setID(GenerateNextCommandID());
- }
- msg->setFileID(commandID);
- msg->setProgress(newProgress);
- sendLocalCommand(msg, progressMask);
- processFileProgress(msg);
- msg->detach();
- }
- }
- }
- /**
- * A client has sent us their run ahead metrics, lets store them away for future calculations.
- */
- void ConnectionManager::processRunAheadMetrics(NetRunAheadMetricsCommandMsg *msg)
- {
- UnsignedInt player = msg->getPlayerID();
- if ((player >= 0) && (player < MAX_SLOTS) && (isPlayerConnected(player))) {
- m_latencyAverages[player] = msg->getAverageLatency();
- m_fpsAverages[player] = msg->getAverageFps();
- //DEBUG_LOG(("ConnectionManager::processRunAheadMetrics - player %d, fps = %d, latency = %f\n", player, msg->getAverageFps(), msg->getAverageLatency()));
- if (m_fpsAverages[player] > 100) {
- // limit the reported frame rate average to 100. This is done because if a
- // user alt-tab's out of the game their frame rate climbs to in the neighborhood of
- // 300, that was deemed "ugly" by the powers that be.
- m_fpsAverages[player] = 100;
- }
- }
- }
- void ConnectionManager::processDisconnectChat(NetDisconnectChatCommandMsg *msg)
- {
- UnicodeString unitext;
- UnicodeString name;
- UnsignedByte playerID = msg->getPlayerID();
- if (playerID == m_localSlot) {
- name = m_localUser->GetName();
- } else if (isPlayerConnected(playerID)) {
- name = m_connections[playerID]->getUser()->GetName();
- }
- unitext.format(L"[%ls] %ls", name.str(), msg->getText().str());
- // DEBUG_LOG(("ConnectionManager::processDisconnectChat - got message from player %d, message is %ls\n", playerID, unitext.str()));
- TheDisconnectMenu->showChat(unitext); // <-- need to implement this
- }
- void ConnectionManager::processChat(NetChatCommandMsg *msg)
- {
- UnicodeString unitext;
- UnicodeString name;
- UnsignedByte playerID = msg->getPlayerID();
- //DEBUG_LOG(("processChat(): playerID = %d\n", playerID));
- if (playerID == m_localSlot) {
- name = m_localUser->GetName();
- //DEBUG_LOG(("connection is NULL, using %ls\n", name.str()));
- } else if (((m_connections[playerID] != NULL) && (m_connections[playerID]->isQuitting() == FALSE))) {
- name = m_connections[playerID]->getUser()->GetName();
- //DEBUG_LOG(("connection is non-NULL, using %ls\n", name.str()));
- }
- unitext.format(L"[%ls] %ls", name.str(), msg->getText().str());
- // DEBUG_LOG(("ConnectionManager::processChat - got message from player %d (mask %8.8X), message is %ls\n", playerID, msg->getPlayerMask(), unitext.str()));
-
- AsciiString playerName;
- playerName.format("player%d", msg->getPlayerID());
- const Player *player = ThePlayerList->findPlayerWithNameKey( TheNameKeyGenerator->nameToKey( playerName ) );
- if (!player)
- {
- TheInGameUI->message(UnicodeString(L"%ls"), unitext.str());
- return;
- }
-
- Bool fromObserver = !player->isPlayerActive();
- Bool amIObserver = !ThePlayerList->getLocalPlayer()->isPlayerActive();
- Bool canSeeChat = amIObserver || !fromObserver && !TheGameInfo->getConstSlot(playerID)->isMuted();
-
- if ( ((1<<m_localSlot) & msg->getPlayerMask() ) && canSeeChat )
- {
- RGBColor rgb;
- rgb.setFromInt(player->getPlayerColor());
- TheInGameUI->messageColor(&rgb, UnicodeString(L"%ls"), unitext.str());
- // feedback for received chat messages in-game
- AudioEventRTS audioEvent("GUICommunicatorIncoming");
- TheAudio->addAudioEvent(&audioEvent);
- }
- }
- void ConnectionManager::processFile(NetFileCommandMsg *msg)
- {
- #ifdef _INTERNAL
- UnicodeString log;
- log.format(L"Saw file transfer: '%hs' of %d bytes from %d", msg->getPortableFilename().str(), msg->getFileLength(), msg->getPlayerID());
- DEBUG_LOG(("%ls\n", log.str()));
- #endif
- if (TheFileSystem->doesFileExist(msg->getRealFilename().str()))
- {
- DEBUG_LOG(("File exists already!\n"));
- //return;
- }
- UnsignedByte *buf = msg->getFileData();
- Int len = msg->getFileLength();
- // uncompress Targas
- #ifdef COMPRESS_TARGAS
- Bool deleteBuf = FALSE;
- if (msg->getFilename().endsWith(".tga") && CompressionManager::isDataCompressed(buf, len))
- {
- Int uncompLen = CompressionManager::getUncompressedSize(buf, len);
- UnsignedByte *uncompBuffer = NEW UnsignedByte[uncompLen];
- Int actualLen = CompressionManager::decompressData(buf, len, uncompBuffer, uncompLen);
- if (actualLen == uncompLen)
- {
- DEBUG_LOG(("Uncompressed Targa after map transfer\n"));
- deleteBuf = TRUE;
- buf = uncompBuffer;
- len = uncompLen;
- }
- else
- {
- DEBUG_LOG(("Failed to uncompress Targa after map transfer\n"));
- delete[] uncompBuffer; // failed to decompress, so just use the source
- }
- }
- #endif // COMPRESS_TARGAS
- File *fp = TheFileSystem->openFile(msg->getRealFilename().str(), File::CREATE | File::BINARY | File::WRITE);
- if (fp)
- {
- fp->write(buf, len);
- fp->close();
- fp = NULL;
- DEBUG_LOG(("Wrote %d bytes to file %s!\n",len,msg->getRealFilename().str()));
- }
- else
- {
- DEBUG_LOG(("Cannot open file!\n"));
- }
- DEBUG_LOG(("ConnectionManager::processFile() - sending a NetFileProgressCommandMsg\n"));
-
- Int commandID = msg->getID();
- Int newProgress = 100;
- s_fileProgressMap[m_localSlot][commandID] = newProgress;
- Int progressMask = 0xff ^ (1 << m_localSlot);
- NetFileProgressCommandMsg *progressMsg = newInstance(NetFileProgressCommandMsg);
- progressMsg->setPlayerID(m_localSlot);
- progressMsg->setID(0);
- if (DoesCommandRequireACommandID(progressMsg->getNetCommandType()))
- {
- progressMsg->setID(GenerateNextCommandID());
- }
- progressMsg->setFileID(commandID);
- progressMsg->setProgress(newProgress);
- sendLocalCommand(progressMsg, progressMask);
- processFileProgress(progressMsg);
- progressMsg->detach();
- #ifdef COMPRESS_TARGAS
- if (deleteBuf)
- {
- delete[] buf;
- buf = NULL;
- }
- #endif // COMPRESS_TARGAS
- }
- void ConnectionManager::processFileAnnounce(NetFileAnnounceCommandMsg *msg)
- {
- DEBUG_LOG(("ConnectionManager::processFileAnnounce() - expecting '%s' (%s) in command %d\n", msg->getPortableFilename().str(), msg->getRealFilename().str(), msg->getFileID()));
- s_fileCommandMap[msg->getFileID()] = msg->getRealFilename();
- s_fileRecipientMaskMap[msg->getFileID()] = msg->getPlayerMask();
- for (Int i=0; i<MAX_SLOTS; ++i)
- {
- if ( (1<<i) & msg->getPlayerMask() )
- {
- s_fileProgressMap[i][msg->getFileID()] = 0;
- }
- else
- {
- s_fileProgressMap[i][msg->getFileID()] = 100; // they don't need to get it, so they're already done.
- }
- }
- }
- void ConnectionManager::processFileProgress(NetFileProgressCommandMsg *msg)
- {
- DEBUG_LOG(("ConnectionManager::processFileProgress() - command %d is at %d%%\n",
- msg->getFileID(), msg->getProgress()));
- Int oldProgress = s_fileProgressMap[msg->getPlayerID()][msg->getFileID()];
- s_fileProgressMap[msg->getPlayerID()][msg->getFileID()] = max(oldProgress, msg->getProgress());
- }
- void ConnectionManager::processProgress( NetProgressCommandMsg *msg )
- {
- TheGameLogic->processProgress(msg->getPlayerID(), msg->getPercentage());
- }
- void ConnectionManager::processLoadComplete( NetCommandMsg *msg )
- {
- TheGameLogic->processProgressComplete(msg->getPlayerID());
- }
- void ConnectionManager::processTimeOutGameStart( NetCommandMsg *msg )
- {
- TheGameLogic->timeOutGameStart();
- }
- /**
- * Another client has sent us the command count for a new frame.
- */
- void ConnectionManager::processFrameInfo(NetFrameCommandMsg *msg) {
- //stupid frame info, why don't you process yourself?
- UnsignedInt playerID = msg->getPlayerID();
- if ((playerID >= 0) && (playerID < MAX_SLOTS)) {
- if (m_frameData[playerID] != NULL) {
- // DEBUG_LOG(("ConnectionManager::processFrameInfo - player %d, frame %d, command count %d, received on frame %d\n", playerID, msg->getExecutionFrame(), msg->getCommandCount(), TheGameLogic->getFrame()));
- m_frameData[playerID]->setFrameCommandCount(msg->getExecutionFrame(), msg->getCommandCount());
- }
- }
- }
- /**
- * We just got a stage 1 ack from someone. So we should remove it from the connection that sent it so
- * it doesn't keep resending it.
- */
- void ConnectionManager::processAckStage1(NetCommandMsg *msg) {
- #if defined(_DEBUG) || defined(_INTERNAL)
- Bool doDebug = (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTFRAME) ? TRUE : FALSE;
- #endif
- UnsignedByte playerID = msg->getPlayerID();
- NetCommandRef *ref = NULL;
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (doDebug == TRUE) {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::processAck - processing ack for command %d from player %d\n", ((NetAckStage1CommandMsg *)msg)->getCommandID(), playerID));
- }
- #endif
-
- if ((playerID >= 0) && (playerID < NUM_CONNECTIONS)) {
- if (m_connections[playerID] != NULL) {
- ref = m_connections[playerID]->processAck(msg);
- }
- } else {
- DEBUG_ASSERTCRASH((playerID >= 0) && (playerID < NUM_CONNECTIONS), ("ConnectionManager::processAck - %d is an invalid player number"));
- }
- if (ref != NULL) {
- if (ref->getCommand()->getNetCommandType() == NETCOMMANDTYPE_FRAMEINFO) {
- m_frameMetrics.processLatencyResponse(((NetFrameCommandMsg *)(ref->getCommand()))->getExecutionFrame());
- }
- ref->deleteInstance();
- ref = NULL;
- }
- }
- /**
- * We just got a stage 2 ack from someone. So remove it from the pending commands list so it doesn't
- * get sent in the case of a new packet router.
- */
- void ConnectionManager::processAckStage2(NetCommandMsg *msg) {
- UnsignedShort commandID = 0;
- UnsignedByte playerID = 0;
- if (msg->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE2) {
- commandID = ((NetAckStage2CommandMsg *)msg)->getCommandID();
- playerID = ((NetAckStage2CommandMsg *)msg)->getOriginalPlayerID();
- } else if (msg->getNetCommandType() == NETCOMMANDTYPE_ACKBOTH) {
- commandID = ((NetAckBothCommandMsg *)msg)->getCommandID();
- playerID = ((NetAckBothCommandMsg *)msg)->getOriginalPlayerID();
- } else {
- return;
- }
- NetCommandRef *ref = m_pendingCommands->findMessage(commandID, playerID);
- if (ref != NULL) {
- //DEBUG_LOG(("ConnectionManager::processAckStage2 - removing command %d from the pending commands list.\n", commandID));
- DEBUG_ASSERTCRASH((m_localSlot == playerID), ("Found a command in the pending commands list that wasn't originated by the local player"));
- m_pendingCommands->removeMessage(ref);
- ref->deleteInstance();
- ref = NULL;
- } else {
- //DEBUG_LOG(("ConnectionManager::processAckStage2 - Couldn't find command %d from player %d in the pending commands list.\n", commandID, playerID));
- }
- ref = m_relayedCommands->findMessage(commandID, playerID);
- if (ref != NULL) {
- //DEBUG_LOG(("ConnectionManager::processAckStage2 - found command ID %d from player %d in the relayed commands list.\n", commandID, playerID));
- UnsignedByte relay = ref->getRelay();
- //DEBUG_LOG(("ConnectionManager::processAckStage2 - relay was %d and is now ", relay));
- relay = relay & ~(1 << msg->getPlayerID());
- //DEBUG_LOG(("%d\n", relay));
- if (relay == 0) {
- //DEBUG_LOG(("ConnectionManager::processAckStage2 - relay is 0, removing command from the relayed commands list.\n"));
- m_relayedCommands->removeMessage(ref);
- NetAckStage2CommandMsg *ackmsg = newInstance(NetAckStage2CommandMsg)(ref->getCommand());
- sendLocalCommand(ackmsg, 1 << ackmsg->getOriginalPlayerID());
- ref->deleteInstance();
- ref = NULL;
- ackmsg->detach();
- ackmsg = NULL;
- } else {
- ref->setRelay(relay);
- }
- }
- }
- /**
- * We just got a "both" ack from someone. So process it as both a stage 1 and stage 2 ack.
- */
- void ConnectionManager::processAck(NetCommandMsg *msg) {
- if ((msg->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE1) || (msg->getNetCommandType() == NETCOMMANDTYPE_ACKBOTH)) {
- processAckStage1(msg);
- }
- if ((msg->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE2) || (msg->getNetCommandType() == NETCOMMANDTYPE_ACKBOTH)) {
- processAckStage2(msg);
- }
- }
- /**
- * A player has just left our game. Delete their connection and frame data manager.
- * return codes are:
- * PLAYERLEAVECODE_UNKNOWN - player didn't have a valid slot number.
- * PLAYERLEAVECODE_CLIENT - someone in the game that wasn't us or the packet router.
- * PLAYERLEAVECODE_LOCAL - We are leaving the game, we could also be the packet router.
- * PLAYERLEAVECODE_PACKETROUTER - The packet router left the game.
- *
- * If we are leaving and are also the packet router, it will return the PLAYERLEAVECODE_LOCAL return code.
- */
- PlayerLeaveCode ConnectionManager::processPlayerLeave(NetPlayerLeaveCommandMsg *msg) {
- UnsignedByte playerID = msg->getLeavingPlayerID();
- if ((playerID != m_localSlot) && (m_connections[playerID] != NULL)) {
- DEBUG_LOG(("ConnectionManager::processPlayerLeave() - setQuitting() on player %d on frame %d\n", playerID, TheGameLogic->getFrame()));
- m_connections[playerID]->setQuitting();
- }
- DEBUG_ASSERTCRASH(m_frameData[playerID]->getIsQuitting() == FALSE, ("Player %d is already quitting", playerID));
- if ((playerID != m_localSlot) && (m_frameData[playerID] != NULL) && (m_frameData[playerID]->getIsQuitting() == FALSE)) {
- DEBUG_LOG(("ConnectionManager::processPlayerLeave - setQuitFrame on player %d for frame %d\n", playerID, TheGameLogic->getFrame()+1));
- m_frameData[playerID]->setQuitFrame(TheGameLogic->getFrame() + FRAMES_TO_KEEP + 1);
- }
- if (playerID == m_localSlot)
- {
- // we're leaving, so mark our connections and frame datas to go away.
- for (Int i=0; i<MAX_SLOTS; ++i)
- {
- if (m_connections[i])
- {
- m_connections[i]->clearCommandsExceptFrom(m_localSlot);
- m_connections[i]->setQuitting();
- }
- }
- }
- PlayerLeaveCode code = disconnectPlayer(playerID);
- DEBUG_LOG(("ConnectionManager::processPlayerLeave() - just disconnected player %d with ret code %d\n", playerID, code));
- if (code == PLAYERLEAVECODE_PACKETROUTER)
- resendPendingCommands();
- PopulateInGameDiplomacyPopup();
- return code;
- }
- UnsignedInt ConnectionManager::getPacketRouterFallbackSlot(Int packetRouterNumber) {
- if ((packetRouterNumber >= 0) && (packetRouterNumber < MAX_SLOTS)) {
- return m_packetRouterFallback[packetRouterNumber];
- }
- return MAX_SLOTS;
- }
- UnsignedInt ConnectionManager::getPacketRouterSlot() {
- return m_packetRouterSlot;
- }
- Bool ConnectionManager::areAllQueuesEmpty(void) {
- Bool retval = TRUE;
- for (Int i = 0; (i < MAX_SLOTS) && retval; ++i) {
- if (m_connections[i] != NULL) {
- if (m_connections[i]->isQueueEmpty() == FALSE) {
- //DEBUG_LOG(("ConnectionManager::areAllQueuesEmpty() - m_connections[%d] is not empty\n", i));
- //m_connections[i]->debugPrintCommands();
- retval = FALSE;
- }
- }
- }
- return retval;
- }
- Bool ConnectionManager::canILeave() {
- return areAllQueuesEmpty();
- }
- /**
- * The local player is leaving. Tell the local player as well as the other players
- * to remove this player at the specified frame.
- */
- void ConnectionManager::handleLocalPlayerLeaving(UnsignedInt frame) {
- NetPlayerLeaveCommandMsg *msg = newInstance(NetPlayerLeaveCommandMsg);
- msg->setLeavingPlayerID(m_localSlot);
- msg->setExecutionFrame(frame);
- if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
- msg->setID(GenerateNextCommandID());
- }
- msg->setPlayerID(m_localSlot);
- DEBUG_LOG(("ConnectionManager::handleLocalPlayerLeaving - Local player leaving on frame %d\n", frame));
- DEBUG_ASSERTCRASH(m_packetRouterSlot >= 0, ("ConnectionManager::handleLocalPlayerLeaving, packet router is %d, illegal value.", m_packetRouterSlot));
- sendLocalCommand(msg);
- msg->detach();
- }
- /**
- * We just got a message that needs to be ack'd, so ack it!
- */
- void ConnectionManager::ackCommand(NetCommandRef *ref, UnsignedInt localSlot) {
- NetCommandMsg *msg = ref->getCommand();
- NetCommandMsg *ackmsg;
- UnsignedShort commandID;
- UnsignedByte originalPlayerID;
- UnsignedByte sendRelay = 0;
- // Make send relay a bitmask for the connections that the relay will actually be sent to. This is
- // necessary to determine whether or not we have to wait to send a stage 2 ack.
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if (((m_connections[i] != NULL) && (m_connections[i]->isQuitting() == FALSE))) {
- sendRelay = sendRelay | (1 << i);
- }
- }
- #if defined(_DEBUG) || defined(_INTERNAL)
- Bool doDebug = (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTFRAME) ? TRUE : FALSE;
- #endif
- sendRelay = sendRelay & ref->getRelay();
- if (sendRelay == 0) {
- NetAckBothCommandMsg *bothmsg = newInstance(NetAckBothCommandMsg)(ref->getCommand());
- ackmsg = bothmsg;
- commandID = bothmsg->getCommandID();
- originalPlayerID = bothmsg->getOriginalPlayerID();
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (doDebug) {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::ackCommand - doing ack both for command %d from player %d\n", bothmsg->getCommandID(), bothmsg->getOriginalPlayerID()));
- }
- #endif
- } else {
- NetAckStage1CommandMsg *stage1msg = newInstance(NetAckStage1CommandMsg)(ref->getCommand());
- ackmsg = stage1msg;
- commandID = stage1msg->getCommandID();
- originalPlayerID = stage1msg->getOriginalPlayerID();
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (doDebug) {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::ackCommand - doing ack stage 1 for command %d from player %d\n", stage1msg->getCommandID(), stage1msg->getOriginalPlayerID()));
- }
- #endif
- }
- ackmsg->setPlayerID(localSlot); // Tell the player who this ack is coming from.
- if (CommandRequiresDirectSend(msg) && CommandRequiresAck(msg)) {
- // Send this ack directly back to the sending player, don't go through the packet router.
- if ((msg->getPlayerID() >= 0) && (msg->getPlayerID() < MAX_SLOTS)) {
- if (m_connections[msg->getPlayerID()] != NULL) {
- m_connections[msg->getPlayerID()]->sendNetCommandMsg(ackmsg, 1 << msg->getPlayerID());
- }
- }
- } else {
- // The local connection may be the packet router, in that case, the connection would be NULL. So do something about it!
- if ((m_packetRouterSlot >= 0) && (m_packetRouterSlot < MAX_SLOTS)) {
- if (m_connections[m_packetRouterSlot] != NULL) {
- // DEBUG_LOG(("ConnectionManager::ackCommand - acking command %d from player %d to packet router.\n", commandID, m_packetRouterSlot));
- m_connections[m_packetRouterSlot]->sendNetCommandMsg(ackmsg, 1 << m_packetRouterSlot);
- } else if (m_localSlot == m_packetRouterSlot) {
- // we are the packet router, send the ack to the player that sent the command.
- if ((msg->getPlayerID() >= 0) && (msg->getPlayerID() < MAX_SLOTS)) {
- if (m_connections[msg->getPlayerID()] != NULL) {
- // DEBUG_LOG(("ConnectionManager::ackCommand - acking command %d from player %d directly to player.\n", commandID, msg->getPlayerID()));
- m_connections[msg->getPlayerID()]->sendNetCommandMsg(ackmsg, 1 << msg->getPlayerID());
- } else {
- // DEBUG_ASSERTCRASH(m_connections[msg->getPlayerID()] != NULL, ("Connection to player is NULL"));
- }
- } else {
- DEBUG_ASSERTCRASH((msg->getPlayerID() >= 0) && (msg->getPlayerID() < MAX_SLOTS), ("Command sent by an invalid player ID."));
- }
- } else {
- DEBUG_ASSERTCRASH(m_connections[m_packetRouterSlot] != NULL, ("Connection to packet router is NULL"));
- }
- } else {
- DEBUG_ASSERTCRASH((m_packetRouterSlot >= 0) && (m_packetRouterSlot < MAX_SLOTS), ("I don't know who the packet router is."));
- }
- }
- ackmsg->detach();
- }
- /**
- * This is where we relay a command from one client to others (including ourselves).
- * This should only be done by the current packet router.
- */
- void ConnectionManager::sendRemoteCommand(NetCommandRef *msg) {
- UnsignedByte actualRelay = 0;
- if (msg->getCommand() == NULL) {
- return;
- }
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendRemoteCommand - sending net command %d of type %s from player %d, relay is 0x%x\n",
- msg->getCommand()->getID(), GetAsciiNetCommandType(msg->getCommand()->getNetCommandType()).str(), msg->getCommand()->getPlayerID(), msg->getRelay()));
- UnsignedByte relay = msg->getRelay();
- if ((relay & (1 << m_localSlot)) && (m_frameData[msg->getCommand()->getPlayerID()] != NULL)) {
- if (IsCommandSynchronized(msg->getCommand()->getNetCommandType())) {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendRemoteCommand - adding net command of type %s to player %d for frame %d\n", GetAsciiNetCommandType(msg->getCommand()->getNetCommandType()).str(), msg->getCommand()->getPlayerID(), msg->getCommand()->getExecutionFrame()));
- m_frameData[msg->getCommand()->getPlayerID()]->addNetCommandMsg(msg->getCommand());
- }
- }
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if ((relay & (1 << i)) && ((m_connections[i] != NULL) && (m_connections[i]->isQuitting() == FALSE))) {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendRemoteCommand - relaying command %d to player %d\n", msg->getCommand()->getID(), i));
- m_connections[i]->sendNetCommandMsg(msg->getCommand(), 1 << i);
- actualRelay = actualRelay | (1 << i);
- }
- }
- if ((actualRelay != 0) && (CommandRequiresAck(msg->getCommand()) == TRUE)) {
- NetCommandRef *ref = m_relayedCommands->addMessage(msg->getCommand());
- if (ref != NULL) {
- ref->setRelay(actualRelay);
- //DEBUG_LOG(("ConnectionManager::sendRemoteCommand - command %d added to relayed commands with relay %d\n", msg->getCommand()->getID(), ref->getRelay()));
- }
- }
- // Do some metrics to find the minimum packet arrival cushion.
- if (IsCommandSynchronized(msg->getCommand()->getNetCommandType())) {
- // DEBUG_LOG(("ConnectionManager::sendRemoteCommand - about to call allCommandsReady\n"));
- if (allCommandsReady(msg->getCommand()->getExecutionFrame(), TRUE)) {
- UnsignedInt cushion = msg->getCommand()->getExecutionFrame() - TheGameLogic->getFrame();
- if ((cushion < m_smallestPacketArrivalCushion) || (m_smallestPacketArrivalCushion == -1)) {
- m_smallestPacketArrivalCushion = cushion;
- }
- m_frameMetrics.addCushion(cushion);
- // DEBUG_LOG(("Adding %d to cushion for frame %d\n", cushion, msg->getCommand()->getExecutionFrame()));
- }
- }
- }
- /**
- * ConnectionManager::update
- * Update the connections. Tell them to do the receive and send. Also relay
- * commands to their final destinations as necessary.
- */
- void ConnectionManager::update(Bool isInGame) {
- //
- // 1. do this
- // 2. do that
- // 3. do something else
- // 4. blow something up
- // 5. bust some cap
- //
- if ((m_localAddr == 0) || (m_localPort == 0)) {
- // we don't have a local address or port yet, this is bad.
- DEBUG_ASSERTCRASH((m_localAddr != 0) && (m_localPort != 0), ("ConnectionManager doesn't have a local address."));
- return;
- }
- m_transport->doRecv();
- if (isInGame) {
- m_disconnectManager->update(this);
- }
- // take the packets from the transport, break them up into commands, and give them to the appropriate connections.
- doRelay();
- // send any necessary keep-alive packets.
- doKeepAlive();
- for (Int i = 0; i < NUM_CONNECTIONS; ++i) {
- if (m_connections[i] != NULL) {
- /*
- if (m_connections[i]->isQueueEmpty() == FALSE) {
- // DEBUG_LOG(("ConnectionManager::update - calling doSend on connection %d\n", i));
- }
- */
- m_connections[i]->doSend();
- if (m_connections[i]->isQuitting() && m_connections[i]->isQueueEmpty())
- {
- DEBUG_LOG(("ConnectionManager::update - deleting connection for slot %d\n", i));
- m_connections[i]->deleteInstance();
- m_connections[i] = NULL;
- }
- }
- if ((m_frameData[i] != NULL) && (m_frameData[i]->getIsQuitting() == TRUE)) {
- if (m_frameData[i]->getQuitFrame() == TheGameLogic->getFrame()) {
- DEBUG_LOG(("ConnectionManager::update - deleting frame data for slot %d on quitting frame %d\n", i, m_frameData[i]->getQuitFrame()));
- m_frameData[i]->deleteInstance();
- m_frameData[i] = NULL;
- }
- }
- }
- m_transport->doSend();
- }
- void ConnectionManager::updateRunAhead(Int oldRunAhead, Int frameRate, Bool didSelfSlug, Int nextExecutionFrame) {
- static time_t lasttimesent = 0;
- time_t curTime = timeGetTime();
- if ((lasttimesent == 0) || ((curTime - lasttimesent) > TheGlobalData->m_networkRunAheadMetricsTime)) {
- if (m_localSlot == m_packetRouterSlot) {
- // We are the packet router, time to compute a new run ahead for this game.
- m_latencyAverages[m_localSlot] = m_frameMetrics.getAverageLatency();
- // since we are now using the display frame rate rather than the logic frame rate to get our average FPS,
- // it doesn't make sense to send the desired logic frame rate if we "slugged" ourself.
- // if (didSelfSlug) {
- // m_fpsAverages[m_localSlot] = frameRate;
- // } else {
- m_fpsAverages[m_localSlot] = m_frameMetrics.getAverageFPS();
- // }
- if (didSelfSlug) {
- //DEBUG_LOG(("ConnectionManager::updateRunAhead - local player run ahead metrics, fps = %d, actual fps = %d, latency = %f, didSelfSlug = true\n", m_fpsAverages[m_localSlot], m_frameMetrics.getAverageFPS(), m_latencyAverages[m_localSlot]));
- } else {
- //DEBUG_LOG(("ConnectionManager::updateRunAhead - local player run ahead metrics, fps = %d, latency = %f, didSelfSlug = false\n", m_fpsAverages[m_localSlot], m_latencyAverages[m_localSlot]));
- }
- Int minFps;
- Int minFpsPlayer;
- getMinimumFps(minFps, minFpsPlayer);
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::updateRunAhead - max latency = %f, min fps = %d, min fps player = %d old FPS = %d\n", getMaximumLatency(), minFps, minFpsPlayer, frameRate));
- if ((minFps >= ((frameRate * 9) / 10)) && (minFps < frameRate)) {
- // if the minimum fps is within 10% of the desired framerate, then keep the current minimum fps.
- minFps = frameRate;
- }
- if (minFps < 5) {
- minFps = 5; // Absolutely do not run below 5 fps.
- }
- if (minFps > TheGlobalData->m_framesPerSecondLimit) {
- minFps = TheGlobalData->m_framesPerSecondLimit; // Cap to 30 FPS.
- }
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::updateRunAhead - minFps after adjustment is %d\n", minFps));
- Int newRunAhead = (Int)((getMaximumLatency() / 2.0) * (Real)minFps);
- newRunAhead += (newRunAhead * TheGlobalData->m_networkRunAheadSlack) / 100; // Add in 10% of slack to the run ahead in case of network hiccups.
- if (newRunAhead < MIN_RUNAHEAD) {
- newRunAhead = MIN_RUNAHEAD; // make sure its at least MIN_RUNAHEAD.
- }
- if (newRunAhead > (MAX_FRAMES_AHEAD / 2)) {
- newRunAhead = MAX_FRAMES_AHEAD / 2; // dont let run ahead get out of hand.
- }
- NetRunAheadCommandMsg *msg = newInstance(NetRunAheadCommandMsg);
- msg->setPlayerID(m_localSlot);
- if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
- msg->setID(GenerateNextCommandID());
- }
- // needs to be set to the greater of getExecutionFrame and TheGameLogic->getFrame() + oldRunAhead
- // This prevents the case of...
- // run ahead starts at 30
- // run ahead changes to 10 at frame 31 (the command was created on frame 1)
- // run ahead changes to 10 at frame 56 (the command was created on frame 46)
- // notice that 56 is within the previous run ahead of 30 which has triggered
- // the frame command count being set for frames 1 through 60 since run ahead
- // didn't change for the first time till frame 31. This creates an extra command
- // for frame 56 that isn't accounted for in the frame command count that is sent
- // out in the NetFrameCommandMsg. sheesh.
- if (nextExecutionFrame > (TheGameLogic->getFrame() + oldRunAhead)) {
- msg->setExecutionFrame(nextExecutionFrame);
- } else {
- msg->setExecutionFrame(TheGameLogic->getFrame() + oldRunAhead);
- }
- msg->setRunAhead(newRunAhead);
- msg->setFrameRate(minFps);
- //DEBUG_LOG(("ConnectionManager::updateRunAhead - new run ahead = %d, new frame rate = %d, execution frame %d\n", newRunAhead, minFps, msg->getExecutionFrame()));
- sendLocalCommand(msg, 0xff ^ (1 << minFpsPlayer)); // Send the packet to everyone but the lowest FPS player.
- NetRunAheadCommandMsg *msg2 = newInstance(NetRunAheadCommandMsg);
- msg2->setPlayerID(m_localSlot);
- if (DoesCommandRequireACommandID(msg2->getNetCommandType())) {
- /*
- * Ok there needs to be a big friggin comment about this change...
- * What happens is that the two run ahead commands get sent to different players
- * using different command ID's. So player 1 has the run ahead command as command x
- * and player 2 has the command as command x+1. This is all good except when it comes
- * to players being disconnected. With the new disconnect scheme player 1 could potentially
- * send his run ahead command to player 2 (or the other way around) to let player 2 catch
- * up to him. So if player 2 has his run ahead command as x+1 and now he gets player 1's
- * command list with the run ahead command listed as command x, he won't see them as being
- * the same command and will now think he has two different run ahead commands for that frame
- * and thus his command list will have an extra command and he will never be able to recover.
- * So to fix this we have to use the same command ID for both run ahead commands. That way
- * when the commands are copied places for the disconnect screen they will be seen as the
- * same command, and all will be good.
- */
- // msg2->setID(GenerateNextCommandID());
- msg2->setID(msg->getID());
- }
- if (nextExecutionFrame > (TheGameLogic->getFrame() + oldRunAhead)) {
- msg2->setExecutionFrame(nextExecutionFrame);
- } else {
- msg2->setExecutionFrame(TheGameLogic->getFrame() + oldRunAhead);
- }
- // Let the player with the slowest FPS run a little faster than the other computers...
- // just in case they are able to. Then we might be able to run the game faster which would be good.
- Int newMinFps = (minFps * 11) / 10;
- if (newMinFps == minFps) {
- newMinFps = minFps + 1;
- }
- if (newMinFps > 30) {
- newMinFps = 30; // Cap FPS to 30.
- }
- msg2->setRunAhead(newRunAhead);
- msg2->setFrameRate(newMinFps);
- sendLocalCommand(msg2, 1 << minFpsPlayer);
- msg->detach();
- msg2->detach();
- } else {
- // We are not the packet router, send our metrics info to the packet router.
- NetRunAheadMetricsCommandMsg *msg = newInstance(NetRunAheadMetricsCommandMsg);
- msg->setPlayerID(m_localSlot);
- if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
- msg->setID(GenerateNextCommandID());
- }
- msg->setAverageLatency(m_frameMetrics.getAverageLatency());
- // see above for explanation.
- // if (didSelfSlug) {
- // msg->setAverageFps(frameRate);
- // } else {
- msg->setAverageFps(m_frameMetrics.getAverageFPS());
- // }
- if (didSelfSlug) {
- //DEBUG_LOG(("ConnectionManager::updateRunAhead - average latency = %f, average fps = %d, actual fps = %d, didSelfSlug = true\n", m_frameMetrics.getAverageLatency(), m_frameMetrics.getAverageFPS(), m_frameMetrics.getAverageFPS()));
- } else {
- //DEBUG_LOG(("ConnectionManager::updateRunAhead - average latency = %f, average fps = %d, didSelfSlug = false\n", m_frameMetrics.getAverageLatency(), m_frameMetrics.getAverageFPS()));
- }
- m_connections[m_packetRouterSlot]->sendNetCommandMsg(msg, 1 << m_packetRouterSlot);
- msg->detach();
- }
- lasttimesent = curTime;
- }
- }
- Real ConnectionManager::getMaximumLatency() {
- // This works for 2 player games because the latency for the packet router is always 0.
- Real lat1 = 0.0;
- Real lat2 = 0.0;
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if (isPlayerConnected(i)) {
- if (m_latencyAverages[i] != 0.0) {
- if (m_latencyAverages[i] > lat1) {
- lat2 = lat1;
- lat1 = m_latencyAverages[i];
- } else if (m_latencyAverages[i] > lat2) {
- lat2 = m_latencyAverages[i];
- }
- }
- }
- }
- return (lat1 + lat2);
- }
- void ConnectionManager::getMinimumFps(Int &minFps, Int &minFpsPlayer) {
- minFps = -1;
- minFpsPlayer = -1;
- // DEBUG_LOG(("ConnectionManager::getMinimumFps -"));
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if ((m_connections[i] != NULL) || (i == m_localSlot)) {
- // DEBUG_LOG((" %d: %d,", i, m_fpsAverages[i]));
- if (m_fpsAverages[i] != -1) {
- if ((minFps == -1) || (m_fpsAverages[i] < minFps)) {
- minFps = m_fpsAverages[i];
- minFpsPlayer = i;
- }
- }
- }
- }
- // DEBUG_LOG(("\n"));
- }
- UnsignedInt ConnectionManager::getMinimumCushion() {
- return m_frameMetrics.getMinimumCushion();
- }
- /**
- * The commands for the given frame are all ready, time to send out our command count for that frame.
- */
- void ConnectionManager::processFrameTick(UnsignedInt frame) {
- if ((m_frameData[m_localSlot] == NULL) || (m_frameData[m_localSlot]->getIsQuitting() == TRUE)) {
- // if the local frame data stuff is NULL, we must be leaving the game.
- return;
- }
- UnsignedShort commandCount = m_frameData[m_localSlot]->getCommandCount(frame);
- NetFrameCommandMsg *msg = newInstance(NetFrameCommandMsg);
- msg->setExecutionFrame(frame);
- msg->setCommandCount(commandCount);
- if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
- msg->setID(GenerateNextCommandID());
- }
- msg->setPlayerID(m_localSlot);
- m_frameMetrics.doPerFrameMetrics(frame);
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::processFrameTick - sending frame info for frame %d, ID %d, command count %d\n", frame, msg->getID(), commandCount));
- sendLocalCommand(msg, 0xff & ~(1 << m_localSlot));
- msg->detach();
- }
- /**
- * Set the local address.
- */
- void ConnectionManager::setLocalAddress(UnsignedInt ip, UnsignedInt port) {
- DEBUG_LOG(("ConnectionManager::setLocalAddress() - local address is %X:%d\n", ip, port));
- m_localAddr = ip;
- m_localPort = port;
- }
- /**
- * Initialize the transport object
- */
- void ConnectionManager::initTransport() {
- DEBUG_ASSERTCRASH((m_transport == NULL), ("m_transport already exists when trying to init it."));
- DEBUG_LOG(("ConnectionManager::initTransport - Initializing Transport\n"));
- if (m_transport != NULL) {
- delete m_transport;
- m_transport = NULL;
- }
- m_transport = new Transport;
- m_transport->reset();
- m_transport->init(m_localAddr, m_localPort);
- }
- /**
- * This is where the commands from the local client are sent to the other clients in
- * the game. This is also where the local commands are put into the frame data for
- * future execution.
- */
- void ConnectionManager::sendLocalGameMessage(GameMessage *msg, UnsignedInt frame) {
- UnsignedShort currentID = 0;
- if (DoesCommandRequireACommandID(NETCOMMANDTYPE_GAMECOMMAND)) {
- currentID = GenerateNextCommandID();
- }
- NetCommandMsg *netmsg = newInstance(NetGameCommandMsg)(msg);
- netmsg->setExecutionFrame(frame);
- netmsg->setPlayerID(m_localSlot);
- netmsg->setID(currentID);
- sendLocalCommand(netmsg);
- netmsg->detach();
- }
- /**
- * This is a NetCommandMsg that originated on the local computer. Send this to everyone specified
- * in the relay field. Commands sent in this way go through the packet router.
- */
- void ConnectionManager::sendLocalCommand(NetCommandMsg *msg, UnsignedByte relay /* = 0xff by default*/) {
- if (CommandRequiresDirectSend(msg) || (m_packetRouterSlot < 0) || (m_packetRouterSlot >= MAX_SLOTS) || (m_connections[m_packetRouterSlot] == NULL)) {
- sendLocalCommandDirect(msg, relay);
- return;
- }
- msg->attach();
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendLocalCommand - sending net command %d of type %s\n", msg->getID(),
- GetAsciiNetCommandType(msg->getNetCommandType()).str()));
- if (relay & (1 << m_localSlot)) {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendLocalCommand - adding net command of type %s to player %d for frame %d\n", GetAsciiNetCommandType(msg->getNetCommandType()).str(), msg->getPlayerID(), msg->getExecutionFrame()));
- m_frameData[m_localSlot]->addNetCommandMsg(msg);
- }
- // Send the packet to everyone else
- if (m_localSlot == m_packetRouterSlot) {
- // I am the packet router, I need to send this packet to everyone individually.
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- // Send it to all open connections.
- if (((m_connections[i] != NULL) && (m_connections[i]->isQuitting() == FALSE)) && (relay & (1 << i))) {
- // Set the relay mask to only go to this player so he knows not to relay it to anyone else.
- UnsignedByte temprelay = 1 << i;
- m_connections[i]->sendNetCommandMsg(msg, temprelay); // This will create a new copy of netmsg for this connection.
- }
- }
- } else {
- // Send the command to everyone else via the packet router.
- UnsignedByte temprelay = relay & ~(1 << m_localSlot); // Tell the packet router to relay the message to everyone but myself.
- // Hopefully the packet router is smart enough to not send it
- // to slots that are not in the game.
- m_connections[m_packetRouterSlot]->sendNetCommandMsg(msg, temprelay); // This will create a new copy of netmsg for this connection.
- if (CommandRequiresAck(msg)) {
- NetCommandRef *ref = m_pendingCommands->addMessage(msg);
- //DEBUG_LOG(("ConnectionManager::sendLocalCommand - added command %d to pending commands list.\n", msg->getID()));
- if (ref != NULL) {
- ref->setRelay(temprelay);
- }
- }
- }
- msg->detach(); // detach from the command msg.
- }
- /**
- * This is a NetCommandMsg that originated on the local computer. Send this to everyone specified
- * in the relay field. Commands sent in this way do not go through the packet router.
- */
- void ConnectionManager::sendLocalCommandDirect(NetCommandMsg *msg, UnsignedByte relay) {
- msg->attach();
- if (((relay & (1 << m_localSlot)) != 0) && (m_frameData[m_localSlot] != NULL)) {
- if (IsCommandSynchronized(msg->getNetCommandType()) == TRUE) {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendLocalCommandDirect - adding net command of type %s to player %d for frame %d\n", GetAsciiNetCommandType(msg->getNetCommandType()).str(), msg->getPlayerID(), msg->getExecutionFrame()));
- m_frameData[m_localSlot]->addNetCommandMsg(msg);
- }
- }
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if ((relay & (1 << i)) != 0) {
- if ((m_connections[i] != NULL) && (m_connections[i]->isQuitting() == FALSE)) {
- UnsignedByte temprelay = 1 << i;
- m_connections[i]->sendNetCommandMsg(msg, temprelay);
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendLocalCommandDirect - Sending direct command %d of type %s to player %d\n", msg->getID(), GetAsciiNetCommandType(msg->getNetCommandType()).str(), i));
- }
- }
- }
- msg->detach();
- }
- Int commandsReadyDebugSpewage = 0;
- /**
- * Returns true if all the commands for the given frame are ready to be executed.
- */
- Bool ConnectionManager::allCommandsReady(UnsignedInt frame, Bool justTesting /* = FALSE */) {
- Bool retval = TRUE;
- FrameDataReturnType frameRetVal;
- // retval = FALSE; // ****for testing purposes only!!!!!!****
- for (Int i = 0; (i < MAX_SLOTS) && retval; ++i) {
- if ((m_frameData[i] != NULL) && (m_frameData[i]->getIsQuitting() == FALSE)) {
- /*
- if (!(m_frameData[i]->allCommandsReady(frame, (frame != commandsReadyDebugSpewage) && (justTesting == FALSE)))) {
- if ((frame != commandsReadyDebugSpewage) && (justTesting == FALSE)) {
- DEBUG_LOG(("ConnectionManager::allCommandsReady, frame %d player %d not ready.\n", frame, i));
- commandsReadyDebugSpewage = frame;
- }
- retval = FALSE;
- } else {
- // DEBUG_LOG(("ConnectionManager::allCommandsReady, frame %d player %d is ready.\n", frame, i));
- }
- */
- frameRetVal = m_frameData[i]->allCommandsReady(frame, (frame != commandsReadyDebugSpewage) && (justTesting == FALSE));
- if (frameRetVal == FRAMEDATA_NOTREADY) {
- retval = FALSE;
- } else if (frameRetVal == FRAMEDATA_RESEND) {
- requestFrameDataResend(i, frame);
- retval = FALSE;
- }
- }
- }
- if (frameRetVal == FRAMEDATA_RESEND) {
- // this frame's data is really screwed up, we need to clean it out so it can be resent to us.
- for (i = 0; i < MAX_SLOTS; ++i) {
- if ((m_frameData[i] != NULL) && (i != m_localSlot)) {
- m_frameData[i]->resetFrame(frame, FALSE);
- }
- }
- }
- if ((retval == TRUE) && (justTesting == FALSE)) {
- m_disconnectManager->allCommandsReady(TheGameLogic->getFrame(), this);
- retval = m_disconnectManager->allowedToContinue(); // allow the disconnect manager to keep us on this frame
- // in case we are waiting for a new packet router or something.
- }
- return retval;
- }
- void ConnectionManager::handleAllCommandsReady(void)
- {
- m_disconnectManager->allCommandsReady(TheGameLogic->getFrame(), this, FALSE);
- }
- /**
- * Only call this after making sure that all the commands are there for this frame.
- * After calling this the commands for this frame will be removed from the connection.
- *
- * BGC - To account for the case where the host disconnects without sending the
- * same commands to all players, we now have to keep around the last 'run ahead'
- * frames so we can potentially send those commands to the other players in the
- * game so they can catch up.
- */
- NetCommandList *ConnectionManager::getFrameCommandList(UnsignedInt frame)
- {
- NetCommandList *retlist = newInstance(NetCommandList);
- retlist->init();
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if (m_frameData[i] != NULL) {
- retlist->appendList(m_frameData[i]->getFrameCommandList(frame));
- if (frame > FRAMES_TO_KEEP) {
- m_frameData[i]->resetFrame(frame - FRAMES_TO_KEEP); // After getting the commands for that frame from this
- // FrameDataManager object, we need to tell it that we're
- // done with the messages for that frame.
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("getFrameCommandList - called reset frame on player %d for frame %d\n", i, frame - FRAMES_TO_KEEP));
- }
- }
- }
- return retlist; // retlist deallocated by calling function.
- }
- void ConnectionManager::setFrameGrouping(time_t frameGrouping) {
- // Since we are the packet router, we should send more packets per second since we
- // may become the latency bottleneck for sending packets from one player to the next.
- // This is probably ok since the packet router should have the fastest connection of all
- // the players in the game.
- if (m_localSlot == m_packetRouterSlot) {
- frameGrouping = frameGrouping / 2;
- }
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if (m_connections[i] != NULL) {
- m_connections[i]->setFrameGrouping(frameGrouping);
- }
- }
- }
- /*
- void ConnectionManager::determineRouterFallbackPlan() {
- memset(m_packetRouterFallback, 0, sizeof(m_packetRouterFallback));
- Int curnum = 1;
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if (m_connections[i] != NULL) {
- m_packetRouterFallback[i] = curnum;
- if (curnum == 1) {
- m_packetRouterSlot = i;
- }
- ++curnum;
- }
- }
- }
- */
- void ConnectionManager::doKeepAlive() {
- static Int nextIndex = 0;
- static time_t startTime = 0;
- time_t curTime = timeGetTime();
- if (startTime == 0) {
- startTime = curTime;
- return;
- }
- time_t numSeconds = (curTime - startTime) / 1000;
- while ((nextIndex <= numSeconds) && (nextIndex < MAX_SLOTS)) {
- // DEBUG_LOG(("ConnectionManager::doKeepAlive - trying to send keep alive message to player %d\n", nextIndex));
- if (m_connections[nextIndex] != NULL) {
- NetKeepAliveCommandMsg *msg = newInstance(NetKeepAliveCommandMsg);
- msg->setPlayerID(m_localSlot);
- if (DoesCommandRequireACommandID(msg->getNetCommandType()) == TRUE) {
- msg->setID(GenerateNextCommandID());
- }
- // DEBUG_LOG(("ConnectionManager::doKeepAlive - sending keep alive message to player %d\n", nextIndex));
- sendLocalCommandDirect(msg, 1 << nextIndex);
- msg->detach();
- }
- ++nextIndex;
- }
- if (nextIndex == MAX_SLOTS) {
- nextIndex = 0;
- startTime = curTime;
- }
- }
- PlayerLeaveCode ConnectionManager::disconnectPlayer(Int slot) {
- // Need to do the deletion of the slot's connection and frame data here.
- PlayerLeaveCode retval = PLAYERLEAVECODE_CLIENT;
- DEBUG_LOG(("ConnectionManager::disconnectPlayer - disconnecting slot %d on frame %d\n", slot, TheGameLogic->getFrame()));
- if ((slot < 0) || (slot >= MAX_SLOTS)) {
- return PLAYERLEAVECODE_UNKNOWN;
- }
- if (TheGameInfo)
- {
- GameSlot *gSlot = TheGameInfo->getSlot( slot );
- if (gSlot && !gSlot->lastFrameInGame())
- {
- DEBUG_LOG(("ConnectionManager::disconnectPlayer(%d) - slot is last in the game on frame %d\n",
- slot, TheGameLogic->getFrame()));
- gSlot->setLastFrameInGame(TheGameLogic->getFrame());
- }
- }
- UnicodeString unicodeName;
- unicodeName = getPlayerName(slot);
- if (unicodeName.getLength() > 0 && m_connections[slot]) {
- TheInGameUI->message("Network:PlayerLeftGame", unicodeName.str());
- // People are boneheads. Also play a sound
- static AudioEventRTS leftGameSound("GUIMessageReceived");
- TheAudio->addAudioEvent(&leftGameSound);
- }
- if ((m_frameData[slot] != NULL) && (m_frameData[slot]->getIsQuitting() == FALSE)) {
- DEBUG_LOG(("ConnectionManager::disconnectPlayer - deleting player %d frame data\n", slot));
- m_frameData[slot]->deleteInstance();
- m_frameData[slot] = NULL;
- }
- if (m_connections[slot] != NULL && !m_connections[slot]->isQuitting()) {
- DEBUG_LOG(("ConnectionManager::disconnectPlayer - deleting player %d connection\n", slot));
- m_connections[slot]->deleteInstance();
- m_connections[slot] = NULL;
- }
- // if (playerID == m_localSlot) {
- // TheMessageStream->appendMessage(GameMessage::MSG_CLEAR_GAME_DATA);
- // }
- if (slot == m_packetRouterSlot) {
- Int index = 0;
- while ((index < (MAX_SLOTS-1)) && (m_packetRouterFallback[index] != m_packetRouterSlot)) {
- ++index;
- }
- ++index;
- m_packetRouterSlot = m_packetRouterFallback[index];
- DEBUG_LOG(("Packet router left. New packet router is slot %d\n", m_packetRouterSlot));
- retval = PLAYERLEAVECODE_PACKETROUTER;
- }
- if (m_localSlot == slot) {
- DEBUG_LOG(("Disconnecting self\n"));
- retval = PLAYERLEAVECODE_LOCAL;
- }
- // Take the player out of the fallback plan
- Int fallbackindex = 0;
- while ((fallbackindex < MAX_SLOTS) && (m_packetRouterFallback[fallbackindex] != slot)) {
- ++fallbackindex;
- }
- for (Int i = fallbackindex; i < MAX_SLOTS-1; ++i) {
- m_packetRouterFallback[i] = m_packetRouterFallback[i+1];
- }
- m_packetRouterFallback[MAX_SLOTS-1] = -1;
- return retval;
- }
- void ConnectionManager::quitGame() {
- // Need to do the NetDisconnectPlayerCommandMsg creation and sending here.
- NetDisconnectPlayerCommandMsg *disconnectMsg = newInstance(NetDisconnectPlayerCommandMsg);
- disconnectMsg->setDisconnectSlot(m_localSlot);
- disconnectMsg->setPlayerID(m_localSlot);
- if (DoesCommandRequireACommandID(disconnectMsg->getNetCommandType())) {
- disconnectMsg->setID(GenerateNextCommandID());
- }
- //DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer - about to send disconnect command\n"));
- sendLocalCommandDirect(disconnectMsg, 0xff ^ (1 << m_localSlot));
- //DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer - about to flush connections\n"));
- flushConnections(); // need to do this so our packet actually gets sent before the connections are deleted.
- //DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer - done flushing connections\n"));
- disconnectMsg->detach();
- // if we get here, we hit Quit on the disconnect screen. Mark everyone as having disconnected from us
- // so the online stats can give us appropriate feedback.
- if (TheGameInfo)
- {
- for (Int i = 0; i < MAX_SLOTS; ++i)
- {
- GameSlot *gSlot = TheGameInfo->getSlot( i );
- if (gSlot && !gSlot->lastFrameInGame())
- {
- gSlot->markAsDisconnected();
- }
- }
- }
- disconnectLocalPlayer();
- }
- void ConnectionManager::disconnectLocalPlayer() {
- // kill the frame data and the connections for all the other players.
- DEBUG_LOG(("ConnectionManager::disconnectLocalPlayer()\n"));
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if (i != m_localSlot) {
- disconnectPlayer(i);
- }
- }
- }
- /**
- * Takes all the commands that are ready to send and sends them right now.
- */
- void ConnectionManager::flushConnections() {
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if (m_connections[i] != NULL) {
- // DEBUG_LOG(("ConnectionManager::flushConnections - flushing connection to player %d\n", i));
- /*
- if (m_connections[i]->isQueueEmpty()) {
- // DEBUG_LOG(("ConnectionManager::flushConnections - connection queue empty\n"));
- }
- */
- m_connections[i]->doSend();
- }
- }
- if (m_transport != NULL) {
- m_transport->doSend();
- }
- }
- void ConnectionManager::resendPendingCommands() {
- //DEBUG_LOG(("ConnectionManager::resendPendingCommands()\n"));
- if (m_pendingCommands == NULL) {
- return;
- }
- NetCommandRef *ref = m_pendingCommands->getFirstMessage();
- while (ref != NULL) {
- //DEBUG_LOG(("ConnectionManager::resendPendingCommands - resending command %d\n", ref->getCommand()->getID()));
- sendLocalCommand(ref->getCommand(), ref->getRelay());
- ref = ref->getNext();
- }
- }
- UnsignedInt ConnectionManager::getLocalPlayerID() {
- return m_localSlot;
- }
- UnicodeString ConnectionManager::getPlayerName(Int playerNum) {
- UnicodeString retval;
- if( playerNum == m_localSlot ) {
- retval = m_localUser->GetName();
- } else if (((m_connections[playerNum] != NULL) && (m_connections[playerNum]->isQuitting() == FALSE))) {
- retval = m_connections[playerNum]->getUser()->GetName();
- }
- return retval;
- }
- /**
- * Take a user list and make connections and frame data manager objects for each of the players.
- * For now, this is also how we'll determine the packet router fallback plan.
- */
- void ConnectionManager::parseUserList(const GameInfo *game)
- {
- if (!game)
- return;
- Int i;
- Int numUsers = 0;
- m_localSlot = -1;
- DEBUG_LOG(("Local slot is %d\n", game->getLocalSlotNum()));
- for (i=0; i<MAX_SLOTS; ++i)
- {
- const GameSlot *slot = game->getConstSlot(i); // badness, but since we cast right back to const, we should be ok
- if (slot->isHuman())
- {
- if (game->getLocalSlotNum() == i)
- {
- m_localSlot = i;
- m_localUser->setName(slot->getName());
- }
- if (m_localSlot != i)
- {
- m_connections[i] = newInstance(Connection)();
- m_connections[i]->init();
- m_connections[i]->attachTransport(m_transport);
- // UnsignedShort port = (TheNAT)?TheNAT->getSlotPort(i):8088;
- UnsignedShort port = slot->getPort();
- m_connections[i]->setUser(newInstance(User)(slot->getName(), slot->getIP(), port));
- m_frameData[i] = newInstance(FrameDataManager)(FALSE);
- DEBUG_LOG(("Remote user is at %X:%d\n", slot->getIP(), slot->getPort()));
- }
- else
- {
- DEBUG_LOG(("Local user is %d (%X:%d)\n", m_localSlot, slot->getIP(), slot->getPort()));
- m_frameData[i] = newInstance(FrameDataManager)(TRUE);
- }
- m_frameData[i]->init();
- m_frameData[i]->reset();
- m_packetRouterFallback[numUsers] = i;
- ++numUsers;
- }
- }
- #ifdef MEMORYPOOL_DEBUG
- TheMemoryPoolFactory->debugSetInitFillerIndex(m_localSlot);
- #endif
- /*
- if ( numUsers < 2 || m_localSlot == -1 )
- {
- DEBUG_CRASH(("FAILED parseUserList - network game won't work as expected\n"));
- return;
- }
- char * list = strdup(buf);
- char *listPtr = list;
- if (!list)
- return;
- User users[MAX_SLOTS];
- int localUser = -1;
- int i;
- for (i=0; i<MAX_SLOTS; i++)
- {
- users[i].setName("");
- }
- char *userStr, *nameStr, *addrStr, *portStr;
- AsciiString addrAsciiStr;
- char *listPos;
- DEBUG_LOG(("ConnectionManager::parseUserList - looking for local user at %d.%d.%d.%d:%d\n",
- (m_localAddr >> 24) & 0xff,
- (m_localAddr >> 16) & 0xff,
- (m_localAddr >> 8) & 0xff,
- m_localAddr & 0xff,
- m_localPort));
- int numUsers = 0;
- while ( (userStr=strtok_r(listPtr, ",", &listPos)) != NULL )
- {
- listPtr = NULL;
- char *pos = NULL;
- nameStr = strtok_r(userStr, "@", &pos);
- addrStr = strtok_r(NULL, "@:", &pos);
- portStr = strtok_r(NULL, ": ", &pos);
- if (!portStr || numUsers >= MAX_SLOTS)
- {
- DEBUG_LOG(("ConnectionManager::parseUserList - (numUsers = %d) FAILED parseUserList with list [%s]\n", numUsers, buf));
- return;
- }
- addrAsciiStr = addrStr;
- UnsignedInt addr = ResolveIP(addrAsciiStr);
- UnsignedInt port = atoi(portStr);
- // if ((m_localAddr != addr) || (m_localPort != port)) {
- if (loginName.compare(nameStr) != 0) {
- m_connections[numUsers] = newInstance(Connection)();
- m_connections[numUsers]->init();
- m_connections[numUsers]->attachTransport(m_transport);
- m_connections[numUsers]->setUser(newInstance(User)(nameStr, addr, port));
- m_frameData[numUsers] = newInstance(FrameDataManager)(FALSE);
- DEBUG_LOG(("ConnectionManager::parseUserList - User %d is %s\n", numUsers, nameStr));
- } else {
- m_localSlot = numUsers;
- m_localUser.setName(nameStr);
- DEBUG_LOG(("ConnectionManager::parseUserList - User %d is %s\n", numUsers, nameStr));
- DEBUG_LOG(("Local user is %d\n", m_localSlot));
- m_frameData[numUsers] = newInstance(FrameDataManager)(TRUE);
- }
- m_frameData[numUsers]->init();
- m_frameData[numUsers]->reset();
- m_packetRouterFallback[numUsers] = numUsers;
- numUsers++;
- }
- if (numUsers < 2 || m_localSlot == -1)
- {
- DEBUG_LOG(("ConnectionManager::parseUserList - FAILED (local user = %d, num players = %d) with list [%s]\n", m_localSlot, numUsers, buf));
- return;
- }
- if (list != NULL) {
- free(list); // from the strdup above.
- list = NULL;
- }
- */
- }
- /**
- * Return the number of incoming bytes per second averaged over 30 sec.
- */
- Real ConnectionManager::getIncomingBytesPerSecond( void )
- {
- if (m_transport)
- return m_transport->getIncomingBytesPerSecond();
- else
- return 0.0;
- }
- /**
- * Return the number of incoming packets per second averaged over the last 30 sec.
- */
- Real ConnectionManager::getIncomingPacketsPerSecond( void )
- {
- if (m_transport)
- return m_transport->getIncomingPacketsPerSecond();
- else
- return 0.0;
- }
- /**
- * Return the number of outgoing bytes per second averaged over the last 30 sec.
- */
- Real ConnectionManager::getOutgoingBytesPerSecond( void )
- {
- if (m_transport)
- return m_transport->getOutgoingBytesPerSecond();
- else
- return 0.0;
- }
- /**
- * Return the number of outgoing packets per second averaged over the last 30 sec.
- */
- Real ConnectionManager::getOutgoingPacketsPerSecond( void )
- {
- if (m_transport) {
- return m_transport->getOutgoingPacketsPerSecond();
- } else {
- return 0.0;
- }
- }
- /**
- * Return the number of bytes not from generals clients received per second averaged over the last 30 sec.
- */
- Real ConnectionManager::getUnknownBytesPerSecond( void )
- {
- if (m_transport)
- return m_transport->getUnknownBytesPerSecond();
- else
- return 0.0;
- }
- /**
- * Return the number ov packets not from generals clients received per second averaged over the last 30 sec.
- */
- Real ConnectionManager::getUnknownPacketsPerSecond( void )
- {
- if (m_transport)
- return m_transport->getUnknownPacketsPerSecond();
- else
- return 0.0;
- }
- /**
- * Return the smallest packet arrival cushion since this was last called.
- */
- UnsignedInt ConnectionManager::getPacketArrivalCushion() {
- UnsignedInt retval = m_smallestPacketArrivalCushion;
- m_smallestPacketArrivalCushion = -1;
- return retval;
- }
- void ConnectionManager::sendChat(UnicodeString text, Int playerMask, UnsignedInt executionFrame)
- {
- NetChatCommandMsg *msg = newInstance(NetChatCommandMsg);
- msg->setText(text);
- msg->setPlayerMask(playerMask);
- msg->setPlayerID(m_localSlot);
- msg->setID(0);
- msg->setExecutionFrame(executionFrame);
- if (DoesCommandRequireACommandID(msg->getNetCommandType()))
- {
- msg->setID(GenerateNextCommandID());
- }
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("Chat message has ID of %d, mask of %8.8X, text of %ls\n", msg->getID(), msg->getPlayerMask(), msg->getText().str()));
- sendLocalCommand(msg, 0xff ^ (1 << m_localSlot));
- processChat(msg);
- msg->detach();
- }
- void ConnectionManager::sendDisconnectChat(UnicodeString text) {
- NetDisconnectChatCommandMsg *msg = newInstance(NetDisconnectChatCommandMsg);
- msg->setPlayerID(m_localSlot);
- if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
- msg->setID(GenerateNextCommandID());
- }
- msg->setText(text);
- sendLocalCommandDirect(msg, 0xff ^ (1 << m_localSlot));
- processDisconnectChat(msg);
- }
- UnsignedShort ConnectionManager::sendFileAnnounce(AsciiString path, UnsignedByte playerMask)
- {
- File *theFile = TheLocalFileSystem->openFile(path.str());
- if (!theFile || !theFile->size())
- {
- UnicodeString log;
- log.format(L"Not sending file '%hs' to %X\n", path.str(), playerMask);
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("%ls\n", log.str()));
- if (TheLAN)
- TheLAN->OnChat(UnicodeString(L"sendFile"), 0, log, LANAPI::LANCHAT_SYSTEM);
- return 0;
- }
- theFile->close();
- Int announceMask = 0xff ^ (1 << m_localSlot);
- NetFileAnnounceCommandMsg *announceMsg = newInstance(NetFileAnnounceCommandMsg);
- announceMsg->setPlayerID(m_localSlot);
- if (DoesCommandRequireACommandID(announceMsg->getNetCommandType()) == TRUE) {
- announceMsg->setID(GenerateNextCommandID());
- }
- announceMsg->setRealFilename(path);
- announceMsg->setPlayerMask(playerMask);
- UnsignedShort fileID = GenerateNextCommandID();
- announceMsg->setFileID(fileID);
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendFileAnnounce() - creating announce message with ID of %d from %d to mask %X for '%s' going to %X as command %d\n",
- announceMsg->getID(), announceMsg->getPlayerID(), announceMask, announceMsg->getRealFilename().str(),
- announceMsg->getPlayerMask(), announceMsg->getFileID()));
- processFileAnnounce(announceMsg); // set up things for the host
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("Sending file announce to %X\n", announceMask));
- sendLocalCommand(announceMsg, announceMask);
- announceMsg->detach();
- return fileID;
- }
- void ConnectionManager::sendFile(AsciiString path, UnsignedByte playerMask, UnsignedShort commandID)
- {
- File *theFile = TheLocalFileSystem->openFile(path.str());
- if (!theFile || !theFile->size())
- {
- UnicodeString log;
- log.format(L"Not sending file '%hs' to %X\n", path.str(), playerMask);
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("%ls\n", log.str()));
- if (TheLAN)
- TheLAN->OnChat(UnicodeString(L"sendFile"), 0, log, LANAPI::LANCHAT_SYSTEM);
- return;
- }
- Int len = theFile->size();
- char *buf = theFile->readEntireAndClose();
- // compress Targas
- #ifdef COMPRESS_TARGAS
- char *compressedBuf = NULL;
- Int compressedLen = path.endsWith(".tga")?CompressionManager::getMaxCompressedSize(len, CompressionManager::getPreferredCompression()):0;
- Int compressedSize = 0;
- if (compressedLen)
- compressedSize = CompressionManager::compressData(CompressionManager::getPreferredCompression(),
- buf, len, compressedBuf, compressedLen);
- if (compressedBuf && !compressedSize)
- {
- delete[] compressedBuf;
- compressedBuf = NULL;
- }
- #endif // COMPRESS_TARGAS
- NetFileCommandMsg *fileMsg = newInstance(NetFileCommandMsg);
- fileMsg->setPlayerID(m_localSlot);
- fileMsg->setID(commandID);
- fileMsg->setRealFilename(path);
- #ifdef COMPRESS_TARGAS
- if (compressedBuf)
- {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("Compressed '%s' from %d to %d (%g%%) before transfer\n", path.str(), len, compressedSize,
- (Real)compressedSize/(Real)len*100.0f));
- fileMsg->setFileData((unsigned char *)compressedBuf, compressedSize);
- }
- else
- #endif // COMPRESS_TARGAS
- {
- fileMsg->setFileData((unsigned char *)buf, len);
- }
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendFile() - creating file message with ID of %d for '%s' going to %X from %d, size of %d\n",
- fileMsg->getID(), fileMsg->getRealFilename().str(), playerMask, fileMsg->getPlayerID(), fileMsg->getFileLength()));
- delete[] buf;
- buf = NULL;
- #ifdef COMPRESS_TARGAS
- if (compressedBuf)
- {
- delete[] compressedBuf;
- compressedBuf = NULL;
- }
- #endif // COMPRESS_TARGAS
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("Sending file: '%s', len %d, to %X\n", path.str(), len, playerMask));
- sendLocalCommand(fileMsg, playerMask);
- fileMsg->detach();
- }
- Int ConnectionManager::getFileTransferProgress(Int playerID, AsciiString path)
- {
- FileCommandMap::iterator commandIt = s_fileCommandMap.begin();
- while (commandIt != s_fileCommandMap.end())
- {
- //DEBUG_LOG(("ConnectionManager::getFileTransferProgress(%s): looking at existing transfer of '%s'\n",
- // path.str(), commandIt->second.str()));
- if (commandIt->second == path)
- {
- return s_fileProgressMap[playerID][commandIt->first];
- }
- ++commandIt;
- }
- //DEBUG_LOG(("Falling back to 0, since we couldn't find the map\n"));
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::getFileTransferProgress: path %s not found\n",path.str()));
- return 0;
- }
- void ConnectionManager::voteForPlayerDisconnect(Int slot) {
- if (m_disconnectManager != NULL) {
- m_disconnectManager->voteForPlayerDisconnect(slot, this);
- }
- }
- Int ConnectionManager::getNumPlayers()
- {
- Int retval = 0;
- for (Int i = 0; i < MAX_SLOTS; ++i)
- {
- if (isPlayerConnected(i)) {
- ++retval;
- }
- }
- return retval;
- }
- void ConnectionManager::updateLoadProgress( Int progress )
- {
- NetProgressCommandMsg *msg = newInstance(NetProgressCommandMsg);
- msg->setPercentage( progress );
- msg->setPlayerID( m_localSlot );
- if (DoesCommandRequireACommandID(msg->getNetCommandType()) == TRUE) {
- msg->setID(GenerateNextCommandID());
- }
- processProgress(msg);
- sendLocalCommand(msg, 0xff ^ (1 << m_localSlot));
- msg->detach();
- }
- void ConnectionManager::loadProgressComplete()
- {
- NetCommandMsg *msg = newInstance(NetCommandMsg);
- msg->setPlayerID( m_localSlot );
- if (DoesCommandRequireACommandID(msg->getNetCommandType()) == TRUE) {
- msg->setID(GenerateNextCommandID());
- }
- msg->setNetCommandType(NETCOMMANDTYPE_LOADCOMPLETE);
- processLoadComplete(msg);
- sendLocalCommand(msg, 0xff ^ (1 << m_localSlot));
- msg->detach();
- }
- void ConnectionManager::sendTimeOutGameStart()
- {
- NetCommandMsg *msg = newInstance(NetCommandMsg);
- msg->setPlayerID( m_localSlot );
- msg->setNetCommandType(NETCOMMANDTYPE_TIMEOUTSTART);
- if (DoesCommandRequireACommandID(msg->getNetCommandType()) == TRUE) {
- msg->setID(GenerateNextCommandID());
- }
- processTimeOutGameStart(msg);
- sendLocalCommand(msg, 0xff ^ (1 << m_localSlot));
- msg->detach();
- }
- Bool ConnectionManager::isPacketRouter( void )
- {
- return m_localSlot == m_packetRouterSlot;
- }
- Int ConnectionManager::getAverageFPS( void )
- {
- return m_frameMetrics.getAverageFPS();
- }
- Int ConnectionManager::getSlotAverageFPS(Int slot) {
- if ((slot < 0) || (slot >= MAX_SLOTS)) {
- return -1;
- }
- if ((m_packetRouterSlot != m_localSlot) && (slot == m_localSlot)) {
- // our framerate data isn't valid for other players unless we are the
- // packet router, so don't fake someone out.
- return -1;
- }
- return m_fpsAverages[slot];
- }
- #if defined(_DEBUG) || defined(_INTERNAL)
- void ConnectionManager::debugPrintConnectionCommands() {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::debugPrintConnectionCommands - begin commands\n"));
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if (m_connections[i] != NULL) {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::debugPrintConnectionCommands - commands for connection %d\n", i));
- m_connections[i]->debugPrintCommands();
- }
- }
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::debugPrintConnectionCommands - end commands\n"));
- }
- #endif
- void ConnectionManager::notifyOthersOfCurrentFrame(Int frame) {
- NetDisconnectFrameCommandMsg *msg = newInstance(NetDisconnectFrameCommandMsg);
- msg->setPlayerID(m_localSlot);
- msg->setDisconnectFrame(frame);
- if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
- msg->setID(GenerateNextCommandID());
- }
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::notifyOthersOfCurrentFrame - sending disconnect frame of %d, command ID = %d\n", frame, msg->getID()));
- sendLocalCommandDirect(msg, 0xff ^ (1 << m_localSlot));
- NetCommandRef *ref = NEW_NETCOMMANDREF(msg);
- ref->setRelay(1 << m_localSlot);
- m_disconnectManager->processDisconnectCommand(ref, this);
- ref->deleteInstance();
- msg->detach();
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::notifyOthersOfCurrentFrame - start screen on debug stuff\n"));
- #if defined(_DEBUG) || defined(_INTERNAL)
- debugPrintConnectionCommands();
- #endif
- }
- void ConnectionManager::notifyOthersOfNewFrame(UnsignedInt frame) {
- NetDisconnectScreenOffCommandMsg *msg = newInstance(NetDisconnectScreenOffCommandMsg);
- msg->setPlayerID(m_localSlot);
- msg->setNewFrame(frame);
- if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
- msg->setID(GenerateNextCommandID());
- }
- sendLocalCommandDirect(msg, 0xff ^ (1 << m_localSlot));
- NetCommandRef *ref = NEW_NETCOMMANDREF(msg);
- ref->setRelay(1 << m_localSlot);
- m_disconnectManager->processDisconnectCommand(ref, this);
- ref->deleteInstance();
- msg->detach();
- }
- void ConnectionManager::sendFrameDataToPlayer(UnsignedInt playerID, UnsignedInt startingFrame) {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendFrameDataToPlayer - sending frame data to player %d starting with frame %d\n", playerID, startingFrame));
- for (UnsignedInt frame = startingFrame; frame < TheGameLogic->getFrame(); ++frame) {
- sendSingleFrameToPlayer(playerID, frame);
- }
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendFrameDataToPlayer - done sending commands to player %d\n", playerID));
- }
- void ConnectionManager::sendSingleFrameToPlayer(UnsignedInt playerID, UnsignedInt frame) {
- if ((TheGameLogic->getFrame() - FRAMES_TO_KEEP) > frame) {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendSingleFrameToPlayer - player %d requested frame %d when we are on frame %d, this is too far in the past.\n", playerID, frame, TheGameLogic->getFrame()));
- return;
- }
- UnsignedByte relay = 1 << playerID;
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendFrameDataToPlayer - sending data for frame %d\n", frame));
- for (Int i = 0; i < MAX_SLOTS; ++i) {
- if ((m_frameData[i] != NULL) && (i != playerID)) { // no need to send his own commands to him.
- NetCommandList *list = m_frameData[i]->getFrameCommandList(frame);
- if (list != NULL) {
- NetCommandRef *ref = list->getFirstMessage();
- while (ref != NULL) {
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendFrameDataToPlayer - sending command %d from player %d to player %d using relay 0x%x\n", ref->getCommand()->getID(), i, playerID, relay));
- sendLocalCommandDirect(ref->getCommand(), relay);
- ref = ref->getNext();
- }
- }
- UnsignedInt frameCommandCount = m_frameData[i]->getFrameCommandCount(frame);
- NetFrameCommandMsg *msg = newInstance(NetFrameCommandMsg);
- msg->setExecutionFrame(frame);
- msg->setCommandCount(frameCommandCount);
- if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
- msg->setID(GenerateNextCommandID());
- }
- msg->setPlayerID(i);
- DEBUG_LOG_LEVEL(DEBUG_LEVEL_NET, ("ConnectionManager::sendFrameDataToPlayer - sending frame info from player %d to player %d for frame %d with command count %d and ID %d and relay %d\n", i, playerID, msg->getExecutionFrame(), msg->getCommandCount(), msg->getID(), relay));
- sendLocalCommandDirect(msg, relay);
- msg->detach();
- }
- }
- }
- UnsignedInt ConnectionManager::getNextPacketRouterSlot(UnsignedInt playerID) {
- Int index = 0;
- while ((index < (MAX_SLOTS-1)) && (m_packetRouterFallback[index] != playerID)) {
- ++index;
- }
- ++index;
- return m_packetRouterFallback[index];
- }
- void ConnectionManager::requestFrameDataResend(Int playerID, UnsignedInt frame) {
- NetFrameResendRequestCommandMsg *msg = newInstance(NetFrameResendRequestCommandMsg);
- msg->setPlayerID(m_localSlot);
- msg->setFrameToResend(frame);
- if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
- msg->setID(GenerateNextCommandID());
- }
- if (isPlayerConnected(playerID) == FALSE) {
- playerID = 0;
- while ((playerID < MAX_SLOTS) && (isPlayerConnected(playerID) == FALSE)) {
- ++playerID;
- }
- }
- if (playerID < MAX_SLOTS) {
- sendLocalCommandDirect(msg, 1 << playerID);
- }
- msg->detach();
- }
|