- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
131 lines
4.8 KiB
C++
131 lines
4.8 KiB
C++
// net_text_codec_test — §1.10 fixed-length wire text: the client keeps UTF-8
|
|
// internally, while the Chinese 40250 wire uses GB2312-compatible bytes.
|
|
#include "../src/net/text_codec.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <string>
|
|
|
|
static int g_fail = 0;
|
|
#define CHECK(c, msg) \
|
|
do { \
|
|
if (!(c)) { \
|
|
std::fprintf(stderr, "FAIL: %s\n", msg); \
|
|
++g_fail; \
|
|
} \
|
|
} while (0)
|
|
|
|
// U+6C49 "汉": 3 bytes in UTF-8, 2 bytes in GB2312 (BA BA).
|
|
static std::string cjk(int n) {
|
|
std::string s;
|
|
for (int i = 0; i < n; ++i) {
|
|
s += "\xE6\xB1\x89";
|
|
}
|
|
return s;
|
|
}
|
|
|
|
int main() {
|
|
using mtnet::from_wire_str;
|
|
using mtnet::to_wire;
|
|
|
|
// CHARACTER_NAME_MAX_LEN is a 24-byte wire capacity. Twelve Chinese
|
|
// characters fit; measuring the UTF-8 input would incorrectly allow eight.
|
|
{
|
|
const std::string in = cjk(12);
|
|
CHECK(in.size() == 36, "12 CJK == 36 UTF-8 bytes");
|
|
const std::string w = to_wire(in, 24);
|
|
CHECK(w.size() == 24, "12 CJK == 24 GB2312 bytes");
|
|
CHECK(from_wire_str(w.data(), w.size()) == in, "12 CJK round-trip through GB2312");
|
|
CHECK(mtnet::wire_text_fits(in, 24), "12 CJK fits 24-byte name field");
|
|
}
|
|
|
|
// Thirteen characters need 26 GB2312 bytes. Conversion truncates only at a
|
|
// legacy character boundary, while input validation rejects the overlong name.
|
|
{
|
|
const std::string in = cjk(13);
|
|
CHECK(in.size() == 39, "13 CJK == 39 UTF-8 bytes");
|
|
const std::string w = to_wire(in, 24);
|
|
CHECK(w.size() == 24, "13 CJK is capped at 24 wire bytes");
|
|
CHECK(from_wire_str(w.data(), w.size()) == cjk(12), "cap keeps 12 whole CJK chars");
|
|
CHECK(!mtnet::wire_text_fits(in, 24), "13 CJK rejected by 24-byte name field");
|
|
}
|
|
|
|
// The preflight used by M2Client must use the selected protocol's rule.
|
|
// A 32-byte 40250 shop-sign field therefore accepts sixteen GB2312 CJK
|
|
// characters even though the UTF-8 input occupies 48 bytes. The generic
|
|
// transport intentionally keeps its own byte contract.
|
|
{
|
|
CHECK(mtnet::protocol_text_fits(cjk(16), 32, true),
|
|
"classic preflight measures the encoded 32-byte sign");
|
|
CHECK(!mtnet::protocol_text_fits(cjk(17), 32, true),
|
|
"classic preflight rejects a 34-byte sign");
|
|
CHECK(!mtnet::protocol_text_fits(cjk(16), 32, false),
|
|
"generic preflight keeps its UTF-8 byte contract");
|
|
}
|
|
|
|
// Invalid UTF-8 is not leaked to the legacy wire and is rejected by the
|
|
// strict validation helper.
|
|
{
|
|
std::string in = cjk(12);
|
|
in += '\xE6'; // dangling UTF-8 lead byte
|
|
CHECK(to_wire(in, 24).size() == 24, "invalid suffix cannot overflow cap");
|
|
CHECK(!mtnet::wire_text_fits(in, 24), "invalid UTF-8 rejected");
|
|
}
|
|
|
|
// ASCII remains one byte per character and round-trips unchanged.
|
|
{
|
|
const std::string in = "Warrior";
|
|
CHECK(to_wire(in, 24) == in, "ascii under cap unchanged");
|
|
CHECK(to_wire(std::string(40, 'x'), 24).size() == 24, "ascii over cap -> 24");
|
|
CHECK(from_wire_str(in.data(), in.size()) == in, "ascii round-trip");
|
|
}
|
|
|
|
// The user-visible UTF-8 text is not limited to the test character 汉. A
|
|
// real Chinese name must round-trip through the 40250 GB2312 boundary too.
|
|
{
|
|
const std::string in = "龙驹简体中文";
|
|
const std::string wire = to_wire(in, 24);
|
|
CHECK(wire.size() == 12, "Chinese name uses GB2312 bytes, not UTF-8 bytes");
|
|
CHECK(from_wire_str(wire.data(), wire.size()) == in,
|
|
"Chinese name round-trips through the 40250 wire code page");
|
|
}
|
|
{
|
|
const std::string in = "龙驹简体中文免费版";
|
|
const std::string wire = to_wire(in, 24);
|
|
CHECK(in.size() == 27, "target Chinese name is 27 UTF-8 bytes");
|
|
CHECK(wire.size() == 18 && mtnet::wire_text_fits(in, 24),
|
|
"target Chinese name fits the 24-byte GB2312 field");
|
|
CHECK(from_wire_str(wire.data(), wire.size()) == in,
|
|
"target Chinese name round-trips through the 40250 wire code page");
|
|
}
|
|
|
|
// Decode legacy bytes up to NUL.
|
|
{
|
|
char buf[25] = {};
|
|
const std::string legacy = "\xBA\xBA\xBA\xBA\xBA\xBA";
|
|
std::memcpy(buf, legacy.data(), legacy.size());
|
|
CHECK(from_wire_str(buf, sizeof(buf) - 1) == cjk(3), "decodes GB2312 up to NUL");
|
|
}
|
|
{
|
|
char buf[25] = {};
|
|
const std::string legacy = to_wire(cjk(12), 24);
|
|
std::memcpy(buf, legacy.data(), legacy.size());
|
|
buf[24] = '\xBA'; // dangling lead byte at the field boundary
|
|
CHECK(from_wire_str(buf, 25) == cjk(12), "dangling legacy lead byte is ignored");
|
|
}
|
|
{
|
|
// Full 24-byte field, no NUL terminator: read all twelve characters.
|
|
char buf[24];
|
|
const std::string legacy = to_wire(cjk(12), 24);
|
|
std::memcpy(buf, legacy.data(), legacy.size());
|
|
CHECK(from_wire_str(buf, 24) == cjk(12), "unterminated full field read whole");
|
|
}
|
|
|
|
if (g_fail) {
|
|
std::fprintf(stderr, "%d checks FAILED\n", g_fail);
|
|
return 1;
|
|
}
|
|
std::puts("net_text_codec_test: OK");
|
|
return 0;
|
|
}
|