核心

  • 1、我们客户端的时间与服务器的时间应该一致。
  • 2、客户端获取时间的流程为:
    • 1)客户端发起获取服务器时间;
    • 1.1)网络通信时间
    • 2)服务器得知客户端获取服务器时间;
    • 3)发送服务器当前的时间;
    • 3.1)网络通信时间
    • 4)客户端获取服务器得之前的时间

思路

  • 前提条件:假设客户端—>服务器以及服务器—>客户端两者通信的时间一致。
  • 由于客户端获取服务器时间存在网络通信的时间,因此可以:
    • 在客户端获取服务器时间时:把当前客户端时间发送到服务器上
    • 在服务器拿到客户端时间后:将客户端时间当前服务器时间打包发送
    • 客户端最新获得的服务器返回的时间包(客户端之前时间,服务器之前时间)
      • 当前服务器时间的计算公式:(客户端当前时间 - 客户端之前时间)/ 2 + 服务器之前时间;
      • 其中 (客户端当前时间 - 客户端之前时间) 为完整的网络通信时间;

部分笔记以及核心代码

  • 服务器与客户端时间同步,其中关键为GetServerTime()函数,其中客户端推算服务器的时间为 客户端的当前时间 + ClientServerDelta
  • 而ClientServerDelta的计算公式为:
    • ClientServerDelta = 当前服务器时间(推断) - 当前的客户端(真实)
    • 当前服务器时间(推断)= 服务器RPC获取的当前时间 + 1 / 2 * 当前客户端到服务器再到客户端的同步时间。
  • 服务器与客户端通信获取时间用于计算ClientServerDelta的流程为:
    • 每次Tick触发同步Tick函数ABlasterPlayerController::CheckTimeSync
      • 该函数将当前时间作为参数对服务器RPC调用ServerRequestServerTime_Implementation(float TimeOfClientRequest)
      • TimeOfClientRequest 为当前客户端对服务器RPC调用时间
    • 在服务器运行RPC函数ABlasterPlayerController::ServerRequestServerTime_Implementation(float TimeOfClientRequest)
      • 获取当前服务器的时间,并使用RPC函数对客户端调用ABlasterPlayerController::ClientReportServerTime_Implementation(float TimeOfClientRequest, float TimeServerReceivedClientRequest)
      • TimeServerReceivedClientRequest这个为当前服务器的时间
      • TimeOfClientRequest为此前客户端时间
    • 客户端RPC执行ABlasterPlayerController::ClientReportServerTime_Implementation(float TimeOfClientRequest, float TimeServerReceivedClientRequest)
      • 计算客户端调用RPC前后之间的时间差 float RoundTripTime = GetWorld()->GetTimeSeconds() - TimeOfClientRequest;
      • 推断当前服务器的时间 float CurrentServerTime = TimeServerReceivedClientRequest + (0.5f * RoundTripTime);
      • 计算当前的ClientServerDelta = CurrentServerTime - GetWorld()->GetTimeSeconds();
        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
        void ABlasterPlayerController::Tick(float DeltaTime)
        {
        Super::Tick(DeltaTime);

        SetHUDTime();
        CheckTimeSync(DeltaTime);
        }
        float ABlasterPlayerController::GetServerTime()
        {
        // 在服务器调用则使用当前时间;
        // 在客户端调用则显示 当前时间 + 服务器与客户端延迟通信
        if (HasAuthority()) return GetWorld()->GetTimeSeconds();
        else return GetWorld()->GetTimeSeconds() + ClientServerDelta;
        }

        void ABlasterPlayerController::SetHUDTime()
        {
        uint32 SecondsLeft = FMath::CeilToInt(MatchTime - GetServerTime());
        if (CountdownInt != SecondsLeft)
        {
        SetHUDMatchCountdown(MatchTime - GetServerTime());
        }

        CountdownInt = SecondsLeft;
        }
        // 每Tick检测时间同步的函数
        void ABlasterPlayerController::CheckTimeSync(float DeltaTime)
        {
        TimeSyncRunningTime += DeltaTime;
        if (IsLocalController() && TimeSyncRunningTime > TimeSyncFrequency)
        {
        ServerRequestServerTime(GetWorld()->GetTimeSeconds());
        TimeSyncRunningTime = 0.f;
        }
        }
        // 服务器RPC可靠传输标签
        UFUNCTION(Server, Reliable)
        void ServerRequestServerTime(float TimeOfClientRequest);

        // 客户端RPC可靠传输标签
        UFUNCTION(Client, Reliable)
        void ClientReportServerTime(float TimeOfClientRequest, float TimeServerReceivedClientRequest);

        void ABlasterPlayerController::ServerRequestServerTime_Implementation(float TimeOfClientRequest)
        {
        // 客户端RPC调用服务器运行函数,获取服务器的当前时间
        float ServerTimeOfReceipt = GetWorld()->GetTimeSeconds();
        ClientReportServerTime(TimeOfClientRequest, ServerTimeOfReceipt);
        }

        void ABlasterPlayerController::ClientReportServerTime_Implementation(float TimeOfClientRequest, float TimeServerReceivedClientRequest)
        {
        // 服务器调用客户端RPC函数,计算当前服务器到客户端TimeServerReceivedClientRequest以及客户端到服务器的时间TimeOfClientRequest,
        float RoundTripTime = GetWorld()->GetTimeSeconds() - TimeOfClientRequest;
        float CurrentServerTime = TimeServerReceivedClientRequest + (0.5f * RoundTripTime);
        ClientServerDelta = CurrentServerTime - GetWorld()->GetTimeSeconds();
        }

        // 通知脚本我们被分配了一个有效的玩家,类似初始化
        void ABlasterPlayerController::ReceivedPlayer()
        {
        Super::ReceivedPlayer();
        if (IsLocalController())
        {
        ServerRequestServerTime(GetWorld()->GetTimeSeconds());
        }
        }