From e867e362a75fed0d8eac142c0610ae4fb0efef7d Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Wed, 29 May 2024 09:17:02 -0400 Subject: [PATCH] conversation participant or observer Add ability to configure if player is in conversation or not while targeting characters cleanup unused struct update naming to Add/Remove instead of Set/Clear to better indicate a list over single --- .../Private/InworldPlayer.cpp | 19 +++++++-- .../InworldPlayerAudioCaptureComponent.cpp | 3 +- .../Private/InworldPlayerComponent.cpp | 37 +++++++++++++----- .../Public/InworldPlayer.h | 10 ++++- .../Public/InworldPlayerComponent.h | 39 ++++++++++--------- 5 files changed, 75 insertions(+), 33 deletions(-) diff --git a/InworldAI/Source/InworldAIIntegration/Private/InworldPlayer.cpp b/InworldAI/Source/InworldAIIntegration/Private/InworldPlayer.cpp index 3b4385b6..c1a7d6f7 100644 --- a/InworldAI/Source/InworldAIIntegration/Private/InworldPlayer.cpp +++ b/InworldAI/Source/InworldAIIntegration/Private/InworldPlayer.cpp @@ -32,6 +32,7 @@ void UInworldPlayer::GetLifetimeReplicatedProps(TArray& OutLi } DOREPLIFETIME(UInworldPlayer, Session); + DOREPLIFETIME(UInworldPlayer, bConversationParticipant); DOREPLIFETIME(UInworldPlayer, TargetCharacters); } @@ -99,7 +100,7 @@ void UInworldPlayer::SendAudioSessionStartToConversation(EInworldMicrophoneMode NO_SESSION_RETURN(void()) EMPTY_ARG_RETURN(ConversationId, void()) - if (bHasAudioSession) + if (bHasAudioSession || !bConversationParticipant) { return; } @@ -130,7 +131,7 @@ void UInworldPlayer::SendSoundMessageToConversation(const TArray& Input, EMPTY_ARG_RETURN(ConversationId, void()) EMPTY_ARG_RETURN(Input, void()) - if (!bHasAudioSession) + if (!bHasAudioSession || !bConversationParticipant) { return; } @@ -146,6 +147,18 @@ TScriptInterface UInworldPlayer::GetInworldPlayerO return TScriptInterface(GetOuter()); } +void UInworldPlayer::SetConversationParticipation(bool bParticipate) +{ + if (bConversationParticipant != bParticipate) + { + bConversationParticipant = bParticipate; + if (!ConversationId.IsEmpty()) + { + UpdateConversation(); + } + } +} + void UInworldPlayer::AddTargetCharacter(UInworldCharacter* TargetCharacter) { if (TargetCharacter && TargetCharacter->IsPossessed() && TargetCharacter->GetTargetPlayer() == nullptr) @@ -215,7 +228,7 @@ void UInworldPlayer::UpdateConversation() { NO_SESSION_RETURN(void()) - FString NextConversationId = Session->GetClient()->UpdateConversation(ConversationId, Inworld::CharactersToAgentIds(TargetCharacters), true); + FString NextConversationId = Session->GetClient()->UpdateConversation(ConversationId, Inworld::CharactersToAgentIds(TargetCharacters), bConversationParticipant); const bool bHadAudioSession = bHasAudioSession; if (bHasAudioSession) { diff --git a/InworldAI/Source/InworldAIIntegration/Private/InworldPlayerAudioCaptureComponent.cpp b/InworldAI/Source/InworldAIIntegration/Private/InworldPlayerAudioCaptureComponent.cpp index fba475d3..803aae0b 100644 --- a/InworldAI/Source/InworldAIIntegration/Private/InworldPlayerAudioCaptureComponent.cpp +++ b/InworldAI/Source/InworldAIIntegration/Private/InworldPlayerAudioCaptureComponent.cpp @@ -297,12 +297,13 @@ void UInworldPlayerAudioCaptureComponent::EvaluateVoiceCapture() { const bool bIsMicHot = !bMuted; const bool bIsWorldPlaying = !GetWorld()->IsPaused(); + const bool bIsParticipating = InworldPlayer->IsConversationParticipant(); const bool bHasConversation = !InworldPlayer->GetConversationId().IsEmpty(); UInworldSession* InworldSession = InworldPlayer->GetSession(); const EInworldConnectionState ConnectionState = InworldSession ? InworldSession->GetConnectionState() : EInworldConnectionState::Idle; const bool bHasActiveInworldSession = InworldSession && InworldSession->IsLoaded() && (ConnectionState == EInworldConnectionState::Connected || ConnectionState == EInworldConnectionState::Reconnecting); - const bool bShouldCaptureVoice = bIsMicHot && bIsWorldPlaying && bHasConversation && bHasActiveInworldSession; + const bool bShouldCaptureVoice = bIsMicHot && bIsWorldPlaying && bIsParticipating && bHasConversation && bHasActiveInworldSession; if (bShouldCaptureVoice != bServerCapturingVoice) { diff --git a/InworldAI/Source/InworldAIIntegration/Private/InworldPlayerComponent.cpp b/InworldAI/Source/InworldAIIntegration/Private/InworldPlayerComponent.cpp index 6dc577ac..38076617 100644 --- a/InworldAI/Source/InworldAIIntegration/Private/InworldPlayerComponent.cpp +++ b/InworldAI/Source/InworldAIIntegration/Private/InworldPlayerComponent.cpp @@ -79,6 +79,16 @@ void UInworldPlayerComponent::UninitializeComponent() } } +void UInworldPlayerComponent::BeginPlay() +{ + Super::BeginPlay(); + + if (GetOwnerRole() == ROLE_Authority) + { + InworldPlayer->SetConversationParticipation(bConversationParticipant); + } +} + void UInworldPlayerComponent::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); @@ -102,6 +112,22 @@ bool UInworldPlayerComponent::ReplicateSubobjects(UActorChannel* Channel, FOutBu #endif } +void UInworldPlayerComponent::SetConversationParticipation(bool bParticipating) +{ + if (InworldPlayer->IsConversationParticipant() != bParticipating) + { + InworldPlayer->SetConversationParticipation(bParticipating); + } + bConversationParticipant = InworldPlayer->IsConversationParticipant(); +} + +void UInworldPlayerComponent::ContinueConversation() +{ + NO_PLAYER_RETURN(void()) + + InworldPlayer->SendTriggerToConversation(TEXT("inworld.conversation.next_turn"), {}); +} + UInworldCharacterComponent* UInworldPlayerComponent::GetTargetInworldCharacter() { TArray TargetCharacters = GetTargetInworldCharacters(); @@ -126,21 +152,14 @@ TArray UInworldPlayerComponent::GetTargetInworldCha return InworldCharacterComponents; } -void UInworldPlayerComponent::ContinueMultiAgentConversation() -{ - NO_PLAYER_RETURN(void()) - - InworldPlayer->SendTriggerToConversation(TEXT("inworld.conversation.next_turn"), {}); -} - -void UInworldPlayerComponent::SetTargetInworldCharacter(UInworldCharacterComponent* Character) +void UInworldPlayerComponent::AddTargetInworldCharacter(UInworldCharacterComponent* Character) { NO_PLAYER_RETURN(void()) InworldPlayer->AddTargetCharacter(IInworldCharacterOwnerInterface::Execute_GetInworldCharacter(Character)); } -void UInworldPlayerComponent::ClearTargetInworldCharacter(UInworldCharacterComponent* Character) +void UInworldPlayerComponent::RemoveTargetInworldCharacter(UInworldCharacterComponent* Character) { NO_PLAYER_RETURN(void()) diff --git a/InworldAI/Source/InworldAIIntegration/Public/InworldPlayer.h b/InworldAI/Source/InworldAIIntegration/Public/InworldPlayer.h index a56b679d..4d0b79d5 100644 --- a/InworldAI/Source/InworldAIIntegration/Public/InworldPlayer.h +++ b/InworldAI/Source/InworldAIIntegration/Public/InworldPlayer.h @@ -60,7 +60,12 @@ class INWORLDAIINTEGRATION_API UInworldPlayer : public UObject UFUNCTION(BlueprintCallable, Category = "Player") TScriptInterface GetInworldPlayerOwner(); - UFUNCTION(BlueprintCallable, Category = "Target") + UFUNCTION(BlueprintCallable, Category = "Participation") + void SetConversationParticipation(bool bParticipate); + UFUNCTION(BlueprintPure, Category = "Participation") + bool IsConversationParticipant() const { return bConversationParticipant; } + + UFUNCTION(BlueprintPure, Category = "Target") const TArray& GetTargetCharacters() const { return TargetCharacters; } UFUNCTION(BlueprintCallable, Category = "Target") @@ -101,6 +106,9 @@ class INWORLDAIINTEGRATION_API UInworldPlayer : public UObject UPROPERTY(Replicated) UInworldSession* Session; + UPROPERTY(Replicated) + bool bConversationParticipant = true; + UPROPERTY(Replicated) TArray TargetCharacters; diff --git a/InworldAI/Source/InworldAIIntegration/Public/InworldPlayerComponent.h b/InworldAI/Source/InworldAIIntegration/Public/InworldPlayerComponent.h index 60d5528a..b99ab956 100644 --- a/InworldAI/Source/InworldAIIntegration/Public/InworldPlayerComponent.h +++ b/InworldAI/Source/InworldAIIntegration/Public/InworldPlayerComponent.h @@ -16,17 +16,6 @@ class UInworldApiSubsystem; class UInworldCharacterComponent; -USTRUCT() -struct FInworldPlayerTargetCharacter -{ - GENERATED_BODY() - - FDelegateHandle UnpossessedHandle; - - UPROPERTY() - FString AgentId; -}; - UCLASS(ClassGroup = (Inworld), meta = (BlueprintSpawnableComponent)) class INWORLDAIINTEGRATION_API UInworldPlayerComponent : public UActorComponent, public IInworldPlayerOwnerInterface { @@ -44,24 +33,33 @@ class INWORLDAIINTEGRATION_API UInworldPlayerComponent : public UActorComponent, virtual void InitializeComponent() override; virtual void UninitializeComponent() override; + virtual void BeginPlay() override; + virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; virtual bool ReplicateSubobjects(UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags) override; + UFUNCTION(BlueprintCallable, Category = "Interaction") + void SetConversationParticipation(bool bParticipant); + + UFUNCTION(BlueprintCallable, Category = "Interaction") + void ContinueConversation(); + UFUNCTION(BlueprintCallable, Category = "Interaction", meta = (Displayname = "GetTargetCharacter")) UInworldCharacterComponent* GetTargetInworldCharacter(); UFUNCTION(BlueprintCallable, Category = "Interaction", meta = (Displayname = "GetTargetCharacters")) TArray GetTargetInworldCharacters(); - UFUNCTION(BlueprintCallable, Category = "Interaction", meta = (DeprecatedFunction, DeprecationMessage = "Will be removed in next release.")) - void ContinueMultiAgentConversation(); - - UFUNCTION(BlueprintCallable, Category = "Interaction", meta = (Displayname = "SetTargetCharacter")) - void SetTargetInworldCharacter(UInworldCharacterComponent* Character); + UFUNCTION(BlueprintCallable, Category = "Interaction", meta = (Displayname = "SetTargetCharacter", DeprecatedFunction, DeprecationMessage="Please use AddTargetCharacter")) + void SetTargetInworldCharacter(UInworldCharacterComponent* Character) { AddTargetInworldCharacter(Character); } + UFUNCTION(BlueprintCallable, Category = "Interaction", meta = (Displayname = "AddTargetCharacter")) + void AddTargetInworldCharacter(UInworldCharacterComponent* Character); - UFUNCTION(BlueprintCallable, Category = "Interaction", meta = (Displayname = "ClearTargetCharacter")) - void ClearTargetInworldCharacter(UInworldCharacterComponent* Character); + UFUNCTION(BlueprintCallable, Category = "Interaction", meta = (Displayname = "ClearTargetCharacter", DeprecatedFunction, DeprecationMessage = "Please use RemoveTargetCharacter")) + void ClearTargetInworldCharacter(UInworldCharacterComponent* Character) { RemoveTargetInworldCharacter(Character); } + UFUNCTION(BlueprintCallable, Category = "Interaction", meta = (Displayname = "RemoveTargetCharacter")) + void RemoveTargetInworldCharacter(UInworldCharacterComponent* Character); - UFUNCTION(BlueprintCallable, Category = "Interaction", meta = (Displayname = "ClearTargetCharacter")) + UFUNCTION(BlueprintCallable, Category = "Interaction", meta = (Displayname = "ClearAllTargetCharacters")) void ClearAllTargetInworldCharacters(); UFUNCTION(BlueprintCallable, Category = "Interaction") @@ -91,6 +89,9 @@ class INWORLDAIINTEGRATION_API UInworldPlayerComponent : public UActorComponent, UPROPERTY(Replicated) UInworldPlayer* InworldPlayer; + UPROPERTY(EditDefaultsOnly, Category = "Conversation") + bool bConversationParticipant = true; + #if defined(WITH_GAMEPLAY_DEBUGGER) && WITH_GAMEPLAY_DEBUGGER friend class FInworldGameplayDebuggerCategory; #endif