mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-29 03:46:50 +01:00
skater.pose changes the skater relative to the game's animation. Some of those changes were applied twice every third frame, so a posed skater shook in a three-frame rhythm, and their board with them. The more a plugin changed, the worse it got: an avatar with the hips scaled and offsets on most of the body needed workarounds in its plugin to hold still. Now every pose is applied exactly once, and a plugin can give skater.pose the values it means. Why: ReSkate draws a pose by writing it into the game's bone buffer just before the render, and the game evaluates the next frame from that same buffer without overwriting all of it. Two of three frames it resets the buffer itself; in the third the pose drawn was still there and the next pose went on top of it. Now the game's own values go back as soon as the renderer has its copy, so the game, the body physics and the next animation update only ever see the game's pose. This replaces own_scale, the earlier fix for the same cause (the Filmer's forearm at 0.001 shrinking to nothing). It covered only scale, and put it back after the game had already run on the posed values, so rotations and offsets still doubled. The new way covers every part of every joint, for pose, carry and reach, with nothing to track per joint and nothing to clean up after a pose ends. Checked in the game: the avatar with the hips at 0.667 and offsets on 48 joints holds still with only skater.pose (shoulder distance 0.117 m every frame, where it was 0.117 0.117 0.067 ...). The Filmer 1.0.0's camcorder preview looks and measures as on main, and the skater is back to normal after it. Details: the renderer copies the bones in submit_render_pose (0x4c7e7a0), which the render pose caller (0x2c20d99) calls right after render_pose with the same render data; found with a guard page on the bone buffer that logged every access with its call stack. native_skater hooks it and, for the render data the pose override just wrote, calls the new set_pose_override_drawn_listener. skater_animation keeps what on_render wrote over and puts it back in on_drawn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
97 lines
6.1 KiB
C++
97 lines
6.1 KiB
C++
#pragma once
|
|
#include "Extension/Multiplayer/Net/protocol.h"
|
|
|
|
namespace dingosdk::multiplayer {
|
|
struct NativeFrame {
|
|
bool ready{};
|
|
std::uintptr_t context{}, parent{}, entity{}, board_entity{};
|
|
Pose pose;
|
|
std::string detail;
|
|
};
|
|
struct NativeAnimationStats {
|
|
std::uint64_t evaluated{}, skipped{}, evaluation_us{}, apply_us{};
|
|
};
|
|
// All entry points except model access run on the verified client update thread.
|
|
// Identity/root capture remains per client callback. Full rig buffers are only
|
|
// needed when the network send deadline is due.
|
|
NativeFrame capture_local(std::uintptr_t base, std::uintptr_t client, bool capture_pose);
|
|
NativeFrame capture_local(std::uintptr_t base, std::uintptr_t client);
|
|
bool show_remote(std::uintptr_t base, std::uintptr_t client, const NativeFrame &, const Pose &,
|
|
std::string &detail);
|
|
void remove_remote(std::uintptr_t base) noexcept;
|
|
void update_remote_cosmetics(std::uintptr_t base, const NativeFrame &, const Appearance &,
|
|
std::string &detail);
|
|
std::uint64_t remote_pose_updates() noexcept;
|
|
std::uint64_t remote_board_pose_updates() noexcept;
|
|
NativeAnimationStats remote_animation_stats() noexcept;
|
|
std::uintptr_t remote_skater_entity() noexcept;
|
|
std::uintptr_t remote_board_entity() noexcept;
|
|
// Changes whenever the current slot's actor is created, removed or destroyed.
|
|
std::uint64_t remote_skater_generation() noexcept;
|
|
// Client thread. Installs the shared animation/destruction hooks without
|
|
// creating a remote actor, for other features that create native actors.
|
|
bool install_entity_hooks(std::uintptr_t base, std::string &detail) noexcept;
|
|
// Called by the destruction hook before the engine frees any entity.
|
|
using EntityDestroyed = void (*)(std::uintptr_t entity) noexcept;
|
|
void set_entity_destroyed_listener(EntityDestroyed listener) noexcept;
|
|
// Called after the native animation update of every skater component that is
|
|
// not a remote peer, once its output pose for this update is written. Requires
|
|
// install_entity_hooks. Runs on the thread that evaluates animation.
|
|
using AnimationEvaluated = void (*)(std::uintptr_t component) noexcept;
|
|
void set_animation_evaluated_listener(AnimationEvaluated listener) noexcept;
|
|
// Runs on the client thread with the pose capture_local is about to return: a feature may change
|
|
// what other players receive of the local skater.
|
|
using LocalPoseFilter = void (*)(std::uintptr_t component, std::vector<Transform> &skater) noexcept;
|
|
void set_local_pose_filter(LocalPoseFilter filter) noexcept;
|
|
// Then, with the whole pose capture_local returns (the skater and the board), so a change to what is
|
|
// drawn of the board (Extension/Skater/board_pose.h, a bone hidden from everyone) is in what the
|
|
// others see too. One listener; nullptr for none. Client thread.
|
|
using LocalSentPoseFilter = void (*)(std::uintptr_t component, Pose &pose) noexcept;
|
|
void set_local_sent_pose_filter(LocalSentPoseFilter filter) noexcept;
|
|
// A pose override of the local skater (Extension/Skater/skater_animation.cpp): called after the
|
|
// listener above, so it sees (and may change) the pose first person left.
|
|
void set_pose_override_listener(AnimationEvaluated listener) noexcept;
|
|
// Called after a skeleton's pose is handed to the renderer, with
|
|
// its animation interface (animation holder + 0xc0), on the calling thread.
|
|
using RenderPosePublished = void (*)(std::uintptr_t animation_interface) noexcept;
|
|
void set_render_pose_listener(RenderPosePublished listener) noexcept;
|
|
// The pose override again, when a pose is handed to the renderer (after the listener above): what it
|
|
// writes then is what is drawn. And for the board, before the listener above, with the render data
|
|
// the pose was handed over in.
|
|
void set_pose_override_render_listener(RenderPosePublished listener) noexcept;
|
|
// The pose override once more, when the renderer has copied that pose (submit_render_pose, right
|
|
// after): what it put in the bone buffer for the render can go again, so that what runs until the next
|
|
// render (the game's own passes over the buffer, the next animation update) reads only the game's pose.
|
|
void set_pose_override_drawn_listener(RenderPosePublished listener) noexcept;
|
|
using BoardPosePublished = void (*)(std::uintptr_t animation_interface, std::uintptr_t render_data) noexcept;
|
|
void set_pose_override_board_listener(BoardPosePublished listener) noexcept;
|
|
// Far-player savings (puppet_cost.cpp): native work a remote player's skater and skateboard
|
|
// skip while far away. Within 200 m of the local skater or the camera nothing changes.
|
|
// Each is on by default; the setter is thread-safe and applies at each player's next
|
|
// note_remote_distance.
|
|
enum class PuppetSaving : std::uint8_t {
|
|
// The skater's AntDriven component no longer re-places the entity every frame from its pose;
|
|
// the network placement (place_actor) is the only one. Any distance.
|
|
driven_placement,
|
|
// The ECS attach that never completes on a remote skater or skateboard is retried once a
|
|
// second instead of every frame. Any distance.
|
|
ecs_retry,
|
|
// Past 200 m the crowd-avoidance agent, AI stimulus and pedestrian collider update 4 times a
|
|
// second with the elapsed game time, instead of every frame.
|
|
far_components,
|
|
};
|
|
void set_puppet_saving(PuppetSaving saving, bool enabled) noexcept;
|
|
bool puppet_saving(PuppetSaving saving) noexcept;
|
|
// Client thread, for the current PeerScope slot, after show_remote: the player's distance in
|
|
// metres to the nearer of the local skater and the camera (non-finite = unknown, treated as
|
|
// near). Call it whenever the player's pose is sampled.
|
|
void note_remote_distance(float metres) noexcept;
|
|
// One console line: which savings are on and how many players each currently applies to.
|
|
std::string puppet_saving_status();
|
|
// First person shrinks one joint (the head) of the local skater's evaluated
|
|
// pose so it is not drawn across the view. Poses sent to other players carry
|
|
// the joint's real scale instead. component 0 ends the override.
|
|
void set_local_hidden_joint(std::uintptr_t component, std::uint16_t joint,
|
|
const std::array<float, 3> &scale) noexcept;
|
|
} // namespace dingosdk::multiplayer
|