// Copyright Epic Games, Inc. All Rights Reserved. #include "LyraGameplayRpcRegistrationComponent.h" #include "Player/LyraPlayerController.h" #include "Character/LyraPawn.h" #include "Player/LyraPlayerState.h" #include "Engine/GameViewportClient.h" #include "Engine/GameInstance.h" #include "Misc/CommandLine.h" #include "EngineMinimal.h" #include "Character/LyraHealthComponent.h" #include "Inventory/LyraInventoryItemDefinition.h" #include "Inventory/LyraInventoryItemInstance.h" #include "Inventory/LyraInventoryManagerComponent.h" #include "Character/LyraPawnExtensionComponent.h" ULyraGameplayRpcRegistrationComponent* ULyraGameplayRpcRegistrationComponent::ObjectInstance = nullptr; ULyraGameplayRpcRegistrationComponent* ULyraGameplayRpcRegistrationComponent::GetInstance() { #if WITH_RPC_REGISTRY if (ObjectInstance == nullptr) { ObjectInstance = NewObject(); FParse::Value(FCommandLine::Get(), TEXT("externalrpclistenaddress="), ObjectInstance->ListenerAddress); FParse::Value(FCommandLine::Get(), TEXT("rpcsenderid="), ObjectInstance->SenderID); if (!UExternalRpcRegistry::GetInstance()) { GLog->Log(TEXT("BotRPC"), ELogVerbosity::Warning, FString::Printf(TEXT("Unable to create RPC Registry Instance. This might lead to issues using the RPC Registry."))); } ObjectInstance->AddToRoot(); } #endif return ObjectInstance; } UWorld* FindGameWorld() { //Find Game World if (GEngine->GameViewport) { UGameInstance* GameInstance = GEngine->GameViewport->GetGameInstance(); return GameInstance ? GameInstance->GetWorld() : nullptr; } return GWorld; } ALyraPlayerController* GetPlayerController() { UWorld* LocalWorld = FindGameWorld(); if (!LocalWorld) { return nullptr; } //Find PlayerController ALyraPlayerController* PlayerController = Cast(LocalWorld->GetFirstPlayerController()); if (!PlayerController) { return nullptr; } else { return PlayerController; } } #if WITH_RPC_REGISTRY TSharedPtr ULyraGameplayRpcRegistrationComponent::GetJsonObjectFromRequestBody(TArray InRequestBody) { FUTF8ToTCHAR WByteBuffer(reinterpret_cast(InRequestBody.GetData()), InRequestBody.Num()); const FString IncomingRequestBody = FString::ConstructFromPtrSize(WByteBuffer.Get(), WByteBuffer.Length()); TSharedPtr BodyObject = MakeShareable(new FJsonObject()); TSharedRef> JsonReader = TJsonReaderFactory<>::Create(IncomingRequestBody); if (FJsonSerializer::Deserialize(JsonReader, BodyObject) && BodyObject.IsValid()) { return BodyObject; } return nullptr; } void ULyraGameplayRpcRegistrationComponent::RegisterAlwaysOnHttpCallbacks() { Super::RegisterAlwaysOnHttpCallbacks(); const FExternalRpcArgumentDesc CommandDesc(TEXT("command"), TEXT("string"), TEXT("The command to tell the executable to run.")); RegisterHttpCallback(FName(TEXT("CheatCommand")), FHttpPath("/core/cheatcommand"), EHttpServerRequestVerbs::VERB_POST, FHttpRequestHandler::CreateUObject(this, &ThisClass::HttpExecuteCheatCommand), true, TEXT("Cheats"), TEXT("raw"), { CommandDesc }); } void ULyraGameplayRpcRegistrationComponent::RegisterInMatchHttpCallbacks() { RegisterHttpCallback(FName(TEXT("GetPlayerStatus")), FHttpPath("/player/status"), EHttpServerRequestVerbs::VERB_GET, FHttpRequestHandler::CreateUObject(this, &ThisClass::HttpGetPlayerVitalsCommand), true); RegisterHttpCallback(FName(TEXT("PlayerFireOnce")), FHttpPath("/player/status"), EHttpServerRequestVerbs::VERB_POST, FHttpRequestHandler::CreateUObject(this, &ThisClass::HttpFireOnceCommand), true); } void ULyraGameplayRpcRegistrationComponent::RegisterFrontendHttpCallbacks() { // TODO: Add Matchmaking RPCs here } void ULyraGameplayRpcRegistrationComponent::DeregisterHttpCallbacks() { Super::DeregisterHttpCallbacks(); } bool ULyraGameplayRpcRegistrationComponent::HttpExecuteCheatCommand(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete) { TSharedPtr BodyObject = GetJsonObjectFromRequestBody(Request.Body); if (!BodyObject.IsValid()) { TUniquePtrResponse = CreateSimpleResponse(false, TEXT("Invalid body object")); OnComplete(MoveTemp(Response)); return true; } if (BodyObject->GetStringField(TEXT("command")).IsEmpty()) { TUniquePtrResponse = CreateSimpleResponse(false, TEXT("command not found in json body")); OnComplete(MoveTemp(Response)); return true; } ALyraPlayerController* LPC = GetPlayerController(); if (!LPC) { TUniquePtrResponse = CreateSimpleResponse(false, TEXT("player controller not found")); OnComplete(MoveTemp(Response)); return true; } FString CheatCommand = FString::Printf(TEXT("%s"), *BodyObject->GetStringField(TEXT("command"))); LPC->ConsoleCommand(*CheatCommand, true); TUniquePtrResponse = CreateSimpleResponse(true); OnComplete(MoveTemp(Response)); return true; } bool ULyraGameplayRpcRegistrationComponent::HttpFireOnceCommand(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete) { ALyraPlayerController* LPC = GetPlayerController(); if (!LPC) { TUniquePtr Response = CreateSimpleResponse(false, TEXT("No player controller found")); OnComplete(MoveTemp(Response)); return true; } APawn* FortPlayerPawn = LPC->GetPawn(); if (!FortPlayerPawn) { TUniquePtr Response = CreateSimpleResponse(false, TEXT("Player pawn not found")); OnComplete(MoveTemp(Response)); return true; } // TODO: Fire Once here TUniquePtr Response = CreateSimpleResponse(true); OnComplete(MoveTemp(Response)); return true; } bool ULyraGameplayRpcRegistrationComponent::HttpGetPlayerVitalsCommand(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete) { ALyraPlayerController* LPC = GetPlayerController(); if (!LPC) { TUniquePtr Response = CreateSimpleResponse(false, TEXT("No player controller found")); OnComplete(MoveTemp(Response)); return true; } APawn* PlayerPawn = LPC->GetPawn(); if (!PlayerPawn) { TUniquePtr Response = CreateSimpleResponse(false, TEXT("Player pawn not found")); OnComplete(MoveTemp(Response)); return true; } ALyraPlayerState* LyraPlayerState = LPC->GetLyraPlayerState(); if (!LyraPlayerState) { TUniquePtr Response = CreateSimpleResponse(false, TEXT("Player state not found")); OnComplete(MoveTemp(Response)); return true; } FString ResponseStr; TSharedRef> JsonWriter = TJsonWriterFactory<>::Create(&ResponseStr); TSharedPtr BodyObject = MakeShareable(new FJsonObject()); JsonWriter->WriteObjectStart(); if (ULyraHealthComponent* HealthComponent = ULyraHealthComponent::FindHealthComponent(PlayerPawn)) { JsonWriter->WriteValue(TEXT("health"), FString::SanitizeFloat(HealthComponent->GetHealth())); } if (ULyraInventoryManagerComponent* InventoryComponent = LPC->GetComponentByClass()) { JsonWriter->WriteArrayStart(TEXT("inventory")); for (ULyraInventoryItemInstance* ItemInstance : InventoryComponent->GetAllItems()) { // TODO: Dump any relevant player info here. } JsonWriter->WriteArrayEnd(); } JsonWriter->WriteObjectEnd(); JsonWriter->Close(); TUniquePtrResponse = FHttpServerResponse::Create(ResponseStr, TEXT("application/json")); OnComplete(MoveTemp(Response)); return true; } #endif