@ -12,6 +12,7 @@
# include <Include/IMultiplayer.h>
# include <Include/IMultiplayerTools.h>
# include <Include/MultiplayerConstants.h>
# include <Source/AutoGen/Multiplayer.AutoPackets.h>
# include <Source/MultiplayerSystemComponent.h>
# include <Source/Editor/MultiplayerEditorSystemComponent.h>
@ -26,16 +27,16 @@
namespace Multiplayer
{
static const AZStd : : string_view s_networkEditorInterfaceName ( " MultiplayerEditorNetworkInterface " ) ;
using namespace AzNetworking ;
AZ_CVAR ( bool , editorsv_enabled , true , nullptr , AZ : : ConsoleFunctorFlags : : DontReplicate ,
" Whether Editor launching a local server to connect to is supported " ) ;
AZ_CVAR ( bool , editorsv_launch , false , nullptr , AZ : : ConsoleFunctorFlags : : DontReplicate ,
" Whether Editor should launch a server when the server address is localhost " ) ;
AZ_CVAR ( AZ : : CVarFixedString , editorsv_process , " " , nullptr , AZ : : ConsoleFunctorFlags : : DontReplicate ,
" The server executable that should be run. Empty to use the current project's ServerLauncher " ) ;
AZ_CVAR ( AZ : : CVarFixedString , editorsv_serveraddr , " 127.0.0.1 " , nullptr , AZ : : ConsoleFunctorFlags : : DontReplicate , " The address of the server to connect to " ) ;
AZ_CVAR ( uint16_t , editorsv_port , 30091 , nullptr , AZ : : ConsoleFunctorFlags : : DontReplicate , " The port that the multiplayer editor gem will bind to for traffic " ) ;
AZ_CVAR ( AZ : : CVarFixedString , editorsv_serveraddr , LocalHost . data ( ) , nullptr , AZ : : ConsoleFunctorFlags : : DontReplicate , " The address of the server to connect to " ) ;
AZ_CVAR ( uint16_t , editorsv_port , DefaultServerEditorPort , nullptr , AZ : : ConsoleFunctorFlags : : DontReplicate , " The port that the multiplayer editor gem will bind to for traffic " ) ;
void MultiplayerEditorSystemComponent : : Reflect ( AZ : : ReflectContext * context )
{
@ -105,7 +106,7 @@ namespace Multiplayer
m_serverProcess - > TerminateProcess ( 0 ) ;
m_serverProcess = nullptr ;
}
INetworkInterface * editorNetworkInterface = AZ : : Interface < INetworking > : : Get ( ) - > RetrieveNetworkInterface ( AZ : : Name ( s_network EditorInterfaceName) ) ;
INetworkInterface * editorNetworkInterface = AZ : : Interface < INetworking > : : Get ( ) - > RetrieveNetworkInterface ( AZ : : Name ( MP EditorInterfaceName) ) ;
if ( editorNetworkInterface )
{
editorNetworkInterface - > Disconnect ( m_editorConnId , AzNetworking : : DisconnectReason : : TerminatedByClient ) ;
@ -114,32 +115,8 @@ namespace Multiplayer
}
}
void MultiplayerEditorSystemComponent : : OnGameEntitiesStarted ( )
{
auto prefabEditorEntityOwnershipInterface = AZ : : Interface < AzToolsFramework : : PrefabEditorEntityOwnershipInterface > : : Get ( ) ;
if ( ! prefabEditorEntityOwnershipInterface )
{
AZ_Error ( " MultiplayerEditor " , prefabEditorEntityOwnershipInterface ! = nullptr , " PrefabEditorEntityOwnershipInterface unavailable " ) ;
}
// BeginGameMode and Prefab Processing have completed at this point
IMultiplayerTools * mpTools = AZ : : Interface < IMultiplayerTools > : : Get ( ) ;
if ( editorsv_enabled & & mpTools ! = nullptr & & mpTools - > DidProcessNetworkPrefabs ( ) )
void LaunchEditorServer ( AzFramework : : ProcessWatcher * outProcess )
{
const AZStd : : vector < AZ : : Data : : Asset < AZ : : Data : : AssetData > > & assetData = prefabEditorEntityOwnershipInterface - > GetPlayInEditorAssetData ( ) ;
AZStd : : vector < uint8_t > buffer ;
AZ : : IO : : ByteContainerStream byteStream ( & buffer ) ;
// Serialize Asset information and AssetData into a potentially large buffer
for ( auto asset : assetData )
{
AZ : : Data : : AssetLoadBehavior assetLoadBehavior = asset . GetAutoLoadBehavior ( ) ;
byteStream . Write ( sizeof ( AZ : : Data : : AssetLoadBehavior ) , reinterpret_cast < void * > ( & assetLoadBehavior ) ) ;
AZ : : Utils : : SaveObjectToStream ( byteStream , AZ : : DataStream : : ST_BINARY , asset . GetData ( ) , asset . GetData ( ) - > GetType ( ) ) ;
}
// Assemble the server's path
AZ : : CVarFixedString serverProcess = editorsv_process ;
if ( serverProcess . empty ( ) )
@ -173,16 +150,63 @@ namespace Multiplayer
processLaunchInfo . m_processPriority = AzFramework : : ProcessPriority : : PROCESSPRIORITY_NORMAL ;
// Launch the Server and give it a few seconds to boot up
m_server Process = AzFramework : : ProcessWatcher : : LaunchProcess (
out Process = AzFramework : : ProcessWatcher : : LaunchProcess (
processLaunchInfo , AzFramework : : ProcessCommunicationType : : COMMUNICATOR_TYPE_NONE ) ;
AZStd : : this_thread : : sleep_for ( AZStd : : chrono : : milliseconds ( 15000 ) ) ;
}
void MultiplayerEditorSystemComponent : : OnGameEntitiesStarted ( )
{
auto prefabEditorEntityOwnershipInterface = AZ : : Interface < AzToolsFramework : : PrefabEditorEntityOwnershipInterface > : : Get ( ) ;
if ( ! prefabEditorEntityOwnershipInterface )
{
AZ_Error ( " MultiplayerEditor " , prefabEditorEntityOwnershipInterface ! = nullptr , " PrefabEditorEntityOwnershipInterface unavailable " ) ;
}
// BeginGameMode and Prefab Processing have completed at this point
IMultiplayerTools * mpTools = AZ : : Interface < IMultiplayerTools > : : Get ( ) ;
if ( editorsv_enabled & & mpTools ! = nullptr & & mpTools - > DidProcessNetworkPrefabs ( ) )
{
const AZStd : : vector < AZ : : Data : : Asset < AZ : : Data : : AssetData > > & assetData = prefabEditorEntityOwnershipInterface - > GetPlayInEditorAssetData ( ) ;
AZStd : : vector < uint8_t > buffer ;
AZ : : IO : : ByteContainerStream byteStream ( & buffer ) ;
// Serialize Asset information and AssetData into a potentially large buffer
for ( auto asset : assetData )
{
AZ : : Data : : AssetId assetId = asset . GetId ( ) ;
AZ : : Data : : AssetLoadBehavior assetLoadBehavior = asset . GetAutoLoadBehavior ( ) ;
AZStd : : string assetHint = asset . GetHint ( ) ;
uint32_t hintSize = aznumeric_cast < uint32_t > ( assetHint . size ( ) ) ;
byteStream . Write ( sizeof ( AZ : : Data : : AssetId ) , reinterpret_cast < void * > ( & assetId ) ) ;
byteStream . Write ( sizeof ( AZ : : Data : : AssetLoadBehavior ) , reinterpret_cast < void * > ( & assetLoadBehavior ) ) ;
byteStream . Write ( sizeof ( uint32_t ) , reinterpret_cast < void * > ( & hintSize ) ) ;
byteStream . Write ( assetHint . size ( ) , assetHint . data ( ) ) ;
AZ : : Utils : : SaveObjectToStream ( byteStream , AZ : : DataStream : : ST_BINARY , asset . GetData ( ) , asset . GetData ( ) - > GetType ( ) ) ;
}
// Now that the server has launched, attempt to connect the NetworkInterface
const AZ : : CVarFixedString remoteAddress = editorsv_serveraddr ;
INetworkInterface * editorNetworkInterface = AZ : : Interface < INetworking > : : Get ( ) - > RetrieveNetworkInterface ( AZ : : Name ( s_networkEditorInterfaceName ) ) ;
if ( editorsv_launch & & LocalHost . compare ( remoteAddress . c_str ( ) ) = = 0 )
{
LaunchEditorServer ( m_serverProcess ) ;
}
// Now that the server has launched, attempt to connect the NetworkInterface
INetworkInterface * editorNetworkInterface = AZ : : Interface < INetworking > : : Get ( ) - > RetrieveNetworkInterface ( AZ : : Name ( MPEditorInterfaceName ) ) ;
AZ_Assert ( editorNetworkInterface , " MP Editor Network Interface was unregistered before Editor could connect. " ) ;
m_editorConnId = editorNetworkInterface - > Connect (
AzNetworking : : IpAddress ( remoteAddress . c_str ( ) , editorsv_port , AzNetworking : : ProtocolType : : Tcp ) ) ;
if ( m_editorConnId = = AzNetworking : : InvalidConnectionId )
{
AZ_Warning (
" MultiplayerEditor " , false ,
" Could not connect to server targeted by Editor. If using a local server, check that it's built and editorsv_launch is true. " ) ;
return ;
}
// Read the buffer into EditorServerInit packets until we've flushed the whole thing
byteStream . Seek ( 0 , AZ : : IO : : GenericStream : : SeekMode : : ST_SEEK_BEGIN ) ;