feat(net): implement message sending in WowConnection

This commit is contained in:
fallenoak 2023-03-22 12:43:05 -05:00
parent 497520e672
commit 0d30ba07ca
No known key found for this signature in database
GPG Key ID: 7628F8E61AEA070D
2 changed files with 175 additions and 0 deletions

View File

@ -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<WowConnection::SENDNODE>() {
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<uint8_t*>(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<uint8_t*>(&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<uint8_t*>(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<void*&>(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<uint8_t*>(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<uint8_t*>(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;

View File

@ -15,6 +15,7 @@
#include <winsock2.h>
#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<WowConnection> 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);