203 lines
5.6 KiB
C#
203 lines
5.6 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]
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|