Actor Sync Quick Reference
March 11, 2026 ยท View on GitHub
Quick lookup for common actor sync tasks and troubleshooting.
Quick Start
Enable Actor Sync for Your Actor Class
- Implement IDefinitionApplicable interface:
// YourActor.h
#include "IDefinitionApplicable.h"
UCLASS()
class AYourActor : public AProjectWorldActor, public IDefinitionApplicable
{
GENERATED_BODY()
public:
virtual bool ApplyDefinition_Implementation(UPrimaryDataAsset* Definition) override;
};
- Implement ApplyDefinition method:
// YourActor.cpp
bool AYourActor::ApplyDefinition_Implementation(UPrimaryDataAsset* Definition)
{
// Cast to your definition type
UYourDefinition* YourDef = Cast<UYourDefinition>(Definition);
if (!YourDef)
{
return false; // Wrong definition type
}
// Structure hash check (safety)
if (!AppliedStructureHash.IsEmpty() && AppliedStructureHash != YourDef->DefinitionStructureHash)
{
return false; // Structure mismatch - needs Replace
}
// Content hash check (idempotency)
if (!AppliedContentHash.IsEmpty() && AppliedContentHash == YourDef->DefinitionContentHash)
{
return true; // Already up to date - no work needed
}
// Mark for undo
Modify();
// Update properties
YourProperty1 = YourDef->Property1;
YourProperty2 = YourDef->Property2;
// Update meshes (if applicable)
// Update capabilities (if applicable - currently TODO for InteractableActor)
// Store hashes
AppliedStructureHash = YourDef->DefinitionStructureHash;
AppliedContentHash = YourDef->DefinitionContentHash;
return true;
}
- Set ObjectDefinitionId when placing actor:
// In factory or placement code
Actor->ObjectDefinitionId = Definition->GetPrimaryAssetId();
Common Tasks
Force Update All Actors from Definition
Editor:
- Modify definition asset
- Save definition (Ctrl+S)
- Wait 5 seconds (or click "Apply Now")
- All actors update automatically
Commandlet:
UnrealEditor-Cmd.exe Alis.uproject -run=WorldPartitionResaveActorsBuilder -AllowCommandletRendering
Manually Update Single Actor
Option 1: Trigger Mode 0 (definition save)
- Open definition asset
- Make any change (add space, remove space)
- Save (Ctrl+S)
- Click "Apply Now"
Option 2: Delete and re-place
- Delete actor
- Place new instance from Content Browser
Check if Actor Will Update
Prerequisites:
- Actor has valid
ObjectDefinitionId - Actor implements
IDefinitionApplicable - Definition asset exists and is loaded
Log check:
// Enable verbose logging
LogDefinitionActorSync
LogProjectWorldActor
Manual check:
// In actor's ApplyDefinition implementation
UE_LOG(LogYourActor, Log, TEXT("ApplyDefinition called for: %s"), *GetActorLabel());
UE_LOG(LogYourActor, Log, TEXT(" Structure Hash: %s -> %s"), *AppliedStructureHash, *YourDef->DefinitionStructureHash);
UE_LOG(LogYourActor, Log, TEXT(" Content Hash: %s -> %s"), *AppliedContentHash, *YourDef->DefinitionContentHash);
Troubleshooting Checklist
Actor Not Updating (Mode 0 - Manual)
- Definition saved in editor? (Ctrl+S triggers update)
- Countdown appeared? (check notification in bottom-right)
- Actor visible in level? (not in unloaded World Partition cell)
- Actor has
ObjectDefinitionIdset? (check Details panel) - Actor implements
IDefinitionApplicable? (check class declaration) -
ApplyDefinitionreturning true? (check log:[ActorSync] Appliedvs[ActorSync] Replacing)
Actor Not Updating (Mode 1 - World Partition Load)
- World Partition enabled? (Project Settings -> World Partition)
- Actor in external package? (not in persistent level)
- Level hooks subscribed? (log:
Subscribed to ULevel::OnLoadedActorAddedToLevelPostEvent) - Content hash different? (log:
[Mode1] Actor X needs update: old -> new) - Apply succeeded? (log:
[Mode1] Applied: Xvs warning notification)
Actor Not Updating (Mode 2 - Commandlet)
- Commandlet run? (
WorldPartitionResaveActorsBuilder) - PreSave guards passed? (ObjectDefinitionId valid, not procedural save, implements interface)
- Definition loaded? (check:
AssetManager.GetPrimaryAssetPath(ObjectDefinitionId)) - Apply succeeded? (log:
[Mode2] Appliedvs[Mode2] Failed)
Structure Mismatch Warnings
Symptom:
LogProjectWorldActor: Warning: [Mode2] Failed to apply definition to: 'Actor_X' (structure mismatch or error)
Cause: Definition structure changed (added/removed UPROPERTY, changed types).
Fix:
- Mode 0: Auto Replace (respawn actor) - no action needed
- Mode 1/2: Manual fix required:
- Open Message Log (Window -> Developer Tools -> Message Log)
- Click actor link to select
- Delete actor
- Place new instance from Content Browser
Prevention:
- Keep definition structures stable
- Use optional properties (add new fields with defaults)
- Use Blueprint properties instead of C++ UPROPERTY
Content Hash Not Updating
Symptom: Actor properties don't match definition, but no update triggered.
Cause:
DefinitionContentHash not computed or not changed.
Fix:
- Check definition generator computed hash:
UE_LOG(LogTemp, Log, TEXT("Definition Content Hash: %s"), *YourDef->DefinitionContentHash); - Verify definition saved after hash update
- Manually trigger regeneration (if needed)
Editor Crashes on Startup
Symptom:
Crash in UEditorEngine::GetEditorWorldContext during subsystem init.
Cause: Trying to access world context before it's ready.
Fix:
Already fixed in current implementation - we use FWorldDelegates::OnPostWorldCreation to defer level hook setup.
If crash persists:
- Check
UDefinitionActorSyncSubsystem::Initialize()doesn't accessGEditor->GetEditorWorldContext().World()directly - Verify
OnWorldCreated()filters forEWorldType::Editor
Performance Tips
Optimize Mode 0 (Manual Updates)
Problem: Countdown blocks designer workflow.
Solutions:
- Reduce countdown time (change
CountdownSecondsconstant) - Add "Skip Countdown" user setting
- Batch multiple definition saves into single update
Optimize Mode 1 (World Partition Load)
Problem: Frame hitches when loading actors.
Solutions:
- Use async definition loading (
StreamableManager.RequestAsyncLoad()) - Defer updates to next frame (accumulate batch)
- Early return on content hash match (already implemented)
Optimize Mode 2 (Commandlet)
Problem: Commandlet takes too long.
Solutions:
- Run commandlet with
-NoShaderCompileflag - Filter actors by definition type (skip unrelated actors)
- Parallelize with multiple commandlet instances (World Partition cells)
Log Analysis
Enable Relevant Log Categories
# DefaultEngine.ini or console command
[Core.Log]
LogDefinitionActorSync=Verbose
LogProjectWorldActor=Verbose
Console commands:
Log LogDefinitionActorSync Verbose
Log LogProjectWorldActor Verbose
Key Log Messages
Subsystem initialization:
LogDefinitionActorSync: Subscribed to FDefinitionEvents::OnDefinitionRegenerated
LogDefinitionActorSync: Subscribed to FWorldDelegates::OnPostWorldCreation (Mode 1)
LogDefinitionActorSync: Subscribed to ULevel::OnLoadedActorAddedToLevelPostEvent (Mode 1) for world: Kazan_MainCity
Mode 0 countdown:
LogDefinitionActorSync: [ActorSync] Found 12 actors to update - showing countdown
LogDefinitionActorSync: [ActorSync] Executing update for 12 actors
LogDefinitionActorSync: [ActorSync] Applied: Door_Instance_01
LogDefinitionActorSync: [ActorSync] Replacing: Door_Instance_02
LogDefinitionActorSync: [ActorSync] === Update complete ===
LogDefinitionActorSync: Reapplied: 10 actors
LogDefinitionActorSync: Replaced: 2 actors
LogDefinitionActorSync: Time: 45.23ms
Mode 1 passive:
LogDefinitionActorSync: [Mode1] Actor Door_Instance_03 needs update: 1a2b3c4d -> 5e6f7g8h
LogDefinitionActorSync: [Mode1] Applied: Door_Instance_03
Mode 2 batch:
LogProjectWorldActor: [Mode2] Applied definition to: Door_Instance_04
LogProjectWorldActor: Warning: [Mode2] Failed to apply definition to: Chest_Instance_01 (structure mismatch or error)
API Reference
IDefinitionApplicable Interface
UINTERFACE(BlueprintType)
class PROJECTWORLD_API UDefinitionApplicable : public UInterface
{
GENERATED_BODY()
};
class PROJECTWORLD_API IDefinitionApplicable
{
GENERATED_BODY()
public:
/**
* Apply definition to actor (update properties in-place).
*
* @param Definition The definition to apply (cast to your definition type)
* @return true if applied successfully, false if structure mismatch or error
*/
UFUNCTION(BlueprintNativeEvent, Category = "Definition")
bool ApplyDefinition(UPrimaryDataAsset* Definition);
};
AProjectWorldActor Properties
// Stored on actor
UPROPERTY(VisibleAnywhere, Category = "Definition")
FPrimaryAssetId ObjectDefinitionId; // Link to definition
UPROPERTY(VisibleAnywhere, Category = "Definition")
FString AppliedStructureHash; // Last applied structure hash (safety)
UPROPERTY(VisibleAnywhere, Category = "Definition")
FString AppliedContentHash; // Last applied content hash (idempotency)
UDefinitionActorSyncSubsystem Methods
// Public (exposed to subsystem users)
void OnDefinitionRegenerated(const FString& TypeName, UObject* RegeneratedAsset);
// Internal (subsystem implementation)
void OnWorldCreated(UWorld* World);
void OnActorsLoadedIntoLevel(const TArray<AActor*>& Actors);
UPrimaryDataAsset* LoadDefinition(const FPrimaryAssetId& DefinitionId);
bool ApplyDefinitionToActor(AProjectWorldActor* Actor, UPrimaryDataAsset* Def);
AActor* ReplaceActorFromDefinition(AActor* OldActor, UPrimaryDataAsset* Def);
Testing Checklist
Manual Testing (Mode 0)
- Place actor from definition
- Modify definition (change property value)
- Save definition (Ctrl+S)
- Verify countdown appears (5 seconds)
- Verify "Apply Now" button works (instant update)
- Verify "Cancel" button works (aborts update)
- Verify actor properties updated
- Verify undo works (Ctrl+Z)
Manual Testing (Mode 1)
- Enable World Partition
- Place actor in external package (WP cell)
- Unload cell (navigate away)
- Modify definition
- Save definition
- Load cell (navigate back)
- Verify actor updated silently (no countdown)
- Check log:
[Mode1] Applied: X
Manual Testing (Mode 2)
- Place actor
- Modify definition
- Save definition
- Run commandlet:
UnrealEditor-Cmd.exe Alis.uproject -run=WorldPartitionResaveActorsBuilder - Check log:
[Mode2] Applied definition to: X - Open level in editor
- Verify actor properties updated
Automated Testing (Unit Tests)
// Example unit test structure
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FActorSyncApplyTest,
"Alis.ActorSync.Apply",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FActorSyncApplyTest::RunTest(const FString& Parameters)
{
// Setup: Create definition and actor
UYourDefinition* Def = NewObject<UYourDefinition>();
Def->Property1 = 123;
AYourActor* Actor = GetWorld()->SpawnActor<AYourActor>();
Actor->ObjectDefinitionId = Def->GetPrimaryAssetId();
// Execute: Apply definition
bool bSuccess = Actor->ApplyDefinition(Def);
// Verify: Properties updated
TestTrue("ApplyDefinition succeeded", bSuccess);
TestEqual("Property1 updated", Actor->YourProperty1, 123);
return true;
}
Known Limitations
The system has three known limitations:
- Capability updates not implemented - Mesh updates work, capability changes require Replace/manual fix
- Mode 1 ObjectDefinition-specific - ItemDefinition actors don't get fast-skip (~0.1ms overhead per actor)
- ItemDefinition Replace not implemented - Manual fix required for structural changes
For detailed explanations, workarounds, and implementation plans, see TODO.md.
Quick workarounds:
- Capability changes: Mode 0 auto Replace, Mode 1/2 manual fix
- ItemDefinition structural changes: Delete + re-place from Content Browser
Related Files
Documentation
- ActorSyncArchitecture.md - Full architecture documentation
- TODO.md - Outstanding tasks, known limitations, implementation plans