/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code 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. Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #ifndef __PACKET_PROCESSOR_H__ #define __PACKET_PROCESSOR_H__ /* ================================================ idPacketProcessor ================================================ */ class idPacketProcessor { public: static const int RETURN_TYPE_NONE = 0; static const int RETURN_TYPE_OOB = 1; static const int RETURN_TYPE_INBAND = 2; typedef uint16 sessionId_t; static const int NUM_LOBBY_TYPE_BITS = 2; static const int LOBBY_TYPE_MASK = ( 1 << NUM_LOBBY_TYPE_BITS ) - 1; static const sessionId_t SESSION_ID_INVALID = 0; static const sessionId_t SESSION_ID_CONNECTIONLESS_PARTY = 1; static const sessionId_t SESSION_ID_CONNECTIONLESS_GAME = 2; static const sessionId_t SESSION_ID_CONNECTIONLESS_GAME_STATE = 3; static const int BANDWIDTH_AVERAGE_PERIOD = 250; idPacketProcessor() { Reset(); } void Reset() { msgWritePos = 0; fragmentSequence = 0; droppedFrags = 0; fragmentedSend = false; reliable = idDataQueue< MAX_RELIABLE_QUEUE, MAX_MSG_SIZE >(); reliableSequenceSend = 1; reliableSequenceRecv = 0; numReliable = 0; queuedReliableAck = -1; unsentMsg = idBitMsg(); lastSendTime = 0; outgoingRateTime = 0; outgoingRateBytes = 0.0f; incomingRateTime = 0; incomingRateBytes = 0.0f; outgoingBytes = 0; incomingBytes = 0; currentOutgoingRate = 0; lastOutgoingRateTime = 0; lastOutgoingBytes = 0; currentIncomingRate = 0; lastIncomingRateTime = 0; lastIncomingBytes = 0; fragmentAccumulator = 0; } static const int MAX_MSG_SIZE = 8000; // This is the max size you can pass into ProcessOutgoing static const int MAX_FINAL_PACKET_SIZE = 1200; // Lowest/safe MTU across all our platforms to avoid fragmentation at the transport layer (which is poorly supported by consumer hardware and may cause nasty latency side effects) static const int MAX_RELIABLE_QUEUE = 64; // TypeInfo doesn't like sizeof( sessionId_t )?? and then fails to understand the #ifdef/#else/#endif //static const int MAX_PACKET_SIZE = MAX_FINAL_PACKET_SIZE - 6 - sizeof( sessionId_t ); // Largest possible packet before headers and such applied (subtract some for various internal header data, and session id) static const int MAX_PACKET_SIZE = MAX_FINAL_PACKET_SIZE - 6 - 2; // Largest possible packet before headers and such applied (subtract some for various internal header data, and session id) static const int MAX_OOB_MSG_SIZE = MAX_PACKET_SIZE - 1; // We don't allow fragmentation for out-of-band msg's, and we need a byte for the header private: void QueueReliableAck( int lastReliable ); int FinalizeRead( idBitMsg & inMsg, idBitMsg & outMsg, int & userValue ); public: bool CanSendMoreData() const; void UpdateOutgoingRate( const int time, const int size ); void UpdateIncomingRate( const int time, const int size ); void RefreshRates( int time ) { UpdateOutgoingRate( time, 0 ); UpdateIncomingRate( time, 0 ); } // Used to queue reliable msg's, to be sent on the next ProcessOutgoing bool QueueReliableMessage( byte type, const byte * data, int dataLen ); // Used to process a msg ready to be sent, could get fragmented into multiple fragments bool ProcessOutgoing( const int time, const idBitMsg & msg, bool isOOB, int userData ); // Used to get each fragment for sending through the actual net connection bool GetSendFragment( const int time, sessionId_t sessionID, idBitMsg & outMsg ); // Used to process a fragment received. Returns true when msg was reconstructed. int ProcessIncoming( int time, sessionId_t expectedSessionID, idBitMsg & msg, idBitMsg & out, int & userData, const int peerNum ); // Returns true if there are more fragments to send bool HasMoreFragments() const { return ( unsentMsg.GetRemainingData() > 0 ); } // Num reliables not ack'd int NumQueuedReliables() { return reliable.Num(); } // True if we need to send a reliable ack int NeedToSendReliableAck() { return queuedReliableAck >= 0 ? true : false; } // Used for out-of-band non connected peers // This doesn't actually support fragmentation, it is just simply here to hide the // header structure, so the caller doesn't have to skip over the header data. static bool ProcessConnectionlessOutgoing( idBitMsg & msg, idBitMsg & out, int lobbyType, int userData ); static bool ProcessConnectionlessIncoming( idBitMsg & msg, idBitMsg & out, int & userData ); // Used to "peek" at a session id of a message fragment static sessionId_t GetSessionID( idBitMsg & msg ); int GetNumReliables() const { return numReliable; } const byte * GetReliable( int i ) const { return reliableMsgPtrs[ i ]; } int GetReliableSize( int i ) const { return reliableMsgSize[ i ]; } void SetLastSendTime( int i ) { lastSendTime = i; } int GetLastSendTime() const { return lastSendTime; } float GetOutgoingRateBytes() const { return outgoingRateBytes; } int GetOutgoingBytes() const { return outgoingBytes; } float GetIncomingRateBytes() const { return incomingRateBytes; } int GetIncomingBytes() const { return incomingBytes; } // more reliable computation, based on a suitably small interval int GetOutgoingRate2() const { return currentOutgoingRate; } int GetIncomingRate2() const { return currentIncomingRate; } // decrease a fragmentation counter, so we reflect how much we're maxing the MTU bool TickFragmentAccumulator() { if ( fragmentAccumulator > 0 ) { fragmentAccumulator--; return true; } return false; } int GetReliableDataSize() const { return reliable.GetDataLength(); } void VerifyEmptyReliableQueue( byte keepMsgBelowThis, byte replaceWithThisMsg ); private: // Packet header types static const int PACKET_TYPE_INBAND = 0; // In-band. Number of reliable msg's stored in userData portion of header static const int PACKET_TYPE_OOB = 1; // Out-of-band. userData free to use by the caller. Cannot fragment. static const int PACKET_TYPE_RELIABLE_ACK = 2; // Header type used to piggy-back on top of msgs to ack reliable msg's static const int PACKET_TYPE_FRAGMENTED = 3; // The msg is fragmented, fragment type stored in the userData portion of header // PACKET_TYPE_FRAGMENTED userData values static const int FRAGMENT_START = 0; static const int FRAGMENT_MIDDLE = 1; static const int FRAGMENT_END = 2; class idOuterPacketHeader { public: idOuterPacketHeader() : sessionID( SESSION_ID_INVALID ) {} idOuterPacketHeader( sessionId_t sessionID_ ) : sessionID( sessionID_ ) {} void WriteToMsg( idBitMsg & msg ) { msg.WriteUShort( sessionID ); } void ReadFromMsg( idBitMsg & msg ) { sessionID = msg.ReadUShort(); } sessionId_t GetSessionID() { return sessionID; } private: sessionId_t sessionID; }; class idInnerPacketHeader { public: idInnerPacketHeader() : type( 0 ), userData( 0 ) {} idInnerPacketHeader( int inType, int inData ) : type( inType ), userData( inData ) {} void WriteToMsg( idBitMsg & msg ) { msg.WriteBits( type, 2 ); msg.WriteBits( userData, 6 ); } void ReadFromMsg( idBitMsg & msg ) { type = msg.ReadBits( 2 ); userData = msg.ReadBits( 6 ); } int Type() { return type; } int Value() { return userData; } private: int type; int userData; }; byte msgBuffer[ MAX_MSG_SIZE ]; // Buffer used to reconstruct the msg int msgWritePos; // Write position into the msg reconstruction buffer int fragmentSequence; // Fragment sequence number int droppedFrags; // Number of dropped fragments bool fragmentedSend; // Used to determine if the current send requires fragmenting idDataQueue< MAX_RELIABLE_QUEUE, MAX_MSG_SIZE > reliable; // list of unacknowledged reliable messages int reliableSequenceSend; // sequence number of the next reliable packet we're going to send to this peer int reliableSequenceRecv; // sequence number of the last reliable packet we received from this peer // These are for receiving reliables, you need to get these before the next process call or they will get cleared int numReliable; byte reliableBuffer[ MAX_MSG_SIZE ]; // We shouldn't have to hold more than this const byte * reliableMsgPtrs[ MAX_RELIABLE_QUEUE ]; int reliableMsgSize[ MAX_RELIABLE_QUEUE ]; int queuedReliableAck; // Used to piggy back on the next send to ack reliables idBitMsg unsentMsg; byte unsentBuffer[ MAX_MSG_SIZE ]; // Buffer used hold the current msg until it's all sent int lastSendTime; // variables to keep track of the rate int outgoingRateTime; float outgoingRateBytes; // B/S int incomingRateTime; float incomingRateBytes; // B/S int outgoingBytes; int incomingBytes; int currentOutgoingRate; int lastOutgoingRateTime; int lastOutgoingBytes; int currentIncomingRate; int lastIncomingRateTime; int lastIncomingBytes; int fragmentAccumulator; // counts max size packets we are sending for the net debug hud }; #endif /* !__PACKET_PROCESSOR_H__ */