quantum-space-buddies/QuantumUNET/QNetworkBehaviour.cs

608 lines
15 KiB
C#
Raw Normal View History

2020-12-23 13:48:31 +00:00
using QuantumUNET.Components;
using QuantumUNET.Logging;
2021-05-04 19:32:05 +01:00
using QuantumUNET.Messages;
using QuantumUNET.Transport;
2020-12-04 09:23:27 +00:00
using System;
2020-12-02 09:51:53 +00:00
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;
2020-12-04 22:14:53 +00:00
namespace QuantumUNET
2020-12-02 09:51:53 +00:00
{
2020-12-23 12:58:45 +00:00
public class QNetworkBehaviour : MonoBehaviour
2020-12-02 09:51:53 +00:00
{
public bool LocalPlayerAuthority => MyView.LocalPlayerAuthority;
public bool IsServer => MyView.IsServer;
public bool IsClient => MyView.IsClient;
public bool IsLocalPlayer => MyView.IsLocalPlayer;
public bool HasAuthority => MyView.HasAuthority;
2021-12-02 19:41:19 -08:00
public QNetworkInstanceId NetId => MyView.NetId;
2020-12-23 12:58:45 +00:00
public QNetworkConnection ConnectionToServer => MyView.ConnectionToServer;
public QNetworkConnection ConnectionToClient => MyView.ConnectionToClient;
2020-12-02 09:51:53 +00:00
public short PlayerControllerId => MyView.PlayerControllerId;
protected uint SyncVarDirtyBits { get; private set; }
protected bool SyncVarHookGuard { get; set; }
2020-12-23 12:58:45 +00:00
public QNetworkIdentity NetIdentity => MyView;
2020-12-02 09:51:53 +00:00
2020-12-23 12:58:45 +00:00
private QNetworkIdentity MyView
2020-12-02 09:51:53 +00:00
{
get
{
2020-12-23 12:58:45 +00:00
QNetworkIdentity myView;
2021-02-15 10:58:21 +00:00
if (gameObject == null)
{
QLog.FatalError($"Trying to get QNetworkIdentity of a null gameobject?");
return null;
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
if (m_MyView == null)
{
2020-12-23 12:58:45 +00:00
m_MyView = GetComponent<QNetworkIdentity>();
2020-12-02 09:51:53 +00:00
if (m_MyView == null)
{
QLog.FatalError($"There is no QNetworkIdentity on this object (name={name}). Please add one.");
2020-12-02 09:51:53 +00:00
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
myView = m_MyView;
}
else
{
myView = m_MyView;
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
return myView;
}
}
2021-05-04 19:32:05 +01:00
protected void ClientSendUpdateVars()
{
var writer = new QNetworkWriter();
2021-05-07 14:58:37 +01:00
writer.StartMessage(QMsgType.ClientUpdateVars);
2021-05-04 19:32:05 +01:00
writer.Write(NetId);
2021-05-07 14:58:37 +01:00
writer.Write(GetType().Name);
2021-05-04 19:32:05 +01:00
if (OnSerialize(writer, false))
{
ClearAllDirtyBits();
writer.FinishMessage();
QClientScene.readyConnection.SendWriter(writer, GetNetworkChannel());
}
}
2020-12-23 12:58:45 +00:00
protected void SendCommandInternal(QNetworkWriter writer, int channelId, string cmdName)
2020-12-02 09:51:53 +00:00
{
if (!IsLocalPlayer && !HasAuthority)
{
QLog.Warning("Trying to send command for object without authority.");
2020-12-02 09:51:53 +00:00
}
2020-12-23 12:58:45 +00:00
else if (QClientScene.readyConnection == null)
2020-12-02 09:51:53 +00:00
{
QLog.Error($"Send command attempted with no client running [client={ConnectionToServer}].");
2020-12-02 09:51:53 +00:00
}
else
{
writer.FinishMessage();
2020-12-23 12:58:45 +00:00
QClientScene.readyConnection.SendWriter(writer, channelId);
2020-12-02 09:51:53 +00:00
}
}
2020-12-23 12:58:45 +00:00
public virtual bool InvokeCommand(int cmdHash, QNetworkReader reader) => InvokeCommandDelegate(cmdHash, reader);
2020-12-02 09:51:53 +00:00
2020-12-23 12:58:45 +00:00
protected void SendRPCInternal(QNetworkWriter writer, int channelId, string rpcName)
2020-12-02 09:51:53 +00:00
{
if (!IsServer)
{
QLog.Warning("ClientRpc call on un-spawned object");
2020-12-18 20:20:54 +00:00
return;
2020-12-02 09:51:53 +00:00
}
2021-06-18 22:39:21 +01:00
2020-12-04 09:23:27 +00:00
writer.FinishMessage();
2020-12-23 12:58:45 +00:00
QNetworkServer.SendWriterToReady(gameObject, writer, channelId);
2020-12-02 09:51:53 +00:00
}
2020-12-23 12:58:45 +00:00
protected void SendTargetRPCInternal(QNetworkConnection conn, QNetworkWriter writer, int channelId, string rpcName)
2020-12-02 09:51:53 +00:00
{
if (!IsServer)
{
QLog.Warning("TargetRpc call on un-spawned object");
2020-12-04 09:23:27 +00:00
return;
2020-12-02 09:51:53 +00:00
}
2021-06-18 22:39:21 +01:00
2020-12-04 09:23:27 +00:00
writer.FinishMessage();
conn.SendWriter(writer, channelId);
2020-12-02 09:51:53 +00:00
}
2020-12-23 12:58:45 +00:00
public virtual bool InvokeRPC(int cmdHash, QNetworkReader reader) => InvokeRpcDelegate(cmdHash, reader);
2020-12-02 09:51:53 +00:00
2020-12-23 12:58:45 +00:00
protected void SendEventInternal(QNetworkWriter writer, int channelId, string eventName)
2020-12-02 09:51:53 +00:00
{
2020-12-23 12:58:45 +00:00
if (!QNetworkServer.active)
2020-12-02 09:51:53 +00:00
{
QLog.Error($"Tried to send event {eventName} on channel {channelId} but QSBNetworkServer isn't active.");
2020-12-04 09:23:27 +00:00
return;
2020-12-02 09:51:53 +00:00
}
2021-06-18 22:39:21 +01:00
2020-12-04 09:23:27 +00:00
writer.FinishMessage();
2020-12-23 12:58:45 +00:00
QNetworkServer.SendWriterToReady(gameObject, writer, channelId);
2020-12-02 09:51:53 +00:00
}
2020-12-23 12:58:45 +00:00
public virtual bool InvokeSyncEvent(int cmdHash, QNetworkReader reader) => InvokeSyncEventDelegate(cmdHash, reader);
2020-12-02 09:51:53 +00:00
2020-12-23 12:58:45 +00:00
public virtual bool InvokeSyncList(int cmdHash, QNetworkReader reader) => InvokeSyncListDelegate(cmdHash, reader);
2020-12-02 09:51:53 +00:00
protected static void RegisterCommandDelegate(Type invokeClass, int cmdHash, CmdDelegate func)
{
if (!s_CmdHandlerDelegates.ContainsKey(cmdHash))
{
var invoker = new Invoker
{
invokeType = UNetInvokeType.Command,
invokeClass = invokeClass,
invokeFunction = func
};
s_CmdHandlerDelegates[cmdHash] = invoker;
}
}
protected static void RegisterRpcDelegate(Type invokeClass, int cmdHash, CmdDelegate func)
{
if (!s_CmdHandlerDelegates.ContainsKey(cmdHash))
{
var invoker = new Invoker
{
invokeType = UNetInvokeType.ClientRpc,
invokeClass = invokeClass,
invokeFunction = func
};
s_CmdHandlerDelegates[cmdHash] = invoker;
}
}
protected static void RegisterEventDelegate(Type invokeClass, int cmdHash, CmdDelegate func)
{
if (!s_CmdHandlerDelegates.ContainsKey(cmdHash))
{
var invoker = new Invoker
{
invokeType = UNetInvokeType.SyncEvent,
invokeClass = invokeClass,
invokeFunction = func
};
s_CmdHandlerDelegates[cmdHash] = invoker;
}
}
protected static void RegisterSyncListDelegate(Type invokeClass, int cmdHash, CmdDelegate func)
{
if (!s_CmdHandlerDelegates.ContainsKey(cmdHash))
{
var invoker = new Invoker
{
invokeType = UNetInvokeType.SyncList,
invokeClass = invokeClass,
invokeFunction = func
};
s_CmdHandlerDelegates[cmdHash] = invoker;
}
}
internal static string GetInvoker(int cmdHash)
{
string result;
if (!s_CmdHandlerDelegates.ContainsKey(cmdHash))
{
result = null;
}
else
{
var invoker = s_CmdHandlerDelegates[cmdHash];
result = invoker.DebugString();
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
return result;
}
2020-12-02 21:23:01 +00:00
internal static bool GetInvokerForHashCommand(int cmdHash, out Type invokeClass, out CmdDelegate invokeFunction)
2020-12-02 09:51:53 +00:00
=> GetInvokerForHash(cmdHash, UNetInvokeType.Command, out invokeClass, out invokeFunction);
2020-12-02 21:23:01 +00:00
internal static bool GetInvokerForHashClientRpc(int cmdHash, out Type invokeClass, out CmdDelegate invokeFunction)
2020-12-02 09:51:53 +00:00
=> GetInvokerForHash(cmdHash, UNetInvokeType.ClientRpc, out invokeClass, out invokeFunction);
2020-12-02 21:23:01 +00:00
internal static bool GetInvokerForHashSyncList(int cmdHash, out Type invokeClass, out CmdDelegate invokeFunction)
2020-12-02 09:51:53 +00:00
=> GetInvokerForHash(cmdHash, UNetInvokeType.SyncList, out invokeClass, out invokeFunction);
2020-12-02 21:23:01 +00:00
internal static bool GetInvokerForHashSyncEvent(int cmdHash, out Type invokeClass, out CmdDelegate invokeFunction)
2020-12-02 09:51:53 +00:00
=> GetInvokerForHash(cmdHash, UNetInvokeType.SyncEvent, out invokeClass, out invokeFunction);
private static bool GetInvokerForHash(int cmdHash, UNetInvokeType invokeType, out Type invokeClass, out CmdDelegate invokeFunction)
{
bool result;
if (!s_CmdHandlerDelegates.TryGetValue(cmdHash, out var invoker))
{
QLog.Error($"GetInvokerForHash hash:{cmdHash} not found");
2020-12-02 09:51:53 +00:00
invokeClass = null;
invokeFunction = null;
result = false;
}
else if (invoker == null)
{
QLog.Error($"GetInvokerForHash hash:{cmdHash} invoker null");
2020-12-02 09:51:53 +00:00
invokeClass = null;
invokeFunction = null;
result = false;
}
else if (invoker.invokeType != invokeType)
{
QLog.Error($"GetInvokerForHash hash:{cmdHash} mismatched invokeType");
2020-12-02 09:51:53 +00:00
invokeClass = null;
invokeFunction = null;
result = false;
}
else
{
invokeClass = invoker.invokeClass;
invokeFunction = invoker.invokeFunction;
result = true;
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
return result;
}
internal static void DumpInvokers()
{
2020-12-23 13:48:31 +00:00
QLog.Log($"DumpInvokers size:{s_CmdHandlerDelegates.Count}");
2020-12-02 09:51:53 +00:00
foreach (var keyValuePair in s_CmdHandlerDelegates)
{
2020-12-23 13:48:31 +00:00
QLog.Log($" Invoker:{keyValuePair.Value.invokeClass}:{keyValuePair.Value.invokeFunction.GetMethodName()} {keyValuePair.Value.invokeType} {keyValuePair.Key}");
2020-12-02 09:51:53 +00:00
}
}
2020-12-02 21:23:01 +00:00
internal bool ContainsCommandDelegate(int cmdHash)
2020-12-02 09:51:53 +00:00
=> s_CmdHandlerDelegates.ContainsKey(cmdHash);
2020-12-23 12:58:45 +00:00
internal bool InvokeCommandDelegate(int cmdHash, QNetworkReader reader)
2020-12-02 09:51:53 +00:00
{
bool result;
if (!s_CmdHandlerDelegates.ContainsKey(cmdHash))
{
result = false;
}
else
{
var invoker = s_CmdHandlerDelegates[cmdHash];
if (invoker.invokeType != UNetInvokeType.Command)
{
result = false;
}
else
{
if (GetType() != invoker.invokeClass)
{
if (!GetType().IsSubclassOf(invoker.invokeClass))
{
return false;
}
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
invoker.invokeFunction(this, reader);
result = true;
}
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
return result;
}
2020-12-23 12:58:45 +00:00
internal bool InvokeRpcDelegate(int cmdHash, QNetworkReader reader)
2020-12-02 09:51:53 +00:00
{
bool result;
if (!s_CmdHandlerDelegates.ContainsKey(cmdHash))
{
result = false;
}
else
{
var invoker = s_CmdHandlerDelegates[cmdHash];
if (invoker.invokeType != UNetInvokeType.ClientRpc)
{
result = false;
}
else
{
if (GetType() != invoker.invokeClass)
{
if (!GetType().IsSubclassOf(invoker.invokeClass))
{
return false;
}
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
invoker.invokeFunction(this, reader);
result = true;
}
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
return result;
}
2020-12-23 12:58:45 +00:00
internal bool InvokeSyncEventDelegate(int cmdHash, QNetworkReader reader)
2020-12-02 09:51:53 +00:00
{
bool result;
if (!s_CmdHandlerDelegates.ContainsKey(cmdHash))
{
result = false;
}
else
{
var invoker = s_CmdHandlerDelegates[cmdHash];
if (invoker.invokeType != UNetInvokeType.SyncEvent)
{
result = false;
}
else
{
invoker.invokeFunction(this, reader);
result = true;
}
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
return result;
}
2020-12-23 12:58:45 +00:00
internal bool InvokeSyncListDelegate(int cmdHash, QNetworkReader reader)
2020-12-02 09:51:53 +00:00
{
bool result;
if (!s_CmdHandlerDelegates.ContainsKey(cmdHash))
{
result = false;
}
else
{
var invoker = s_CmdHandlerDelegates[cmdHash];
if (invoker.invokeType != UNetInvokeType.SyncList)
{
result = false;
}
else if (GetType() != invoker.invokeClass)
{
result = false;
}
else
{
invoker.invokeFunction(this, reader);
result = true;
}
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
return result;
}
internal static string GetCmdHashHandlerName(int cmdHash)
{
string result;
if (!s_CmdHandlerDelegates.ContainsKey(cmdHash))
{
result = cmdHash.ToString();
}
else
{
var invoker = s_CmdHandlerDelegates[cmdHash];
result = $"{invoker.invokeType}:{invoker.invokeFunction.GetMethodName()}";
2020-12-02 09:51:53 +00:00
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
return result;
}
private static string GetCmdHashPrefixName(int cmdHash, string prefix)
{
string result;
if (!s_CmdHandlerDelegates.ContainsKey(cmdHash))
{
result = cmdHash.ToString();
}
else
{
var invoker = s_CmdHandlerDelegates[cmdHash];
var text = invoker.invokeFunction.GetMethodName();
var num = text.IndexOf(prefix);
if (num > -1)
{
text = text.Substring(prefix.Length);
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
result = text;
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
return result;
}
2020-12-02 21:23:01 +00:00
internal static string GetCmdHashCmdName(int cmdHash)
2020-12-02 09:51:53 +00:00
=> GetCmdHashPrefixName(cmdHash, "InvokeCmd");
2020-12-02 21:23:01 +00:00
internal static string GetCmdHashRpcName(int cmdHash)
2020-12-02 09:51:53 +00:00
=> GetCmdHashPrefixName(cmdHash, "InvokeRpc");
2020-12-02 21:23:01 +00:00
internal static string GetCmdHashEventName(int cmdHash)
2020-12-02 09:51:53 +00:00
=> GetCmdHashPrefixName(cmdHash, "InvokeSyncEvent");
2020-12-02 21:23:01 +00:00
internal static string GetCmdHashListName(int cmdHash)
2020-12-02 09:51:53 +00:00
=> GetCmdHashPrefixName(cmdHash, "InvokeSyncList");
2021-12-02 19:41:19 -08:00
protected void SetSyncVarGameObject(GameObject newGameObject, ref GameObject gameObjectField, uint dirtyBit, ref QNetworkInstanceId netIdField)
2020-12-02 09:51:53 +00:00
{
if (!SyncVarHookGuard)
{
2021-12-02 19:41:19 -08:00
QNetworkInstanceId networkInstanceId = default;
2020-12-02 09:51:53 +00:00
if (newGameObject != null)
{
2020-12-23 12:58:45 +00:00
var component = newGameObject.GetComponent<QNetworkIdentity>();
2020-12-02 09:51:53 +00:00
if (component != null)
{
networkInstanceId = component.NetId;
if (networkInstanceId.IsEmpty())
{
QLog.Warning(
$"SetSyncVarGameObject GameObject {newGameObject} has a zero netId. Maybe it is not spawned yet?");
2020-12-02 09:51:53 +00:00
}
}
}
2021-06-18 22:39:21 +01:00
2021-12-02 19:41:19 -08:00
QNetworkInstanceId networkInstanceId2 = default;
2020-12-02 09:51:53 +00:00
if (gameObjectField != null)
{
2020-12-23 12:58:45 +00:00
networkInstanceId2 = gameObjectField.GetComponent<QNetworkIdentity>().NetId;
2020-12-02 09:51:53 +00:00
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
if (networkInstanceId != networkInstanceId2)
{
2020-12-23 13:48:31 +00:00
QLog.Log(
$"SetSyncVar GameObject {GetType().Name} bit [{dirtyBit}] netfieldId:{networkInstanceId2}->{networkInstanceId}");
2020-12-02 09:51:53 +00:00
SetDirtyBit(dirtyBit);
gameObjectField = newGameObject;
netIdField = networkInstanceId;
}
}
}
2021-05-04 20:09:29 +01:00
protected bool SetSyncVar<T>(T value, ref T fieldValue, uint dirtyBit)
2020-12-02 09:51:53 +00:00
{
2021-05-04 20:09:29 +01:00
var shouldSet = false;
2020-12-02 09:51:53 +00:00
if (value == null)
{
if (fieldValue != null)
{
2021-05-04 20:09:29 +01:00
shouldSet = true;
2020-12-02 09:51:53 +00:00
}
}
else
{
2021-05-04 20:09:29 +01:00
shouldSet = !value.Equals(fieldValue);
2020-12-02 09:51:53 +00:00
}
2020-12-04 09:23:27 +00:00
2021-05-04 20:09:29 +01:00
if (shouldSet)
2020-12-02 09:51:53 +00:00
{
2020-12-23 13:48:31 +00:00
QLog.Log($"SetSyncVar {GetType().Name} bit [{dirtyBit}] {fieldValue}->{value}");
2020-12-04 09:23:27 +00:00
2020-12-02 09:51:53 +00:00
SetDirtyBit(dirtyBit);
fieldValue = value;
}
2021-05-04 20:09:29 +01:00
return shouldSet;
2020-12-02 09:51:53 +00:00
}
public void SetDirtyBit(uint dirtyBit) => SyncVarDirtyBits |= dirtyBit;
public void ClearAllDirtyBits()
{
m_LastSendTime = Time.time;
SyncVarDirtyBits = 0U;
}
internal int GetDirtyChannel()
{
if (Time.time - m_LastSendTime > GetNetworkSendInterval())
{
if (SyncVarDirtyBits != 0U)
{
return GetNetworkChannel();
}
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
return -1;
}
2020-12-23 12:58:45 +00:00
public virtual bool OnSerialize(QNetworkWriter writer, bool initialState)
2020-12-02 09:51:53 +00:00
{
if (!initialState)
{
writer.WritePackedUInt32(0U);
}
2021-06-18 22:39:21 +01:00
2020-12-02 09:51:53 +00:00
return false;
}
2020-12-23 12:58:45 +00:00
public virtual void OnDeserialize(QNetworkReader reader, bool initialState)
2020-12-02 09:51:53 +00:00
{
if (!initialState)
{
reader.ReadPackedUInt32();
}
}
2020-12-04 09:29:23 +00:00
public virtual void PreStartClient()
{
}
public virtual void OnNetworkDestroy()
{
}
public virtual void OnStartServer()
{
}
public virtual void OnStartClient()
{
}
public virtual void OnStartLocalPlayer()
{
}
public virtual void OnStartAuthority()
{
}
public virtual void OnStopAuthority()
{
}
2020-12-23 12:58:45 +00:00
public virtual bool OnRebuildObservers(HashSet<QNetworkConnection> observers, bool initialize) => false;
2020-12-04 09:29:23 +00:00
public virtual void OnSetLocalVisibility(bool vis)
{
}
2020-12-23 12:58:45 +00:00
public virtual bool OnCheckObserver(QNetworkConnection conn) => true;
2020-12-04 09:29:23 +00:00
2020-12-02 09:51:53 +00:00
public virtual int GetNetworkChannel() => 0;
2020-12-04 09:29:23 +00:00
2020-12-02 09:51:53 +00:00
public virtual float GetNetworkSendInterval() => 0.1f;
private float m_LastSendTime;
2020-12-23 12:58:45 +00:00
private QNetworkIdentity m_MyView;
2020-12-02 09:51:53 +00:00
2021-11-20 19:55:54 +00:00
private static readonly Dictionary<int, Invoker> s_CmdHandlerDelegates = new();
2020-12-02 09:51:53 +00:00
2020-12-23 12:58:45 +00:00
public delegate void CmdDelegate(QNetworkBehaviour obj, QNetworkReader reader);
2020-12-02 09:51:53 +00:00
2020-12-23 12:58:45 +00:00
protected delegate void EventDelegate(List<Delegate> targets, QNetworkReader reader);
2020-12-02 09:51:53 +00:00
protected enum UNetInvokeType
{
Command,
ClientRpc,
SyncEvent,
SyncList
}
protected class Invoker
{
public string DebugString() =>
$"{invokeType}:{invokeClass}:{invokeFunction.GetMethodName()}";
2020-12-02 09:51:53 +00:00
public UNetInvokeType invokeType;
public Type invokeClass;
public CmdDelegate invokeFunction;
}
}
internal static class DotNetCompatibility
{
internal static string GetMethodName(this Delegate func) => func.Method.Name;
internal static Type GetBaseType(this Type type) => type.BaseType;
internal static string GetErrorCode(this SocketException e) => e.ErrorCode.ToString();
}
2020-12-03 08:28:05 +00:00
}