GameplayRequestSystem框架
在联网游戏中经常需要有多个玩家之间请求交互的操作,提供一套 GameplayRequestSystem
用于快速自定义出一类新的 Request
,自定义可发起请求交互对象、操作回调;
基本框架
从一次 Request
基本的流程出发:
对于某一类 Request
,首先 A 需要筛选出关心的 B (其它玩家) ,判断这些玩家是否可以发起该类请求,如果不可以的话,还可能需要知道无法发起请求的原因;
然后由 A 发起请求,并等待一系列请求交互操作:
SendRequest
:由 A 向 B 发起某个请求,等待请求回复;
AcceptRequest
:B / Manager 接受某个请求;
RejectRequest
:B / Manager 拒绝某个请求;
CancelRequest
:A / B / Manager 取消某个请求;
每个操作需要通知给请求交互的双方 A、B;
对于一个新的 Request
,两个玩家的 Client
需要同步到该 Request
信息,获取请求当前状态与一些同步属性等;
由 Player
的 RequestComponent
执行对应 Request
交互、持有 RequestFilter
、同步 Request
;
由 Player
的 RequestFilter
进行 可以发起请求的目标 的注册与更新等;
由RequestMananger
持有实际的 Request
,管理其生命周期;
对于 Request
的数据与操作,需要一些自定义参数,通过 CommonParams 实现;
classDiagram
direction TD
UGameplayRequestUtils ..> UGameplayRequestManager
UGameplayRequestUtils ..> UGameplayRequestComponent
class UGameplayRequestManager {
CreateRequest()
CancelRequest()
ResponseRequest()
ActiveRequests : TMap~uint64|UGameplayRequestBase*~
}
class UGameplayRequestComponent {
SendRequest()
CancelRequest()
AcceptRequest()
RejectRequest()
GetFilter()
Requests : TArray~UGameplayRequestBase*~
Filters : FGameplaySubSystemCollection~UGameplayRequestFilterBase~
}
UGameplayRequestManager --> UGameplayRequestBase
UGameplayRequestComponent --> UGameplayRequestBase
class UGameplayRequestBase {
GetType()
GetFilterType()
State : EGameplayRequestState
Result : EGameplayRequestResult
}
UGameplayRequestComponent --> UGameplayRequestFilterBase
UGameplayRequestFilterBase --> FGameplayRequestFilterTargetData
class UGameplayRequestFilterBase {
GetType()
IsTargetValid()
RegisterPossibleTarget()
UnregisterPossibleTarget()
FilterTargets()
FilterTargetDatas : TArray~FGameplayRequestFilterTargetData~
}
class FGameplayRequestFilterTargetData {
PlayerUID : uint64
bValid : bool
Params : FCommonVariantParams
}
Request Manager
作为全局的 Manager
管理所有 Request
的生命周期,持有 Request
实例;
在这里控制 Request
增加/移除,将其同步 增加/移除 到 Sender
与 Target
的 RequestComponent
上,通过 RequestComponent
同步 Request
给对应的 Client
;
根据 UGameplayRequestBase
注册所有的 RequestCDO
用于生成 Request
实例;
特别的,为了 Request
状态能正确同步与通知,当其 Finish
时,并不会直接将其 Remove
,而是 KeepAlive
,到 KeepAliveDuration
时间结束才将其实例移除;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 UCLASS (meta = (BlueprintSpawnableComponent))class UGameplayRequestManager : public UGameStateComponentBase{ GENERATED_BODY () #pragma region Base protected : UGameplayRequestManager (); virtual void OnInitSelf (AGameStateBase* Owner) override ; virtual void OnUninit () override ; #pragma endregion Base #pragma region Interact public : uint64 CreateRequest (uint32 Type, uint64 SenderUID, uint64 TargetUID, const FCommonVariantParams& Params = {}) ; bool CancelRequest (uint64 RequestID, uint64 CancelUID = 0 , FCommonVariantParams Params = {}) ; bool ResponseRequest (uint64 RequestID, bool bAccept, uint64 ResponseUID = 0 , FCommonVariantParams Params = {}) ; #pragma endregion Interact #pragma region Request public : UGameplayRequestBase* GetRequest (uint64 RequestID) const ; TArray <UGameplayRequestBase*> GetAllRequests () const ; private : void InitRequests () ; void UninitRequests () ; private : void AddRequest (UGameplayRequestBase* Request) ; void RemoveRequest (uint64 RequestID) ; void ClearRequests () ; private : UPROPERTY () TMap <uint32, UGameplayRequestBase*> RequestCDOs; private : UPROPERTY () TMap<uint64, UGameplayRequestBase*> ActiveRequests; UPROPERTY () TMap<uint64, FTimerHandle> RequestKeepAliveTimers; #pragma endregion Request #pragma region Helper private : void ExecuteFunctionForRequestPlayers (UGameplayRequestBase* Request, TFunction<void (UGameplayRequestComponent*)> Callback) ; #pragma endregion Helper };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 #pragma region Base UGameplayRequestManager::UGameplayRequestManager () { SetIsReplicatedByDefault (true ); PrimaryComponentTick.bCanEverTick = false ; } void UGameplayRequestManager::OnInitSelf (AGameStateBase* Owner) { Super::OnInitSelf (Owner); InitRequests (); } void UGameplayRequestManager::OnUninit () { Super::OnUninit (); UninitRequests (); } #pragma endregion Base #pragma region Interact uint64 UGameplayRequestManager::CreateRequest (uint32 Type, uint64 SenderUID, uint64 TargetUID, const FCommonVariantParams& Params) { auto RequestCDO = RequestCDOs.FindRef (Type); if (!IsValid (RequestCDO)) { return 0 ; } if (SenderUID == TargetUID) { return 0 ; } UGameplayRequestBase* NewRequest = NewObject<UGameplayRequestBase>(this , RequestCDO->GetClass ()); if (!IsValid (NewRequest)) return 0 ; uint64 RequestID = NewRequest->Init (SenderUID, TargetUID, Params); if (RequestID <= 0 ) return 0 ; AddRequest ( NewRequest ); float KeepAliveDuration = FMath::Max ( 10.0f , NewRequest->GetKeepAliveTime ()); FTimerHandle& TimerHandle = RequestKeepAliveTimers.FindOrAdd (RequestID); UWorldTimerManager::SetTimer (GetWorld (), TimerHandle, [WeakSelfPtr = TWeakObjectPtr<UGameplayRequestManager>(this ), RequestID = RequestID]() { if (!WeakSelfPtr.IsValid ()) return ; WeakSelfPtr->RemoveRequest ( RequestID ); }, KeepAliveDuration, false ); NewRequest->NotifyCreated (); return RequestID; } bool UGameplayRequestManager::CancelRequest (uint64 RequestID, uint64 CancelUID, FCommonVariantParams Params) { auto Request = GetRequest (RequestID); if (!IsValid (Request)) return false ; if ( !TSet<uint64>{ 0LL , Request->GetSenderUID (), Request->GetTargetUID () }.Contains ( CancelUID )) { return false ; } if (Request->IsFinished ()) { return false ; } Params.SetValue ( "CancelUID" , CancelUID ); Request->Cancel ( Params ); return true ; } bool UGameplayRequestManager::ResponseRequest (uint64 RequestID, bool bAccept, uint64 ResponseUID, FCommonVariantParams Params) { auto Request = GetRequest (RequestID); if (!IsValid (Request)) return false ; if ( !TSet<uint64>{ 0LL , Request->GetTargetUID () }.Contains ( ResponseUID )) { return false ; } if (Request->IsFinished ()) { return false ; } Params.SetValue ( "ResponseUID" , ResponseUID ); if (bAccept) { Request->Accept ( Params ); } else { Request->Reject ( Params ); } return true ; } #pragma endregion Interact #pragma region Request UGameplayRequestBase* UGameplayRequestManager::GetRequest (uint64 RequestID) const { return ActiveRequests.FindRef ( RequestID ); } TArray<UGameplayRequestBase*> UGameplayRequestManager::GetAllRequests () const { TArray <UGameplayRequestBase*> OutArray; for (const auto & [RequestID, Request] : ActiveRequests) { if (!IsValid (Request)) continue ;; OutArray.Add ( Request ); } return OutArray; } void UGameplayRequestManager::InitRequests () { if (!IsStandaloneOrDS (this )) return ; TArray<UClass*> RequestClasses; GetDerivedClasses (UGameplayRequestBase::StaticClass (), RequestClasses, true ); for (auto Class : RequestClasses) { if (!IsValid (Class)) continue ; if (Class->HasAnyClassFlags (CLASS_Abstract)) continue ; auto CDO = Class->GetDefaultObject<UGameplayRequestBase>(); if (!IsValid (CDO)) continue ; RequestCDOs.Add ( CDO->GetType (), CDO ); } } void UGameplayRequestManager::UninitRequests () { if (!IsStandaloneOrDS (this )) return ; ClearRequests (); } void UGameplayRequestManager::AddRequest (UGameplayRequestBase* Request) { if (!IsValid (Request)) return ; ActiveRequests.Add ( Request->GetRequestID (), Request ); ExecuteFunctionForRequestPlayers (Request, [Request](UGameplayRequestComponent* Component) { if (IsValid (Component)) { Component->AddRequest ( Request ); } }); } void UGameplayRequestManager::RemoveRequest (uint64 RequestID) { auto Request = GetRequest (RequestID); if (IsValid (Request)) { ExecuteFunctionForRequestPlayers (Request, [RequestID](UGameplayRequestComponent* Component) { if (IsValid (Component)) { Component->RemoveRequest ( RequestID ); } }); Request->Uninit (); } if (auto * TimerHandle = RequestKeepAliveTimers.Find (RequestID)) { UWorldTimerManager::ClearTimer (GetWorld (), *TimerHandle); RequestKeepAliveTimers.Remove (RequestID); } ActiveRequests.Remove (RequestID); } void UGameplayRequestManager::ClearRequests () { TArray <uint64> RequestIDs; ActiveRequests.GetKeys (RequestIDs); for (auto RequestID : RequestIDs) { RemoveRequest ( RequestID ); } } #pragma endregion Request #pragma region Helper void UGameplayRequestManager::ExecuteFunctionForRequestPlayers (UGameplayRequestBase* Request, TFunction<void (UGameplayRequestComponent*)> Callback) { if (!IsValid (Request)) return ; if (auto Sender = UGameplayRequestUtils::GetComponentByUID (GetWorld (), Request->GetSenderUID ()); IsValid (Sender)) { Callback (Sender); } if (auto Target = UGameplayRequestUtils::GetComponentByUID (GetWorld (), Request->GetTargetUID ()); IsValid (Target)) { Callback (Target); } } #pragma endregion Helper
RequestComponent
作为 Player
组件进行 Request
操作(请求、同意、拒绝、取消);
将与 Player
相关的 Request
同步(当 Player
为 Sender
/ Target
时认为其相关);
通过 GameplaySubSystem 持有每一种 RequestType
对应的 RequestFilter
,进行 FilterTargetData
(可能发起请求的人的相关信息)的更新与同步;
后续每次对 Request
进行 Verify
时,需要通过 Player
上对应 RequestFilter
进行校验是否 IsTargetValid
;
同时客户端可以根据 RequestFilter
上的 FilterTargetDatas
信息,提前进行部分操作(比如 Client
知道该类请求无可以请求对象时、自己 / 对方处于不可请求状态时,进行提示)
这里 ReplicateRequest
时,在 Client
创建对应 UObject
,需要通过 ReplicateSubobjects_OwnerOnly
来进行同步:
当 Request
作为 Subobject
同步下去时,会在 Client
找到其对应的 Outer
(也就是 UGameplayRequestManager
,所以 UGameplayRequestManager
需要开启同步)创建在其 ActorChannel
的 CreateSubObjects
中;
在 RequestComponent
中,由一个同步字段 Requests
持有对应指针;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 bool UPlayerStateComponentBase::ReplicateSubobjects (UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags) { bool WroteSomething = Super::ReplicateSubobjects (Channel, Bunch, RepFlags); if (APlayerStateBase* PS = GetPlayerState (); IsValid (PS) && IsValid (Channel) && IsValid (Channel->Connection)) { if (Channel->Connection->PlayerController == PS->GetPlayerController ()) { WroteSomething |= ReplicateSubobjects_OwnerOnly (Channel, Bunch, RepFlags); } } return WroteSomething; }
以下为具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 UCLASS (meta=(BlueprintSpawnableComponent))class UGameplayRequestComponent : public UPlayerStateComponentBase, public IGameplaySubSystemCollectionOwnerInterface{ GENERATED_BODY () #pragma region Base public : UGameplayRequestComponent (); virtual void GetLifetimeReplicatedProps (TArray<FLifetimeProperty>& OutLifetimeProps) const override ; protected : virtual void OnInitSelf () override ; virtual void OnUninit () override ; #pragma endregion Base #pragma region Interact public : bool SendRequest (uint32 Type, uint64 TargetUID, const FCommonVariantParams& Params = {}) ; bool CancelRequest (uint64 RequestID, const FCommonVariantParams& Params = {}) ; bool AcceptRequest (uint64 RequestID, const FCommonVariantParams& Params = {}) ; bool RejectRequest (uint64 RequestID, const FCommonVariantParams& Params = {}) ; #pragma region C2S public : UFUNCTION (Reliable, Server) void C2S_SendRequest (uint32 Type, uint64 TargetUID, const FCommonVariantParams& Params = {}) ; UFUNCTION (Reliable, Server) void C2S_AcceptRequest (uint64 RequestID, const FCommonVariantParams& Params = {}) ; UFUNCTION (Reliable, Server) void C2S_RejectRequest (uint64 RequestID, const FCommonVariantParams& Params = {}) ; UFUNCTION (Reliable, Server) void C2S_CancelRequest (uint64 RequestID, const FCommonVariantParams& Params = {}) ; #pragma endregion C2S #pragma endregion Interact #pragma region Request public : UFUNCTION () UGameplayRequestBase* GetRequest (uint64 RequestID) ; UFUNCTION () TArray <UGameplayRequestBase*> GetRequestsByType (uint32 Type); public : void AddRequest (UGameplayRequestBase* Request) ; void RemoveRequest (uint64 RequestID) ; void ClearRequests () ; private : UFUNCTION () void OnRep_Requests () ; private : UPROPERTY (ReplicatedUsing=OnRep_Requests) TArray <UGameplayRequestBase*> Requests; #pragma endregion Request #pragma region Filter protected : virtual TArray<UClass*> GetFilterClasses () const { return {}; } public : UFUNCTION () UGameplayRequestFilterBase* GetFilter (UClass* InClass) ; protected : virtual bool ReplicateSubobjects_OwnerOnly (UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags) override ; public : DECLARE_GAMEPLAYSUBSYSTEM_ACCESSORS_BY_NAME (Filter, UGameplayRequestFilterBase, Filters); virtual TArray<FGameplaySubSystemCollectionBase*> GetSubSystemCollectionPtrs () override { return { &Filters }; } private : FGameplaySubSystemCollection <UGameplayRequestFilterBase> Filters; #pragma endregion Filter #pragma region Debug public : UFUNCTION (Reliable, Server) void C2S_DebugSendRequest (uint32 Type, uint64 SenderUID, uint64 TargetUID) ; UFUNCTION (Reliable, Server) void C2S_DebugAcceptRequests (uint64 PlayerUID) ; UFUNCTION (Reliable, Server) void C2S_DebugRejectRequests (uint64 PlayerUID) ; UFUNCTION (Reliable, Server) void C2S_DebugCancelRequests (uint64 PlayerUID) ; #pragma endregion Debug };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 #pragma region Base UGameplayRequestComponent::UGameplayRequestComponent () { SetIsReplicatedByDefault (true ); PrimaryComponentTick.bCanEverTick = false ; } void UGameplayRequestComponent::GetLifetimeReplicatedProps (TArray<FLifetimeProperty>& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps (OutLifetimeProps); FDoRepLifetimeParams OwnerOnlyRepParams; OwnerOnlyRepParams.bIsPushBased = true ; OwnerOnlyRepParams.Condition = COND_OwnerOnly; DOREPLIFETIME_WITH_PARAMS_FAST (UGameplayRequestComponent, Requests, OwnerOnlyRepParams); } void UGameplayRequestComponent::OnInitSelf () { Super::OnInitSelf (); if (IsStandaloneOrDS (this )) { Filters.Init (this , GetFilterClasses ()); } } void UGameplayRequestComponent::OnUninit () { Super::OnUninit (); Filters.Uninit (); ClearRequests (); } #pragma endregion Base #pragma region Interact bool UGameplayRequestComponent::SendRequest (uint32 Type, uint64 TargetUID, const FCommonVariantParams& Params) { if (!IsStandaloneOrDS (this )) return false ; auto Manager = UGameplayRequestUtils::GetManager (GetWorld ()); if (!IsValid (Manager)) return false ; bool bSucceed = Manager->CreateRequest (Type, GetUID (), TargetUID, Params); return bSucceed; } bool UGameplayRequestComponent::AcceptRequest (uint64 RequestID, const FCommonVariantParams& Params) { if (!IsStandaloneOrDS (this )) return false ; auto Manager = UGameplayRequestUtils::GetManager (GetWorld ()); if (!IsValid (Manager)) return false ; bool bSucceed = Manager->ResponseRequest (RequestID, true , GetUID (), Params); return bSucceed; } bool UGameplayRequestComponent::RejectRequest (uint64 RequestID, const FCommonVariantParams& Params) { if (!IsStandaloneOrDS (this )) return false ; auto Manager = UGameplayRequestUtils::GetManager (GetWorld ()); if (!IsValid (Manager)) return false ; bool bSucceed = Manager->ResponseRequest (RequestID, false , GetUID (), Params); return bSucceed; } bool UGameplayRequestComponent::CancelRequest (uint64 RequestID, const FCommonVariantParams& Params) { if (!IsStandaloneOrDS (this )) return false ; auto Manager = UGameplayRequestUtils::GetManager (GetWorld ()); if (!IsValid (Manager)) return false ; bool bSucceed = Manager->CancelRequest (RequestID, GetUID (), Params); return bSucceed; } #pragma region C2S void UGameplayRequestComponent::C2S_SendRequest_Implementation (uint32 Type, uint64 TargetUID, const FCommonVariantParams& Params) { SendRequest (Type, TargetUID, Params); } void UGameplayRequestComponent::C2S_AcceptRequest_Implementation (uint64 RequestID, const FCommonVariantParams& Params) { AcceptRequest (RequestID, Params); } void UGameplayRequestComponent::C2S_RejectRequest_Implementation (uint64 RequestID, const FCommonVariantParams& Params) { RejectRequest (RequestID, Params); } void UGameplayRequestComponent::C2S_CancelRequest_Implementation (uint64 RequestID, const FCommonVariantParams& Params) { CancelRequest (RequestID, Params); } #pragma endregion C2S #pragma endregion Interact #pragma region Request UGameplayRequestBase* UGameplayRequestComponent::GetRequest (uint64 RequestID) { for (auto Request : Requests) { if (!IsValid (Request)) continue ; if (Request->GetRequestID () == RequestID) { return Request; } } return nullptr ; } TArray<UGameplayRequestBase*> UGameplayRequestComponent::GetRequestsByType (uint32 Type) { TArray <UGameplayRequestBase*> OutArray; for (auto Request : Requests) { if (!IsValid (Request)) continue ; if (Request->GetType () == Type) { OutArray.Add ( Request ); } } return OutArray; } void UGameplayRequestComponent::AddRequest (UGameplayRequestBase* Request) { if (!IsValid (Request)) return ; GetRequests_Mutable ().AddUnique ( Request ); } void UGameplayRequestComponent::RemoveRequest (uint64 RequestID) { auto Request = GetRequest ( RequestID ); if (!IsValid (Request)) return ; GetRequests_Mutable ().Remove ( Request ); } void UGameplayRequestComponent::ClearRequests () { GetRequests_Mutable ().Empty (); } void UGameplayRequestComponent::OnRep_Requests () { for (auto Request : Requests) { if (!IsValid (Request)) continue ; Request->NotifyCreated (); } } #pragma endregion Request #pragma region Filter bool UGameplayRequestComponent::ReplicateSubobjects_OwnerOnly (UActorChannel* Channel, FOutBunch* Bunch, FReplicationFlags* RepFlags) { bool WroteSomething = false ; WroteSomething |= Super::ReplicateSubobjects_OwnerOnly (Channel, Bunch, RepFlags); WroteSomething |= Filters.ReplicateSubSystems ( Channel, Bunch, RepFlags ); if (IsValid (Channel) && RepFlags->bNetInitial == false ) { for (auto Request : Requests) { if (!IsValid (Request)) continue ; WroteSomething |= Channel->ReplicateSubobject (Request, *Bunch, *RepFlags); } } return WroteSomething; } UGameplayRequestFilterBase* UGameplayRequestComponent::GetFilter (UClass* InClass) { for (auto Filter : GetFilters ()) { if (IsValid (Filter) && Filter->GetClass () == InClass) { return Filter; } } return nullptr ; } #pragma endregion Filter #pragma region Debug void UGameplayRequestComponent::C2S_DebugSendRequest_Implementation (uint32 Type, uint64 SenderUID, uint64 TargetUID) {#if UE_BUILD_SHIPPING return ; #endif SenderUID = SenderUID > 0 ? SenderUID : GetUID (); TargetUID = TargetUID > 0 ? TargetUID : GetUID (); auto Component = UGameplayRequestUtils::GetComponentByUID (GetWorld (), SenderUID); if (!IsValid (Component)) return ; Component->SendRequest ( Type, TargetUID ); } void UGameplayRequestComponent::C2S_DebugAcceptRequests_Implementation (uint64 PlayerUID) {#if UE_BUILD_SHIPPING return ; #endif PlayerUID = PlayerUID > 0 ? PlayerUID : GetUID (); auto Component = UGameplayRequestUtils::GetComponentByUID (GetWorld (), PlayerUID); if (!IsValid (Component)) return ; TArray <UGameplayRequestBase*> PlayerRequests = Component->GetRequests (); for (auto Request : PlayerRequests) { if (!IsValid (Request)) continue ; Component->AcceptRequest (Request->GetRequestID ()); } } void UGameplayRequestComponent::C2S_DebugRejectRequests_Implementation (uint64 PlayerUID) {#if UE_BUILD_SHIPPING return ; #endif PlayerUID = PlayerUID > 0 ? PlayerUID : GetUID (); auto Component = UGameplayRequestUtils::GetComponentByUID (GetWorld (), PlayerUID); if (!IsValid (Component)) return ; TArray <UGameplayRequestBase*> PlayerRequests = Component->GetRequests (); for (auto Request : PlayerRequests) { if (!IsValid (Request)) continue ; Component->RejectRequest (Request->GetRequestID ()); } } void UGameplayRequestComponent::C2S_DebugCancelRequests_Implementation (uint64 PlayerUID) {#if UE_BUILD_SHIPPING return ; #endif PlayerUID = PlayerUID > 0 ? PlayerUID : GetUID (); auto Component = UGameplayRequestUtils::GetComponentByUID (GetWorld (), PlayerUID); if (!IsValid (Component)) return ; TArray <UGameplayRequestBase*> PlayerRequests = Component->GetRequests (); for (auto Request : PlayerRequests) { if (!IsValid (Request)) continue ; Component->CancelRequest (Request->GetRequestID ()); } } #pragma endregion Debug
RequestFilter
对于每一类 Request
,每个玩家身上会有一个对应的 RequestFilter
,用来更新可能可以发起请求的目标;
通过 Register / Unregister
PossibleTarget
,然后更新对应的 FilterTargetData
并同步给 Client
;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 USTRUCT ()struct FGameplayRequestFilterTargetData { GENERATED_BODY () FGameplayRequestFilterTargetData () = default ; FGameplayRequestFilterTargetData (uint64 PlayerUID) : PlayerUID (PlayerUID) {} public : bool operator ==(const FGameplayRequestFilterTargetData& Other) const { if (PlayerUID != Other.PlayerUID) return false ; return true ; } bool operator !=(const FGameplayRequestFilterTargetData& Other) const { return !(*this == Other); } public : FString ToString () const ; public : UPROPERTY () uint64 PlayerUID; UPROPERTY () bool bValid = false ; UPROPERTY () FCommonVariantParams Params = {}; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 UCLASS (Abstract)class UGameplayRequestFilterBase : public UGameplaySubSystemBase{ GENERATED_BODY () #pragma region Config public : virtual uint32 GetType () const PURE_VIRTUAL (UGameplayRequestFilterBase::GetType, return 0 ;) ; #pragma endregion Config #pragma region Base protected : virtual void OnInit () override ; virtual void OnUninit () override ; virtual FString ToString () const ; private : TWeakObjectPtr <class UGameplayRequestComponent > Owner; #pragma endregion Base #pragma region Replicate protected : virtual void GetLifetimeReplicatedProps (TArray<FLifetimeProperty> & OutLifetimeProps) const override ; protected : virtual bool IsSupportedForNetworking () const override { return true ; } #pragma endregion Replicate #pragma region FilterTarget public : UFUNCTION () virtual bool IsTargetValid (uint64 TargetUID) const ; UFUNCTION () FGameplayRequestFilterTargetData GetFilterTargetData (uint64 PlayerUID) const ; protected : void FilterTargets () ; protected : void ClearPossibleTargets () ; void RegisterPossibleTarget (uint64 TargetUID) ; void UnregisterPossibleTarget (uint64 TargetUID) ; protected : virtual bool CheckSelfConditionValid () { return true ; } virtual bool CheckTargetConditionValid (uint64 TargetUID) { return true ; } private : UFUNCTION () void OnRep_FilterTargetDatas () ; private : UPROPERTY (Replicated, ReplicatedUsing = OnRep_FilterTargetDatas) TArray <FGameplayRequestFilterTargetData> FilterTargetDatas; #pragma endregion FilterTarget #pragma region Helper protected : uint64 GetUID () const ; APlayerStateBase* GetPlayerState () const ; float GetTimeNow () const ; #pragma endregion Helper };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 #pragma region Base void UGameplayRequestFilterBase::OnInit () { Super::OnInit (); Owner = Cast<UGameplayRequestComponent>(GetOuter ()); } void UGameplayRequestFilterBase::OnUninit () { Super::OnUninit (); } FString UGameplayRequestFilterBase::ToString () const { return FString::Printf (TEXT ("[%llu|%s]" ), GetUID (), *GetName ()); } #pragma endregion Base #pragma region Replicate void UGameplayRequestFilterBase::GetLifetimeReplicatedProps (TArray<FLifetimeProperty>& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps (OutLifetimeProps); FDoRepLifetimeParams OwnerOnlyRepParams; OwnerOnlyRepParams.bIsPushBased = true ; OwnerOnlyRepParams.Condition = COND_OwnerOnly; DOREPLIFETIME_WITH_PARAMS_FAST (UGameplayRequestFilterBase, FilterTargetDatas, OwnerOnlyRepParams); } #pragma endregion Replicate #pragma region FilterTarget bool UGameplayRequestFilterBase::IsTargetValid (uint64 TargetUID) const { const auto & Data = GetFilterTargetData (TargetUID); return Data.bValid; } FGameplayRequestFilterTargetData UGameplayRequestFilterBase::GetFilterTargetData (uint64 PlayerUID) const { for (const auto & Data : FilterTargetDatas) { if (Data.PlayerUID == PlayerUID) { return Data; } } return {}; } void UGameplayRequestFilterBase::ClearPossibleTargets () { if (!IsStandaloneOrDS (this )) return ; GetFilterTargetDatas_Mutable ().Empty (); } void UGameplayRequestFilterBase::RegisterPossibleTarget (uint64 TargetUID) { if (!IsStandaloneOrDS (this )) return ; if (FilterTargetDatas.Contains ( TargetUID )) return ; FilterTargetDatas.Add ( { TargetUID } ); } void UGameplayRequestFilterBase::UnregisterPossibleTarget (uint64 TargetUID) { if (!IsStandaloneOrDS (this )) return ; if (!FilterTargetDatas.Contains ( TargetUID )) return ; FilterTargetDatas.Remove ( TargetUID ); } void UGameplayRequestFilterBase::FilterTargets () { bool bSelfConditionValid = CheckSelfConditionValid (); for (auto & Data : FilterTargetDatas) { bool bTargetConditionValid = CheckTargetConditionValid (Data.PlayerUID); Data.bValid = (bSelfConditionValid && bTargetConditionValid); Data.Params.Clear (); Data.Params.SetValue ( "bSelfConditionValid" , bSelfConditionValid ); Data.Params.SetValue ( "bTargetConditionValid" , bTargetConditionValid ); } MARK_PROPERTY_DIRTY_FROM_NAME (UGameplayRequestFilterBase, FilterTargetDatas, this ); OnRep_FilterTargetDatas (); } void UGameplayRequestFilterBase::OnRep_FilterTargetDatas () {} #pragma endregion FilterTarget #pragma region Helper uint64 UGameplayRequestFilterBase::GetUID () const { if (!Owner.IsValid ()) return 0 ; return Owner->GetUID (); } APlayerStateBase* UGameplayRequestFilterBase::GetPlayerState () const { if (!Owner.IsValid ()) return nullptr ; return Owner->GetPlayerState<APlayerStateBase>(); } float UGameplayRequestFilterBase::GetTimeNow () const { return UFunctionLibrary::GetGameServerTimeWithSecond (GetWorld ()); } #pragma endregion Helper
Request
一个 Request
由 RequestManager
持有实际的 Request
;
通过 Sender
、Target
的 RequestComponent
将其同步到对应 Client
;
对于 Request
,定义基本属性、状态以及提供生命周期相关回调;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 UCLASS (Abstract)class UGameplayRequestBase : public UObject{ GENERATED_BODY () #pragma region Config public : UFUNCTION () virtual uint32 GetType () const PURE_VIRTUAL (UGameplayRequestBase::GetType, return 0 ;) ; virtual UClass* GetFilterType () const PURE_VIRTUAL (UGameplayRequestBase::GetFilterType, return UGameplayRequestFilterBase::StaticClass();) ; #pragma endregion Config #pragma region Base public : virtual FString ToString () const ; uint64 Init (uint64 SenderUID, uint64 TargetUID, const FCommonVariantParams& InParams) ; void Uninit () ; private : inline static uint64 REQUEST_ID_COUNTER = 0 ; #pragma endregion Base #pragma region Replicate protected : virtual void GetLifetimeReplicatedProps (TArray<FLifetimeProperty>& OutLifetimeProps) const override ; private : virtual bool IsSupportedForNetworking () const override { return true ; } #pragma endregion Replicate #pragma region Validate public : bool ValidateRequest () ; #pragma endregion Validata #pragma region Timeout private : void RegisterTimeoutTimer () ; private : FTimerHandle TimeoutTimer; #pragma endregion Timeout #pragma region Interact private : friend class UGameplayRequestManager ; private : void Accept (const FCommonVariantParams& Params = {}) ; void Reject (const FCommonVariantParams& Params = {}) ; void Cancel (const FCommonVariantParams& Params = {}) ; void Timeout () ; void Interrupt () ; protected : virtual void OnAccepted () {}; virtual void OnRejected () {}; virtual void OnCanceled () {}; #pragma endregion Interact #pragma region Create public : void NotifyCreated () ; private : bool bHasNotifyCreated = false ; #pragma endregion Create #pragma region Complete private : void NotifyCompleted (EGameplayRequestResult InResult) ; protected : virtual void OnCompleted (EGameplayRequestResult InResult) {} private : UFUNCTION () void OnRep_Result () ; private : UPROPERTY (Replicated, ReplicatedUsing=OnRep_Result) EGameplayRequestResult Result = EGameplayRequestResult::None; #pragma endregion Complete #pragma region State private : void UpdateState (EGameplayRequestState InState) ; protected : virtual void OnStateChanged (EGameplayRequestState) {} private : UFUNCTION () virtual void OnRep_State () ; private : UPROPERTY (Replicated, ReplicatedUsing=OnRep_State) EGameplayRequestState State = EGameplayRequestState::Invalid; #pragma endregion State #pragma region KeepAlive public : float GetKeepAliveTime () const { return KeepAliveTime; } private : float KeepAliveTime = 60.0f ; #pragma endregion KeepAlive #pragma region Params public : void CombineParams (const FCommonVariantParams& Params) ; private : UPROPERTY (Replicated) FCommonVariantParams RequestParams; #pragma endregion Params #pragma region Data private : UFUNCTION () void OnRep_RequestID () ; private : UPROPERTY (Replicated) uint64 SenderUID = 0 ; UPROPERTY (Replicated) uint64 TargetUID = 0 ; UPROPERTY (Replicated) float CreateTime = 0.0f ; UPROPERTY (Replicated) float TimeoutDuration = 30.0f ; UPROPERTY (Replicated, ReplicatedUsing=OnRep_RequestID) uint64 RequestID = 0 ; #pragma endregion Data #pragma region Helper public : bool IsFinished () const ; protected : float GetTimeNow () const ; float GetRunningTime () const ; #pragma endregion Helper };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 #pragma region Base FString UGameplayRequestBase::ToString () const { return FString::Printf (TEXT ("{[ID=%llu][Type=%d][UID=%llu->%llu][Params=%s][%s][Time=%.f+%.f(s)][%.f(s), %.f(s)]}" ), RequestID, GetType (), SenderUID, TargetUID, *RequestParams.ToString (), *UStringUtils::ToString (State), CreateTime, GetRunningTime (), TimeoutDuration, KeepAliveTime ); } uint64 UGameplayRequestBase::Init (uint64 InSenderUID, uint64 InTargetUID, const FCommonVariantParams& InParams) { if (!IsStandaloneOrDS (this )) return 0 ; SetRequestID ( ++REQUEST_ID_COUNTER ); SetSenderUID ( InSenderUID ); SetTargetUID ( InTargetUID ); SetRequestParams ( InParams ); SetState ( EGameplayRequestState::Waiting ); SetCreateTime ( GetTimeNow () ); SetTimeoutDuration ( 30.0f ); KeepAliveTime = FMath::Max (KeepAliveTime, TimeoutDuration); bool bValid = ValidateRequest (); if (bValid == false ) { return 0 ; } RegisterTimeoutTimer (); return RequestID; } void UGameplayRequestBase::Uninit () { UWorldTimerManager::ClearTimer (GetWorld (), TimeoutTimer); } #pragma endregion Base #pragma region Replicate void UGameplayRequestBase::GetLifetimeReplicatedProps (TArray<FLifetimeProperty>& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps (OutLifetimeProps); FDoRepLifetimeParams SharedParams; SharedParams.bIsPushBased = true ; DOREPLIFETIME_WITH_PARAMS_FAST (UGameplayRequestBase, RequestID, SharedParams); DOREPLIFETIME_WITH_PARAMS_FAST (UGameplayRequestBase, SenderUID, SharedParams); DOREPLIFETIME_WITH_PARAMS_FAST (UGameplayRequestBase, TargetUID, SharedParams); DOREPLIFETIME_WITH_PARAMS_FAST (UGameplayRequestBase, RequestParams, SharedParams); DOREPLIFETIME_WITH_PARAMS_FAST (UGameplayRequestBase, CreateTime, SharedParams); DOREPLIFETIME_WITH_PARAMS_FAST (UGameplayRequestBase, TimeoutDuration, SharedParams); DOREPLIFETIME_WITH_PARAMS_FAST (UGameplayRequestBase, RequestID, SharedParams); DOREPLIFETIME_WITH_PARAMS_FAST (UGameplayRequestBase, State, SharedParams); DOREPLIFETIME_WITH_PARAMS_FAST (UGameplayRequestBase, Result, SharedParams); } #pragma endregion Replicate #pragma region Validate bool UGameplayRequestBase::ValidateRequest () { if (GetType () <= 0 ) return false ; if (auto Filter = UGameplayRequestUtils::GetPlayerFilterByUID (GetWorld (), SenderUID, GetFilterType ()); IsValid (Filter)) { if (!Filter->IsTargetValid (TargetUID)) return false ; } return true ; } #pragma endregion Validate #pragma region Timeout void UGameplayRequestBase::RegisterTimeoutTimer () { if (TimeoutDuration <= 0.0f ) return ; UWorldTimerManager::SetTimer (GetWorld (), TimeoutTimer, [WeakSelfPtr = TWeakObjectPtr<UGameplayRequestBase>(this )]() { if (!WeakSelfPtr.IsValid ()) return ; WeakSelfPtr->Timeout (); }, TimeoutDuration, false ); } #pragma endregion Timeout #pragma region Interact void UGameplayRequestBase::Accept (const FCommonVariantParams& Params) { if (IsFinished ()) return ; CombineParams (Params); UpdateState (EGameplayRequestState::Accepted); OnAccepted (); NotifyCompleted (EGameplayRequestResult::Success); } void UGameplayRequestBase::Reject (const FCommonVariantParams& Params) { if (IsFinished ()) return ; CombineParams (Params); UpdateState (EGameplayRequestState::Rejected); OnRejected (); NotifyCompleted (EGameplayRequestResult::Failed); } void UGameplayRequestBase::Cancel (const FCommonVariantParams& Params) { if (IsFinished ()) return ; CombineParams (Params); UpdateState (EGameplayRequestState::Canceled); OnCanceled (); NotifyCompleted (EGameplayRequestResult::Canceled); } void UGameplayRequestBase::Timeout () { if (IsFinished ()) return ; Cancel ( { "CancelReason" , EGameplayRequestCancelReason::Timeout } ); } void UGameplayRequestBase::Interrupt () { if (IsFinished ()) return ; Cancel ( {"CancelReason" , EGameplayRequestCancelReason::Interrupt} ); } #pragma endregion Interact #pragma region Create void UGameplayRequestBase::NotifyCreated () { if (bHasNotifyCreated) return ; bHasNotifyCreated = true ; } #pragma endregion Create #pragma region Complete void UGameplayRequestBase::NotifyCompleted (EGameplayRequestResult InResult) { SetResult (InResult); OnCompleted (InResult); OnRep_Result (); } void UGameplayRequestBase::OnRep_Result () {} #pragma endregion Complete #pragma region State void UGameplayRequestBase::UpdateState (EGameplayRequestState InState) { if (InState == State) return ; SetState (InState); OnStateChanged (InState); OnRep_State (); } void UGameplayRequestBase::OnRep_State () {} #pragma endregion State #pragma region Params void UGameplayRequestBase::CombineParams (const FCommonVariantParams& Params) { if (Params.IsEmpty ()) return ; SetRequestParams ( RequestParams + Params ); } #pragma endregion Params #pragma region Data void UGameplayRequestBase::OnRep_RequestID () { } #pragma endregion Data #pragma region Helper bool UGameplayRequestBase::IsFinished () const { return State != EGameplayRequestState::Waiting && State != EGameplayRequestState::Invalid; } float UGameplayRequestBase::GetTimeNow () const { return UFunctionLibrary::GetGameServerTimeWithSecond (GetWorld ()); } float UGameplayRequestBase::GetRunningTime () const { float TimeNow = GetTimeNow (); return TimeNow - CreateTime; } #pragma endregion Helper
GameplayRequestUtils
通过该 Utils
提供一些方法给外部使用;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 UCLASS ()class UGameplayRequestUtils : public UBlueprintFunctionLibrary{ GENERATED_BODY () #pragma region Base public : UFUNCTION () static UGameplayRequestManager* GetManager (UWorld* InWorld) ; UFUNCTION () static UGameplayRequestComponent* GetComponent (APlayerStateBase* PS) ; UFUNCTION () static UGameplayRequestComponent* GetComponentByUID (UWorld* InWorld, uint64 UID) ; #pragma endregion Base #pragma region Filter public : UFUNCTION () static UGameplayRequestFilterBase* GetPlayerFilter (APlayerStateBase* PS, UClass* FilterType) ; UFUNCTION () static UGameplayRequestFilterBase* GetPlayerFilterByUID (UWorld* InWorld, uint64 UID, UClass* FilterType) ; #pragma endregion Filter #pragma region Request public : UFUNCTION () static UGameplayRequestBase* GetRequest (UWorld* InWorld, uint64 RequestID) ; UFUNCTION () static UGameplayRequestBase* GetPlayerRequest (APlayerStateBase* PS, uint64 RequestID) ; UFUNCTION () static TArray<UGameplayRequestBase*> GetPlayerRequests (APlayerStateBase* PS) ; UFUNCTION () static TArray<UGameplayRequestBase*> GetPlayerRequestsByType (APlayerStateBase* PS, uint32 Type) ; #pragma endregion Request };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 #pragma region Base UGameplayRequestManager* UGameplayRequestUtils::GetManager (UWorld* InWorld) { if (InWorld == nullptr ) return nullptr ; return UFunctionLibrary::GetGameStateComponentFromWorld<UGameplayRequestManager>(InWorld); } UGameplayRequestComponent* UGameplayRequestUtils::GetComponent (APlayerStateBase* PS) { if (!IsValid (PS)) return nullptr ; return UFunctionLibrary::GetPlayerStateComponent<UGameplayRequestComponent>(PS); } UGameplayRequestComponent* UGameplayRequestUtils::GetComponentByUID (UWorld* InWorld, uint64 UID) { if (!IsValid (InWorld)) return nullptr ; auto PS = UFunctionLibrary::GetPlayerStateByUID (InWorld, UID); if (!IsValid (PS)) return nullptr ; return GetComponent (PS); } #pragma endregion Base #pragma region Filter UGameplayRequestFilterBase* UGameplayRequestUtils::GetPlayerFilter (APlayerStateBase* PS, UClass* FilterType) { auto Component = GetComponent (PS); if (!IsValid (Component)) return nullptr ; return Component->GetFilter (FilterType); } UGameplayRequestFilterBase* UGameplayRequestUtils::GetPlayerFilterByUID (UWorld* InWorld, uint64 UID, UClass* FilterType) { auto Component = GetComponentByUID (InWorld, UID); if (!IsValid (Component)) return nullptr ; return Component->GetFilter (FilterType); } #pragma endregion Filter #pragma region Request UGameplayRequestBase* UGameplayRequestUtils::GetRequest (UWorld* InWorld, uint64 RequestID) { if (!IsValid (InWorld)) return nullptr ; if (IsStandaloneOrDS (InWorld)) { auto Manager = GetManager (InWorld); if (IsValid (Manager)) { return Manager->GetRequest ( RequestID ); } } else if (IsClient (InWorld)) { APlayerStateBase* HostPS = UFunctionLibrary::GetPlayerStateByUID (InWorld, UFunctionLibrary::GetMainPlayerUID (InWorld)); if (IsValid (HostPS)) { return GetPlayerRequest ( HostPS, RequestID ); } } return nullptr ; } UGameplayRequestBase* UGameplayRequestUtils::GetPlayerRequest (APlayerStateBase* PS, uint64 RequestID) { auto Component = GetComponent (PS); if (!IsValid (Component)) return nullptr ; return Component->GetRequest ( RequestID ); } TArray<UGameplayRequestBase*> UGameplayRequestUtils::GetPlayerRequests (APlayerStateBase* PS) { auto Component = GetComponent (PS); if (!IsValid (Component)) return {}; return Component->GetRequests (); } TArray<UGameplayRequestBase*> UGameplayRequestUtils::GetPlayerRequestsByType (APlayerStateBase* PS, uint32 Type) { auto Requests = GetPlayerRequests (PS); TArray <UGameplayRequestBase*> OutRequests; for (auto Request : Requests) { if (!IsValid (Request)) continue ; if (Request->GetType () == Type) { OutRequests.Add ( Request ); } } return OutRequests; } #pragma endregion Request
Sample
创建一个请求时,只需要创建该类型对应的 RequestFilter
、Request
即可;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 UCLASS ()class UGameplayRequest_Test : public UGameplayRequestBase{ GENERATED_BODY () #pragma region Config public : virtual uint32 GetType () const override { return 1001 ; } virtual UClass* GetFilterType () const override { return UGameplayRequestFilter_Test::StaticClass (); } #pragma endregion Config private : virtual void OnAccepted () override {}; virtual void OnRejected () override {}; virtual void OnCanceled () override {}; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 UCLASS ()class UGameplayRequestFilter_Test : public UGameplayRequestFilterBase{ GENERATED_BODY () #pragma region Config public : virtual uint32 GetType () const override { return 1001 ; } #pragma endregion Config public : virtual bool IsTargetValid (uint64 TargetUID) const override { return true ; }; };