UObject FObjectReader and FObjectWriter

Got stuck on a feature for my plugin “MultiEdit” to synchronize components of actors between editors. For example if one user changes the light color to blue, the other editor on a different machine will update that component, so that light will become blue as well.

I am using FObjectReader and FObjectWriter and my reference is the code in DiffPackagesCommendlet.cpp at line 352/360 (I am using UE4.6) in the function CopyObjectToPackage. I tried this locally (in one editor) and that worked fine.

TArray<uint8> Bytes;
FObjectWriter(Object, Bytes);

// make a new object
UObject* NewObject = StaticConstructObject(Object->GetClass(), NewOuter, Object->GetFName(), Object->GetFlags(), Object->GetArchetype(), true);

// serialize old objects on top of the new object
FObjectReader Reader(NewObject, Bytes);

Here is my code:

//Message created, initialized and send by an editor
bool FMessageUpdateComponent::Serialize(MEMessageHandler* MessageHandler, AActor* Actor)
{
	ID = ID_UPDATECOMPONENT;

	Object = Actor->GetRootComponent();
	ObjectName = Object->GetFName();
	ObjectClassName = Object->GetClass()->GetName();
	ActorID = MessageHandler->GetIDFromActor(Actor);

	// serialize out the original object
	FObjectWriter(Object, ObjectBytes);
	
	return FMessage::Serialize(MessageHandler);
}
//The serializing and deserializing of the message
void FMessageUpdateComponent::SaveLoad(FArchive& Archive)
{
	//TArray<uint8> The component in binary 
	Archive << ObjectBytes;		
	//The ID of the actor that owns that component
	Archive << ActorID;	
	//The class name of the component		
	Archive << ObjectClassName;
	//The name of the component
	Archive << ObjectName;		
}

//The handle for when an editor received the message to update a component
bool FMessageUpdateComponent::Handle(FMemoryReader& Reader, MEMessageHandler* MessageHandler, FSocket* Sender)
{
	Deserialize(Reader);
	
	if (ObjectClassName.Equals("BrushComponent")) return false;
	
	//Find the class from the name
	UClass* const ObjectClass = FindObject<UClass>(ANY_PACKAGE, *ObjectClassName);
	
	//Make a new object (things I tried to create an UObject)
	//UObject* CreatedObject = StaticConstructObject(ObjectClass, (UObject*)GetTransientPackage(), ObjectName, RF_NoFlags, (UObject*)0, true);
	//UObject* CreatedObject = ConstructObject<UObject>(ObjectClass, (UObject*)GetTransientPackage(), ObjectName, RF_NoFlags);
	
	UObject* CreatedObject = NewObject<UObject>((UObject*)GetTransientPackage(), ObjectClass);
	FObjectReader ObjectReader(CreatedObject, ObjectBytes); //BREAKS HERE!
	
	if (Object)
	{
		//TODO update the component of the actor here
	}

	return true;
}

Main differences from the reference:

  • The binary TArray created by the FObjectWriter is not local in one function but it is send (with FSocket) to another editor that receives the message.
  • I don’t use the StaticConstructObject like the reference does, because this creates an instance.

Notes:

  • I debugged the TArray (ObjectBytes) and it appears to be the same before sending it in one editor and after receiving it in the other editor. So the data is not corrupted or something.
  • I tested the above mentioned case with this technique in one editor and it worked.

Currently it crashes on FObjectReader(CreatedObject, ObjectBytes)

Many thanks in advance!

Hi -

What is the crash that you’re seeing in the reader?

There are some other references of using the FObjectWriter / Reader pair that might also be useful. In UEngine::CopyPropertiesForUnrelatedObjects(), we do the copying for an actor, along with lots of it’s sub components. You might be running into issues there with subcomponents, so that might be a good thing to check first. If the components between your two instances don’t perfectly match, you may catch some asserts. In the CPFUO function, we serialize out actor components individually to do the mapping more accurately.

Thanks for your reply! I found FindField because of your answer, so I will be using that. I could maybe optimize the networking data if I can notify when a certain property in the detail window is changed. Do you know if any function like this exists?

There is a delegate that gets fired when a property node has changed…SSingleProperty::OnPropertyValueChanged . That might be a good place to hook!