/**
	 * Initializes the Slate Remote server with the current settings.
	 */
	void InitializeRemoteServer( )
	{
		ShutdownRemoteServer();

		USlateRemoteSettings* Settings = GetMutableDefault<USlateRemoteSettings>();

		// load settings
		bool ResaveSettings = false;

		FIPv4Endpoint ServerEndpoint;

		if (GIsEditor)
		{
			if (!FIPv4Endpoint::Parse(Settings->EditorServerEndpoint, ServerEndpoint))
			{
				if (!Settings->EditorServerEndpoint.IsEmpty())
				{
					GLog->Logf(TEXT("Warning: Invalid Slate Remote EditorServerEndpoint '%s' - binding to all local network adapters instead"), *Settings->EditorServerEndpoint);
				}

				ServerEndpoint = SLATE_REMOTE_SERVER_DEFAULT_EDITOR_ENDPOINT;
				Settings->EditorServerEndpoint = ServerEndpoint.ToText().ToString();
				ResaveSettings = true;
			}
		}
		else
		{
			if (!FIPv4Endpoint::Parse(Settings->GameServerEndpoint, ServerEndpoint))
			{
				if (!Settings->GameServerEndpoint.IsEmpty())
				{
					GLog->Logf(TEXT("Warning: Invalid Slate Remote GameServerEndpoint '%s' - binding to all local network adapters instead"), *Settings->GameServerEndpoint);
				}

				ServerEndpoint = SLATE_REMOTE_SERVER_DEFAULT_GAME_ENDPOINT;
				Settings->GameServerEndpoint = ServerEndpoint.ToText().ToString();
				ResaveSettings = true;
			}
		}

		if (ResaveSettings)
		{
			Settings->SaveConfig();
		}

		// create server
		ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);

		if (SocketSubsystem != nullptr)
		{
			RemoteServer = MakeShareable(new FSlateRemoteServer(*SocketSubsystem, ServerEndpoint));
		}
		else
		{
			GLog->Logf(TEXT("Error: SlateRemote: Failed to acquire socket subsystem."));
		}
	}
	/** Initializes the message bridge with the current settings. */
	void InitializeBridge()
	{
		ShutdownBridge();

		UUdpMessagingSettings* Settings = GetMutableDefault<UUdpMessagingSettings>();
		bool ResaveSettings = false;

		FIPv4Endpoint UnicastEndpoint;
		FIPv4Endpoint MulticastEndpoint;

		if (!FIPv4Endpoint::Parse(Settings->UnicastEndpoint, UnicastEndpoint))
		{
			if (!Settings->UnicastEndpoint.IsEmpty())
			{
				GLog->Logf(TEXT("Warning: Invalid UDP Messaging UnicastEndpoint '%s' - binding to all local network adapters instead"), *Settings->UnicastEndpoint);
			}

			UnicastEndpoint = FIPv4Endpoint::Any;
			Settings->UnicastEndpoint = UnicastEndpoint.ToText().ToString();
			ResaveSettings = true;
		}

		if (!FIPv4Endpoint::Parse(Settings->MulticastEndpoint, MulticastEndpoint))
		{
			if (!Settings->MulticastEndpoint.IsEmpty())
			{
				GLog->Logf(TEXT("Warning: Invalid UDP Messaging MulticastEndpoint '%s' - using default endpoint '%s' instead"), *Settings->MulticastEndpoint, *UDP_MESSAGING_DEFAULT_MULTICAST_ENDPOINT.ToText().ToString());
			}

			MulticastEndpoint = UDP_MESSAGING_DEFAULT_MULTICAST_ENDPOINT;
			Settings->MulticastEndpoint = MulticastEndpoint.ToText().ToString();
			ResaveSettings = true;
		}

		if (Settings->MulticastTimeToLive == 0)
		{
			Settings->MulticastTimeToLive = 1;
			ResaveSettings = true;		
		}

		if (ResaveSettings)
		{
			Settings->SaveConfig();
		}

		GLog->Logf(TEXT("UdpMessaging: Initializing bridge on interface %s to multicast group %s."), *UnicastEndpoint.ToText().ToString(), *MulticastEndpoint.ToText().ToString());

		MessageBridge = FMessageBridgeBuilder()
			.UsingTransport(MakeShareable(new FUdpMessageTransport(UnicastEndpoint, MulticastEndpoint, Settings->MulticastTimeToLive)));
	}
bool FSlateRemoteServer::StartServer( const FIPv4Endpoint& ServerEndpoint )
{
	ServerSocket = FUdpSocketBuilder(TEXT("SlateRemoteServerSocket"))
		.AsNonBlocking()
		.AsReusable()
		.BoundToEndpoint(ServerEndpoint);

	if (ServerSocket == nullptr)
	{
		GLog->Logf(TEXT("SlateRemoteServer: Failed to create server socket on %s"), *ServerEndpoint.ToText().ToString());

		return false;
	}

	TickDelegate = FTickerDelegate::CreateRaw(this, &FSlateRemoteServer::HandleTicker);
	TickDelegateHandle = FTicker::GetCoreTicker().AddTicker(TickDelegate, 0.0f);

	return true;
}
	/** Initializes the message tunnel with the current settings. */
	void InitializeTunnel()
	{
		ShutdownTunnel();

		UUdpMessagingSettings* Settings = GetMutableDefault<UUdpMessagingSettings>();
		bool ResaveSettings = false;

		FIPv4Endpoint UnicastEndpoint;
		FIPv4Endpoint MulticastEndpoint;

		if (!FIPv4Endpoint::Parse(Settings->TunnelUnicastEndpoint, UnicastEndpoint))
		{
			GLog->Logf(TEXT("Warning: Invalid UDP Tunneling UnicastEndpoint '%s' - binding to all local network adapters instead"), *Settings->UnicastEndpoint);
			UnicastEndpoint = FIPv4Endpoint::Any;
			Settings->UnicastEndpoint = UnicastEndpoint.ToString();
			ResaveSettings = true;
		}

		if (!FIPv4Endpoint::Parse(Settings->TunnelMulticastEndpoint, MulticastEndpoint))
		{
			GLog->Logf(TEXT("Warning: Invalid UDP Tunneling MulticastEndpoint '%s' - using default endpoint '%s' instead"), *Settings->MulticastEndpoint, *UDP_MESSAGING_DEFAULT_MULTICAST_ENDPOINT.ToText().ToString());
			MulticastEndpoint = UDP_MESSAGING_DEFAULT_MULTICAST_ENDPOINT;
			Settings->MulticastEndpoint = MulticastEndpoint.ToString();
			ResaveSettings = true;
		}

		if (ResaveSettings)
		{
			Settings->SaveConfig();
		}

		GLog->Logf(TEXT("UdpMessaging: Initializing tunnel on interface %s to multicast group %s."), *UnicastEndpoint.ToText().ToString(), *MulticastEndpoint.ToText().ToString());

		MessageTunnel = MakeShareable(new FUdpMessageTunnel(UnicastEndpoint, MulticastEndpoint));

		// initiate connections
		for (int32 EndpointIndex = 0; EndpointIndex < Settings->RemoteTunnelEndpoints.Num(); ++EndpointIndex)
		{
			FIPv4Endpoint RemoteEndpoint;

			if (FIPv4Endpoint::Parse(Settings->RemoteTunnelEndpoints[EndpointIndex], RemoteEndpoint))
			{
				MessageTunnel->Connect(RemoteEndpoint);
			}
			else
			{
				GLog->Logf(TEXT("Warning: Invalid UDP RemoteTunnelEndpoint '%s' - skipping"), *Settings->RemoteTunnelEndpoints[EndpointIndex]);
			}
		}
	}