diff --git a/common.c b/common.c index b5a17ce..ade79e7 100644 --- a/common.c +++ b/common.c @@ -3,6 +3,45 @@ #include "common.h" +__declspec(naked) +void * __stdcall Queue_AtPosition_QWORD(struct CQueue *queue, int position) +{ + _asm { + pop eax + pop ecx + push eax + mov eax, 0x45B690 + jmp eax + } +} + +__declspec(naked) +void __stdcall Queue_Push_QWORD(struct CQueue *queue, void *value) +{ + _asm { + pop eax + pop ecx + push eax + mov eax, 0x45B5B0 + jmp eax + } +} + +/** +@param value a POINTER to the value (so for a queue of pointers, a **value) +*/ +__declspec(naked) +void __stdcall Queue_Push_DWORD(struct CQueue *queue, void *value) +{ + _asm { + pop eax + pop ecx + push eax + mov eax, 0x450FA0 + jmp eax + } +} + __declspec(naked) void * __stdcall thiscall0(void *address, void *this) { @@ -100,6 +139,17 @@ int __stdcall BPlusTree__IsEmpty(void *this) } } +void RaknetTimeNS_Add(struct CRaknetTimeNS *this, unsigned int value) +{ + unsigned int oldLo; + + oldLo = this->lo32; + this->lo32 += value; + if (this->lo32 < oldLo) { + this->hi32++; + } +} + int RaknetTimeNS_IsBigger( struct CRaknetTimeNS isbigger, struct CRaknetTimeNS than) diff --git a/common.h b/common.h index 15f7a70..f20056c 100644 --- a/common.h +++ b/common.h @@ -6,6 +6,8 @@ #define STATIC_ASSERT(E) typedef char __static_assert_[(E)?1:-1] #define EXPECT_SIZE(S,SIZE) STATIC_ASSERT(sizeof(S)==(SIZE)) +#define DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE 0x200 + #pragma pack(push, 1) struct PlayerID { int binaryAddress; @@ -45,15 +47,19 @@ EXPECT_SIZE(struct CRaknetTimeNS, 0x8); struct CQueue { void *array; - void *head; - void *tail; + /*index of head element*/ + int head; + /*index of tail element*/ + int tail; int allocationSize; }; EXPECT_SIZE(struct CQueue, 0x10); struct CReliabilityLayer { /*000*/ char _pad0[0xC]; +/**packets ready for handling*/ /*00C*/ struct CQueue outputQueue; +/**this is acknowlegements to send on next update*/ /*01C*/ void *acknowlegements; // DataStructures::RangeList /*020*/ char _pad20[0x20]; /*040*/ void *resendList; @@ -93,7 +99,16 @@ struct CReliabilityLayer { /*3EC*/ char _pad3EC[0x28]; /*414*/ int someCounter; /*418*/ char _pad418[0xC]; +/** +Queue that stores received packets with a higher message number than the first +next packet that we are supposed to receive. 0 values means the packet with that +number (the index == messagenumber) has been received, non-0 values are the +timeNS when this packet hole should be expired (removed). +*/ /*424*/ struct CQueue hasReceivedPacketQueue; +/** +Next message number that is needed. +*/ /*434*/ short receivedPacketsBaseIndex; /*436*/ char resetReceivedPackets; /*437*/ char _pad437[0x11]; @@ -131,6 +146,17 @@ struct CInternalPacket { EXPECT_SIZE(struct CInternalPacket, 0x38); #pragma pack(pop) + +#define QUEUE_SIZE(X) \ + ((X.tail > X.head) ? \ + (X.tail - X.head) : (X.allocationSize - X.head + X.tail)) + +#define QUEUE_POP_IGNOREVALUE(X) \ + (X.head = (X.head+1 == X.allocationSize) ? 0 : (X.head+1)) + +void * __stdcall Queue_AtPosition_QWORD(struct CQueue *queue, int position); +void __stdcall Queue_Push_QWORD(struct CQueue *queue, void *value); +void __stdcall Queue_Push_DWORD(struct CQueue *queue, void *value); void * __stdcall thiscall0(void *address, void *this); void * __stdcall thiscall1(void *address, void *this, int); void * __stdcall thiscall2(void *address, void *this, int, int); @@ -139,6 +165,7 @@ void * __stdcall thiscall4(void *address, void *this, int, int, int, int); void __stdcall RangeList__ctor(struct CRangeList *this); void __stdcall RangeList__dtor(struct CRangeList *this); int __stdcall BPlusTree__IsEmpty(void *this); +void RaknetTimeNS_Add(struct CRaknetTimeNS *this, unsigned int value); int RaknetTimeNS_IsBigger( struct CRaknetTimeNS isbigger, struct CRaknetTimeNS than); diff --git a/rangelist_deserialize.c b/rangelist_deserialize.c index f1c86b0..ba9d45c 100644 --- a/rangelist_deserialize.c +++ b/rangelist_deserialize.c @@ -5,7 +5,6 @@ #include "rangelist_deserialize.h" #include "bitstream.h" -static __declspec(naked) int __stdcall RangeList__Deserialize( struct CRangeList *this, diff --git a/reliability.c b/reliability.c index 82033ee..e683d8e 100644 --- a/reliability.c +++ b/reliability.c @@ -1,7 +1,7 @@ /* vim: set filetype=c ts=8 noexpandtab: */ -#define CALL_ORIGINAL +//#define CALL_ORIGINAL #define RELIABILITY_PRINT @@ -51,13 +51,163 @@ void SendAckForPacket( } static -void HandlePacket( +void ResetReceivedPackets(struct CReliabilityLayer *this) +{ + if (!this->resetReceivedPackets) { + return; + } + + thiscall1((void*) 0x45B770, &this->hasReceivedPacketQueue, + DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE); +} + +/** +@return 0 if packet should be discarded +*/ +static +int HandlePacketHole( + struct CReliabilityLayer *this, + int holeSize, + struct CRaknetTimeNS time) +{ + struct CQueue *queueptr; + struct CRaknetTimeNS *timeAt, timeTmp; + +#define queue this->hasReceivedPacketQueue + queueptr = &this->hasReceivedPacketQueue; + + if (holeSize == 0) { + if (QUEUE_SIZE(queue)) { + QUEUE_POP_IGNOREVALUE(queue); + } + this->receivedPacketsBaseIndex++; + return 1; + } + + if (holeSize > 0x8000) { + /*? underflow apparently*/ + this->statistics_duplicateMessagesReceived++; + return 0; + } + + if (holeSize < QUEUE_SIZE(queue)) { + timeAt = Queue_AtPosition_QWORD(queueptr, holeSize); + + if (timeAt->lo32 == 0 && timeAt->hi32 == 0) { + /*was already received, so duplicate packet*/ + this->statistics_duplicateMessagesReceived++; + return 0; + } + + /*it was a hole that has just been filled*/ + timeAt->lo32 = timeAt->hi32 = 0; + return 1; + } + + /*add to queue, with holes in between*/ + while (holeSize > QUEUE_SIZE(queue)) { + timeTmp = time; + RaknetTimeNS_Add(&timeTmp, this->timeoutTimeMS * 1000); + Queue_Push_QWORD(queueptr, &time); + } + timeTmp.lo32 = timeTmp.hi32 = 0; + Queue_Push_QWORD(queueptr, &timeTmp); + return 1; +} + +static +void RemovePacketHolesNotReceivedInTime( + struct CReliabilityLayer *this, + struct CRaknetTimeNS time) +{ + struct CQueue *queueptr; + struct CRaknetTimeNS *expireTime; + +#define queue this->hasReceivedPacketQueue + queueptr = &this->hasReceivedPacketQueue; + + while (QUEUE_SIZE(queue)) { + expireTime = Queue_AtPosition_QWORD(queueptr, queue.head); + if (!RaknetTimeNS_IsBigger(time, *expireTime)) { + return; + } + QUEUE_POP_IGNOREVALUE(queue); + this->receivedPacketsBaseIndex++; + } +} + +static +void CompressMessageHoleQueue(struct CReliabilityLayer *this) +{ +#define queue this->hasReceivedPacketQueue + + if (queue.allocationSize <= DEFAULT_HAS_RECEIVED_PACKET_QUEUE_SIZE) { + return; + } + + if (QUEUE_SIZE(queue) * 3 <= queue.allocationSize) { + return; + } + + /*Queue::Compress*/ + thiscall0((void*) 0x45B6C0, &this->hasReceivedPacketQueue); +} + +static +int HandleSequencedPacket( struct CReliabilityLayer *this, struct CInternalPacket *packet) { - SendAckForPacket(this, packet); + if (packet->packetReliability != RELIABLE_SEQUENCED || + packet->packetReliability != UNRELIABLE_SEQUENCED) + { + return 0; + } + return 1; +} - // here lol @45FC87 +static +int HandleOrdenedPacket( + struct CReliabilityLayer *this, + struct CInternalPacket *packet) +{ + if (packet->packetReliability != RELIABLE_ORDENED) { + return 0; + } + return 1; +} + +static +void HandlePacket( + struct CReliabilityLayer *this, + struct CInternalPacket *packet, + struct CRaknetTimeNS time) +{ + short holes; + + SendAckForPacket(this, packet); + ResetReceivedPackets(this); + + holes = packet->messageNumber - this->receivedPacketsBaseIndex; + if (!HandlePacketHole(this, holes, time)) { + return; + } + + RemovePacketHolesNotReceivedInTime(this, time); + this->statistics_messagesReceived++; + CompressMessageHoleQueue(this); + + /*split packets are dropped, skipping 0x45FECE*/ + + if (HandleSequencedPacket(this, packet)) { + return; + } + + if (HandleOrdenedPacket(this, packet)) { + return; + } + + Queue_Push_DWORD(&this->outputQueue, &packet); } __declspec(naked) @@ -274,7 +424,7 @@ int __stdcall ReliabilityLayer__HandleSocketReceiveFromConnectedPlayer( this, &bitStream, timeNS)) { returnValue = 1; - HandlePacket(this, packet); + HandlePacket(this, packet, timeNS); } this->receivePacketCount++;