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