Files
Commonwealth-Online-Public/plugin/include/F4TRemotePlayerState.h
T
andrew d328bfb647 Add complexionFormId to appearance payload
Introduce complexionFormId (TXST) into RemoteAppearance and the appearance protocol so complexion textures are captured and serialized. Updates: header/state, Networking JSON parse/serialize, proxy actor comparisons, main capture logic, protocol docs, fake client/player samples, changelog and dev-log. Capture reads PlayerCharacter::complexion and serializes it, but applying to proxies is intentionally a no-op because TESNPC proxies lack a complexion field (documented in comments/dev-log). Files: plugin/include/F4TRemotePlayerState.h, plugin/src/*, protocol/packets.md, server/fake_client.py, server/fake_player.py, changelog.md, docs/dev-log.md.
2026-06-30 22:20:44 +12:00

135 lines
4.1 KiB
C++

#pragma once
#include <chrono>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
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 RemoteEquippedItem
{
std::string slot;
std::uint32_t formId{};
};
struct RemoteAppearanceMorph
{
std::uint32_t id{};
float value{};
};
struct RemoteAppearanceTintColor
{
std::uint8_t r{};
std::uint8_t g{};
std::uint8_t b{};
std::uint8_t a{};
};
struct RemoteAppearanceMorphWeight
{
float thin{};
float muscular{};
float large{};
};
// Bone-based facial morph (TESNPC::facialBoneRegionSliderValues entry).
// Mirrors BGSCharacterMorph::Transform (position, rotation, scale).
struct RemoteAppearanceFacialBoneMorph
{
std::uint32_t id{};
float position[3]{};
float rotation[3]{};
float scale[3]{ 1.0F, 1.0F, 1.0F };
};
// Character tint layer (TESNPC/PlayerCharacter tintingData entry).
// Drives skin tone, complexion, makeup, dirt, scars, and beard shade.
struct RemoteAppearanceTint
{
std::uint16_t id{}; // idLink, matches a race tint template uniqueID
std::uint32_t type{}; // BGSCharacterTint::EntryType (mask/palette/texture)
std::uint8_t value{}; // tingingValue intensity (0-255)
std::uint32_t color{}; // ARGB packed color (palette entries only)
std::uint16_t swatch{}; // swatchID (palette entries only)
bool hasColor{ false }; // whether color/swatch are meaningful
bool operator==(const RemoteAppearanceTint&) const = default;
};
struct RemoteAppearance
{
std::uint32_t version{ 3 };
std::uint32_t raceFormId{};
float height{ 1.0F };
RemoteAppearanceMorphWeight morphWeight;
RemoteAppearanceTintColor bodyTintColor;
std::uint32_t hairColorFormId{};
std::uint32_t facialHairColorFormId{};
std::uint32_t complexionFormId{};
std::vector<std::uint32_t> headPartFormIds;
std::vector<RemoteAppearanceMorph> morphSliders;
std::vector<float> morphRegionSliders;
std::vector<RemoteAppearanceFacialBoneMorph> facialBoneMorphs;
std::vector<RemoteAppearanceTint> tints;
};
struct RemotePlayerState
{
std::uint32_t playerId{};
float x{};
float y{};
float z{};
float angleZ{};
std::string movementType;
std::string cellId;
std::string worldspaceId;
std::optional<double> clientTime;
std::optional<double> serverTime;
bool isMoving{ false };
bool isSprinting{ false };
bool isSneaking{ false };
bool isJumping{ false };
bool isCrouching{ false };
bool weaponDrawn{ false };
float movementSpeed{ 0.0F };
float animationGraphSpeed{ -1.0F };
bool hasEquipmentUpdate{ false };
std::vector<RemoteEquippedItem> equippedItems;
bool hasAppearanceUpdate{ false };
RemoteAppearance appearance;
std::chrono::steady_clock::time_point lastReceivedLocalTime{};
// Phase 2: Actor state and action events (TiltedEvolution alignment)
std::uint32_t actorStateFlags1{};
std::uint32_t actorStateFlags2{};
std::vector<RemoteActionEvent> actionEvents;
};
void SetAssignedPlayerId(std::uint32_t a_playerId);
std::optional<std::uint32_t> GetAssignedPlayerId();
bool IsAssignedPlayerId(std::uint32_t a_playerId);
void ClearAssignedPlayerId();
void UpdateRemotePlayer(RemotePlayerState a_state);
bool RemoveRemotePlayer(std::uint32_t a_playerId);
void ClearRemotePlayers();
std::vector<RemotePlayerState> GetRemotePlayerSnapshot();
}