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