Add animation sync docs and descriptor infra
Add detailed design and implementation scaffolding for TiltedEvolution-style animation synchronization. New documentation: animation-architecture-alignment.md, animation-sync-analysis.md, f4-animation-descriptor.md, phase1-3-completion-report.md and updates to dev-log.md describing Phase 1-3 progress. Plugin: introduce F4AnimationDescriptor (plugin/include/F4AnimationDescriptor.h, plugin/src/F4AnimationDescriptor.cpp) and extend remote state handling (plugin/include/F4TRemotePlayerState.h). Integrate protocol/state extensions and refactor points: plugin/src/F4TNetworking.cpp now parses optional actorStateFlags and actionEvents; proxy controller and animation sync files (F4TProxyActorController.cpp, F4TProxyAnimationSync.cpp) updated to support descriptor-based bulk variable snapshots and actor state replication. Server/tools: update server/dev_server_app.py and server/fake_client.py to handle and display the new optional fields. These changes are additive and backward-compatible and set up phases for action capture, dynamic proxy spawn, and action-replay integration.
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "RE/I/IAnimationGraphManagerHolder.h"
|
||||
|
||||
namespace F4T::AnimationDescriptor
|
||||
{
|
||||
// Forward declarations
|
||||
struct AnimationVariableSnapshot;
|
||||
class F4AnimationDescriptor;
|
||||
|
||||
// Variable type enumeration
|
||||
enum class VariableType : std::uint8_t
|
||||
{
|
||||
Float,
|
||||
Bool,
|
||||
Int,
|
||||
};
|
||||
|
||||
// Snapshot of animation graph variables (used for action replay)
|
||||
struct AnimationVariableSnapshot
|
||||
{
|
||||
std::vector<float> floats;
|
||||
std::vector<bool> bools;
|
||||
std::vector<std::uint32_t> ints;
|
||||
};
|
||||
|
||||
// Animation descriptor for a specific graph type (humanoid, creature, etc.)
|
||||
// Uses indexed variable cache read/write instead of string-based lookups
|
||||
// This matches TiltedEvolution's descriptor model for optimal performance
|
||||
class F4AnimationDescriptor
|
||||
{
|
||||
public:
|
||||
// Get the humanoid master behavior graph descriptor (most common)
|
||||
static const F4AnimationDescriptor& GetHumanoidDescriptor();
|
||||
|
||||
// Prevent copies (singleton pattern)
|
||||
F4AnimationDescriptor(const F4AnimationDescriptor&) = delete;
|
||||
F4AnimationDescriptor& operator=(const F4AnimationDescriptor&) = delete;
|
||||
|
||||
// Get variable names by index
|
||||
const std::string& GetFloatVariableName(std::size_t a_index) const;
|
||||
const std::string& GetBoolVariableName(std::size_t a_index) const;
|
||||
const std::string& GetIntVariableName(std::size_t a_index) const;
|
||||
|
||||
// Get index by variable name (for reverse lookup)
|
||||
std::size_t GetFloatVariableIndex(const std::string& a_name) const;
|
||||
std::size_t GetBoolVariableIndex(const std::string& a_name) const;
|
||||
std::size_t GetIntVariableIndex(const std::string& a_name) const;
|
||||
|
||||
// Check if variable exists
|
||||
bool HasFloatVariable(const std::string& a_name) const;
|
||||
bool HasBoolVariable(const std::string& a_name) const;
|
||||
bool HasIntVariable(const std::string& a_name) const;
|
||||
|
||||
// Bulk read animation variables from actor's graph cache
|
||||
// Locks the graph manager during read
|
||||
bool SaveAnimationVariablesFromCache(
|
||||
RE::IAnimationGraphManagerHolder& a_holder,
|
||||
AnimationVariableSnapshot& a_snapshot) const;
|
||||
|
||||
// Bulk write animation variables to actor's graph cache
|
||||
// Locks the graph manager during write
|
||||
bool LoadAnimationVariablesToCache(
|
||||
RE::IAnimationGraphManagerHolder& a_holder,
|
||||
const AnimationVariableSnapshot& a_snapshot) const;
|
||||
|
||||
// Get counts of each variable type
|
||||
std::size_t GetFloatVariableCount() const { return m_floatVariables.size(); }
|
||||
std::size_t GetBoolVariableCount() const { return m_boolVariables.size(); }
|
||||
std::size_t GetIntVariableCount() const { return m_intVariables.size(); }
|
||||
|
||||
private:
|
||||
// Private constructor (singleton)
|
||||
F4AnimationDescriptor() = default;
|
||||
|
||||
// Variable name tables (indices match animation system's variable cache)
|
||||
std::vector<std::string> m_floatVariables;
|
||||
std::vector<std::string> m_boolVariables;
|
||||
std::vector<std::string> m_intVariables;
|
||||
|
||||
// Reverse lookup tables (name → index) for faster search
|
||||
std::unordered_map<std::string, std::size_t> m_floatVariableIndexMap;
|
||||
std::unordered_map<std::string, std::size_t> m_boolVariableIndexMap;
|
||||
std::unordered_map<std::string, std::size_t> m_intVariableIndexMap;
|
||||
|
||||
// Initialize humanoid descriptor tables
|
||||
// Called once at startup; contains FO4-specific graph variable indices
|
||||
void InitializeHumanoidVariables();
|
||||
|
||||
// Helper: Try to read a variable by index from graph cache
|
||||
bool TryReadFloatVariable(
|
||||
RE::IAnimationGraphManagerHolder& a_holder,
|
||||
std::size_t a_index,
|
||||
float& a_outValue) const;
|
||||
|
||||
bool TryReadBoolVariable(
|
||||
RE::IAnimationGraphManagerHolder& a_holder,
|
||||
std::size_t a_index,
|
||||
bool& a_outValue) const;
|
||||
|
||||
bool TryReadIntVariable(
|
||||
RE::IAnimationGraphManagerHolder& a_holder,
|
||||
std::size_t a_index,
|
||||
std::uint32_t& a_outValue) const;
|
||||
|
||||
// Helper: Try to write a variable by index to graph cache
|
||||
bool TryWriteFloatVariable(
|
||||
RE::IAnimationGraphManagerHolder& a_holder,
|
||||
std::size_t a_index,
|
||||
float a_value) const;
|
||||
|
||||
bool TryWriteBoolVariable(
|
||||
RE::IAnimationGraphManagerHolder& a_holder,
|
||||
std::size_t a_index,
|
||||
bool a_value) const;
|
||||
|
||||
bool TryWriteIntVariable(
|
||||
RE::IAnimationGraphManagerHolder& a_holder,
|
||||
std::size_t a_index,
|
||||
std::uint32_t a_value) const;
|
||||
|
||||
// Friend for logging
|
||||
friend class F4TProxyAnimationSync;
|
||||
};
|
||||
}
|
||||
@@ -8,6 +8,20 @@
|
||||
|
||||
namespace F4T::RemotePlayerState
|
||||
{
|
||||
// Represents a remote action event captured from another player
|
||||
// Used for action replay on proxy actors (combat idles, equipping, etc.)
|
||||
struct RemoteActionEvent
|
||||
{
|
||||
std::uint32_t type{}; // Action type (see ActorMediator::ActorAction enum)
|
||||
std::string eventName; // Action event name (e.g., "ActorMovementStart")
|
||||
std::vector<float> animationVariablesFloat; // Float variables snapshot (Speed, Direction, etc.)
|
||||
std::vector<bool> animationVariablesBool; // Bool variables snapshot (IsSprinting, IsSneaking, etc.)
|
||||
std::vector<std::uint32_t> animationVariablesInt; // Int variables snapshot (weapon types, etc.)
|
||||
std::uint32_t actorStateFlags1{}; // ActorState.flags1 at action time
|
||||
std::uint32_t actorStateFlags2{}; // ActorState.flags2 at action time
|
||||
std::chrono::steady_clock::time_point captureTime{};
|
||||
};
|
||||
|
||||
struct RemotePlayerState
|
||||
{
|
||||
std::uint32_t playerId{};
|
||||
@@ -27,6 +41,11 @@ namespace F4T::RemotePlayerState
|
||||
bool weaponDrawn{ false };
|
||||
float movementSpeed{ 0.0F };
|
||||
std::chrono::steady_clock::time_point lastReceivedLocalTime{};
|
||||
|
||||
// Phase 2: Actor state and action events (TiltedEvolution alignment)
|
||||
std::uint32_t actorStateFlags1{}; // ActorState.flags1 (combat, sneaking, etc.)
|
||||
std::uint32_t actorStateFlags2{}; // ActorState.flags2 (additional state flags)
|
||||
std::vector<RemoteActionEvent> actionEvents; // Queue of pending action events
|
||||
};
|
||||
|
||||
void SetAssignedPlayerId(std::uint32_t a_playerId);
|
||||
|
||||
Reference in New Issue
Block a user