fgm24/Assets/Scripts/Player/Input/ReconciliationPlayerControl...

205 lines
5.7 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Unity.Netcode;
using UnityEditor.Events;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.UIElements;
using UnityEngine.Windows;
/// This script is for networking the player with reconciliation
[RequireComponent(typeof(ClientNetworkTransform))]
public class ReconciliationPlayerControllerMiddleman : NetworkBehaviour, IMoveData
{
// Current
private InputPayload currentInputState;
private int currentTick => NetworkManager.Singleton.NetworkTickSystem.LocalTime.Tick;
private int bufferIndex => currentTick % BUFFER_SIZE;
private PlayerInput inputSource;
// Shared
private const int BUFFER_SIZE = 1024;
// Local
private StatePayload[] stateBuffer;
private InputPayload[] inputBuffer;
private StatePayload latestServerState;
private StatePayload lastProcessedState;
private Queue<InputPayload> inputQueue = new Queue<InputPayload>();
private ClientNetworkTransform clientNetworkTransform;
[Header("Debug")]
[SerializeField] private bool ForceEnableComponent = false;
public event Action<MoveData> OnNewMoveData;
void OnEnable()
{
if (NetworkManager.Singleton == null)
{
Debug.Log("[Movement] No network manager found. Ignoring reconciliation.");
if (!ForceEnableComponent)
{
this.enabled = false;
return;
}
}
if (!TryGetComponent(out inputSource))
{
Debug.LogError("[Movement] No player input found. Ignoring reconciliation.");
this.enabled = false;
return;
}
stateBuffer = new StatePayload[BUFFER_SIZE];
inputBuffer = new InputPayload[BUFFER_SIZE];
currentInputState = new InputPayload();
inputSource.OnNewMoveData += (moveData) => currentInputState.moveData = moveData;
clientNetworkTransform = GetComponent<ClientNetworkTransform>();
SwitchAuthorityMode(AuthorityMode.Client);
}
private void Update()
{
if (IsServer)
ServerTick();
if (IsOwner)
ClientTick();
}
private void ServerTick()
{
foreach (InputPayload payload in inputQueue)
{
stateBuffer[bufferIndex] = ProcessMovement(payload);
}
OnServerStateRecieved_ServerRpc(stateBuffer[bufferIndex]);
}
private void ClientTick()
{
if (!latestServerState.Equals(default(StatePayload)) &&
(lastProcessedState.Equals(default(StatePayload)) ||
!latestServerState.Equals(lastProcessedState)))
{
Reconciliation();
}
// Add payload to inputBuffer
InputPayload payload = new InputPayload();
payload.tick = currentTick;
payload.moveData = currentInputState.moveData;
inputBuffer[bufferIndex] = payload;
stateBuffer[bufferIndex] = ProcessMovement(payload);
if (!(IsHost || IsServer))
OnClientInput_ClientRpc(payload);
}
[ClientRpc]
private void OnClientInput_ClientRpc(InputPayload payload)
{
inputQueue.Enqueue(payload);
}
[ServerRpc(RequireOwnership = false)]
private void OnServerStateRecieved_ServerRpc(StatePayload serverState)
{
if (!IsOwner) return;
latestServerState = serverState;
}
private void Reconciliation()
{
lastProcessedState = latestServerState;
int serverStateBufferIndex = latestServerState.tick % BUFFER_SIZE;
float positionError = Vector3.Distance(latestServerState.position, stateBuffer[serverStateBufferIndex].position);
if (positionError > 0.001f)
{
transform.position = latestServerState.position;
stateBuffer[serverStateBufferIndex] = latestServerState;
int tickToProcess = latestServerState.tick + 1;
for (; tickToProcess < currentTick; tickToProcess++)
{
// Reprocess movement
StatePayload reprocessedState = ProcessMovement(inputBuffer[tickToProcess]);
// Update buffer with reprocessed state
int bufferIndex = tickToProcess % BUFFER_SIZE;
stateBuffer[bufferIndex] = reprocessedState;
}
Debug.Log($"[{latestServerState.tick}] Reconciliated");
}
}
private void SwitchAuthorityMode(AuthorityMode mode)
{
clientNetworkTransform.authorityMode = mode;
bool shouldSync = mode == AuthorityMode.Client;
clientNetworkTransform.SyncPositionX = shouldSync;
clientNetworkTransform.SyncPositionY = shouldSync;
clientNetworkTransform.SyncPositionZ = shouldSync;
clientNetworkTransform.SyncScaleX = shouldSync;
}
private StatePayload ProcessMovement(InputPayload payload)
{
OnNewMoveData?.Invoke(payload.moveData);
return new StatePayload()
{
tick = payload.tick,
position = transform.position
};
}
}
public struct InputPayload : INetworkSerializable
{
public int tick;
public MoveData moveData;
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref tick);
serializer.SerializeValue(ref moveData);
}
}
public struct StatePayload : INetworkSerializable
{
public int tick;
public Vector3 position;
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref tick);
serializer.SerializeValue(ref position);
}
}