#include "net/connection/NetClient.hpp" #include "net/connection/WowConnection.hpp" #include "glue/CGlueMgr.hpp" #include #include #include #include #include #include #include #include HPROPCONTEXT s_propContext; int32_t NetClient::s_clientCount; void InitializePropContext() { if (PropGetSelectedContext() != s_propContext) { PropSelectContext(s_propContext); } } NETEVENTQUEUE::~NETEVENTQUEUE() { this->Clear(); } void NETEVENTQUEUE::AddEvent(EVENTID eventId, void* conn, NetClient* client, const void* data, uint32_t bytes) { this->m_critSect.Enter(); auto node = this->m_eventQueue.NewNode(2, 0, 0x0); node->m_eventId = eventId; if (bytes) { node->m_data = SMemAlloc(bytes, __FILE__, __LINE__, 0x0); memcpy(node->m_data, data, bytes); node->m_dataSize = bytes; } else { node->m_data = nullptr; node->m_dataSize = 0; } node->m_timeReceived = OsGetAsyncTimeMsPrecise(); client->AddRef(); this->m_critSect.Leave(); } void NETEVENTQUEUE::Clear() { this->m_critSect.Enter(); this->m_eventQueue.Clear(); this->m_critSect.Leave(); } void NETEVENTQUEUE::Poll() { this->m_critSect.Enter(); auto deleted = false; auto client = this->m_client; client->AddRef(); for (auto node = this->m_eventQueue.Head(); node; node = this->m_eventQueue.Next(node)) { if (!client->GetDelete()) { switch (node->m_eventId) { case EVENT_ID_NET_DATA: client->HandleData(node->m_timeReceived, node->m_data, node->m_dataSize); break; case EVENT_ID_NET_CONNECT: client->HandleConnect(); break; case EVENT_ID_NET_DISCONNECT: client->HandleDisconnect(); break; case EVENT_ID_NET_CANTCONNECT: client->HandleCantConnect(); break; case EVENT_ID_NET_DESTROY: client->SetDelete(); deleted = true; break; case EVENT_ID_NET_AUTH_CHALLENGE: client->HandleAuthChallenge(static_cast(node->m_data)); break; default: break; } } // Matching 1:1 with the ref added by NETEVENTQUEUE::AddEvent client->DelRef(); } if (!deleted) { client->HandleIdle(); } client->DelRef(); this->m_eventQueue.Clear(); this->m_critSect.Leave(); } void NetClient::AddRef() { SInterlockedIncrement(&this->m_refCount); } void NetClient::AuthChallengeHandler(WowConnection* conn, CDataStore* msg) { auto challenge = static_cast(SMemAlloc(sizeof(AuthenticationChallenge), __FILE__, __LINE__, 0x0)); uint32_t v14; msg->Get(v14); msg->Get(challenge->uint0); // TODO calculate client seed? if (conn == this->m_serverConnection) { this->m_netEventQueue->AddEvent(EVENT_ID_NET_AUTH_CHALLENGE, conn, this, challenge, sizeof(AuthenticationChallenge)); } else if (conn == this->m_redirectConnection) { // TODO } else { conn->Disconnect(); } delete challenge; } void NetClient::Connect(const char* addrStr) { if (this->m_netState != NS_INITIALIZED) { SErrDisplayAppFatal("Expected (m_netState == NS_INITIALIZED), got %d", this->m_netState); } uint16_t port = 9090; char host[1024]; SStrCopy(host, addrStr, sizeof(host)); auto portDelim = SStrChr(host, ':'); if (portDelim) { *portDelim = '\0'; port = atoi(portDelim + 1); } this->m_serverConnection->SetEncryption(false); this->m_netState = NS_INITIALIZED; this->ConnectInternal(host, port); } int32_t NetClient::ConnectInternal(const char* host, uint16_t port) { if (this->m_netState != NS_INITIALIZED) { SErrDisplayAppFatal("Expected (m_netState == NS_INITIALIZED), got %d", this->m_netState); } this->m_netState = NS_CONNECTING; this->m_serverConnection->Connect(host, port, -1); // TODO return 1; } void NetClient::DelRef() { auto refCount = SInterlockedDecrement(&this->m_refCount); if (refCount == 0 && this->m_deleteMe) { delete this; } } void NetClient::Disconnect() { auto redirectConnection = this->m_redirectConnection; if (redirectConnection) { // TODO // redirectConnection->SetResponse(0, 0); redirectConnection->Disconnect(); redirectConnection->Release(); this->m_redirectConnection = nullptr; } auto serverConnection = this->m_serverConnection; if (this->m_netState == NS_CONNECTED) { this->m_netState = NS_DISCONNECTING; serverConnection->Disconnect(); } else { // TODO // serverConnection->SetResponse(0, 0); serverConnection->Disconnect(); this->m_netEventQueue->Clear(); serverConnection->Release(); this->m_serverConnection = STORM_NEW(WowConnection)(this, nullptr); this->m_netState = NS_INITIALIZED; } } void NetClient::EnableEncryption(WowConnection* conn, uint8_t* seed, uint8_t seedLen) { conn->SetEncryptionKey( this->m_loginData.m_sessionKey, sizeof(this->m_loginData.m_sessionKey), 1, seed, seedLen ); conn->uint375 = 4; conn->uint376 = 2; conn->SetEncryption(true); } bool NetClient::GetDelete() { return this->m_deleteMe; } const LoginData& NetClient::GetLoginData() { return this->m_loginData; } NETSTATE NetClient::GetState() { return this->m_netState; } int32_t NetClient::HandleCantConnect() { // TODO return 1; } int32_t NetClient::HandleConnect() { // TODO push obj mgr this->m_netState = NS_CONNECTED; // TODO pop obj mgr return 1; } int32_t NetClient::HandleData(uint32_t timeReceived, void* data, int32_t size) { // TODO push obj mgr CDataStore msg = CDataStore(static_cast(data), size); this->ProcessMessage(timeReceived, &msg, 0); // TODO pop obj mgr return 1; } int32_t NetClient::HandleDisconnect() { // TODO push obj mgr this->m_netState = NS_INITIALIZED; // ConsolePrintf("NetClient::HandleDisconnect()"); CGlueMgr::NetDisconnectHandler(this, nullptr); // TODO pop obj mgr return 1; } void NetClient::HandleIdle() { // TODO; } int32_t NetClient::Initialize() { STORM_ASSERT(this->m_netState == NS_UNINITIALIZED); if (NetClient::s_clientCount == 0) { s_propContext = PropGetSelectedContext(); if (!WowConnection::InitOsNet(nullptr, InitializePropContext, 1, false)) { return 0; } } NetClient::s_clientCount++; auto queueMem = SMemAlloc(sizeof(NETEVENTQUEUE), __FILE__, __LINE__, 0x0); auto queue = new (queueMem) NETEVENTQUEUE(this); this->m_netEventQueue = queue; memset(this->m_handlers, 0, sizeof(this->m_handlers)); memset(this->m_handlerParams, 0, sizeof(this->m_handlerParams)); auto connectionMem = SMemAlloc(sizeof(WowConnection), __FILE__, __LINE__, 0x0); auto connection = new (connectionMem) WowConnection(this, nullptr); this->m_serverConnection = connection; this->m_netState = NS_INITIALIZED; return 1; } void NetClient::PollEventQueue() { this->m_netEventQueue->Poll(); } void NetClient::PongHandler(WowConnection* conn, CDataStore* msg) { // TODO } void NetClient::ProcessMessage(uint32_t timeReceived, CDataStore* msg, int32_t a4) { // TODO s_stats.messagesReceived++ uint16_t msgId; msg->Get(msgId); // TODO virtual function call on NetClient if (msgId >= NUM_MSG_TYPES || !this->m_handlers[msgId]) { msg->Reset(); return; } this->m_handlers[msgId]( this->m_handlerParams[msgId], static_cast(msgId), timeReceived, msg ); } void NetClient::Send(CDataStore* msg) { if (this->m_netState != NS_CONNECTED) { return; } auto v4 = msg->Size() - msg->Tell(); if (!v4) { return; } if (this->m_suspended) { // TODO } else { this->m_serverConnection->Send(msg, 0); // TODO this->m_bytesSent += v4; if (!this->m_serverConnection->m_encrypt) { this->EnableEncryption(this->m_serverConnection, nullptr, 0); } } } void NetClient::SetDelete() { this->m_deleteMe = true; } void NetClient::SetLoginData(LoginData* loginData) { memcpy(&this->m_loginData, loginData, sizeof(this->m_loginData)); } void NetClient::SetMessageHandler(NETMESSAGE msgId, MESSAGE_HANDLER handler, void* param) { this->m_handlers[msgId] = handler; this->m_handlerParams[msgId] = param; } void NetClient::WCCantConnect(WowConnection* conn, uint32_t timeStamp, NETCONNADDR* addr) { if (conn == this->m_redirectConnection) { // TODO return; } if (conn == this->m_serverConnection) { this->m_netEventQueue->AddEvent(EVENT_ID_NET_CANTCONNECT, conn, this, nullptr, 0); } } void NetClient::WCConnected(WowConnection* conn, WowConnection* inbound, uint32_t timeStamp, const NETCONNADDR* addr) { if (conn != this->m_serverConnection) { return; } this->m_pingLock.Enter(); this->m_connectedTimestamp = timeStamp; this->m_bytesReceived = 0; this->m_bytesSent = 0; this->m_latencyStart = 0; this->m_latencyEnd = 0; this->m_pingSent = OsGetAsyncTimeMsPrecise(); this->m_pingLock.Leave(); this->m_netEventQueue->AddEvent(EVENT_ID_NET_CONNECT, conn, this, nullptr, 0); } void NetClient::WCDisconnected(WowConnection* conn, uint32_t timeStamp, NETCONNADDR* addr) { // TODO this->DisplayNetworkStats(); if (this->m_netEventQueue) { this->m_netEventQueue->AddEvent(EVENT_ID_NET_DISCONNECT, conn, this, nullptr, 0); } } void NetClient::WCMessageReady(WowConnection* conn, uint32_t timeStamp, CDataStore* msg) { uint8_t* data; msg->GetDataInSitu(reinterpret_cast(data), msg->Size()); // TODO increment byte counter // SInterlockedExchangeAdd(this->m_bytesReceived, msg->m_size); msg->Seek(0); uint16_t msgId; msg->Get(msgId); // TODO SMSG_SUSPEND_COMMS (0x50F) // TODO SMSG_FORCE_SEND_QUEUED_PACKETS (0x511) // TODO SMSG_REDIRECT_CLIENT (0x50D) if (msgId == SMSG_PONG) { this->PongHandler(conn, msg); return; } else if (msgId == SMSG_AUTH_CHALLENGE) { this->AuthChallengeHandler(conn, msg); return; } if (conn == this->m_serverConnection && !this->m_suspended) { msg->Seek(msg->Size()); this->m_netEventQueue->AddEvent(EVENT_ID_NET_DATA, conn, this, data, msg->Size()); } else { conn->Disconnect(); } }