Client prediction improved

This commit is contained in:
Sewmina
2022-06-16 02:24:15 +05:30
parent cdaa627f52
commit 102a525c6d
22 changed files with 154 additions and 76 deletions

View File

@@ -15,7 +15,7 @@ public class CameraFollower : MonoBehaviour
}
// Update is called once per frame
void Update()
void FixedUpdate()
{
if(target==null){return;}
transform.position = Vector3.Lerp(transform.position, target.position + offset, smoothness * Time.deltaTime);

View File

@@ -1,4 +1,3 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
@@ -14,13 +13,15 @@ public class SpaceshipController : NetworkBehaviour
[Header("Client Prediction")]
public SortedDictionary<int, StatePayload> serverStateBuffer = new SortedDictionary<int, StatePayload>();
public Vector3 Detour = Vector3.zero;
public Quaternion RotationDetour = Quaternion.identity;
public float DetourCorrectionFactor = 0.5f;
public bool showDebugHUD = false;
void Start()
{
if (isLocalPlayer)
{
if (joystick == null) { joystick = FindObjectOfType<Joystick>(); }
@@ -35,6 +36,9 @@ public class SpaceshipController : NetworkBehaviour
// Update is called once per frame
int timeInMillis => (int)(NetworkTime.time * 1000);
int roundedTime => Mathf.FloorToInt((float)timeInMillis / 100f) * 100;
int lastClientUpdateTime = 0;
void FixedUpdate()
{
if (isLocalPlayer)
@@ -52,37 +56,59 @@ public class SpaceshipController : NetworkBehaviour
body.rotation = Quaternion.Lerp(body.rotation, Quaternion.AngleAxis(angle1 + 90, Vector3.forward), turningSmoothFactor * joyInput.magnitude);
}
}
if(isLocalPlayer){
///Diff = rot1 . rot2
///1 = rot1 . rot2 / Diff
///1 * Diff * Diff = rot1.rot2. Diff
if (isLocalPlayer)
{
CmdUpdateJoyInput(joyInput);
//Fix Detours
if (Mathf.Abs(Detour.magnitude) > 0.1f)
{
Vector3 newPosition = body.position + Detour;
Quaternion newRotation = body.rotation * RotationDetour;
body.position = Vector3.Lerp(body.position, newPosition, (Mathf.Abs(Detour.magnitude) > 0.2f) ? DetourCorrectionFactor * 2 * Detour.magnitude : DetourCorrectionFactor);
Detour = newPosition - body.position;
body.rotation = Quaternion.Lerp(body.rotation, newRotation, DetourCorrectionFactor * ((joystick.touchDown) ? 0.1f : 1));
RotationDetour = Quaternion.Inverse(transform.rotation) * newRotation;
}
}
//Fill input and position buffer for client predictions
int lastUpdatedTime = 0;
if (timeInMillis % 32 == 0 && lastUpdatedTime != timeInMillis)
{
lastUpdatedTime = timeInMillis;
StatePayload currentState = new StatePayload(timeInMillis, transform.position, transform.rotation, joyInput);
StatePayload currentState = new StatePayload(timeInMillis, transform.position, transform.rotation, joyInput);
if (isLocalPlayer)
if (isLocalPlayer)
{
if (lastClientUpdateTime < roundedTime)
{
CmdValidateMovement(timeInMillis, currentState.Position, currentState.Rotation, currentState.Input);
CmdValidateMovement(roundedTime, currentState.Position, currentState.Rotation, currentState.Input);
lastClientUpdateTime = roundedTime;
}
if (isServer)
}
if (isServer)
{
int lastUpdatedTime = serverStateBuffer.Count > 0 ? serverStateBuffer.Keys.Last() : 0;
if (timeInMillis >= lastUpdatedTime + 100)
{
if (!serverStateBuffer.ContainsKey(timeInMillis))
serverStateBuffer.Add(roundedTime, currentState);
if (serverStateBuffer.Count > 1024)
{
serverStateBuffer.Add(timeInMillis, currentState);
serverStateBuffer.Remove(serverStateBuffer.Keys.First());
}
}
}
}
[Command]
void CmdUpdateJoyInput(Vector2 input){
void CmdUpdateJoyInput(Vector2 input)
{
joyInput = input;
}
@@ -90,46 +116,81 @@ public class SpaceshipController : NetworkBehaviour
void CmdValidateMovement(int time, Vector3 position, Quaternion rotation, Vector2 input)
{
StatePayload payload = new StatePayload(time, position, rotation, input);
StatePayload lastServerState = serverStateBuffer.Values.Last();
StatePayload payloadToCompare;
if (serverStateBuffer.Count < 1) { Debug.Log("still Initiating server buffer"); return; }
joyInput = payload.Input;
if (!serverStateBuffer.ContainsKey(time))
{
payloadToCompare = lastServerState;
if (lastServerState.Time < time)
{
Debug.Log("Server doesn't have that data yet\nYou asked for " + time + " best I can do is " + lastServerState.Time);
}
else
{
for (int i = 0; i < 50; i++)
{
if (serverStateBuffer.ContainsKey(time - i))
{
payloadToCompare = serverStateBuffer[time - i];
Debug.Log($"Found closest state at {payloadToCompare.Time}, requested {time}");
break;
}
}
}
if(!serverStateBuffer.ContainsKey(time)){Debug.Log("Server doesn't have that data yet");return;}
}
else
{
payloadToCompare = serverStateBuffer[time];
}
Vector3 positionDiff = payloadToCompare.Position - payload.Position;
int timeDiff = lastServerState.Time - time;
if (serverStateBuffer[time] == payload)
if (Mathf.Abs(positionDiff.magnitude) < 0.2f && lastServerState.Time - time < 500)
{
//Validated, move on
}
else
{
RpcRubberBand(serverStateBuffer.Values.Last().Position,serverStateBuffer.Values.Last().Rotation, NetworkTime.time);
{
Debug.Log($"RB : {positionDiff.magnitude} [{timeDiff}ms]");
RpcRubberBand(serverStateBuffer.Values.Last().Position, serverStateBuffer.Values.Last().Rotation, timeInMillis);
}
}
void ValidateCSBuffer()
{
}
double lastRubberBandTime = 0;
[ClientRpc]
void RpcRubberBand(Vector3 position, Quaternion rotation, double sentTime)
{
if (isLocalPlayer)
{
if (sentTime < lastRubberBandTime) { Debug.Log("Old rubber band rpc, ignoree..."); return; }
//Lag comprehension
double delay = NetworkTime.time - sentTime;
transform.position = position;
transform.Translate(new Vector3(0, movingSpeed * ((float)delay / 0.02f)));
transform.rotation = rotation;
double delay = timeInMillis - sentTime + 10;
Vector3 newPosition = position + new Vector3(0, movingSpeed * ((float)(delay) / 20f));
Detour = newPosition - transform.position;
// RotationDetour = rotation;
RotationDetour = Quaternion.Inverse(transform.rotation) * rotation;
lastRubberBandTime = sentTime;
Debug.Log($"Rubber banded (Detour of {Detour}) you to {transform.position}, @ {sentTime} (delay: {delay}");
}
}
void OnGUI()
{
if (!isLocalPlayer) { return; }
if (showDebugHUD)
{
GUI.Label(new Rect(Screen.width - 120, 10, 100, 20), transform.position.ToString());
GUI.Label(new Rect(Screen.width - 120, 30, 100, 20), timeInMillis.ToString());
GUI.Label(new Rect(Screen.width - 100, Screen.height - 30, 50, 20), (NetworkTime.rtt * 1000).ToString() + " ms");
}
}