This commit is contained in:
2023-01-09 02:31:23 +05:30
parent b3ccd81816
commit 8ee203e932
105 changed files with 1149 additions and 430 deletions

View File

@@ -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;
}
}