// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "MessageEndpoint.h" #include "Misc/Guid.h" #include "IMessageContext.h" #include "IMessageRpcCall.h" #include "Async/IAsyncProgress.h" #include "Async/IAsyncTask.h" #include "Async/Future.h" #include "Async/AsyncResult.h" /** * Interface for RPC clients. */ class IMessageRpcClient { /** Abstract base class for RPC calls. */ class FCall : public IAsyncProgress , public IAsyncTask , public IMessageRpcCall { public: FCall() : State(EAsyncTaskState::Running) , TimeCreated(FDateTime::UtcNow()) { } public: FSimpleDelegate& OnCancelled() { return CanceledDelegate; } public: virtual TOptional GetCompletion() override { return Completion; } virtual FText GetStatusText() override { return StatusText; } virtual FSimpleDelegate& OnProgressChanged() override { return ProgressChangedDelegate; } public: virtual void Cancel() override { State = EAsyncTaskState::Cancelled; CanceledDelegate.ExecuteIfBound(); } virtual EAsyncTaskState GetTaskState() override { return State; } public: virtual FDateTime GetLastUpdated() const override { return LastUpdated; } virtual FDateTime GetTimeCreated() const override { return TimeCreated; } virtual void UpdateProgress(float InCompletion, const FText& InStatusText) override { Completion = InCompletion; StatusText = InStatusText; LastUpdated = FDateTime::UtcNow(); ProgressChangedDelegate.ExecuteIfBound(); } protected: float Completion; FDateTime LastUpdated; EAsyncTaskState State; FText StatusText; FDateTime TimeCreated; private: FSimpleDelegate CanceledDelegate; FSimpleDelegate ProgressChangedDelegate; }; /** Template for RPC requests. */ template class TCall : public FCall { public: TCall(const FGuid& CallId, P... Params) : MessageTemplate(new typename RpcType::FRequest(Params...)) { MessageTemplate->CallId = CallId; } virtual ~TCall() { delete MessageTemplate; } virtual void Complete(const TSharedRef& ResponseContext) override { if (ResponseContext->GetMessageTypeInfo() == RpcType::FResponse::StaticStruct()) { State = EAsyncTaskState::Completed; Promise.SetValue(static_cast(ResponseContext->GetMessage())->Result); } else { State = EAsyncTaskState::Failed; Promise.SetValue(typename RpcType::FResult()); } } virtual void* ConstructMessage() const override { return FMessageEndpoint::MakeMessage(*MessageTemplate); } TFuture GetFuture() { return Promise.GetFuture(); } virtual const FGuid& GetId() const override { return MessageTemplate->CallId; } virtual void* GetMessageTemplate() const override { return MessageTemplate; } virtual UScriptStruct* GetMessageType() const override { return RpcType::FRequest::StaticStruct(); } virtual void TimeOut() override { State = EAsyncTaskState::Failed; Promise.SetValue(typename RpcType::FResult()); } private: TPromise Promise; typename RpcType::FRequest* MessageTemplate; }; public: /** * Connect this client to an RPC server. * * @param InServerAddress The RPC server's message address. * @see Disconnect */ virtual void Connect(const FMessageAddress& InServerAddress) = 0; /** * Disconnect this client from the RPC server. * * @see Connect */ virtual void Disconnect() = 0; /** * Whether the MessageRpcClient is currently connected to an RPC server. * * @return true if the client is connected, false otherwise. */ virtual bool IsConnected() const = 0; public: /** * Call a remote procedure. * * @param RpcType The RPC type definition. * @param P The call parameter types. * @param Params The call parameter list. */ template TAsyncResult Call(P... Params) { typedef TCall CallType; FGuid CallId = FGuid::NewGuid(); TSharedRef Call = MakeShareable(new CallType(CallId, Params...)); { AddCall(Call); Call->OnCancelled().BindLambda([this, CallId]() { CancelCall(CallId); }); } return TAsyncResult(Call->GetFuture(), Call, Call); } protected: /** * Add an RPC request. * * @param Request The call to add. */ virtual void AddCall(const TSharedRef& Call) = 0; /** * Cancel the specified RPC call. * * @param CallId The unique identifier of the call to cancel. */ virtual void CancelCall(const FGuid& CallId) = 0; public: /** Virtual destructor. */ virtual ~IMessageRpcClient() { } }; Expose_TNameOf(IMessageRpcClient)