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>
This commit is contained in:
j.foucher 2026-03-03 13:43:48 +01:00
parent d035f5410a
commit ac2d40b67b
2 changed files with 12 additions and 11 deletions

View File

@ -260,15 +260,12 @@ void UPS_AI_ConvAgent_ElevenLabsComponent::StartConversation()
{ {
if (GetOwnerRole() == ROLE_Authority) if (GetOwnerRole() == ROLE_Authority)
{ {
// Server (or standalone): open WebSocket directly. // Set conversation state (used by ApplyConversationPosture, posture, LOD, etc.).
// In networked mode, also set replicated conversation state. // In standalone these aren't replicated but are still needed as local state flags.
if (GetWorld() && GetWorld()->GetNetMode() != NM_Standalone) APlayerController* PC = GetWorld() ? GetWorld()->GetFirstPlayerController() : nullptr;
{ bNetIsConversing = true;
APlayerController* PC = GetWorld()->GetFirstPlayerController(); NetConversatingPlayer = PC;
bNetIsConversing = true; NetConversatingPawn = PC ? PC->GetPawn() : nullptr;
NetConversatingPlayer = PC;
NetConversatingPawn = PC ? PC->GetPawn() : nullptr;
}
// In persistent mode the WebSocket was already opened by the first StartConversation. // In persistent mode the WebSocket was already opened by the first StartConversation.
// Reuse the existing connection — only set up conversation state. // Reuse the existing connection — only set up conversation state.

View File

@ -311,10 +311,13 @@ void UPS_AI_ConvAgent_InteractionComponent::SetSelectedAgent(UPS_AI_ConvAgent_El
NewAgent->GetOwner() ? *NewAgent->GetOwner()->GetName() : TEXT("(null)")); NewAgent->GetOwner() ? *NewAgent->GetOwner()->GetName() : TEXT("(null)"));
} }
// Network: auto-start conversation if the agent isn't connected yet. // Network: auto-start conversation if no active conversation.
// In persistent session mode, the WebSocket stays connected but
// bNetIsConversing is false between interactions — we still need
// to call StartConversation() to re-activate posture and mic.
// Only when bAutoStartConversation is true — otherwise the user must // Only when bAutoStartConversation is true — otherwise the user must
// call StartConversationWithSelectedAgent() explicitly (e.g. on key press). // call StartConversationWithSelectedAgent() explicitly (e.g. on key press).
if (bAutoStartConversation && !NewAgent->IsConnected() && !NewAgent->bNetIsConversing) if (bAutoStartConversation && !NewAgent->bNetIsConversing)
{ {
// On the server, call StartConversation() directly. // On the server, call StartConversation() directly.
// On clients, route through our relay RPC (clients can't call // On clients, route through our relay RPC (clients can't call
@ -494,6 +497,7 @@ void UPS_AI_ConvAgent_InteractionComponent::AttachPostureTarget(
if (UPS_AI_ConvAgent_PostureComponent* Posture = FindPostureOnAgent(AgentPtr)) if (UPS_AI_ConvAgent_PostureComponent* Posture = FindPostureOnAgent(AgentPtr))
{ {
Posture->TargetActor = GetOwner(); Posture->TargetActor = GetOwner();
Posture->bActive = true;
// Reset the body target to the actor's current facing so body tracking // Reset the body target to the actor's current facing so body tracking
// starts fresh on re-entry. Without this, TargetBodyWorldYaw retains // starts fresh on re-entry. Without this, TargetBodyWorldYaw retains