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>
This commit is contained in:
parent
215cb398fd
commit
bf08bb67d9
@ -305,6 +305,7 @@ void UPS_AI_ConvAgent_ElevenLabsComponent::EndConversation()
|
||||
|
||||
// Reset replicated state so other players can talk to this NPC.
|
||||
bNetIsConversing = false;
|
||||
ApplyConversationPosture();
|
||||
NetConversatingPlayer = nullptr;
|
||||
NetConversatingPawn = nullptr;
|
||||
}
|
||||
@ -714,6 +715,7 @@ void UPS_AI_ConvAgent_ElevenLabsComponent::HandleDisconnected(int32 StatusCode,
|
||||
if (GetOwnerRole() == ROLE_Authority)
|
||||
{
|
||||
bNetIsConversing = false;
|
||||
ApplyConversationPosture();
|
||||
NetConversatingPlayer = nullptr;
|
||||
NetConversatingPawn = nullptr;
|
||||
}
|
||||
@ -1445,6 +1447,9 @@ void UPS_AI_ConvAgent_ElevenLabsComponent::ServerRequestConversation_Implementat
|
||||
NetConversatingPlayer = RequestingPlayer;
|
||||
NetConversatingPawn = RequestingPlayer ? RequestingPlayer->GetPawn() : nullptr;
|
||||
|
||||
// Update NPC posture on the server (OnRep never fires on Authority).
|
||||
ApplyConversationPosture();
|
||||
|
||||
StartConversation_Internal();
|
||||
}
|
||||
|
||||
@ -1460,7 +1465,10 @@ void UPS_AI_ConvAgent_ElevenLabsComponent::ServerReleaseConversation_Implementat
|
||||
WebSocketProxy = nullptr;
|
||||
}
|
||||
|
||||
// Clear posture before nullifying the pawn pointer (ApplyConversationPosture
|
||||
// uses NetConversatingPawn to guard against clearing someone else's target).
|
||||
bNetIsConversing = false;
|
||||
ApplyConversationPosture();
|
||||
NetConversatingPlayer = nullptr;
|
||||
NetConversatingPawn = nullptr;
|
||||
}
|
||||
@ -1694,3 +1702,33 @@ UPS_AI_ConvAgent_InteractionComponent* UPS_AI_ConvAgent_ElevenLabsComponent::Fin
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UPS_AI_ConvAgent_ElevenLabsComponent::ApplyConversationPosture()
|
||||
{
|
||||
// Same logic as OnRep_ConversationState's posture section, but callable
|
||||
// from the server side where OnRep never fires (Authority).
|
||||
AActor* Owner = GetOwner();
|
||||
if (!Owner) return;
|
||||
|
||||
auto* Posture = Owner->FindComponentByClass<UPS_AI_ConvAgent_PostureComponent>();
|
||||
if (!Posture) return;
|
||||
|
||||
if (bNetIsConversing && NetConversatingPawn)
|
||||
{
|
||||
Posture->bActive = true;
|
||||
Posture->TargetActor = NetConversatingPawn;
|
||||
Posture->ResetBodyTarget();
|
||||
Posture->bEnableBodyTracking = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only clear if the posture is still pointing at the departing player.
|
||||
// Another InteractionComponent may have already set a new TargetActor.
|
||||
if (!Posture->TargetActor || Posture->TargetActor == NetConversatingPawn)
|
||||
{
|
||||
Posture->bActive = false;
|
||||
Posture->TargetActor = nullptr;
|
||||
Posture->bEnableBodyTracking = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,18 +249,31 @@ void UPS_AI_ConvAgent_InteractionComponent::SetSelectedAgent(UPS_AI_ConvAgent_El
|
||||
OldAgent->GetOwner() ? *OldAgent->GetOwner()->GetName() : TEXT("(null)"));
|
||||
}
|
||||
|
||||
// ── Listening: stop ──────────────────────────────────────────────
|
||||
if (bAutoManageListening)
|
||||
// ── Conversation: end if auto-started ────────────────────────────
|
||||
// If we auto-started the conversation on selection, end it now so the
|
||||
// NPC becomes available for other players. EndConversation() also calls
|
||||
// StopListening() internally, so we skip the separate StopListening below.
|
||||
if (bAutoStartConversation && (OldAgent->IsConnected() || OldAgent->bNetIsConversing))
|
||||
{
|
||||
OldAgent->EndConversation();
|
||||
}
|
||||
else if (bAutoManageListening)
|
||||
{
|
||||
OldAgent->StopListening();
|
||||
}
|
||||
|
||||
// Disable body tracking on deselection.
|
||||
// Disable body tracking on deselection — but only if we were the
|
||||
// one who set the TargetActor. The conversation system (OnRep or
|
||||
// server ApplyConversationPosture) may have set TargetActor to a
|
||||
// different player; don't overwrite that.
|
||||
if (bAutoManagePosture)
|
||||
{
|
||||
if (UPS_AI_ConvAgent_PostureComponent* Posture = FindPostureOnAgent(OldAgent))
|
||||
{
|
||||
Posture->bEnableBodyTracking = false;
|
||||
if (Posture->TargetActor == GetOwner())
|
||||
{
|
||||
Posture->bEnableBodyTracking = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -514,12 +527,18 @@ void UPS_AI_ConvAgent_InteractionComponent::DetachPostureTarget(
|
||||
|
||||
if (UPS_AI_ConvAgent_PostureComponent* Posture = FindPostureOnAgent(AgentPtr))
|
||||
{
|
||||
Posture->TargetActor = nullptr;
|
||||
|
||||
if (bDebug)
|
||||
// Only clear if we are the one who set the TargetActor.
|
||||
// The conversation system (OnRep / ApplyConversationPosture on server)
|
||||
// may have set TargetActor to a different player — don't overwrite that.
|
||||
if (Posture->TargetActor == GetOwner())
|
||||
{
|
||||
UE_LOG(LogPS_AI_ConvAgent_Select, Log, TEXT("Posture detached: %s"),
|
||||
AgentPtr->GetOwner() ? *AgentPtr->GetOwner()->GetName() : TEXT("(null)"));
|
||||
Posture->TargetActor = nullptr;
|
||||
|
||||
if (bDebug)
|
||||
{
|
||||
UE_LOG(LogPS_AI_ConvAgent_Select, Log, TEXT("Posture detached: %s"),
|
||||
AgentPtr->GetOwner() ? *AgentPtr->GetOwner()->GetName() : TEXT("(null)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -617,4 +617,9 @@ private:
|
||||
void StartConversation_Internal();
|
||||
/** Find the InteractionComponent on the local player's pawn (for relay RPCs). */
|
||||
class UPS_AI_ConvAgent_InteractionComponent* FindLocalRelayComponent() const;
|
||||
|
||||
/** Update the NPC's PostureComponent from the current conversation state.
|
||||
* Called on the server when bNetIsConversing / NetConversatingPawn change,
|
||||
* because OnRep_ConversationState never fires on the Authority. */
|
||||
void ApplyConversationPosture();
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user