void UPendingNetGame::InitNetDriver()
{
	if (!GDisallowNetworkTravel)
	{
		NETWORK_PROFILER(GNetworkProfiler.TrackSessionChange(true, URL));

		// Try to create network driver.
		if (GEngine->CreateNamedNetDriver(this, NAME_PendingNetDriver, NAME_GameNetDriver))
		{
			NetDriver = GEngine->FindNamedNetDriver(this, NAME_PendingNetDriver);
		}
		check(NetDriver);

		if( NetDriver->InitConnect( this, URL, ConnectionError ) )
		{
			UNetConnection* ServerConn = NetDriver->ServerConnection;

			// Kick off the connection handshake
			if (ServerConn->StatelessConnectComponent.IsValid())
			{
				ServerConn->StatelessConnectComponent.Pin()->SendInitialConnect();
			}


			// Send initial message.
			uint8 IsLittleEndian = uint8(PLATFORM_LITTLE_ENDIAN);
			check(IsLittleEndian == !!IsLittleEndian); // should only be one or zero
			
			uint32 LocalNetworkVersion = FNetworkVersion::GetLocalNetworkVersion();

			FNetControlMessage<NMT_Hello>::Send(ServerConn, IsLittleEndian, LocalNetworkVersion);

			ServerConn->FlushNet();
		}
		else
		{
			// error initializing the network stack...
			UE_LOG(LogNet, Warning, TEXT("error initializing the network stack"));
			GEngine->DestroyNamedNetDriver(this, NetDriver->NetDriverName);
			NetDriver = NULL;

			// ConnectionError should be set by calling InitConnect...however, if we set NetDriver to NULL without setting a
			// value for ConnectionError, we'll trigger the assertion at the top of UPendingNetGame::Tick() so make sure it's set
			if ( ConnectionError.Len() == 0 )
			{
				ConnectionError = NSLOCTEXT("Engine", "NetworkInit", "Error initializing network layer.").ToString();
			}
		}
	}
	else
	{
		ConnectionError = NSLOCTEXT("Engine", "UsedCheatCommands", "Console commands were used which are disallowed in netplay.  You must restart the game to create a match.").ToString();
	}
}
void AOnlineBeaconHost::DisconnectClient(AOnlineBeaconClient* ClientActor)
{
	if (ClientActor && ClientActor->GetConnectionState() != EBeaconConnectionState::Closed && !ClientActor->IsPendingKill())
	{
		ClientActor->SetConnectionState(EBeaconConnectionState::Closed);

		UNetConnection* Connection = ClientActor->GetNetConnection();

		UE_LOG(LogBeacon, Log, TEXT("DisconnectClient for %s. UNetConnection %s UNetDriver %s State %d"), 
			*GetNameSafe(ClientActor), 
			*GetNameSafe(Connection), 
			Connection ? *GetNameSafe(Connection->Driver) : TEXT("null"),
			Connection ? Connection->State : -1);

		// Closing the connection will start the chain of events leading to the removal from lists and destruction of the actor
		if (Connection && Connection->State != USOCK_Closed)
		{
			Connection->FlushNet(true);
			Connection->Close();
		}
	}
}
예제 #3
0
void ANUTActor::NetFlush()
{
	UNetDriver* CurNetDriver = GetNetDriver();

	if (CurNetDriver != NULL)
	{
		UNetConnection* ServerConn = CurNetDriver->ServerConnection;

		if (ServerConn != NULL)
		{
			UE_LOG(LogUnitTest, Log, TEXT("Flushing ServerConnection"));
			ServerConn->FlushNet();
		}
		else
		{
			UE_LOG(LogUnitTest, Log, TEXT("Flushing ClientConnections"));

			for (auto CurConn : CurNetDriver->ClientConnections)
			{
				CurConn->FlushNet();
			}
		}
	}
}
예제 #4
0
void UIpNetDriver::ProcessRemoteFunction(class AActor* Actor, UFunction* Function, void* Parameters, FFrame* Stack, class UObject * SubObject )
{
	bool bIsServer = IsServer();

	UNetConnection* Connection = NULL;
	if (bIsServer)
	{
		if ((Function->FunctionFlags & FUNC_NetMulticast))
		{
			// Multicast functions go to every client
			TArray<UNetConnection*> UniqueRealConnections;
			for (int32 i=0; i<ClientConnections.Num(); ++i)
			{
				Connection = ClientConnections[i];
				if (Connection)
				{
					// Do relevancy check if unreliable.
					// Reliables will always go out. This is odd behavior. On one hand we wish to garuntee "reliables always get there". On the other
					// hand, replicating a reliable to something on the other side of the map that is non relevant seems weird. 
					//
					// Multicast reliables should probably never be used in gameplay code for actors that have relevancy checks. If they are, the 
					// rpc will go through and the channel will be closed soon after due to relevancy failing.

					bool IsRelevant = true;
					if ((Function->FunctionFlags & FUNC_NetReliable) == 0)
					{
						if (Connection->Viewer)
						{
							FNetViewer Viewer(Connection, 0.f);
							IsRelevant = Actor->IsNetRelevantFor(Viewer.InViewer, Viewer.Viewer, Viewer.ViewLocation);
						}
						else
						{
							// No viewer for this connection(?), just let it go through.
							UE_LOG(LogNet, Log, TEXT("Multicast function called on connection with no Viewer"));
						}
					}
					
					if (IsRelevant)
					{
						if (Connection->GetUChildConnection() != NULL)
						{
							Connection = ((UChildConnection*)Connection)->Parent;
						}
						
						InternalProcessRemoteFunction( Actor, SubObject, Connection, Function, Parameters, Stack, bIsServer );
					}
				}
			}			

			// Return here so we don't call InternalProcessRemoteFunction again at the bottom of this function
			return;
		}
	}

	// Send function data to remote.
	Connection = Actor->GetNetConnection();
	if (Connection)
	{
		InternalProcessRemoteFunction( Actor, SubObject, Connection, Function, Parameters, Stack, bIsServer );
	}
}
예제 #5
0
bool NUTNet::CreateFakePlayer(UWorld* InWorld, UNetDriver*& InNetDriver, FString ServerIP, FNetworkNotify* InNotify/*=NULL*/,
								bool bSkipJoin/*=false*/, FUniqueNetIdRepl* InNetID/*=NULL*/, bool bBeaconConnect/*=false*/,
								FString InBeaconType/*=TEXT("")*/)
{
	bool bSuccess = false;

	if (InNetDriver == NULL)
	{
		InNetDriver = (InWorld != NULL ? NUTNet::CreateUnitTestNetDriver(InWorld) : NULL);
	}

	if (InNetDriver != NULL)
	{
		if (InNotify == NULL)
		{
			FNetworkNotifyHook* NotifyHook = new FNetworkNotifyHook();
			InNotify = NotifyHook;

			auto DefaultRejectChan = [](UChannel* Channel)
				{
					UE_LOG(LogUnitTest, Log, TEXT("UnitTestNetDriver: NotifyAcceptingChannel: %s"), *Channel->Describe());

					return false;
				};

			NotifyHook->NotifyAcceptingChannelDelegate.BindLambda(DefaultRejectChan);
		}

		FURL DefaultURL;
		FURL TravelURL(&DefaultURL, *ServerIP, TRAVEL_Absolute);
		FString ConnectionError;

		if (InNetDriver->InitConnect(InNotify, TravelURL, ConnectionError))
		{
			UNetConnection* TargetConn = InNetDriver->ServerConnection;

			UE_LOG(LogUnitTest, Log, TEXT("Successfully kicked off connect to IP '%s'"), *ServerIP);


			int ControlBunchSequence = 0;

			FOutBunch* ControlChanBunch = NUTNet::CreateChannelBunch(ControlBunchSequence, TargetConn, CHTYPE_Control, 0);

			// Need to send 'NMT_Hello' to start off the connection (the challenge is not replied to)
			uint8 IsLittleEndian = uint8(PLATFORM_LITTLE_ENDIAN);

			// We need to construct the NMT_Hello packet manually, for the initial connection
			uint8 MessageType = NMT_Hello;

			*ControlChanBunch << MessageType;
			*ControlChanBunch << IsLittleEndian;


#if TARGET_UE4_CL >= CL_FNETWORKVERSION
			// Starting with 4.8.0, the network protocol has changed slightly
			// @todo JohnB: Refactor this, to toggle at compile time only, based on CL (might require more accurate UT integrate CLs)
			FString VersionStr = GEngineVersion.ToString(EVersionComponent::Minor);
			int32 VersionDelim = VersionStr.Find(TEXT("."));
			int32 MajorVersion = FCString::Atoi(*VersionStr.Left(VersionDelim));
			int32 MinorVersion = FCString::Atoi(*VersionStr.Mid(VersionDelim+1));


			bool bOldProtocol = (MajorVersion <= 4 && MinorVersion <= 7) &&
				/** Exception for UT (treat 4.7 as having the new protocol) */
				(FString(FApp::GetGameName()) != TEXT("UnrealTournament") || (MajorVersion <= 4 && MinorVersion <= 6));

			if (bOldProtocol)
#endif
			{
				int32 EngineMinNetVersion = GEngineMinNetVersion;
				*ControlChanBunch << EngineMinNetVersion;
				int32 EngineNetVersion = GEngineNetVersion;
				*ControlChanBunch << EngineNetVersion;
				*ControlChanBunch << (FGuid&)GetDefault<UGeneralProjectSettings>()->ProjectID;
			}
#if TARGET_UE4_CL >= CL_FNETWORKVERSION
			else
			{
				uint32 LocalNetworkVersion = FNetworkVersion::GetLocalNetworkVersion();
				*ControlChanBunch << LocalNetworkVersion;
			}
#endif


			if (bBeaconConnect)
			{
				if (!bSkipJoin)
				{
					MessageType = NMT_BeaconJoin;
					*ControlChanBunch << MessageType;
					*ControlChanBunch << InBeaconType;

					// Also immediately ack the beacon GUID setup; we're just going to let the server setup the client beacon,
					// through the actor channel
					MessageType = NMT_BeaconNetGUIDAck;
					*ControlChanBunch << MessageType;
					*ControlChanBunch << InBeaconType;
				}
			}
			else
			{
				// Then send NMT_Login
#if TARGET_UE4_CL < CL_CONSTUNIQUEID
				TSharedPtr<FUniqueNetId> DudPtr = MakeShareable(new FUniqueNetIdString(TEXT("Dud")));
#else
				TSharedPtr<const FUniqueNetId> DudPtr = MakeShareable(new FUniqueNetIdString(TEXT("Dud")));
#endif

				FUniqueNetIdRepl PlayerUID(DudPtr);
				FString BlankStr = TEXT("");
				FString ConnectURL = UUnitTest::UnitEnv->GetDefaultClientConnectURL();

				if (InNetID != NULL)
				{
					PlayerUID = *InNetID;
				}

				MessageType = NMT_Login;
				*ControlChanBunch << MessageType;
				*ControlChanBunch << BlankStr;
				*ControlChanBunch << ConnectURL;
				*ControlChanBunch << PlayerUID;


				// Now send NMT_Join, to trigger a fake player, which should then trigger replication of basic actor channels
				if (!bSkipJoin)
				{
					MessageType = NMT_Join;
					*ControlChanBunch << MessageType;
				}
			}


			// Big hack: Store OutRec value on the unit test control channel, to enable 'retry-send' code
			TargetConn->Channels[0]->OutRec = ControlChanBunch;

			TargetConn->SendRawBunch(*ControlChanBunch, true);


			bSuccess = true;

		}
		else
		{
			UE_LOG(LogUnitTest, Log, TEXT("Failed to kickoff connect to IP '%s', error: %s"), *ServerIP, *ConnectionError);
		}
	}
	else if (InNetDriver == NULL)
	{
		UE_LOG(LogUnitTest, Log, TEXT("Failed to create an instance of the unit test net driver"));
	}
	else
	{
		UE_LOG(LogUnitTest, Log, TEXT("Failed to get a reference to WorldContext"));
	}

	return bSuccess;
}
예제 #6
0
void UGameEngine::Tick( float DeltaSeconds, bool bIdleMode )
{
	SCOPE_CYCLE_COUNTER(STAT_GameEngineTick);
	NETWORK_PROFILER(GNetworkProfiler.TrackFrameBegin());

	int32 LocalTickCycles=0;
	CLOCK_CYCLES(LocalTickCycles);
	
	// -----------------------------------------------------
	// Non-World related stuff
	// -----------------------------------------------------

	if( DeltaSeconds < 0.0f )
	{
#if (UE_BUILD_SHIPPING && WITH_EDITOR)
		// End users don't have access to the secure parts of UDN.  Regardless, they won't
		// need the warning because the game ships with AMD drivers that address the issue.
		UE_LOG(LogEngine, Fatal,TEXT("Negative delta time!"));
#else
		// Send developers to the support list thread.
		UE_LOG(LogEngine, Fatal,TEXT("Negative delta time! Please see https://udn.epicgames.com/lists/showpost.php?list=ue3bugs&id=4364"));
#endif
	}

	// Tick allocator
	if( GMalloc != NULL )
	{
		GMalloc->Tick( DeltaSeconds );
	}

	// Tick the module manager
	FModuleManager::Get().Tick();

	if (!IsRunningDedicatedServer() && !IsRunningCommandlet())
	{
		// Clean up the game viewports that have been closed.
		CleanupGameViewport();
	}

	// If all viewports closed, time to exit.
	if(GIsClient && GameViewport == NULL )
	{
		UE_LOG(LogEngine, Log,  TEXT("All Windows Closed") );
		FPlatformMisc::RequestExit( 0 );
		return;
	}

	if ( GameViewport != NULL )
	{
		// Decide whether to drop high detail because of frame rate.
		GameViewport->SetDropDetail(DeltaSeconds);
	}

	// Update subsystems.
	{
		// This assumes that UObject::StaticTick only calls ProcessAsyncLoading.
		StaticTick( DeltaSeconds );
	}

	// -----------------------------------------------------
	// Begin ticking worlds
	// -----------------------------------------------------

	int32 OriginalGWorldContext = INDEX_NONE;
	for (int32 i=0; i < WorldList.Num(); ++i)
	{
		if (WorldList[i].World() == GWorld)
		{
			OriginalGWorldContext = WorldList[i].ContextHandle;
			break;
		}
	}

	bool WorldWasPaused = false;

	for (int32 WorldIdx = 0; WorldIdx < WorldList.Num(); ++WorldIdx)
	{
		FWorldContext &Context = WorldList[WorldIdx];
		if (Context.World() == NULL)
		{
			continue;
		}

		WorldWasPaused |= Context.World()->IsPaused();

		GWorld = Context.World();

		// Tick all travel and Pending NetGames (Seamless, server, client)
		TickWorldTravel(Context, DeltaSeconds);

		if (!IsRunningDedicatedServer() && !IsRunningCommandlet())
		{
			// Only update reflection captures in game once all 'always loaded' levels have been loaded
			// This won't work with actual level streaming though
			if (Context.World()->AreAlwaysLoadedLevelsLoaded())
			{
				// Update sky light first because it's considered direct lighting, sky diffuse will be visible in reflection capture indirect specular
				USkyLightComponent::UpdateSkyCaptureContents(Context.World());
				UReflectionCaptureComponent::UpdateReflectionCaptureContents(Context.World());
			}
		}

		if (!bIdleMode)
		{
			// Tick the world.
			GameCycles=0;
			CLOCK_CYCLES(GameCycles);
			Context.World()->Tick( LEVELTICK_All, DeltaSeconds );
			UNCLOCK_CYCLES(GameCycles);
		}

		// Issue cause event after first tick to provide a chance for the game to spawn the player and such.
		if( Context.World()->bWorldWasLoadedThisTick )
		{
			Context.World()->bWorldWasLoadedThisTick = false;
			
			const TCHAR* InitialExec = Context.LastURL.GetOption(TEXT("causeevent="),NULL);
			ULocalPlayer* GamePlayer = Context.GamePlayers.Num() > 0 ? Context.GamePlayers[0] : NULL;
			if( InitialExec && GamePlayer )
			{
				UE_LOG(LogEngine, Log, TEXT("Issuing initial cause event passed from URL: %s"), InitialExec);
				GamePlayer->Exec( GamePlayer->GetWorld(), *(FString("CAUSEEVENT ") + InitialExec), *GLog );
			}

			Context.World()->bTriggerPostLoadMap = true;
		}

		// Tick the viewports.
		if ( GameViewport != NULL && !bIdleMode )
		{
			SCOPE_CYCLE_COUNTER(STAT_GameViewportTick);
			GameViewport->Tick(DeltaSeconds);
		}
	
		UpdateTransitionType(Context.World());
	
		// fixme: this will only happen once due to the static bool, but still need to figure out how to handle this for multiple worlds
		if (FPlatformProperties::SupportsWindowedMode())
		{
			// Hide the splashscreen and show the game window
			static bool bFirstTime = true;
			if ( bFirstTime )
			{
				bFirstTime = false;
				FPlatformSplash::Hide();
				if ( GameViewportWindow.IsValid() )
				{
					GameViewportWindow.Pin()->ShowWindow();
					FSlateApplication::Get().RegisterGameViewport( GameViewportWidget.ToSharedRef() );
				}
			}
		}

		if (!bIdleMode && !IsRunningDedicatedServer() && !IsRunningCommandlet())
		{
			// Render everything.
			RedrawViewports();
		}

		// Block on async loading if requested.
		if( Context.World()->bRequestedBlockOnAsyncLoading )
		{
			// Only perform work if there is anything to do. This ensures we are not syncronizing with the GPU
			// and suspending the device needlessly.
			bool bWorkToDo = IsAsyncLoading();
			if (!bWorkToDo)
			{
				Context.World()->UpdateLevelStreaming();
				bWorkToDo = Context.World()->IsVisibilityRequestPending();
			}
			if (bWorkToDo)
			{
				// tell clients to do the same so they don't fall behind
				for( FConstPlayerControllerIterator Iterator = Context.World()->GetPlayerControllerIterator(); Iterator; ++Iterator )
				{
					APlayerController* PlayerController = *Iterator;
					UNetConnection* Conn = Cast<UNetConnection>(PlayerController->Player);
					if (Conn != NULL && Conn->GetUChildConnection() == NULL)
					{
						// call the event to replicate the call
						PlayerController->ClientSetBlockOnAsyncLoading();
						// flush the connection to make sure it gets sent immediately
						Conn->FlushNet(true);
					}
				}

				FStreamingPause::GameThreadWantsToSuspendRendering( GameViewport ? GameViewport->Viewport : NULL );

				// Flushes level streaming requests, blocking till completion.
				Context.World()->FlushLevelStreaming();

				FStreamingPause::GameThreadWantsToResumeRendering();
			}
			Context.World()->bRequestedBlockOnAsyncLoading = false;
		}

		// streamingServer
		if( GIsServer == true )
		{
			SCOPE_CYCLE_COUNTER(STAT_UpdateLevelStreaming);
			Context.World()->UpdateLevelStreaming();
		}

		// Update Audio. This needs to occur after rendering as the rendering code updates the listener position.
		if( GetAudioDevice() )
		{
			GetAudioDevice()->Update( !Context.World()->IsPaused() );
		}

	

		if( GIsClient )
		{
			// GStreamingManager is updated outside of a world context. For now, assuming it needs to tick here, before possibly calling PostLoadMap. 
			// Will need to take another look when trying to support multiple worlds.

			// Update resource streaming after viewports have had a chance to update view information. Normal update.
			GStreamingManager->Tick( DeltaSeconds );

			if ( Context.World()->bTriggerPostLoadMap )
			{
				Context.World()->bTriggerPostLoadMap = false;

				// Turns off the loading movie (if it was turned on by LoadMap) and other post-load cleanup.
				PostLoadMap();
			}
		}

		UNCLOCK_CYCLES(LocalTickCycles);
		TickCycles=LocalTickCycles;

		// See whether any map changes are pending and we requested them to be committed.
		ConditionalCommitMapChange(Context);
	}

	// ----------------------------
	//	End per-world ticking
	// ----------------------------

	// Restore original GWorld*. This will go away one day.
	if (OriginalGWorldContext != INDEX_NONE)
	{
		GWorld = WorldContextFromHandle(OriginalGWorldContext).World();
	}

	// tell renderer about GWorld->IsPaused(), before rendering
	{
		ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
			SetPaused,
			bool, bGamePaused, WorldWasPaused,
		{
			GRenderingRealtimeClock.SetGamePaused(bGamePaused);
		});
	}