Implement 40250 classic client port

This commit is contained in:
shenlei
2026-09-01 18:49:28 +09:00
parent 2277b1dd60
commit 8f61990a83
481 changed files with 169826 additions and 293 deletions
+145
View File
@@ -0,0 +1,145 @@
#include "classic/classic_mark_client.h"
#include <cassert>
#include <cstdint>
#include <cstring>
#include <vector>
using namespace mtnet::classic;
static std::vector<uint8_t> take_all(ClassicStream &stream) {
std::vector<uint8_t> out(stream.outgoing_pending());
if (!out.empty()) {
assert(stream.take_outgoing(out.data(), out.size()) == out.size());
}
return out;
}
static void put_u16(std::vector<uint8_t> &p, size_t off, uint16_t v) {
p[off] = static_cast<uint8_t>(v);
p[off + 1] = static_cast<uint8_t>(v >> 8);
}
static void put_u32(std::vector<uint8_t> &p, size_t off, uint32_t v) {
p[off] = static_cast<uint8_t>(v);
p[off + 1] = static_cast<uint8_t>(v >> 8);
p[off + 2] = static_cast<uint8_t>(v >> 16);
p[off + 3] = static_cast<uint8_t>(v >> 24);
}
static void test_mark_download() {
ClassicMarkClient client(0x11223344u, 0x55667788u);
Handshake handshake{};
handshake.header = HDR_HANDSHAKE;
handshake.handshake = 7;
handshake.time = 100;
client.stream().feed(&handshake, sizeof(handshake));
std::vector<uint8_t> login = take_all(client.stream());
assert(login.size() == sizeof(Handshake) + sizeof(CGMarkLogin));
assert(login[sizeof(Handshake)] == HDR_CG_MARK_LOGIN);
uint32_t handle = 0;
std::memcpy(&handle, login.data() + sizeof(Handshake) + 1, sizeof(handle));
assert(handle == 0x11223344u);
uint32_t random_key = 0;
std::memcpy(&random_key, login.data() + sizeof(Handshake) + 5, sizeof(random_key));
assert(random_key == 0x55667788u);
Handshake resync = handshake;
resync.time = 200;
resync.delta = 3;
client.stream().feed(&resync, sizeof(resync));
std::vector<uint8_t> repeated_login = take_all(client.stream());
assert(repeated_login.size() == sizeof(Handshake) + sizeof(CGMarkLogin));
assert(repeated_login[0] == HDR_HANDSHAKE);
assert(repeated_login[sizeof(Handshake)] == HDR_CG_MARK_LOGIN);
std::vector<uint8_t> phase{HDR_GC_PHASE, PHASE_LOGIN};
client.stream().feed(phase.data(), phase.size());
std::vector<uint8_t> request = take_all(client.stream());
assert(request.size() == sizeof(CGMarkIDXList));
assert(request[0] == HDR_CG_MARK_IDXLIST);
// One guild maps to image zero, position one. Feed the whole-size frame in
// two chunks to exercise the custom raw framing path.
std::vector<uint8_t> idx(sizeof(GCMarkIDXList) + 4);
idx[0] = HDR_GC_MARK_IDXLIST;
put_u32(idx, 1, static_cast<uint32_t>(idx.size()));
put_u16(idx, 5, 1);
put_u16(idx, 7, 42);
put_u16(idx, 9, 1);
client.stream().feed(idx.data(), 6);
assert(client.stream().outgoing_pending() == 0);
client.stream().feed(idx.data() + 6, idx.size() - 6);
request = take_all(client.stream());
assert(request.size() == sizeof(CGMarkCRCList));
assert(request[0] == HDR_CG_MARK_CRCLIST && request[1] == 0);
assert(client.marks().has_mark(42));
// A valid zero-block image response advances the image state and completes
// the download without requiring LZO data in this framing test.
std::vector<uint8_t> block(sizeof(GCMarkBlock));
block[0] = HDR_GC_MARK_BLOCK;
put_u32(block, 1, static_cast<uint32_t>(block.size()));
block[5] = 0;
put_u32(block, 6, 0);
client.stream().feed(block.data(), block.size());
assert(client.complete());
}
static void test_symbol_download() {
ClassicMarkClient client(0x11u, 0x22u);
client.set_download_symbol(42);
std::vector<uint8_t> phase{HDR_GC_PHASE, PHASE_LOGIN};
client.stream().feed(phase.data(), phase.size());
std::vector<uint8_t> request = take_all(client.stream());
assert(request.size() == sizeof(CGSymbolCRC));
assert(request[0] == HDR_CG_SYMBOL_CRC);
uint32_t gid = 0;
std::memcpy(&gid, request.data() + 1, sizeof(gid));
assert(gid == 42);
const std::vector<uint8_t> payload{0xAA, 0xBB, 0xCC};
std::vector<uint8_t> symbol(sizeof(GCSymbolData) + payload.size());
symbol[0] = HDR_GC_SYMBOL_DATA;
put_u16(symbol, 1, static_cast<uint16_t>(symbol.size()));
put_u32(symbol, 3, 42);
std::memcpy(symbol.data() + sizeof(GCSymbolData), payload.data(), payload.size());
client.stream().feed(symbol.data(), symbol.size());
assert(client.complete());
assert(client.symbol_guild_id() == 42);
assert(client.symbol_data() == payload);
}
static void test_upload_layouts() {
uint32_t px[GUILD_MARK_WIDTH * GUILD_MARK_HEIGHT] = {};
px[0] = 0x44332211u;
ClassicMarkClient mark(1, 2);
mark.set_upload_mark(42, px);
std::vector<uint8_t> phase{HDR_GC_PHASE, PHASE_LOGIN};
mark.stream().feed(phase.data(), phase.size());
std::vector<uint8_t> packet = take_all(mark.stream());
assert(packet.size() == sizeof(CGMarkUpload));
assert(packet[0] == HDR_CG_MARK_UPLOAD);
uint32_t gid = 0;
std::memcpy(&gid, packet.data() + 1, sizeof(gid));
assert(gid == 42);
assert(packet[5] == 0x11 && packet[6] == 0x22 && packet[7] == 0x33 && packet[8] == 0x44);
ClassicMarkClient symbol(3, 4);
symbol.set_upload_symbol(99, std::vector<uint8_t>{1, 2, 3});
symbol.stream().feed(phase.data(), phase.size());
packet = take_all(symbol.stream());
assert(packet.size() == sizeof(CGGuildSymbolUpload) + 3);
assert(packet[0] == HDR_CG_GUILD_SYMBOL_UPLOAD);
assert(packet[1] == packet.size() && packet[2] == 0);
std::memcpy(&gid, packet.data() + 3, sizeof(gid));
assert(gid == 99);
assert(packet[7] == 1 && packet[8] == 2 && packet[9] == 3);
}
int main() {
test_mark_download();
test_symbol_download();
test_upload_layouts();
return 0;
}