UE5 Editor crash on startup
Hi,
I have just ported the plugin to UE5 to test it with my project. After some trivial fixes I have faced an editor startup crash caused by FPsNetworkEvent , more exactly in the void operator<<(FStructuredArchive::FSlot Slot, FPsNetworkEvent& Value) it is caused by the 2 lines of:
Slot << Value.Path;
Slot << Value.Data;
they are resulting in errors in FStructuredArchive after void FStructuredArchiveSlot::operator<< (FString& Value) and void FStructuredArchiveSlot::Serialize(TArray<uint8>& Data) respectively:
LogWindows: Error: === Critical error: ===
LogWindows: Error:
LogWindows: Error: Assertion failed: ElementId == CurrentSlotElementId [File:D:\build\++UE5\Sync\Engine\Source\Runtime\Core\Private\Serialization\StructuredArchive.cpp] [Line: 80]
LogWindows: Error: Attempt to serialize data into an invalid slot
LogWindows: Error:
LogWindows: Error:
LogWindows: Error: [Callstack] 0x00007ff9a8ccaa6a UnrealEditor-Core.dll!FStructuredArchive::EnterSlot() [D:\build\++UE5\Sync\Engine\Source\Runtime\Core\Private\Serialization\StructuredArchive.cpp:80]
LogWindows: Error: [Callstack] 0x00007ff9a8caaa2f UnrealEditor-Core.dll!FStructuredArchiveSlot::operator<<() [D:\build\++UE5\Sync\Engine\Source\Runtime\Core\Private\Serialization\StructuredArchiveSlots.cpp:245]
LogWindows: Error: [Callstack] 0x00007ff9510a24ce UnrealEditor-PsData.dll!UScriptStruct::TCppStructOps<FPsNetworkEvent>::Serialize() [D:\ProgramFiles\Epic Games\UE_5.4\Engine\Source\Runtime\CoreUObject\Public\UObject\Class.h:1295]
LogWindows: Error: [Callstack] 0x00007ff9a7ecf214 UnrealEditor-CoreUObject.dll!UScriptStruct::SerializeItem() [D:\build\++UE5\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\Class.cpp:2985]
There have been some engine changes, and I'm really got stuck with it. Thanks.
I've just pushed a UE5 integration to the develop branch. Let me know if this problem persists on the latest commit The commit in question: https://github.com/PushkinStudio/PsData/commit/1681eb263d16ce08c8eb9566fd8ae6096d0bd7d3
wow, thank you for the lightning fast commit! it works now.
and is there any short docs or examples over the readme.md? I mean simple how-to-start examples of handling properties and RPCs, it would be quite handy.
Sorry, despite using this plugin for many years I don't know many of its features (for example, any networking features). I also don't have better examples or example projects or any sort of documentation. Maybe I'll add some when I have more free time.
Personally, I use PsData for events, links, and for json serialization, so at least I can show how to do that. btw I wrote these examples from memory, so I don't guarantee any of this code will compile
/**
* The root of your object tree must be a child of UPsDataRoot
*/
UCLASS(Blueprintable, BlueprintType)
class UMyRootData : public UPsDataRoot
{
GENERATED_BODY()
/** User state. Contains player's state and other mutable state
* I don't remember what Strict does
*/
DMETA(Strict)
DPROP_CONST(UMyUserData*, UserData);
/** Config data. Contains configs and other immutable state (stuff from datatables and dataassets and such) */
DMETA(Strict)
DPROP_CONST(UMyConfigData*, ConfigData);
};
/**
* Mission config. Each object contains config for a single mission
*/
UCLASS(Blueprintable, BlueprintType)
class UMyMissionConfigData : public UPsData
{
GENERATED_BODY()
/** How much money is received for completing this mission */
DPROP(int32, RewardMoney);
/** Name of this mission */
DPROP(FText, NameText);
/** This macro generates a struct that can be used for datatables.
* The generated struct will contain the same properties as this object
*/
MAKE_TABLE_STRUCT();
};
/**
* Configs
*/
UCLASS(Blueprintable, BlueprintType)
class UMyConfigData : public UPsData
{
GENERATED_BODY()
/** Mission configs */
DMAP(UMyMissionConfigData*, Missions);
};
/**
* User state
*/
UCLASS(Blueprintable, BlueprintType)
class UMyUserData : public UPsData
{
GENERATED_BODY()
/** Player's money. DMETA(Event) allows us to Bind to this property (examples below) */
DMETA(Event)
DPROP(int32, Money);
/** Currently active missions. It is a key in a map UMyConfigData::Missions
* Tag `Nullable` means this value can be set to an empty string
* `ConfigData.Missions` is a path to that map: RootData - ConfigData - Missions
*/
DMETA(Event, Nullable)
DPROP(FString, CurrentMission);
DLINK(UMyMissionConfigData*, CompletedMissions, ConfigData.Missions);
/** Completed missions. Each element is a key in a map UMyConfigData::Missions */
DMETA(Event)
DARRAY(FString, CompletedMissions);
DLINK(UMyMissionConfigData*, CompletedMissions, ConfigData.Missions);
};
// Initialize UMyRootData
{
auto RootData = NewObject<UMyRootData>(this);
// RootData->UserData is initialized because of the Strict tag
// Initialize RootData->ConfigData from a datatable
// MissionsDataTable is a datable that uses a struct type generated with MAKE_TABLE_STRUCT in the UMyMissionConfigData class
auto Deserializer = FPsDataTableDeserializer(MissionsDataTable, RootData->ConfigData->MissionConfigs);
WorldData->DataDeserialize(&Deserializer, true);
}
// Bind to a property
{
// You can also do this in blueprints btw
RootData->UserData->Money.Bind(FPsDataDelegate::CreateLambda(
[RootData](UPsDataEvent* Event) {
UE_LOG(LogTemp, Log, TEXT("Money has changed. Current value: %d"), RootData->UserData->Money.Get());
}));
RootData->UserData->Money = 999;
// `Money has changed. Current value: 999` will be printed to log
}
// Get objects from a map by using "links"
{
// MissionConfigData will contain a value from teh map RootData->ConfigData->Missions
// This is the same as calling RootData->ConfigData->Missions[RootData->UserData->CurrentMission]
const auto MissionConfigData = RootData->UserData->LinkByCurrentMission;
// CompletedMissions will contain values from the map RootData->ConfigData->Missions
// This is the same as calling RootData->ConfigData->Missions[Key] for each Key in RootData->UserData->CompletedMissions
const auto CompletedMissions = RootData->UserData->LinkByCompletedMissions;
}
// Serialize objects to json
{
FPsDataJsonSerializer Serializer;
RootData->DataSerialize(&Serializer);
const auto ResultJson = WorldDataSerializer.GetJson();
// now ResultJson is a serialized RootData object
}
Thank, you! Now I think I can start with it! Apparently, the Event binding is the most important for me, to imitate receiving an RPC or an OnRep. And now I see I need one RootData that holds all the other data objects...
I do not really need complex data structures, I just want to optimize in my personal RTS game project the soldier movement replication (max 1000 soldiers), currently solved by sending movement vectors, and a few other parameters, when they change. It works so far only with UE properties and RPCs through EOS if using a 4G mobile net, but they need a large bandwidth comparing to the VR games I'm developing at job, so better to optimize my systems... So probably I try to replace a few functionalities with PsData solutions beside keeping some UE replicated ones.