void EQStream::AdjustRates(uint32 average_delta) { if(GetExecutablePlatform() == ExePlatformWorld || GetExecutablePlatform() == ExePlatformZone) { if (average_delta && (average_delta <= AVERAGE_DELTA_MAX)) { MRate.lock(); RateThreshold=RATEBASE/average_delta; DecayRate=DECAYBASE/average_delta; Log.Out(Logs::Detail, Logs::Netcode, _L "Adjusting data rate to thresh %d, decay %d based on avg delta %d" __L, RateThreshold, DecayRate, average_delta); MRate.unlock(); } else { Log.Out(Logs::Detail, Logs::Netcode, _L "Not adjusting data rate because avg delta over max (%d > %d)" __L, average_delta, AVERAGE_DELTA_MAX); } } else { if (average_delta) { MRate.lock(); RateThreshold=RATEBASE/average_delta; DecayRate=DECAYBASE/average_delta; Log.Out(Logs::Detail, Logs::Netcode, _L "Adjusting data rate to thresh %d, decay %d based on avg delta %d" __L, RateThreshold, DecayRate, average_delta); MRate.unlock(); } } }
EQRawApplicationPacket::EQRawApplicationPacket(const unsigned char *buf, const uint32 len) : EQApplicationPacket(OP_Unknown, buf+sizeof(uint16), len-sizeof(uint16)) { if(GetExecutablePlatform() != ExePlatformUCS) { opcode = *((const uint16 *) buf); if(opcode == 0x0000) { if(len >= 3) { opcode = *((const uint16 *) (buf + 1)); const unsigned char *packet_start = (buf + 3); const int32 packet_length = len - 3; safe_delete_array(pBuffer); if(len >= 0) { size = packet_length; pBuffer = new unsigned char[size]; memcpy(pBuffer, packet_start, size); } else { size = 0; } } else { safe_delete_array(pBuffer); size = 0; } } } else { opcode = *((const uint8 *) buf); } }
void EQStream::init(bool resetSession) { // we only reset these statistics if it is a 'new' connection if ( resetSession ) { streamactive = false; sessionAttempts = 0; } active_users = 0; Session=0; Key=0; MaxLen=0; NextInSeq=0; NextOutSeq=0; NextAckToSend=-1; LastAckSent=-1; MaxSends=5; LastPacket=0; oversize_buffer=nullptr; oversize_length=0; oversize_offset=0; RateThreshold=RATEBASE/250; DecayRate=DECAYBASE/250; BytesWritten=0; SequencedBase = 0; NextSequencedSend = 0; if(GetExecutablePlatform() == ExePlatformWorld || GetExecutablePlatform() == ExePlatformZone) { retransmittimer = Timer::GetCurrentTime(); retransmittimeout = 500 * RETRANSMIT_TIMEOUT_MULT; } OpMgr = nullptr; if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { Log.Out(Logs::Detail, Logs::Netcode, _L "init Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } if(NextSequencedSend > SequencedQueue.size()) { Log.Out(Logs::Detail, Logs::Netcode, _L "init Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); } }
bool EQEMuLog::open(LogIDs id) { if (!logFileValid) { return false; } if (id >= MaxLogID) { return false; } LockMutex lock(&MOpen); if (pLogStatus[id] & 4) { return false; } if (fp[id]) { //cerr<<"Warning: LogFile already open"<<endl; return true; } char exename[200] = ""; const EQEmuExePlatform &platform = GetExecutablePlatform(); if(platform == ExePlatformWorld) { snprintf(exename, sizeof(exename), "_world"); } else if(platform == ExePlatformZone) { snprintf(exename, sizeof(exename), "_zone"); } else if(platform == ExePlatformLaunch) { snprintf(exename, sizeof(exename), "_launch"); } else if(platform == ExePlatformUCS) { snprintf(exename, sizeof(exename), "_ucs"); } else if(platform == ExePlatformQueryServ) { snprintf(exename, sizeof(exename), "_queryserv"); } else if(platform == ExePlatformSharedMemory) { snprintf(exename, sizeof(exename), "_shared_memory"); } else if(platform == ExePlatformClientImport) { snprintf(exename, sizeof(exename), "_import"); } else if(platform == ExePlatformClientExport) { snprintf(exename, sizeof(exename), "_export"); } char filename[200]; #ifndef NO_PIDLOG snprintf(filename, sizeof(filename), "%s%s_%04i.log", FileNames[id], exename, getpid()); #else snprintf(filename, sizeof(filename), "%s%s.log", FileNames[id], exename); #endif fp[id] = fopen(filename, "a"); if (!fp[id]) { std::cerr << "Failed to open log file: " << filename << std::endl; pLogStatus[id] |= 4; // set file state to error return false; } fputs("---------------------------------------------\n",fp[id]); write(id, "Starting Log: %s", filename); return true; }
void EQStream::Write(int eq_fd) { std::queue<EQProtocolPacket *> ReadyToSend; bool SeqEmpty=false, NonSeqEmpty=false; std::deque<EQProtocolPacket *>::iterator sitr; // Check our rate to make sure we can send more MRate.lock(); int32 threshold=RateThreshold; MRate.unlock(); if (BytesWritten > threshold) { return; } // If we got more packets to we need to ack, send an ack on the highest one MAcks.lock(); if (CompareSequence(LastAckSent, NextAckToSend) == SeqFuture) SendAck(NextAckToSend); MAcks.unlock(); // Lock the outbound queues while we process MOutboundQueue.lock(); // Place to hold the base packet t combine into EQProtocolPacket *p=nullptr; if(GetExecutablePlatform() == ExePlatformWorld || GetExecutablePlatform() == ExePlatformZone) { // if we have a timeout defined and we have not received an ack recently enough, retransmit from beginning of queue if (RETRANSMIT_TIMEOUT_MULT && !SequencedQueue.empty() && NextSequencedSend && (GetState()==ESTABLISHED) && ((retransmittimer+retransmittimeout) < Timer::GetCurrentTime())) { Log.Out(Logs::Detail, Logs::Netcode, _L "Timeout since last ack received, starting retransmit at the start of our unacked " "buffer (seq %d, was %d)." __L, SequencedBase, SequencedBase+NextSequencedSend); NextSequencedSend = 0; retransmittimer = Timer::GetCurrentTime(); // don't want to endlessly retransmit the first packet } } // Find the next sequenced packet to send from the "queue" sitr = SequencedQueue.begin(); if (sitr!=SequencedQueue.end()) sitr += NextSequencedSend; // Loop until both are empty or MaxSends is reached while(!SeqEmpty || !NonSeqEmpty) { // See if there are more non-sequenced packets left if (!NonSequencedQueue.empty()) { if (!p) { // If we don't have a packet to try to combine into, use this one as the base // And remove it form the queue p = NonSequencedQueue.front(); Log.Out(Logs::Detail, Logs::Netcode, _L "Starting combined packet with non-seq packet of len %d" __L, p->size); NonSequencedQueue.pop(); } else if (!p->combine(NonSequencedQueue.front())) { // Tryint to combine this packet with the base didn't work (too big maybe) // So just send the base packet (we'll try this packet again later) Log.Out(Logs::Detail, Logs::Netcode, _L "Combined packet full at len %d, next non-seq packet is len %d" __L, p->size, (NonSequencedQueue.front())->size); ReadyToSend.push(p); BytesWritten+=p->size; p=nullptr; if (BytesWritten > threshold) { // Sent enough this round, lets stop to be fair Log.Out(Logs::Detail, Logs::Netcode, _L "Exceeded write threshold in nonseq (%d > %d)" __L, BytesWritten, threshold); break; } } else { // Combine worked, so just remove this packet and it's spot in the queue Log.Out(Logs::Detail, Logs::Netcode, _L "Combined non-seq packet of len %d, yeilding %d combined." __L, (NonSequencedQueue.front())->size, p->size); delete NonSequencedQueue.front(); NonSequencedQueue.pop(); } } else { // No more non-sequenced packets NonSeqEmpty=true; } if (sitr!=SequencedQueue.end()) { if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { Log.Out(Logs::Detail, Logs::Netcode, _L "Pre-Send Seq NSS=%d Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, NextSequencedSend, SequencedBase, SequencedQueue.size(), NextOutSeq); } if(NextSequencedSend > SequencedQueue.size()) { Log.Out(Logs::Detail, Logs::Netcode, _L "Pre-Send Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); } uint16 seq_send = SequencedBase + NextSequencedSend; //just for logging... if(SequencedQueue.empty()) { Log.Out(Logs::Detail, Logs::Netcode, _L "Tried to write a packet with an empty queue (%d is past next out %d)" __L, seq_send, NextOutSeq); SeqEmpty=true; continue; } if(GetExecutablePlatform() == ExePlatformWorld || GetExecutablePlatform() == ExePlatformZone) { if (!RETRANSMIT_ACKED_PACKETS && (*sitr)->acked) { Log.Out(Logs::Detail, Logs::Netcode, _L "Not retransmitting seq packet %d because already marked as acked" __L, seq_send); sitr++; NextSequencedSend++; } else if (!p) { // If we don't have a packet to try to combine into, use this one as the base // Copy it first as it will still live until it is acked p=(*sitr)->Copy(); Log.Out(Logs::Detail, Logs::Netcode, _L "Starting combined packet with seq packet %d of len %d" __L, seq_send, p->size); ++sitr; NextSequencedSend++; } else if (!p->combine(*sitr)) { // Trying to combine this packet with the base didn't work (too big maybe) // So just send the base packet (we'll try this packet again later) Log.Out(Logs::Detail, Logs::Netcode, _L "Combined packet full at len %d, next seq packet %d is len %d" __L, p->size, seq_send, (*sitr)->size); ReadyToSend.push(p); BytesWritten+=p->size; p=nullptr; if (BytesWritten > threshold) { // Sent enough this round, lets stop to be fair Log.Out(Logs::Detail, Logs::Netcode, _L "Exceeded write threshold in seq (%d > %d)" __L, BytesWritten, threshold); break; } } else { // Combine worked Log.Out(Logs::Detail, Logs::Netcode, _L "Combined seq packet %d of len %d, yeilding %d combined." __L, seq_send, (*sitr)->size, p->size); ++sitr; NextSequencedSend++; } } else { if (!p) { // If we don't have a packet to try to combine into, use this one as the base // Copy it first as it will still live until it is acked p=(*sitr)->Copy(); Log.Out(Logs::Detail, Logs::Netcode, _L "Starting combined packet with seq packet %d of len %d" __L, seq_send, p->size); ++sitr; NextSequencedSend++; } else if (!p->combine(*sitr)) { // Trying to combine this packet with the base didn't work (too big maybe) // So just send the base packet (we'll try this packet again later) Log.Out(Logs::Detail, Logs::Netcode, _L "Combined packet full at len %d, next seq packet %d is len %d" __L, p->size, seq_send, (*sitr)->size); ReadyToSend.push(p); BytesWritten+=p->size; p=nullptr; if (BytesWritten > threshold) { // Sent enough this round, lets stop to be fair Log.Out(Logs::Detail, Logs::Netcode, _L "Exceeded write threshold in seq (%d > %d)" __L, BytesWritten, threshold); break; } } else { // Combine worked Log.Out(Logs::Detail, Logs::Netcode, _L "Combined seq packet %d of len %d, yeilding %d combined." __L, seq_send, (*sitr)->size, p->size); ++sitr; NextSequencedSend++; } } if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { Log.Out(Logs::Detail, Logs::Netcode, _L "Post send Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } if(NextSequencedSend > SequencedQueue.size()) { Log.Out(Logs::Detail, Logs::Netcode, _L "Post send Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); } } else { // No more sequenced packets SeqEmpty=true; } } // Unlock the queue MOutboundQueue.unlock(); // We have a packet still, must have run out of both seq and non-seq, so send it if (p) { Log.Out(Logs::Detail, Logs::Netcode, _L "Final combined packet not full, len %d" __L, p->size); ReadyToSend.push(p); BytesWritten+=p->size; } // Send all the packets we "made" while(!ReadyToSend.empty()) { p = ReadyToSend.front(); WritePacket(eq_fd,p); delete p; ReadyToSend.pop(); } //see if we need to send our disconnect and finish our close if(SeqEmpty && NonSeqEmpty) { //no more data to send if(CheckState(CLOSING)) { Log.Out(Logs::Detail, Logs::Netcode, _L "All outgoing data flushed, closing stream." __L ); //we are waiting for the queues to empty, now we can do our disconnect. //this packet will not actually go out until the next call to Write(). _SendDisconnect(); SetState(DISCONNECTING); } } }
void EQStream::ProcessPacket(EQProtocolPacket *p) { uint32 processed=0, subpacket_length=0; if (p == nullptr) return; // Raw Application packet if (p->opcode > 0xff) { p->opcode = htons(p->opcode); //byte order is backwards in the protocol packet EQRawApplicationPacket *ap=MakeApplicationPacket(p); if (ap) InboundQueuePush(ap); return; } if (!Session && p->opcode!=OP_SessionRequest && p->opcode!=OP_SessionResponse) { Log.Out(Logs::Detail, Logs::Netcode, _L "Session not initialized, packet ignored" __L); // _raw(NET__DEBUG, 0xFFFF, p); return; } switch (p->opcode) { case OP_Combined: { processed=0; while(processed < p->size) { subpacket_length=*(p->pBuffer+processed); EQProtocolPacket *subp=MakeProtocolPacket(p->pBuffer+processed+1,subpacket_length); Log.Out(Logs::Detail, Logs::Netcode, _L "Extracting combined packet of length %d" __L, subpacket_length); // _raw(NET__NET_CREATE_HEX, 0xFFFF, subp); subp->copyInfo(p); ProcessPacket(subp); delete subp; processed+=subpacket_length+1; } } break; case OP_AppCombined: { processed=0; while(processed<p->size) { EQRawApplicationPacket *ap=nullptr; if ((subpacket_length=(unsigned char)*(p->pBuffer+processed))!=0xff) { Log.Out(Logs::Detail, Logs::Netcode, _L "Extracting combined app packet of length %d, short len" __L, subpacket_length); ap=MakeApplicationPacket(p->pBuffer+processed+1,subpacket_length); processed+=subpacket_length+1; } else { subpacket_length=ntohs(*(uint16 *)(p->pBuffer+processed+1)); Log.Out(Logs::Detail, Logs::Netcode, _L "Extracting combined app packet of length %d, short len" __L, subpacket_length); ap=MakeApplicationPacket(p->pBuffer+processed+3,subpacket_length); processed+=subpacket_length+3; } if (ap) { ap->copyInfo(p); InboundQueuePush(ap); } } } break; case OP_Packet: { if(!p->pBuffer || (p->Size() < 4)) { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_Packet that was of malformed size" __L); break; } uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); SeqOrder check=CompareSequence(NextInSeq,seq); if (check == SeqFuture) { Log.Out(Logs::Detail, Logs::Netcode, _L "Future OP_Packet: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); // _raw(NET__DEBUG, seq, p); PacketQueue[seq]=p->Copy(); Log.Out(Logs::Detail, Logs::Netcode, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); //SendOutOfOrderAck(seq); } else if (check == SeqPast) { Log.Out(Logs::Detail, Logs::Netcode, _L "Duplicate OP_Packet: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); // _raw(NET__DEBUG, seq, p); SendOutOfOrderAck(seq); //we already got this packet but it was out of order } else { // In case we did queue one before as well. EQProtocolPacket *qp=RemoveQueue(seq); if (qp) { Log.Out(Logs::General, Logs::Netcode, "[NET_TRACE] OP_Packet: Removing older queued packet with sequence %d", seq); delete qp; } SetNextAckToSend(seq); NextInSeq++; // Check for an embedded OP_AppCombinded (protocol level 0x19) if (*(p->pBuffer+2)==0x00 && *(p->pBuffer+3)==0x19) { EQProtocolPacket *subp=MakeProtocolPacket(p->pBuffer+2,p->size-2); Log.Out(Logs::Detail, Logs::Netcode, _L "seq %d, Extracting combined packet of length %d" __L, seq, subp->size); // _raw(NET__NET_CREATE_HEX, seq, subp); subp->copyInfo(p); ProcessPacket(subp); delete subp; } else { EQRawApplicationPacket *ap=MakeApplicationPacket(p->pBuffer+2,p->size-2); if (ap) { ap->copyInfo(p); InboundQueuePush(ap); } } } } break; case OP_Fragment: { if(!p->pBuffer || (p->Size() < 4)) { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_Fragment that was of malformed size" __L); break; } uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); SeqOrder check=CompareSequence(NextInSeq,seq); if (check == SeqFuture) { Log.Out(Logs::Detail, Logs::Netcode, _L "Future OP_Fragment: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); // _raw(NET__DEBUG, seq, p); PacketQueue[seq]=p->Copy(); Log.Out(Logs::Detail, Logs::Netcode, _L "OP_Fragment Queue size=%d" __L, PacketQueue.size()); //SendOutOfOrderAck(seq); } else if (check == SeqPast) { Log.Out(Logs::Detail, Logs::Netcode, _L "Duplicate OP_Fragment: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); // _raw(NET__DEBUG, seq, p); SendOutOfOrderAck(seq); } else { // In case we did queue one before as well. EQProtocolPacket *qp=RemoveQueue(seq); if (qp) { Log.Out(Logs::General, Logs::Netcode, "[NET_TRACE] OP_Fragment: Removing older queued packet with sequence %d", seq); delete qp; } SetNextAckToSend(seq); NextInSeq++; if (oversize_buffer) { memcpy(oversize_buffer+oversize_offset,p->pBuffer+2,p->size-2); oversize_offset+=p->size-2; Log.Out(Logs::Detail, Logs::Netcode, _L "Fragment of oversized of length %d, seq %d: now at %d/%d" __L, p->size-2, seq, oversize_offset, oversize_length); if (oversize_offset==oversize_length) { if (*(p->pBuffer+2)==0x00 && *(p->pBuffer+3)==0x19) { EQProtocolPacket *subp=MakeProtocolPacket(oversize_buffer,oversize_offset); Log.Out(Logs::Detail, Logs::Netcode, _L "seq %d, Extracting combined oversize packet of length %d" __L, seq, subp->size); //// _raw(NET__NET_CREATE_HEX, subp); subp->copyInfo(p); ProcessPacket(subp); delete subp; } else { EQRawApplicationPacket *ap=MakeApplicationPacket(oversize_buffer,oversize_offset); Log.Out(Logs::Detail, Logs::Netcode, _L "seq %d, completed combined oversize packet of length %d" __L, seq, ap->size); if (ap) { ap->copyInfo(p); InboundQueuePush(ap); } } delete[] oversize_buffer; oversize_buffer=nullptr; oversize_offset=0; } } else { oversize_length=ntohl(*(uint32 *)(p->pBuffer+2)); oversize_buffer=new unsigned char[oversize_length]; memcpy(oversize_buffer,p->pBuffer+6,p->size-6); oversize_offset=p->size-6; Log.Out(Logs::Detail, Logs::Netcode, _L "First fragment of oversized of seq %d: now at %d/%d" __L, seq, oversize_offset, oversize_length); } } } break; case OP_KeepAlive: { #ifndef COLLECTOR NonSequencedPush(new EQProtocolPacket(p->opcode,p->pBuffer,p->size)); Log.Out(Logs::Detail, Logs::Netcode, _L "Received and queued reply to keep alive" __L); #endif } break; case OP_Ack: { if(!p->pBuffer || (p->Size() < 4)) { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_Ack that was of malformed size" __L); break; } #ifndef COLLECTOR uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); AckPackets(seq); if(GetExecutablePlatform() == ExePlatformWorld || GetExecutablePlatform() == ExePlatformZone) { retransmittimer = Timer::GetCurrentTime(); } #endif } break; case OP_SessionRequest: { if(p->Size() < sizeof(SessionRequest)) { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_SessionRequest that was of malformed size" __L); break; } #ifndef COLLECTOR if (GetState()==ESTABLISHED) { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_SessionRequest in ESTABLISHED state (%d) streamactive (%i) attempt (%i)" __L, GetState(),streamactive,sessionAttempts); // client seems to try a max of 30 times (initial+3 retries) then gives up, giving it a few more attempts just in case // streamactive means we identified the opcode for the stream, we cannot re-establish this connection if ( streamactive || ( sessionAttempts > MAX_SESSION_RETRIES ) ) { _SendDisconnect(); SetState(CLOSED); break; } } #endif sessionAttempts++; // we set established below, so statistics will not be reset for session attempts/stream active. init(GetState()!=ESTABLISHED); OutboundQueueClear(); SessionRequest *Request=(SessionRequest *)p->pBuffer; Session=ntohl(Request->Session); SetMaxLen(ntohl(Request->MaxLength)); Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_SessionRequest: session %lu, maxlen %d" __L, (unsigned long)Session, MaxLen); SetState(ESTABLISHED); #ifndef COLLECTOR Key=0x11223344; SendSessionResponse(); #endif } break; case OP_SessionResponse: { if(p->Size() < sizeof(SessionResponse)) { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_SessionResponse that was of malformed size" __L); break; } init(); OutboundQueueClear(); SessionResponse *Response=(SessionResponse *)p->pBuffer; SetMaxLen(ntohl(Response->MaxLength)); Key=ntohl(Response->Key); NextInSeq=0; SetState(ESTABLISHED); if (!Session) Session=ntohl(Response->Session); compressed=(Response->Format&FLAG_COMPRESSED); encoded=(Response->Format&FLAG_ENCODED); Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_SessionResponse: session %lu, maxlen %d, key %lu, compressed? %s, encoded? %s" __L, (unsigned long)Session, MaxLen, (unsigned long)Key, compressed?"yes":"no", encoded?"yes":"no"); // Kinda kludgy, but trie for now if (StreamType==UnknownStream) { if (compressed) { if (remote_port==9000 || (remote_port==0 && p->src_port==9000)) { SetStreamType(WorldStream); } else { SetStreamType(ZoneStream); } } else if (encoded) { SetStreamType(ChatOrMailStream); } else { SetStreamType(LoginStream); } } } break; case OP_SessionDisconnect: { //NextInSeq=0; EQStreamState state = GetState(); if(state == ESTABLISHED) { //client initiated disconnect? Log.Out(Logs::Detail, Logs::Netcode, _L "Received unsolicited OP_SessionDisconnect. Treating like a client-initiated disconnect." __L); _SendDisconnect(); SetState(CLOSED); } else if(state == CLOSING) { //we were waiting for this anyways, ignore pending messages, send the reply and be closed. Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_SessionDisconnect when we have a pending close, they beat us to it. Were happy though." __L); _SendDisconnect(); SetState(CLOSED); } else { //we are expecting this (or have already gotten it, but dont care either way) Log.Out(Logs::Detail, Logs::Netcode, _L "Received expected OP_SessionDisconnect. Moving to closed state." __L); SetState(CLOSED); } } break; case OP_OutOfOrderAck: { if(!p->pBuffer || (p->Size() < 4)) { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_OutOfOrderAck that was of malformed size" __L); break; } #ifndef COLLECTOR uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); MOutboundQueue.lock(); if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { Log.Out(Logs::Detail, Logs::Netcode, _L "Pre-OOA Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } if(NextSequencedSend > SequencedQueue.size()) { Log.Out(Logs::Detail, Logs::Netcode, _L "Pre-OOA Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); } //if the packet they got out of order is between our last acked packet and the last sent packet, then its valid. if (CompareSequence(SequencedBase,seq) != SeqPast && CompareSequence(NextOutSeq,seq) == SeqPast) { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_OutOfOrderAck for sequence %d, starting retransmit at the start of our unacked buffer (seq %d, was %d)." __L, seq, SequencedBase, SequencedBase+NextSequencedSend); bool retransmit_acked_packets = false; if(GetExecutablePlatform() == ExePlatformWorld || GetExecutablePlatform() == ExePlatformZone) { retransmit_acked_packets = RETRANSMIT_ACKED_PACKETS; } if(!retransmit_acked_packets) { uint16 sqsize = SequencedQueue.size(); uint16 index = seq - SequencedBase; Log.Out(Logs::Detail, Logs::Netcode, _L "OP_OutOfOrderAck marking packet acked in queue (queue index = %d, queue size = %d)." __L, index, sqsize); if (index < sqsize) { std::deque<EQProtocolPacket *>::iterator sitr; sitr = SequencedQueue.begin(); sitr += index; (*sitr)->acked = true; } } if(RETRANSMIT_TIMEOUT_MULT) { retransmittimer = Timer::GetCurrentTime(); } NextSequencedSend = 0; } else { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_OutOfOrderAck for out-of-window %d. Window (%d->%d)." __L, seq, SequencedBase, NextOutSeq); } if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { Log.Out(Logs::Detail, Logs::Netcode, _L "Post-OOA Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } if(NextSequencedSend > SequencedQueue.size()) { Log.Out(Logs::Detail, Logs::Netcode, _L "Post-OOA Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); } MOutboundQueue.unlock(); #endif } break; case OP_SessionStatRequest: { if(p->Size() < sizeof(SessionStats)) { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_SessionStatRequest that was of malformed size" __L); break; } #ifndef COLLECTOR SessionStats *Stats=(SessionStats *)p->pBuffer; Log.Out(Logs::Detail, Logs::Netcode, _L "Received Stats: %lu packets received, %lu packets sent, Deltas: local %lu, (%lu <- %lu -> %lu) remote %lu" __L, (unsigned long)ntohl(Stats->packets_received), (unsigned long)ntohl(Stats->packets_sent), (unsigned long)ntohl(Stats->last_local_delta), (unsigned long)ntohl(Stats->low_delta), (unsigned long)ntohl(Stats->average_delta), (unsigned long)ntohl(Stats->high_delta), (unsigned long)ntohl(Stats->last_remote_delta)); uint64 x=Stats->packets_received; Stats->packets_received=Stats->packets_sent; Stats->packets_sent=x; NonSequencedPush(new EQProtocolPacket(OP_SessionStatResponse,p->pBuffer,p->size)); AdjustRates(ntohl(Stats->average_delta)); if(GetExecutablePlatform() == ExePlatformWorld || GetExecutablePlatform() == ExePlatformZone) { if(RETRANSMIT_TIMEOUT_MULT && ntohl(Stats->average_delta)) { //recalculate retransmittimeout using the larger of the last rtt or average rtt, which is multiplied by the rule value if((ntohl(Stats->last_local_delta) + ntohl(Stats->last_remote_delta)) > (ntohl(Stats->average_delta) * 2)) { retransmittimeout = (ntohl(Stats->last_local_delta) + ntohl(Stats->last_remote_delta)) * RETRANSMIT_TIMEOUT_MULT; } else { retransmittimeout = ntohl(Stats->average_delta) * 2 * RETRANSMIT_TIMEOUT_MULT; } if(retransmittimeout > RETRANSMIT_TIMEOUT_MAX) retransmittimeout = RETRANSMIT_TIMEOUT_MAX; Log.Out(Logs::Detail, Logs::Netcode, _L "Retransmit timeout recalculated to %dms" __L, retransmittimeout); } } #endif } break; case OP_SessionStatResponse: { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_SessionStatResponse. Ignoring." __L); } break; case OP_OutOfSession: { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_OutOfSession. Ignoring." __L); } break; default: EQRawApplicationPacket *ap = MakeApplicationPacket(p); if (ap) InboundQueuePush(ap); break; } }