quantum-space-buddies/QuantumUNET/QSBNetworkBehaviour.cs

607 lines
15 KiB
C#
Raw Normal View History

2020-12-04 22:15:41 +00:00
using OWML.Logging;
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;
using UnityEngine.Networking;
2020-12-04 22:14:53 +00:00
namespace QuantumUNET
2020-12-02 09:51:53 +00:00
{
public class QSBNetworkBehaviour : MonoBehaviour
{
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;
public NetworkInstanceId NetId => MyView.NetId;
2020-12-02 12:42:26 +00:00
public QSBNetworkConnection ConnectionToServer => MyView.ConnectionToServer;
public QSBNetworkConnection 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; }
internal QSBNetworkIdentity NetIdentity => MyView;
private QSBNetworkIdentity MyView
{
get
{
QSBNetworkIdentity myView;
if (m_MyView == null)
{
m_MyView = GetComponent<QSBNetworkIdentity>();
if (m_MyView == null)
{
Debug.LogError("There is no NetworkIdentity on this object. Please add one.");
}
myView = m_MyView;
}
else
{
myView = m_MyView;
}
return myView;
}
}
2020-12-04 09:23:27 +00:00
protected void SendCommandInternal(QSBNetworkWriter writer, int channelId, string cmdName)
2020-12-02 09:51:53 +00:00
{
if (!IsLocalPlayer && !HasAuthority)
{
Debug.LogWarning("Trying to send command for object without authority.");
}
2020-12-02 12:42:26 +00:00
else if (QSBClientScene.readyConnection == null)
2020-12-02 09:51:53 +00:00
{
Debug.LogError("Send command attempted with no client running [client=" + ConnectionToServer + "].");
}
else
{
writer.FinishMessage();
2020-12-02 12:42:26 +00:00
QSBClientScene.readyConnection.SendWriter(writer, channelId);
2020-12-02 09:51:53 +00:00
}
}
2020-12-04 09:23:27 +00:00
public virtual bool InvokeCommand(int cmdHash, QSBNetworkReader reader) => InvokeCommandDelegate(cmdHash, reader);
2020-12-02 09:51:53 +00:00
2020-12-04 09:23:27 +00:00
protected void SendRPCInternal(QSBNetworkWriter writer, int channelId, string rpcName)
2020-12-02 09:51:53 +00:00
{
if (!IsServer)
{
if (LogFilter.logWarn)
{
Debug.LogWarning("ClientRpc call on un-spawned object");
2020-12-04 09:23:27 +00:00
return;
2020-12-02 09:51:53 +00:00
}
}
2020-12-04 09:23:27 +00:00
writer.FinishMessage();
QSBNetworkServer.SendWriterToReady(base.gameObject, writer, channelId);
2020-12-02 09:51:53 +00:00
}
2020-12-04 09:23:27 +00:00
protected void SendTargetRPCInternal(QSBNetworkConnection conn, QSBNetworkWriter writer, int channelId, string rpcName)
2020-12-02 09:51:53 +00:00
{
if (!IsServer)
{
Debug.LogWarning("TargetRpc call on un-spawned object");
2020-12-04 09:23:27 +00:00
return;
2020-12-02 09:51:53 +00: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-04 09:23:27 +00:00
public virtual bool InvokeRPC(int cmdHash, QSBNetworkReader reader) => InvokeRpcDelegate(cmdHash, reader);
2020-12-02 09:51:53 +00:00
2020-12-04 09:23:27 +00:00
protected void SendEventInternal(QSBNetworkWriter writer, int channelId, string eventName)
2020-12-02 09:51:53 +00:00
{
2020-12-02 18:40:38 +00:00
if (!QSBNetworkServer.active)
2020-12-02 09:51:53 +00:00
{
2020-12-04 22:14:53 +00:00
ModConsole.OwmlConsole.WriteLine($"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
}
2020-12-04 09:23:27 +00:00
writer.FinishMessage();
QSBNetworkServer.SendWriterToReady(gameObject, writer, channelId);
2020-12-02 09:51:53 +00:00
}
2020-12-04 09:23:27 +00:00
public virtual bool InvokeSyncEvent(int cmdHash, QSBNetworkReader reader) => InvokeSyncEventDelegate(cmdHash, reader);
2020-12-02 09:51:53 +00:00
2020-12-04 09:23:27 +00:00
public virtual bool InvokeSyncList(int cmdHash, QSBNetworkReader 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();
}
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))
{
Debug.Log("GetInvokerForHash hash:" + cmdHash + " not found");
invokeClass = null;
invokeFunction = null;
result = false;
}
else if (invoker == null)
{
Debug.Log("GetInvokerForHash hash:" + cmdHash + " invoker null");
invokeClass = null;
invokeFunction = null;
result = false;
}
else if (invoker.invokeType != invokeType)
{
Debug.LogError("GetInvokerForHash hash:" + cmdHash + " mismatched invokeType");
invokeClass = null;
invokeFunction = null;
result = false;
}
else
{
invokeClass = invoker.invokeClass;
invokeFunction = invoker.invokeFunction;
result = true;
}
return result;
}
internal static void DumpInvokers()
{
Debug.Log("DumpInvokers size:" + s_CmdHandlerDelegates.Count);
foreach (var keyValuePair in s_CmdHandlerDelegates)
{
Debug.Log(string.Concat(new object[]
{
" Invoker:",
keyValuePair.Value.invokeClass,
":",
keyValuePair.Value.invokeFunction.GetMethodName(),
" ",
keyValuePair.Value.invokeType,
" ",
keyValuePair.Key
}));
}
}
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-04 09:23:27 +00:00
internal bool InvokeCommandDelegate(int cmdHash, QSBNetworkReader 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;
}
}
invoker.invokeFunction(this, reader);
result = true;
}
}
return result;
}
2020-12-04 09:23:27 +00:00
internal bool InvokeRpcDelegate(int cmdHash, QSBNetworkReader 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;
}
}
invoker.invokeFunction(this, reader);
result = true;
}
}
return result;
}
2020-12-04 09:23:27 +00:00
internal bool InvokeSyncEventDelegate(int cmdHash, QSBNetworkReader 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;
}
}
return result;
}
2020-12-04 09:23:27 +00:00
internal bool InvokeSyncListDelegate(int cmdHash, QSBNetworkReader 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;
}
}
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();
}
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);
}
result = text;
}
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");
protected void SetSyncVarGameObject(GameObject newGameObject, ref GameObject gameObjectField, uint dirtyBit, ref NetworkInstanceId netIdField)
{
if (!SyncVarHookGuard)
{
NetworkInstanceId networkInstanceId = default;
if (newGameObject != null)
{
var component = newGameObject.GetComponent<QSBNetworkIdentity>();
if (component != null)
{
networkInstanceId = component.NetId;
if (networkInstanceId.IsEmpty())
{
if (LogFilter.logWarn)
{
Debug.LogWarning("SetSyncVarGameObject GameObject " + newGameObject + " has a zero netId. Maybe it is not spawned yet?");
}
}
}
}
NetworkInstanceId networkInstanceId2 = default;
if (gameObjectField != null)
{
networkInstanceId2 = gameObjectField.GetComponent<QSBNetworkIdentity>().NetId;
}
if (networkInstanceId != networkInstanceId2)
{
Debug.Log(string.Concat(new object[]
{
"SetSyncVar GameObject ",
base.GetType().Name,
" bit [",
dirtyBit,
"] netfieldId:",
networkInstanceId2,
"->",
networkInstanceId
}));
SetDirtyBit(dirtyBit);
gameObjectField = newGameObject;
netIdField = networkInstanceId;
}
}
}
protected void SetSyncVar<T>(T value, ref T fieldValue, uint dirtyBit)
{
var flag = false;
if (value == null)
{
if (fieldValue != null)
{
flag = true;
}
}
else
{
flag = !value.Equals(fieldValue);
}
2020-12-04 09:23:27 +00:00
2020-12-02 09:51:53 +00:00
if (flag)
{
Debug.Log(string.Concat(new object[]
{
"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;
}
}
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();
}
}
return -1;
}
2020-12-04 09:23:27 +00:00
public virtual bool OnSerialize(QSBNetworkWriter writer, bool initialState)
2020-12-02 09:51:53 +00:00
{
if (!initialState)
{
writer.WritePackedUInt32(0U);
}
return false;
}
2020-12-04 09:23:27 +00:00
public virtual void OnDeserialize(QSBNetworkReader 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-02 12:42:26 +00:00
public virtual bool OnRebuildObservers(HashSet<QSBNetworkConnection> observers, bool initialize) => false;
2020-12-04 09:29:23 +00:00
public virtual void OnSetLocalVisibility(bool vis)
{
}
2020-12-02 18:40:38 +00:00
public virtual bool OnCheckObserver(QSBNetworkConnection 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;
private QSBNetworkIdentity m_MyView;
private static readonly Dictionary<int, Invoker> s_CmdHandlerDelegates = new Dictionary<int, Invoker>();
2020-12-04 09:23:27 +00:00
public delegate void CmdDelegate(QSBNetworkBehaviour obj, QSBNetworkReader reader);
2020-12-02 09:51:53 +00:00
2020-12-04 09:23:27 +00:00
protected delegate void EventDelegate(List<Delegate> targets, QSBNetworkReader reader);
2020-12-02 09:51:53 +00:00
protected enum UNetInvokeType
{
Command,
ClientRpc,
SyncEvent,
SyncList
}
protected class Invoker
{
public string DebugString()
{
return string.Concat(new object[]
{
invokeType,
":",
invokeClass,
":",
invokeFunction.GetMethodName()
});
}
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
}