UE 客戶端和伺服器上的時間同步

XTG111發表於2024-07-01

多客戶端出現時間不同步的情況,,

由於每個客戶端本地的時間,時區不同或者存在網路延遲造成的。所以為了能夠使每個客戶端顯示的時間相同,就需要在Server上利用GetWorld()->GetTimeSeconds()獲取伺服器時間,然後傳給客戶端顯示。
在傳輸過程中,要考慮到每個客戶端的延遲需要做一些計算偏移,才能實現最終每個客戶端的時間一致

利用RPC實現同步

UE中提過了RPC實現伺服器和客戶端的網路連線功能,其中ServerRPC表示在客戶端呼叫,伺服器執行,ClientRPC表示伺服器呼叫,客戶端執行。
在這裡預設從客戶端到伺服器,從伺服器到客戶端的時間傳輸是一樣的。

計算時間差

該時間差是指從客戶端請求當前時間到伺服器上呼叫ClientRPC後Client執行計算到的時間的差值,而該時間差就是產生每個客戶端時間不同步的那些主要因素導致的。

  1. 首先在客戶端上呼叫ServerRPC函式,該函式將接受客戶端當前的時間GetTimeSeconds()作為引數,該時間作為起始時間,在伺服器執行該函式,將得到伺服器上當前時間
  2. ClientRPC將接受兩個引數分別是客戶端的起始時間和伺服器當前時間,其中起始時間是用來計算RTT的,伺服器當前時間是用來計算需要顯示的時間的。
    Time_RTT = PreClientTime - NowClientTime。
    透過該RTT可以計算得到當伺服器呼叫的ClientRPC函式在得到NowClientTime後,即該函式命令傳遞到客戶端時,此時的ServerTime。
    NowServerTime = PreServerTime + RTT/2;
    當前的這個時間就應該是每個客戶端最後應該顯示的時間。為了分離,利用一個變數來接受差值NowServerTime - NowClientTime而不是直接的數值
void AXBlasterPlayerController::ServerRequestServerTime_Implementation(float TimeOfClientRequese)
{
	//獲取接受時伺服器的時刻
	float SeverTimeOfReceipt = GetWorld()->GetTimeSeconds();
	ClientReportServerTime(TimeOfClientRequese, SeverTimeOfReceipt);
}
//Client Run GetWorld()->GetTimeSeconds() is ClientCurrentTime
void AXBlasterPlayerController::ClientReportServerTime_Implementation(float TimeOfClientRequest, float TimeServerReceivedClientRequest)
{
	//計算RTT
	float RoundTripTime = GetWorld()->GetTimeSeconds() - TimeOfClientRequest;
	SingleTripTime = RoundTripTime * 0.5f;
	//ServerCurrentTime
	float CurrentServerTime = TimeServerReceivedClientRequest + SingleTripTime;

	ClientServerDelta = CurrentServerTime - GetWorld()->GetTimeSeconds();
}

Tick呼叫

該過程是確保客戶端始終保持同步,透過設定的時間間隔呼叫

void AXBlasterPlayerController::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	SetHUDTime();
	CheckTimeSync(DeltaTime);
}
//在遊戲過程中每隔一段時間同步伺服器時間到客戶端
void AXBlasterPlayerController::CheckTimeSync(float DeltaTime)
{

	TimeSyncRunningTime += DeltaTime;
	if (IsLocalController() && TimeSyncRunningTime > TimeSyncFrequency)
	{
		ServerRequestServerTime(GetWorld()->GetTimeSeconds());
		TimeSyncRunningTime = 0.f;
	}
}

HUD顯示時間

透過對不同遊戲狀態(比如遊戲中,中途加入遊戲或者等待)提供相應的偏移,然後加上之前獲得的NowServerTime即可

獲取ServerTime

float AXBlasterPlayerController::GetSeverTime()
{
  //伺服器上執行
	if (HasAuthority())
	{
		return GetWorld()->GetTimeSeconds();

	}
//客戶端上執行需要加上偏移量,最終顯現的是伺服器上時間
	else
	{
		return GetWorld()->GetTimeSeconds() + ClientServerDelta;
	}
}

相關文章