Optimize logging and make world packet parser callback-safe

This commit is contained in:
Kelsi 2026-02-22 06:38:41 -08:00
parent 85c8b5d5f4
commit 4ea4cb761c
5 changed files with 49 additions and 27 deletions

View file

@ -74,6 +74,7 @@ private:
std::mutex mutex; std::mutex mutex;
std::ofstream fileStream; std::ofstream fileStream;
bool fileReady = false; bool fileReady = false;
bool echoToStdout_ = true;
std::chrono::steady_clock::time_point lastFlushTime_{}; std::chrono::steady_clock::time_point lastFlushTime_{};
uint32_t flushIntervalMs_ = 250; uint32_t flushIntervalMs_ = 250;
void ensureFile(); void ensureFile();

View file

@ -12,6 +12,7 @@ public:
Packet() = default; Packet() = default;
explicit Packet(uint16_t opcode); explicit Packet(uint16_t opcode);
Packet(uint16_t opcode, const std::vector<uint8_t>& data); Packet(uint16_t opcode, const std::vector<uint8_t>& data);
Packet(uint16_t opcode, std::vector<uint8_t>&& data);
void writeUInt8(uint8_t value); void writeUInt8(uint8_t value);
void writeUInt16(uint16_t value); void writeUInt16(uint16_t value);

View file

@ -16,6 +16,11 @@ Logger& Logger::getInstance() {
void Logger::ensureFile() { void Logger::ensureFile() {
if (fileReady) return; if (fileReady) return;
fileReady = true; fileReady = true;
if (const char* logStdout = std::getenv("WOWEE_LOG_STDOUT")) {
if (logStdout[0] == '0') {
echoToStdout_ = false;
}
}
if (const char* flushMs = std::getenv("WOWEE_LOG_FLUSH_MS")) { if (const char* flushMs = std::getenv("WOWEE_LOG_FLUSH_MS")) {
char* end = nullptr; char* end = nullptr;
unsigned long parsed = std::strtoul(flushMs, &end, 10); unsigned long parsed = std::strtoul(flushMs, &end, 10);
@ -67,7 +72,9 @@ void Logger::log(LogLevel level, const std::string& message) {
line << "] " << message; line << "] " << message;
std::cout << line.str() << '\n'; if (echoToStdout_) {
std::cout << line.str() << '\n';
}
if (fileStream.is_open()) { if (fileStream.is_open()) {
fileStream << line.str() << '\n'; fileStream << line.str() << '\n';
bool shouldFlush = (level >= LogLevel::WARNING); bool shouldFlush = (level >= LogLevel::WARNING);

View file

@ -1,5 +1,6 @@
#include "network/packet.hpp" #include "network/packet.hpp"
#include <cstring> #include <cstring>
#include <utility>
namespace wowee { namespace wowee {
namespace network { namespace network {
@ -9,6 +10,9 @@ Packet::Packet(uint16_t opcode) : opcode(opcode) {}
Packet::Packet(uint16_t opcode, const std::vector<uint8_t>& data) Packet::Packet(uint16_t opcode, const std::vector<uint8_t>& data)
: opcode(opcode), data(data), readPos(0) {} : opcode(opcode), data(data), readPos(0) {}
Packet::Packet(uint16_t opcode, std::vector<uint8_t>&& data)
: opcode(opcode), data(std::move(data)), readPos(0) {}
void Packet::writeUInt8(uint8_t value) { void Packet::writeUInt8(uint8_t value) {
data.push_back(value); data.push_back(value);
} }

View file

@ -325,27 +325,31 @@ void WorldSocket::update() {
void WorldSocket::tryParsePackets() { void WorldSocket::tryParsePackets() {
// World server packets have 4-byte incoming header: size(2) + opcode(2) // World server packets have 4-byte incoming header: size(2) + opcode(2)
int parsedThisTick = 0; int parsedThisTick = 0;
while (receiveBuffer.size() >= 4 && parsedThisTick < kMaxParsedPacketsPerUpdate) { size_t parseOffset = 0;
size_t localHeaderBytesDecrypted = headerBytesDecrypted;
std::vector<Packet> parsedPackets;
parsedPackets.reserve(32);
while ((receiveBuffer.size() - parseOffset) >= 4 && parsedThisTick < kMaxParsedPacketsPerUpdate) {
uint8_t rawHeader[4] = {0, 0, 0, 0}; uint8_t rawHeader[4] = {0, 0, 0, 0};
std::memcpy(rawHeader, receiveBuffer.data(), 4); std::memcpy(rawHeader, receiveBuffer.data() + parseOffset, 4);
// Decrypt header bytes in-place if encryption is enabled // Decrypt header bytes in-place if encryption is enabled
// Only decrypt bytes we haven't already decrypted // Only decrypt bytes we haven't already decrypted
if (encryptionEnabled && headerBytesDecrypted < 4) { if (encryptionEnabled && localHeaderBytesDecrypted < 4) {
size_t toDecrypt = 4 - headerBytesDecrypted; size_t toDecrypt = 4 - localHeaderBytesDecrypted;
if (useVanillaCrypt) { if (useVanillaCrypt) {
vanillaCrypt.decrypt(receiveBuffer.data() + headerBytesDecrypted, toDecrypt); vanillaCrypt.decrypt(receiveBuffer.data() + parseOffset + localHeaderBytesDecrypted, toDecrypt);
} else { } else {
decryptCipher.process(receiveBuffer.data() + headerBytesDecrypted, toDecrypt); decryptCipher.process(receiveBuffer.data() + parseOffset + localHeaderBytesDecrypted, toDecrypt);
} }
headerBytesDecrypted = 4; localHeaderBytesDecrypted = 4;
} }
// Parse header (now decrypted in-place). // Parse header (now decrypted in-place).
// Size: 2 bytes big-endian. For world packets, this includes opcode bytes. // Size: 2 bytes big-endian. For world packets, this includes opcode bytes.
uint16_t size = (receiveBuffer[0] << 8) | receiveBuffer[1]; uint16_t size = (receiveBuffer[parseOffset + 0] << 8) | receiveBuffer[parseOffset + 1];
// Opcode: 2 bytes little-endian. // Opcode: 2 bytes little-endian.
uint16_t opcode = receiveBuffer[2] | (receiveBuffer[3] << 8); uint16_t opcode = receiveBuffer[parseOffset + 2] | (receiveBuffer[parseOffset + 3] << 8);
if (size < 2) { if (size < 2) {
LOG_ERROR("World packet framing desync: invalid size=", size, LOG_ERROR("World packet framing desync: invalid size=", size,
" rawHdr=", std::hex, " rawHdr=", std::hex,
@ -381,45 +385,50 @@ void WorldSocket::tryParsePackets() {
static_cast<int>(rawHeader[2]), " ", static_cast<int>(rawHeader[2]), " ",
static_cast<int>(rawHeader[3]), static_cast<int>(rawHeader[3]),
" dec=", " dec=",
static_cast<int>(receiveBuffer[0]), " ", static_cast<int>(receiveBuffer[parseOffset + 0]), " ",
static_cast<int>(receiveBuffer[1]), " ", static_cast<int>(receiveBuffer[parseOffset + 1]), " ",
static_cast<int>(receiveBuffer[2]), " ", static_cast<int>(receiveBuffer[parseOffset + 2]), " ",
static_cast<int>(receiveBuffer[3]), static_cast<int>(receiveBuffer[parseOffset + 3]),
std::dec, std::dec,
" size=", size, " size=", size,
" payload=", payloadLen, " payload=", payloadLen,
" opcode=0x", std::hex, opcode, std::dec, " opcode=0x", std::hex, opcode, std::dec,
" buffered=", receiveBuffer.size()); " buffered=", (receiveBuffer.size() - parseOffset));
--headerTracePacketsLeft; --headerTracePacketsLeft;
} }
if (isLoginPipelineSmsg(opcode)) { if (isLoginPipelineSmsg(opcode)) {
LOG_INFO("WS RX LOGIN opcode=0x", std::hex, opcode, std::dec, LOG_INFO("WS RX LOGIN opcode=0x", std::hex, opcode, std::dec,
" size=", size, " payload=", payloadLen, " size=", size, " payload=", payloadLen,
" buffered=", receiveBuffer.size(), " buffered=", (receiveBuffer.size() - parseOffset),
" enc=", encryptionEnabled ? "yes" : "no"); " enc=", encryptionEnabled ? "yes" : "no");
} }
if (receiveBuffer.size() < totalSize) { if ((receiveBuffer.size() - parseOffset) < totalSize) {
// Not enough data yet - header stays decrypted in buffer // Not enough data yet - header stays decrypted in buffer
break; break;
} }
// Extract payload (skip header) // Extract payload (skip header)
std::vector<uint8_t> packetData(receiveBuffer.begin() + 4, std::vector<uint8_t> packetData(receiveBuffer.begin() + parseOffset + 4,
receiveBuffer.begin() + totalSize); receiveBuffer.begin() + parseOffset + totalSize);
// Create packet with opcode and payload // Queue packet; callbacks run after buffer state is finalized.
Packet packet(opcode, packetData); parsedPackets.emplace_back(opcode, std::move(packetData));
parseOffset += totalSize;
localHeaderBytesDecrypted = 0;
++parsedThisTick;
}
// Remove parsed data from buffer and reset header decryption counter if (parseOffset > 0) {
receiveBuffer.erase(receiveBuffer.begin(), receiveBuffer.begin() + totalSize); receiveBuffer.erase(receiveBuffer.begin(), receiveBuffer.begin() + parseOffset);
headerBytesDecrypted = 0; }
headerBytesDecrypted = localHeaderBytesDecrypted;
// Call callback if set if (packetCallback) {
if (packetCallback) { for (const auto& packet : parsedPackets) {
if (!connected) break;
packetCallback(packet); packetCallback(packet);
} }
++parsedThisTick;
} }
if (parsedThisTick >= kMaxParsedPacketsPerUpdate && receiveBuffer.size() >= 4) { if (parsedThisTick >= kMaxParsedPacketsPerUpdate && receiveBuffer.size() >= 4) {