new CP
This commit is contained in:
@@ -60,20 +60,16 @@ public class SpaceshipController : NetworkBehaviour
|
||||
private float minTimeBetweenTicks;
|
||||
private const float SERVER_TICK_RATE = 30f;
|
||||
private const int BUFFER_SIZE = 2048;
|
||||
private const bool CUSTOM_NET_TRANSFORM = false;
|
||||
private const bool CUSTOM_NET_TRANSFORM = true;
|
||||
private float ERROR_THRESHOLD =0.25f;
|
||||
private int MIN_ERROR_COUNT = 10;
|
||||
public bool showDebugHUD = false;
|
||||
private double clientNetworkRTT;
|
||||
|
||||
public InputState[] clientInputBuffer;
|
||||
public InputState[] serverInputBuffer;
|
||||
public PlayerState[] clientStateBuffer;
|
||||
public PlayerState[] serverStateBuffer;
|
||||
public Queue<InputState> inputQueue;
|
||||
public PlayerState[] stateBuffer;
|
||||
|
||||
[SerializeField]private int ErrorInputCount;
|
||||
[SerializeField]private int ErrorStateCount;
|
||||
[SerializeField]private int RubberBandsCount;
|
||||
public GameObject DeathEffect;
|
||||
public GameObject debrisEffect;
|
||||
// public GameObject boostStartEffect;
|
||||
@@ -231,10 +227,9 @@ public class SpaceshipController : NetworkBehaviour
|
||||
void Start()
|
||||
{
|
||||
clientInputBuffer = new InputState[BUFFER_SIZE];
|
||||
serverInputBuffer = new InputState[BUFFER_SIZE];
|
||||
clientStateBuffer = new PlayerState[BUFFER_SIZE];
|
||||
serverStateBuffer = new PlayerState[BUFFER_SIZE];
|
||||
NetworkTime.PingFrequency = SERVER_TICK_RATE;
|
||||
inputQueue = new Queue<InputState>();
|
||||
stateBuffer = new PlayerState[BUFFER_SIZE];
|
||||
|
||||
minTimeBetweenTicks = 1f / SERVER_TICK_RATE;
|
||||
string[] args = System.Environment.GetCommandLineArgs();
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
@@ -317,20 +312,14 @@ 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;
|
||||
Vector3 lineCorrection;
|
||||
float scale => Mathf.Clamp(1 + (trailTime * _scaleMultiplier), 1, 10);
|
||||
long lastTime ;
|
||||
|
||||
void Update()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// Debug.Log(FindObjectsOfType<PickupItem>().Length);
|
||||
#endif
|
||||
if (joystick == null) { joystick = FindObjectOfType<Joystick>(); if(joystick==null){return;} }
|
||||
|
||||
if (MinigameManager.instance.isRanked && !MinigameManager.instance.RankedGameStarted) { return; }
|
||||
distanceFromCenter = Vector3.Distance(transform.position, Vector3.zero);
|
||||
@@ -343,6 +332,8 @@ public class SpaceshipController : NetworkBehaviour
|
||||
if (dead) { return; }
|
||||
|
||||
if(isLocalPlayer){
|
||||
if (joystick == null) { joystick = FindObjectOfType<Joystick>(); if(joystick==null){return;} }
|
||||
|
||||
input = joystick.input;
|
||||
if (Input.GetKeyDown(KeyCode.F))
|
||||
{
|
||||
@@ -361,56 +352,42 @@ public class SpaceshipController : NetworkBehaviour
|
||||
DBmanager.SetMostTime((int)survivalTime);
|
||||
}
|
||||
SceneData.SetTimerTxt(survivalTime);
|
||||
|
||||
if(CUSTOM_NET_TRANSFORM){
|
||||
body.position = Vector3.Lerp(body.position,targetState.Position,MovementSmoothnessFactor * movingSpeed * Vector2.Distance(targetState.Position, body.position));
|
||||
body.rotation = Quaternion.Lerp(body.rotation, targetState.Rotation,MovementSmoothnessFactor*movingSpeed);
|
||||
}
|
||||
// Debug.Log(lastTime - currentTick);
|
||||
lastTime = targetState.Tick;
|
||||
if(CUSTOM_NET_TRANSFORM){
|
||||
CameraFollower.UpdateFrame();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
timer += Time.deltaTime;
|
||||
while(timer >= minTimeBetweenTicks){
|
||||
timer -= minTimeBetweenTicks;
|
||||
if(isServer){
|
||||
HandleTick();
|
||||
}else if(isLocalPlayer){
|
||||
ClientHandleTick();
|
||||
}
|
||||
currentTick++;
|
||||
}
|
||||
|
||||
// return;
|
||||
|
||||
}
|
||||
|
||||
void FixedUpdate(){
|
||||
// timer += Time.deltaTime;
|
||||
|
||||
// while(timer >= minTimeBetweenTicks){
|
||||
// timer -= minTimeBetweenTicks;
|
||||
// HandleTick();
|
||||
// currentTick++;
|
||||
// }
|
||||
if (MinigameManager.instance.isRanked && !MinigameManager.instance.RankedGameStarted) { return; }
|
||||
|
||||
HandleTick();
|
||||
currentTick++;
|
||||
}
|
||||
Vector3 serverPosition => serverStateBuffer[bufferIndex].Position;
|
||||
Vector3 clientPosition => clientStateBuffer[bufferIndex].Position;
|
||||
int curBufferIndex;
|
||||
int errorCounter = 0;
|
||||
int bufferIndex => currentTick % BUFFER_SIZE;
|
||||
int ticksGap => (int)(clientNetworkRTT * SERVER_TICK_RATE);
|
||||
int latencyBufferIndex {
|
||||
get{
|
||||
int val =(int)(bufferIndex - (clientNetworkRTT * SERVER_TICK_RATE));
|
||||
if(val < 0){
|
||||
val = BUFFER_SIZE - val;
|
||||
}
|
||||
if(val >= BUFFER_SIZE || val < 0){
|
||||
Debug.LogError(val + " is bigger than buffer size, latency: " + clientNetworkRTT);
|
||||
val = BUFFER_SIZE-1;
|
||||
}
|
||||
return val;
|
||||
if(!isLocalPlayer && !isServer){
|
||||
transform.position = latestServerState.Position;
|
||||
transform.rotation = latestServerState.Rotation;
|
||||
}
|
||||
}
|
||||
public float MovementSmoothnessFactor = 0.1f;
|
||||
|
||||
#region Movement
|
||||
IEnumerator SendToClient(PlayerState statePayload)
|
||||
{
|
||||
yield return new WaitForSeconds(0.02f);
|
||||
|
||||
OnServerMovementState(statePayload);
|
||||
}
|
||||
void HandleTick(){
|
||||
#region obsoleteCommented
|
||||
/*
|
||||
curBufferIndex = bufferIndex;
|
||||
if(isLocalPlayer){
|
||||
// HandleInput(input);
|
||||
@@ -485,11 +462,167 @@ public class SpaceshipController : NetworkBehaviour
|
||||
{
|
||||
speed = movingSpeed * speedMultiplier;
|
||||
}
|
||||
}else{ // not server
|
||||
engineAudio.pitch = Mathf.Lerp(engineAudio.pitch, (boosting) ? boostedPitch : normalPitch, 0.1f);
|
||||
}*/
|
||||
#endregion
|
||||
CheckForPickups();
|
||||
if (boosting && scale > 1)
|
||||
{
|
||||
speed = movingSpeed * 2 * speedMultiplier;
|
||||
DecreaseTrail(Time.deltaTime * boostConsumption * 0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
speed = movingSpeed * speedMultiplier;
|
||||
}
|
||||
|
||||
int bufferIndex = -1;
|
||||
while(inputQueue.Count > 0){
|
||||
InputState inputState = inputQueue.Dequeue();
|
||||
|
||||
bufferIndex = inputState.Tick % BUFFER_SIZE;
|
||||
|
||||
PlayerState statePayload = ProcessMovement(inputState);
|
||||
stateBuffer[bufferIndex] = statePayload;
|
||||
}
|
||||
|
||||
if(bufferIndex != -1){
|
||||
StartCoroutine(SendToClient(stateBuffer[bufferIndex]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ClientHandleTick(){
|
||||
engineAudio.pitch = Mathf.Lerp(engineAudio.pitch, (boosting) ? boostedPitch : normalPitch, 0.1f);
|
||||
|
||||
if (!latestServerState.Equals(default(PlayerState)) &&
|
||||
(lastProcessedState.Equals(default(PlayerState)) ||
|
||||
!latestServerState.Equals(lastProcessedState)))
|
||||
{
|
||||
// Debug.Log("Consider Reconciliation");
|
||||
HandleServerReconciliation();
|
||||
}
|
||||
|
||||
int bufferIndex = currentTick % BUFFER_SIZE;
|
||||
|
||||
// Add payload to inputBuffer
|
||||
InputState inputPayload = new InputState();
|
||||
inputPayload.Tick = currentTick;
|
||||
inputPayload.Input = joystick.input;
|
||||
clientInputBuffer[bufferIndex] = inputPayload;
|
||||
|
||||
// Add payload to stateBuffer
|
||||
stateBuffer[bufferIndex] = ProcessMovement(inputPayload);
|
||||
if(isLocalPlayer){
|
||||
CameraFollower.UpdateFrame();
|
||||
}
|
||||
// Send input to server
|
||||
StartCoroutine(SendToServer(inputPayload));
|
||||
}
|
||||
|
||||
|
||||
public void OnClientInput(InputState inputPayload){
|
||||
if(isServer){
|
||||
m_OnClientInput(inputPayload);
|
||||
}else{
|
||||
CmdOnClientInput(inputPayload);
|
||||
}
|
||||
}
|
||||
|
||||
[Command]
|
||||
public void CmdOnClientInput(InputState inputPayload){
|
||||
m_OnClientInput(inputPayload);
|
||||
}
|
||||
|
||||
public void m_OnClientInput(InputState inputPayload)
|
||||
{
|
||||
inputQueue.Enqueue(inputPayload);
|
||||
}
|
||||
|
||||
private PlayerState latestServerState;
|
||||
private PlayerState lastProcessedState;
|
||||
|
||||
void HandleServerReconciliation()
|
||||
{
|
||||
lastProcessedState = latestServerState;
|
||||
|
||||
int serverStateBufferIndex = latestServerState.Tick % BUFFER_SIZE;
|
||||
float positionError = Vector3.Distance(latestServerState.Position, stateBuffer[serverStateBufferIndex].Position);
|
||||
float rotationError = CustomExtensions.QuaternionExtensions.Difference(latestServerState.Rotation, stateBuffer[serverStateBufferIndex].Rotation);
|
||||
|
||||
if (positionError > 0.001f || !latestServerState.Rotation.Approximately(stateBuffer[serverStateBufferIndex].Rotation, 0.0001f))
|
||||
{
|
||||
Debug.Log($"We have to reconcile bro, Errors\npos:{positionError}, rot:{rotationError}");
|
||||
// Rewind & Replay
|
||||
transform.position = latestServerState.Position;
|
||||
transform.rotation = latestServerState.Rotation;
|
||||
|
||||
// Update buffer at index of latest server state
|
||||
stateBuffer[serverStateBufferIndex] = latestServerState;
|
||||
|
||||
// Now re-simulate the rest of the ticks up to the current tick on the client
|
||||
int tickToProcess = latestServerState.Tick + 1;
|
||||
|
||||
while (tickToProcess < currentTick)
|
||||
{
|
||||
int bufferIndex = tickToProcess % BUFFER_SIZE;
|
||||
|
||||
// Process new movement with reconciled state
|
||||
PlayerState statePayload = ProcessMovement(clientInputBuffer[bufferIndex]);
|
||||
|
||||
// Update buffer with recalculated state
|
||||
stateBuffer[bufferIndex] = statePayload;
|
||||
|
||||
tickToProcess++;
|
||||
}
|
||||
}else{
|
||||
// Debug.Log($"Reconciliation cancelled\n, Errors\npos:{positionError}, rot:{rotationError}");
|
||||
}
|
||||
}
|
||||
|
||||
public void m_OnServerMovementState(PlayerState serverState)
|
||||
{
|
||||
latestServerState = serverState;
|
||||
}
|
||||
|
||||
public void OnServerMovementState(PlayerState serverState){
|
||||
if(isServer){
|
||||
RpcOnServerMovementState(serverState);
|
||||
}else{
|
||||
m_OnServerMovementState(serverState);
|
||||
// CmdOnServerMovementState(serverState);
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
public void RpcOnServerMovementState(PlayerState serverState){
|
||||
m_OnServerMovementState(serverState);
|
||||
// Debug.Log(serverState.Position + ":" + transform.position);
|
||||
|
||||
}
|
||||
|
||||
IEnumerator SendToServer(InputState inputPayload)
|
||||
{
|
||||
yield return new WaitForSeconds(0.02f);
|
||||
|
||||
OnClientInput(inputPayload);
|
||||
}
|
||||
|
||||
PlayerState ProcessMovement(InputState input)
|
||||
{
|
||||
// Should always be in sync with same function on Client
|
||||
body.Translate(new Vector3(0, speed), body);
|
||||
Turn(input.Input);
|
||||
|
||||
return new PlayerState()
|
||||
{
|
||||
Tick = input.Tick,
|
||||
Position = transform.position,
|
||||
Rotation = transform.rotation
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
void CheckForPickups(){
|
||||
Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, 3, pickupsLayer);
|
||||
foreach(Collider2D hit in hits){
|
||||
@@ -505,60 +638,12 @@ public class SpaceshipController : NetworkBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
void HandleInput(Vector2 _input){
|
||||
// transform.Translate(transform.forward * _input.y);
|
||||
// transform.Rotate(transform.up * _input.x);
|
||||
body.Translate(new Vector3(0, speed), body);
|
||||
Turn(_input);
|
||||
}
|
||||
|
||||
void UpdateStates(int tick,double rtt,Vector2 m_input, Vector3 m_position, Quaternion m_rotation){
|
||||
|
||||
if(isServer){
|
||||
m_updateInput(tick,rtt,m_input, m_position, m_rotation);
|
||||
}else{
|
||||
CmdUpdateInput(tick,rtt,m_input, m_position, m_rotation);
|
||||
}
|
||||
}
|
||||
|
||||
[Command]
|
||||
void CmdUpdateInput(int tick,double rtt, Vector2 m_input, Vector3 m_position, Quaternion m_rotation){
|
||||
m_updateInput(tick,rtt,m_input, m_position, m_rotation);
|
||||
}
|
||||
|
||||
private void m_updateInput(int tick,double rtt, Vector2 m_input, Vector3 m_position, Quaternion m_rotation){
|
||||
clientNetworkRTT = rtt;
|
||||
m_Input = m_input;
|
||||
clientInputBuffer[bufferIndex] = new InputState(){Tick=tick, Input=m_input};
|
||||
clientStateBuffer[bufferIndex] = new PlayerState(){Tick=tick, Position=m_position, Rotation=m_rotation};
|
||||
}
|
||||
[ClientRpc]
|
||||
void RpcRubberband(Vector3 m_position, Quaternion m_rotation){
|
||||
return;
|
||||
if(!CUSTOM_NET_TRANSFORM){return;}
|
||||
PlayerState serverState = new PlayerState(){Tick=0, Position = m_position, Rotation = m_rotation};
|
||||
PlayerState clientState = new PlayerState(){Tick=0, Position = transform.position, Rotation = transform.rotation};
|
||||
float diff = serverState.Difference(clientState);
|
||||
Debug.Log("Rubber banded, Strength -> " + diff);
|
||||
if(diff < 0.3f){
|
||||
transform.position = Vector3.Lerp(transform.position, m_position, rubberbandSmoothness);
|
||||
// transform.rotation = Quaternion.Lerp(transform.rotation, m_rotation,rubberbandSmoothness);
|
||||
transform.rotation = m_rotation;
|
||||
return;
|
||||
}
|
||||
transform.position = m_position;
|
||||
transform.rotation = m_rotation;
|
||||
RubberBandsCount++;
|
||||
}
|
||||
|
||||
PlayerState targetState;
|
||||
[ClientRpc]
|
||||
void RpcUpdateOnClient(Vector3 m_position, Quaternion m_rotation, Vector2 _input){
|
||||
if(isLocalPlayer){
|
||||
// return;
|
||||
}
|
||||
targetState = new PlayerState(){Position = m_position, Rotation = m_rotation};
|
||||
}
|
||||
// void HandleInput(Vector2 _input){
|
||||
// // transform.Translate(transform.forward * _input.y);
|
||||
// // transform.Rotate(transform.up * _input.x);
|
||||
// body.Translate(new Vector3(0, speed), body);
|
||||
// Turn(_input);
|
||||
// }
|
||||
|
||||
void Turn(Vector2 input)
|
||||
{
|
||||
@@ -578,31 +663,6 @@ public class SpaceshipController : NetworkBehaviour
|
||||
//trailMgr.trail.SetPositions(positions);
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (!isLocalPlayer) { return; }
|
||||
if (showDebugHUD)
|
||||
{
|
||||
Vector3 bodyOnScreen = Camera.main.WorldToScreenPoint(body.position);
|
||||
// Vector3 DetourOnScreen = Camera.main.WorldToScreenPoint(body.position + Detour);
|
||||
Vector3 bodyUpOnScreen = Camera.main.WorldToScreenPoint(body.position + body.up);
|
||||
Vector3 bodyRightOnScreen = Camera.main.WorldToScreenPoint(body.position + body.right);
|
||||
|
||||
|
||||
// GUI.Label(new Rect(DetourOnScreen.x,DetourOnScreen.y, 100, 20), Detour.ToString());
|
||||
// GUI.Label(new Rect(bodyUpOnScreen.x,bodyUpOnScreen.y, 100, 20), body.up.ToString());
|
||||
// GUI.Label(new Rect(bodyRightOnScreen.x, bodyRightOnScreen.y, 100, 20), body.right.ToString());
|
||||
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public float Angle(Vector2 vector2)
|
||||
{
|
||||
return 360 - (Mathf.Atan2(vector2.x, vector2.y) * Mathf.Rad2Deg * Mathf.Sign(vector2.x));
|
||||
@@ -963,22 +1023,4 @@ public class SpaceshipController : NetworkBehaviour
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class StatePayload
|
||||
{
|
||||
public int Time;
|
||||
public Vector3 Position;
|
||||
public Quaternion Rotation;
|
||||
public Vector2 Input;
|
||||
|
||||
public StatePayload(int time, Vector3 position, Quaternion rotation, Vector2 input)
|
||||
{
|
||||
Time = time;
|
||||
Position = position;
|
||||
Rotation = rotation;
|
||||
Input = input;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user