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:
shenlei
2026-09-23 23:41:31 +09:00
co-authored by Claude Opus 5.5
parent 157f7c3bb4
commit ff0983d3c8
39 changed files with 10170 additions and 2039 deletions
+4
View File
@@ -131,6 +131,10 @@ struct Animation {
float oversampling = 1.0f;
int32_t track_group_count = 0;
std::vector<TextTrack> text_tracks; // TrackGroups[0].TextTracks
// TrackGroups[0].AccumulationFlags / LoopTranslation(root motion:
// 1 = AccumulationExtracted,4 = AccumulationIsVDA)。
int32_t accumulation_flags = 0;
float loop_translation[3] = {0.0f, 0.0f, 0.0f};
std::vector<BoneTrack> tracks;
// t 处每个 track 的局部 4x4(granny_transform 组合语义,行主序)。
// out 按 tracks 顺序;bone_index 见各 track。
+11
View File
@@ -109,6 +109,17 @@ bool File::Impl::extract_animation(Ref anim_obj, Ref anim_type, const Skeleton*
out.track_group_count = int32_t(groups.size());
const auto& tgms = parse_type(mTG->ref_type);
// TrackGroups[0].AccumulationFlags (Int32) + LoopTranslation (Real32[3])
if (!groups.empty() && groups[0].valid()) {
if (const TypeMember* m = find_member(tgms, "AccumulationFlags"))
out.accumulation_flags = rd_i32(member_slot(groups[0], *m));
if (const TypeMember* m = find_member(tgms, "LoopTranslation")) {
Ref r = member_slot(groups[0], *m);
for (int i = 0; i < 3; ++i)
out.loop_translation[i] = rd_f32(Ref{r.section, r.offset + uint32_t(i) * 4u});
}
}
// TrackGroups[0].TextTracks: RefToArray text_track{String Name; RefToArray Entries(
// text_track_entry{Real32 TimeStamp; String Text})}(motion event 用)
if (!groups.empty() && groups[0].valid())