首页 > 其他分享 >UE实现获取客户端与服务器的延迟

UE实现获取客户端与服务器的延迟

时间:2024-01-11 14:58:57浏览次数:43  
标签:ClientTimestamp void float AMyPlayerController Client UE 服务器 Server 客户端

思路简述

主要是采用NTP的思想:
image-20240111135752355

图中的时间点参数:

  1. Client's Send Time (t1): 客户端发送请求时的时间戳
  2. Server's Receive Time (t2): 服务器收到请求的时间戳
  3. Server's Transmit Time (t3): 服务器发送回应的时间戳
  4. Client's Receive Time (t4): 客户端收到服务器回应的时间戳

因为在UE中t2t3太过于接近,于是就将两个时间合并仅有t3

实现

以下是参考代码,采用UE自带的RPC实现。

AMyPlayerController.h

class AMyPlayerController : public APlayerController
{
	GENERATED_BODY()
	// ... other things
	
	// TIME SYNC
public:
	void StartSynchronization();

private:
	// RPC
	UFUNCTION(Server, Reliable)
	void Server_HandleTimeSyncRequest(float ClientTimestamp);

	UFUNCTION(Client, Reliable)
	void Client_ReceiveServerTime(float ClientTimestamp, float ServerTimestamp);
	
	void CalculateTimeOffset(float ClientTimestamp, float ServerTimestamp);

	// 为了求NumberOfSyncs的平均时延
	TArray<float> TimeOffsets;
	int32 NumberOfSyncs;
	int32 CurrentSyncIndex;

	void SendNextSyncRequest();
	void CalculateAverageOffset();
};

AMyPlayerController.cpp

AMyPlayerController::AMyPlayerController()
{
	// ....
	// other init
	
	NumberOfSyncs = 10;  // Number of sync requests
	CurrentSyncIndex = 0;
}

void AMyPlayerController::BeginPlay()
{
	// Call the base class  
	Super::BeginPlay();

    // ...
    // other code
	
    
    // only client calls
	if (!HasAuthority())
	{
		StartSynchronization();
	}
}

void AMyPlayerController::StartSynchronization()
{
	TimeOffsets.Empty();
	SendNextSyncRequest();
}

void AMyPlayerController::SendNextSyncRequest()
{
	if (CurrentSyncIndex < NumberOfSyncs)
	{
		if (HasAuthority())
		{
			// Server does nothing here
		}
		else
		{
			float ClientTimestamp = GetWorld()->GetTimeSeconds();
			Server_HandleTimeSyncRequest(ClientTimestamp);
		}
	}
	else
	{
		CalculateAverageOffset();
	}
}

void AMyPlayerController::Server_HandleTimeSyncRequest_Implementation(float ClientTimestamp)
{
	float ServerTimestamp = GetWorld()->GetTimeSeconds();
	Client_ReceiveServerTime(ClientTimestamp, ServerTimestamp);
}

void AMyPlayerController::Client_ReceiveServerTime_Implementation(float ClientTimestamp, float ServerTimestamp)
{
	CalculateTimeOffset(ClientTimestamp, ServerTimestamp);
	CurrentSyncIndex++;
	SendNextSyncRequest(); // Send next request
}

void AMyPlayerController::CalculateTimeOffset(float ClientTimestamp, float ServerTimestamp)
{
	float CurrentClientTime = GetWorld()->GetTimeSeconds();
	float RoundTripTime = CurrentClientTime - ClientTimestamp;
	float Offset = ServerTimestamp - ClientTimestamp - RoundTripTime / 2.0f;
	TimeOffsets.Add(Offset);
	
	AverTimeOffset = Offset;
}

void AMyPlayerController::CalculateAverageOffset()
{
	float SumOffsets = 0.0f;
	for (float Offset : TimeOffsets)
	{
		SumOffsets += Offset;
	}
	
	AverTimeOffset = SumOffsets / TimeOffsets.Num();
}

基本上的调用流程就是:

  • Client调用SendNextSyncRequest()

  • 这个函数中调用了Server的RPC即Server_HandleTimeSyncRequest_Implementation(t0),把客户端自己的时间传过去

  • Server的这个RPC中又调用了Client的RPC即Client_ReceiveServerTime_Implementation(t0, t1)

  • 客户端在调用Client_ReceiveServerTime_Implementation(t0, t1)时会调用t3 = GetWorld()->GetTimeSeconds()

  • t0, t1, t3三个时间点凑齐,开始计算时延

最后还暴露了一个参数NumberOfSyncs,用于调整获取NumberOfSyncs次时延的平均值。

疑问

Q1: 为什么放在APlayerController中?

因为RPC的调用有要求,需要客户端实际拥有这个Actor,显然APlayerController是符合这个要求。

RPC-要求和注意事项

标签:ClientTimestamp,void,float,AMyPlayerController,Client,UE,服务器,Server,客户端
From: https://www.cnblogs.com/Vikyanite/p/17958568

相关文章

  • MySQL运维实战(3.1) MySQL官方客户端使用介绍
    作者:俊达引言MySQL是MySQL安装包默认的客户端,该客户端程序通常位于二进制安装包的bin目录中,或者通过rpm安装包安装mysql-community-client,是数据库管理系统的重要组成部分。MySQL客户端不仅仅是一个简单的软件工具,更是连接用户与数据库之间的桥梁,对于有效地使用MySQL数据库的功......
  • vue 高德地图异步引用
    先建立一个文件,引入高德  gDMapLoader.jsconstak='4e9f15de14b05fd8f19e1d8fbe91f0a3'exportdefaultfunctionload(){returnnewPromise(function(resolve,reject){if(window.AMap){resolve(window.AMap)}else{varscript=doc......
  • Linux服务器日志分析shell命令总结
    1、查看有多少个IP访问:awk'{print$1}'log_file|sort|uniq|wc-l2、查看某一个页面被访问的次数:grep"/index.php"log_file|wc-l3、查看每一个IP访问了多少个页面:awk'{++S[$1]}END{for(ainS)printa,S[a]}'log_file>log.txtsort-n-t''-k2log......
  • No 'Access-Control-Allow-Origin' header is present on the requested resource', 跨
    https://blog.csdn.net/dear_little_bear/article/details/839993911.当请求不在同一域名下的资源文件(ip地址+端口号)时,会报如下错误:“No‘Access-Control-Allow-Origin’headerispresentontherequestedresource.Origin‘http://localhost:8080’isthereforenotall......
  • node 快速搭建http服务器
    1.新建目录demo然后在当前目录下打开cmd窗口执行npminit一直回车,执行结束该目录下出现一个package.json2.node环境自行安装,需要依赖如下*npmaddexpress*npmaddnodemon*npminstall-gaxios--registry=https://registry.npm.taobao.org3.当前目录下新建index.js......
  • Vue2 使用 Knova Canvas 合成图片、多个视频、音频在一个画面中并播放,自定义 video co
    本文转载https://blog.csdn.net/RosaChampagne/article/details/128020428?spm=1001.2014.3001.5502的文章安装插件npminstallvue-konva@2konva--save在main.js中使用importVuefrom'vue';importVueKonvafrom'vue-konva';Vue.use(VueKonva);相关实现代......
  • 微信小程序创建formdata对象,并通过 wx.request 发送file文件
    本文申明:仅作学习使用需求:需要绕过wx.uploadFile进行图片的传输,通过接口给服务器问题:因涉及到域名安全问题,不能使用wx.uploadFile。微信本身没有FormData对象,无法使用newFormData后端接口需要接收一个file,所以只能想办法,通过wx.request发送multipart/form-data请......
  • IIS——应用程序池——高级设置——启用32位应用程序 :true
    IIS——应用程序池——高级设置——启用32位应用程序:true打开InternetInformationServices(IIS)管理器。在左侧导航栏中,展开服务器节点并选择“应用程序池”。在中间窗格中,选择要更改的应用程序池。在右侧操作窗格中,点击“高级设置”。在高级设置对话框中,找到“启用32位......
  • 银河麒麟服务器操作系统V10SP2离线安装Google Chrome浏览器
     https://blog.csdn.net/ShenSeKyun/article/details/132224932https://www.google.cn/chrome/index.htmlGOOGLE网址最下方下载LINUX版本的浏览器安装包打开终端,输入rpm-ivhgoogle-chrome-stable_current_x86_64.rpmyuminstall google-chrome-stable_current_x86_64.r......
  • Vue-Router: 如何在Vue应用程序中使用编程式导航?
    Laravel是一个流行的PHP框架,它具有出色的可测试性,可以帮助开发人员在更短的时间内编写可靠的代码。但是,即使使用了这个框架,也可能会出现测试覆盖率较低的情况。测试覆盖率是指代码中已由测试案例覆盖的部分比例。测试覆盖率越高,代码质量越高。在本文中,我们将分享几种技巧,帮助您提......