128 Commits

Author SHA1 Message Date
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
e4cf657077 prepare test replication 2026-03-02 13:46:00 +01:00
11c473f3ab Merge branch 'main' of https://git.polymorph.fr/j.foucher/PS_AI_Agent 2026-03-02 12:39:23 +01:00
0b07a0493f ini 2026-03-02 12:39:04 +01:00
82b134bcc3 Resolve some Bugs 2026-03-02 12:37:28 +01:00
259a77f9f6 Add Agent Config data asset system with ElevenLabs editor integration
Introduces UPS_AI_ConvAgent_AgentConfig_ElevenLabs data asset to encapsulate
full agent configuration (voice, LLM, prompt, language, emotions) with a
custom Detail Customization providing:
- Voice/TTS Model/LLM/Language pickers with Fetch buttons (ElevenLabs API)
- LLM latency hints in dropdown (~250ms, ~700ms, etc.)
- Create/Update/Fetch Agent buttons for REST API CRUD
- Auto-fetch on editor open, auto-select first voice for new assets
- Prompt fragment management (language, multilingual, emotion tool)
- Smart defaults: gemini-2.5-flash LLM, eleven_turbo_v2_5 TTS, English
- Speed range expanded to 0.7-1.95 (was 0.7-1.2)
- bAutoStartConversation + StartConversationWithSelectedAgent() on InteractionComponent

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 20:51:05 +01:00
8175375c28 remove unwanted old plugins 2026-03-01 17:18:30 +01:00
275065f5aa Revert SSL cert path to Content/Certificates for packaged build staging
Saved/ is not staged in packaged builds, so Content/Certificates/ is the
only reliable location. Simplified code by removing Android-specific
writable fallback (Content/ works on all platforms with NonUFS staging).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 16:42:16 +01:00
33ec54150f Add bActive with smooth blend and auto-activation to all 3 AnimNode components
- Posture, FacialExpression, LipSync: bActive + ActivationBlendDuration for
  smooth alpha blend in/out (linear interp via FInterpConstantTo).
- Auto-activation: components bind to OnAgentConnected/OnAgentDisconnected,
  starting inactive and blending in when conversation begins.
- Without an agent component, bActive defaults to true (backward compatible).
- Add BlueprintFunctionLibrary with SetPostProcessAnimBlueprint helper
  (wraps UE5.5 SetOverridePostProcessAnimBP for per-instance BP setup).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 15:24:57 +01:00
5fcd98ba73 Add Android platform support to plugin
Whitelist Android in .uplugin Runtime module and handle
read-only APK paths for SSL certificate copy on Android
(ProjectSavedDir fallback). No change to Win64 behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 14:14:30 +01:00