天天看點

postman rpc本地調用_[UE4 Network] RPC 之哪裡調用 和 哪裡執行

postman rpc本地調用_[UE4 Network] RPC 之哪裡調用 和 哪裡執行

區分調用 和 執行

對于由 Client , Server , NetMulticast 說明符聲明的三種類型的 RPC 函數,要了解它們的使用規範首先我們要區分調用 和 執行概念的差別。

假設我們聲明了一個名為 FunctionName() 的 RPC 函數,使用者并不需要實作這個函數,而是要額外的實作一個名為 FunctionName_Implementation() 函數處理真正要執行邏輯。而這個 FunctionName() 函數會由 UHT 來為我們實作來處理 RPC 調用的邏輯。

我們将使用者調用 FunctionName() 函數行為稱為使用者發起了這個 RPC 函數的調用,而将真正執行到 FunctionName_Implementation() 函數時稱為這個 RPC 函數被執行了。

RPC 調用和執行的簡單流程

發送端調用流程
  1. 假設由 UFUNCTION( Client ) 聲明了一個名為 FunctionName() 的 RPC 函數。這個函數會由 UHT 實作,使用者無需對它實作,但使用者需要額外的實作一個 FunctionName_Implementation() 函數處理函數的真正執行邏輯。

    UHT 首先會生成一個名為 execFunctionName() 函數,由它來調用使用者定義的 FunctionName_Implementation() 函數,并會以 "FunctionName" 為函數名,來将這個 execFunctionName() 函數注冊為目前 UClass 類的一個 UFunction 對象。

    然後 UHT 會生成 FunctionName() 函數的實作,其内就是擷取自己所屬 UClass 中名為 "FunctionName" 的 UFunction 對象,并調用 UObject::ProcessEvent() 處理這個 UFunction 的執行邏輯。是以 RPC 函數如何被遠端調用,就是由 UObject::ProcessEvent() 來實作的

  2. 在 UObject::ProcessEvent() 中首先會調用 AActor::GetFunctionCallspace() 來擷取一個 FunctionCallspace 枚舉來辨別此 UFunction 的執行空間。其取值如下

    FunctionCallspace::Absorbed 終止執行,即不會本地執行也不會遠端執行

    FunctionCallspace::Remote 遠端執行,需要通過UNetDriver來遠端調用

    FunctionCallspace::Local 本地執行,僅本地執行

    在擷取了函數的執行空間後,若需要本地執行則本地執行,若還需要遠端執行則參見 UObject::ProcessEvent() 會回調自己的 UObject::CallRemoteFunction() 來處理遠端執行的邏輯。

    參見 AActor::CallRemoteFunction() 中,會調用UNetDriver::ProcessRemoteFunction() 來處理 RPC 遠端調用。最終會調用 FRepLayout::SendPropertiesForRPC() 來将 RPC 調用實參封裝成一個 RPC Bunch ,并通過 UActorChannel 通道發送給對端

接收端執行流程
  1. 在接收到的 Bunch 中通常會包含許多個屬性塊,來儲存 AActor 和其子對象的同步屬性。且每個對象都對應于一個 FObjectReplicator 對象來處理其屬性塊資料的解析。是以在通道的 UActorChannel::ProcessBunch() 中解析 Bunch 中每個屬性塊時,會擷取每個屬性塊對應的 FObjectReplicator 對象,并調用其 FObjectReplicator::ReceivedBunch() 來解析此對象的屬性。
  2. 在 FObjectReplicator::ReceivedBunch() 中若發現一個和 UFunction 相關的資料塊,通常表示這是對端發送來的 RPC 調用的資料。此時會調用 FObjectReplicator::ReceivedRPC() 來處理這個 UFunction 的執行邏輯。大緻如下。

    首先會先驗證此 UFunction 是否為能在本機合法執行的 RPC 函數

    >>1 若本機為用戶端,則被執行的需要為 UFUNCTION( Client ) 或 UFUNCTION( NetMulticast ) 修飾的函數 ,否則不能執行

    >>2 若本機為伺服器,則被執行的需要為 UFUNCTION( Server ) 修飾的函數 ,且同時要求目前連結擁有這個 AActor 才能執行,否則不能執行

    在驗證可以調用後,會擷取這個 UFunction 對應的 FRepLayout

    并調用 FRepLayout::ReceivePropertiesForRPC() 解析 Bunch 中的 RPC 調用實參。

    然後會傳入調用實參,并以目前正在解析的這個屬性塊所屬的對象作為 this 來調用 UObject::ProcessEvent() 來執行這個 UFunction

    通常此函數會在接收端立即執行,參見 RPC 調用的一些細節 中 [ 可靠 RPC 在用戶端的延遲執行 ] 的分析,在用戶端調用一個可靠的 RPC 函數,且其包含未求解成功的對象引用類型的實參時,還可能發生延遲執行。

三種 RPC 函數

相關概念的簡介

  1. 由 AActor->LocalRole == ROLE_Authority 檢測是否為本地權威角色
  2. 由 AActor::GetNetConnection() != NULL 檢測 AActor 是否有所屬連結
  3. 由 AActor->RemoteRole != ROLE_None 檢測 AActor 是否為參與屬性同步
UFUNCTION( Server )

用于聲明由用戶端發起調用,在伺服器執行的 RPC 函數。

無論是否可靠,構造的 RPC Bunch 都會由 UChannel::SendBunch() 立即發送。

執行空間
  1. 在伺服器調用時僅本地執行
  2. 在用戶端調用時,對于本地權威角色的 AActor ,若其參與屬性同步且有所屬連結則遠端執行,否則僅本地執行
  3. 在用戶端調用時,對于非本地權威角色的 AActor ,若其參與屬性同步且有所屬連結則遠端執行,否則終止執行
遠端執行的過濾情況
  1. 若是不可靠的 RPC 調用,且連結發送緩存已飽和,則用戶端會抛棄此調用
  2. 若還未在連結中為 AActor 建立通道則抛棄調用,需要等伺服器由 AActor 通道發送 OpenBunch 促使用戶端建立通道後,用戶端才能發起 RPC 調用
  3. 若伺服器接收到這個 RPC Bunch 後找不到對應的 UActorChannel 通道處理,伺服器不會響應這個 RPC 調用
UFUNCTION( Client )

用于聲明由伺服器發起調用,在用戶端執行的 RPC 函數。

無論是否可靠,構造的 RPC Bunch 都會由 UChannel::SendBunch() 立即發送。

執行空間
  1. 在用戶端調用時則僅本地執行
  2. 在伺服器調用時,對于本地權威角色的 AActor ,若其參與屬性同步且有所屬連結則遠端執行,否則僅本地執行
  3. 在伺服器調用時,對于非本地權威角色的 AActor ,若其參與屬性同步且有所屬連結則遠端執行,否則終止執行
遠端執行的過濾情況
  1. 若 AActor 在 PendingKill 或 Unreachable 狀态,或已對其調用 UWorld::DestroyActor() 來請求析構,則伺服器會抛棄調用
  2. 若是不可靠的 RPC 調用,且連結發送緩存已飽和,則伺服器會抛棄此調用
  3. 若還未在連結中為 AActor 建立通道,則要驗證連結的用戶端已加載此 AActor 所在的 ULevel 後,才為 AActor 建立通道,否則抛棄調用
  4. 若通道還未發送 OpenBunch ,則會自動發送一個 AActor 屬性同步的 Bunch 作為 OpenBunch 促使用戶端建立 AActor 和通道 ,然後才繼續進行 RPC 調用
UFUNCTION( NetMulticast )

用于聲明由伺服器發起調用,并廣播到所有用戶端執行的 RPC 函數。

若為可靠的則 RPC Bunch 會立即發送,若為不可靠的則 RPC Bunch 會随着下次此 RPC 函數所在對象向此連結進行屬性同步時,才會一起發送。

此外注意,由于在伺服器的 UNetDriver 可能會使用 UReplicationGraph 來處理同步,是以這裡的過濾情況分為兩種。

執行空間
  1. 在用戶端調用時僅本地執行
  2. 在伺服器調用時,對于參與同步的 AActor 即會本地執行也會遠端執行,對于不參與同步的 AActor 僅本地執行
UNetDriver 處理時遠端執行的過濾情況
  1. 周遊 UNetDriver->ClientConnections 數組中的每個連結,進行如下檢測來分發
  2. 調用 AActor::IsNetRelevantFor() 來檢測此 AActor 和每個連結是否相關,若不相關則并不向該連結分發。注意,若調用的是可靠的廣播 RPC 函數,當 AActor 和連結不相關,但還沒有從此連結中移除此 AActor 通道前,也任向此連結分發
  3. 若還未在連結中為 AActor 建立通道,則需要驗證連結的用戶端已加載此 AActor 所在的 ULevel 後,才為 AActor 建立通道,否則抛棄調用
  4. 若通道還未發送 OpenBunch ,則會自動發送一個 AActor 屬性同步的 Bunch 作為 OpenBunch 促使用戶端建立 AActor 和通道 ,然後才繼續進行 RPC 調用
UReplicationGraph 處理時遠端執行的過濾情況
  1. 若 AActor 在 PendingKill 或 Unreachable 狀态,或已對其調用 UWorld::DestroyActor() 來請求析構,則伺服器會抛棄調用,不對任何連結進行分發
  2. 否則,周遊 UReplicationGraph->Connections 數組中的每個連結,進行如下檢測來分發
  3. 若連結的 UNetConnection->ViewTarget 沒有引用連結的觀察對象,這表示連結還未建立好 APlayerController ,此時不向此連結分發
  4. 若目前連結的用戶端還未已加載好此 AActor 所在的 ULevel,則不向此連結分發
  5. 若 AActor 在目前連結中還未打開 UActorChannel 通道,取決于是否為此 AActor 是否配置了同步範圍。若未配置,則直接在此連結中為此 AActor 建立 UActorChannel 通道,并向此連結分發。 若配置了,則要驗證連結的觀察位置是否在此 AActor 同步範圍内,才在連結中為 AActor 建立通道并繼續分發,否則不向此連結分發。
  6. 若通道還未發送 OpenBunch ,則會自動發送一個 AActor 屬性同步的 Bunch 作為 OpenBunch 促使用戶端建立 AActor 和通道 ,然後才繼續進行 RPC 調用

繼續閱讀