From 8ccfa02c2d16b4a3ac23999d97b83da8d767e0f1 Mon Sep 17 00:00:00 2001 From: Lukas Marek Date: Mon, 29 Jun 2026 15:35:20 +0200 Subject: [PATCH] Add Unreal Engine 5.8 support UE 5.8 changed two engine APIs the plugin depended on: 1. FJsonObject keys are now UE::FSharedString (FJsonObject::FStringType) instead of FString. Iterating Values yields shared-string keys and the field interface takes FStringView, so code assuming FString keys no longer compiles. (The UE_JSONOBJECT_LEGACY_STRING_KEYS=1 escape hatch changes FJsonObject's layout and is unsafe to define only in a plugin against an installed engine, so keys are handled properly instead.) 2. StaticEnum() is deleted for namespaced UENUMs such as ESIOConnectionCloseReason, breaking UEnum::GetValueAsString(). Changes are engine-compatibility only; no behavioural or public API changes. - uplugin: EngineVersion -> 5.8, VersionName -> 2.12.0 - SIOJConvert: ToFStringKeyedMap() helper for the bundled converter copy; FJsonObjectWrapper round-trip via SetField; FString(*Key) in key trim/replace - SIOJsonObject::GetFieldNames: copy shared-string keys into FString array - SIOJRequestJSON / SIOMessageConvert: build FString keys from shared-string keys - SocketIONative: map ESIOConnectionCloseReason to a string for the disconnect log --- SocketIOClient.uplugin | 4 +- .../Private/CUBlueprintLibrary.cpp | 4 +- Source/SIOJson/Private/SIOJConvert.cpp | 42 ++++++++++++++----- Source/SIOJson/Private/SIOJRequestJSON.cpp | 4 +- Source/SIOJson/Private/SIOJsonObject.cpp | 9 +++- .../Private/SIOMessageConvert.cpp | 6 +-- .../SocketIOClient/Private/SocketIONative.cpp | 4 +- 7 files changed, 50 insertions(+), 23 deletions(-) diff --git a/SocketIOClient.uplugin b/SocketIOClient.uplugin index 2334cd4..92c6906 100644 --- a/SocketIOClient.uplugin +++ b/SocketIOClient.uplugin @@ -1,8 +1,8 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "2.11.2", - "EngineVersion": "5.7", + "VersionName": "2.12.0", + "EngineVersion": "5.8", "FriendlyName": "Socket.IO Client", "Description": "Real-time WebSocket networking via Socket.IO protocol usable from blueprints and c++.", "Category": "Networking", diff --git a/Source/CoreUtility/Private/CUBlueprintLibrary.cpp b/Source/CoreUtility/Private/CUBlueprintLibrary.cpp index 953b065..1a7c771 100644 --- a/Source/CoreUtility/Private/CUBlueprintLibrary.cpp +++ b/Source/CoreUtility/Private/CUBlueprintLibrary.cpp @@ -96,7 +96,7 @@ UTexture2D* UCUBlueprintLibrary::Conv_BytesToTexture(const TArray& InByte ENQUEUE_RENDER_COMMAND(BytesToTextureCommand)( [UpdateData](FRHICommandList& CommandList) { - RHIUpdateTexture2D( + CommandList.UpdateTexture2D( ((FTextureResource*)UpdateData->Texture2D->GetResource())->GetTextureRHI()->GetTexture2D(), 0, UpdateData->Region, @@ -472,7 +472,7 @@ TFuture UCUBlueprintLibrary::Conv_BytesToTexture_Async(const TArray ENQUEUE_RENDER_COMMAND(BytesToTextureAsyncCommand)( [UpdateData](FRHICommandList& CommandList) { - RHIUpdateTexture2D( + CommandList.UpdateTexture2D( ((FTextureResource*)UpdateData->Texture2D->GetResource())->GetTextureRHI()->GetTexture2D(), 0, UpdateData->Region, diff --git a/Source/SIOJson/Private/SIOJConvert.cpp b/Source/SIOJson/Private/SIOJConvert.cpp index ac4fadc..7f0758a 100644 --- a/Source/SIOJson/Private/SIOJConvert.cpp +++ b/Source/SIOJson/Private/SIOJConvert.cpp @@ -24,6 +24,20 @@ namespace { FJsonObjectConverter::CustomExportCallback EnumOverrideExportCallback; + //UE 5.8: FJsonObject stores keys as UE::FSharedString (FJsonObject::FStringType) + //instead of FString. Convert to an FString-keyed map so the partial converter copy + //below can keep its FString interface unchanged. + TMap> ToFStringKeyedMap(const TMap>& In) + { + TMap> Out; + Out.Reserve(In.Num()); + for (const auto& Pair : In) + { + Out.Add(FString(*Pair.Key), Pair.Value); + } + return Out; + } + //Begin partial copy of FJsonObjectConverter for BP enum workaround bool JsonValueToFPropertyWithContainer(const TSharedPtr& JsonValue, FProperty* Property, void* OutValue, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags); bool JsonAttributesToUStructWithContainer(const TMap< FString, TSharedPtr >& JsonAttributes, const UStruct* StructDefinition, void* OutStruct, const UStruct* ContainerStruct, void* Container, int64 CheckFlags, int64 SkipFlags); @@ -175,7 +189,7 @@ namespace { int32 NewIndex = Helper.AddDefaultValue_Invalid_NeedsRehash(); - TSharedPtr TempKeyValue = MakeShared(Entry.Key); + TSharedPtr TempKeyValue = MakeShared(FString(*Entry.Key)); const bool bKeySuccess = JsonValueToFPropertyWithContainer(TempKeyValue, MapProperty->KeyProp, Helper.GetKeyPtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags); const bool bValueSuccess = JsonValueToFPropertyWithContainer(Entry.Value, MapProperty->ValueProp, Helper.GetValuePtr(NewIndex), ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags); @@ -264,7 +278,7 @@ namespace { TSharedPtr Obj = JsonValue->AsObject(); check(Obj.IsValid()); // should not fail if Type == EJson::Object - if (!JsonAttributesToUStructWithContainer(Obj->Values, StructProperty->Struct, OutValue, ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags)) + if (!JsonAttributesToUStructWithContainer(ToFStringKeyedMap(Obj->Values), StructProperty->Struct, OutValue, ContainerStruct, Container, CheckFlags & (~CPF_ParmFlags), SkipFlags)) { UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - FJsonObjectConverter::JsonObjectToUStruct failed for property %s"), *Property->GetNameCPP()); return false; @@ -362,7 +376,7 @@ namespace TSharedPtr Obj = JsonValue->AsObject(); check(Obj.IsValid()); // should not fail if Type == EJson::Object - if (!JsonAttributesToUStructWithContainer(Obj->Values, ObjectProperty->PropertyClass, CreatedObj, ObjectProperty->PropertyClass, CreatedObj, CheckFlags & (~CPF_ParmFlags), SkipFlags)) + if (!JsonAttributesToUStructWithContainer(ToFStringKeyedMap(Obj->Values), ObjectProperty->PropertyClass, CreatedObj, ObjectProperty->PropertyClass, CreatedObj, CheckFlags & (~CPF_ParmFlags), SkipFlags)) { UE_LOG(LogJson, Error, TEXT("BPEnumWA-JsonValueToUProperty - FJsonObjectConverter::JsonObjectToUStruct failed for property %s"), *Property->GetNameCPP()); return false; @@ -484,7 +498,12 @@ namespace // Just copy it into the object FJsonObjectWrapper* ProxyObject = (FJsonObjectWrapper *)OutStruct; ProxyObject->JsonObject = MakeShared(); - ProxyObject->JsonObject->Values = JsonAttributes; + //UE 5.8: assign via SetField so FString keys are interned into the + //object's shared-string set (Values is keyed by UE::FSharedString). + for (const auto& Pair : JsonAttributes) + { + ProxyObject->JsonObject->SetField(Pair.Key, Pair.Value); + } return true; } @@ -543,7 +562,7 @@ namespace public: static bool JsonObjectToUStruct(const TSharedRef& JsonObject, const UStruct* StructDefinition, void* OutStruct, int64 CheckFlags, int64 SkipFlags) { - return JsonAttributesToUStructWithContainer(JsonObject->Values, StructDefinition, OutStruct, StructDefinition, OutStruct, CheckFlags, SkipFlags); + return JsonAttributesToUStructWithContainer(ToFStringKeyedMap(JsonObject->Values), StructDefinition, OutStruct, StructDefinition, OutStruct, CheckFlags, SkipFlags); } }; } @@ -901,7 +920,7 @@ void USIOJConvert::TrimValueKeyNames(const TSharedPtr& JsonValue) auto JsonObject = JsonValue->AsObject(); for (auto Pair : JsonObject->Values) { - const FString& Key = Pair.Key; + const FString Key(*Pair.Key); FString TrimmedKey; bool DidNeedTrimming = TrimKey(Key, TrimmedKey); @@ -1061,25 +1080,26 @@ void USIOJConvert::ReplaceJsonValueNamesWithMap(TSharedPtr& JsonValu for (auto Pair : AllValues) { + const FString Key(*Pair.Key); if (SubMap.Contains(TMAP_STRING)) { FString TMapString = FString(TMAP_STRING); //If we found a tmap, replace each sub key with list of keys ReplaceJsonValueNamesWithMap(Pair.Value, SubMap[TMapString]); } - else if (SubMap.Num() > 0 && SubMap.Contains(Pair.Key)) + else if (SubMap.Num() > 0 && SubMap.Contains(Key)) { //Get the long key for entry - const FString& LongKey = SubMap[Pair.Key]->LongKey; + const FString& LongKey = SubMap[Key]->LongKey; //loop nested structures - ReplaceJsonValueNamesWithMap(Pair.Value, SubMap[Pair.Key]); + ReplaceJsonValueNamesWithMap(Pair.Value, SubMap[Key]); - if (Pair.Key != LongKey) + if (Key != LongKey) { //finally set the field and remove the old field Object->SetField(LongKey, Pair.Value); - Object->RemoveField(Pair.Key); + Object->RemoveField(Key); } } } diff --git a/Source/SIOJson/Private/SIOJRequestJSON.cpp b/Source/SIOJson/Private/SIOJRequestJSON.cpp index edd19e6..0a57af6 100644 --- a/Source/SIOJson/Private/SIOJRequestJSON.cpp +++ b/Source/SIOJson/Private/SIOJRequestJSON.cpp @@ -270,7 +270,7 @@ void USIOJRequestJSON::ProcessRequest() // Loop through all the values and prepare additional url part for (auto RequestIt = RequestJsonObj->GetRootObject()->Values.CreateIterator(); RequestIt; ++RequestIt) { - FString Key = RequestIt.Key(); + FString Key(*RequestIt.Key()); FString Value = RequestIt.Value().Get()->AsString(); if (!Key.IsEmpty() && !Value.IsEmpty()) @@ -299,7 +299,7 @@ void USIOJRequestJSON::ProcessRequest() // Loop through all the values and prepare additional url part for (auto RequestIt = RequestJsonObj->GetRootObject()->Values.CreateIterator(); RequestIt; ++RequestIt) { - FString Key = RequestIt.Key(); + FString Key(*RequestIt.Key()); FString Value = RequestIt.Value().Get()->AsString(); if (!Key.IsEmpty() && !Value.IsEmpty()) diff --git a/Source/SIOJson/Private/SIOJsonObject.cpp b/Source/SIOJson/Private/SIOJsonObject.cpp index afe811b..e17028f 100644 --- a/Source/SIOJson/Private/SIOJsonObject.cpp +++ b/Source/SIOJson/Private/SIOJsonObject.cpp @@ -105,8 +105,13 @@ TArray USIOJsonObject::GetFieldNames() return Result; } - JsonObj->Values.GetKeys(Result); - + //UE 5.8: FJsonObject keys are UE::FSharedString; copy them into FString results. + Result.Reserve(JsonObj->Values.Num()); + for (const auto& Pair : JsonObj->Values) + { + Result.Add(FString(*Pair.Key)); + } + return Result; } diff --git a/Source/SocketIOClient/Private/SIOMessageConvert.cpp b/Source/SocketIOClient/Private/SIOMessageConvert.cpp index 0919e63..03feee2 100644 --- a/Source/SocketIOClient/Private/SIOMessageConvert.cpp +++ b/Source/SocketIOClient/Private/SIOMessageConvert.cpp @@ -141,7 +141,7 @@ sio::message::ptr USIOMessageConvert::ToSIOMessage(const TSharedPtr& for (auto ItemPair : ValueTmap) { //important to use get_map() directly to insert the key in the correct map and not a pointer copy - ObjectMessage->get_map()[StdString(ItemPair.Key)] = ToSIOMessage(ItemPair.Value); + ObjectMessage->get_map()[StdString(FString(*ItemPair.Key))] = ToSIOMessage(ItemPair.Value); } return ObjectMessage; @@ -176,7 +176,7 @@ std::map USIOMessageConvert::JsonObjectToStdStringMap( //If it's a string value, add it to the std map if (Value->Type == EJson::String) { - ParamMap[USIOMessageConvert::StdString(Pair.Key)] = USIOMessageConvert::StdString(Value->AsString()); + ParamMap[USIOMessageConvert::StdString(FString(*Pair.Key))] = USIOMessageConvert::StdString(Value->AsString()); } } } @@ -197,7 +197,7 @@ TMap USIOMessageConvert::JsonObjectToFStringMap(TSharedPtrType == EJson::String) { - ParamMap.Add(Pair.Key, Value->AsString()); + ParamMap.Add(FString(*Pair.Key), Value->AsString()); } } } diff --git a/Source/SocketIOClient/Private/SocketIONative.cpp b/Source/SocketIOClient/Private/SocketIONative.cpp index 3dcffb5..2d50ae3 100644 --- a/Source/SocketIOClient/Private/SocketIONative.cpp +++ b/Source/SocketIOClient/Private/SocketIONative.cpp @@ -489,7 +489,9 @@ void FSocketIONative::SetupInternalCallbacks() bIsConnected = false; ESIOConnectionCloseReason DisconnectReason = (ESIOConnectionCloseReason)reason; - FString DisconnectReasonString = UEnum::GetValueAsString(DisconnectReason); + //UE 5.8: StaticEnum<>() is deleted for this namespaced UENUM, so map the close + //reason to a string directly for the diagnostic log below. + FString DisconnectReasonString = (DisconnectReason == CLOSE_REASON_NORMAL) ? TEXT("CLOSE_REASON_NORMAL") : TEXT("CLOSE_REASON_DROP"); if (VerboseLog) { UE_LOG(SocketIO, Log, TEXT("SocketIO Disconnected %s reason: %s"), *SessionId, *DisconnectReasonString);