diff --git a/src/net/connection/WowConnection.cpp b/src/net/connection/WowConnection.cpp index 4b785f9..b0020f5 100644 --- a/src/net/connection/WowConnection.cpp +++ b/src/net/connection/WowConnection.cpp @@ -27,10 +27,52 @@ uint64_t WowConnection::s_countTotalBytes; int32_t WowConnection::s_destroyed; +int32_t WowConnection::s_lagTestDelayMin; WowConnectionNet* WowConnection::s_network; ATOMIC32 WowConnection::s_numWowConnections; bool (*WowConnection::s_verifyAddr)(const NETADDR*); +WowConnection::SENDNODE::SENDNODE(void* data, int32_t size, uint8_t* buf, bool raw) : TSLinkedNode() { + if (data) { + this->data = buf; + } + + if (raw) { + memcpy(this->data, data, size); + this->size = size; + } else { + uint32_t headerSize = size > 0x7FFF ? 3 : 2; + + if (!data) { + this->data = &buf[-headerSize]; + } + + auto headerBuf = static_cast(this->data); + + // Write 2 or 3 byte data size value to header in big endian order + if (size > 0x7FFF) { + headerBuf[0] = ((size >> (8 * 2)) & 0xff) | 0x80; + headerBuf[1] = (size >> (8 * 1)) & 0xff; + headerBuf[2] = (size >> (8 * 0)) & 0xff; + } else { + headerBuf[0] = (size >> (8 * 1)) & 0xff; + headerBuf[1] = (size >> (8 * 0)) & 0xff; + } + + if (data) { + memcpy(static_cast(&this->data[headerSize]), data, size); + } + + this->size = size + headerSize; + } + + this->datasize = size; + this->offset = 0; + this->allocsize = 0; + + memcpy(this->header, this->data, std::min(this->size, 8u)); +} + int32_t WowConnection::CreateSocket() { int32_t sock = socket(AF_INET, SOCK_STREAM, 0); @@ -556,6 +598,11 @@ void WowConnection::DoWrites() { this->Release(); } +void WowConnection::FreeSendNode(SENDNODE* sn) { + // TODO WDataStore::FreeBuffer(sn, sn->datasize + sizeof(SENDNODE) + 3); + SMemFree(sn, __FILE__, __LINE__, 0x0); +} + WOW_CONN_STATE WowConnection::GetState() { return this->m_connState; } @@ -568,6 +615,12 @@ void WowConnection::Init(WowConnectionResponse* response, void (*func)(void)) { // TODO + this->m_sendDepth = 0; + this->m_sendDepthBytes = 0; + this->m_maxSendDepth = 100000; + + // TODO + this->m_connState = WOWC_UNINITIALIZED; // TODO @@ -591,6 +644,7 @@ void WowConnection::Init(WowConnectionResponse* response, void (*func)(void)) { this->m_readBufferSize = 0; this->m_event = nullptr; + this->m_encrypt = false; // TODO @@ -598,6 +652,25 @@ void WowConnection::Init(WowConnectionResponse* response, void (*func)(void)) { this->m_type = WOWC_TYPE_MESSAGES; } +WowConnection::SENDNODE* WowConnection::NewSendNode(void* data, int32_t size, bool raw) { + // TODO counters + + // SENDNODEs are prefixed to their buffers, with an extra 3 bytes reserved for size-prefixing + uint32_t allocsize = size + sizeof(SENDNODE) + 3; + + // TODO WDataStore::AllocBuffer(allocsize); + + auto m = SMemAlloc(allocsize, __FILE__, __LINE__, 0x0); + auto buf = &static_cast(m)[sizeof(SENDNODE)]; + auto sn = new (m) SENDNODE(data, size, buf, raw); + + sn->allocsize = allocsize; + + // TODO latency tracking + + return sn; +} + void WowConnection::Release() { if (SInterlockedDecrement(&this->m_refCount) <= 0) { if (WowConnection::s_network) { @@ -626,6 +699,94 @@ void WowConnection::ReleaseResponseRef() { this->m_responseLock.Leave(); } +WC_SEND_RESULT WowConnection::Send(CDataStore* msg, int32_t a3) { + uint8_t* data; + msg->GetDataInSitu(reinterpret_cast(data), msg->Size()); + + WowConnection::s_countTotalBytes += msg->Size(); + + this->m_lock.Enter(); + + if (msg->Size() == 0 || this->m_connState != WOWC_CONNECTED) { + this->m_lock.Leave(); + return WC_SEND_ERROR; + } + + // Queue send + + if (WowConnection::s_lagTestDelayMin || this->m_sendList.Head()) { + auto sn = this->NewSendNode(data, msg->Size(), false); + + if (this->m_encrypt) { + // TODO encryption + } + + this->m_sendList.LinkToTail(sn); + + this->m_sendDepth++; + this->m_sendDepthBytes += sn->size; + + WowConnection::s_network->PlatformChangeState(this, this->m_connState); + + if (this->m_sendDepth < this->m_maxSendDepth) { + this->m_lock.Leave(); + return WC_SEND_QUEUED; + } else { + // TODO handle max queue send depth reached + + this->m_lock.Leave(); + return WC_SEND_ERROR; + } + } + + // Send immediately + + SENDNODE* sn; + bool snOnStack; + + if (msg->Size() > 100000) { + snOnStack = false; + sn = this->NewSendNode(data, msg->Size(), false); + } else if (a3 < 3) { + snOnStack = true; + + auto m = alloca(msg->Size() + sizeof(SENDNODE) + 3); + auto buf = &static_cast(m)[sizeof(SENDNODE)]; + sn = new (m) SENDNODE(data, msg->Size(), buf, false); + } else { + snOnStack = true; + + auto m = alloca(msg->Size() + sizeof(SENDNODE)); + auto buf = &static_cast(m)[sizeof(SENDNODE)]; + sn = new (m) SENDNODE(nullptr, msg->Size(), buf, false); + } + + if (this->m_encrypt) { + // TODO encryption + } + + uint32_t written; +#if defined(WHOA_SYSTEM_WIN) + written = send(this->m_sock, sn->data, sn->size, 0x0); +#elif defined(WHOA_SYSTEM_MAC) + written = write(this->m_sock, sn->data, sn->size); +#endif + + if (written == sn->size) { + if (!snOnStack) { + this->FreeSendNode(sn); + } + + this->m_lock.Leave(); + return WC_SEND_SENT; + } + + // TODO split writes, errors, etc + STORM_ASSERT(false); + + return WC_SEND_ERROR; +} + WC_SEND_RESULT WowConnection::SendRaw(uint8_t* data, int32_t len, bool a4) { WowConnection::s_countTotalBytes += len; diff --git a/src/net/connection/WowConnection.hpp b/src/net/connection/WowConnection.hpp index 7b064ae..366b1f3 100644 --- a/src/net/connection/WowConnection.hpp +++ b/src/net/connection/WowConnection.hpp @@ -15,6 +15,7 @@ #include #endif +class CDataStore; class WowConnectionNet; class WowConnectionResponse; @@ -26,11 +27,17 @@ class WowConnection { uint32_t size; uint32_t offset; uint32_t datasize; + uint8_t header[8]; + uint32_t uint20; + uint32_t allocsize; + + SENDNODE(void* data, int32_t size, uint8_t* buf, bool raw); }; // Static variables static uint64_t s_countTotalBytes; static int32_t s_destroyed; + static int32_t s_lagTestDelayMin; static WowConnectionNet* s_network; static ATOMIC32 s_numWowConnections; static bool (*s_verifyAddr)(const NETADDR*); @@ -54,12 +61,16 @@ class WowConnection { int32_t m_responseRef; uintptr_t m_responseRefThread; STORM_LIST(SENDNODE) m_sendList; + int32_t m_sendDepth; + uint32_t m_sendDepthBytes; + int32_t m_maxSendDepth; uint32_t m_serviceFlags; TSLink m_netlink; SCritSect m_lock; ATOMIC32 m_serviceCount; void* m_event; WOWC_TYPE m_type; + bool m_encrypt; // Member functions WowConnection(WowConnectionResponse* response, void (*func)(void)); @@ -78,10 +89,13 @@ class WowConnection { void DoReads(); void DoStreamReads(); void DoWrites(); + void FreeSendNode(SENDNODE* sn); WOW_CONN_STATE GetState(); void Init(WowConnectionResponse* response, void (*func)(void)); + SENDNODE* NewSendNode(void* data, int32_t size, bool raw); void Release(); void ReleaseResponseRef(); + WC_SEND_RESULT Send(CDataStore* msg, int32_t a3); WC_SEND_RESULT SendRaw(uint8_t* data, int32_t len, bool a4); void SetEncryptionType(WC_ENCRYPT_TYPE encryptType); void SetState(WOW_CONN_STATE state);