2V3-a: walk with the keyboard through the ported player chain
PythonPlayer, PythonPlayerInput{,Keyboard,Mouse}, PythonPlayerEventHandler,
PythonPlayerSkill, PythonPlayerModule and GameType are verbatim copies; the
`player` stub module and the pending PythonPlayer/GameType stand-ins are gone.
Unported callees (CPythonSkill, CItemData, CItemManager, CPythonItem, ...) are
pending stubs. 64-bit fixes: emotion icon handles use Py_BuildPointer, and
PyDict_Next takes Py_ssize_t.
The actor did not move because GrannyUpdateModelMatrix passed the model matrix
through. Walk/run motions use constant root-motion extraction
(AccumulationFlags=AccumulationExtracted, LoopTranslation e.g. (0,-300,0) for
the warrior run, same as the .msa Accumulation). libgr2 now reads both fields
from TrackGroups[0], and the runtime moves by LoopTranslation over each
control's local-clock span, blended by control weight. VDA extraction is not
implemented.
port.login_flow presses DIK_UP; the actor walks more than 100 cm, and the fake
server parses CG packets and sees CG_MOVE FUNC_MOVE, then FUNC_WAIT at the new
position. port.login_live against the real server walked 132 cm.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
157f7c3bb4
commit
ff0983d3c8
@@ -509,9 +509,46 @@ bool FakeLoginServer::ServeGame(Connection& c, int index)
|
||||
return Fail(name + ": CG_PONG after the entergame burst: " + error);
|
||||
Event(name + ":burst_pong");
|
||||
|
||||
// Hold the connection until the test stops us.
|
||||
std::uint8_t byte = 0;
|
||||
while (!m_stop && c.Read(&byte, 1) == 1)
|
||||
;
|
||||
return true;
|
||||
// Then hold the connection until the test stops us, reading what the game phase sends the way
|
||||
// CInputProcessor::Process frames it (static size or TDynamicSizePacketHeader, then the sequence
|
||||
// byte). A CG_MOVE (input_main.cpp CInputMain::Move) becomes "<name>:move <func> <arg> <rot> <x> <y>".
|
||||
for (;;)
|
||||
{
|
||||
std::uint8_t packet[1024] = {};
|
||||
if (c.Read(packet, 1) != 1)
|
||||
return true;
|
||||
const std::uint8_t header = packet[0];
|
||||
size_t size = static_cast<size_t>(packet_size_cg(header));
|
||||
if (is_dynamic_cg(header) || header == HDR_CG_SYNC_POSITION)
|
||||
{
|
||||
if (c.Read(packet + 1, sizeof(DynHead) - 1) != 1)
|
||||
return true;
|
||||
DynHead head;
|
||||
std::memcpy(&head, packet, sizeof(head));
|
||||
size = head.size;
|
||||
if (size < sizeof(DynHead) || size > sizeof(packet) ||
|
||||
(size > sizeof(DynHead) && c.Read(packet + sizeof(DynHead), size - sizeof(DynHead)) != 1))
|
||||
return Fail(name + ": dynamic CG packet " + std::to_string(header) + " in the game phase");
|
||||
}
|
||||
else if (size == 0 || size > sizeof(packet))
|
||||
return Fail(name + ": unknown CG header " + std::to_string(header) + " in the game phase");
|
||||
else if (size > 1 && c.Read(packet + 1, size - 1) != 1)
|
||||
return true;
|
||||
if (is_sequence_cg(header))
|
||||
{
|
||||
std::uint8_t sequence = 0;
|
||||
if (c.Read(&sequence, 1) != 1)
|
||||
return true;
|
||||
if (sequence != SEQUENCE_TABLE[c.sequence])
|
||||
return Fail(name + ": SEQUENCE mismatch on CG header " + std::to_string(header));
|
||||
c.sequence = (c.sequence + 1) % SEQUENCE_TABLE_SIZE;
|
||||
}
|
||||
if (header == HDR_CG_MOVE)
|
||||
{
|
||||
CGMove move;
|
||||
std::memcpy(&move, packet, sizeof(move));
|
||||
Event(name + ":move " + std::to_string(move.func) + " " + std::to_string(move.arg) + " " +
|
||||
std::to_string(move.rot) + " " + std::to_string(move.x) + " " + std::to_string(move.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user