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 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)); }
void UIpNetDriver::TickDispatch( float DeltaTime ) { Super::TickDispatch( DeltaTime ); ISocketSubsystem* SocketSubsystem = GetSocketSubsystem(); // Process all incoming packets. uint8 Data[NETWORK_MAX_PACKET]; TSharedRef<FInternetAddr> FromAddr = SocketSubsystem->CreateInternetAddr(); for( ; Socket != NULL; ) { int32 BytesRead = 0; // Get data, if any. CLOCK_CYCLES(RecvCycles); bool bOk = Socket->RecvFrom(Data, sizeof(Data), BytesRead, *FromAddr); UNCLOCK_CYCLES(RecvCycles); // Handle result. if( bOk == false ) { ESocketErrors Error = SocketSubsystem->GetLastErrorCode(); if(Error == SE_EWOULDBLOCK || Error == SE_NO_ERROR) { // No data or no error? break; } else { if( Error != SE_ECONNRESET && Error != SE_UDP_ERR_PORT_UNREACH ) { UE_LOG(LogNet, Warning, TEXT("UDP recvfrom error: %i (%s) from %s"), (int32)Error, SocketSubsystem->GetSocketError(Error), *FromAddr->ToString(true)); break; } } } // Figure out which socket the received data came from. UIpConnection* Connection = NULL; if (GetServerConnection() && (*GetServerConnection()->RemoteAddr == *FromAddr)) { Connection = GetServerConnection(); } for( int32 i=0; i<ClientConnections.Num() && !Connection; i++ ) { UIpConnection* TestConnection = (UIpConnection*)ClientConnections[i]; check(TestConnection); if(*TestConnection->RemoteAddr == *FromAddr) { Connection = TestConnection; } } if( bOk == false ) { if( Connection ) { if( Connection != GetServerConnection() ) { // We received an ICMP port unreachable from the client, meaning the client is no longer running the game // (or someone is trying to perform a DoS attack on the client) // rcg08182002 Some buggy firewalls get occasional ICMP port // unreachable messages from legitimate players. Still, this code // will drop them unceremoniously, so there's an option in the .INI // file for servers with such flakey connections to let these // players slide...which means if the client's game crashes, they // might get flooded to some degree with packets until they timeout. // Either way, this should close up the usual DoS attacks. if ((Connection->State != USOCK_Open) || (!AllowPlayerPortUnreach)) { if (LogPortUnreach) { UE_LOG(LogNet, Log, TEXT("Received ICMP port unreachable from client %s. Disconnecting."), *FromAddr->ToString(true)); } Connection->CleanUp(); } } } else { if (LogPortUnreach) { UE_LOG(LogNet, Log, TEXT("Received ICMP port unreachable from %s. No matching connection found."), *FromAddr->ToString(true)); } } } else { // If we didn't find a client connection, maybe create a new one. if( !Connection ) { // Determine if allowing for client/server connections const bool bAcceptingConnection = Notify->NotifyAcceptingConnection() == EAcceptConnection::Accept; if (bAcceptingConnection) { Connection = ConstructObject<UIpConnection>(NetConnectionClass); check(Connection); Connection->InitRemoteConnection( this, Socket, FURL(), *FromAddr, USOCK_Open); Notify->NotifyAcceptedConnection( Connection ); AddClientConnection(Connection); } } // Send the packet to the connection for processing. if( Connection ) { Connection->ReceivedRawPacket( Data, BytesRead ); } } } }
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); }); }