port: complete 100% logic layer, eliminate pending dir, and unify client_main on port route
- Completed batch 4-f (Messenger, Guild, GuildMark), 4-g (PythonSystem, systemSetting), 4-h (PythonSoundManagerModule, MilesLib CSoundManager) - Ported UserInterface/Locale.cpp, EterLocale/Japanese.cpp, EterLib/PathStack, EterLib/NetDatagram* UDP suite - Modularized PythonApplication into Camera and Event handlers aligning with 40250 signatures - Completely removed extension/src/platform/pending/ directory - Logic layer reached 100.0% completion (3338/3338 functions, 0 TODO, 0 DIVERGENT) with empty port-map queue - Removed legacy AppFlow route and fallback from project/client_main.gd, switching main loop unconditionally to native port route - Verified 15/15 port.login_flow integration steps and 19/19 port_gate suites passing
This commit is contained in:
@@ -616,6 +616,69 @@ bool FakeLoginServer::ServeGame(Connection& c, int index)
|
||||
elder_update.vid = kQuestVID;
|
||||
if (!c.Send(elder) || !c.Send(elder_info) || !c.Send(elder_update))
|
||||
return Fail(name + ": send quest NPC");
|
||||
GCCharacterAdd storekeeper = keeper;
|
||||
storekeeper.vid = kSafeboxVID;
|
||||
storekeeper.x = kSafeboxX;
|
||||
storekeeper.y = kSafeboxY;
|
||||
storekeeper.race = kSafeboxRace;
|
||||
GCCharAddInfo storekeeper_info = zeroed<GCCharAddInfo>();
|
||||
storekeeper_info.header = HDR_GC_CHAR_ADDITIONAL_INFO;
|
||||
storekeeper_info.vid = kSafeboxVID;
|
||||
std::strncpy(storekeeper_info.name, "Storekeeper", sizeof(storekeeper_info.name) - 1);
|
||||
GCCharacterUpdate storekeeper_update = keeper_update;
|
||||
storekeeper_update.vid = kSafeboxVID;
|
||||
if (!c.Send(storekeeper) || !c.Send(storekeeper_info) || !c.Send(storekeeper_update))
|
||||
return Fail(name + ": send storekeeper NPC");
|
||||
|
||||
// Messenger (批次 4-f): Send initial friend list
|
||||
struct
|
||||
{
|
||||
GCMessengerHead head;
|
||||
MessengerListEntry entry;
|
||||
char name[10];
|
||||
} __attribute__((packed)) messenger_pkt = {
|
||||
{HDR_GC_MESSENGER, static_cast<std::uint16_t>(sizeof(messenger_pkt)), MESSENGER_GC_LIST},
|
||||
{1 /* MESSENGER_CONNECTED_STATE_ONLINE */, static_cast<std::uint8_t>(std::strlen(kFriendName))},
|
||||
{}
|
||||
};
|
||||
std::memcpy(messenger_pkt.name, kFriendName, sizeof(messenger_pkt.name));
|
||||
if (!c.Send(messenger_pkt))
|
||||
return Fail(name + ": send messenger list");
|
||||
|
||||
// Guild (批次 4-f): Send guild name
|
||||
struct
|
||||
{
|
||||
GCMessengerHead head;
|
||||
GuildName16 item;
|
||||
} __attribute__((packed)) guild_name_pkt = {
|
||||
{HDR_GC_GUILD, static_cast<std::uint16_t>(sizeof(guild_name_pkt)), 16 /* GUILD_GC_NAME */},
|
||||
{kGuildID, {}}
|
||||
};
|
||||
std::strncpy(guild_name_pkt.item.name, kGuildName, sizeof(guild_name_pkt.item.name));
|
||||
if (!c.Send(guild_name_pkt))
|
||||
return Fail(name + ": send guild name");
|
||||
|
||||
// Guild (批次 4-f): Send guild info (enables guild for player)
|
||||
struct
|
||||
{
|
||||
GCMessengerHead head;
|
||||
GuildInfo35 info;
|
||||
} __attribute__((packed)) guild_info_pkt = {
|
||||
{HDR_GC_GUILD, static_cast<std::uint16_t>(sizeof(guild_info_pkt)), 8 /* GUILD_GC_INFO */},
|
||||
zeroed<GuildInfo35>()
|
||||
};
|
||||
guild_info_pkt.info.guild_id = kGuildID;
|
||||
guild_info_pkt.info.master_pid = 1;
|
||||
guild_info_pkt.info.level = 20;
|
||||
guild_info_pkt.info.exp = 50000;
|
||||
guild_info_pkt.info.member_count = 1;
|
||||
guild_info_pkt.info.max_member_count = 50;
|
||||
guild_info_pkt.info.gold = 123456;
|
||||
guild_info_pkt.info.has_land = 1;
|
||||
std::strncpy(guild_info_pkt.info.name, kGuildName, sizeof(guild_info_pkt.info.name) - 1);
|
||||
if (!c.Send(guild_info_pkt))
|
||||
return Fail(name + ": send guild info");
|
||||
|
||||
Event(name + ":burst_sent");
|
||||
|
||||
// A ping after the burst proves that GamePhase consumed every packet above and can reply.
|
||||
@@ -633,6 +696,9 @@ bool FakeLoginServer::ServeGame(Connection& c, int index)
|
||||
std::int32_t gold = kStartGold;
|
||||
std::uint32_t bag0 = 0; // vnum in inventory cell 0 (the fake character starts with an empty bag)
|
||||
int quest_page = 0; // the quest dialogue's last page sent (1 question, 2 next, 3 done)
|
||||
bool safebox_open = false;
|
||||
std::uint32_t safebox0 = kSafeboxItemVnum;
|
||||
std::int32_t safebox_gold = kSafeboxMoney;
|
||||
// CQuestManager::SendScript: packet_script {header, size, skin, src_size} and the script text.
|
||||
auto send_script = [&](const std::string& script) {
|
||||
struct
|
||||
@@ -743,11 +809,44 @@ bool FakeLoginServer::ServeGame(Connection& c, int index)
|
||||
const std::string text(reinterpret_cast<const char*>(packet + sizeof(head)),
|
||||
strnlen(reinterpret_cast<const char*>(packet + sizeof(head)), text_len));
|
||||
Event(name + ":chat " + std::to_string(head.type) + " " + text);
|
||||
const std::string line = std::string(kCharacterName) + " : " + text;
|
||||
GCChatHead chat = {HDR_GC_CHAT, static_cast<std::uint16_t>(sizeof(GCChatHead) + line.size() + 1),
|
||||
0 /* CHAT_TYPE_TALKING */, kMainVID, 1};
|
||||
if (!c.Send(chat) || !c.Send(line.c_str(), line.size() + 1))
|
||||
return Fail(name + ": send GC_CHAT");
|
||||
if (text.rfind("/safebox_password ", 0) == 0)
|
||||
{
|
||||
const std::string pass = text.substr(18);
|
||||
Event(name + ":safebox_password " + pass);
|
||||
safebox_open = true;
|
||||
GCSafeboxSize size_pkt = {HDR_GC_SAFEBOX_SIZE, FakeLoginServer::kSafeboxSize};
|
||||
GCSafeboxMoneyChange money_pkt = {HDR_GC_SAFEBOX_MONEY_CHANGE, safebox_gold};
|
||||
if (!c.Send(size_pkt) || !c.Send(money_pkt))
|
||||
return Fail(name + ": send GC_SAFEBOX_SIZE / MONEY");
|
||||
if (safebox0 != 0)
|
||||
{
|
||||
GCItemSet set = zeroed<GCItemSet>();
|
||||
set.header = HDR_GC_SAFEBOX_SET;
|
||||
set.cell = {3 /* SAFEBOX */, 0};
|
||||
set.vnum = safebox0;
|
||||
set.count = 1;
|
||||
if (!c.Send(set))
|
||||
return Fail(name + ": send GC_SAFEBOX_SET");
|
||||
}
|
||||
}
|
||||
else if (text == "/safebox_close")
|
||||
{
|
||||
Event(name + ":safebox_close");
|
||||
safebox_open = false;
|
||||
const std::string cmd = "CloseSafebox";
|
||||
GCChatHead chat = {HDR_GC_CHAT, static_cast<std::uint16_t>(sizeof(GCChatHead) + cmd.size() + 1),
|
||||
5 /* CHAT_TYPE_COMMAND */, 0, 1};
|
||||
if (!c.Send(chat) || !c.Send(cmd.c_str(), cmd.size() + 1))
|
||||
return Fail(name + ": send CloseSafebox");
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string line = std::string(kCharacterName) + " : " + text;
|
||||
GCChatHead chat = {HDR_GC_CHAT, static_cast<std::uint16_t>(sizeof(GCChatHead) + line.size() + 1),
|
||||
0 /* CHAT_TYPE_TALKING */, kMainVID, 1};
|
||||
if (!c.Send(chat) || !c.Send(line.c_str(), line.size() + 1))
|
||||
return Fail(name + ": send GC_CHAT");
|
||||
}
|
||||
}
|
||||
// CInputMain::Target → CHARACTER::SetTarget → BroadcastTargetPacket: GC_TARGET with the monster's HP percent.
|
||||
if (header == HDR_CG_TARGET)
|
||||
@@ -786,6 +885,14 @@ bool FakeLoginServer::ServeGame(Connection& c, int index)
|
||||
CGOnClick click;
|
||||
std::memcpy(&click, packet, sizeof(click));
|
||||
Event(name + ":click " + std::to_string(click.vid));
|
||||
if (click.vid == kSafeboxVID)
|
||||
{
|
||||
const std::string cmd = "ShowMeSafeboxPassword";
|
||||
GCChatHead chat = {HDR_GC_CHAT, static_cast<std::uint16_t>(sizeof(GCChatHead) + cmd.size() + 1),
|
||||
5 /* CHAT_TYPE_COMMAND */, 0, 1};
|
||||
if (!c.Send(chat) || !c.Send(cmd.c_str(), cmd.size() + 1))
|
||||
return Fail(name + ": send ShowMeSafeboxPassword");
|
||||
}
|
||||
// CHARACTER::OnClick → CQuestManager::Click: say(...) then select("Yes", "No") → GotoSelectState.
|
||||
if (click.vid == kQuestVID && !send_script("Greetings, young warrior.[ENTER]Shall I tell you a story?[ENTER][QUESTION 1;Yes|2;No]"))
|
||||
return Fail(name + ": send GC_SCRIPT question");
|
||||
@@ -881,6 +988,46 @@ bool FakeLoginServer::ServeGame(Connection& c, int index)
|
||||
if (!ok)
|
||||
return Fail(name + ": send shop reply");
|
||||
}
|
||||
if (header == HDR_CG_SAFEBOX_CHECKIN)
|
||||
{
|
||||
CGSafeboxCheckin in;
|
||||
std::memcpy(&in, packet, sizeof(in));
|
||||
Event(name + ":safebox_checkin " + std::to_string(in.item_pos.cell) + " -> " + std::to_string(in.safe_pos));
|
||||
if (in.item_pos.cell == 0 && in.safe_pos == 0 && bag0 != 0)
|
||||
{
|
||||
safebox0 = bag0;
|
||||
bag0 = 0;
|
||||
GCItemDelDeprecated del = zeroed<GCItemDelDeprecated>();
|
||||
del.header = HDR_GC_ITEM_DEL;
|
||||
del.cell = {1, 0};
|
||||
GCItemSet set = zeroed<GCItemSet>();
|
||||
set.header = HDR_GC_SAFEBOX_SET;
|
||||
set.cell = {3 /* SAFEBOX */, 0};
|
||||
set.vnum = safebox0;
|
||||
set.count = 1;
|
||||
if (!c.Send(del) || !c.Send(set))
|
||||
return Fail(name + ": send safebox checkin replies");
|
||||
}
|
||||
}
|
||||
if (header == HDR_CG_SAFEBOX_CHECKOUT)
|
||||
{
|
||||
CGSafeboxCheckout out;
|
||||
std::memcpy(&out, packet, sizeof(out));
|
||||
Event(name + ":safebox_checkout " + std::to_string(out.safe_pos) + " -> " + std::to_string(out.item_pos.cell));
|
||||
if (out.safe_pos == 0 && out.item_pos.cell == 0 && safebox0 != 0)
|
||||
{
|
||||
bag0 = safebox0;
|
||||
safebox0 = 0;
|
||||
GCItemDel del = {HDR_GC_SAFEBOX_DEL, 0};
|
||||
GCItemSet set = zeroed<GCItemSet>();
|
||||
set.header = HDR_GC_ITEM_SET;
|
||||
set.cell = {1, 0};
|
||||
set.vnum = bag0;
|
||||
set.count = 1;
|
||||
if (!c.Send(del) || !c.Send(set))
|
||||
return Fail(name + ": send safebox checkout replies");
|
||||
}
|
||||
}
|
||||
if (header == HDR_CG_MOVE)
|
||||
{
|
||||
CGMove move;
|
||||
|
||||
Reference in New Issue
Block a user