ULevel* CreateNewLevel( UWorld* InWorld, bool bMoveSelectedActorsIntoNewLevel, UClass* LevelStreamingClass, const FString& DefaultFilename ) { // Editor modes cannot be active when any level saving occurs. GEditorModeTools().ActivateMode( FBuiltinEditorModes::EM_Default ); // Create a new world - so we can 'borrow' its level UWorld* NewGWorld = UWorld::CreateWorld(EWorldType::None, false ); check(NewGWorld); // Save the new world to disk. const bool bNewWorldSaved = FEditorFileUtils::SaveLevel( NewGWorld->PersistentLevel, DefaultFilename ); FString NewPackageName; if ( bNewWorldSaved ) { NewPackageName = NewGWorld->GetOutermost()->GetName(); } // Destroy the new world we created and collect the garbage NewGWorld->DestroyWorld( false ); CollectGarbage( GARBAGE_COLLECTION_KEEPFLAGS ); // If the new world was saved successfully, import it as a streaming level. ULevel* NewLevel = NULL; if ( bNewWorldSaved ) { NewLevel = AddLevelToWorld( InWorld, *NewPackageName, LevelStreamingClass ); // If we are moving the selected actors to the new level move them now if ( bMoveSelectedActorsIntoNewLevel ) { GEditor->MoveSelectedActorsToLevel( NewLevel ); } // Finally make the new level the current one InWorld->SetCurrentLevel( NewLevel ); } // Broadcast the levels have changed (new style) InWorld->BroadcastLevelsChanged(); return NewLevel; }
void FWorldTileModel::AddStreamingLevel(UClass* InStreamingClass, const FName& InPackageName) { if (LevelCollectionModel.IsReadOnly() || !IsEditable() || IsRootTile()) { return; } UWorld* LevelWorld = CastChecked<UWorld>(GetLevelObject()->GetOuter()); // check uniqueness if (LevelWorld->StreamingLevels.FindMatch(ULevelStreaming::FPackageNameMatcher(InPackageName)) != INDEX_NONE) { return; } ULevelStreaming* StreamingLevel = static_cast<ULevelStreaming*>(StaticConstructObject(InStreamingClass, LevelWorld, NAME_None, RF_NoFlags, NULL)); // Associate a package name. StreamingLevel->PackageName = InPackageName; // Seed the level's draw color. StreamingLevel->DrawColor = FColor::MakeRandomColor(); StreamingLevel->LevelTransform = FTransform::Identity; StreamingLevel->PackageNameToLoad = InPackageName; // Add the streaming level to level's world LevelWorld->StreamingLevels.Add(StreamingLevel); LevelWorld->GetOutermost()->MarkPackageDirty(); }
void ANUTActor::ServerClientPing_Implementation() { // If any clients have not loaded the current level, do nothing bool bNotLoaded = false; UWorld* CurWorld = GetWorld(); UNetDriver* CurNetDriver = (CurWorld != NULL ? NUTUtil::GetActiveNetDriver(CurWorld) : NULL); for (auto CurConn : CurNetDriver->ClientConnections) { // Based on UNetDriver::IsLevelInitializeForActor bNotLoaded = !(CurConn->ClientWorldPackageName == CurWorld->GetOutermost()->GetFName() && CurConn->ClientHasInitializedLevelFor(this)); // Also trigger if there is no PlayerController yet set for the connection if (CurConn->OwningActor == NULL) { bNotLoaded = true; } if (bNotLoaded) { break; } } if (!bNotLoaded) { NetMulticastPing(); } }
void AOnlineBeaconHost::NotifyControlMessage(UNetConnection* Connection, uint8 MessageType, class FInBunch& Bunch) { if(NetDriver->ServerConnection == NULL) { bool bCloseConnection = false; // We are the server. #if !(UE_BUILD_SHIPPING && WITH_EDITOR) UE_LOG(LogNet, Verbose, TEXT("Beacon: Host received: %s"), FNetControlMessageInfo::GetName(MessageType)); #endif switch (MessageType) { case NMT_Hello: { UE_LOG(LogNet, Log, TEXT("Beacon Hello")); uint8 IsLittleEndian; int32 RemoteMinVer, RemoteVer; FGuid RemoteGameGUID; FNetControlMessage<NMT_Hello>::Receive(Bunch, IsLittleEndian, RemoteMinVer, RemoteVer, RemoteGameGUID); if (!IsNetworkCompatible(Connection->Driver->RequireEngineVersionMatch, RemoteVer, RemoteMinVer)) { FNetControlMessage<NMT_Upgrade>::Send(Connection, GEngineMinNetVersion, GEngineNetVersion); bCloseConnection = true; } else { Connection->NegotiatedVer = FMath::Min(RemoteVer, GEngineNetVersion); // Make sure the server has the same GameGUID as we do if( RemoteGameGUID != GetDefault<UGeneralProjectSettings>()->ProjectID ) { FString ErrorMsg = NSLOCTEXT("NetworkErrors", "ServerHostingDifferentGame", "Incompatible game connection.").ToString(); FNetControlMessage<NMT_Failure>::Send(Connection, ErrorMsg); bCloseConnection = true; break; } Connection->Challenge = FString::Printf(TEXT("%08X"), FPlatformTime::Cycles()); FNetControlMessage<NMT_BeaconWelcome>::Send(Connection); Connection->FlushNet(); } break; } case NMT_Netspeed: { int32 Rate; FNetControlMessage<NMT_Netspeed>::Receive(Bunch, Rate); Connection->CurrentNetSpeed = FMath::Clamp(Rate, 1800, NetDriver->MaxClientRate); UE_LOG(LogNet, Log, TEXT("Beacon: Client netspeed is %i"), Connection->CurrentNetSpeed); break; } case NMT_BeaconJoin: { FString ErrorMsg; FString BeaconType; FNetControlMessage<NMT_BeaconJoin>::Receive(Bunch, BeaconType); UE_LOG(LogNet, Log, TEXT("Beacon Join %s"), *BeaconType); if (Connection->ClientWorldPackageName == NAME_None) { AOnlineBeaconClient* ClientActor = GetClientActor(Connection); if (ClientActor == NULL) { UWorld* World = GetWorld(); Connection->ClientWorldPackageName = World->GetOutermost()->GetFName(); AOnlineBeaconClient* NewClientActor = SpawnBeaconActor(); if (NewClientActor && BeaconType == NewClientActor->GetBeaconType()) { FNetworkGUID NetGUID = Connection->PackageMap->AssignNewNetGUID(NewClientActor); NewClientActor->SetNetConnection(Connection); Connection->OwningActor = NewClientActor; NewClientActor->Role = ROLE_None; NewClientActor->SetReplicates(false); ClientActors.Add(NewClientActor); FNetControlMessage<NMT_BeaconAssignGUID>::Send(Connection, NetGUID); } else { ErrorMsg = NSLOCTEXT("NetworkErrors", "BeaconSpawnFailureError", "Join failure, Couldn't spawn beacon.").ToString(); } } else { ErrorMsg = NSLOCTEXT("NetworkErrors", "BeaconSpawnExistingActorError", "Join failure, existing beacon actor.").ToString();; } } else { ErrorMsg = NSLOCTEXT("NetworkErrors", "BeaconSpawnClientWorldPackageNameError", "Join failure, existing ClientWorldPackageName.").ToString();; } if (!ErrorMsg.IsEmpty()) { UE_LOG(LogNet, Log, TEXT("%s"), *ErrorMsg); FNetControlMessage<NMT_Failure>::Send(Connection, ErrorMsg); bCloseConnection = true; } break; } case NMT_BeaconNetGUIDAck: { FString BeaconType; FNetControlMessage<NMT_BeaconNetGUIDAck>::Receive(Bunch, BeaconType); AOnlineBeaconClient* ClientActor = GetClientActor(Connection); if (ClientActor && BeaconType == ClientActor->GetBeaconType()) { ClientActor->Role = ROLE_Authority; ClientActor->SetReplicates(true); ClientActor->SetAutonomousProxy(true); // Send an RPC to the client to open the actor channel and guarantee RPCs will work ClientActor->ClientOnConnected(); UE_LOG(LogNet, Log, TEXT("Beacon Handshake complete!")); FOnBeaconConnected* OnBeaconConnectedDelegate = OnBeaconConnectedMapping.Find(FName(*BeaconType)); if (OnBeaconConnectedDelegate) { OnBeaconConnectedDelegate->ExecuteIfBound(ClientActor, Connection); } } else { // Failed to connect. FString ErrorMsg = NSLOCTEXT("NetworkErrors", "BeaconSpawnNetGUIDAckError", "Join failure, no actor at NetGUIDAck.").ToString(); UE_LOG(LogNet, Log, TEXT("%s"), *ErrorMsg); FNetControlMessage<NMT_Failure>::Send(Connection, ErrorMsg); bCloseConnection = true; } break; } case NMT_BeaconWelcome: case NMT_BeaconAssignGUID: default: { FString ErrorMsg = NSLOCTEXT("NetworkErrors", "BeaconSpawnUnexpectedError", "Join failure, unexpected control message.").ToString(); UE_LOG(LogNet, Log, TEXT("%s"), *ErrorMsg); FNetControlMessage<NMT_Failure>::Send(Connection, ErrorMsg); bCloseConnection = true; } break; } if (bCloseConnection) { AOnlineBeaconClient* ClientActor = GetClientActor(Connection); if (ClientActor) { RemoveClientActor(ClientActor); } Connection->FlushNet(true); Connection->Close(); } } }
void UWorldComposition::Rescan() { // Save tiles state, so we can restore it for dirty tiles after rescan is done FTilesList SavedTileList = Tiles; Reset(); UWorld* OwningWorld = GetWorld(); FString RootPackageName = GetOutermost()->GetName(); RootPackageName = UWorld::StripPIEPrefixFromPackageName(RootPackageName, OwningWorld->StreamingLevelsPrefix); if (!FPackageName::DoesPackageExist(RootPackageName)) { return; } WorldRoot = FPaths::GetPath(RootPackageName) + TEXT("/"); // Gather tiles packages from a specified folder FWorldTilesGatherer Gatherer; FString WorldRootFilename = FPackageName::LongPackageNameToFilename(WorldRoot); FPlatformFileManager::Get().GetPlatformFile().IterateDirectoryRecursively(*WorldRootFilename, Gatherer); // Make sure we have persistent level name without PIE prefix FString PersistentLevelPackageName = UWorld::StripPIEPrefixFromPackageName(OwningWorld->GetOutermost()->GetName(), OwningWorld->StreamingLevelsPrefix); // Add found tiles to a world composition, except persistent level for (const auto& TilePackageName : Gatherer.TilesCollection) { // Discard persistent level entry if (TilePackageName == PersistentLevelPackageName) { continue; } FWorldTileInfo Info; FString TileFilename = FPackageName::LongPackageNameToFilename(TilePackageName, FPackageName::GetMapPackageExtension()); if (!FWorldTileInfo::Read(TileFilename, Info)) { continue; } FWorldCompositionTile Tile; Tile.PackageName = FName(*TilePackageName); Tile.Info = Info; // Assign LOD tiles FString TileShortName = FPackageName::GetShortName(TilePackageName); TArray<FPackageNameAndLODIndex> TileLODList; Gatherer.TilesLODCollection.MultiFind(TileShortName, TileLODList); if (TileLODList.Num()) { Tile.LODPackageNames.SetNum(WORLDTILE_LOD_MAX_INDEX); FString TilePath = FPackageName::GetLongPackagePath(TilePackageName) + TEXT("/"); for (const auto& TileLOD : TileLODList) { // LOD tiles should be in the same directory or in nested directory // Basically tile path should be a prefix of a LOD tile path if (TileLOD.PackageName.StartsWith(TilePath)) { Tile.LODPackageNames[TileLOD.LODIndex-1] = FName(*FString::Printf(TEXT("%s_LOD%d"), *TileLOD.PackageName, TileLOD.LODIndex)); } } // Remove null entries in LOD list int32 NullEntryIdx; if (Tile.LODPackageNames.Find(FName(), NullEntryIdx)) { Tile.LODPackageNames.SetNum(NullEntryIdx); } } Tiles.Add(Tile); } #if WITH_EDITOR RestoreDirtyTilesInfo(SavedTileList); #endif// WITH_EDITOR // Create streaming levels for each Tile PopulateStreamingLevels(); // Calculate absolute positions since they are not serialized to disk CaclulateTilesAbsolutePositions(); }
void ULevelStreaming::AsyncLevelLoadComplete(const FName& InPackageName, UPackage* InLoadedPackage, EAsyncLoadingResult::Type Result) { bHasLoadRequestPending = false; if( InLoadedPackage ) { UPackage* LevelPackage = InLoadedPackage; // Try to find a UWorld object in the level package. UWorld* World = UWorld::FindWorldInPackage(LevelPackage); if ( World ) { ULevel* Level = World->PersistentLevel; if( Level ) { check(PendingUnloadLevel == NULL); SetLoadedLevel(Level); // Broadcast level loaded event to blueprints OnLevelLoaded.Broadcast(this); // Make sure this level will start to render only when it will be fully added to the world if (LODPackageNames.Num() > 0) { Level->bRequireFullVisibilityToRender = true; // LOD levels should not be visible on server Level->bClientOnlyVisible = LODPackageNames.Contains(InLoadedPackage->GetFName()); } // In the editor levels must be in the levels array regardless of whether they are visible or not if (ensure(Level->OwningWorld) && Level->OwningWorld->WorldType == EWorldType::Editor) { Level->OwningWorld->AddLevel(Level); #if WITH_EDITOR // We should also at this point, apply the level's editor transform if (!Level->bAlreadyMovedActors) { FLevelUtils::ApplyEditorTransform(this, false); Level->bAlreadyMovedActors = true; } #endif // WITH_EDITOR } } else { UE_LOG(LogLevelStreaming, Warning, TEXT("Couldn't find ULevel object in package '%s'"), *InPackageName.ToString() ); } } else { // There could have been a redirector in the package. Attempt to follow it. UObjectRedirector* WorldRedirector = nullptr; UWorld* DestinationWorld = UWorld::FollowWorldRedirectorInPackage(LevelPackage, &WorldRedirector); if (DestinationWorld) { // To follow the world redirector for level streaming... // 1) Update all globals that refer to the redirector package by name // 2) Update the PackageNameToLoad to refer to the new package location // 3) If the package name to load was the same as the destination package name... // ... update the package name to the new package and let the next RequestLevel try this process again. // If the package name to load was different... // ... it means the specified package name was explicit and we will just load from another file. FName OldDesiredPackageName = InPackageName; TWeakObjectPtr<UWorld>* OwningWorldPtr = ULevel::StreamedLevelsOwningWorld.Find(OldDesiredPackageName); UWorld* OwningWorld = OwningWorldPtr ? OwningWorldPtr->Get() : NULL; ULevel::StreamedLevelsOwningWorld.Remove(OldDesiredPackageName); // Try again with the destination package to load. // IMPORTANT: check this BEFORE changing PackageNameToLoad, otherwise you wont know if the package name was supposed to be different. const bool bLoadingIntoDifferentPackage = (GetWorldAssetPackageFName() != PackageNameToLoad) && (PackageNameToLoad != NAME_None); // ... now set PackageNameToLoad PackageNameToLoad = DestinationWorld->GetOutermost()->GetFName(); if ( PackageNameToLoad != OldDesiredPackageName ) { EWorldType::Type* OldPackageWorldType = UWorld::WorldTypePreLoadMap.Find(OldDesiredPackageName); if ( OldPackageWorldType ) { UWorld::WorldTypePreLoadMap.FindOrAdd(PackageNameToLoad) = *OldPackageWorldType; UWorld::WorldTypePreLoadMap.Remove(OldDesiredPackageName); } } // Now determine if we are loading into the package explicitly or if it is okay to just load the other package. if ( bLoadingIntoDifferentPackage ) { // Loading into a new custom package explicitly. Load the destination world directly into the package. // Detach the linker to load from a new file into the same package. FLinkerLoad* PackageLinker = FLinkerLoad::FindExistingLinkerForPackage(LevelPackage); if (PackageLinker) { PackageLinker->Detach(); DeleteLoader(PackageLinker); PackageLinker = nullptr; } // Make sure the redirector is not in the way of the new world. // Pass NULL as the name to make a new unique name and GetTransientPackage() for the outer to remove it from the package. WorldRedirector->Rename(NULL, GetTransientPackage(), REN_DoNotDirty | REN_DontCreateRedirectors | REN_ForceNoResetLoaders | REN_NonTransactional); // Change the loaded world's type back to inactive since it won't be used. DestinationWorld->WorldType = EWorldType::Inactive; } else { // Loading the requested package normally. Fix up the destination world then update the requested package to the destination. if (OwningWorld) { if (DestinationWorld->PersistentLevel) { DestinationWorld->PersistentLevel->OwningWorld = OwningWorld; } // In some cases, BSP render data is not created because the OwningWorld was not set correctly. // Regenerate that render data here DestinationWorld->PersistentLevel->InvalidateModelSurface(); DestinationWorld->PersistentLevel->CommitModelSurfaces(); } WorldAsset = DestinationWorld; } } } } else if (Result == EAsyncLoadingResult::Canceled) { // Cancel level streaming bHasLoadRequestPending = false; bShouldBeLoaded = false; } else { UE_LOG(LogLevelStreaming, Warning, TEXT("Failed to load package '%s'"), *InPackageName.ToString() ); } // Clean up the world type list and owning world list now that PostLoad has occurred UWorld::WorldTypePreLoadMap.Remove(InPackageName); ULevel::StreamedLevelsOwningWorld.Remove(InPackageName); STAT_ADD_CUSTOMMESSAGE_NAME( STAT_NamedMarker, *(FString( TEXT( "RequestLevelComplete - " ) + InPackageName.ToString() )) ); }
bool ULevelStreaming::RequestLevel(UWorld* PersistentWorld, bool bAllowLevelLoadRequests, bool bBlockOnLoad) { // Quit early in case load request already issued if (bHasLoadRequestPending) { return true; } // Previous attempts have failed, no reason to try again if (bFailedToLoad) { return false; } QUICK_SCOPE_CYCLE_COUNTER(STAT_ULevelStreaming_RequestLevel); FScopeCycleCounterUObject Context(PersistentWorld); // Package name we want to load FName DesiredPackageName = PersistentWorld->IsGameWorld() ? GetLODPackageName() : GetWorldAssetPackageFName(); FName DesiredPackageNameToLoad = PersistentWorld->IsGameWorld() ? GetLODPackageNameToLoad() : PackageNameToLoad; // Check if currently loaded level is what we want right now if (LoadedLevel != NULL && LoadedLevel->GetOutermost()->GetFName() == DesiredPackageName) { return true; } // Can not load new level now, there is still level pending unload if (PendingUnloadLevel != NULL) { return false; } // Try to find the [to be] loaded package. UPackage* LevelPackage = (UPackage*) StaticFindObjectFast(UPackage::StaticClass(), NULL, DesiredPackageName, 0, 0, RF_PendingKill); // Package is already or still loaded. if (LevelPackage) { // Find world object and use its PersistentLevel pointer. UWorld* World = UWorld::FindWorldInPackage(LevelPackage); // Check for a redirector. Follow it, if found. if ( !World ) { World = UWorld::FollowWorldRedirectorInPackage(LevelPackage); if ( World ) { LevelPackage = World->GetOutermost(); } } if (World != NULL) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (World->PersistentLevel == NULL) { UE_LOG(LogLevelStreaming, Log, TEXT("World exists but PersistentLevel doesn't for %s, most likely caused by reference to world of unloaded level and GC setting reference to NULL while keeping world object"), *World->GetOutermost()->GetName()); // print out some debug information... StaticExec(World, *FString::Printf(TEXT("OBJ REFS CLASS=WORLD NAME=%s shortest"), *World->GetPathName())); TMap<UObject*,UProperty*> Route = FArchiveTraceRoute::FindShortestRootPath( World, true, GARBAGE_COLLECTION_KEEPFLAGS ); FString ErrorString = FArchiveTraceRoute::PrintRootPath( Route, World ); UE_LOG(LogLevelStreaming, Log, TEXT("%s"), *ErrorString); // before asserting checkf(World->PersistentLevel,TEXT("Most likely caused by reference to world of unloaded level and GC setting reference to NULL while keeping world object")); return false; } #endif if (World->PersistentLevel != LoadedLevel) { SetLoadedLevel(World->PersistentLevel); // Broadcast level loaded event to blueprints OnLevelLoaded.Broadcast(this); } return true; } } EPackageFlags PackageFlags = PKG_None; int32 PIEInstanceID = INDEX_NONE; // copy streaming level on demand if we are in PIE // (the world is already loaded for the editor, just find it and copy it) if ( PersistentWorld->IsPlayInEditor() ) { #if WITH_EDITOR if (PersistentWorld->GetOutermost()->HasAnyPackageFlags(PKG_PlayInEditor)) { PackageFlags |= PKG_PlayInEditor; } PIEInstanceID = PersistentWorld->GetOutermost()->PIEInstanceID; #endif const FString NonPrefixedLevelName = UWorld::StripPIEPrefixFromPackageName(DesiredPackageName.ToString(), PersistentWorld->StreamingLevelsPrefix); UPackage* EditorLevelPackage = FindObjectFast<UPackage>(nullptr, FName(*NonPrefixedLevelName)); bool bShouldDuplicate = EditorLevelPackage && (bBlockOnLoad || EditorLevelPackage->IsDirty() || !GEngine->PreferToStreamLevelsInPIE()); if (bShouldDuplicate) { // Do the duplication UWorld* PIELevelWorld = UWorld::DuplicateWorldForPIE(NonPrefixedLevelName, PersistentWorld); if (PIELevelWorld) { PIELevelWorld->PersistentLevel->bAlreadyMovedActors = true; // As we have duplicated the world, the actors will already have been transformed check(PendingUnloadLevel == NULL); SetLoadedLevel(PIELevelWorld->PersistentLevel); // Broadcast level loaded event to blueprints { QUICK_SCOPE_CYCLE_COUNTER(STAT_OnLevelLoaded_Broadcast); OnLevelLoaded.Broadcast(this); } return true; } else if (PersistentWorld->WorldComposition == NULL) // In world composition streaming levels are not loaded by default { if ( bAllowLevelLoadRequests ) { UE_LOG(LogLevelStreaming, Log, TEXT("World to duplicate for PIE '%s' not found. Attempting load."), *NonPrefixedLevelName); } else { UE_LOG(LogLevelStreaming, Warning, TEXT("Unable to duplicate PIE World: '%s'"), *NonPrefixedLevelName); } } } } // Async load package if world object couldn't be found and we are allowed to request a load. if (bAllowLevelLoadRequests) { FString PackageNameToLoadFrom = DesiredPackageName.ToString(); if (DesiredPackageNameToLoad != NAME_None) { PackageNameToLoadFrom = DesiredPackageNameToLoad.ToString(); } if (GUseSeekFreeLoading) { // Only load localized package if it exists as async package loading doesn't handle errors gracefully. FString LocalizedPackageName = PackageNameToLoadFrom + LOCALIZED_SEEKFREE_SUFFIX; FString LocalizedFileName; if (FPackageName::DoesPackageExist(LocalizedPackageName, NULL, &LocalizedFileName)) { // Load localized part of level first in case it exists. We don't need to worry about GC or completion // callback as we always kick off another async IO for the level below. LoadPackageAsync(*(GetWorldAssetPackageName() + LOCALIZED_SEEKFREE_SUFFIX), nullptr, *LocalizedPackageName, FLoadPackageAsyncDelegate(), PackageFlags, PIEInstanceID); } } if (FPackageName::DoesPackageExist(PackageNameToLoadFrom, NULL, NULL)) { bHasLoadRequestPending = true; ULevel::StreamedLevelsOwningWorld.Add(DesiredPackageName, PersistentWorld); UWorld::WorldTypePreLoadMap.FindOrAdd(DesiredPackageName) = PersistentWorld->WorldType; // Kick off async load request. STAT_ADD_CUSTOMMESSAGE_NAME( STAT_NamedMarker, *(FString( TEXT( "RequestLevel - " ) + DesiredPackageName.ToString() )) ); LoadPackageAsync(DesiredPackageName.ToString(), nullptr, *PackageNameToLoadFrom, FLoadPackageAsyncDelegate::CreateUObject(this, &ULevelStreaming::AsyncLevelLoadComplete), PackageFlags, PIEInstanceID); // streamingServer: server loads everything? // Editor immediately blocks on load and we also block if background level streaming is disabled. if (bBlockOnLoad || ShouldBeAlwaysLoaded()) { // Finish all async loading. FlushAsyncLoading(); } } else { UE_LOG(LogStreaming, Error,TEXT("Couldn't find file for package %s."), *PackageNameToLoadFrom); bFailedToLoad = true; return false; } } return true; }
void AOnlineBeaconHost::NotifyControlMessage(UNetConnection* Connection, uint8 MessageType, class FInBunch& Bunch) { if(NetDriver->ServerConnection == nullptr) { bool bCloseConnection = false; // We are the server. #if !(UE_BUILD_SHIPPING && WITH_EDITOR) UE_LOG(LogBeacon, Verbose, TEXT("%s[%s] Host received: %s"), *GetName(), Connection ? *Connection->GetName() : TEXT("Invalid"), FNetControlMessageInfo::GetName(MessageType)); #endif switch (MessageType) { case NMT_Hello: { UE_LOG(LogBeacon, Log, TEXT("Beacon Hello")); uint8 IsLittleEndian; uint32 RemoteNetworkVersion = 0; uint32 LocalNetworkVersion = FNetworkVersion::GetLocalNetworkVersion(); FNetControlMessage<NMT_Hello>::Receive(Bunch, IsLittleEndian, RemoteNetworkVersion); if (!FNetworkVersion::IsNetworkCompatible(LocalNetworkVersion, RemoteNetworkVersion)) { UE_LOG(LogBeacon, Log, TEXT("Client not network compatible %s"), *Connection->GetName()); FNetControlMessage<NMT_Upgrade>::Send(Connection, LocalNetworkVersion); bCloseConnection = true; } else { Connection->Challenge = FString::Printf(TEXT("%08X"), FPlatformTime::Cycles()); FNetControlMessage<NMT_BeaconWelcome>::Send(Connection); Connection->FlushNet(); } break; } case NMT_Netspeed: { int32 Rate; FNetControlMessage<NMT_Netspeed>::Receive(Bunch, Rate); Connection->CurrentNetSpeed = FMath::Clamp(Rate, 1800, NetDriver->MaxClientRate); UE_LOG(LogBeacon, Log, TEXT("Client netspeed is %i"), Connection->CurrentNetSpeed); break; } case NMT_BeaconJoin: { FString ErrorMsg; FString BeaconType; FUniqueNetIdRepl UniqueId; FNetControlMessage<NMT_BeaconJoin>::Receive(Bunch, BeaconType, UniqueId); UE_LOG(LogBeacon, Log, TEXT("Beacon Join %s %s"), *BeaconType, *UniqueId.ToDebugString()); if (Connection->ClientWorldPackageName == NAME_None) { AOnlineBeaconClient* ClientActor = GetClientActor(Connection); if (ClientActor == nullptr) { UWorld* World = GetWorld(); Connection->ClientWorldPackageName = World->GetOutermost()->GetFName(); AOnlineBeaconClient* NewClientActor = nullptr; FOnBeaconSpawned* OnBeaconSpawnedDelegate = OnBeaconSpawnedMapping.Find(BeaconType); if (OnBeaconSpawnedDelegate && OnBeaconSpawnedDelegate->IsBound()) { NewClientActor = OnBeaconSpawnedDelegate->Execute(Connection); } if (NewClientActor && BeaconType == NewClientActor->GetBeaconType()) { NewClientActor->SetConnectionState(EBeaconConnectionState::Pending); FNetworkGUID NetGUID = Connection->Driver->GuidCache->AssignNewNetGUID_Server(NewClientActor); NewClientActor->SetNetConnection(Connection); Connection->PlayerId = UniqueId; Connection->OwningActor = NewClientActor; NewClientActor->Role = ROLE_Authority; NewClientActor->SetReplicates(false); check(NetDriverName == NetDriver->NetDriverName); NewClientActor->SetNetDriverName(NetDriverName); ClientActors.Add(NewClientActor); FNetControlMessage<NMT_BeaconAssignGUID>::Send(Connection, NetGUID); } else { ErrorMsg = NSLOCTEXT("NetworkErrors", "BeaconSpawnFailureError", "Join failure, Couldn't spawn beacon.").ToString(); } } else { ErrorMsg = NSLOCTEXT("NetworkErrors", "BeaconSpawnExistingActorError", "Join failure, existing beacon actor.").ToString(); } } else { ErrorMsg = NSLOCTEXT("NetworkErrors", "BeaconSpawnClientWorldPackageNameError", "Join failure, existing ClientWorldPackageName.").ToString(); } if (!ErrorMsg.IsEmpty()) { UE_LOG(LogBeacon, Log, TEXT("%s: %s"), *Connection->GetName(), *ErrorMsg); FNetControlMessage<NMT_Failure>::Send(Connection, ErrorMsg); bCloseConnection = true; } break; } case NMT_BeaconNetGUIDAck: { FString ErrorMsg; FString BeaconType; FNetControlMessage<NMT_BeaconNetGUIDAck>::Receive(Bunch, BeaconType); AOnlineBeaconClient* ClientActor = GetClientActor(Connection); if (ClientActor && BeaconType == ClientActor->GetBeaconType()) { FOnBeaconConnected* OnBeaconConnectedDelegate = OnBeaconConnectedMapping.Find(BeaconType); if (OnBeaconConnectedDelegate) { ClientActor->SetReplicates(true); ClientActor->SetAutonomousProxy(true); ClientActor->SetConnectionState(EBeaconConnectionState::Open); // Send an RPC to the client to open the actor channel and guarantee RPCs will work ClientActor->ClientOnConnected(); UE_LOG(LogBeacon, Log, TEXT("Handshake complete for %s!"), *ClientActor->GetName()); OnBeaconConnectedDelegate->ExecuteIfBound(ClientActor, Connection); } else { // Failed to connect. ErrorMsg = NSLOCTEXT("NetworkErrors", "BeaconSpawnNetGUIDAckError1", "Join failure, no host object at NetGUIDAck.").ToString(); } } else { // Failed to connect. ErrorMsg = NSLOCTEXT("NetworkErrors", "BeaconSpawnNetGUIDAckError2", "Join failure, no actor at NetGUIDAck.").ToString(); } if (!ErrorMsg.IsEmpty()) { UE_LOG(LogBeacon, Log, TEXT("%s: %s"), *Connection->GetName(), *ErrorMsg); FNetControlMessage<NMT_Failure>::Send(Connection, ErrorMsg); bCloseConnection = true; } break; } case NMT_BeaconWelcome: case NMT_BeaconAssignGUID: default: { FString ErrorMsg = NSLOCTEXT("NetworkErrors", "BeaconSpawnUnexpectedError", "Join failure, unexpected control message.").ToString(); UE_LOG(LogBeacon, Log, TEXT("%s: %s"), *Connection->GetName(), *ErrorMsg); FNetControlMessage<NMT_Failure>::Send(Connection, ErrorMsg); bCloseConnection = true; } break; } if (bCloseConnection) { UE_LOG(LogBeacon, Verbose, TEXT("Closing connection %s: %s"), *Connection->GetName(), *Connection->PlayerId.ToDebugString()); AOnlineBeaconClient* ClientActor = GetClientActor(Connection); if (ClientActor) { UE_LOG(LogBeacon, Verbose, TEXT("- BeaconActor: %s %s"), *ClientActor->GetName(), *ClientActor->GetBeaconType()); AOnlineBeaconHostObject* BeaconHostObject = GetHost(ClientActor->GetBeaconType()); if (BeaconHostObject) { UE_LOG(LogBeacon, Verbose, TEXT("- HostObject: %s"), *BeaconHostObject->GetName()); BeaconHostObject->NotifyClientDisconnected(ClientActor); } RemoveClientActor(ClientActor); } Connection->FlushNet(true); Connection->Close(); UE_LOG(LogBeacon, Verbose, TEXT("--------------------------------")); } } }