Files
megakoop/Game/Scripts/Networking/SteamCharacterNetworkBridge.cs

282 lines
9.4 KiB
C#

using Steamworks;
using UnityEngine;
namespace MegaKoop.Game.Networking
{
[DisallowMultipleComponent]
public class SteamCharacterNetworkBridge : MonoBehaviour
{
[Header("References")]
[SerializeField] private ThirdPersonCharacterController characterController;
[SerializeField] private NetworkIdentity identity;
[SerializeField] private Transform rootTransform;
[SerializeField] private NetworkCharacterInputProxy networkInputProxy;
[Header("Settings")]
[SerializeField] private float transformBroadcastInterval = 0.05f;
[SerializeField] private float remoteLerpSpeed = 12f;
[SerializeField] private ulong ownerSteamId;
[SerializeField] private bool autoAssignOwnerToLocalPlayer = true;
private SteamCoopNetworkManager networkManager;
private float broadcastTimer;
private bool isRegistered;
private bool isLocalPlayer;
private bool isAuthority;
private Vector3 remoteTargetPosition;
private Quaternion remoteTargetRotation;
private Vector3 remoteTargetVelocity;
private bool haveRemoteState;
private bool localOverrideSet;
public void AssignOwner(ulong steamId, bool localPlayer)
{
ownerSteamId = steamId;
isLocalPlayer = localPlayer;
localOverrideSet = true;
UpdateAuthority();
ConfigureController();
}
public bool IsLocalPlayer => isLocalPlayer;
public bool IsAuthority => isAuthority;
private void Awake()
{
if (characterController == null)
{
characterController = GetComponent<ThirdPersonCharacterController>();
}
if (identity == null)
{
identity = GetComponent<NetworkIdentity>();
}
if (rootTransform == null)
{
rootTransform = transform;
}
if (networkInputProxy == null)
{
networkInputProxy = GetComponent<NetworkCharacterInputProxy>();
if (networkInputProxy == null)
{
networkInputProxy = gameObject.AddComponent<NetworkCharacterInputProxy>();
networkInputProxy.hideFlags = HideFlags.HideInInspector;
}
}
remoteTargetPosition = rootTransform.position;
remoteTargetRotation = rootTransform.rotation;
}
private void Start()
{
networkManager = SteamCoopNetworkManager.Instance;
UpdateAuthority();
ConfigureController();
}
private void OnEnable()
{
networkManager = SteamCoopNetworkManager.Instance;
RegisterHandlers();
}
private void OnDisable()
{
UnregisterHandlers();
}
private void Update()
{
if (networkManager == null)
{
networkManager = SteamCoopNetworkManager.Instance;
if (networkManager != null)
{
RegisterHandlers();
}
}
if (networkManager == null)
{
return;
}
if (isAuthority)
{
broadcastTimer -= Time.deltaTime;
if (broadcastTimer <= 0f)
{
BroadcastTransform();
broadcastTimer = transformBroadcastInterval;
}
}
else
{
if (haveRemoteState)
{
rootTransform.position = Vector3.Lerp(rootTransform.position, remoteTargetPosition, remoteLerpSpeed * Time.deltaTime);
rootTransform.rotation = Quaternion.Slerp(rootTransform.rotation, remoteTargetRotation, remoteLerpSpeed * Time.deltaTime);
}
}
}
private void RegisterHandlers()
{
if (networkManager == null || isRegistered)
{
return;
}
networkManager.RegisterHandler(NetworkMessageType.PlayerInput, HandlePlayerInputMessage);
networkManager.RegisterHandler(NetworkMessageType.CharacterTransform, HandleCharacterTransformMessage);
isRegistered = true;
}
private void UnregisterHandlers()
{
if (networkManager == null || !isRegistered)
{
return;
}
networkManager.UnregisterHandler(NetworkMessageType.PlayerInput, HandlePlayerInputMessage);
networkManager.UnregisterHandler(NetworkMessageType.CharacterTransform, HandleCharacterTransformMessage);
isRegistered = false;
}
private void UpdateAuthority()
{
if (networkManager == null)
{
networkManager = SteamCoopNetworkManager.Instance;
}
bool isHost = networkManager != null && networkManager.IsHost;
ulong localSteamId = SteamBootstrap.IsInitialized ? SteamUser.GetSteamID().m_SteamID : 0UL;
if (!localOverrideSet && autoAssignOwnerToLocalPlayer && ownerSteamId == 0 && localSteamId != 0)
{
ownerSteamId = localSteamId;
}
if (!localOverrideSet)
{
isLocalPlayer = ownerSteamId != 0 && ownerSteamId == localSteamId;
}
isAuthority = isHost; // Host drives authoritative simulation.
}
private void ConfigureController()
{
if (characterController == null)
{
return;
}
if (isAuthority)
{
characterController.enabled = true;
characterController.SetInputSource(isLocalPlayer ? null : networkInputProxy);
var unityController = characterController.GetComponent<UnityEngine.CharacterController>();
if (unityController != null)
{
unityController.enabled = true;
}
}
else
{
characterController.enabled = false;
characterController.SetInputSource(null);
var unityController = characterController.GetComponent<UnityEngine.CharacterController>();
if (unityController != null)
{
unityController.enabled = false;
}
}
}
private void BroadcastTransform()
{
if (identity == null)
{
return;
}
var unityController = GetComponent<CharacterController>();
Vector3 velocity = unityController != null ? unityController.velocity : Vector3.zero;
var message = new CharacterTransformMessage(identity.NetworkId, rootTransform.position, rootTransform.rotation, velocity);
byte[] payload = CharacterTransformMessage.Serialize(message);
networkManager.SendToAll(NetworkMessageType.CharacterTransform, payload, EP2PSend.k_EP2PSendUnreliableNoDelay);
}
private void HandlePlayerInputMessage(NetworkMessage message)
{
if (!isAuthority)
{
return;
}
PlayerInputMessage inputMessage = PlayerInputMessage.Deserialize(message.Payload);
if (inputMessage.NetworkId != identity.NetworkId)
{
return;
}
networkInputProxy.SetInput(inputMessage.MoveInput, inputMessage.JumpPressed);
}
private void HandleCharacterTransformMessage(NetworkMessage message)
{
if (isAuthority)
{
return;
}
CharacterTransformMessage transformMessage = CharacterTransformMessage.Deserialize(message.Payload);
if (transformMessage.NetworkId != identity.NetworkId)
{
return;
}
remoteTargetPosition = transformMessage.Position;
remoteTargetRotation = transformMessage.Rotation;
remoteTargetVelocity = transformMessage.Velocity;
haveRemoteState = true;
}
public void SendLocalInput(Vector2 moveInput, bool jump)
{
if (networkManager == null || identity == null)
{
return;
}
var message = new PlayerInputMessage(identity.NetworkId, moveInput, jump);
byte[] payload = PlayerInputMessage.Serialize(message);
if (!networkManager.IsConnected || networkManager.IsHost)
{
// If we are host, feed directly.
HandlePlayerInputMessage(new NetworkMessage(NetworkMessageType.PlayerInput, payload, SteamUser.GetSteamID().m_SteamID));
}
else
{
CSteamID lobby = networkManager.ActiveLobby;
CSteamID lobbyOwner = lobby != CSteamID.Nil ? SteamMatchmaking.GetLobbyOwner(lobby) : CSteamID.Nil;
if (lobbyOwner == CSteamID.Nil)
{
lobbyOwner = SteamUser.GetSteamID();
}
networkManager.SendToPlayer(lobbyOwner, NetworkMessageType.PlayerInput, payload, EP2PSend.k_EP2PSendUnreliableNoDelay);
}
}
}
}