2020-12-23 13:48:31 +00:00
using QuantumUNET.Components ;
using QuantumUNET.Logging ;
2020-12-07 21:19:16 +00:00
using QuantumUNET.Messages ;
2020-12-16 09:08:38 +00:00
using QuantumUNET.Transport ;
2020-12-02 18:40:38 +00:00
using System ;
2020-12-02 12:42:26 +00:00
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
2020-12-21 00:50:24 +01:00
using System.Linq ;
2020-12-02 12:42:26 +00:00
using UnityEngine ;
using UnityEngine.Networking ;
2020-12-02 18:40:38 +00:00
using UnityEngine.Networking.Types ;
2020-12-02 12:42:26 +00:00
2020-12-04 22:14:53 +00:00
namespace QuantumUNET
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
public class QNetworkServer
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
private QNetworkServer ( )
2020-12-02 12:42:26 +00:00
{
NetworkTransport . Init ( ) ;
2020-12-16 08:57:15 +00:00
m_RemoveList = new HashSet < NetworkInstanceId > ( ) ;
2020-12-02 12:42:26 +00:00
m_ExternalConnections = new HashSet < int > ( ) ;
2020-12-23 12:58:45 +00:00
m_NetworkScene = new QNetworkScene ( ) ;
2020-12-02 12:42:26 +00:00
m_SimpleServerSimple = new ServerSimpleWrapper ( this ) ;
}
2020-12-23 12:58:45 +00:00
public static List < QNetworkConnection > localConnections = > instance . m_LocalConnectionsFakeList ;
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public static int listenPort = > instance . m_SimpleServerSimple . listenPort ;
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public static int serverHostId = > instance . m_SimpleServerSimple . serverHostId ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
public static ReadOnlyCollection < QNetworkConnection > connections = > instance . m_SimpleServerSimple . connections ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
public static Dictionary < short , QNetworkMessageDelegate > handlers = > instance . m_SimpleServerSimple . handlers ;
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public static HostTopology hostTopology = > instance . m_SimpleServerSimple . hostTopology ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
public static Dictionary < NetworkInstanceId , QNetworkIdentity > objects = > instance . m_NetworkScene . localObjects ;
2020-12-02 12:42:26 +00:00
2020-12-07 21:04:52 +00:00
public static bool dontListen { get ; set ; }
2020-12-02 12:42:26 +00:00
public static bool useWebSockets
{
2020-12-18 20:28:22 +00:00
get = > instance . m_SimpleServerSimple . useWebSockets ;
set = > instance . m_SimpleServerSimple . useWebSockets = value ;
2020-12-02 12:42:26 +00:00
}
2020-12-23 12:58:45 +00:00
internal static QNetworkServer instance
2020-12-02 12:42:26 +00:00
{
get
{
if ( s_Instance = = null )
{
2020-12-02 18:40:38 +00:00
var obj = s_Sync ;
2020-12-02 12:42:26 +00:00
lock ( obj )
{
if ( s_Instance = = null )
{
2020-12-23 12:58:45 +00:00
s_Instance = new QNetworkServer ( ) ;
2020-12-02 12:42:26 +00:00
}
}
}
return s_Instance ;
}
}
2020-12-07 21:04:52 +00:00
public static bool active { get ; private set ; }
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public static bool localClientActive = > instance . m_LocalClientActive ;
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public static int numChannels = > instance . m_SimpleServerSimple . hostTopology . DefaultConfig . ChannelCount ;
2020-12-02 12:42:26 +00:00
public static float maxDelay
{
2020-12-18 20:28:22 +00:00
get = > instance . m_MaxDelay ;
set = > instance . InternalSetMaxDelay ( value ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-16 09:08:38 +00:00
public static Type networkConnectionClass = > instance . m_SimpleServerSimple . networkConnectionClass ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
public static void SetNetworkConnectionClass < T > ( ) where T : QNetworkConnection = > instance . m_SimpleServerSimple . SetNetworkConnectionClass < T > ( ) ;
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public static bool Configure ( ConnectionConfig config , int maxConnections ) = > instance . m_SimpleServerSimple . Configure ( config , maxConnections ) ;
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public static bool Configure ( HostTopology topology ) = > instance . m_SimpleServerSimple . Configure ( topology ) ;
2020-12-02 12:42:26 +00:00
public static void Reset ( )
{
NetworkTransport . Shutdown ( ) ;
NetworkTransport . Init ( ) ;
s_Instance = null ;
2020-12-07 21:04:52 +00:00
active = false ;
2020-12-02 12:42:26 +00:00
}
public static void Shutdown ( )
{
if ( s_Instance ! = null )
{
s_Instance . InternalDisconnectAll ( ) ;
2020-12-07 21:04:52 +00:00
if ( ! dontListen )
2020-12-02 12:42:26 +00:00
{
s_Instance . m_SimpleServerSimple . Stop ( ) ;
}
s_Instance = null ;
}
2020-12-07 21:04:52 +00:00
dontListen = false ;
active = false ;
2020-12-02 12:42:26 +00:00
}
internal void RegisterMessageHandlers ( )
{
2020-12-23 12:58:45 +00:00
m_SimpleServerSimple . RegisterHandlerSafe ( QMsgType . Ready , OnClientReadyMessage ) ;
m_SimpleServerSimple . RegisterHandlerSafe ( QMsgType . Command , OnCommandMessage ) ;
2021-03-31 10:30:51 +01:00
m_SimpleServerSimple . RegisterHandlerSafe ( QMsgType . LocalPlayerTransform , QNetworkTransform . HandleTransform ) ;
m_SimpleServerSimple . RegisterHandlerSafe ( QMsgType . LocalChildTransform , QNetworkTransformChild . HandleChildTransform ) ;
2020-12-23 12:58:45 +00:00
m_SimpleServerSimple . RegisterHandlerSafe ( QMsgType . RemovePlayer , OnRemovePlayerMessage ) ;
m_SimpleServerSimple . RegisterHandlerSafe ( QMsgType . Animation , QNetworkAnimator . OnAnimationServerMessage ) ;
m_SimpleServerSimple . RegisterHandlerSafe ( QMsgType . AnimationParameters , QNetworkAnimator . OnAnimationParametersServerMessage ) ;
m_SimpleServerSimple . RegisterHandlerSafe ( QMsgType . AnimationTrigger , QNetworkAnimator . OnAnimationTriggerServerMessage ) ;
2020-12-02 18:40:38 +00:00
maxPacketSize = hostTopology . DefaultConfig . PacketSize ;
2020-12-02 12:42:26 +00:00
}
2020-12-16 09:08:38 +00:00
public static void ListenRelay ( string relayIp , int relayPort , NetworkID netGuid , SourceID sourceId , NodeID nodeId ) = > instance . InternalListenRelay ( relayIp , relayPort , netGuid , sourceId , nodeId ) ;
2020-12-02 12:42:26 +00:00
private void InternalListenRelay ( string relayIp , int relayPort , NetworkID netGuid , SourceID sourceId , NodeID nodeId )
{
m_SimpleServerSimple . ListenRelay ( relayIp , relayPort , netGuid , sourceId , nodeId ) ;
2020-12-07 21:04:52 +00:00
active = true ;
2020-12-02 12:42:26 +00:00
RegisterMessageHandlers ( ) ;
}
2020-12-16 09:08:38 +00:00
public static bool Listen ( int serverPort ) = > instance . InternalListen ( null , serverPort ) ;
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public static bool Listen ( string ipAddress , int serverPort ) = > instance . InternalListen ( ipAddress , serverPort ) ;
2020-12-02 12:42:26 +00:00
internal bool InternalListen ( string ipAddress , int serverPort )
{
2020-12-07 21:04:52 +00:00
if ( dontListen )
2020-12-02 12:42:26 +00:00
{
m_SimpleServerSimple . Initialize ( ) ;
}
else if ( ! m_SimpleServerSimple . Listen ( ipAddress , serverPort ) )
{
return false ;
}
2020-12-02 18:40:38 +00:00
maxPacketSize = hostTopology . DefaultConfig . PacketSize ;
2020-12-07 21:04:52 +00:00
active = true ;
2020-12-02 12:42:26 +00:00
RegisterMessageHandlers ( ) ;
return true ;
}
private void InternalSetMaxDelay ( float seconds )
{
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
2020-12-21 00:50:24 +01:00
networkConnection ? . SetMaxDelay ( seconds ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-21 00:50:24 +01:00
2020-12-02 12:42:26 +00:00
m_MaxDelay = seconds ;
}
2020-12-23 12:58:45 +00:00
internal int AddLocalClient ( QLocalClient localClient )
2020-12-02 12:42:26 +00:00
{
int result ;
if ( m_LocalConnectionsFakeList . Count ! = 0 )
{
2020-12-31 12:10:55 +00:00
QLog . Error ( "Local Connection already exists" ) ;
2020-12-02 12:42:26 +00:00
result = - 1 ;
}
else
{
2020-12-23 12:58:45 +00:00
m_LocalConnection = new QULocalConnectionToClient ( localClient )
2020-12-13 22:25:23 +00:00
{
connectionId = 0
} ;
2020-12-02 12:42:26 +00:00
m_SimpleServerSimple . SetConnectionAtIndex ( m_LocalConnection ) ;
m_LocalConnectionsFakeList . Add ( m_LocalConnection ) ;
m_LocalConnection . InvokeHandlerNoData ( 32 ) ;
result = 0 ;
}
return result ;
}
2020-12-23 12:58:45 +00:00
internal void RemoveLocalClient ( QNetworkConnection localClientConnection )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
for ( var i = 0 ; i < m_LocalConnectionsFakeList . Count ; i + + )
2020-12-02 12:42:26 +00:00
{
if ( m_LocalConnectionsFakeList [ i ] . connectionId = = localClientConnection . connectionId )
{
m_LocalConnectionsFakeList . RemoveAt ( i ) ;
break ;
}
}
if ( m_LocalConnection ! = null )
{
m_LocalConnection . Disconnect ( ) ;
m_LocalConnection . Dispose ( ) ;
m_LocalConnection = null ;
}
m_LocalClientActive = false ;
m_SimpleServerSimple . RemoveConnectionAtIndex ( 0 ) ;
}
2020-12-16 08:57:15 +00:00
internal void SetLocalObjectOnServer ( NetworkInstanceId netId , GameObject obj )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Debug ( $"SetLocalObjectOnServer {netId} {obj}" ) ;
2020-12-02 12:42:26 +00:00
m_NetworkScene . SetLocalObject ( netId , obj , false , true ) ;
}
internal void ActivateLocalClientScene ( )
{
if ( ! m_LocalClientActive )
{
m_LocalClientActive = true ;
2020-12-02 18:40:38 +00:00
foreach ( var networkIdentity in objects . Values )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
if ( ! networkIdentity . IsClient )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"ActivateClientScene {networkIdentity.NetId} {networkIdentity.gameObject}" ) ;
2020-12-23 12:58:45 +00:00
QClientScene . SetLocalObject ( networkIdentity . NetId , networkIdentity . gameObject ) ;
2020-12-02 12:42:26 +00:00
networkIdentity . OnStartClient ( ) ;
}
}
}
}
2020-12-23 12:58:45 +00:00
public static bool SendToAll ( short msgType , QMessageBase msg )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Debug ( $"Server.SendToAll msgType:{msgType}" ) ;
2020-12-02 18:40:38 +00:00
var flag = true ;
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null )
{
flag & = networkConnection . Send ( msgType , msg ) ;
}
}
return flag ;
}
2020-12-23 12:58:45 +00:00
private static bool SendToObservers ( GameObject contextObj , short msgType , QMessageBase msg )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Debug ( $"Server.SendToObservers id:{msgType}" ) ;
2020-12-02 18:40:38 +00:00
var flag = true ;
2020-12-23 12:58:45 +00:00
var component = contextObj . GetComponent < QNetworkIdentity > ( ) ;
2020-12-02 12:42:26 +00:00
bool result ;
2020-12-02 18:40:38 +00:00
if ( component = = null | | component . Observers = = null )
2020-12-02 12:42:26 +00:00
{
result = false ;
}
else
{
2020-12-02 18:40:38 +00:00
var count = component . Observers . Count ;
for ( var i = 0 ; i < count ; i + + )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
var networkConnection = component . Observers [ i ] ;
2020-12-02 12:42:26 +00:00
flag & = networkConnection . Send ( msgType , msg ) ;
}
result = flag ;
}
return result ;
}
2020-12-23 12:58:45 +00:00
public static bool SendToReady ( GameObject contextObj , short msgType , QMessageBase msg )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Debug ( $"Server.SendToReady id:{msgType}" ) ;
2020-12-02 12:42:26 +00:00
bool result ;
if ( contextObj = = null )
{
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null & & networkConnection . isReady )
{
networkConnection . Send ( msgType , msg ) ;
}
}
2020-12-21 00:50:24 +01:00
2020-12-02 12:42:26 +00:00
result = true ;
}
else
{
2020-12-02 18:40:38 +00:00
var flag = true ;
2020-12-23 12:58:45 +00:00
var component = contextObj . GetComponent < QNetworkIdentity > ( ) ;
2020-12-02 18:40:38 +00:00
if ( component = = null | | component . Observers = = null )
2020-12-02 12:42:26 +00:00
{
result = false ;
}
else
{
2020-12-02 18:40:38 +00:00
var count = component . Observers . Count ;
for ( var j = 0 ; j < count ; j + + )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
var networkConnection2 = component . Observers [ j ] ;
2020-12-02 12:42:26 +00:00
if ( networkConnection2 . isReady )
{
flag & = networkConnection2 . Send ( msgType , msg ) ;
}
}
result = flag ;
}
}
return result ;
}
2020-12-23 12:58:45 +00:00
public static void SendWriterToReady ( GameObject contextObj , QNetworkWriter writer , int channelId )
2020-12-02 12:42:26 +00:00
{
2020-12-04 21:04:18 +00:00
var arraySegment = writer . AsArraySegment ( ) ;
2020-12-02 18:40:38 +00:00
if ( arraySegment . Count > 32767 )
2020-12-02 12:42:26 +00:00
{
throw new UnityException ( "NetworkWriter used buffer is too big!" ) ;
}
2020-12-02 18:40:38 +00:00
SendBytesToReady ( contextObj , arraySegment . Array , arraySegment . Count , channelId ) ;
2020-12-02 12:42:26 +00:00
}
public static void SendBytesToReady ( GameObject contextObj , byte [ ] buffer , int numBytes , int channelId )
{
if ( contextObj = = null )
{
2020-12-02 18:40:38 +00:00
var flag = true ;
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null & & networkConnection . isReady )
{
if ( ! networkConnection . SendBytes ( buffer , numBytes , channelId ) )
{
flag = false ;
}
}
}
if ( ! flag )
{
2020-12-31 12:10:55 +00:00
QLog . Error ( "SendBytesToReady failed" ) ;
2020-12-02 12:42:26 +00:00
}
}
else
{
2020-12-23 12:58:45 +00:00
var component = contextObj . GetComponent < QNetworkIdentity > ( ) ;
2020-12-02 12:42:26 +00:00
try
{
2020-12-02 18:40:38 +00:00
var flag2 = true ;
var count = component . Observers . Count ;
for ( var j = 0 ; j < count ; j + + )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
var networkConnection2 = component . Observers [ j ] ;
2020-12-02 12:42:26 +00:00
if ( networkConnection2 . isReady )
{
if ( ! networkConnection2 . SendBytes ( buffer , numBytes , channelId ) )
{
flag2 = false ;
}
}
}
if ( ! flag2 )
{
2020-12-31 12:10:55 +00:00
QLog . Error ( $"SendBytesToReady failed for {contextObj}" ) ;
2020-12-02 12:42:26 +00:00
}
}
catch ( NullReferenceException )
{
2020-12-31 12:10:55 +00:00
QLog . Error ( $"SendBytesToReady object {contextObj} has not been spawned" ) ;
2020-12-02 12:42:26 +00:00
}
}
}
public static void SendBytesToPlayer ( GameObject player , byte [ ] buffer , int numBytes , int channelId )
{
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null )
{
2020-12-21 00:50:24 +01:00
foreach ( var controller in networkConnection . PlayerControllers )
2020-12-02 12:42:26 +00:00
{
2020-12-21 00:50:24 +01:00
if ( controller . IsValid & & controller . Gameobject = = player )
2020-12-02 12:42:26 +00:00
{
networkConnection . SendBytes ( buffer , numBytes , channelId ) ;
break ;
}
}
}
}
}
2020-12-23 12:58:45 +00:00
public static bool SendUnreliableToAll ( short msgType , QMessageBase msg )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"Server.SendUnreliableToAll msgType:{msgType}" ) ;
2020-12-02 18:40:38 +00:00
var flag = true ;
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null )
{
flag & = networkConnection . SendUnreliable ( msgType , msg ) ;
}
}
return flag ;
}
2020-12-23 12:58:45 +00:00
public static bool SendUnreliableToReady ( GameObject contextObj , short msgType , QMessageBase msg )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"Server.SendUnreliableToReady id:{msgType}" ) ;
2020-12-02 12:42:26 +00:00
bool result ;
if ( contextObj = = null )
{
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null & & networkConnection . isReady )
{
networkConnection . SendUnreliable ( msgType , msg ) ;
}
}
2020-12-21 00:50:24 +01:00
2020-12-02 12:42:26 +00:00
result = true ;
}
else
{
2020-12-02 18:40:38 +00:00
var flag = true ;
2020-12-23 12:58:45 +00:00
var component = contextObj . GetComponent < QNetworkIdentity > ( ) ;
2020-12-02 18:40:38 +00:00
var count = component . Observers . Count ;
for ( var j = 0 ; j < count ; j + + )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
var networkConnection2 = component . Observers [ j ] ;
2020-12-02 12:42:26 +00:00
if ( networkConnection2 . isReady )
{
flag & = networkConnection2 . SendUnreliable ( msgType , msg ) ;
}
}
result = flag ;
}
return result ;
}
2020-12-23 12:58:45 +00:00
public static bool SendByChannelToAll ( short msgType , QMessageBase msg , int channelId )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"Server.SendByChannelToAll id:{msgType}" ) ;
2020-12-02 18:40:38 +00:00
var flag = true ;
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null )
{
flag & = networkConnection . SendByChannel ( msgType , msg , channelId ) ;
}
}
return flag ;
}
2020-12-23 12:58:45 +00:00
public static bool SendByChannelToReady ( GameObject contextObj , short msgType , QMessageBase msg , int channelId )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"Server.SendByChannelToReady msgType:{msgType}" ) ;
2020-12-02 12:42:26 +00:00
bool result ;
if ( contextObj = = null )
{
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null & & networkConnection . isReady )
{
networkConnection . SendByChannel ( msgType , msg , channelId ) ;
}
}
2020-12-21 00:50:24 +01:00
2020-12-02 12:42:26 +00:00
result = true ;
}
else
{
2020-12-02 18:40:38 +00:00
var flag = true ;
2020-12-23 12:58:45 +00:00
var component = contextObj . GetComponent < QNetworkIdentity > ( ) ;
2020-12-02 18:40:38 +00:00
var count = component . Observers . Count ;
for ( var j = 0 ; j < count ; j + + )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
var networkConnection2 = component . Observers [ j ] ;
2020-12-02 12:42:26 +00:00
if ( networkConnection2 . isReady )
{
flag & = networkConnection2 . SendByChannel ( msgType , msg , channelId ) ;
}
}
result = flag ;
}
return result ;
}
2020-12-16 09:08:38 +00:00
public static void DisconnectAll ( ) = > instance . InternalDisconnectAll ( ) ;
2020-12-02 12:42:26 +00:00
internal void InternalDisconnectAll ( )
{
m_SimpleServerSimple . DisconnectAllConnections ( ) ;
if ( m_LocalConnection ! = null )
{
m_LocalConnection . Disconnect ( ) ;
m_LocalConnection . Dispose ( ) ;
m_LocalConnection = null ;
}
m_LocalClientActive = false ;
}
2020-12-22 21:39:53 +00:00
internal static void Update ( ) = > s_Instance ? . InternalUpdate ( ) ;
2020-12-02 12:42:26 +00:00
private void UpdateServerObjects ( )
{
2020-12-02 18:40:38 +00:00
foreach ( var networkIdentity in objects . Values )
2020-12-02 12:42:26 +00:00
{
try
{
networkIdentity . UNetUpdate ( ) ;
}
catch ( NullReferenceException )
{
}
catch ( MissingReferenceException )
{
}
}
if ( m_RemoveListCount + + % 100 = = 0 )
{
CheckForNullObjects ( ) ;
}
}
private void CheckForNullObjects ( )
{
2020-12-02 18:40:38 +00:00
foreach ( var networkInstanceId in objects . Keys )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
var networkIdentity = objects [ networkInstanceId ] ;
2020-12-02 12:42:26 +00:00
if ( networkIdentity = = null | | networkIdentity . gameObject = = null )
{
m_RemoveList . Add ( networkInstanceId ) ;
}
}
if ( m_RemoveList . Count > 0 )
{
2020-12-02 18:40:38 +00:00
foreach ( var key in m_RemoveList )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
objects . Remove ( key ) ;
2020-12-02 12:42:26 +00:00
}
m_RemoveList . Clear ( ) ;
}
}
internal void InternalUpdate ( )
{
m_SimpleServerSimple . Update ( ) ;
2020-12-07 21:04:52 +00:00
if ( dontListen )
2020-12-02 12:42:26 +00:00
{
m_SimpleServerSimple . UpdateConnections ( ) ;
}
UpdateServerObjects ( ) ;
}
2020-12-23 12:58:45 +00:00
private void OnConnected ( QNetworkConnection conn )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"Server accepted client:{conn.connectionId}" ) ;
2020-12-02 12:42:26 +00:00
conn . SetMaxDelay ( m_MaxDelay ) ;
conn . InvokeHandlerNoData ( 32 ) ;
2020-12-02 18:40:38 +00:00
SendCrc ( conn ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-23 12:58:45 +00:00
private void OnDisconnected ( QNetworkConnection conn )
2020-12-02 12:42:26 +00:00
{
conn . InvokeHandlerNoData ( 33 ) ;
2020-12-21 00:50:24 +01:00
foreach ( var controller in conn . PlayerControllers )
2020-12-02 12:42:26 +00:00
{
2020-12-21 00:50:24 +01:00
if ( controller . Gameobject ! = null )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Warning ( "Player not destroyed when connection disconnected." ) ;
2020-12-02 12:42:26 +00:00
}
}
2020-12-23 13:48:31 +00:00
QLog . Log ( $"Server lost client:{conn.connectionId}" ) ;
2020-12-02 12:42:26 +00:00
conn . RemoveObservers ( ) ;
conn . Dispose ( ) ;
}
2020-12-23 12:58:45 +00:00
private void OnData ( QNetworkConnection conn , int receivedSize , int channelId ) = > conn . TransportReceive ( m_SimpleServerSimple . messageBuffer , receivedSize , channelId ) ;
2020-12-02 12:42:26 +00:00
private void GenerateConnectError ( int error )
{
2020-12-31 12:10:55 +00:00
QLog . Error ( $"UNet Server Connect Error: {error}" ) ;
2020-12-02 12:42:26 +00:00
GenerateError ( null , error ) ;
}
2020-12-23 12:58:45 +00:00
private void GenerateDataError ( QNetworkConnection conn , int error )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Error ( $"UNet Server Data Error: {(NetworkError)error}" ) ;
2020-12-02 12:42:26 +00:00
GenerateError ( conn , error ) ;
}
2020-12-23 12:58:45 +00:00
private void GenerateDisconnectError ( QNetworkConnection conn , int error )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Error ( $"UNet Server Disconnect Error: {(NetworkError)error} conn:[{conn}]:{conn.connectionId}" ) ;
2020-12-02 12:42:26 +00:00
GenerateError ( conn , error ) ;
}
2020-12-23 12:58:45 +00:00
private void GenerateError ( QNetworkConnection conn , int error )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
if ( handlers . ContainsKey ( 34 ) )
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
var errorMessage = new QErrorMessage
2020-12-16 09:08:38 +00:00
{
errorCode = error
} ;
2020-12-23 12:58:45 +00:00
var writer = new QNetworkWriter ( ) ;
2020-12-02 12:42:26 +00:00
errorMessage . Serialize ( writer ) ;
2020-12-23 12:58:45 +00:00
var reader = new QNetworkReader ( writer ) ;
2020-12-02 12:42:26 +00:00
conn . InvokeHandler ( 34 , reader , 0 ) ;
}
}
2020-12-23 12:58:45 +00:00
public static void RegisterHandler ( short msgType , QNetworkMessageDelegate handler ) = > instance . m_SimpleServerSimple . RegisterHandler ( msgType , handler ) ;
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public static void UnregisterHandler ( short msgType ) = > instance . m_SimpleServerSimple . UnregisterHandler ( msgType ) ;
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public static void ClearHandlers ( ) = > instance . m_SimpleServerSimple . ClearHandlers ( ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
public static void ClearSpawners ( ) = > QNetworkScene . ClearSpawners ( ) ;
2020-12-02 12:42:26 +00:00
public static void GetStatsOut ( out int numMsgs , out int numBufferedMsgs , out int numBytes , out int lastBufferedPerSecond )
{
numMsgs = 0 ;
numBufferedMsgs = 0 ;
numBytes = 0 ;
lastBufferedPerSecond = 0 ;
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null )
{
2020-12-16 09:08:38 +00:00
networkConnection . GetStatsOut ( out var num , out var num2 , out var num3 , out var num4 ) ;
2020-12-02 12:42:26 +00:00
numMsgs + = num ;
numBufferedMsgs + = num2 ;
numBytes + = num3 ;
lastBufferedPerSecond + = num4 ;
}
}
}
public static void GetStatsIn ( out int numMsgs , out int numBytes )
{
numMsgs = 0 ;
numBytes = 0 ;
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null )
{
2020-12-16 09:08:38 +00:00
networkConnection . GetStatsIn ( out var num , out var num2 ) ;
2020-12-02 12:42:26 +00:00
numMsgs + = num ;
numBytes + = num2 ;
}
}
}
2020-12-23 12:58:45 +00:00
public static void SendToClientOfPlayer ( GameObject player , short msgType , QMessageBase msg )
2020-12-02 12:42:26 +00:00
{
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null )
{
2020-12-21 00:50:24 +01:00
foreach ( var controller in networkConnection . PlayerControllers )
2020-12-02 12:42:26 +00:00
{
2020-12-21 00:50:24 +01:00
if ( controller . IsValid & & controller . Gameobject = = player )
2020-12-02 12:42:26 +00:00
{
networkConnection . Send ( msgType , msg ) ;
return ;
}
}
}
}
2020-12-31 12:10:55 +00:00
QLog . Error ( $"Failed to send message to player object '{player.name}, not found in connection list" ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-23 12:58:45 +00:00
public static void SendToClient ( int connectionId , short msgType , QMessageBase msg )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
if ( connectionId < connections . Count )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
var networkConnection = connections [ connectionId ] ;
2020-12-02 12:42:26 +00:00
if ( networkConnection ! = null )
{
networkConnection . Send ( msgType , msg ) ;
return ;
}
}
2020-12-31 12:10:55 +00:00
QLog . Error ( $"Failed to send message to connection ID '{connectionId}, not found in connection list" ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-23 12:58:45 +00:00
public static bool AddPlayerForConnection ( QNetworkConnection conn , GameObject player , short playerControllerId ) = > instance . InternalAddPlayerForConnection ( conn , player , playerControllerId ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
internal bool InternalAddPlayerForConnection ( QNetworkConnection conn , GameObject playerGameObject , short playerControllerId )
2020-12-02 12:42:26 +00:00
{
bool result ;
2020-12-16 09:08:38 +00:00
if ( ! GetNetworkIdentity ( playerGameObject , out var networkIdentity ) )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log (
2020-12-21 00:50:24 +01:00
$"AddPlayer: playerGameObject has no NetworkIdentity. Please add a NetworkIdentity to {playerGameObject}" ) ;
2020-12-02 12:42:26 +00:00
result = false ;
}
else
{
networkIdentity . Reset ( ) ;
2020-12-02 18:40:38 +00:00
if ( ! CheckPlayerControllerIdForConnection ( conn , playerControllerId ) )
2020-12-02 12:42:26 +00:00
{
result = false ;
}
else
{
GameObject x = null ;
2020-12-16 09:08:38 +00:00
if ( conn . GetPlayerController ( playerControllerId , out var playerController ) )
2020-12-02 12:42:26 +00:00
{
2020-12-03 11:56:32 +00:00
x = playerController . Gameobject ;
2020-12-02 12:42:26 +00:00
}
if ( x ! = null )
{
2020-12-23 13:48:31 +00:00
QLog . Log (
2020-12-21 00:50:24 +01:00
$"AddPlayer: player object already exists for playerControllerId of {playerControllerId}" ) ;
2020-12-02 12:42:26 +00:00
result = false ;
}
else
{
2020-12-23 12:58:45 +00:00
var playerController2 = new QPlayerController ( playerGameObject , playerControllerId ) ;
2020-12-02 12:42:26 +00:00
conn . SetPlayerController ( playerController2 ) ;
2020-12-03 11:56:32 +00:00
networkIdentity . SetConnectionToClient ( conn , playerController2 . PlayerControllerId ) ;
2020-12-02 18:40:38 +00:00
SetClientReady ( conn ) ;
2020-12-02 12:42:26 +00:00
if ( SetupLocalPlayerForConnection ( conn , networkIdentity , playerController2 ) )
{
result = true ;
}
else
{
2020-12-23 13:48:31 +00:00
QLog . Log (
2020-12-23 12:58:45 +00:00
$"Adding new playerGameObject object netId: {playerGameObject.GetComponent<QNetworkIdentity>().NetId} asset ID {playerGameObject.GetComponent<QNetworkIdentity>().AssetId}" ) ;
2020-12-02 18:40:38 +00:00
FinishPlayerForConnection ( conn , networkIdentity , playerGameObject ) ;
if ( networkIdentity . LocalPlayerAuthority )
2020-12-02 12:42:26 +00:00
{
networkIdentity . SetClientOwner ( conn ) ;
}
result = true ;
}
}
}
}
return result ;
}
2020-12-23 12:58:45 +00:00
private static bool CheckPlayerControllerIdForConnection ( QNetworkConnection conn , short playerControllerId )
2020-12-02 12:42:26 +00:00
{
bool result ;
if ( playerControllerId < 0 )
{
2020-12-31 12:10:55 +00:00
QLog . Error ( $"AddPlayer: playerControllerId of {playerControllerId} is negative" ) ;
2020-12-02 12:42:26 +00:00
result = false ;
}
else if ( playerControllerId > 32 )
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"AddPlayer: playerControllerId of {playerControllerId} is too high. max is {32}" ) ;
2020-12-02 12:42:26 +00:00
result = false ;
}
else
{
if ( playerControllerId > 16 )
{
2020-12-31 12:10:55 +00:00
QLog . Warning ( $"AddPlayer: playerControllerId of {playerControllerId} is unusually high" ) ;
2020-12-02 12:42:26 +00:00
}
result = true ;
}
return result ;
}
2020-12-23 12:58:45 +00:00
private bool SetupLocalPlayerForConnection ( QNetworkConnection conn , QNetworkIdentity uv , QPlayerController newPlayerController )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"NetworkServer SetupLocalPlayerForConnection netID:{uv.NetId}" ) ;
2020-12-02 12:42:26 +00:00
bool result ;
2020-12-23 12:58:45 +00:00
if ( conn is QULocalConnectionToClient ulocalConnectionToClient )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( "NetworkServer AddPlayer handling ULocalConnectionToClient" ) ;
2020-12-02 18:40:38 +00:00
if ( uv . NetId . IsEmpty ( ) )
2020-12-02 12:42:26 +00:00
{
uv . OnStartServer ( true ) ;
}
uv . RebuildObservers ( true ) ;
SendSpawnMessage ( uv , null ) ;
2020-12-08 09:03:10 +00:00
ulocalConnectionToClient . LocalClient . AddLocalPlayer ( newPlayerController ) ;
2020-12-02 12:42:26 +00:00
uv . SetClientOwner ( conn ) ;
uv . ForceAuthority ( true ) ;
2020-12-03 11:56:32 +00:00
uv . SetLocalPlayer ( newPlayerController . PlayerControllerId ) ;
2020-12-02 12:42:26 +00:00
result = true ;
}
else
{
result = false ;
}
return result ;
}
2020-12-23 12:58:45 +00:00
private static void FinishPlayerForConnection ( QNetworkConnection conn , QNetworkIdentity uv , GameObject playerGameObject )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
if ( uv . NetId . IsEmpty ( ) )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
Spawn ( playerGameObject ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-23 12:58:45 +00:00
conn . Send ( 4 , new QOwnerMessage
2020-12-02 12:42:26 +00:00
{
2020-12-03 11:56:32 +00:00
NetId = uv . NetId ,
PlayerControllerId = uv . PlayerControllerId
2020-12-02 12:42:26 +00:00
} ) ;
}
2020-12-23 12:58:45 +00:00
internal bool InternalReplacePlayerForConnection ( QNetworkConnection conn , GameObject playerGameObject , short playerControllerId )
2020-12-02 12:42:26 +00:00
{
bool result ;
2020-12-16 09:08:38 +00:00
if ( ! GetNetworkIdentity ( playerGameObject , out var networkIdentity ) )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Error ( $"ReplacePlayer: playerGameObject has no NetworkIdentity. Please add a NetworkIdentity to {playerGameObject}" ) ;
2020-12-02 12:42:26 +00:00
result = false ;
}
2020-12-02 18:40:38 +00:00
else if ( ! CheckPlayerControllerIdForConnection ( conn , playerControllerId ) )
2020-12-02 12:42:26 +00:00
{
result = false ;
}
else
{
2020-12-23 13:48:31 +00:00
QLog . Log ( "NetworkServer ReplacePlayer" ) ;
2020-12-16 09:08:38 +00:00
if ( conn . GetPlayerController ( playerControllerId , out var playerController ) )
2020-12-02 12:42:26 +00:00
{
2020-12-03 11:56:32 +00:00
playerController . UnetView . SetNotLocalPlayer ( ) ;
playerController . UnetView . ClearClientOwner ( ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-23 12:58:45 +00:00
var playerController2 = new QPlayerController ( playerGameObject , playerControllerId ) ;
2020-12-02 12:42:26 +00:00
conn . SetPlayerController ( playerController2 ) ;
2020-12-03 11:56:32 +00:00
networkIdentity . SetConnectionToClient ( conn , playerController2 . PlayerControllerId ) ;
2020-12-23 13:48:31 +00:00
QLog . Log ( "NetworkServer ReplacePlayer setup local" ) ;
2020-12-02 12:42:26 +00:00
if ( SetupLocalPlayerForConnection ( conn , networkIdentity , playerController2 ) )
{
result = true ;
}
else
{
2020-12-23 13:48:31 +00:00
QLog . Log (
2020-12-21 00:50:24 +01:00
$"Replacing playerGameObject object netId: {playerGameObject.GetComponent<NetworkIdentity>().netId} asset ID {playerGameObject.GetComponent<NetworkIdentity>().assetId}" ) ;
2020-12-02 18:40:38 +00:00
FinishPlayerForConnection ( conn , networkIdentity , playerGameObject ) ;
if ( networkIdentity . LocalPlayerAuthority )
2020-12-02 12:42:26 +00:00
{
networkIdentity . SetClientOwner ( conn ) ;
}
result = true ;
}
}
return result ;
}
2020-12-23 12:58:45 +00:00
private static bool GetNetworkIdentity ( GameObject go , out QNetworkIdentity view )
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
view = go . GetComponent < QNetworkIdentity > ( ) ;
2020-12-02 12:42:26 +00:00
bool result ;
if ( view = = null )
{
2020-12-31 12:10:55 +00:00
QLog . Error ( "UNET failure. GameObject doesn't have NetworkIdentity." ) ;
2020-12-02 12:42:26 +00:00
result = false ;
}
else
{
result = true ;
}
return result ;
}
2020-12-23 12:58:45 +00:00
public static void SetClientReady ( QNetworkConnection conn ) = > instance . SetClientReadyInternal ( conn ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
internal void SetClientReadyInternal ( QNetworkConnection conn )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"SetClientReadyInternal for conn:{conn.connectionId}" ) ;
2020-12-02 12:42:26 +00:00
if ( conn . isReady )
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"SetClientReady conn {conn.connectionId} already ready" ) ;
2020-12-02 12:42:26 +00:00
}
else
{
2020-12-02 18:40:38 +00:00
if ( conn . PlayerControllers . Count = = 0 )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Warning ( "Ready with no player object" ) ;
2020-12-02 12:42:26 +00:00
}
conn . isReady = true ;
2020-12-23 12:58:45 +00:00
if ( conn is QULocalConnectionToClient )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( "NetworkServer Ready handling ULocalConnectionToClient" ) ;
2020-12-02 18:40:38 +00:00
foreach ( var networkIdentity in objects . Values )
2020-12-02 12:42:26 +00:00
{
if ( networkIdentity ! = null & & networkIdentity . gameObject ! = null )
{
2020-12-16 09:08:38 +00:00
var flag = networkIdentity . OnCheckObserver ( conn ) ;
2020-12-02 12:42:26 +00:00
if ( flag )
{
networkIdentity . AddObserver ( conn ) ;
}
2020-12-02 18:40:38 +00:00
if ( ! networkIdentity . IsClient )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( "LocalClient.SetSpawnObject calling OnStartClient" ) ;
2020-12-02 12:42:26 +00:00
networkIdentity . OnStartClient ( ) ;
}
}
}
}
else
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"Spawning {objects.Count} objects for conn {conn.connectionId}" ) ;
2020-12-23 12:58:45 +00:00
var objectSpawnFinishedMessage = new QObjectSpawnFinishedMessage
2020-12-16 09:08:38 +00:00
{
State = 0 U
} ;
2020-12-02 12:42:26 +00:00
conn . Send ( 12 , objectSpawnFinishedMessage ) ;
2020-12-02 18:40:38 +00:00
foreach ( var networkIdentity2 in objects . Values )
2020-12-02 12:42:26 +00:00
{
if ( networkIdentity2 = = null )
{
2020-12-31 12:10:55 +00:00
QLog . Warning ( "Invalid object found in server local object list (null NetworkIdentity)." ) ;
2020-12-02 12:42:26 +00:00
}
else if ( networkIdentity2 . gameObject . activeSelf )
{
2020-12-31 12:10:55 +00:00
QLog . Debug (
2020-12-21 00:50:24 +01:00
$"Sending spawn message for current server objects name='{networkIdentity2.gameObject.name}' netId={networkIdentity2.NetId}" ) ;
2020-12-16 09:08:38 +00:00
var flag2 = networkIdentity2 . OnCheckObserver ( conn ) ;
2020-12-02 12:42:26 +00:00
if ( flag2 )
{
networkIdentity2 . AddObserver ( conn ) ;
}
}
}
2020-12-03 11:56:32 +00:00
objectSpawnFinishedMessage . State = 1 U ;
2020-12-02 12:42:26 +00:00
conn . Send ( 12 , objectSpawnFinishedMessage ) ;
}
}
}
2020-12-23 12:58:45 +00:00
internal static void ShowForConnection ( QNetworkIdentity uv , QNetworkConnection conn )
2020-12-02 12:42:26 +00:00
{
if ( conn . isReady )
{
instance . SendSpawnMessage ( uv , conn ) ;
}
}
2020-12-23 12:58:45 +00:00
internal static void HideForConnection ( QNetworkIdentity uv , QNetworkConnection conn )
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
conn . Send ( 13 , new QObjectDestroyMessage
2020-12-02 12:42:26 +00:00
{
2020-12-03 11:56:32 +00:00
NetId = uv . NetId
2020-12-02 12:42:26 +00:00
} ) ;
}
public static void SetAllClientsNotReady ( )
{
2020-12-21 00:50:24 +01:00
foreach ( var networkConnection in connections )
2020-12-02 12:42:26 +00:00
{
if ( networkConnection ! = null )
{
2020-12-02 18:40:38 +00:00
SetClientNotReady ( networkConnection ) ;
2020-12-02 12:42:26 +00:00
}
}
}
2020-12-23 12:58:45 +00:00
public static void SetClientNotReady ( QNetworkConnection conn ) = > instance . InternalSetClientNotReady ( conn ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
internal void InternalSetClientNotReady ( QNetworkConnection conn )
2020-12-02 12:42:26 +00:00
{
if ( conn . isReady )
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"PlayerNotReady {conn}" ) ;
2020-12-02 12:42:26 +00:00
conn . isReady = false ;
conn . RemoveObservers ( ) ;
2020-12-23 12:58:45 +00:00
var msg = new QNotReadyMessage ( ) ;
2020-12-02 12:42:26 +00:00
conn . Send ( 36 , msg ) ;
}
}
2020-12-23 12:58:45 +00:00
private static void OnClientReadyMessage ( QNetworkMessage netMsg )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"Default handler for ready message from {netMsg.Connection}" ) ;
2020-12-03 11:56:32 +00:00
SetClientReady ( netMsg . Connection ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-23 12:58:45 +00:00
private static void OnRemovePlayerMessage ( QNetworkMessage netMsg )
2020-12-02 12:42:26 +00:00
{
2020-12-21 00:50:24 +01:00
netMsg . ReadMessage ( s_RemovePlayerMessage ) ;
2020-12-16 09:08:38 +00:00
netMsg . Connection . GetPlayerController ( s_RemovePlayerMessage . PlayerControllerId , out var playerController ) ;
2020-12-02 12:42:26 +00:00
if ( playerController ! = null )
{
2020-12-03 11:56:32 +00:00
netMsg . Connection . RemovePlayerController ( s_RemovePlayerMessage . PlayerControllerId ) ;
Destroy ( playerController . Gameobject ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-18 20:20:54 +00:00
else
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Error (
2020-12-21 00:50:24 +01:00
$"Received remove player message but could not find the player ID: {s_RemovePlayerMessage.PlayerControllerId}" ) ;
2020-12-02 12:42:26 +00:00
}
}
2020-12-23 12:58:45 +00:00
private static void OnCommandMessage ( QNetworkMessage netMsg )
2020-12-02 12:42:26 +00:00
{
2020-12-03 11:56:32 +00:00
var cmdHash = ( int ) netMsg . Reader . ReadPackedUInt32 ( ) ;
var networkInstanceId = netMsg . Reader . ReadNetworkId ( ) ;
2020-12-02 18:40:38 +00:00
var gameObject = FindLocalObject ( networkInstanceId ) ;
2020-12-02 12:42:26 +00:00
if ( gameObject = = null )
{
2020-12-31 12:10:55 +00:00
QLog . Warning ( $"Instance not found when handling Command message [netId={networkInstanceId}]" ) ;
2020-12-02 12:42:26 +00:00
}
else
{
2020-12-23 12:58:45 +00:00
var component = gameObject . GetComponent < QNetworkIdentity > ( ) ;
2020-12-02 12:42:26 +00:00
if ( component = = null )
{
2020-12-31 12:10:55 +00:00
QLog . Warning (
2020-12-21 00:50:24 +01:00
$"NetworkIdentity deleted when handling Command message [netId={networkInstanceId}]" ) ;
2020-12-02 12:42:26 +00:00
}
else
{
2020-12-02 18:40:38 +00:00
var flag = false ;
2020-12-21 00:50:24 +01:00
foreach ( var playerController in netMsg . Connection . PlayerControllers )
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
if ( playerController . Gameobject ! = null & & playerController . Gameobject . GetComponent < QNetworkIdentity > ( ) . NetId = = component . NetId )
2020-12-02 12:42:26 +00:00
{
flag = true ;
break ;
}
}
if ( ! flag )
{
2020-12-03 11:56:32 +00:00
if ( component . ClientAuthorityOwner ! = netMsg . Connection )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Warning ( $"Command for object without authority [netId={networkInstanceId}]" ) ;
2020-12-02 12:42:26 +00:00
return ;
}
}
2020-12-23 13:48:31 +00:00
QLog . Log ( $"OnCommandMessage for netId={networkInstanceId} conn={netMsg.Connection}" ) ;
2020-12-03 11:56:32 +00:00
component . HandleCommand ( cmdHash , netMsg . Reader ) ;
2020-12-02 12:42:26 +00:00
}
}
}
internal void SpawnObject ( GameObject obj )
{
2020-12-02 18:40:38 +00:00
if ( ! active )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Error (
2020-12-23 13:48:31 +00:00
$"SpawnObject for {obj}, NetworkServer is not active. Cannot spawn objects without an active server." ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-16 09:08:38 +00:00
else if ( ! GetNetworkIdentity ( obj , out var networkIdentity ) )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Error ( $"SpawnObject {obj} has no QSBNetworkIdentity. Please add a NetworkIdentity to {obj}" ) ;
2020-12-02 12:42:26 +00:00
}
else
{
networkIdentity . Reset ( ) ;
networkIdentity . OnStartServer ( false ) ;
networkIdentity . RebuildObservers ( true ) ;
}
}
2020-12-23 12:58:45 +00:00
internal void SendSpawnMessage ( QNetworkIdentity uv , QNetworkConnection conn )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
if ( ! uv . ServerOnly )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
if ( uv . SceneId . IsEmpty ( ) )
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
var objectSpawnMessage = new QObjectSpawnMessage
2020-12-16 09:08:38 +00:00
{
NetId = uv . NetId ,
assetId = uv . AssetId ,
Position = uv . transform . position ,
Rotation = uv . transform . rotation
} ;
2020-12-23 12:58:45 +00:00
var networkWriter = new QNetworkWriter ( ) ;
2020-12-02 12:42:26 +00:00
uv . UNetSerializeAllVars ( networkWriter ) ;
if ( networkWriter . Position > 0 )
{
2020-12-03 11:56:32 +00:00
objectSpawnMessage . Payload = networkWriter . ToArray ( ) ;
2020-12-02 12:42:26 +00:00
}
if ( conn ! = null )
{
conn . Send ( 3 , objectSpawnMessage ) ;
}
else
{
2020-12-02 18:40:38 +00:00
SendToReady ( uv . gameObject , 3 , objectSpawnMessage ) ;
2020-12-02 12:42:26 +00:00
}
}
else
{
2020-12-23 12:58:45 +00:00
var objectSpawnSceneMessage = new QObjectSpawnSceneMessage
2020-12-16 09:08:38 +00:00
{
NetId = uv . NetId ,
SceneId = uv . SceneId ,
Position = uv . transform . position
} ;
2020-12-23 12:58:45 +00:00
var networkWriter2 = new QNetworkWriter ( ) ;
2020-12-02 12:42:26 +00:00
uv . UNetSerializeAllVars ( networkWriter2 ) ;
if ( networkWriter2 . Position > 0 )
{
2020-12-03 11:56:32 +00:00
objectSpawnSceneMessage . Payload = networkWriter2 . ToArray ( ) ;
2020-12-02 12:42:26 +00:00
}
if ( conn ! = null )
{
conn . Send ( 10 , objectSpawnSceneMessage ) ;
}
else
{
2020-12-02 18:40:38 +00:00
SendToReady ( uv . gameObject , 3 , objectSpawnSceneMessage ) ;
2020-12-02 12:42:26 +00:00
}
}
}
}
2020-12-23 12:58:45 +00:00
public static void DestroyPlayersForConnection ( QNetworkConnection conn )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
if ( conn . PlayerControllers . Count = = 0 )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Warning ( "Empty player list given to NetworkServer.Destroy(), nothing to do." ) ;
2020-12-02 12:42:26 +00:00
}
else
{
2020-12-02 18:40:38 +00:00
if ( conn . ClientOwnedObjects ! = null )
2020-12-02 12:42:26 +00:00
{
2020-12-16 08:57:15 +00:00
var hashSet = new HashSet < NetworkInstanceId > ( conn . ClientOwnedObjects ) ;
2020-12-21 00:50:24 +01:00
foreach ( var gameObject in hashSet . Select ( FindLocalObject ) . Where ( gameObject = > gameObject ! = null ) )
2020-12-02 12:42:26 +00:00
{
2020-12-21 00:50:24 +01:00
DestroyObject ( gameObject ) ;
2020-12-02 12:42:26 +00:00
}
}
2020-12-21 00:50:24 +01:00
foreach ( var playerController in conn . PlayerControllers )
2020-12-02 12:42:26 +00:00
{
if ( playerController . IsValid )
{
2020-12-03 11:56:32 +00:00
if ( ! ( playerController . UnetView = = null ) )
2020-12-02 12:42:26 +00:00
{
2020-12-03 11:56:32 +00:00
DestroyObject ( playerController . UnetView , true ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-03 11:56:32 +00:00
playerController . Gameobject = null ;
2020-12-02 12:42:26 +00:00
}
}
2020-12-02 18:40:38 +00:00
conn . PlayerControllers . Clear ( ) ;
2020-12-02 12:42:26 +00:00
}
}
private static void UnSpawnObject ( GameObject obj )
{
if ( obj = = null )
{
2020-12-23 13:48:31 +00:00
QLog . Log ( "NetworkServer UnspawnObject is null" ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-16 09:08:38 +00:00
else if ( GetNetworkIdentity ( obj , out var uv ) )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
UnSpawnObject ( uv ) ;
2020-12-02 12:42:26 +00:00
}
}
2020-12-23 12:58:45 +00:00
private static void UnSpawnObject ( QNetworkIdentity uv ) = > DestroyObject ( uv , false ) ;
2020-12-02 12:42:26 +00:00
private static void DestroyObject ( GameObject obj )
{
if ( obj = = null )
{
2020-12-23 13:48:31 +00:00
QLog . Log ( "NetworkServer DestroyObject is null" ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-16 09:08:38 +00:00
else if ( GetNetworkIdentity ( obj , out var uv ) )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
DestroyObject ( uv , true ) ;
2020-12-02 12:42:26 +00:00
}
}
2020-12-23 12:58:45 +00:00
private static void DestroyObject ( QNetworkIdentity uv , bool destroyServerObject )
2020-12-02 12:42:26 +00:00
{
2020-12-23 13:48:31 +00:00
QLog . Log ( $"DestroyObject instance:{uv.NetId}" ) ;
2020-12-02 18:40:38 +00:00
if ( objects . ContainsKey ( uv . NetId ) )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
objects . Remove ( uv . NetId ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-21 00:50:24 +01:00
uv . ClientAuthorityOwner ? . RemoveOwnedObject ( uv ) ;
2020-12-23 12:58:45 +00:00
var objectDestroyMessage = new QObjectDestroyMessage
2020-12-16 09:08:38 +00:00
{
NetId = uv . NetId
} ;
2020-12-02 18:40:38 +00:00
SendToObservers ( uv . gameObject , 1 , objectDestroyMessage ) ;
2020-12-02 12:42:26 +00:00
uv . ClearObservers ( ) ;
2020-12-23 12:58:45 +00:00
if ( QNetworkClient . active & & instance . m_LocalClientActive )
2020-12-02 12:42:26 +00:00
{
uv . OnNetworkDestroy ( ) ;
2020-12-23 12:58:45 +00:00
QClientScene . SetLocalObject ( objectDestroyMessage . NetId , null ) ;
2020-12-02 12:42:26 +00:00
}
if ( destroyServerObject )
{
2020-12-02 18:40:38 +00:00
UnityEngine . Object . Destroy ( uv . gameObject ) ;
2020-12-02 12:42:26 +00:00
}
uv . MarkForReset ( ) ;
}
2020-12-16 09:08:38 +00:00
public static void ClearLocalObjects ( ) = > objects . Clear ( ) ;
2020-12-02 12:42:26 +00:00
public static void Spawn ( GameObject obj )
{
2020-12-02 18:40:38 +00:00
if ( VerifyCanSpawn ( obj ) )
2020-12-02 12:42:26 +00:00
{
instance . SpawnObject ( obj ) ;
}
}
2020-12-16 09:08:38 +00:00
private static bool CheckForPrefab ( GameObject obj ) = > false ;
2020-12-02 12:42:26 +00:00
private static bool VerifyCanSpawn ( GameObject obj )
{
bool result ;
2020-12-02 18:40:38 +00:00
if ( CheckForPrefab ( obj ) )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Error ( $"GameObject {obj.name} is a prefab, it can't be spawned. This will cause errors in builds." ) ;
2020-12-02 12:42:26 +00:00
result = false ;
}
else
{
result = true ;
}
return result ;
}
public static bool SpawnWithClientAuthority ( GameObject obj , GameObject player )
{
2020-12-23 12:58:45 +00:00
var component = player . GetComponent < QNetworkIdentity > ( ) ;
2020-12-02 12:42:26 +00:00
bool result ;
if ( component = = null )
{
2020-12-31 12:10:55 +00:00
QLog . Error ( "SpawnWithClientAuthority player object has no NetworkIdentity" ) ;
2020-12-02 12:42:26 +00:00
result = false ;
}
2020-12-02 18:40:38 +00:00
else if ( component . ConnectionToClient = = null )
2020-12-02 12:42:26 +00:00
{
2020-12-31 12:10:55 +00:00
QLog . Error ( "SpawnWithClientAuthority player object is not a player." ) ;
2020-12-02 12:42:26 +00:00
result = false ;
}
else
{
2020-12-02 18:40:38 +00:00
result = SpawnWithClientAuthority ( obj , component . ConnectionToClient ) ;
2020-12-02 12:42:26 +00:00
}
return result ;
}
2020-12-23 12:58:45 +00:00
public static bool SpawnWithClientAuthority ( GameObject obj , QNetworkConnection conn )
2020-12-02 12:42:26 +00:00
{
bool result ;
if ( ! conn . isReady )
{
2020-12-31 12:10:55 +00:00
QLog . Error ( "SpawnWithClientAuthority NetworkConnection is not ready!" ) ;
2020-12-02 12:42:26 +00:00
result = false ;
}
else
{
2020-12-02 18:40:38 +00:00
Spawn ( obj ) ;
2020-12-23 12:58:45 +00:00
var component = obj . GetComponent < QNetworkIdentity > ( ) ;
2020-12-21 00:50:24 +01:00
result = ! ( component = = null ) & & component . IsServer & & component . AssignClientAuthority ( conn ) ;
2020-12-02 12:42:26 +00:00
}
return result ;
}
2020-12-23 12:58:45 +00:00
public static bool SpawnWithClientAuthority ( GameObject obj , NetworkHash128 assetId , QNetworkConnection conn )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
Spawn ( obj , assetId ) ;
2020-12-23 12:58:45 +00:00
var component = obj . GetComponent < QNetworkIdentity > ( ) ;
2020-12-02 18:40:38 +00:00
return ! ( component = = null ) & & component . IsServer & & component . AssignClientAuthority ( conn ) ;
2020-12-02 12:42:26 +00:00
}
2020-12-16 08:45:58 +00:00
public static void Spawn ( GameObject obj , NetworkHash128 assetId )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
if ( VerifyCanSpawn ( obj ) )
2020-12-02 12:42:26 +00:00
{
2020-12-16 09:08:38 +00:00
if ( GetNetworkIdentity ( obj , out var networkIdentity ) )
2020-12-02 12:42:26 +00:00
{
networkIdentity . SetDynamicAssetId ( assetId ) ;
}
instance . SpawnObject ( obj ) ;
}
}
2020-12-16 09:08:38 +00:00
public static void Destroy ( GameObject obj ) = > DestroyObject ( obj ) ;
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public static void UnSpawn ( GameObject obj ) = > UnSpawnObject ( obj ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
internal bool InvokeBytes ( QULocalConnectionToServer conn , byte [ ] buffer , int numBytes , int channelId )
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
var networkReader = new QNetworkReader ( buffer ) ;
2020-12-02 12:42:26 +00:00
networkReader . ReadInt16 ( ) ;
2020-12-02 18:40:38 +00:00
var num = networkReader . ReadInt16 ( ) ;
2020-12-02 12:42:26 +00:00
bool result ;
2020-12-02 18:40:38 +00:00
if ( handlers . ContainsKey ( num ) & & m_LocalConnection ! = null )
2020-12-02 12:42:26 +00:00
{
m_LocalConnection . InvokeHandler ( num , networkReader , channelId ) ;
result = true ;
}
else
{
result = false ;
}
return result ;
}
2020-12-23 12:58:45 +00:00
internal bool InvokeHandlerOnServer ( QULocalConnectionToServer conn , short msgType , QMessageBase msg , int channelId )
2020-12-02 12:42:26 +00:00
{
bool result ;
2020-12-02 18:40:38 +00:00
if ( handlers . ContainsKey ( msgType ) & & m_LocalConnection ! = null )
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
var writer = new QNetworkWriter ( ) ;
2020-12-02 12:42:26 +00:00
msg . Serialize ( writer ) ;
2020-12-23 12:58:45 +00:00
var reader = new QNetworkReader ( writer ) ;
2020-12-02 12:42:26 +00:00
m_LocalConnection . InvokeHandler ( msgType , reader , channelId ) ;
result = true ;
}
else
{
2020-12-31 12:10:55 +00:00
QLog . Error ( $"Local invoke: Failed to find local connection to invoke handler on [connectionId={conn.connectionId}] for MsgId:{msgType}" ) ;
2020-12-02 12:42:26 +00:00
result = false ;
}
return result ;
}
2020-12-16 08:57:15 +00:00
public static GameObject FindLocalObject ( NetworkInstanceId netId ) = > instance . m_NetworkScene . FindLocalObject ( netId ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
private static bool ValidateSceneObject ( QNetworkIdentity netId ) = > netId . gameObject . hideFlags ! = HideFlags . NotEditable & & netId . gameObject . hideFlags ! = HideFlags . HideAndDontSave & & ! netId . SceneId . IsEmpty ( ) ;
2020-12-02 12:42:26 +00:00
public static bool SpawnObjects ( )
{
bool result ;
2020-12-02 18:40:38 +00:00
if ( ! active )
2020-12-02 12:42:26 +00:00
{
result = true ;
}
else
{
2020-12-23 12:58:45 +00:00
var objectsOfTypeAll = Resources . FindObjectsOfTypeAll < QNetworkIdentity > ( ) ;
2020-12-02 18:40:38 +00:00
foreach ( var networkIdentity in objectsOfTypeAll )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
if ( ValidateSceneObject ( networkIdentity ) )
2020-12-02 12:42:26 +00:00
{
2020-12-21 00:50:24 +01:00
Debug . Log (
$"SpawnObjects sceneId:{networkIdentity.SceneId} name:{networkIdentity.gameObject.name}" ) ;
2020-12-02 12:42:26 +00:00
networkIdentity . Reset ( ) ;
networkIdentity . gameObject . SetActive ( true ) ;
}
}
2020-12-02 18:40:38 +00:00
foreach ( var networkIdentity2 in objectsOfTypeAll )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
if ( ValidateSceneObject ( networkIdentity2 ) )
2020-12-02 12:42:26 +00:00
{
2020-12-02 18:40:38 +00:00
Spawn ( networkIdentity2 . gameObject ) ;
2020-12-02 12:42:26 +00:00
networkIdentity2 . ForceAuthority ( true ) ;
}
}
result = true ;
}
return result ;
}
2020-12-23 12:58:45 +00:00
private static void SendCrc ( QNetworkConnection targetConnection )
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
if ( QNetworkCRC . singleton ! = null )
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
if ( QNetworkCRC . scriptCRCCheck )
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
var crcmessage = new QCRCMessage ( ) ;
var list = new List < QCRCMessageEntry > ( ) ;
foreach ( var text in QNetworkCRC . singleton . scripts . Keys )
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
list . Add ( new QCRCMessageEntry
2020-12-02 12:42:26 +00:00
{
name = text ,
2020-12-23 12:58:45 +00:00
channel = ( byte ) QNetworkCRC . singleton . scripts [ text ]
2020-12-02 12:42:26 +00:00
} ) ;
}
crcmessage . scripts = list . ToArray ( ) ;
targetConnection . Send ( 14 , crcmessage ) ;
}
}
}
2020-12-23 12:58:45 +00:00
private static volatile QNetworkServer s_Instance ;
2020-12-02 12:42:26 +00:00
2020-12-18 20:32:16 +00:00
private static readonly object s_Sync = new UnityEngine . Object ( ) ;
2020-12-02 12:42:26 +00:00
private bool m_LocalClientActive ;
2020-12-23 12:58:45 +00:00
private readonly List < QNetworkConnection > m_LocalConnectionsFakeList = new List < QNetworkConnection > ( ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
private QULocalConnectionToClient m_LocalConnection ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
private readonly QNetworkScene m_NetworkScene ;
2020-12-02 12:42:26 +00:00
2020-12-18 20:32:16 +00:00
private readonly HashSet < int > m_ExternalConnections ;
2020-12-02 12:42:26 +00:00
2020-12-18 20:32:16 +00:00
private readonly ServerSimpleWrapper m_SimpleServerSimple ;
2020-12-02 12:42:26 +00:00
private float m_MaxDelay = 0.1f ;
2020-12-18 20:32:16 +00:00
private readonly HashSet < NetworkInstanceId > m_RemoveList ;
2020-12-02 12:42:26 +00:00
private int m_RemoveListCount ;
private const int k_RemoveListInterval = 100 ;
internal static ushort maxPacketSize ;
2020-12-23 12:58:45 +00:00
private static readonly QRemovePlayerMessage s_RemovePlayerMessage = new QRemovePlayerMessage ( ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
private class ServerSimpleWrapper : QNetworkServerSimple
2020-12-02 12:42:26 +00:00
{
2020-12-24 16:34:24 +00:00
public ServerSimpleWrapper ( QNetworkServer server ) = > m_Server = server ;
2020-12-02 12:42:26 +00:00
2020-12-16 09:08:38 +00:00
public override void OnConnectError ( int connectionId , byte error ) = > m_Server . GenerateConnectError ( error ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
public override void OnDataError ( QNetworkConnection conn , byte error ) = > m_Server . GenerateDataError ( conn , error ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
public override void OnDisconnectError ( QNetworkConnection conn , byte error ) = > m_Server . GenerateDisconnectError ( conn , error ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
public override void OnConnected ( QNetworkConnection conn ) = > m_Server . OnConnected ( conn ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
public override void OnDisconnected ( QNetworkConnection conn ) = > m_Server . OnDisconnected ( conn ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
public override void OnData ( QNetworkConnection conn , int receivedSize , int channelId ) = > m_Server . OnData ( conn , receivedSize , channelId ) ;
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
private readonly QNetworkServer m_Server ;
2020-12-02 12:42:26 +00:00
}
}
2020-12-03 08:28:05 +00:00
}