void UIpConnection::LowLevelSend(void* Data, int32 CountBytes, int32 CountBits)
{
	const uint8* DataToSend = reinterpret_cast<uint8*>(Data);

	if( ResolveInfo )
	{
		// If destination address isn't resolved yet, send nowhere.
		if( !ResolveInfo->IsComplete() )
		{
			// Host name still resolving.
			return;
		}
		else if( ResolveInfo->GetErrorCode() != SE_NO_ERROR )
		{
			// Host name resolution just now failed.
			UE_LOG(LogNet, Log,  TEXT("Host name resolution failed with %d"), ResolveInfo->GetErrorCode() );
			Driver->ServerConnection->State = USOCK_Closed;
			delete ResolveInfo;
			ResolveInfo = NULL;
			return;
		}
		else
		{
			uint32 Addr;
			// Host name resolution just now succeeded.
			ResolveInfo->GetResolvedAddress().GetIp(Addr);
			RemoteAddr->SetIp(Addr);
			UE_LOG(LogNet, Log, TEXT("Host name resolution completed"));
			delete ResolveInfo;
			ResolveInfo = NULL;
		}
	}
	 
	// Process any packet modifiers
	if (Handler.IsValid() && !Handler->GetRawSend())
	{
		const ProcessedPacket ProcessedData = Handler->Outgoing(reinterpret_cast<uint8*>(Data), CountBits);

		DataToSend = ProcessedData.Data;
		CountBytes = FMath::DivideAndRoundUp(ProcessedData.CountBits, 8);
		CountBits = ProcessedData.CountBits;
	}

	// Send to remote.
	int32 BytesSent = 0;
	CLOCK_CYCLES(Driver->SendCycles);

	if ( CountBytes > MaxPacket )
	{
		UE_LOG( LogNet, Warning, TEXT( "UIpConnection::LowLevelSend: CountBytes > MaxPacketSize! Count: %i, MaxPacket: %i %s" ), CountBytes, MaxPacket, *Describe() );
	}

	Socket->SendTo(DataToSend, CountBytes, BytesSent, *RemoteAddr);
	UNCLOCK_CYCLES(Driver->SendCycles);
	NETWORK_PROFILER(GNetworkProfiler.FlushOutgoingBunches(this));
	NETWORK_PROFILER(GNetworkProfiler.TrackSocketSendTo(Socket->GetDescription(),DataToSend,BytesSent,NumPacketIdBits,NumBunchBits,NumAckBits,NumPaddingBits,this));
}
void UGameEngine::PreExit()
{
	FAVIWriter* AVIWriter = FAVIWriter::GetInstance();
	if (AVIWriter)
	{
		AVIWriter->Close();
	}

	Super::PreExit();

	// Stop tracking, automatically flushes.
	NETWORK_PROFILER(GNetworkProfiler.EnableTracking(false));

	CancelAllPending();

	// Clean up world.
	for (int32 WorldIndex = 0; WorldIndex < WorldList.Num(); ++WorldIndex)
	{
		UWorld* const World = WorldList[WorldIndex].World();
		if ( World != NULL )
		{
			// notify the GameMode
			AGameMode* const GameMode = World->GetAuthGameMode();
			if (GameMode != NULL)
			{
				GameMode->PreExit();
			}
			World->FlushLevelStreaming( NULL, true );
			World->CleanupWorld();
		}
	}
}
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();
	}
}
Beispiel #4
0
void UGameEngine::PreExit()
{
	FAVIWriter* AVIWriter = FAVIWriter::GetInstance();
	if (AVIWriter)
	{
		AVIWriter->Close();
	}

	Super::PreExit();

	// Stop tracking, automatically flushes.
	NETWORK_PROFILER(GNetworkProfiler.EnableTracking(false));

	CancelAllPending();

	// Clean up all worlds
	for (int32 WorldIndex = 0; WorldIndex < WorldList.Num(); ++WorldIndex)
	{
		UWorld* const World = WorldList[WorldIndex].World();
		if ( World != NULL )
		{
			World->bIsTearingDown = true;

			// Cancel any pending connection to a server
			CancelPending(World);

			// Shut down any existing game connections
			ShutdownWorldNetDriver(World);

			for (FActorIterator ActorIt(World); ActorIt; ++ActorIt)
			{
				ActorIt->RouteEndPlay(EEndPlayReason::Quit);
			}

			World->GetGameInstance()->Shutdown();

			World->FlushLevelStreaming(EFlushLevelStreamingType::Visibility);
			World->CleanupWorld();
		}
	}
}
void UIpConnection::LowLevelSend( void* Data, int32 Count )
{
	if( ResolveInfo )
	{
		// If destination address isn't resolved yet, send nowhere.
		if( !ResolveInfo->IsComplete() )
		{
			// Host name still resolving.
			return;
		}
		else if( ResolveInfo->GetErrorCode() != SE_NO_ERROR )
		{
			// Host name resolution just now failed.
			UE_LOG(LogNet, Log,  TEXT("Host name resolution failed with %d"), ResolveInfo->GetErrorCode() );
			Driver->ServerConnection->State = USOCK_Closed;
			delete ResolveInfo;
			ResolveInfo = NULL;
			return;
		}
		else
		{
			uint32 Addr;
			// Host name resolution just now succeeded.
			ResolveInfo->GetResolvedAddress().GetIp(Addr);
			RemoteAddr->SetIp(Addr);
			UE_LOG(LogNet, Log, TEXT("Host name resolution completed"));
			delete ResolveInfo;
			ResolveInfo = NULL;
		}
	}
	// Send to remote.
	int32 BytesSent = 0;
	CLOCK_CYCLES(Driver->SendCycles);
	Socket->SendTo((uint8*)Data, Count, BytesSent, *RemoteAddr);
	UNCLOCK_CYCLES(Driver->SendCycles);
	NETWORK_PROFILER(GNetworkProfiler.TrackSocketSendTo(Socket->GetDescription(),Data,BytesSent,*RemoteAddr));
}
/** Replicates properties to the Bunch. Returns true if it wrote anything */
bool FObjectReplicator::ReplicateProperties( FOutBunch & Bunch, FReplicationFlags RepFlags )
{
	UObject* Object = GetObject();

	if ( Object == NULL )
	{
		UE_LOG(LogRep, Verbose, TEXT("ReplicateProperties: Object == NULL"));
		return false;
	}

	check( Object );
	check( OwningChannel );
	check( RepLayout.IsValid() );
	check( RepState )
	check( RepState->StaticBuffer.Num() );

	UNetConnection* OwningChannelConnection = OwningChannel->Connection;

	const int32 StartingBitNum = Bunch.GetNumBits();

	bool bContentBlockWritten = false;

	// Replicate all the custom delta properties (fast arrays, etc)
	ReplicateCustomDeltaProperties( Bunch, RepFlags, bContentBlockWritten );

	// Replicate properties in the layout
	RepLayout->ReplicateProperties( RepState, (uint8*)Object, ObjectClass, OwningChannel, Bunch, RepFlags, bContentBlockWritten );

	// LastUpdateEmpty - this is done before dequeing the multicasted unreliable functions on purpose as they should not prevent
	// an actor channel from going dormant.
	bLastUpdateEmpty = ( Bunch.GetNumBits() == StartingBitNum );

	// Replicate Queued (unreliable functions)
	if ( RemoteFunctions != NULL && RemoteFunctions->GetNumBits() > 0 )
	{
		static const auto* CVar = IConsoleManager::Get().FindTConsoleVariableDataInt( TEXT( "net.RPC.Debug" ) );

		if ( CVar && CVar->GetValueOnGameThread() == 1 )
		{
			UE_LOG( LogRepTraffic, Warning,	TEXT("      Sending queued RPCs: %s. Channel[%d] [%.1f bytes]"), *Object->GetName(), OwningChannel->ChIndex, RemoteFunctions->GetNumBits() / 8.f );
		}

		if ( !bContentBlockWritten )
		{
			OwningChannel->BeginContentBlock( Object, Bunch );
			bContentBlockWritten = true;
		}

		Bunch.SerializeBits( RemoteFunctions->GetData(), RemoteFunctions->GetNumBits() );
		RemoteFunctions->Reset();
		RemoteFuncInfo.Empty();

		NETWORK_PROFILER(GNetworkProfiler.FlushQueuedRPCs(OwningChannelConnection, Object));
	}

	// See if we wrote something important (anything but the 'end' int below).
	// Note that queued unreliable functions are considered important (WroteImportantData) but not for bLastUpdateEmpty. LastUpdateEmpty
	// is used for dormancy purposes. WroteImportantData is for determining if we should not include a component in replication.
	const bool WroteImportantData = ( Bunch.GetNumBits() != StartingBitNum );

	if ( WroteImportantData )
	{
		OwningChannel->EndContentBlock( Object, Bunch, OwningChannelConnection->Driver->NetCache->GetClassNetCache( ObjectClass ) );
	}

	return WroteImportantData;
}
void FObjectReplicator::ReplicateCustomDeltaProperties( FOutBunch & Bunch, FReplicationFlags RepFlags, bool & bContentBlockWritten )
{
	if ( LifetimeCustomDeltaProperties.Num() == 0 )
	{
		// No custom properties
		return;
	}

	UObject* Object = GetObject();

	check( Object );
	check( OwningChannel );

	UNetConnection * OwningChannelConnection = OwningChannel->Connection;

	// Initialize a map of which conditions are valid

	bool ConditionMap[COND_Max];
	const bool bIsInitial = RepFlags.bNetInitial ? true : false;
	const bool bIsOwner = RepFlags.bNetOwner ? true : false;
	const bool bIsSimulated = RepFlags.bNetSimulated ? true : false;
	const bool bIsPhysics = RepFlags.bRepPhysics ? true : false;

	ConditionMap[COND_None] = true;
	ConditionMap[COND_InitialOnly] = bIsInitial;
	ConditionMap[COND_OwnerOnly] = bIsOwner;
	ConditionMap[COND_SkipOwner] = !bIsOwner;
	ConditionMap[COND_SimulatedOnly] = bIsSimulated;
	ConditionMap[COND_AutonomousOnly] = !bIsSimulated;
	ConditionMap[COND_SimulatedOrPhysics] = bIsSimulated || bIsPhysics;
	ConditionMap[COND_InitialOrOwner] = bIsInitial || bIsOwner;
	ConditionMap[COND_Custom] = true;

	// Replicate those properties.
	for ( int32 i = 0; i < LifetimeCustomDeltaProperties.Num(); i++ )
	{
		// Get info.
		const int32				RetireIndex	= LifetimeCustomDeltaProperties[i];
		FPropertyRetirement &	Retire		= Retirement[RetireIndex];
		FRepRecord *			Rep			= &ObjectClass->ClassReps[RetireIndex];
		UProperty *				It			= Rep->Property;
		int32					Index		= Rep->Index;

		if (LifetimeCustomDeltaPropertyConditions.IsValidIndex(i))
		{
			// Check the replication condition here
			ELifetimeCondition RepCondition = LifetimeCustomDeltaPropertyConditions[i];

			check(RepCondition >= 0 && RepCondition < COND_Max);

			if (!ConditionMap[RepCondition])
			{
				// We didn't pass the condition so don't replicate us
				continue;
			}
		}

		const int32 BitsWrittenBeforeThis = Bunch.GetNumBits();

		// If this is a dynamic array, we do the delta here
		TSharedPtr<INetDeltaBaseState> & OldState = RecentCustomDeltaState.FindOrAdd( RetireIndex );
		TSharedPtr<INetDeltaBaseState> NewState;

		// Update Retirement records with this new state so we can handle packet drops.
		// LastNext will be pointer to the last "Next" pointer in the list (so pointer to a pointer)
		FPropertyRetirement ** LastNext = UpdateAckedRetirements( Retire, OwningChannelConnection->OutAckPacketId );

		check( LastNext != NULL );
		check( *LastNext == NULL );

		ValidateRetirementHistory( Retire );

		FNetBitWriter TempBitWriter( OwningChannel->Connection->PackageMap, 0 );

		//-----------------------------------------
		//	Do delta serialization on dynamic properties
		//-----------------------------------------
		const bool WroteSomething = SerializeCustomDeltaProperty( OwningChannelConnection, (void*)Object, It, Index, TempBitWriter, NewState, OldState );

		if ( !WroteSomething )
		{
			continue;
		}

		*LastNext = new FPropertyRetirement();

		// Remember what the old state was at this point in time.  If we get a nak, we will need to revert back to this.
		(*LastNext)->DynamicState = OldState;		

		// Save NewState into the RecentCustomDeltaState array (old state is a reference into our RecentCustomDeltaState map)
		OldState = NewState; 

		// Write header, and data to send to the actual bunch
		RepLayout->WritePropertyHeader( Object, ObjectClass, OwningChannel, It, Bunch, Index, bContentBlockWritten );

		const int NumStartingBits = Bunch.GetNumBits();

		// Send property.
		Bunch.SerializeBits( TempBitWriter.GetData(), TempBitWriter.GetNumBits() );

		NETWORK_PROFILER(GNetworkProfiler.TrackReplicateProperty(It, Bunch.GetNumBits() - NumStartingBits));
	}
}
Beispiel #8
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 the module manager
	IHotReloadInterface* HotReload = IHotReloadInterface::GetPtr();
	if(HotReload != nullptr)
	{
		HotReload->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.
		QUICK_SCOPE_CYCLE_COUNTER(STAT_UGameEngine_Tick_SetDropDetail);
		GameViewport->SetDropDetail(DeltaSeconds);
	}

	// Update subsystems.
	{
		// This assumes that UObject::StaticTick only calls ProcessAsyncLoading.
		StaticTick(DeltaSeconds, !!GAsyncLoadingUseFullTimeLimit, GAsyncLoadingTimeLimit / 1000.f);
	}

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

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

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

		GWorld = Context.World();

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

		if (!IsRunningDedicatedServer() && !IsRunningCommandlet())
		{
			QUICK_SCOPE_CYCLE_COUNTER(STAT_UGameEngine_Tick_CheckCaptures);
			// 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.OwningGameInstance ? Context.OwningGameInstance->GetFirstGamePlayer() : 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)
		{
			BlockTillLevelStreamingCompleted(Context.World());
			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 (FAudioDevice* AudioDevice = Context.World()->GetAudioDevice())
		{
			AudioDevice->Update(!Context.World()->IsPaused());
		}

		if( GIsClient )
		{
			// IStreamingManager 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.
			{
				QUICK_SCOPE_CYCLE_COUNTER(STAT_UGameEngine_Tick_IStreamingManager);
				IStreamingManager::Get().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.
				QUICK_SCOPE_CYCLE_COUNTER(STAT_UGameEngine_Tick_IStreamingManager);
				PostLoadMap();
			}
		}

		UNCLOCK_CYCLES(LocalTickCycles);
		TickCycles=LocalTickCycles;

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

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

	// Restore original GWorld*. This will go away one day.
	if (OriginalGWorldContext != NAME_None)
	{
		QUICK_SCOPE_CYCLE_COUNTER(STAT_UGameEngine_Tick_GetWorldContextFromHandleChecked);
		GWorld = GetWorldContextFromHandleChecked(OriginalGWorldContext).World();
	}

	// rendering thread commands
	{
		ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
			TickRenderingTimer,
			bool, bPauseRenderingRealtimeClock, GPauseRenderingRealtimeClock,
			float, DeltaTime, DeltaSeconds,
		{
			if(!bPauseRenderingRealtimeClock)
			{
				// Tick the GRenderingRealtimeClock, unless it's paused
				GRenderingRealtimeClock.Tick(DeltaTime);
			}

			GetRendererModule().TickRenderTargetPool();
		});
	}
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);
		});
	}