Description

Other than the presence of the error and the performance cost of looking for a package on disk that does not exist, there are no behavioral problems.

When running with legacy (non-iostore) pakfiles, and loading a map on the server that has a BlueprintGeneratedClass with a SceneComponent of a type in a module that is excluded from server, the /Script/<ModuleName> for that module is still referenced as a hard import and will attempt to be loaded and will fail. It emits the error:

LogStreaming: Error: Couldn't find file for package /Script/Text3D requested by async loading code. NameToLoad: /Script/Text3D
LogStreaming: Error: Found 1 dependent packages...
LogStreaming: Error:   <PathToBlueprintWithTheComponent>
LogStreaming, Error: This will hitch streaming because it ends up searching the disk instead of finding the file in the pak file.

The import of the script package comes from the FObjectProperty for the SceneComponent; FObjectPropertyBase has a PropertyClass pointer that points to the SceneComponent's class. Even when the SceneComponent instance is stripped from the exports by ClassesExcludedOnDedicatedServer, the class is not stripped from the imports even though it is in a package that is stripped from deny-listed from server configurations via "TargetDenyList": [ "Server" ].

We should do one of:
1. Strip the imports of classes in TargetDenyList Modules from SavePackage
2. Add something in AsyncLoading to avoid attempting to load the missing script package.
3. Suppress the error.

(1) seems like the best solution for performance.

Implementation note:
This problem does not occur for classes that have been marked as IsEditorOnly. We should try to use the same solution for IsEditorOnly to handle classes of objects that are excluded due to TargetDenyList.

Callstack for serialization of the FObjectPropertyBase's PropertyClass pointer:

UActorComponent::IsEditorOnly() Line 584	C++
UActorComponent::NeedsLoadForClient() Line 843	C++
UBlueprintGeneratedClass::NeedsLoadForClient() Line 2112	C++
GenerateMarksForObject(const UObject * InObject, FSaveContext & SaveContext) Line 29	C++
ConditionallyExcludeObjectForRealm(FSaveContext & SaveContext, TObjectPtr<UObject> Obj, ESaveRealm HarvestingContext) Line 106	C++
FPackageHarvester::EnterNotExcludedScope::__l2::<lambda_1>::operator()(ESaveRealm HarvestingRealm) Line 337	C++
TArray<enum ESaveRealm,TSizedInlineAllocator<2,32,TSizedDefaultAllocator<32>>>::RemoveAllSwap<`FPackageHarvester::EnterNotExcludedScope'::`2'::<lambda_1>>(const FPackageHarvester::EnterNotExcludedScope::__l2::<lambda_1> & Predicate, EAllowShrinking AllowShrinking) Line 2761	C++
FPackageHarvester::EnterNotExcludedScope(TObjectPtr<UObject> Object) Line 338	C++
FPackageHarvester::TryHarvestImport(TObjectPtr<UObject> InObject) Line 693	C++
FPackageHarvester::operator<<(UObject * & Obj) Line 856	C++
FArchiveUObject::SerializeObjectPtr(FArchive & Ar, FObjectPtr & Value) Line 91	C++
ObjectPtr_Private::Friend::Serialize(FArchive &) Line 761	C++
operator<<(FArchive &) Line 839	C++
FObjectPropertyBase::Serialize(FArchive & Ar) Line 147	C++
UStruct::SerializeProperties(FArchive & Ar) Line 2195	C++
UStruct::Serialize(FArchive & Ar) Line 2290	C++
UClass::Serialize(FArchive & Ar) Line 5548	C++
UBlueprintGeneratedClass::Serialize(FArchive & Ar) Line 2530	C++

In the IsEditorOnly=true case, UActorComponent::IsEditorOnly returns true, and suppresses the import of the class despite its reference from the FObjectPropertyBase.

Steps to Reproduce

Create a blueprint actor with a component from a module that is excluded on the server. Make the component class also excluded on the server. Load the actor in runtime. In this example we use Text3DComponent from Engine\Plugins\Experimental\Text3D.

1. Run Lyra Editor, Edit -> Plugins, select Text3D plugin. Shutdown editor, recompile editor, and launch again.
2. Create a Blueprint of Type Actor, open in the blueprint editor.
3. Add a SceneComponent of type Text3DComponent to the Actor Blueprint.
4. Open the map /ShooterMaps/Maps/L_Convolution_Blockout. Make all WorldPartition Actors visible from the WorldPartition tab on the right.
5. Drag the Actor with Text3DComponent into the map, place it anywhere.
6. Save All, close the editor.
7. Edit BaseEngine.ini, section [/Script/UnrealEd.CookerSettings[, add +ClassesExcludedOnDedicatedServer=Text3DComponent
8. Change Lyra to packag WindowsServer for Server config by default: edit Lyra\Config\DefaultEngine.ini, section [/Script/BuildSettings.BuildSettings], add key DefaultServerTarget=LyraServer.
9. Package Lyra for Server, without iostore. (Replace <UNREALENGINEROOT> with your root (e.g. D:/root1) in the commandline below

RunUAT.bat  -ScriptsForProject=<UNREALENGINEROOT>/Samples/Games/Lyra/Lyra.uproject BuildCookRun -nop4 -project=<UNREALENGINEROOT>/Samples/Games/Lyra/Lyra.uproject -stage -archive -archivedirectory=D:/scratch/Lyra -pak -package -prereqs -targetplatform=Win64 -build -serverconfig=Development -utf8output -compile -cook -AdditionalCookerOptions="-cookcultures=en -skipzenstore" -noclient -server -skipiostore

10. Launch the server from the staged location with no arguments (or with argument -log to see a log window). It will load L_Convolution_Blockout by default and will emit the error.

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-208069 in the post.

2
Login to Vote

Unresolved
ComponentUE - Foundation
Affects Versions5.3
Target Fix5.6
CreatedFeb 25, 2024
UpdatedOct 18, 2024
View Jira Issue