2020-09-24 00:43:27 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
2020-10-22 19:19:16 -04:00
# include "DirectLinkEndpoint.h"
2020-09-24 00:43:27 -04:00
2020-10-29 13:38:15 -04:00
# include "DirectLinkConnectionRequestHandler.h"
2021-04-29 19:32:06 -04:00
# include "DirectLinkLog.h"
2020-10-22 19:19:16 -04:00
# include "DirectLinkMessages.h"
2021-04-29 19:32:06 -04:00
# include "DirectLinkSceneGraphNode.h"
2020-10-29 13:38:15 -04:00
# include "DirectLinkStreamConnectionPoint.h"
# include "DirectLinkStreamDescription.h"
# include "DirectLinkStreamDestination.h"
# include "DirectLinkStreamReceiver.h"
# include "DirectLinkStreamSender.h"
2020-10-22 19:19:16 -04:00
# include "DirectLinkStreamSource.h"
2020-09-24 00:43:27 -04:00
# include "Async/Async.h"
# include "MessageEndpointBuilder.h"
namespace DirectLink
{
struct
{
// heartbeat message periodically sent to keep the connections alive
double HeartbeatThreshold_s = 5.0 ;
// endpoint not seen for a long time:
bool bPeriodicalyCleanupTimedOutEndpoint = true ;
double ThresholdEndpointCleanup_s = 30.0 ;
double CleanupOldEndpointPeriod_s = 10.0 ;
// auto connect streams by name
bool bAutoconnectFromSources = true ;
bool bAutoconnectFromDestination = false ;
} gConfig ;
2021-03-05 19:27:14 -04:00
double gUdpMessagingInitializationTime = - 1. ;
ECommunicationStatus ValidateCommunicationStatus ( )
{
if ( ! FModuleManager : : Get ( ) . IsModuleLoaded ( " UdpMessaging " ) )
{
gUdpMessagingInitializationTime = FPlatformTime : : Seconds ( ) ;
}
2021-04-29 19:32:06 -04:00
return ( FModuleManager : : Get ( ) . LoadModule ( " Messaging " ) ? ECommunicationStatus : : NoIssue : ECommunicationStatus : : ModuleNotLoaded_Messaging )
| ( FModuleManager : : Get ( ) . LoadModule ( " UdpMessaging " ) ? ECommunicationStatus : : NoIssue : ECommunicationStatus : : ModuleNotLoaded_UdpMessaging )
| ( FModuleManager : : Get ( ) . LoadModule ( " Networking " ) ? ECommunicationStatus : : NoIssue : ECommunicationStatus : : ModuleNotLoaded_Networking ) ;
2021-03-05 19:27:14 -04:00
}
2020-10-29 13:38:15 -04:00
class FSharedState
{
public :
FSharedState ( const FString & NiceName ) : NiceName ( NiceName ) { }
mutable FRWLock SourcesLock ;
TArray < TSharedPtr < FStreamSource > > Sources ;
std : : atomic < bool > bDirtySources { false } ;
mutable FRWLock DestinationsLock ;
TArray < TSharedPtr < FStreamDestination > > Destinations ;
std : : atomic < bool > bDirtyDestinations { false } ;
mutable FRWLock StreamsLock ;
FStreamPort StreamPortIdGenerator = InvalidStreamPort ;
TArray < FStreamDescription > Streams ; // map streamportId -> stream ? array of N ports ?
// cleared on inner thread loop start
mutable FRWLock ObserversLock ;
TArray < IEndpointObserver * > Observers ;
mutable FRWLock RawInfoCopyLock ;
FRawInfo RawInfo ;
std : : atomic < bool > bInnerThreadShouldRun { false } ;
bool bDebugLog = false ;
const FString NiceName ; // not locked (wrote once)
TSharedPtr < FMessageEndpoint , ESPMode : : ThreadSafe > MessageEndpoint ;
FStreamDescription * GetStreamByLocalPort ( FStreamPort LocalPort , const FRWScopeLock & _ ) ;
void CloseStreamInternal ( FStreamDescription & Stream , const FRWScopeLock & _ , bool bNotifyRemote = true ) ;
} ;
/** Inner thread allows async network communication, which avoids user thread to be locked on every sync. */
class FInternalThreadState
{
public :
FInternalThreadState ( FEndpoint & Owner , FSharedState & SharedState ) : Owner ( Owner ) , SharedState ( SharedState ) { }
2021-02-18 18:13:28 -04:00
bool Init ( ) ; // once, any thread
2020-10-29 13:38:15 -04:00
void Run ( ) ; // once, blocking, inner thread only
2021-02-18 18:13:28 -04:00
FEvent * InnerThreadEvent = nullptr ;
2020-10-29 13:38:15 -04:00
TFuture < void > InnerThreadResult ; // allow to join() in the dtr
private :
void Handle_DeltaMessage ( const FDirectLinkMsg_DeltaMessage & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context ) ;
void Handle_HaveListMessage ( const FDirectLinkMsg_HaveListMessage & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context ) ;
void Handle_EndpointLifecycle ( const FDirectLinkMsg_EndpointLifecycle & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context ) ;
void Handle_QueryEndpointState ( const FDirectLinkMsg_QueryEndpointState & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context ) ;
void Handle_EndpointState ( const FDirectLinkMsg_EndpointState & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context ) ;
void Handle_OpenStreamRequest ( const FDirectLinkMsg_OpenStreamRequest & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context ) ;
void Handle_OpenStreamAnswer ( const FDirectLinkMsg_OpenStreamAnswer & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context ) ;
void Handle_CloseStreamRequest ( const FDirectLinkMsg_CloseStreamRequest & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context ) ;
/** Check if a received message is sent by 'this' endpoint.
* Can be useful to skip handling of own messages . Makes sense in handlers of subscribed messages .
* @ param MaybeRemoteAddress Address of the sender ( see Context - > GetSender ( ) )
* @ returns whether given address is this address */
bool IsMine ( const FMessageAddress & MaybeRemoteAddress ) const ;
/**
* Check if the given address is an incompatible endpoint
*/
bool IsIgnoredEndpoint ( const FMessageAddress & Address ) const ;
/** Note on state replication:
* On local state edition ( eg . when a source is added ) the new state is broadcasted .
* On top of that , the state revision is broadcasted on heartbeats every few seconds .
* This allow other endpoint to detect when a replicated state is no longer valid , and query an update .
* This covers all failure case , and is lightweight as only the revision number is frequently broadcasted . */
void ReplicateState ( const FMessageAddress & RemoteEndpointAddress ) const ;
void ReplicateState_Broadcast ( ) const ;
FString ToString_dbg ( ) const ;
void UpdateSourceDescription ( ) ;
void UpdateDestinationDescription ( ) ;
TUniquePtr < IStreamReceiver > MakeReceiver ( FGuid SourceGuid , FGuid DestinationGuid , FMessageAddress RemoteAddress , FStreamPort RemotePort ) ;
TSharedPtr < IStreamSender > MakeSender ( FGuid SourceGuid , FMessageAddress RemoteAddress , FStreamPort RemotePort ) ;
void RemoveEndpoint ( const FMessageAddress & RemoteEndpointAddress ) ;
void MarkRemoteAsSeen ( const FMessageAddress & RemoteEndpointAddress ) ;
void CleanupTimedOutEndpoint ( ) ;
private :
FEndpoint & Owner ;
FSharedState & SharedState ;
TSharedPtr < FMessageEndpoint , ESPMode : : ThreadSafe > MessageEndpoint ;
TMap < FMessageAddress , FDirectLinkMsg_EndpointState > RemoteEndpointDescriptions ;
FDirectLinkMsg_EndpointState ThisDescription ;
// state replication
double Now_s = 0 ;
double LastHeartbeatTime_s = 0 ;
double LastEndpointCleanupTime_s = 0 ;
mutable uint32 LastBroadcastedStateRevision = 0 ;
TMap < FMessageAddress , double > RemoteLastSeenTime ;
TSet < FMessageAddress > IgnoredEndpoints ;
} ;
2020-09-24 00:43:27 -04:00
FEndpoint : : FEndpoint ( const FString & InName )
2020-10-29 13:38:15 -04:00
: SharedStatePtr ( MakeUnique < FSharedState > ( InName ) )
, SharedState ( * SharedStatePtr )
, InternalPtr ( MakeUnique < FInternalThreadState > ( * this , SharedState ) )
, Internal ( * InternalPtr )
2020-09-24 00:43:27 -04:00
{
2021-03-05 19:27:14 -04:00
ECommunicationStatus ComStatus = ValidateCommunicationStatus ( ) ;
2021-04-29 19:32:06 -04:00
if ( ComStatus ! = ECommunicationStatus : : NoIssue )
2021-03-05 19:27:14 -04:00
{
UE_LOG ( LogDirectLinkNet , Error , TEXT ( " Endpoint '%s': Unable to start communication (error code:%d): " ) , * SharedState . NiceName , ComStatus ) ;
return ;
}
2021-02-18 18:13:28 -04:00
if ( Internal . Init ( ) )
{
UE_LOG ( LogDirectLinkNet , Log , TEXT ( " Endpoint '%s' Start internal thread " ) , * SharedState . NiceName ) ;
2020-09-24 00:43:27 -04:00
2021-02-18 18:13:28 -04:00
Internal . InnerThreadEvent = FPlatformProcess : : GetSynchEventFromPool ( ) ;
Internal . InnerThreadResult = Async ( EAsyncExecution : : Thread ,
[ & , this ]
{
FPlatformProcess : : SetThreadName ( TEXT ( " DirectLink " ) ) ;
Internal . Run ( ) ;
}
) ;
}
2020-09-24 00:43:27 -04:00
}
2020-10-29 13:38:15 -04:00
void FEndpoint : : SetVerbose ( bool bVerbose )
{
SharedState . bDebugLog = bVerbose ;
}
2020-09-24 00:43:27 -04:00
FEndpoint : : ~ FEndpoint ( )
{
2020-10-29 13:38:15 -04:00
UE_LOG ( LogDirectLinkNet , Log , TEXT ( " Endpoint '%s' closing... " ) , * SharedState . NiceName ) ;
2020-09-24 00:43:27 -04:00
SharedState . bInnerThreadShouldRun = false ;
2021-02-18 18:13:28 -04:00
if ( Internal . InnerThreadEvent )
{
Internal . InnerThreadEvent - > Trigger ( ) ;
Internal . InnerThreadResult . Get ( ) ; // join
FPlatformProcess : : ReturnSynchEventToPool ( Internal . InnerThreadEvent ) ;
}
2020-09-24 00:43:27 -04:00
2020-10-29 13:38:15 -04:00
UE_LOG ( LogDirectLinkNet , Log , TEXT ( " Endpoint '%s' closed " ) , * SharedState . NiceName ) ;
2020-09-24 00:43:27 -04:00
}
FSourceHandle FEndpoint : : AddSource ( const FString & Name , EVisibility Visibility )
{
FGuid Id ;
{
FRWScopeLock _ ( SharedState . SourcesLock , SLT_Write ) ;
TSharedPtr < FStreamSource > & NewSource = SharedState . Sources . Add_GetRef ( MakeShared < FStreamSource > ( Name , Visibility ) ) ;
Id = NewSource - > GetId ( ) ;
}
SharedState . bDirtySources = true ;
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Source added '%s' " ) , * SharedState . NiceName , * Name ) ;
return Id ;
}
void FEndpoint : : RemoveSource ( const FSourceHandle & SourceId )
{
{
// first remove linked streams
FRWScopeLock _ ( SharedState . StreamsLock , SLT_Write ) ;
for ( FStreamDescription & Stream : SharedState . Streams )
{
if ( Stream . SourcePoint = = SourceId
2022-01-10 15:07:05 -05:00
& & Stream . Status ! = EStreamConnectionState : : Closed )
2020-09-24 00:43:27 -04:00
{
2020-10-29 13:38:15 -04:00
SharedState . CloseStreamInternal ( Stream , _ ) ;
2020-09-24 00:43:27 -04:00
}
}
}
int32 RemovedCount = 0 ;
{
FRWScopeLock _ ( SharedState . SourcesLock , SLT_Write ) ;
RemovedCount = SharedState . Sources . RemoveAll ( [ & ] ( const auto & Source ) { return Source - > GetId ( ) = = SourceId ; } ) ;
}
if ( RemovedCount )
{
SharedState . bDirtySources = true ;
}
}
void FEndpoint : : SetSourceRoot ( const FSourceHandle & SourceId , ISceneGraphNode * InRoot , bool bSnapshot )
{
{
FRWScopeLock _ ( SharedState . SourcesLock , SLT_Write ) ;
for ( TSharedPtr < FStreamSource > & Source : SharedState . Sources ) // #ue_directlink_cleanup: readonly on array, write on specific source lock
{
if ( Source - > GetId ( ) = = SourceId )
{
Source - > SetRoot ( InRoot ) ;
break ;
}
}
}
if ( bSnapshot )
{
SnapshotSource ( SourceId ) ;
}
}
void FEndpoint : : SnapshotSource ( const FSourceHandle & SourceId )
{
{
FRWScopeLock _ ( SharedState . SourcesLock , SLT_Write ) ; // make this a read, and detailed thread-safety inside the source
for ( TSharedPtr < FStreamSource > & Source : SharedState . Sources )
{
if ( Source - > GetId ( ) = = SourceId )
{
Source - > Snapshot ( ) ;
break ;
}
}
}
}
2020-10-29 13:38:15 -04:00
FDestinationHandle FEndpoint : : AddDestination ( const FString & Name , EVisibility Visibility , const TSharedPtr < IConnectionRequestHandler > & Provider )
2020-09-24 00:43:27 -04:00
{
FDestinationHandle Id ;
if ( ensure ( Provider . IsValid ( ) ) )
{
FRWScopeLock _ ( SharedState . DestinationsLock , SLT_Write ) ;
TSharedPtr < FStreamDestination > & NewDest = SharedState . Destinations . Add_GetRef ( MakeShared < FStreamDestination > ( Name , Visibility , Provider ) ) ;
Id = NewDest - > GetId ( ) ;
2021-04-29 19:32:06 -04:00
SharedState . bDirtyDestinations = true ;
2020-09-24 00:43:27 -04:00
}
return Id ;
}
void FEndpoint : : RemoveDestination ( const FDestinationHandle & Destination )
{
{
// first close associated streams
FRWScopeLock _ ( SharedState . StreamsLock , SLT_Write ) ;
for ( FStreamDescription & Stream : SharedState . Streams )
{
if ( Stream . DestinationPoint = = Destination
2022-01-10 15:07:05 -05:00
& & Stream . Status ! = EStreamConnectionState : : Closed )
2020-09-24 00:43:27 -04:00
{
2020-10-29 13:38:15 -04:00
SharedState . CloseStreamInternal ( Stream , _ ) ;
2020-09-24 00:43:27 -04:00
}
}
}
int32 RemovedCount = 0 ;
{
FRWScopeLock _ ( SharedState . DestinationsLock , SLT_Write ) ;
RemovedCount = SharedState . Destinations . RemoveAll ( [ & ] ( const auto & Dest ) { return Dest - > GetId ( ) = = Destination ; } ) ;
}
if ( RemovedCount )
{
SharedState . bDirtyDestinations = true ;
}
}
FRawInfo FEndpoint : : GetRawInfoCopy ( ) const
{
FRWScopeLock _ ( SharedState . RawInfoCopyLock , SLT_ReadOnly ) ;
return SharedState . RawInfo ;
}
void FEndpoint : : AddEndpointObserver ( IEndpointObserver * Observer )
{
if ( Observer )
{
FRWScopeLock _ ( SharedState . ObserversLock , SLT_Write ) ;
SharedState . Observers . AddUnique ( Observer ) ;
}
}
void FEndpoint : : RemoveEndpointObserver ( IEndpointObserver * Observer )
{
FRWScopeLock _ ( SharedState . ObserversLock , SLT_Write ) ;
SharedState . Observers . RemoveSwap ( Observer ) ;
}
FEndpoint : : EOpenStreamResult FEndpoint : : OpenStream ( const FSourceHandle & SourceId , const FDestinationHandle & DestinationId )
{
2021-03-05 19:27:14 -04:00
// #ue_directlink: should be an async api
2020-09-24 00:43:27 -04:00
// #ue_directlink_cleanup Merge with Handle_OpenStreamRequest
// #ue_directlink_syncprotocol tempo before next allowed request ?
2021-03-05 19:27:14 -04:00
2020-09-24 00:43:27 -04:00
// check if the stream is already opened
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_ReadOnly ) ;
for ( const FStreamDescription & Stream : SharedState . Streams )
{
if ( Stream . SourcePoint = = SourceId & & Stream . DestinationPoint = = DestinationId )
{
2022-01-10 15:07:05 -05:00
if ( Stream . Status = = EStreamConnectionState : : Active
| | Stream . Status = = EStreamConnectionState : : RequestSent )
2020-09-24 00:43:27 -04:00
{
// useless case, temp because of the unfinished connection policy.
// #ue_directlink_connexion Replace with proper policy (user driven connection map) + log if this happen
return EOpenStreamResult : : AlreadyOpened ;
}
}
}
}
bool bRequestFromSource = false ;
bool bRequestFromDestination = false ;
{
FRWScopeLock _ ( SharedState . SourcesLock , SLT_ReadOnly ) ;
for ( TSharedPtr < FStreamSource > & Source : SharedState . Sources )
{
if ( Source - > GetId ( ) = = SourceId )
{
bRequestFromSource = true ;
break ;
}
}
}
if ( ! bRequestFromSource )
{
// make sure we have the destination
FRWScopeLock _ ( SharedState . DestinationsLock , SLT_ReadOnly ) ;
for ( TSharedPtr < FStreamDestination > & Destination : SharedState . Destinations )
{
if ( Destination - > GetId ( ) = = DestinationId )
{
bRequestFromDestination = true ;
break ;
}
}
}
if ( ! bRequestFromSource & & ! bRequestFromDestination )
{
// we don't have any side of the connection...
UE_LOG ( LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Cannot open stream: no source or destination point found. " ) , * SharedState . NiceName ) ;
return EOpenStreamResult : : SourceAndDestinationNotFound ;
}
if ( bRequestFromSource & & bRequestFromDestination )
{
UE_LOG ( LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Cannot open stream: have source and destination. " ) , * SharedState . NiceName ) ;
return EOpenStreamResult : : Unsuppported ;
}
// Find Remote address.
FMessageAddress RemoteAddress ;
{
const FGuid & RemoteDataPointId = bRequestFromSource ? DestinationId : SourceId ;
// #ue_directlink_cleanup sad that we rely on raw info.
FRWScopeLock _ ( SharedState . RawInfoCopyLock , SLT_ReadOnly ) ;
if ( FRawInfo : : FDataPointInfo * DataPointInfo = SharedState . RawInfo . DataPointsInfo . Find ( RemoteDataPointId ) )
{
if ( DataPointInfo - > bIsPublic )
{
RemoteAddress = DataPointInfo - > EndpointAddress ;
}
else
{
UE_LOG ( LogDirectLinkNet , Warning , TEXT ( " Endpoint '%s': Cannot open stream: Remote connection Point is private. " ) , * SharedState . NiceName ) ;
return EOpenStreamResult : : CannotConnectToPrivate ;
}
}
}
if ( RemoteAddress . IsValid ( ) )
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_Write ) ;
FStreamPort StreamPort = + + SharedState . StreamPortIdGenerator ;
2021-08-10 11:26:13 -04:00
FDirectLinkMsg_OpenStreamRequest * Request = FMessageEndpoint : : MakeMessage < FDirectLinkMsg_OpenStreamRequest > ( ) ;
2020-09-24 00:43:27 -04:00
Request - > bRequestFromSource = bRequestFromSource ;
Request - > RequestFromStreamPort = StreamPort ;
Request - > SourceGuid = SourceId ;
Request - > DestinationGuid = DestinationId ;
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Send FDirectLinkMsg_OpenStreamRequest " ) , * SharedState . NiceName ) ;
SharedState . MessageEndpoint - > Send ( Request , RemoteAddress ) ;
FStreamDescription & NewStream = SharedState . Streams . AddDefaulted_GetRef ( ) ;
NewStream . bThisIsSource = bRequestFromSource ;
NewStream . SourcePoint = SourceId ;
NewStream . DestinationPoint = DestinationId ;
NewStream . LocalStreamPort = StreamPort ;
NewStream . RemoteAddress = RemoteAddress ;
2022-01-10 15:07:05 -05:00
NewStream . Status = EStreamConnectionState : : RequestSent ;
2020-09-24 00:43:27 -04:00
}
else
{
2021-02-18 18:13:28 -04:00
UE_LOG ( LogDirectLinkNet , Error , TEXT ( " Connection Request failed: no recipent found " ) ) ;
2020-09-24 00:43:27 -04:00
return EOpenStreamResult : : RemoteEndpointNotFound ;
}
return EOpenStreamResult : : Opened ;
}
void FEndpoint : : CloseStream ( const FSourceHandle & SourceId , const FDestinationHandle & DestinationId )
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_Write ) ;
for ( FStreamDescription & Stream : SharedState . Streams )
{
if ( Stream . SourcePoint = = SourceId
& & Stream . DestinationPoint = = DestinationId
2022-01-10 15:07:05 -05:00
& & Stream . Status ! = EStreamConnectionState : : Closed )
2020-09-24 00:43:27 -04:00
{
2020-10-29 13:38:15 -04:00
SharedState . CloseStreamInternal ( Stream , _ ) ;
2020-09-24 00:43:27 -04:00
}
}
}
2020-10-29 13:38:15 -04:00
FString FInternalThreadState : : ToString_dbg ( ) const
2020-09-24 00:43:27 -04:00
{
FString Out ;
{
Out . Appendf ( TEXT ( " Endpoint '%s' (%s): \n " ) , * SharedState . NiceName , * MessageEndpoint - > GetAddress ( ) . ToString ( ) ) ;
}
auto PrintEndpoint = [ & ] ( const FDirectLinkMsg_EndpointState & Endpoint , int32 RemoteEndpointIndex )
{
Out . Appendf ( TEXT ( " -- endpoint #%d %s/%d:'%s' \n " ) ,
RemoteEndpointIndex ,
* Endpoint . ComputerName ,
Endpoint . ProcessId ,
* Endpoint . NiceName
) ;
Out . Appendf ( TEXT ( " -- %d Sources: \n " ) , Endpoint . Sources . Num ( ) ) ;
int32 SrcIndex = 0 ;
for ( const FNamedId & Src : Endpoint . Sources )
{
Out . Appendf ( TEXT ( " --- Source #%d: '%s' (%08X) %s \n " ) , SrcIndex , * Src . Name , Src . Id . A ,
Src . bIsPublic ? TEXT ( " public " ) : TEXT ( " private " ) ) ;
SrcIndex + + ;
}
Out . Appendf ( TEXT ( " -- %d Destinations: \n " ) , Endpoint . Destinations . Num ( ) ) ;
int32 DestinationIndex = 0 ;
for ( const FNamedId & Dest : Endpoint . Destinations )
{
Out . Appendf ( TEXT ( " --- Dest #%d: '%s' (%08X) %s \n " ) , DestinationIndex , * Dest . Name , Dest . Id . A ,
Dest . bIsPublic ? TEXT ( " public " ) : TEXT ( " private " ) ) ;
DestinationIndex + + ;
}
} ;
Out . Appendf ( TEXT ( " - This: \n " ) ) ;
PrintEndpoint ( ThisDescription , 0 ) ;
Out . Appendf ( TEXT ( " - Remotes: \n " ) ) ;
int32 RemoteEndpointIndex = 0 ;
for ( const auto & KeyValue : RemoteEndpointDescriptions )
{
const FDirectLinkMsg_EndpointState & Remote = KeyValue . Value ;
PrintEndpoint ( Remote , RemoteEndpointIndex ) ;
RemoteEndpointIndex + + ;
}
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_ReadOnly ) ;
Out . Appendf ( TEXT ( " - %d Streams: \n " ) , SharedState . Streams . Num ( ) ) ;
for ( const FStreamDescription & Stream : SharedState . Streams )
{
FGuid LocalPoint = Stream . bThisIsSource ? Stream . SourcePoint : Stream . DestinationPoint ;
FGuid RemotePoint = Stream . bThisIsSource ? Stream . DestinationPoint : Stream . SourcePoint ;
const TCHAR * OrientationText = Stream . bThisIsSource ? TEXT ( " >>> " ) : TEXT ( " <<< " ) ;
const TCHAR * StatusText = TEXT ( " ? " ) ; //Stream.stabThisIsSource ? 'S' : 'D';
switch ( Stream . Status )
{
2022-01-10 15:07:05 -05:00
case EStreamConnectionState : : Uninitialized : StatusText = TEXT ( " Uninitialized " ) ; break ;
case EStreamConnectionState : : RequestSent : StatusText = TEXT ( " RequestSent " ) ; break ;
case EStreamConnectionState : : Active : StatusText = TEXT ( " Active " ) ; break ;
case EStreamConnectionState : : Closed : StatusText = TEXT ( " Closed " ) ; break ;
2020-09-24 00:43:27 -04:00
}
Out . Appendf ( TEXT ( " -- [%s] stream: %08X:%d %s %08X:%d \n " ) , StatusText , LocalPoint . A , Stream . LocalStreamPort , OrientationText , RemotePoint . A , Stream . RemoteStreamPort ) ;
}
}
return Out ;
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : Handle_DeltaMessage ( const FDirectLinkMsg_DeltaMessage & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context )
2020-09-24 00:43:27 -04:00
{
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_Write ) ;
// #ue_directlink_cleanup read array, lock specific stream ? TArray<TUniquePtr<>> ?
// -> decorelate streams descriptions from actualr sender receiver
FStreamDescription * StreamPtr = SharedState . GetStreamByLocalPort ( Message . DestinationStreamPort , _ ) ;
if ( StreamPtr = = nullptr )
{
UE_LOG ( LogDirectLinkNet , Warning , TEXT ( " Endpoint '%s': Dropped delta message (no stream at port %d) " ) , * SharedState . NiceName , Message . DestinationStreamPort ) ;
return ;
}
FStreamDescription & Stream = * StreamPtr ;
2022-01-10 15:07:05 -05:00
bool bIsActive = Stream . Status = = EStreamConnectionState : : Active ;
2020-09-24 00:43:27 -04:00
bool bIsReceiver = Stream . Receiver . IsValid ( ) ;
bool bIsExpectedSender = Stream . RemoteAddress = = Context - > GetSender ( ) ;
if ( ! bIsActive | | ! bIsReceiver | | ! bIsExpectedSender )
{
UE_LOG ( LogDirectLinkNet , Warning , TEXT ( " Endpoint '%s': Dropped delta message (inactive stream used on port %d) " ) , * SharedState . NiceName , Message . DestinationStreamPort ) ;
return ;
}
2020-10-22 19:19:16 -04:00
FDirectLinkMsg_DeltaMessage StolenMessage = MoveTemp ( const_cast < FDirectLinkMsg_DeltaMessage & > ( Message ) ) ;
Stream . Receiver - > HandleDeltaMessage ( StolenMessage ) ;
2020-09-24 00:43:27 -04:00
}
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : Handle_HaveListMessage ( const FDirectLinkMsg_HaveListMessage & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context )
2020-09-24 00:43:27 -04:00
{
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_Write ) ;
FStreamDescription * StreamPtr = SharedState . GetStreamByLocalPort ( Message . SourceStreamPort , _ ) ;
if ( StreamPtr = = nullptr )
{
UE_LOG ( LogDirectLinkNet , Warning , TEXT ( " Endpoint '%s': Dropped havelist message (no stream at port %d) " ) , * SharedState . NiceName , Message . SourceStreamPort ) ;
return ;
}
FStreamDescription & Stream = * StreamPtr ;
2022-01-10 15:07:05 -05:00
bool bIsActive = Stream . Status = = EStreamConnectionState : : Active ;
2020-09-24 00:43:27 -04:00
bool bIsSender = Stream . Sender . IsValid ( ) ;
bool bIsExpectedRemote = Stream . RemoteAddress = = Context - > GetSender ( ) ;
if ( ! bIsActive | | ! bIsSender | | ! bIsExpectedRemote )
{
UE_LOG ( LogDirectLinkNet , Warning , TEXT ( " Endpoint '%s': Dropped havelist message (inactive stream used on port %d) " ) , * SharedState . NiceName , Message . SourceStreamPort ) ;
return ;
}
Stream . Sender - > HandleHaveListMessage ( Message ) ;
}
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : Handle_EndpointLifecycle ( const FDirectLinkMsg_EndpointLifecycle & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context )
2020-09-24 00:43:27 -04:00
{
const FMessageAddress & RemoteEndpointAddress = Context - > GetSender ( ) ;
2020-10-22 19:19:16 -04:00
if ( IsMine ( RemoteEndpointAddress ) | | IsIgnoredEndpoint ( RemoteEndpointAddress ) )
2020-09-24 00:43:27 -04:00
{
return ;
}
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Verbose , TEXT ( " Endpoint '%s': Handle_EndpointLifecycle " ) , * SharedState . NiceName ) ;
MarkRemoteAsSeen ( RemoteEndpointAddress ) ;
switch ( Message . LifecycleState )
{
case FDirectLinkMsg_EndpointLifecycle : : ELifecycle : : Start :
{
// Noop: remote endpoint will broadcast it's state later
ReplicateState ( RemoteEndpointAddress ) ;
break ;
}
case FDirectLinkMsg_EndpointLifecycle : : ELifecycle : : Heartbeat :
{
// #ue_directlink_streams handle connection loss, threshold, and last_message_time.
// if now-last_message_time > threshold -> mark as dead
FDirectLinkMsg_EndpointState * RemoteState = RemoteEndpointDescriptions . Find ( RemoteEndpointAddress ) ;
bool bIsUpToDate = RemoteState
& & RemoteState - > StateRevision ! = 0
& & RemoteState - > StateRevision = = Message . EndpointStateRevision ;
if ( ! bIsUpToDate )
{
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Send FDirectLinkMsg_QueryEndpointState " ) , * SharedState . NiceName ) ;
2021-08-10 11:26:13 -04:00
MessageEndpoint - > Send ( FMessageEndpoint : : MakeMessage < FDirectLinkMsg_QueryEndpointState > ( ) , RemoteEndpointAddress ) ;
2020-09-24 00:43:27 -04:00
}
break ;
}
case FDirectLinkMsg_EndpointLifecycle : : ELifecycle : : Stop :
{
RemoveEndpoint ( RemoteEndpointAddress ) ;
break ;
}
}
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : Handle_QueryEndpointState ( const FDirectLinkMsg_QueryEndpointState & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context )
2020-09-24 00:43:27 -04:00
{
ReplicateState ( Context - > GetSender ( ) ) ;
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : Handle_EndpointState ( const FDirectLinkMsg_EndpointState & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context )
2020-09-24 00:43:27 -04:00
{
const FMessageAddress & RemoteEndpointAddress = Context - > GetSender ( ) ;
2020-10-22 19:19:16 -04:00
if ( IsMine ( RemoteEndpointAddress ) | | IsIgnoredEndpoint ( RemoteEndpointAddress ) )
2020-09-24 00:43:27 -04:00
{
return ;
}
2020-10-22 19:19:16 -04:00
// check protocol compatibility
2020-10-29 13:38:15 -04:00
if ( GetMinSupportedProtocolVersion ( ) > Message . ProtocolVersion
| | GetCurrentProtocolVersion ( ) < Message . MinProtocolVersion )
2020-09-24 00:43:27 -04:00
{
2020-10-22 19:19:16 -04:00
bool bAlreadyIn = false ;
IgnoredEndpoints . Add ( RemoteEndpointAddress , & bAlreadyIn ) ;
UE_CLOG ( ! bAlreadyIn , LogDirectLinkNet , Warning , TEXT ( " Endpoint '%s': Remote Endpoint %s ignored, incompatible protocol versions. Supported: [%d..%d], Remote [%d..%d] " )
, * SharedState . NiceName , * Message . NiceName
2020-10-29 13:38:15 -04:00
, GetMinSupportedProtocolVersion ( ) , GetCurrentProtocolVersion ( )
2020-10-22 19:19:16 -04:00
, Message . MinProtocolVersion , Message . ProtocolVersion
) ;
return ;
// #ue_directlink_design We could have a fancier handling than a simple 'ghosting'. UI could eg display a grayed out endpoint.
2020-09-24 00:43:27 -04:00
}
2020-10-22 19:19:16 -04:00
FDirectLinkMsg_EndpointState & RemoteState = RemoteEndpointDescriptions . FindOrAdd ( RemoteEndpointAddress ) ;
RemoteState = Message ;
MarkRemoteAsSeen ( RemoteEndpointAddress ) ;
2020-09-24 00:43:27 -04:00
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Log , TEXT ( " Endpoint '%s' Handle_EndpointState " ) , * SharedState . NiceName ) ;
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Log , TEXT ( " %s " ) , * ToString_dbg ( ) ) ;
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : Handle_OpenStreamRequest ( const FDirectLinkMsg_OpenStreamRequest & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context )
2020-09-24 00:43:27 -04:00
{
// #ue_directlink_cleanup refuse connection if local connection point is private
2020-10-22 19:19:16 -04:00
// #ue_directlink_cleanup endpoint messages should be flagged Reliable
2020-09-24 00:43:27 -04:00
const FMessageAddress & RemoteEndpointAddress = Context - > GetSender ( ) ;
2021-08-10 11:26:13 -04:00
FDirectLinkMsg_OpenStreamAnswer * Answer = FMessageEndpoint : : MakeMessage < FDirectLinkMsg_OpenStreamAnswer > ( ) ;
2020-09-24 00:43:27 -04:00
Answer - > RecipientStreamPort = Message . RequestFromStreamPort ;
2020-10-22 19:19:16 -04:00
auto DenyConnection = [ & ] ( const FString & Reason )
{
Answer - > bAccepted = false ;
Answer - > Error = TEXT ( " connection already active " ) ; // #ue_directlink_cleanup merge with OpenStream, and enum to text
UE_LOG ( LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Refused connection: %s " ) , * SharedState . NiceName , * Reason ) ;
MessageEndpoint - > Send ( Answer , RemoteEndpointAddress ) ;
} ;
if ( IsIgnoredEndpoint ( RemoteEndpointAddress ) )
{
DenyConnection ( TEXT ( " Request from incompatible endpoint " ) ) ;
return ;
}
2020-09-24 00:43:27 -04:00
// first, check if that stream is already opened
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_Write ) ;
for ( FStreamDescription & Stream : SharedState . Streams )
{
if ( Stream . SourcePoint = = Message . SourceGuid & & Stream . DestinationPoint = = Message . DestinationGuid )
{
// #ue_directlink_cleanup implement a robust handling of duplicated connections, reopened connections, etc...
2022-01-10 15:07:05 -05:00
if ( Stream . Status = = EStreamConnectionState : : Active )
2020-09-24 00:43:27 -04:00
{
2020-10-22 19:19:16 -04:00
DenyConnection ( TEXT ( " Connection already active " ) ) ;
2020-09-24 00:43:27 -04:00
return ;
}
}
}
}
2020-10-29 13:38:15 -04:00
TUniquePtr < IStreamReceiver > NewReceiver ;
TSharedPtr < IStreamSender > NewSender ;
2020-09-24 00:43:27 -04:00
if ( Message . bRequestFromSource )
{
NewReceiver = MakeReceiver ( Message . SourceGuid , Message . DestinationGuid , RemoteEndpointAddress , Message . RequestFromStreamPort ) ;
}
else
{
NewSender = MakeSender ( Message . SourceGuid , RemoteEndpointAddress , Message . RequestFromStreamPort ) ;
}
2020-10-22 19:19:16 -04:00
Answer - > bAccepted = NewSender . IsValid ( ) | | NewReceiver . IsValid ( ) ;
if ( ! Answer - > bAccepted )
{
DenyConnection ( TEXT ( " Unknown " ) ) ;
}
else
2020-09-24 00:43:27 -04:00
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_Write ) ;
FStreamPort StreamPort = + + SharedState . StreamPortIdGenerator ;
Answer - > OpenedStreamPort = StreamPort ;
FStreamDescription & NewStream = SharedState . Streams . AddDefaulted_GetRef ( ) ;
NewStream . bThisIsSource = ! Message . bRequestFromSource ;
NewStream . SourcePoint = Message . SourceGuid ;
NewStream . DestinationPoint = Message . DestinationGuid ;
NewStream . RemoteAddress = RemoteEndpointAddress ;
NewStream . RemoteStreamPort = Message . RequestFromStreamPort ;
NewStream . LocalStreamPort = StreamPort ;
NewStream . Sender = MoveTemp ( NewSender ) ;
NewStream . Receiver = MoveTemp ( NewReceiver ) ;
2022-01-10 15:07:05 -05:00
NewStream . Status = EStreamConnectionState : : Active ;
2021-03-05 19:27:14 -04:00
UE_LOG ( LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Accepted connection " ) , * SharedState . NiceName ) ;
MessageEndpoint - > Send ( Answer , RemoteEndpointAddress ) ;
2020-09-24 00:43:27 -04:00
}
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Verbose , TEXT ( " Endpoint '%s': Handle_OpenStreamRequest " ) , * SharedState . NiceName ) ;
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Verbose , TEXT ( " %s " ) , * ToString_dbg ( ) ) ;
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : Handle_OpenStreamAnswer ( const FDirectLinkMsg_OpenStreamAnswer & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context )
2020-09-24 00:43:27 -04:00
{
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Verbose , TEXT ( " Endpoint '%s': Handle_OpenStreamAnswer " ) , * SharedState . NiceName ) ;
const FMessageAddress & RemoteEndpointAddress = Context - > GetSender ( ) ;
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_Write ) ;
if ( FStreamDescription * StreamPtr = SharedState . GetStreamByLocalPort ( Message . RecipientStreamPort , _ ) )
{
FStreamDescription & Stream = * StreamPtr ;
2022-01-10 15:07:05 -05:00
if ( Stream . Status = = EStreamConnectionState : : RequestSent )
2020-09-24 00:43:27 -04:00
{
if ( Message . bAccepted )
{
Stream . RemoteStreamPort = Message . OpenedStreamPort ;
if ( Stream . bThisIsSource )
{
Stream . Sender = MakeSender ( Stream . SourcePoint , RemoteEndpointAddress , Message . OpenedStreamPort ) ;
}
else
{
Stream . Receiver = MakeReceiver ( Stream . SourcePoint , Stream . DestinationPoint , RemoteEndpointAddress , Message . OpenedStreamPort ) ;
}
check ( Stream . Receiver | | Stream . Sender )
2022-01-10 15:07:05 -05:00
Stream . Status = EStreamConnectionState : : Active ;
2020-09-24 00:43:27 -04:00
}
else
{
2022-01-10 15:07:05 -05:00
Stream . Status = EStreamConnectionState : : Closed ;
2020-09-24 00:43:27 -04:00
UE_LOG ( LogDirectLinkNet , Warning , TEXT ( " stream connection refused. %s " ) , * Message . Error ) ;
}
}
}
else
{
UE_LOG ( LogDirectLinkNet , Warning , TEXT ( " error: no such stream (%d) " ) , Message . RecipientStreamPort ) ;
}
}
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Verbose , TEXT ( " %s " ) , * ToString_dbg ( ) ) ;
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : Handle_CloseStreamRequest ( const FDirectLinkMsg_CloseStreamRequest & Message , const TSharedRef < IMessageContext , ESPMode : : ThreadSafe > & Context )
2020-09-24 00:43:27 -04:00
{
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_Write ) ; // read array, lock specific stream ?
if ( FStreamDescription * StreamPtr = SharedState . GetStreamByLocalPort ( Message . RecipientStreamPort , _ ) )
{
bool bNotifyRemote = false ; // since it's already a request from Remote...
2020-10-29 13:38:15 -04:00
SharedState . CloseStreamInternal ( * StreamPtr , _ , bNotifyRemote ) ;
2020-09-24 00:43:27 -04:00
}
}
2020-10-29 13:38:15 -04:00
UE_LOG ( LogDirectLinkNet , Verbose , TEXT ( " Endpoint '%s': Handle_CloseStreamRequest " ) , * SharedState . NiceName ) ;
2020-09-24 00:43:27 -04:00
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Verbose , TEXT ( " %s " ) , * ToString_dbg ( ) ) ;
}
2020-10-29 13:38:15 -04:00
bool FInternalThreadState : : IsMine ( const FMessageAddress & MaybeRemoteAddress ) const
2020-09-24 00:43:27 -04:00
{
return MessageEndpoint - > GetAddress ( ) = = MaybeRemoteAddress ;
}
2020-10-29 13:38:15 -04:00
bool FInternalThreadState : : IsIgnoredEndpoint ( const FMessageAddress & MaybeRemoteAddress ) const
2020-10-22 19:19:16 -04:00
{
return IgnoredEndpoints . Contains ( MaybeRemoteAddress ) ;
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : ReplicateState ( const FMessageAddress & RemoteEndpointAddress ) const
2020-09-24 00:43:27 -04:00
{
if ( MessageEndpoint . IsValid ( ) )
{
2021-08-10 11:26:13 -04:00
FDirectLinkMsg_EndpointState * EndpointStateMessage = FMessageEndpoint : : MakeMessage < FDirectLinkMsg_EndpointState > ( ) ;
2020-09-24 00:43:27 -04:00
* EndpointStateMessage = ThisDescription ;
if ( RemoteEndpointAddress . IsValid ( ) )
{
2020-10-29 13:38:15 -04:00
UE_LOG ( LogDirectLinkNet , Verbose , TEXT ( " Endpoint '%s': Send FDirectLinkMsg_EndpointState " ) , * SharedState . NiceName ) ;
2020-09-24 00:43:27 -04:00
MessageEndpoint - > Send ( EndpointStateMessage , RemoteEndpointAddress ) ;
}
else
{
2020-10-29 13:38:15 -04:00
UE_LOG ( LogDirectLinkNet , Verbose , TEXT ( " Endpoint '%s': Publish FDirectLinkMsg_EndpointState " ) , * SharedState . NiceName ) ;
2020-09-24 00:43:27 -04:00
LastBroadcastedStateRevision = EndpointStateMessage - > StateRevision ;
MessageEndpoint - > Publish ( EndpointStateMessage ) ;
}
}
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : ReplicateState_Broadcast ( ) const
2020-09-24 00:43:27 -04:00
{
FMessageAddress Invalid ;
ReplicateState ( Invalid ) ;
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : UpdateSourceDescription ( )
2020-09-24 00:43:27 -04:00
{
{
ThisDescription . Sources . Reset ( ) ;
FRWScopeLock _ ( SharedState . SourcesLock , SLT_ReadOnly ) ;
for ( const TSharedPtr < FStreamSource > & Source : SharedState . Sources )
{
ThisDescription . Sources . Add ( { Source - > GetName ( ) , Source - > GetId ( ) , Source - > IsPublic ( ) } ) ;
}
}
ThisDescription . StateRevision + + ;
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : UpdateDestinationDescription ( )
2020-09-24 00:43:27 -04:00
{
{
ThisDescription . Destinations . Reset ( ) ;
FRWScopeLock _ ( SharedState . DestinationsLock , SLT_ReadOnly ) ;
for ( const TSharedPtr < FStreamDestination > & Dest : SharedState . Destinations )
{
ThisDescription . Destinations . Add ( { Dest - > GetName ( ) , Dest - > GetId ( ) , Dest - > IsPublic ( ) } ) ;
}
}
ThisDescription . StateRevision + + ;
}
2020-10-29 13:38:15 -04:00
TUniquePtr < IStreamReceiver > FInternalThreadState : : MakeReceiver ( FGuid SourceGuid , FGuid DestinationGuid , FMessageAddress RemoteAddress , FStreamPort RemotePort )
2020-09-24 00:43:27 -04:00
{
{
FRWScopeLock _ ( SharedState . DestinationsLock , SLT_ReadOnly ) ;
for ( const TSharedPtr < FStreamDestination > & Dest : SharedState . Destinations )
{
if ( Dest - > GetId ( ) = = DestinationGuid )
{
2020-10-29 13:38:15 -04:00
const TSharedPtr < IConnectionRequestHandler > & RequestHandler = Dest - > GetProvider ( ) ;
check ( RequestHandler ) ;
2020-09-24 00:43:27 -04:00
2020-10-29 13:38:15 -04:00
IConnectionRequestHandler : : FSourceInformation SourceInfo ;
2020-09-24 00:43:27 -04:00
SourceInfo . Id = SourceGuid ;
2020-10-29 13:38:15 -04:00
if ( RequestHandler - > CanOpenNewConnection ( SourceInfo ) )
2020-09-24 00:43:27 -04:00
{
2020-10-29 13:38:15 -04:00
if ( TSharedPtr < ISceneReceiver > DeltaConsumer = RequestHandler - > GetSceneReceiver ( SourceInfo ) )
2020-09-24 00:43:27 -04:00
{
return MakeUnique < FStreamReceiver > ( MessageEndpoint , RemoteAddress , RemotePort , DeltaConsumer . ToSharedRef ( ) ) ;
}
}
2020-10-29 13:38:15 -04:00
UE_LOG ( LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Handle_OpenStreamRequest: new connection refused by provider " ) , * SharedState . NiceName ) ;
2020-09-24 00:43:27 -04:00
break ;
}
}
}
return nullptr ;
}
2020-10-29 13:38:15 -04:00
TSharedPtr < IStreamSender > FInternalThreadState : : MakeSender ( FGuid SourceGuid , FMessageAddress RemoteAddress , FStreamPort RemotePort )
2020-09-24 00:43:27 -04:00
{
{
FRWScopeLock _ ( SharedState . SourcesLock , SLT_ReadOnly ) ;
for ( const TSharedPtr < FStreamSource > & Source : SharedState . Sources )
{
if ( Source - > GetId ( ) = = SourceGuid )
{
TSharedPtr < FStreamSender > Sender = MakeShared < FStreamSender > ( MessageEndpoint , RemoteAddress , RemotePort ) ;
Source - > LinkSender ( Sender ) ;
return Sender ;
}
}
}
return nullptr ;
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : RemoveEndpoint ( const FMessageAddress & RemoteEndpointAddress )
2020-09-24 00:43:27 -04:00
{
if ( FDirectLinkMsg_EndpointState * RemoteState = RemoteEndpointDescriptions . Find ( RemoteEndpointAddress ) )
{
2021-03-05 19:27:14 -04:00
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Display , TEXT ( " Endpoint '%s' removes remote Endpoint '%s' " ) , * SharedState . NiceName , * RemoteState - > NiceName ) ;
2020-09-24 00:43:27 -04:00
}
RemoteEndpointDescriptions . Remove ( RemoteEndpointAddress ) ;
RemoteLastSeenTime . Remove ( RemoteEndpointAddress ) ;
// close remaining associated streams
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_Write ) ;
for ( auto & Stream : SharedState . Streams )
{
if ( Stream . RemoteAddress = = RemoteEndpointAddress
2022-01-10 15:07:05 -05:00
& & Stream . Status ! = EStreamConnectionState : : Closed )
2020-09-24 00:43:27 -04:00
{
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Closed connection (reason: remote endpoint removed) " ) , * SharedState . NiceName ) ;
bool bNotifyRemote = false ;
2020-10-29 13:38:15 -04:00
SharedState . CloseStreamInternal ( Stream , _ , bNotifyRemote ) ;
2020-09-24 00:43:27 -04:00
}
}
}
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : MarkRemoteAsSeen ( const FMessageAddress & RemoteEndpointAddress )
2020-09-24 00:43:27 -04:00
{
RemoteLastSeenTime . Add ( RemoteEndpointAddress , Now_s ) ;
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : CleanupTimedOutEndpoint ( )
2020-09-24 00:43:27 -04:00
{
TArray < FMessageAddress > RemovableEndpoints ;
for ( const auto & KV : RemoteEndpointDescriptions )
{
if ( double * LastSeen = RemoteLastSeenTime . Find ( KV . Key ) )
{
if ( Now_s - * LastSeen > gConfig . ThresholdEndpointCleanup_s )
{
RemovableEndpoints . Add ( KV . Key ) ;
UE_LOG ( LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Removed Endpoint %s (timeout) " ) , * SharedState . NiceName , * KV . Value . NiceName ) ;
}
}
}
for ( const FMessageAddress & RemovableEndpoint : RemovableEndpoints )
{
RemoveEndpoint ( RemovableEndpoint ) ;
}
}
2021-03-05 19:27:14 -04:00
FRawInfo : : FEndpointInfo FromMsg ( const FDirectLinkMsg_EndpointState & Msg )
2020-09-24 00:43:27 -04:00
{
2021-03-05 19:27:14 -04:00
FRawInfo : : FEndpointInfo Info ;
Info . Name = Msg . NiceName ;
2021-04-29 19:32:06 -04:00
FEngineVersion : : Parse ( Msg . UEVersion , Info . Version ) ;
2021-03-05 19:27:14 -04:00
for ( const auto & S : Msg . Sources )
{
Info . Sources . Add ( { S . Name , S . Id , S . bIsPublic } ) ;
}
for ( const auto & S : Msg . Destinations )
{
Info . Destinations . Add ( { S . Name , S . Id , S . bIsPublic } ) ;
}
Info . UserName = Msg . UserName ;
Info . ExecutableName = Msg . ExecutableName ;
Info . ComputerName = Msg . ComputerName ;
Info . bIsLocal = Msg . ComputerName = = FPlatformProcess : : ComputerName ( ) ;
Info . ProcessId = Msg . ProcessId ;
return Info ;
2020-09-24 00:43:27 -04:00
}
2021-02-18 18:13:28 -04:00
bool FInternalThreadState : : Init ( )
2020-09-24 00:43:27 -04:00
{
MessageEndpoint = FMessageEndpoint : : Builder ( TEXT ( " DirectLinkEndpoint " ) )
. Handling < FDirectLinkMsg_DeltaMessage > ( this , & FInternalThreadState : : Handle_DeltaMessage )
. Handling < FDirectLinkMsg_HaveListMessage > ( this , & FInternalThreadState : : Handle_HaveListMessage )
. Handling < FDirectLinkMsg_EndpointLifecycle > ( this , & FInternalThreadState : : Handle_EndpointLifecycle )
. Handling < FDirectLinkMsg_QueryEndpointState > ( this , & FInternalThreadState : : Handle_QueryEndpointState )
. Handling < FDirectLinkMsg_EndpointState > ( this , & FInternalThreadState : : Handle_EndpointState )
. Handling < FDirectLinkMsg_OpenStreamRequest > ( this , & FInternalThreadState : : Handle_OpenStreamRequest )
. Handling < FDirectLinkMsg_OpenStreamAnswer > ( this , & FInternalThreadState : : Handle_OpenStreamAnswer )
. Handling < FDirectLinkMsg_CloseStreamRequest > ( this , & FInternalThreadState : : Handle_CloseStreamRequest )
. WithInbox ( ) ;
2021-03-05 19:27:14 -04:00
if ( ! ensure ( MessageEndpoint . IsValid ( ) ) )
2020-09-24 00:43:27 -04:00
{
2021-02-18 18:13:28 -04:00
return false ;
2020-09-24 00:43:27 -04:00
}
2021-03-05 19:27:14 -04:00
MessageEndpoint - > Subscribe < FDirectLinkMsg_EndpointLifecycle > ( ) ;
MessageEndpoint - > Subscribe < FDirectLinkMsg_EndpointState > ( ) ;
SharedState . MessageEndpoint = MessageEndpoint ;
SharedState . bInnerThreadShouldRun = true ;
Now_s = FPlatformTime : : Seconds ( ) ;
return true ;
2020-09-24 00:43:27 -04:00
}
2020-10-29 13:38:15 -04:00
void FInternalThreadState : : Run ( )
2020-09-24 00:43:27 -04:00
{
// setup local endpoint description (aka replicated state)
2020-10-29 13:38:15 -04:00
ThisDescription = FDirectLinkMsg_EndpointState ( 1 , GetMinSupportedProtocolVersion ( ) , GetCurrentProtocolVersion ( ) ) ;
2020-09-24 00:43:27 -04:00
ThisDescription . ComputerName = FPlatformProcess : : ComputerName ( ) ;
ThisDescription . UserName = FPlatformProcess : : UserName ( ) ;
ThisDescription . ProcessId = ( int32 ) FPlatformProcess : : GetCurrentProcessId ( ) ;
ThisDescription . ExecutableName = FPlatformProcess : : ExecutableName ( ) ;
ThisDescription . NiceName = SharedState . NiceName ;
2021-03-05 19:27:14 -04:00
if ( gUdpMessagingInitializationTime > 0. )
{
double WaitTime = FMath : : Min ( FPlatformTime : : Seconds ( ) - gUdpMessagingInitializationTime , 0.5 ) ;
if ( WaitTime > 0. )
{
UE_LOG ( LogDirectLinkNet , Verbose , TEXT ( " Endpoint '%s': wait after UDP init. (In order to avoid that temporisation, Load 'UdpMessaging' module sooner in the game thread). " ) , * SharedState . NiceName ) ;
FPlatformProcess : : Sleep ( WaitTime ) ;
}
}
2020-09-24 00:43:27 -04:00
2021-03-05 19:27:14 -04:00
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Verbose , TEXT ( " Endpoint '%s': Publishing FDirectLinkMsg_EndpointLifecycle Start " ) , * SharedState . NiceName ) ;
2021-08-10 10:58:07 -04:00
MessageEndpoint - > Publish ( FMessageEndpoint : : MakeMessage < FDirectLinkMsg_EndpointLifecycle > ( FDirectLinkMsg_EndpointLifecycle : : ELifecycle : : Start ) ) ;
2020-09-24 00:43:27 -04:00
while ( SharedState . bInnerThreadShouldRun )
{
Now_s = FPlatformTime : : Seconds ( ) ;
// process local signals
if ( SharedState . bDirtySources . exchange ( false ) )
{
UpdateSourceDescription ( ) ;
}
if ( SharedState . bDirtyDestinations . exchange ( false ) )
{
UpdateDestinationDescription ( ) ;
}
if ( LastBroadcastedStateRevision ! = ThisDescription . StateRevision )
{
ReplicateState_Broadcast ( ) ;
}
if ( Now_s - LastHeartbeatTime_s > gConfig . HeartbeatThreshold_s )
{
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Verbose , TEXT ( " Endpoint '%s': Publishing FDirectLinkMsg_EndpointLifecycle Heartbeat %f " ) , * SharedState . NiceName , Now_s ) ;
2021-08-10 10:58:07 -04:00
MessageEndpoint - > Publish ( FMessageEndpoint : : MakeMessage < FDirectLinkMsg_EndpointLifecycle > ( FDirectLinkMsg_EndpointLifecycle : : ELifecycle : : Heartbeat , ThisDescription . StateRevision ) ) ;
2020-09-24 00:43:27 -04:00
LastHeartbeatTime_s = Now_s ;
}
// consume remote messages
MessageEndpoint - > ProcessInbox ( ) ;
// cleanup old endpoints
if ( gConfig . bPeriodicalyCleanupTimedOutEndpoint
& & ( Now_s - LastEndpointCleanupTime_s > gConfig . CleanupOldEndpointPeriod_s ) )
{
CleanupTimedOutEndpoint ( ) ;
LastEndpointCleanupTime_s = Now_s ;
}
// sync send
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_ReadOnly ) ;
for ( FStreamDescription & Stream : SharedState . Streams )
{
2022-01-10 15:07:05 -05:00
if ( Stream . Status = = EStreamConnectionState : : Active
2020-09-24 00:43:27 -04:00
& & Stream . bThisIsSource & & ensure ( Stream . Sender . IsValid ( ) ) )
{
Stream . Sender - > Tick ( Now_s ) ;
}
}
}
// rebuild description of remote endpoints
if ( true ) // #ue_directlink_integration flag based: user-side api driven
{
// prepare data - Endpoint part
TMap < FMessageAddress , FRawInfo : : FEndpointInfo > EndpointsInfo ;
EndpointsInfo . Reserve ( RemoteEndpointDescriptions . Num ( ) ) ;
for ( const auto & KV : RemoteEndpointDescriptions )
{
2021-03-05 19:27:14 -04:00
EndpointsInfo . Add ( KV . Key , FromMsg ( KV . Value ) ) ;
2020-09-24 00:43:27 -04:00
}
FMessageAddress ThisEndpointAddress = MessageEndpoint - > GetAddress ( ) ;
2021-03-05 19:27:14 -04:00
EndpointsInfo . Add ( ThisEndpointAddress , FromMsg ( ThisDescription ) ) ;
2020-09-24 00:43:27 -04:00
// prepare data - sources and destinations
TMap < FGuid , FRawInfo : : FDataPointInfo > DataPointsInfo ;
auto l = [ & ] ( const FDirectLinkMsg_EndpointState & EpDescription , const FMessageAddress & EpAddress , bool bIsLocal )
{
for ( const auto & Src : EpDescription . Sources )
{
DataPointsInfo . Add ( Src . Id , FRawInfo : : FDataPointInfo { EpAddress , Src . Name , true , bIsLocal , Src . bIsPublic } ) ;
}
for ( const auto & Dst : EpDescription . Destinations )
{
DataPointsInfo . Add ( Dst . Id , FRawInfo : : FDataPointInfo { EpAddress , Dst . Name , false , bIsLocal , Dst . bIsPublic } ) ;
}
} ;
l ( ThisDescription , ThisEndpointAddress , true ) ;
for ( const auto & KV : RemoteEndpointDescriptions )
{
l ( KV . Value , KV . Key , false ) ;
}
// prepare data - Streams part
TArray < FRawInfo : : FStreamInfo > StreamsInfo ;
{
FRWScopeLock _ ( SharedState . StreamsLock , SLT_ReadOnly ) ;
StreamsInfo . Reserve ( SharedState . Streams . Num ( ) ) ;
for ( FStreamDescription & Stream : SharedState . Streams )
{
FRawInfo : : FStreamInfo StreamInfo ;
StreamInfo . StreamId = Stream . LocalStreamPort ;
StreamInfo . Source = Stream . SourcePoint ;
StreamInfo . Destination = Stream . DestinationPoint ;
2022-01-10 15:07:05 -05:00
StreamInfo . ConnectionState = Stream . Status ;
if ( Stream . Status = = EStreamConnectionState : : Active )
2020-09-24 00:43:27 -04:00
{
if ( Stream . Sender )
{
StreamInfo . CommunicationStatus = Stream . Sender - > GetCommunicationStatus ( ) ;
}
else if ( ensure ( Stream . Receiver ) )
{
StreamInfo . CommunicationStatus = Stream . Receiver - > GetCommunicationStatus ( ) ;
}
}
StreamsInfo . Add ( StreamInfo ) ;
}
}
{
// update info for local observers
FRWScopeLock _ ( SharedState . RawInfoCopyLock , SLT_Write ) ;
SharedState . RawInfo . ThisEndpointAddress = ThisEndpointAddress ;
SharedState . RawInfo . EndpointsInfo = MoveTemp ( EndpointsInfo ) ;
SharedState . RawInfo . DataPointsInfo = MoveTemp ( DataPointsInfo ) ;
SharedState . RawInfo . StreamsInfo = MoveTemp ( StreamsInfo ) ;
}
{
// Notify observers
FRawInfo RawInfo = Owner . GetRawInfoCopy ( ) ; // stupid copy, but avoids locking 2 mutexes at once
FRWScopeLock _ ( SharedState . ObserversLock , SLT_ReadOnly ) ;
for ( IEndpointObserver * Observer : SharedState . Observers )
{
Observer - > OnStateChanged ( RawInfo ) ;
}
}
}
// #ue_directlink_connexion temp autoconnect policy.
// for all local source, connect to all remote dest with the same name
// reimpl with named broadcast source, and client connect themselves
if ( gConfig . bAutoconnectFromSources | | gConfig . bAutoconnectFromDestination )
{
TArray < FNamedId > AllSources = gConfig . bAutoconnectFromSources ? ThisDescription . Sources : TArray < FNamedId > { } ;
TArray < FNamedId > AllDestinations = gConfig . bAutoconnectFromDestination ? ThisDescription . Destinations : TArray < FNamedId > { } ;
for ( const auto & KV : RemoteEndpointDescriptions )
{
if ( gConfig . bAutoconnectFromSources )
{
for ( const auto & Dst : KV . Value . Destinations )
{
if ( Dst . bIsPublic ) AllDestinations . Add ( Dst ) ;
}
}
if ( gConfig . bAutoconnectFromDestination )
{
for ( const auto & Src : KV . Value . Sources )
{
if ( Src . bIsPublic ) AllSources . Add ( Src ) ;
}
}
}
for ( const auto & Src : AllSources )
{
for ( const auto & Dst : AllDestinations )
{
if ( Src . Name = = Dst . Name )
{
Owner . OpenStream ( Src . Id , Dst . Id ) ;
}
}
}
}
if ( MessageEndpoint - > IsInboxEmpty ( ) )
{
2020-10-29 13:38:15 -04:00
InnerThreadEvent - > Wait ( FTimespan : : FromMilliseconds ( 50 ) ) ;
2020-09-24 00:43:27 -04:00
}
}
2022-02-02 10:02:46 -05:00
UE_CLOG ( SharedState . bDebugLog , LogDirectLinkNet , Verbose , TEXT ( " Endpoint '%s': Publishing FDirectLinkMsg_EndpointLifecycle Stop " ) , * SharedState . NiceName ) ;
2021-08-10 10:58:07 -04:00
MessageEndpoint - > Publish ( FMessageEndpoint : : MakeMessage < FDirectLinkMsg_EndpointLifecycle > ( FDirectLinkMsg_EndpointLifecycle : : ELifecycle : : Stop ) ) ;
2020-09-24 00:43:27 -04:00
FMessageEndpoint : : SafeRelease ( MessageEndpoint ) ;
}
2020-10-29 13:38:15 -04:00
FStreamDescription * FSharedState : : GetStreamByLocalPort ( FStreamPort LocalPort , const FRWScopeLock & _ )
2020-09-24 00:43:27 -04:00
{
// try to skip a lookup
if ( Streams . IsValidIndex ( LocalPort - 1 )
& & ensure ( Streams [ LocalPort - 1 ] . LocalStreamPort = = LocalPort ) )
{
return & Streams [ LocalPort - 1 ] ;
}
for ( FStreamDescription & Stream : Streams )
{
if ( Stream . LocalStreamPort = = LocalPort )
{
return & Stream ;
}
}
return nullptr ;
}
2020-10-29 13:38:15 -04:00
void FSharedState : : CloseStreamInternal ( FStreamDescription & Stream , const FRWScopeLock & _ , bool bNotifyRemote )
{
2022-01-10 15:07:05 -05:00
if ( Stream . Status = = EStreamConnectionState : : Closed )
2020-10-29 13:38:15 -04:00
{
return ;
}
if ( bNotifyRemote & & Stream . RemoteAddress . IsValid ( ) )
{
UE_CLOG ( bDebugLog , LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Stream removed " ) , * NiceName , * Stream . SourcePoint . ToString ( ) ) ;
2021-08-10 11:26:13 -04:00
FDirectLinkMsg_CloseStreamRequest * Request = FMessageEndpoint : : MakeMessage < FDirectLinkMsg_CloseStreamRequest > ( ) ;
2020-10-29 13:38:15 -04:00
Request - > RecipientStreamPort = Stream . RemoteStreamPort ;
UE_CLOG ( bDebugLog , LogDirectLinkNet , Log , TEXT ( " Endpoint '%s': Send FDirectLinkMsg_CloseStreamRequest " ) , * NiceName ) ;
MessageEndpoint - > Send ( Request , Stream . RemoteAddress ) ;
}
// close local stream
2022-01-10 15:07:05 -05:00
Stream . Status = EStreamConnectionState : : Closed ;
2020-10-29 13:38:15 -04:00
Stream . Sender . Reset ( ) ;
Stream . Receiver . Reset ( ) ; // #ue_directlink_cleanup notify associated scene provider
}
2020-09-24 00:43:27 -04:00
} // namespace DirectLink