Merge pull request #104 from MindRapist/mr-11

Several bug fixes
This commit is contained in:
rtw1x1
2026-02-12 23:51:49 +00:00
committed by GitHub
6 changed files with 424 additions and 241 deletions

248
README.md
View File

@@ -16,238 +16,30 @@ This repository contains the source code necessary to compile the game client ex
## 📋 Changelog ## 📋 Changelog
### Encryption & Security Overhaul ### ⬆️ Feature Improvements
- **Packet dump has its own log file**: The debug process now generates 4 log files:
- `log.txt`: What you know and love, minus packet dumps
- `syserr.txt`: What you know and hate to see, as you know and hate it!
- `packetdump.txt`: All packet dumps go here
- `pdlog.txt`: The complete log with everything included
- **Console outputs are now colored!**: Not a big difference, red for syserr outputs, dimmer color for packet dumps.
- **TempTrace**: A type of output made to stand out in the console! Made to be used as a temporary debugging helper, it has blue background so you can easily spot it in the console. It logs normally in files and it is exported to the Python system via the C++ API!
Available C++ calls:
- `TraceTemp`
- `TraceTempf`
- `TraceTempn`
- `TraceTempfn`
Available Python calls:
- `TempTrace`
Usage: same as `Trace(f/n/fn)`, and `ErrorTrace` for Python
The entire legacy encryption system has been replaced with [libsodium](https://doc.libsodium.org/). <br>
<br>
#### Removed Legacy Crypto
* **Crypto++ (cryptopp) vendor library** — Completely removed from the project
* **Panama cipher** (`CFilterEncoder`, `CFilterDecoder`) — Removed from `NetStream`
* **TEA encryption** (`tea.h`, `tea.cpp`) — Removed from both client and server
* **DH2 key exchange** (`cipher.h`, `cipher.cpp`) — Removed from `EterBase`
* **Camellia cipher** — Removed all references
* **`_IMPROVED_PACKET_ENCRYPTION_`** — Entire system removed (XTEA key scheduling, sequence encryption, key agreement)
* **`adwClientKey[4]`** — Removed from all packet structs (`TPacketCGLogin2`, `TPacketCGLogin3`, `TPacketGDAuthLogin`, `TPacketGDLoginByKey`, `TPacketLoginOnSetup`) and all associated code on both client and server
* **`LSS_SECURITY_KEY`** — Dead code removed (`"testtesttesttest"` hardcoded key, `GetSecurityKey()` function)
#### New Encryption System (libsodium)
* **X25519 key exchange** — `SecureCipher` class handles keypair generation and session key derivation via `crypto_kx_client_session_keys` / `crypto_kx_server_session_keys`
* **XChaCha20-Poly1305 AEAD** — Used for authenticated encryption of handshake tokens (key exchange, session tokens)
* **XChaCha20 stream cipher** — Used for in-place network buffer encryption via `EncryptInPlace()` / `DecryptInPlace()` (zero overhead, nonce-counter based replay prevention)
* **Challenge-response authentication** — HMAC-based (`crypto_auth`) verification during key exchange to prove shared secret derivation
* **New handshake protocol** — `HEADER_GC_KEY_CHALLENGE` / `HEADER_CG_KEY_RESPONSE` / `HEADER_GC_KEY_COMPLETE` packet flow for secure session establishment
#### Network Encryption Pipeline
* **Client send path** — Data is encrypted at queue time in `CNetworkStream::Send()` (prevents double-encryption on partial TCP sends)
* **Client receive path** — Data is decrypted immediately after `recv()` in `__RecvInternalBuffer()`, before being committed to the buffer
* **Server send path** — Data is encrypted in `DESC::Packet()` via `EncryptInPlace()` after encoding to the output buffer
* **Server receive path** — Newly received bytes are decrypted in `DESC::ProcessInput()` via `DecryptInPlace()` before buffer commit
#### Login Security Hardening
* **Removed plaintext login path** — `HEADER_CG_LOGIN` (direct password to game server) has been removed. All game server logins now require a login key obtained through the auth server (`HEADER_CG_LOGIN2` / `LoginByKey`)
* **CSPRNG login keys** — `CreateLoginKey()` now uses `randombytes_uniform()` (libsodium) instead of the non-cryptographic Xoshiro128PlusPlus PRNG
* **Single-use login keys** — Keys are consumed (removed from the map) immediately after successful authentication
* **Shorter key expiry** — Expired login keys are cleaned up after 15 seconds (down from 60 seconds). Orphaned keys (descriptor gone, never expired) are also cleaned up
* **Login rate limiting** — Per-IP tracking of failed login attempts. After 5 failures within 60 seconds, the IP is blocked with a `BLOCK` status and disconnected. Counter resets after cooldown or successful login
* **Removed Brazil password bypass** — The `LC_IsBrazil()` block that unconditionally disabled password verification has been removed
#### Pack File Encryption
* **libsodium-based pack encryption** — `PackLib` now uses XChaCha20-Poly1305 for pack file encryption, replacing the legacy Camellia/XTEA system
* **Secure key derivation** — Pack encryption keys are derived using `crypto_pwhash` (Argon2id)
--- ---
### Networking Modernization Roadmap <br>
<br>
A 5-phase modernization of the entire client/server networking stack — packet format, buffer management, handshake protocol, connection architecture, and packet dispatching. Every phase is complete and verified on both client and server.
---
#### Phase 1 — Packet Format + Buffer System + Memory Safety
Replaced the legacy 1-byte packet headers and raw C-style buffers with a modern, uniform protocol.
##### What changed
* **2-byte headers + 2-byte length prefix** — All packet types (`CG::`, `GC::`, `GG::`, `GD::`, `DG::`) now use `uint16_t` header + `uint16_t` length. This increases the addressable packet space from 256 to 65,535 unique packet types and enables safe variable-length parsing
* **Namespaced packet headers** — All headers moved from flat `HEADER_CG_*` defines to C++ namespaces: `CG::MOVE`, `GC::PING`, `GG::LOGIN`, `GD::PLAYER_SAVE`, `DG::BOOT`. Subheaders similarly namespaced: `GuildSub::GC::LOGIN`, `ShopSub::CG::BUY`, etc.
* **RAII RingBuffer** — All raw `buffer_t` / `LPBUF` / `new[]`/`delete[]` patterns replaced with a single `RingBuffer` class featuring lazy compaction at 50% read position, exponential growth, and inlined accessors
* **PacketReader / PacketWriter** — Type-safe helpers that wrap buffer access with bounds checking, eliminating raw pointer arithmetic throughout the codebase
* **Sequence system modernized** — Packet sequence tracking retained for debugging but fixed to byte offset 4 (after header + length)
* **SecureCipher** — XChaCha20-Poly1305 stream cipher for all post-handshake traffic (see Encryption section above)
##### What was removed
* `buffer.h` / `buffer.cpp` (legacy C buffer library)
* All `LPBUF`, `buffer_new()`, `buffer_delete()`, `buffer_read()`, `buffer_write()` calls
* Raw `new[]`/`delete[]` buffer allocations in DESC classes
* 1-byte header constants (`HEADER_CG_*`, `HEADER_GC_*`, etc.)
##### Why
The legacy 1-byte header system limited the protocol to 256 packet types (already exhausted), raw C buffers had no bounds checking and were prone to buffer overflows, and the flat namespace caused header collisions between subsystems.
---
#### Phase 2 — Modern Buffer System *(merged into Phase 1)*
All connection types now use `RingBuffer` uniformly.
##### What changed
* **All DESC types** (`DESC`, `CLIENT_DESC`, `DESC_P2P`) use `RingBuffer` for `m_inputBuffer`, `m_outputBuffer`, and `m_bufferedOutputBuffer`
* **PeerBase** (db layer) ported to `RingBuffer`
* **TEMP_BUFFER** (local utility for building packets) backed by `RingBuffer`
##### What was removed
* `libthecore/buffer.h` and `libthecore/buffer.cpp` — the entire legacy buffer library
##### Why
The legacy buffer system used separate implementations across different connection types, had no RAII semantics (manual malloc/free), and offered no protection against buffer overflows.
---
#### Phase 3 — Simplified Handshake
Replaced the legacy multi-step handshake (4+ round trips with time synchronization and UDP binding) with a streamlined 1.5 round-trip flow.
##### What changed
* **1.5 round-trip handshake** — Server sends `GC::KEY_CHALLENGE` (with embedded time sync), client responds with `CG::KEY_RESPONSE`, server confirms with `GC::KEY_COMPLETE`. Session is encrypted from that point forward
* **Time sync embedded** — Initial time synchronization folded into `GC::KEY_CHALLENGE`; periodic time sync handled by `GC::PING` / `CG::PONG`
* **Handshake timeout** — 5-second expiry on handshake phase; stale connections are automatically cleaned up in `DestroyClosed()`
##### What was removed
* **6 dead packet types**: `CG_HANDSHAKE`, `GC_HANDSHAKE`, `CG_TIME_SYNC`, `GC_TIME_SYNC`, `GC_HANDSHAKE_OK`, `GC_BINDUDP`
* **Server functions**: `StartHandshake()`, `SendHandshake()`, `HandshakeProcess()`, `CreateHandshake()`, `FindByHandshake()`, `m_map_handshake`
* **Client functions**: `RecvHandshakePacket()`, `RecvHandshakeOKPacket()`, `m_HandshakeData`, `SendHandshakePacket()`
* ~12 server files and ~10 client files modified
##### Why
The original handshake required 4+ round trips, included dead UDP binding steps, had no timeout protection (stale connections could linger indefinitely), and the time sync was a separate multi-step sub-protocol that added latency to every new connection.
---
#### Phase 4 — Unified Connection (Client-Side Deduplication)
Consolidated duplicated connection logic into the base `CNetworkStream` class.
##### What changed
* **Key exchange** (`RecvKeyChallenge` / `RecvKeyComplete`) moved from 4 separate implementations to `CNetworkStream` base class
* **Ping/pong** (`RecvPingPacket` / `SendPongPacket`) moved from 3 separate implementations to `CNetworkStream` base class
* **CPythonNetworkStream** overrides `RecvKeyChallenge` only for time sync, delegates all crypto to base
* **CGuildMarkDownloader/Uploader** — `RecvKeyCompleteAndLogin` wraps base + sends `CG::MARK_LOGIN`
* **CAccountConnector** — Fixed raw `crypto_aead` bug (now uses base class `cipher.DecryptToken`)
* **Control-plane structs** extracted to `EterLib/ControlPackets.h` (Phase, Ping, Pong, KeyChallenge, KeyResponse, KeyComplete)
* **CGuildMarkUploader** — `m_pbySymbolBuf` migrated from `new[]`/`delete[]` to `std::vector<uint8_t>`
##### What was removed
* ~200 lines of duplicated code across `CAccountConnector`, `CGuildMarkDownloader`, `CGuildMarkUploader`, and `CPythonNetworkStream`
##### Why
The same key exchange and ping/pong logic was copy-pasted across 3-4 connection subclasses, leading to inconsistent behavior (the `CAccountConnector` had a raw crypto bug), difficult maintenance, and unnecessary code volume.
---
#### Phase 5 — Packet Handler Registration / Dispatcher
Replaced giant `switch` statements with `std::unordered_map` dispatch tables for O(1) packet routing.
##### What changed
**Client:**
* `CPythonNetworkStream` — Phase-specific handler maps for Game, Loading, Login, Select, and Handshake phases
* Registration pattern: `m_gameHandlers[GC::MOVE] = &CPythonNetworkStream::RecvCharacterMovePacket;`
* Dispatch: `DispatchPacket(m_gameHandlers)` — reads header, looks up handler, calls it
**Server:**
* `CInputMain`, `CInputDead`, `CInputAuth`, `CInputLogin`, `CInputP2P`, `CInputHandshake`, `CInputDB` — all converted to dispatch tables
* `CInputDB` uses 3 template adapters (`DataHandler`, `DescHandler`, `TypedHandler`) + 14 custom adapters for the diverse DB callback signatures
##### What was removed
* All `switch (header)` blocks across 7 server input processors and 5 client phase handlers
* ~3,000 lines of switch/case boilerplate
##### Why
The original dispatch used switch statements with 50-100+ cases each. Adding a new packet required modifying a massive switch block, which was error-prone and caused merge conflicts. The table-driven approach enables O(1) lookup, self-documenting handler registration, and trivial addition of new packet types.
---
### Post-Phase 5 Cleanup
Follow-up tasks after the core roadmap was complete.
#### One-Liner Adapter Reformat
* 24 adapter methods across 4 server files (`input_main.cpp`, `input_p2p.cpp`, `input_auth.cpp`, `input_login.cpp`) reformatted from single-line to multi-line for readability
#### MAIN_CHARACTER Packet Merge
* **4 mutually exclusive packets** (`GC::MAIN_CHARACTER`, `MAIN_CHARACTER2_EMPIRE`, `MAIN_CHARACTER3_BGM`, `MAIN_CHARACTER4_BGM_VOL`) merged into a single unified `GC::MAIN_CHARACTER` packet
* Single struct always includes BGM fields (zero when unused — 29 extra bytes on a one-time-per-load packet)
* 4 nearly identical client handlers merged into 1
* 3 redundant server send paths merged into 1
#### UDP Leftover Removal
* **7 client files deleted**: `NetDatagram.h/.cpp`, `NetDatagramReceiver.h/.cpp`, `NetDatagramSender.h/.cpp`, `PythonNetworkDatagramModule.cpp`
* **8 files edited**: Removed dead stubs (`PushUDPState`, `initudp`, `netSetUDPRecvBufferSize`, `netConnectUDP`), declarations, and Python method table entries
* **Server**: Removed `socket_udp_read()`, `socket_udp_bind()`, `__UDP_BLOCK__` define
#### Subheader Dispatch
* Extended the Phase 5 table-driven pattern to subheader switches with 8+ cases
* **Client**: Guild (19 sub-handlers), Shop (10), Exchange (8) in `PythonNetworkStreamPhaseGame.cpp`
* **Server**: Guild (15 sub-handlers) in `input_main.cpp`
* Small switches intentionally kept as-is: Messenger (5), Fishing (6), Dungeon (2), Server Shop (4)
---
### Performance Audit & Optimization
Comprehensive audit of all Phase 1-5 changes to identify and eliminate performance overhead.
#### Debug Logging Cleanup
* **Removed all hot-path `TraceError`/`Tracef`/`sys_log`** from networking code on both client and server
* Client: `NetStream.cpp`, `SecureCipher.cpp`, `PythonNetworkStream*.cpp` — eliminated per-frame and per-packet traces that caused disk I/O every frame
* Server: `desc.cpp`, `input.cpp`, `input_login.cpp`, `input_auth.cpp`, `SecureCipher.cpp` — eliminated `[SEND]`, `[RECV]`, `[CIPHER]` logs that fired on every packet
#### Packet Processing Throughput
* **`MAX_RECV_COUNT` 4 → 32** — Game phase now processes up to 32 packets per frame (was 4, severely limiting entity spawning on map entry)
* **Loading phase while-loop** — Changed from processing 1 packet per frame to draining all available packets, making phase transitions near-instant
#### Flood Check Optimization
* **Replaced `get_dword_time()` with `thecore_pulse()`** in `DESC::CheckPacketFlood()` — eliminates a `gettimeofday()` syscall on every single packet received. `thecore_pulse()` is cached once per game-loop iteration
#### Flood Protection
* **Per-IP connection limits** — Configurable maximum connections per IP address (`flood_max_connections_per_ip`, default: 10)
* **Global connection limits** — Configurable maximum total connections (`flood_max_global_connections`, default: 8192)
* **Per-second packet rate limiting** — Connections exceeding `flood_max_packets_per_sec` (default: 300) are automatically disconnected
* **Handshake timeout** — 5-second expiry prevents connection slot exhaustion from incomplete handshakes
---
### Pre-Phase 3 Cleanup
Preparatory cleanup performed before the handshake simplification.
* **File consolidation** — Merged scattered packet definitions into centralized header files
* **Alias removal** — Removed legacy `#define` aliases that mapped old names to new identifiers
* **Monarch system removal** — Completely removed the unused Monarch (emperor) system from both client and server, including all related packets, commands, quest functions, and UI code
* **TrafficProfiler removal** — Removed the `TrafficProfiler` class and all references (unnecessary runtime overhead)
* **Quest management stub removal** — Removed empty `questlua_mgmt.cpp` (monarch-era placeholder with no functions)
---
### Summary of Removed Legacy Systems
A consolidated reference of all legacy systems, files, and dead code removed across the entire modernization effort.
| System | What was removed | Replaced by |
|--------|-----------------|-------------|
| **Legacy C buffer** | `buffer.h`, `buffer.cpp`, all `LPBUF`/`buffer_new()`/`buffer_delete()` calls, raw `new[]`/`delete[]` buffer allocations | RAII `RingBuffer` class |
| **1-byte packet headers** | All `HEADER_CG_*`, `HEADER_GC_*`, `HEADER_GG_*`, `HEADER_GD_*`, `HEADER_DG_*` defines | 2-byte namespaced headers (`CG::`, `GC::`, `GG::`, `GD::`, `DG::`) |
| **Old handshake protocol** | 6 packet types (`CG_HANDSHAKE`, `GC_HANDSHAKE`, `CG_TIME_SYNC`, `GC_TIME_SYNC`, `GC_HANDSHAKE_OK`, `GC_BINDUDP`), all handshake functions and state | 1.5 round-trip key exchange (`KEY_CHALLENGE`/`KEY_RESPONSE`/`KEY_COMPLETE`) |
| **UDP networking** | 7 client files (`NetDatagram*.h/.cpp`, `PythonNetworkDatagramModule.cpp`), server `socket_udp_read()`/`socket_udp_bind()`/`__UDP_BLOCK__` | Removed entirely (game is TCP-only) |
| **Old sequence system** | `m_seq`, `SetSequence()`, `GetSequence()`, old sequence variables | Modernized sequence at fixed byte offset 4 |
| **TrafficProfiler** | `TrafficProfiler` class and all references | Removed entirely |
| **Monarch system** | All monarch/emperor packets, commands (`do_monarch_*`), quest functions (`questlua_monarch.cpp`, `questlua_mgmt.cpp`), UI code, GM commands | Removed entirely (unused feature) |
| **Legacy crypto** | Crypto++, Panama cipher, TEA, DH2, Camellia, XTEA, `adwClientKey[4]`, `LSS_SECURITY_KEY` | libsodium (X25519 + XChaCha20-Poly1305) |
| **Switch-based dispatch** | Giant `switch (header)` blocks (50-100+ cases each) across 7 server input processors and 5 client phase handlers | `std::unordered_map` dispatch tables |
| **Duplicated connection code** | Key exchange and ping/pong copy-pasted across 3-4 client subclasses | Consolidated in `CNetworkStream` base class |
---
# Installation/Configuration # Installation/Configuration
This is the third part of the entire project and it's about the client binary, the executable of the game. This is the third part of the entire project and it's about the client binary, the executable of the game.

View File

@@ -80,6 +80,57 @@ static TCachedTimestamp g_cachedTimestamp;
#define DBG_OUT_W_UTF8(psz) do { (void)(psz); } while (0) #define DBG_OUT_W_UTF8(psz) do { (void)(psz); } while (0)
#endif #endif
// MR-11: Colored console output for syserr and packet dumps
#ifdef _DEBUG
static WORD g_consoleDefaultAttrs = 0;
static bool g_consoleAttrsInit = false;
static void InitConsoleDefaultAttrs()
{
if (g_consoleAttrsInit)
return;
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (!hConsole || hConsole == INVALID_HANDLE_VALUE)
return;
CONSOLE_SCREEN_BUFFER_INFO info;
if (GetConsoleScreenBufferInfo(hConsole, &info))
{
g_consoleDefaultAttrs = info.wAttributes;
g_consoleAttrsInit = true;
}
}
static void WriteConsoleColored(const char* text, WORD attrs)
{
if (!text)
return;
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (!hConsole || hConsole == INVALID_HANDLE_VALUE)
{
fputs(text, stdout);
return;
}
InitConsoleDefaultAttrs();
if (g_consoleAttrsInit)
SetConsoleTextAttribute(hConsole, attrs);
fputs(text, stdout);
if (g_consoleAttrsInit)
SetConsoleTextAttribute(hConsole, g_consoleDefaultAttrs);
}
static const WORD kConsoleSyserrRed = FOREGROUND_RED | FOREGROUND_INTENSITY;
static const WORD kConsolePacketDumpDim = FOREGROUND_INTENSITY;
static const WORD kConsoleTempTraceBg = BACKGROUND_BLUE | BACKGROUND_INTENSITY |
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
#endif
// MR-11: -- END OF -- Colored console output for syserr and packet dumps
// Buffered log file writer // Buffered log file writer
// OPTIMIZATION: Buffered writes with periodic flush instead of per-write fflush() // OPTIMIZATION: Buffered writes with periodic flush instead of per-write fflush()
// - Collects writes in memory buffer // - Collects writes in memory buffer
@@ -171,6 +222,112 @@ class CLogFile : public CSingleton<CLogFile>
static CLogFile gs_logfile; static CLogFile gs_logfile;
// MR-11: Separate packet dump log from the main log file
class CExtraLogFile
{
public:
CExtraLogFile() : m_fp(NULL), m_bufferPos(0), m_lastFlushMs(0) {}
~CExtraLogFile()
{
Flush();
if (m_fp)
fclose(m_fp);
m_fp = NULL;
}
bool Initialize(const char* path)
{
m_fp = fopen(path, "w");
m_bufferPos = 0;
m_lastFlushMs = ELTimer_GetMSec();
return m_fp != NULL;
}
bool IsOpen() const
{
return m_fp != NULL;
}
void Write(const char* c_pszMsg)
{
if (!m_fp)
return;
g_cachedTimestamp.Update();
char timestamp[32];
g_cachedTimestamp.Format(timestamp, sizeof(timestamp));
size_t timestampLen = strlen(timestamp);
size_t msgLen = c_pszMsg ? strlen(c_pszMsg) : 0;
size_t totalLen = timestampLen + msgLen;
if (m_bufferPos + totalLen >= BUFFER_SIZE - 1)
Flush();
if (totalLen >= BUFFER_SIZE - 1)
{
fputs(timestamp, m_fp);
if (c_pszMsg)
fputs(c_pszMsg, m_fp);
fflush(m_fp);
return;
}
memcpy(m_buffer + m_bufferPos, timestamp, timestampLen);
m_bufferPos += timestampLen;
if (msgLen > 0)
{
memcpy(m_buffer + m_bufferPos, c_pszMsg, msgLen);
m_bufferPos += msgLen;
}
DWORD now = ELTimer_GetMSec();
if (now - m_lastFlushMs > 500 || m_bufferPos > BUFFER_SIZE * 3 / 4)
Flush();
}
void Flush()
{
if (!m_fp || m_bufferPos == 0)
return;
m_buffer[m_bufferPos] = '\0';
fputs(m_buffer, m_fp);
fflush(m_fp);
m_bufferPos = 0;
m_lastFlushMs = ELTimer_GetMSec();
}
private:
static const size_t BUFFER_SIZE = 8192;
FILE* m_fp;
char m_buffer[BUFFER_SIZE];
size_t m_bufferPos;
DWORD m_lastFlushMs;
};
#ifdef _PACKETDUMP
static CExtraLogFile g_packetDumpFile;
static CExtraLogFile g_pdlogFile;
static bool g_packetDumpEnabled = false;
static bool g_pdlogEnabled = false;
static bool g_pdlogRequested = false;
static void EnsurePacketDumpFiles(bool enablePdlog)
{
if (!std::filesystem::exists("log"))
std::filesystem::create_directory("log");
if (!g_packetDumpEnabled)
g_packetDumpEnabled = g_packetDumpFile.Initialize("log/packetdump.txt");
if (enablePdlog && !g_pdlogEnabled)
g_pdlogEnabled = g_pdlogFile.Initialize("log/pdlog.txt");
}
#endif
// MR-11: -- END OF -- Separate packet dump log from the main log file
static UINT gs_uLevel = 0; static UINT gs_uLevel = 0;
void SetLogLevel(UINT uLevel) void SetLogLevel(UINT uLevel)
@@ -360,6 +517,21 @@ static struct TSyserrBuffer
} }
} g_syserrBuffer; } g_syserrBuffer;
// MR-11: Seperate packet dump log from the main log file
static void WriteSyserrPlain(const char* msg)
{
if (!msg)
return;
g_cachedTimestamp.Update();
char timestamp[32];
g_cachedTimestamp.Format(timestamp, sizeof(timestamp));
g_syserrBuffer.Write(timestamp, strlen(timestamp));
g_syserrBuffer.Write(msg, strlen(msg));
}
// MR-11: -- END OF -- Seperate packet dump log from the main log file
void TraceError(const char* c_szFormat, ...) void TraceError(const char* c_szFormat, ...)
{ {
//#ifndef _DISTRIBUTE //#ifndef _DISTRIBUTE
@@ -394,7 +566,7 @@ void TraceError(const char* c_szFormat, ...)
#ifdef _DEBUG #ifdef _DEBUG
DBG_OUT_W_UTF8(szBuf); DBG_OUT_W_UTF8(szBuf);
fputs(szBuf, stdout); WriteConsoleColored(szBuf, kConsoleSyserrRed);
#endif #endif
if (isLogFile) if (isLogFile)
@@ -423,7 +595,7 @@ void TraceErrorWithoutEnter(const char* c_szFormat, ...)
#ifdef _DEBUG #ifdef _DEBUG
DBG_OUT_W_UTF8(szBuf); DBG_OUT_W_UTF8(szBuf);
fputs(szBuf, stdout); WriteConsoleColored(szBuf, kConsoleSyserrRed);
#endif #endif
if (isLogFile) if (isLogFile)
@@ -431,6 +603,162 @@ void TraceErrorWithoutEnter(const char* c_szFormat, ...)
//#endif //#endif
} }
// MR-11: Temporary trace functions for debugging (not for regular logging)
void TempTrace(const char* c_szMsg, bool errType)
{
if (!c_szMsg)
return;
#ifdef _DEBUG
DBG_OUT_W_UTF8(c_szMsg);
WriteConsoleColored(c_szMsg, kConsoleTempTraceBg);
#endif
if (errType)
{
WriteSyserrPlain(c_szMsg);
return;
}
if (isLogFile)
LogFile(c_szMsg);
}
void TempTracef(const char* c_szFormat, bool errType, ...)
{
char szBuf[DEBUG_STRING_MAX_LEN + 1];
va_list args;
va_start(args, errType);
_vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args);
va_end(args);
#ifdef _DEBUG
DBG_OUT_W_UTF8(szBuf);
WriteConsoleColored(szBuf, kConsoleTempTraceBg);
#endif
if (errType)
{
WriteSyserrPlain(szBuf);
return;
}
if (isLogFile)
LogFile(szBuf);
}
void TempTracen(const char* c_szMsg, bool errType)
{
if (!c_szMsg)
return;
char szBuf[DEBUG_STRING_MAX_LEN + 2];
_snprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, "%s\n", c_szMsg);
#ifdef _DEBUG
DBG_OUT_W_UTF8(szBuf);
WriteConsoleColored(szBuf, kConsoleTempTraceBg);
#endif
if (errType)
{
WriteSyserrPlain(szBuf);
return;
}
if (isLogFile)
LogFile(szBuf);
}
void TempTracenf(const char* c_szFormat, bool errType, ...)
{
char szBuf[DEBUG_STRING_MAX_LEN + 2];
va_list args;
va_start(args, errType);
_vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args);
va_end(args);
size_t cur = strnlen(szBuf, sizeof(szBuf));
if (cur + 1 < sizeof(szBuf)) {
szBuf[cur] = '\n';
szBuf[cur + 1] = '\0';
}
else {
szBuf[sizeof(szBuf) - 2] = '\n';
szBuf[sizeof(szBuf) - 1] = '\0';
}
#ifdef _DEBUG
DBG_OUT_W_UTF8(szBuf);
WriteConsoleColored(szBuf, kConsoleTempTraceBg);
#endif
if (errType)
{
WriteSyserrPlain(szBuf);
return;
}
if (isLogFile)
LogFile(szBuf);
}
// MR-11: -- END OF -- Temporary trace functions for debugging (not for regular logging)
// MR-11: Seperate packet dump from the rest of the logs
void PacketDump(const char* c_szMsg)
{
#ifdef _PACKETDUMP
if (!c_szMsg)
return;
PacketDumpf("%s", c_szMsg);
#else
(void)c_szMsg;
#endif
}
void PacketDumpf(const char* c_szFormat, ...)
{
#ifdef _PACKETDUMP
char szBuf[DEBUG_STRING_MAX_LEN + 2];
strncpy_s(szBuf, sizeof(szBuf), "PACKET_DUMP: ", _TRUNCATE);
int prefixLen = (int)strlen(szBuf);
va_list args;
va_start(args, c_szFormat);
_vsnprintf_s(szBuf + prefixLen, sizeof(szBuf) - prefixLen, _TRUNCATE, c_szFormat, args);
va_end(args);
size_t cur = strnlen(szBuf, sizeof(szBuf));
if (cur + 1 < sizeof(szBuf)) {
szBuf[cur] = '\n';
szBuf[cur + 1] = '\0';
}
else {
szBuf[sizeof(szBuf) - 2] = '\n';
szBuf[sizeof(szBuf) - 1] = '\0';
}
#ifdef _DEBUG
DBG_OUT_W_UTF8(szBuf);
WriteConsoleColored(szBuf, kConsolePacketDumpDim);
#endif
EnsurePacketDumpFiles(g_pdlogRequested);
if (g_packetDumpEnabled)
g_packetDumpFile.Write(szBuf);
if (g_pdlogEnabled)
g_pdlogFile.Write(szBuf);
#else
(void)c_szFormat;
#endif
}
// MR-11: -- END OF -- Seperate packet dump from the rest of the logs
void LogBoxf(const char* c_szFormat, ...) void LogBoxf(const char* c_szFormat, ...)
{ {
va_list args; va_list args;
@@ -461,6 +789,13 @@ void LogBox(const char* c_szMsg, const char* c_szCaption, HWND hWnd)
void LogFile(const char* c_szMsg) void LogFile(const char* c_szMsg)
{ {
CLogFile::Instance().Write(c_szMsg); CLogFile::Instance().Write(c_szMsg);
// MR-11: Separate packet dump log from the main log file
#ifdef _PACKETDUMP
if (g_pdlogEnabled)
g_pdlogFile.Write(c_szMsg);
#endif
// MR-11: -- END OF -- Separate packet dump log from the main log file
} }
void LogFilef(const char* c_szMessage, ...) void LogFilef(const char* c_szMessage, ...)
@@ -474,6 +809,13 @@ void LogFilef(const char* c_szMessage, ...)
va_end(args); va_end(args);
CLogFile::Instance().Write(szBuf); CLogFile::Instance().Write(szBuf);
// MR-11: Separate packet dump log from the main log file
#ifdef _PACKETDUMP
if (g_pdlogEnabled)
g_pdlogFile.Write(szBuf);
#endif
// MR-11: -- END OF -- Separate packet dump log from the main log file
} }
void OpenLogFile(bool bUseLogFIle) void OpenLogFile(bool bUseLogFIle)
@@ -490,7 +832,13 @@ void OpenLogFile(bool bUseLogFIle)
isLogFile = true; isLogFile = true;
CLogFile::Instance().Initialize(); CLogFile::Instance().Initialize();
} }
//#endif
// MR-11: Separate packet dump log from the main log file
#ifdef _PACKETDUMP
g_pdlogRequested = bUseLogFIle;
EnsurePacketDumpFiles(g_pdlogRequested);
#endif
// MR-11: -- END OF -- Separate packet dump log from the main log file
} }
void CloseLogFile() void CloseLogFile()
@@ -498,6 +846,15 @@ void CloseLogFile()
// Flush all buffered output before shutdown // Flush all buffered output before shutdown
g_syserrBuffer.Flush(); g_syserrBuffer.Flush();
CLogFile::Instance().Flush(); CLogFile::Instance().Flush();
// MR-11: Separate packet dump log from the main log file
#ifdef _PACKETDUMP
if (g_packetDumpEnabled)
g_packetDumpFile.Flush();
if (g_pdlogEnabled)
g_pdlogFile.Flush();
#endif
// MR-11: -- END OF -- Separate packet dump log from the main log file
} }
void OpenConsoleWindow() void OpenConsoleWindow()

View File

@@ -3,6 +3,10 @@
#include <windows.h> #include <windows.h>
#if defined(_DEBUG) && !defined(_PACKETDUMP)
#define _PACKETDUMP
#endif
extern void SetLogLevel(UINT uLevel); extern void SetLogLevel(UINT uLevel);
extern void Log(UINT uLevel, const char* c_szMsg); extern void Log(UINT uLevel, const char* c_szMsg);
extern void Logn(UINT uLevel, const char* c_szMsg); extern void Logn(UINT uLevel, const char* c_szMsg);
@@ -15,6 +19,15 @@ extern void Tracenf(const char* c_szFormat, ...);
extern void Tracef(const char* c_szFormat, ...); extern void Tracef(const char* c_szFormat, ...);
extern void TraceError(const char* c_szFormat, ...); extern void TraceError(const char* c_szFormat, ...);
extern void TraceErrorWithoutEnter(const char* c_szFormat, ...); extern void TraceErrorWithoutEnter(const char* c_szFormat, ...);
// MR-11: Separate packet dump log from the main log file
extern void TempTrace(const char* c_szMsg, bool errType = false);
extern void TempTracef(const char* c_szFormat, bool errType = false, ...);
extern void TempTracen(const char* c_szMsg, bool errType = false);
extern void TempTracenf(const char* c_szFormat, bool errType = false, ...);
extern void PacketDump(const char* c_szMsg);
extern void PacketDumpf(const char* c_szFormat, ...);
// MR-11: -- END OF -- Separate packet dump log from the main log file
extern void LogBox(const char* c_szMsg, const char * c_szCaption = NULL, HWND hWnd = NULL); extern void LogBox(const char* c_szMsg, const char * c_szCaption = NULL, HWND hWnd = NULL);
extern void LogBoxf(const char* c_szMsg, ...); extern void LogBoxf(const char* c_szMsg, ...);

View File

@@ -3,7 +3,7 @@
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#ifdef _DEBUG #if defined(_DEBUG) && !defined(_PACKETDUMP)
#define _PACKETDUMP #define _PACKETDUMP
#endif #endif
@@ -513,11 +513,13 @@ bool CNetworkStream::Recv(int size, char * pDestBuf)
#ifdef _PACKETDUMP #ifdef _PACKETDUMP
if (size >= 2) if (size >= 2)
{ {
// MR-11: Separate packet dump log from the main log file
const uint16_t kHeader = *reinterpret_cast<const uint16_t*>(pDestBuf); const uint16_t kHeader = *reinterpret_cast<const uint16_t*>(pDestBuf);
TraceError("RECV< %s 0x%04X (%d bytes)", GetHeaderName(kHeader), kHeader, size); PacketDumpf("RECV< %s 0x%04X (%d bytes)", GetHeaderName(kHeader), kHeader, size);
const auto contents = dump_hex(reinterpret_cast<const uint8_t*>(pDestBuf), size); const auto contents = dump_hex(reinterpret_cast<const uint8_t*>(pDestBuf), size);
TraceError("%s", contents.c_str()); PacketDumpf("%s", contents.c_str());
// MR-11: -- END OF -- Separate packet dump log from the main log file
} }
#endif #endif
@@ -564,11 +566,13 @@ bool CNetworkStream::Send(int size, const char * pSrcBuf)
#ifdef _PACKETDUMP #ifdef _PACKETDUMP
if (size >= 2) if (size >= 2)
{ {
// MR-11: Separate packet dump log from the main log file
const uint16_t kHeader = *reinterpret_cast<const uint16_t*>(pSrcBuf); const uint16_t kHeader = *reinterpret_cast<const uint16_t*>(pSrcBuf);
TraceError("SEND> %s 0x%04X (%d bytes)", GetHeaderName(kHeader), kHeader, size); PacketDumpf("SEND> %s 0x%04X (%d bytes)", GetHeaderName(kHeader), kHeader, size);
const auto contents = dump_hex(reinterpret_cast<const uint8_t*>(pSrcBuf), size); const auto contents = dump_hex(reinterpret_cast<const uint8_t*>(pSrcBuf), size);
TraceError("%s", contents.c_str()); PacketDumpf("%s", contents.c_str());
// MR-11: -- END OF -- Separate packet dump log from the main log file
} }
#endif #endif

View File

@@ -73,9 +73,11 @@ namespace UI
CImageBox * pSignImage; CImageBox * pSignImage;
CAniImageBox * pFinishCoolTimeEffect; CAniImageBox * pFinishCoolTimeEffect;
} TSlot; } TSlot;
typedef std::list<TSlot> TSlotList; typedef std::list<TSlot> TSlotList;
typedef TSlotList::iterator TSlotListIterator; typedef TSlotList::iterator TSlotListIterator;
typedef struct SStoreCoolDown { float fCoolTime; float fElapsedTime; bool bActive; };
struct SStoreCoolDown { float fCoolTime; float fElapsedTime; bool bActive; };
public: public:
@@ -121,13 +123,13 @@ namespace UI
void LockSlot(DWORD dwIndex); void LockSlot(DWORD dwIndex);
void UnlockSlot(DWORD dwIndex); void UnlockSlot(DWORD dwIndex);
BOOL IsLockSlot(DWORD dwIndex); // BOOL IsLockSlot(DWORD dwIndex);
void SetCantUseSlot(DWORD dwIndex); void SetCantUseSlot(DWORD dwIndex);
void SetUseSlot(DWORD dwIndex); void SetUseSlot(DWORD dwIndex);
BOOL IsCantUseSlot(DWORD dwIndex); // BOOL IsCantUseSlot(DWORD dwIndex);
void EnableSlot(DWORD dwIndex); void EnableSlot(DWORD dwIndex);
void DisableSlot(DWORD dwIndex); void DisableSlot(DWORD dwIndex);
BOOL IsEnableSlot(DWORD dwIndex); // BOOL IsEnableSlot(DWORD dwIndex);
// Select // Select
void ClearSelected(); void ClearSelected();

View File

@@ -49,6 +49,18 @@ PyObject* dbgTraceError(PyObject* poSelf, PyObject* poArgs)
return Py_BuildNone(); return Py_BuildNone();
} }
// MR-11: Temporary trace functions for debugging (not for regular logging)
PyObject* dbgTraceTemp(PyObject* poSelf, PyObject* poArgs)
{
char* szMsg;
if (!PyTuple_GetString(poArgs, 0, &szMsg))
return Py_BuildException();
TempTrace(szMsg, false);
return Py_BuildNone();
}
// MR-11: -- END OF -- Temporary trace functions for debugging (not for regular logging)
PyObject* dbgRegisterExceptionString(PyObject* poSelf, PyObject* poArgs) PyObject* dbgRegisterExceptionString(PyObject* poSelf, PyObject* poArgs)
{ {
char* szMsg; char* szMsg;
@@ -69,8 +81,11 @@ void initdbg()
{ "Trace", dbgTrace, METH_VARARGS }, { "Trace", dbgTrace, METH_VARARGS },
{ "Tracen", dbgTracen, METH_VARARGS }, { "Tracen", dbgTracen, METH_VARARGS },
{ "TraceError", dbgTraceError, METH_VARARGS }, { "TraceError", dbgTraceError, METH_VARARGS },
// MR-11: Temporary trace functions for debugging (not for regular logging)
{ "TraceTemp", dbgTraceTemp, METH_VARARGS },
// MR-11: -- END OF -- Temporary trace functions for debugging (not for regular logging)
{ "RegisterExceptionString", dbgRegisterExceptionString, METH_VARARGS }, { "RegisterExceptionString", dbgRegisterExceptionString, METH_VARARGS },
{ NULL, NULL}, { NULL, NULL },
}; };
Py_InitModule("dbg", s_methods); Py_InitModule("dbg", s_methods);