137 Commits

Author SHA1 Message Date
2169c58cd7 Update memory: latency HUD status + future server-side metrics TODO
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 18:47:11 +01:00
5fad6376bc Fix latency HUD: anchor all metrics to agent_response_started
user_transcript arrives AFTER agent_response_started in Server VAD mode
(the server detects end of speech via VAD, starts generating immediately,
and STT completes later). This caused Transcript>Gen to show stale values
(19s) and Total < Gen>Audio (impossible).

Now all metrics are anchored to GenerationStartTime (agent_response_started),
which is the closest client-side proxy for "user stopped speaking":

- Gen>Audio: generation start → first audio chunk (LLM + TTS)
- Pre-buffer: wait before playback
- Gen>Ear: generation start → playback starts (user-perceived)

Removed STTToGenMs, TotalMs, EndToEarMs, UserSpeechMs (all depended on
unreliable timestamps). Simpler, always correct, 3 clear metrics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 18:39:49 +01:00
0b190c3149 Rework latency HUD: base all measurements on first user_transcript
Previous approach used TurnEndTime (from StopListening) which was never
set in Server VAD mode. Now all latency measurements are anchored to
TurnStartTime, captured when the first user_transcript arrives from the
ElevenLabs server — the earliest client-side confirmation of user speech.

Timeline: [user speaks] → STT → user_transcript(=T0) → agent_response_started → audio → playback

Metrics shown:
- Transcript>Gen: T0 → generation start (LLM think time)
- Gen>Audio: generation start → first audio chunk (LLM + TTS)
- Total: T0 → first audio chunk (full pipeline)
- Pre-buffer: wait before playback
- End-to-Ear: T0 → playback starts (user-perceived)

Removed UserSpeechMs (unmeasurable without client-side VAD).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 18:32:59 +01:00
56b072c45e Fix UserSpeechMs growing indefinitely in Server VAD mode
TurnStartTime was only set in StartListening(), which is called once.
In Server VAD + interruption mode the mic stays open, so TurnStartTime
was never updated between turns. Now reset TurnStartTime when the agent
stops speaking (normal end + interruption), marking the start of the
next potential user turn.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 18:27:14 +01:00
a8dd5a022f Fix latency HUD showing --- in Server VAD mode
Move latency reset from StopListening to HandleAgentResponseStarted.
In Server VAD + interruption mode, StopListening is never called so
TurnEndTime stayed at 0 and all dependent metrics showed ---. Now
HandleAgentResponseStarted detects whether StopListening provided a
fresh TurnEndTime; if not (Server VAD), it uses Now as approximation.
Also fix DisplayTime from 0 to 1s to prevent HUD flicker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 18:22:28 +01:00
955c97e0dd Improve latency debug HUD: separate toggle, per-turn reset, multi-line display
- Add bDebugLatency property + CVar (ps.ai.ConvAgent.Debug.Latency)
  independent from bDebug to save HUD space
- Reset latencies to zero each turn (StopListening) instead of persisting
- Add UserSpeechMs and PreBufferMs to the latency struct
- Move latency captures outside bDebug guard (always measured)
- Replace single-line latency in DrawDebugHUD with dedicated DrawLatencyHUD()
  showing 6 metrics on separate lines with color coding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 18:06:39 +01:00
d60f8d8484 Add response latency metrics to ElevenLabs debug HUD
Track 4 latencies per conversation turn (computed only when bDebug is active):
- STT→Gen: user stops talking → server starts generating
- Gen→Audio: server generating → first audio chunk received
- Total: user stops talking → first audio chunk (end-to-end)
- End-to-Ear: user stops talking → audio playback starts (includes pre-buffer)

New timestamps: GenerationStartTime (HandleAgentResponseStarted),
PlaybackStartTime (3 OnAudioPlaybackStarted sites). Values persist on
HUD between turns, reset when new turn starts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:51:25 +01:00
6d4ef21269 Merge feature/multi-player-shared-agent into main 2026-03-05 17:39:05 +01:00
c922fd304c Fix multi-player regressions: audio/text drops, thread safety
1. StartConversationWithSelectedAgent: remove early return when WebSocket
   is already connected (persistent mode). Always call ServerJoinConversation
   so the pawn is added to NetConnectedPawns and bNetIsConversing is set.

2. ServerSendMicAudioFromPlayer: bypass speaker arbitration in standalone
   mode (<=1 connected pawn). Send audio directly to avoid silent drops
   caused by pawn not being in NetConnectedPawns array. Add warning logs
   for multi-player drops to aid debugging.

3. OnMicrophoneDataCaptured: restore direct WebSocketProxy->SendAudioChunk
   on the server path. This callback runs on the WASAPI audio thread —
   accessing game-thread state (NetConnectedPawns, LastSpeakTime) was
   causing undefined behavior. Internal mic is always the local player,
   no speaker arbitration needed.

4. StopListening flush: send directly to WebSocket (active speaker already
   established, no arbitration needed for the tail of the current turn).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 15:33:43 +01:00
ca10689bb6 Fix thread-safety crash in LipSync anim node: TMap race condition
GetCurrentBlendshapes() was copying CurrentBlendshapes on the anim worker
thread while the game thread mutated it (TSet::UnhashElements crash).
Use a snapshot pattern: game thread copies to ThreadSafeBlendshapes under
FCriticalSection at end of TickComponent, anim node reads the snapshot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 15:16:10 +01:00
8d4065944c Multi-player shared agent: multiple players can converse with the same agent simultaneously
Replace exclusive single-player agent lock with shared multi-player model:
- NetConversatingPawn/Player → NetConnectedPawns array + NetActiveSpeakerPawn
- Server-side speaker arbitration with hysteresis (0.3s) prevents gaze ping-pong
- Speaker idle timeout (3.0s) clears active speaker after silence
- Agent gaze follows the active speaker via replicated OnRep_ActiveSpeaker
- New ServerJoinConversation/ServerLeaveConversation RPCs (idempotent join/leave)
- Backward-compatible: old ServerRequest/Release delegate to new Join/Leave
- InteractionComponent no longer skips occupied agents
- DrawDebugHUD shows connected player count and active speaker
- All mic audio paths (FeedExternalAudio, OnMicCapture, StopListening flush) route
  through speaker arbitration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 15:01:26 +01:00
8ad66ae4d5 Use CameraComponent for view point and gaze target (VR/FPS compatibility)
- InteractionComponent: GetPawnViewPoint() now prefers UCameraComponent
  over PlayerController::GetPlayerViewPoint() — fixes agent selection
  in VR where the pawn root stays at spawn while the HMD moves freely
- GazeComponent: ResolveTargetPosition() uses camera first for locally
  controlled pawns (VR HMD / FPS eye position), falls back to bone
  chain for NPCs and remote players in multiplayer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 14:13:09 +01:00
e5f40c65ec sln 2026-03-05 13:34:04 +01:00
9321e21a3b Debug HUD: fix flicker, add CVars, mic VU meter, reuse existing mic component
- Fix DisplayTime=0.0f causing flicker on all debug HUDs (now 1.0f)
- Add per-component CVars (ps.ai.ConvAgent.Debug.*) for console debug toggle
- Add MicrophoneCapture debug HUD with VU meter (RMS/peak/dB bar)
- InteractionComponent reuses existing MicrophoneCaptureComponent on pawn
  instead of always creating a new one

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 13:33:33 +01:00
fb641d5aa4 Fix body expression sync, conversation stability, and persistent session disconnect
- Sync body animation with actual audio playback via new OnAudioPlaybackStarted
  delegate instead of OnAgentStartedSpeaking (accounts for pre-buffer delay)
- Fix stale pre-buffer broadcasts by cancelling bPreBuffering on silence detection
  and guarding pre-buffer timeout with bAgentSpeaking check
- Smooth body crossfade using FInterpTo instead of linear interpolation
- Add conversation lock in EvaluateBestAgent: keep agent selected during active
  conversation regardless of view cone (distance-only check prevents deselect
  flicker on fast camera turns)
- Broadcast OnAgentDisconnected in persistent session EndConversation so all
  expression components (body, facial, lip sync, gaze) properly deactivate
  when the player leaves the interaction zone

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 12:18:35 +01:00
2e96e3c766 Smooth transitions, lip sync speech blend, body expression override mode, SendTextToSelectedAgent
- Replace FInterpConstantTo with FInterpTo (exponential ease-out) in all 4 anim components
- Apply SmoothStep to crossfade alphas for smooth animation transitions
- Add SpeechBlendAlpha to LipSync: fades out mouth curves when not speaking,
  letting FacialExpression emotion curves (including mouth) show through
- Remove LipSync AnimNode grace period zeroing to avoid overwriting emotion curves
- Revert BodyExpression to override mode (additive broken with full-pose anims)
- Default ExcludeBones = neck_01 to prevent Gaze/Posture conflicts
- Fix mid-crossfade animation pop in BodyExpression SwitchToNewAnim
- Add Neutral emotion fallback in Body and Facial expression components
- Add SendTextToSelectedAgent convenience method on InteractionComponent
- Add debug HUD display for BodyExpression component
- Update CoreRedirects for Posture → Gaze rename

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 11:09:00 +01:00
8bb4371a74 Metahuman 2026-03-04 18:27:02 +01:00
acfae96420 Body Expression system, auto external mic, gaze auto-target eyes, cleanup bActive
- Add BodyExpression system: emotion-driven body animations with per-emotion
  anim lists (Idle/Normal/Medium/Extreme), random selection, auto-cycle on
  loop complete, crossfade transitions, upper-body-only or full-body mode
- Replace bExternalMicManagement with auto-detecting ShouldUseExternalMic()
- Add bAutoTargetEyes to GazeComponent: auto-aim at target's eye bones
  (MetaHuman), with fallback chain (eyes > head > FallbackEyeHeight)
- Hide bActive from Details panel on all 4 anim components (read-only,
  code-managed): FacialExpression, Gaze, LipSync, BodyExpression
- Remove misleading mh_arkit_mapping_pose warning from LipSync
- Add bUpperBodyOnly toggle to BodyExpression AnimNode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:26:39 +01:00
ae40153252 V1 2026-03-03 14:01:26 +01:00
ac2d40b67b Fix head/eye tracking lost on 2nd conversation cycle (persistent session)
Three issues prevented posture re-activation on re-entry:
- StartConversation skipped bNetIsConversing/NetConversatingPawn in standalone
  (guarded by NM_Standalone check) so ApplyConversationPosture always deactivated
- SetSelectedAgent required !IsConnected() to auto-start conversation, but in
  persistent mode the WS stays connected → StartConversation never called on re-entry
- AttachPostureTarget never set Posture->bActive = true, relying solely on
  ApplyConversationPosture which could be skipped due to the above condition

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 13:43:48 +01:00
d035f5410a Add bPersistentSession: keep WebSocket alive across conversation cycles
When true (default), the first StartConversation() opens the WebSocket
and EndConversation() only stops the mic/posture — the WebSocket stays
open until EndPlay. The agent remembers the full conversation context.
When false, each Start/EndConversation opens/closes the WebSocket (previous behavior).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 12:50:56 +01:00
35d217f6ec Reliability & perf: atomic flags, WebSocket reconnect, zero-alloc eye curves, audio queue read-offset
- Make bAgentGenerating, bWaitingForAgentResponse, bWaitingForResponse,
  bFirstAudioResponseLogged, bAgentResponseStartedFired (std::atomic<bool>)
  and LastInterruptEventId (std::atomic<int32>) for thread-safety
- Add WebSocket auto-reconnection with exponential backoff (1s→30s cap,
  max 5 attempts), distinguishing intentional vs unexpected disconnects
- Add FillCurrentEyeCurves() zero-allocation method using FindOrAdd()
  to eliminate per-frame TMap heap allocation in anim thread
- Replace AudioQueue RemoveAt(0,N) with read-offset pattern — O(1) per
  underflow callback, periodic compaction when offset > half buffer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 12:19:45 +01:00
8886e7a7a2 Client: override actor rotation with smoothed yaw to eliminate body mesh jitter
Previous fix only smoothed cascade inputs (head/eyes) via SmoothedBodyYaw
but the body mesh still followed the raw replicated actor rotation which
jumped at ~30Hz network rate. Now the client calls SetActorRotation with
the smoothed yaw so the mesh visually interpolates. Replication overwrites
on next network update; SmoothedBodyYaw absorbs the correction smoothly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 11:37:59 +01:00
28964f0a40 fine tune eye look values 2026-03-03 11:28:12 +01:00
b5c52c9236 Client-side smoothing for replicated body rotation
Server-only AddActorWorldRotation (HasAuthority guard) prevents
client/server tug-of-war. Client interpolates toward replicated
rotation via SmoothedBodyYaw (angle-aware FInterpTo at 3x body
speed) to eliminate step artifacts from ~30Hz network updates.
Cascade (DeltaYaw, head, eyes) uses SmoothedBodyYaw on all machines.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 11:23:44 +01:00
7f92dcab51 Revert animation-only body tracking — restore AddActorWorldRotation
The pelvis bone rotation approach didn't work in practice. Reverts to
the previous AddActorWorldRotation() body tracking (replicated actor
rotation). Thread-safety fix and deprecated IsValid() removal are kept.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 11:07:24 +01:00
677f08e936 Fix thread-safety crash and body rotation saccades in network play
- FacialExpressionComponent: add FCriticalSection around emotion curves
  to prevent race between TickComponent (game thread) and anim worker
  thread — root cause of EXCEPTION_ACCESS_VIOLATION at Evaluate_AnyThread
- Remove deprecated FBlendedCurve::IsValid() guards (UE 5.5: always true)
- Body tracking: replace AddActorWorldRotation() with animation-only
  pelvis bone rotation via AnimNode — eliminates replication tug-of-war
  that caused client-side saccades when server overwrote local rotation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:55:59 +01:00
45ee0c6f7d fix crash + posture replication 2026-03-03 10:21:52 +01:00
1c4dbfc402 Merge branch 'main' of ssh://git.polymorph.fr:5070/j.foucher/PS_AI_Agent 2026-03-03 09:30:44 +01:00
86b7d9744e Opus-compress mic audio on client→server relay path (~16x bandwidth reduction)
- Create OpusEncoder on ALL machines (was Authority-only) — clients now
  encode mic audio, server decodes it; server still encodes agent audio
- FeedExternalAudio / OnMicrophoneDataCaptured: Opus-encode accumulated
  PCM buffer before sending via ServerRelayMicAudio RPC on client path
  (~200 bytes/100ms instead of 3200 bytes = ~16 Kbits/s vs 256 Kbits/s)
- ServerRelayMicAudio_Implementation: auto-detect Opus (size < raw chunk)
  and decode back to PCM before forwarding to WebSocket
- Add public DecompressMicAudio() helper for clean API access from
  InteractionComponent relay without exposing private Opus members
- Graceful fallback: if Opus unavailable, raw PCM is sent/received as before

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:27:40 +01:00
5e18e7cc8c disable ray tracing 2026-03-02 18:20:35 +01:00
3952847ece Enable audio spatialization by default for agent voice
- Attach AudioPlaybackComponent to owner's root component for proper
  3D world positioning (was unattached = stuck at origin)
- Enable default inline spatialization with 15m falloff distance when
  no external SoundAttenuation asset is set
- External SoundAttenuation asset still overrides the default if set

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:58:10 +01:00
76dd13944a remove lumen from scene 2026-03-02 17:54:13 +01:00
bf08bb67d9 Fix conversation handoff and server-side posture for network
- Auto-end conversation on deselection: when bAutoStartConversation is
  true and the player walks out of range, EndConversation() is called
  so the NPC becomes available for other players
- Server-side posture: add ApplyConversationPosture() helper called
  from ServerRequestConversation, ServerReleaseConversation,
  EndConversation (Authority path), and HandleDisconnected — fixes
  NPC not tracking the client on the listen server (OnRep never fires
  on Authority)
- Guard DetachPostureTarget: only clear TargetActor if it matches our
  pawn, preventing the server IC from overwriting posture set by the
  conversation system for a remote client

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:44:48 +01:00
215cb398fd Fix network saturation, lazy init, body tracking, and mic race condition
- Silence gate: skip sending silent mic audio over network RPCs on clients
  (~256 Kbits/s saved when not speaking, fixes chaotic teleporting)
- Lazy init: defer InteractionComponent mic creation from BeginPlay to
  TickComponent with IsLocallyControlled guard (fixes "No owning connection"
  from server-side replicas of remote pawns)
- Body tracking: use bNetIsConversing as fallback for IsConnected() on
  clients where WebSocket doesn't exist
- EvaluateBestAgent: null-check NetConversatingPawn before comparison
- MicCaptureComponent: use TWeakObjectPtr in AsyncTask lambda to prevent
  FMRSWRecursiveAccessDetector race on component destruction

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:23:26 +01:00
3c4389a43d Fix InteractionComponent ticking on server for remote client pawns
In a listen server, the server-side copy of a remote client's pawn also
has an InteractionComponent that ticks. This caused a race condition:
the server-side tick would start conversations using GetFirstPlayerController()
(= server's PC), setting NetConversatingPawn to the server's pawn instead
of the client's. The client's relay RPC arrived too late and was rejected
because bNetIsConversing was already true.

Fix: disable tick and skip mic creation in BeginPlay for non-locally-controlled
pawns. The client handles all interaction locally via relay RPCs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 16:57:33 +01:00
4d662ebada debug Network 2026-03-02 16:55:11 +01:00
8a3304fea9 Add relay RPC pattern for client→NPC communication over network
UE5 clients cannot call Server RPCs on actors they don't own. NPC actors
are server-owned, causing "No owning connection" errors when remote clients
try to start conversations, send mic audio, or interrupt agents.

Solution: relay all client→NPC RPCs through the InteractionComponent on
the player's pawn (which IS owned by the client). The relay forwards
commands to the NPC's ElevenLabsComponent on the server side.

Changes:
- InteractionComponent: add Server relay RPCs (Start/End conversation,
  mic audio, text message, interrupt) and Client relay RPCs
  (ConversationStarted/Failed) with GetLifetimeReplicatedProps
- ElevenLabsComponent: implement FindLocalRelayComponent(), route all
  client-side calls through relay (StartConversation, EndConversation,
  SendTextMessage, InterruptAgent, FeedExternalAudio, mic capture)
- Fix HandleConnected/ServerRequestConversation to route Client RPCs
  through the player pawn's relay instead of the NPC (no owning connection)
- Fix StartListening/FeedExternalAudio/StopListening to accept
  bNetIsConversing on clients (WebSocket only exists on server)
- Fix EvaluateBestAgent to use NetConversatingPawn instead of
  NetConversatingPlayer (NULL on remote clients due to bOnlyRelevantToOwner)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 16:40:27 +01:00
ce84a5dc58 Fix head tracking on remote clients: activate PostureComponent in OnRep
PostureComponent starts with bActive=false and waits for
OnConversationConnected, which only fires on the server (WebSocket).
Remote clients never got bActive=true, so CurrentActiveAlpha stayed
at 0 and all head/eye rotation was zeroed out.

Now OnRep_ConversationState also sets Posture->bActive alongside
TargetActor, matching what was already done for FacialExpression
and LipSync components.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 15:33:07 +01:00
daf79d0d89 Add diagnostic logs to OnRep_ConversationState for head tracking debug
Logs NetConversatingPawn, PostureComponent availability, and TargetActor
assignment to diagnose why head tracking doesn't work on remote clients.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 15:26:28 +01:00
0124de8b53 Fix audio pre-buffer bypass on clients due to network sub-chunking
Raw PCM is split into 32KB sub-chunks for network transmission.
On the client, these sub-chunks arrive nearly simultaneously,
triggering the "second chunk arrived" fast-path which cancelled
the pre-buffer after ~10ms instead of the intended 2000ms.

Now the fast-path only applies on Authority (server) where
chunks represent genuine separate TTS batches. Clients always
wait the full pre-buffer duration via TickComponent timer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 15:19:05 +01:00
c0c1b2cea4 Fix head tracking on remote clients: replicate Pawn instead of PlayerController
PlayerControllers have bOnlyRelevantToOwner=true, so NetConversatingPlayer
was always nullptr on remote clients. Added NetConversatingPawn (APawn*)
which IS replicated to all clients. OnRep_ConversationState now uses
NetConversatingPawn as the posture TargetActor, enabling head/eye
tracking on all clients.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 15:16:48 +01:00
fc728454d0 Fix raw PCM multicast exceeding UE5 replicated array limit
UE5 limits replicated TArrays to 65535 elements. ElevenLabs sends
audio chunks up to ~72K bytes, exceeding this limit when sent as
raw PCM. Split into 32000-byte sub-chunks (1s of 16kHz 16-bit mono)
before calling MulticastReceiveAgentAudio.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 15:04:33 +01:00
6cac56fa06 Fix network audio: raw PCM fallback when Opus codecs unavailable
UE5 5.5's FVoiceModule returns NULL encoder/decoder, breaking
Opus-based audio replication. This adds a transparent fallback:
- Server sends raw PCM when OpusEncoder is null (~32KB/s, fine for LAN)
- Client accepts raw PCM when OpusDecoder is null
- Changed MulticastReceiveAgentAudio from Unreliable to Reliable
  to handle larger uncompressed payloads without packet loss
- Added OnlineSubsystemNull config for future Opus compatibility
- Removed premature bAgentSpeaking=true from MulticastAgentStartedSpeaking
  to fix race condition with audio initialization

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:58:21 +01:00
5e1c50edf8 Add diagnostic logs for Opus codec initialization and audio multicast
- Log at InitOpusCodec: FVoiceModule availability, encoder/decoder creation, net role
- Log at HandleAudioReceived: warn once if encoder is null or role is not Authority
- These fire unconditionally (not behind bDebug) to catch the root cause

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:38:49 +01:00
1990b828a2 Move posture per-frame debug log to verbosity level 2
Reduces log spam when bDebug is on with DebugVerbosity=1.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:31:18 +01:00
913be3c631 Add server-side Opus encoding diagnostic logs
Logs compressed audio size in HandleAudioReceived to diagnose
why MulticastReceiveAgentAudio (Unreliable) never reaches clients.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:28:18 +01:00
84a4fd7e0e Add Debug to component to debug replication 2026-03-02 14:20:16 +01:00
11255db576 Fix network replication: head/eyes tracking, facial expressions, lip sync and audio on remote clients
- OnRep_ConversationState now sets PostureComponent TargetActor from
  replicated NetConversatingPlayer so remote clients see head/eyes tracking
- Activate FacialExpressionComponent and LipSyncComponent on remote clients
  (OnAgentConnected never fires on clients since WebSocket is server-only)
- Fix audio race condition: MulticastAgentStartedSpeaking no longer sets
  bAgentSpeaking prematurely, letting EnqueueAgentAudio handle the full
  first-chunk initialization (pre-buffer, Play(), state reset)
- Add diagnostic logging to MulticastReceiveAgentAudio for silent failures
  (OpusDecoder invalid, LOD culling, decode failure)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:17:49 +01:00
9ee7960855 Merge branch 'main' of https://git.polymorph.fr/j.foucher/PS_AI_Agent 2026-03-02 13:47:11 +01:00