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