using PlayerController; using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using Unity.Netcode; using UnityEditor.Events; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.UIElements; /// This script is for networking the player with reconciliation public class ReconciliationPlayerControllerMiddleman : MonoBehaviour { #region Ugly ass code // Convert to C# events because superiority public void In_OnLook(CustomInput ctx) => OnIn_OnLook?.Invoke(ctx); public void In_OnMove(CustomInput ctx) => OnIn_OnMove?.Invoke(ctx); public void In_OnJump(CustomInput ctx) => OnIn_OnJump?.Invoke(ctx); public void In_OnCrouch(CustomInput ctx) => OnIn_OnCrouch?.Invoke(ctx); public void In_OnSliding(CustomInput ctx) => OnIn_OnSliding?.Invoke(ctx); private Action OnIn_OnLook; private Action OnIn_OnMove; private Action OnIn_OnJump; private Action OnIn_OnCrouch; private Action OnIn_OnSliding; public UnityEvent OnLook; public UnityEvent OnMove; public UnityEvent OnJump; public UnityEvent OnCrouch; public UnityEvent OnSliding; #endregion // Shared private float timer; private int currentTick; private float minTimeBetweenTicks; private uint serverTickRate; private const int BUFFER_SIZE = 1024; // Local private StatePayload[] stateBuffer; private InputPayload[] inputBuffer; private StatePayload latestServerState; private StatePayload lastProcessedState; private InputGroup currentInputState; void OnEnable() { PlayerController.PlayerInput blockThisScript; if (!TryGetComponent(out blockThisScript)) { Debug.LogError("Could not find playerinput component."); return; } if (NetworkManager.Singleton == null) { Debug.Log("[Movement] No network manager found. Ignoring reconciliation."); return; } serverTickRate = NetworkManager.Singleton.NetworkTickSystem.TickRate; stateBuffer = new StatePayload[BUFFER_SIZE]; inputBuffer = new InputPayload[BUFFER_SIZE]; currentInputState = new InputGroup(); #region Ugly ass code // Remove old hooks blockThisScript.OnLook.RemoveAllPersistentListeners(); blockThisScript.OnMove.RemoveAllPersistentListeners(); blockThisScript.OnJump.RemoveAllPersistentListeners(); blockThisScript.OnCrouch.RemoveAllPersistentListeners(); blockThisScript.OnSliding.RemoveAllPersistentListeners(); // Rehook C# hooks (Entry) blockThisScript.OnLook.AddListener(In_OnLook); blockThisScript.OnMove.AddListener(In_OnMove); blockThisScript.OnJump.AddListener(In_OnJump); blockThisScript.OnCrouch.AddListener(In_OnCrouch); blockThisScript.OnSliding.AddListener(In_OnSliding); // Pass hook from hook to hook (Connect) OnIn_OnLook += (ctx) => {currentInputState.OnLook.Input = ctx; currentInputState.OnLook.WasCalled = true;}; OnIn_OnMove += (ctx) => {currentInputState.OnMove.Input = ctx; currentInputState.OnMove.WasCalled = true;}; OnIn_OnJump += (ctx) => {currentInputState.OnJump.Input = ctx; currentInputState.OnJump.WasCalled = true;}; OnIn_OnCrouch += (ctx) => {currentInputState.OnCrouch.Input = ctx; currentInputState.OnCrouch.WasCalled = true;}; OnIn_OnSliding += (ctx) => {currentInputState.OnSliding.Input = ctx; currentInputState.OnSliding.WasCalled = true;}; #endregion } private void Update() { int tickTime = NetworkManager.Singleton.NetworkTickSystem.LocalTime.Tick; int bufferIndex = tickTime % BUFFER_SIZE; // Add payload to inputBuffer InputPayload payload = new InputPayload(); payload.tick = tickTime; payload.inputs = currentInputState; inputBuffer[bufferIndex] = payload; stateBuffer[bufferIndex] = ProcessMovement(payload); } private StatePayload ProcessMovement(InputPayload payload) { // Invokes movement only if something changed. if (payload.inputs.OnLook.WasCalled) OnLook?.Invoke(payload.inputs.OnLook.Input); if (payload.inputs.OnMove.WasCalled) OnMove?.Invoke(payload.inputs.OnMove.Input); if (payload.inputs.OnJump.WasCalled) OnJump?.Invoke(payload.inputs.OnJump.Input); if (payload.inputs.OnCrouch.WasCalled) OnCrouch?.Invoke(payload.inputs.OnCrouch.Input); if (payload.inputs.OnSliding.WasCalled) OnSliding?.Invoke(payload.inputs.OnSliding.Input); // Resets inputs currentInputState.OnLook.WasCalled = false; currentInputState.OnMove.WasCalled = false; currentInputState.OnJump.WasCalled = false; currentInputState.OnCrouch.WasCalled = false; currentInputState.OnSliding.WasCalled = false; return new StatePayload() { tick = payload.tick, position = transform.position }; } private void OnDisable() { // Clears subscribers from events OnIn_OnLook = null; OnIn_OnMove = null; OnIn_OnJump = null; OnIn_OnCrouch = null; OnIn_OnSliding = null; } } public struct InputPayload { public int tick; public InputGroup inputs; } public struct StatePayload { public int tick; public Vector3 position; } public struct InputGroup { public InputState OnLook; public InputState OnMove; public InputState OnJump; public InputState OnCrouch; public InputState OnSliding; } public struct InputState { public bool WasCalled; public CustomInput Input; }