├── .gitignore ├── Config ├── BaseVaRest.ini ├── DefaultVaRest.ini └── FilterPlugin.ini ├── LICENSE ├── README.md ├── Resources └── Icon128.png ├── SCREENSHOT.jpg ├── Source ├── .clang-format ├── VaRest │ ├── Private │ │ ├── VaRest.cpp │ │ ├── VaRestJsonObject.cpp │ │ ├── VaRestJsonParser.cpp │ │ ├── VaRestJsonParser.h │ │ ├── VaRestJsonValue.cpp │ │ ├── VaRestLibrary.cpp │ │ ├── VaRestRequestJSON.cpp │ │ ├── VaRestSettings.cpp │ │ └── VaRestSubsystem.cpp │ ├── Public │ │ ├── VaRest.h │ │ ├── VaRestDefines.h │ │ ├── VaRestJsonObject.h │ │ ├── VaRestJsonValue.h │ │ ├── VaRestLibrary.h │ │ ├── VaRestRequestJSON.h │ │ ├── VaRestSettings.h │ │ ├── VaRestSubsystem.h │ │ └── VaRestTypes.h │ └── VaRest.Build.cs └── VaRestEditor │ ├── Private │ ├── VaRestEditor.cpp │ └── VaRest_BreakJson.cpp │ ├── Public │ ├── VaRestEditor.h │ └── VaRest_BreakJson.h │ └── VaRestEditor.Build.cs └── VaRest.uplugin /.gitignore: -------------------------------------------------------------------------------- 1 | /Intermediate/ 2 | /Binaries/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /Config/BaseVaRest.ini: -------------------------------------------------------------------------------- 1 | [CoreRedirects] 2 | +PackageRedirects=(OldName="/VaRestPlugin/", NewName="/VaRest/", MatchSubstring=true) 3 | +ClassRedirects=(OldName="/Script/VaRestPlugin.VaRestJsonObject",NewName="/Script/VaRest.VaRestJsonObject") 4 | +ClassRedirects=(OldName="/Script/VaRestPlugin.VaRestJsonValue",NewName="/Script/VaRest.VaRestJsonValue") 5 | +ClassRedirects=(OldName="/Script/VaRestPlugin.VaRestLibrary",NewName="/Script/VaRest.VaRestLibrary") 6 | +ClassRedirects=(OldName="/Script/VaRestPlugin.VaRestRequestJSON",NewName="/Script/VaRest.VaRestRequestJSON") 7 | +StructRedirects=(OldName="/Script/VaRestPlugin.VaRestCallResponse",NewName="/Script/VaRest.VaRestCallResponse") 8 | +EnumRedirects=(OldName="/Script/VaRestPlugin.EVaJson",NewName="/Script/VaRest.EVaJson") 9 | +EnumRedirects=(OldName="/Script/VaRestPlugin.EVaRestRequestVerb",NewName="/Script/VaRest.EVaRestRequestVerb") 10 | +EnumRedirects=(OldName="/Script/VaRestPlugin.EVaRestRequestContentType",NewName="/Script/VaRest.EVaRestRequestContentType") 11 | +EnumRedirects=(OldName="/Script/VaRestPlugin.EVaRestRequestStatus",NewName="/Script/VaRest.EVaRestRequestStatus") 12 | +EnumRedirects=(OldName="/Script/VaRest.ERequestVerb",NewName="/Script/VaRest.EVaRestRequestVerb") 13 | +EnumRedirects=(OldName="/Script/VaRest.ERequestContentType",NewName="/Script/VaRest.EVaRestRequestContentType") 14 | +EnumRedirects=(OldName="/Script/VaRest.ERequestStatus",NewName="/Script/VaRest.EVaRestRequestStatus") 15 | +EnumRedirects=(OldName="/Script/VaRest.EHttpStatusCode",NewName="/Script/VaRest.EVaRestHttpStatusCode") 16 | +FunctionRedirects=(OldName="VaRestRequestJSON.ConstructRequest",NewName="VaRestRequestJSON.ConstructVaRestRequest") 17 | +FunctionRedirects=(OldName="VaRestRequestJSON.ConstructRequestExt",NewName="VaRestRequestJSON.ConstructVaRestRequestExt") 18 | +FunctionRedirects=(OldName="VaRestJsonObject.ConstructJsonObject",NewName="VaRestJsonObject.ConstructVaRestJsonObject") 19 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValueNumber",NewName="VaRestSubsystem.ConstructJsonValueNumber") 20 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValueString",NewName="VaRestSubsystem.ConstructJsonValueString") 21 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValueBool",NewName="VaRestSubsystem.ConstructJsonValueBool") 22 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValueArray",NewName="VaRestSubsystem.ConstructJsonValueArray") 23 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValueObject",NewName="VaRestSubsystem.ConstructJsonValueObject") 24 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValue",NewName="VaRestSubsystem.ConstructJsonValue") -------------------------------------------------------------------------------- /Config/DefaultVaRest.ini: -------------------------------------------------------------------------------- 1 | [CoreRedirects] 2 | +PackageRedirects=(OldName="/VaRestPlugin/", NewName="/VaRest/", MatchSubstring=true) 3 | +ClassRedirects=(OldName="/Script/VaRestPlugin.VaRestJsonObject",NewName="/Script/VaRest.VaRestJsonObject") 4 | +ClassRedirects=(OldName="/Script/VaRestPlugin.VaRestJsonValue",NewName="/Script/VaRest.VaRestJsonValue") 5 | +ClassRedirects=(OldName="/Script/VaRestPlugin.VaRestLibrary",NewName="/Script/VaRest.VaRestLibrary") 6 | +ClassRedirects=(OldName="/Script/VaRestPlugin.VaRestRequestJSON",NewName="/Script/VaRest.VaRestRequestJSON") 7 | +StructRedirects=(OldName="/Script/VaRestPlugin.VaRestCallResponse",NewName="/Script/VaRest.VaRestCallResponse") 8 | +EnumRedirects=(OldName="/Script/VaRestPlugin.EVaJson",NewName="/Script/VaRest.EVaJson") 9 | +EnumRedirects=(OldName="/Script/VaRestPlugin.EVaRestRequestVerb",NewName="/Script/VaRest.EVaRestRequestVerb") 10 | +EnumRedirects=(OldName="/Script/VaRestPlugin.EVaRestRequestContentType",NewName="/Script/VaRest.EVaRestRequestContentType") 11 | +EnumRedirects=(OldName="/Script/VaRestPlugin.EVaRestRequestStatus",NewName="/Script/VaRest.EVaRestRequestStatus") 12 | +EnumRedirects=(OldName="/Script/VaRest.ERequestVerb",NewName="/Script/VaRest.EVaRestRequestVerb") 13 | +EnumRedirects=(OldName="/Script/VaRest.ERequestContentType",NewName="/Script/VaRest.EVaRestRequestContentType") 14 | +EnumRedirects=(OldName="/Script/VaRest.ERequestStatus",NewName="/Script/VaRest.EVaRestRequestStatus") 15 | +EnumRedirects=(OldName="/Script/VaRest.EHttpStatusCode",NewName="/Script/VaRest.EVaRestHttpStatusCode") 16 | +FunctionRedirects=(OldName="VaRestRequestJSON.ConstructRequest",NewName="VaRestRequestJSON.ConstructVaRestRequest") 17 | +FunctionRedirects=(OldName="VaRestRequestJSON.ConstructRequestExt",NewName="VaRestRequestJSON.ConstructVaRestRequestExt") 18 | +FunctionRedirects=(OldName="VaRestJsonObject.ConstructJsonObject",NewName="VaRestJsonObject.ConstructVaRestJsonObject") 19 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValueNumber",NewName="VaRestSubsystem.ConstructJsonValueNumber") 20 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValueString",NewName="VaRestSubsystem.ConstructJsonValueString") 21 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValueBool",NewName="VaRestSubsystem.ConstructJsonValueBool") 22 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValueArray",NewName="VaRestSubsystem.ConstructJsonValueArray") 23 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValueObject",NewName="VaRestSubsystem.ConstructJsonValueObject") 24 | +FunctionRedirects=(OldName="VaRestJsonValue.ConstructJsonValue",NewName="VaRestSubsystem.ConstructJsonValue") -------------------------------------------------------------------------------- /Config/FilterPlugin.ini: -------------------------------------------------------------------------------- 1 | [FilterPlugin] 2 | /Config/BaseVaRest.ini 3 | /Config/DefaultVaRest.ini 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Vladimir Alyamkin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHub](https://img.shields.io/github/license/ufna/VaRest) 2 | ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/ufna/VaRest?include_prereleases) 3 | 4 | > [!IMPORTANT] 5 | > Hi there. Thank you for your interest and support over the years! I want to inform you that I will be archiving the VaRest project. While this means I will no longer be actively maintaining it, I encourage anyone interested to fork the repository and continue development if you wish. 6 | > 7 | > Feel free to use the existing code as a foundation for your own projects. I appreciate the contributions and enthusiasm of this community, and I hope VaRest continues to be a valuable resource for you. 8 | 9 | # VaRest 10 | 11 | VaRest is the plugin for [Unreal Engine 4](https://www.unrealengine.com/) that makes REST server communications easier to use. 12 | 13 | Key features: 14 | 15 | * Flexible Http/Https request management with support of different Verbs and Content Types 16 | * **No C++ coding required**, everything can be managed via blueprints 17 | * Blueprintable FJsonObject wrapper with almost full support of Json features: different types of values, **arrays**, **binary data** content, both ways serializarion to FString, etc. 18 | * Blueprintable FJsonValue wrapper - **full Json features made for blueprints!** 19 | * Both bindable events and **latent functions** are provided to control the asynchronous requests 20 | 21 | Check the [Wiki](http://bit.ly/VaRest-Docs) for plugin usage examples and installation notes. 22 | 23 | Current version: **1.1 R 33** (UE 5.2) 24 | 25 | ![SCREENSHOT](SCREENSHOT.jpg) 26 | 27 | 28 | Legal info 29 | ---------- 30 | 31 | Unreal® is a trademark or registered trademark of Epic Games, Inc. in the United States of America and elsewhere. 32 | 33 | Unreal® Engine, Copyright 1998 – 2021, Epic Games, Inc. All rights reserved. 34 | 35 | -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufna/VaRest/84d02079d48bd461f0ccf4a5f81c36d527799b1a/Resources/Icon128.png -------------------------------------------------------------------------------- /SCREENSHOT.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ufna/VaRest/84d02079d48bd461f0ccf4a5f81c36d527799b1a/SCREENSHOT.jpg -------------------------------------------------------------------------------- /Source/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | IndentWidth: 4 5 | TabWidth: 4 6 | UseTab: ForContinuationAndIndentation 7 | Standard: Cpp11 8 | AccessModifierOffset: -4 9 | AlignAfterOpenBracket: DontAlign 10 | AlignEscapedNewlines: Right 11 | AlignTrailingComments: true 12 | AllowShortCaseLabelsOnASingleLine: true 13 | AllowShortFunctionsOnASingleLine: InlineOnly 14 | AllowShortLambdasOnASingleLine: All 15 | BraceWrapping: 16 | AfterCaseLabel: true 17 | AfterClass: true 18 | AfterControlStatement: true 19 | AfterEnum: true 20 | AfterFunction: true 21 | AfterNamespace: true 22 | AfterStruct: true 23 | AfterUnion: true 24 | AfterExternBlock: false 25 | BeforeCatch: true 26 | BeforeElse: true 27 | BeforeLambdaBody: false 28 | BeforeWhile: true 29 | SplitEmptyFunction: true 30 | SplitEmptyRecord: true 31 | SplitEmptyNamespace: true 32 | BreakBeforeBraces: Custom 33 | BreakConstructorInitializersBeforeComma: true 34 | ColumnLimit: 0 35 | PointerAlignment: Left 36 | SpacesInAngles: false 37 | CommentPragmas: '^[^ ]' 38 | --- 39 | Language: ObjC 40 | DisableFormat: true 41 | ... 42 | -------------------------------------------------------------------------------- /Source/VaRest/Private/VaRest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #include "VaRest.h" 4 | 5 | #include "VaRestDefines.h" 6 | #include "VaRestLibrary.h" 7 | #include "VaRestSettings.h" 8 | 9 | #include "Developer/Settings/Public/ISettingsModule.h" 10 | #include "UObject/Package.h" 11 | 12 | #define LOCTEXT_NAMESPACE "FVaRestModule" 13 | 14 | void FVaRestModule::StartupModule() 15 | { 16 | ModuleSettings = NewObject(GetTransientPackage(), "VaRestSettings", RF_Standalone); 17 | ModuleSettings->AddToRoot(); 18 | 19 | // Register settings 20 | if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) 21 | { 22 | SettingsModule->RegisterSettings("Project", "Plugins", "VaRest", 23 | LOCTEXT("RuntimeSettingsName", "VaRest"), 24 | LOCTEXT("RuntimeSettingsDescription", "Configure VaRest plugin settings"), 25 | ModuleSettings); 26 | } 27 | 28 | UE_LOG(LogVaRest, Log, TEXT("%s: VaRest (%s) module started"), *VA_FUNC_LINE, *UVaRestLibrary::GetVaRestVersion()); 29 | } 30 | 31 | void FVaRestModule::ShutdownModule() 32 | { 33 | if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) 34 | { 35 | SettingsModule->UnregisterSettings("Project", "Plugins", "VaRest"); 36 | } 37 | 38 | if (!GExitPurge) 39 | { 40 | ModuleSettings->RemoveFromRoot(); 41 | } 42 | else 43 | { 44 | ModuleSettings = nullptr; 45 | } 46 | } 47 | 48 | UVaRestSettings* FVaRestModule::GetSettings() const 49 | { 50 | check(ModuleSettings); 51 | return ModuleSettings; 52 | } 53 | 54 | IMPLEMENT_MODULE(FVaRestModule, VaRest) 55 | 56 | DEFINE_LOG_CATEGORY(LogVaRest); 57 | 58 | #undef LOCTEXT_NAMESPACE 59 | -------------------------------------------------------------------------------- /Source/VaRest/Private/VaRestJsonObject.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #include "VaRestJsonObject.h" 4 | 5 | #include "VaRestDefines.h" 6 | #include "VaRestJsonParser.h" 7 | #include "VaRestJsonValue.h" 8 | 9 | #include "HAL/FileManager.h" 10 | #include "Misc/Paths.h" 11 | #include "Policies/CondensedJsonPrintPolicy.h" 12 | #include "Serialization/JsonSerializer.h" 13 | #include "Serialization/JsonWriter.h" 14 | 15 | typedef TJsonWriterFactory> FCondensedJsonStringWriterFactory; 16 | typedef TJsonWriter> FCondensedJsonStringWriter; 17 | 18 | UVaRestJsonObject::UVaRestJsonObject(const FObjectInitializer& ObjectInitializer) 19 | : Super(ObjectInitializer) 20 | , JsonObj(MakeShared()) 21 | { 22 | } 23 | 24 | void UVaRestJsonObject::Reset() 25 | { 26 | JsonObj = MakeShared(); 27 | } 28 | 29 | TSharedRef& UVaRestJsonObject::GetRootObject() 30 | { 31 | return JsonObj; 32 | } 33 | 34 | void UVaRestJsonObject::SetRootObject(const TSharedPtr& JsonObject) 35 | { 36 | if (JsonObject.IsValid()) 37 | { 38 | JsonObj = JsonObject.ToSharedRef(); 39 | } 40 | else 41 | { 42 | UE_LOG(LogVaRest, Error, TEXT("%s: Trying to set invalid json object as root one. Reset now."), *VA_FUNC_LINE); 43 | Reset(); 44 | } 45 | } 46 | 47 | ////////////////////////////////////////////////////////////////////////// 48 | // Serialization 49 | 50 | FString UVaRestJsonObject::EncodeJson() const 51 | { 52 | FString OutputString; 53 | const auto Writer = TJsonWriterFactory<>::Create(&OutputString); 54 | FJsonSerializer::Serialize(JsonObj, Writer); 55 | 56 | return OutputString; 57 | } 58 | 59 | FString UVaRestJsonObject::EncodeJsonToSingleString() const 60 | { 61 | FString OutputString; 62 | const auto Writer = FCondensedJsonStringWriterFactory::Create(&OutputString); 63 | FJsonSerializer::Serialize(JsonObj, Writer); 64 | 65 | return OutputString; 66 | } 67 | 68 | bool UVaRestJsonObject::DecodeJson(const FString& JsonString, bool bUseIncrementalParser) 69 | { 70 | if (bUseIncrementalParser) 71 | { 72 | const int32 BytesRead = DeserializeFromTCHARBytes(JsonString.GetCharArray().GetData(), JsonString.Len()); 73 | 74 | // JsonObj is always valid, but read bytes is zero when something went wrong 75 | if (BytesRead > 0) 76 | { 77 | return true; 78 | } 79 | } 80 | else 81 | { 82 | const TSharedRef> Reader = TJsonReaderFactory<>::Create(*JsonString); 83 | TSharedPtr OutJsonObj; 84 | if (FJsonSerializer::Deserialize(Reader, OutJsonObj)) 85 | { 86 | JsonObj = OutJsonObj.ToSharedRef(); 87 | return true; 88 | } 89 | } 90 | 91 | // If we've failed to deserialize the string, we should clear our internal data 92 | Reset(); 93 | 94 | UE_LOG(LogVaRest, Error, TEXT("Json decoding failed for: %s"), *JsonString); 95 | 96 | return false; 97 | } 98 | 99 | ////////////////////////////////////////////////////////////////////////// 100 | // FJsonObject API 101 | 102 | FString UVaRestJsonObject::GetFieldTypeString(const FString& FieldName) const 103 | { 104 | if (JsonObj->HasTypedField(FieldName)) 105 | { 106 | return TEXT("Null"); 107 | } 108 | else if (JsonObj->HasTypedField(FieldName)) 109 | { 110 | return TEXT("String"); 111 | } 112 | else if (JsonObj->HasTypedField(FieldName)) 113 | { 114 | return TEXT("Number"); 115 | } 116 | else if (JsonObj->HasTypedField(FieldName)) 117 | { 118 | return TEXT("Boolean"); 119 | } 120 | else if (JsonObj->HasTypedField(FieldName)) 121 | { 122 | return TEXT("Object"); 123 | } 124 | else if (JsonObj->HasTypedField(FieldName)) 125 | { 126 | return TEXT("Array"); 127 | } 128 | 129 | UE_LOG(LogVaRest, Warning, TEXT("Field with name %s type unknown"), *FieldName); 130 | return ""; 131 | } 132 | 133 | TArray UVaRestJsonObject::GetFieldNames() const 134 | { 135 | TArray Result; 136 | JsonObj->Values.GetKeys(Result); 137 | 138 | return Result; 139 | } 140 | 141 | bool UVaRestJsonObject::HasField(const FString& FieldName) const 142 | { 143 | if (FieldName.IsEmpty()) 144 | { 145 | return false; 146 | } 147 | 148 | return JsonObj->HasField(FieldName); 149 | } 150 | 151 | void UVaRestJsonObject::RemoveField(const FString& FieldName) 152 | { 153 | if (FieldName.IsEmpty()) 154 | { 155 | return; 156 | } 157 | 158 | JsonObj->RemoveField(FieldName); 159 | } 160 | 161 | UVaRestJsonValue* UVaRestJsonObject::GetField(const FString& FieldName) const 162 | { 163 | if (FieldName.IsEmpty()) 164 | { 165 | return nullptr; 166 | } 167 | 168 | TSharedPtr NewVal = JsonObj->TryGetField(FieldName); 169 | if (NewVal.IsValid()) 170 | { 171 | UVaRestJsonValue* NewValue = NewObject(); 172 | NewValue->SetRootValue(NewVal); 173 | 174 | return NewValue; 175 | } 176 | 177 | return nullptr; 178 | } 179 | 180 | void UVaRestJsonObject::SetField(const FString& FieldName, UVaRestJsonValue* JsonValue) 181 | { 182 | if (FieldName.IsEmpty()) 183 | { 184 | return; 185 | } 186 | 187 | JsonObj->SetField(FieldName, JsonValue->GetRootValue()); 188 | } 189 | 190 | ////////////////////////////////////////////////////////////////////////// 191 | // FJsonObject API Helpers (easy to use with simple Json objects) 192 | 193 | float UVaRestJsonObject::GetNumberField(const FString& FieldName) const 194 | { 195 | if (!JsonObj->HasTypedField(FieldName)) 196 | { 197 | UE_LOG(LogVaRest, Warning, TEXT("No field with name %s of type Number"), *FieldName); 198 | return 0.0f; 199 | } 200 | 201 | return JsonObj->GetNumberField(FieldName); 202 | } 203 | 204 | void UVaRestJsonObject::SetNumberField(const FString& FieldName, float Number) 205 | { 206 | if (FieldName.IsEmpty()) 207 | { 208 | return; 209 | } 210 | 211 | JsonObj->SetNumberField(FieldName, Number); 212 | } 213 | 214 | void UVaRestJsonObject::SetNumberFieldDouble(const FString& FieldName, double Number) 215 | { 216 | if (FieldName.IsEmpty()) 217 | { 218 | return; 219 | } 220 | 221 | JsonObj->SetNumberField(FieldName, Number); 222 | } 223 | 224 | int32 UVaRestJsonObject::GetIntegerField(const FString& FieldName) const 225 | { 226 | if (!JsonObj->HasTypedField(FieldName)) 227 | { 228 | UE_LOG(LogVaRest, Warning, TEXT("No field with name %s of type Number"), *FieldName); 229 | return 0; 230 | } 231 | 232 | return JsonObj->GetIntegerField(FieldName); 233 | } 234 | 235 | void UVaRestJsonObject::SetIntegerField(const FString& FieldName, int32 Number) 236 | { 237 | if (FieldName.IsEmpty()) 238 | { 239 | return; 240 | } 241 | 242 | JsonObj->SetNumberField(FieldName, Number); 243 | } 244 | 245 | int64 UVaRestJsonObject::GetInt64Field(const FString& FieldName) const 246 | { 247 | if (!JsonObj->HasTypedField(FieldName)) 248 | { 249 | UE_LOG(LogVaRest, Warning, TEXT("No field with name %s of type Number"), *FieldName); 250 | return 0; 251 | } 252 | 253 | return static_cast(JsonObj->GetNumberField(FieldName)); 254 | } 255 | 256 | void UVaRestJsonObject::SetInt64Field(const FString& FieldName, int64 Number) 257 | { 258 | if (FieldName.IsEmpty()) 259 | { 260 | return; 261 | } 262 | 263 | JsonObj->SetNumberField(FieldName, Number); 264 | } 265 | 266 | FString UVaRestJsonObject::GetStringField(const FString& FieldName) const 267 | { 268 | if (!JsonObj->HasTypedField(FieldName)) 269 | { 270 | UE_LOG(LogVaRest, Warning, TEXT("No field with name %s of type String"), *FieldName); 271 | return TEXT(""); 272 | } 273 | 274 | return JsonObj->GetStringField(FieldName); 275 | } 276 | 277 | void UVaRestJsonObject::SetStringField(const FString& FieldName, const FString& StringValue) 278 | { 279 | if (FieldName.IsEmpty()) 280 | { 281 | return; 282 | } 283 | 284 | JsonObj->SetStringField(FieldName, StringValue); 285 | } 286 | 287 | bool UVaRestJsonObject::GetBoolField(const FString& FieldName) const 288 | { 289 | if (!JsonObj->HasTypedField(FieldName)) 290 | { 291 | UE_LOG(LogVaRest, Warning, TEXT("No field with name %s of type Boolean"), *FieldName); 292 | return false; 293 | } 294 | 295 | return JsonObj->GetBoolField(FieldName); 296 | } 297 | 298 | void UVaRestJsonObject::SetBoolField(const FString& FieldName, bool InValue) 299 | { 300 | if (FieldName.IsEmpty()) 301 | { 302 | return; 303 | } 304 | 305 | JsonObj->SetBoolField(FieldName, InValue); 306 | } 307 | 308 | TArray UVaRestJsonObject::GetArrayField(const FString& FieldName) const 309 | { 310 | TArray OutArray; 311 | if (FieldName.IsEmpty()) 312 | { 313 | return OutArray; 314 | } 315 | 316 | if (!JsonObj->HasTypedField(FieldName)) 317 | { 318 | UE_LOG(LogVaRest, Warning, TEXT("%s: No field with name %s of type Array"), *VA_FUNC_LINE, *FieldName); 319 | return OutArray; 320 | } 321 | 322 | TArray> ValArray = JsonObj->GetArrayField(FieldName); 323 | for (auto Value : ValArray) 324 | { 325 | UVaRestJsonValue* NewValue = NewObject(); 326 | NewValue->SetRootValue(Value); 327 | 328 | OutArray.Add(NewValue); 329 | } 330 | 331 | return OutArray; 332 | } 333 | 334 | void UVaRestJsonObject::SetArrayField(const FString& FieldName, const TArray& InArray) 335 | { 336 | if (FieldName.IsEmpty()) 337 | { 338 | return; 339 | } 340 | 341 | TArray> ValArray; 342 | 343 | // Process input array and COPY original values 344 | for (auto InVal : InArray) 345 | { 346 | if (InVal == nullptr) 347 | continue; 348 | 349 | const TSharedPtr JsonVal = InVal->GetRootValue(); 350 | 351 | switch (InVal->GetType()) 352 | { 353 | case EVaJson::None: 354 | break; 355 | 356 | case EVaJson::Null: 357 | ValArray.Add(MakeShareable(new FJsonValueNull())); 358 | break; 359 | 360 | case EVaJson::String: 361 | ValArray.Add(MakeShareable(new FJsonValueString(JsonVal->AsString()))); 362 | break; 363 | 364 | case EVaJson::Number: 365 | ValArray.Add(MakeShareable(new FJsonValueNumber(JsonVal->AsNumber()))); 366 | break; 367 | 368 | case EVaJson::Boolean: 369 | ValArray.Add(MakeShareable(new FJsonValueBoolean(JsonVal->AsBool()))); 370 | break; 371 | 372 | case EVaJson::Array: 373 | ValArray.Add(MakeShareable(new FJsonValueArray(JsonVal->AsArray()))); 374 | break; 375 | 376 | case EVaJson::Object: 377 | ValArray.Add(MakeShareable(new FJsonValueObject(JsonVal->AsObject()))); 378 | break; 379 | 380 | default: 381 | break; 382 | } 383 | } 384 | 385 | JsonObj->SetArrayField(FieldName, ValArray); 386 | } 387 | 388 | void UVaRestJsonObject::MergeJsonObject(UVaRestJsonObject* InJsonObject, bool Overwrite) 389 | { 390 | if (!InJsonObject || !InJsonObject->IsValidLowLevel()) 391 | { 392 | return; 393 | } 394 | 395 | TArray Keys = InJsonObject->GetFieldNames(); 396 | 397 | for (auto Key : Keys) 398 | { 399 | if (Overwrite == false && HasField(Key)) 400 | { 401 | continue; 402 | } 403 | 404 | SetField(Key, InJsonObject->GetField(Key)); 405 | } 406 | } 407 | 408 | UVaRestJsonObject* UVaRestJsonObject::GetObjectField(const FString& FieldName) const 409 | { 410 | if (!JsonObj->HasTypedField(FieldName)) 411 | { 412 | UE_LOG(LogVaRest, Warning, TEXT("%s: No field with name %s of type Object"), *VA_FUNC_LINE, *FieldName); 413 | return nullptr; 414 | } 415 | 416 | const TSharedPtr JsonObjField = JsonObj->GetObjectField(FieldName); 417 | 418 | UVaRestJsonObject* OutRestJsonObj = NewObject(); 419 | OutRestJsonObj->SetRootObject(JsonObjField); 420 | 421 | return OutRestJsonObj; 422 | } 423 | 424 | void UVaRestJsonObject::SetObjectField(const FString& FieldName, UVaRestJsonObject* JsonObject) 425 | { 426 | if (FieldName.IsEmpty() || !JsonObject || !JsonObject->IsValidLowLevel()) 427 | { 428 | return; 429 | } 430 | 431 | JsonObj->SetObjectField(FieldName, JsonObject->GetRootObject()); 432 | } 433 | 434 | void UVaRestJsonObject::SetMapFields_string(const TMap& Fields) 435 | { 436 | for (auto& field : Fields) 437 | { 438 | SetStringField(field.Key, field.Value); 439 | } 440 | } 441 | 442 | void UVaRestJsonObject::SetMapFields_uint8(const TMap& Fields) 443 | { 444 | SetMapFields_Impl(Fields); 445 | } 446 | 447 | void UVaRestJsonObject::SetMapFields_int32(const TMap& Fields) 448 | { 449 | SetMapFields_Impl(Fields); 450 | } 451 | 452 | void UVaRestJsonObject::SetMapFields_int64(const TMap& Fields) 453 | { 454 | SetMapFields_Impl(Fields); 455 | } 456 | 457 | void UVaRestJsonObject::SetMapFields_bool(const TMap& Fields) 458 | { 459 | SetMapFields_Impl(Fields); 460 | } 461 | 462 | ////////////////////////////////////////////////////////////////////////// 463 | // Array fields helpers (uniform arrays) 464 | 465 | TArray UVaRestJsonObject::GetNumberArrayField(const FString& FieldName) const 466 | { 467 | return GetTypeArrayField(FieldName); 468 | } 469 | 470 | TArray UVaRestJsonObject::GetIntegerArrayField(const FString& FieldName) const 471 | { 472 | return GetTypeArrayField(FieldName); 473 | } 474 | 475 | void UVaRestJsonObject::SetNumberArrayField(const FString& FieldName, const TArray& NumberArray) 476 | { 477 | if (FieldName.IsEmpty()) 478 | { 479 | return; 480 | } 481 | 482 | TArray> EntriesArray; 483 | 484 | for (auto Number : NumberArray) 485 | { 486 | EntriesArray.Add(MakeShareable(new FJsonValueNumber(Number))); 487 | } 488 | 489 | JsonObj->SetArrayField(FieldName, EntriesArray); 490 | } 491 | 492 | void UVaRestJsonObject::SetNumberArrayFieldDouble(const FString& FieldName, const TArray& NumberArray) 493 | { 494 | if (FieldName.IsEmpty()) 495 | { 496 | return; 497 | } 498 | 499 | TArray> EntriesArray; 500 | 501 | for (auto Number : NumberArray) 502 | { 503 | EntriesArray.Add(MakeShareable(new FJsonValueNumber(Number))); 504 | } 505 | 506 | JsonObj->SetArrayField(FieldName, EntriesArray); 507 | } 508 | 509 | TArray UVaRestJsonObject::GetStringArrayField(const FString& FieldName) const 510 | { 511 | TArray StringArray; 512 | if (!JsonObj->HasTypedField(FieldName) || FieldName.IsEmpty()) 513 | { 514 | UE_LOG(LogVaRest, Warning, TEXT("%s: No field with name %s of type Array"), *VA_FUNC_LINE, *FieldName); 515 | return StringArray; 516 | } 517 | 518 | const TArray> JsonArrayValues = JsonObj->GetArrayField(FieldName); 519 | for (TArray>::TConstIterator It(JsonArrayValues); It; ++It) 520 | { 521 | const auto Value = (*It).Get(); 522 | if (Value->Type != EJson::String) 523 | { 524 | UE_LOG(LogVaRest, Error, TEXT("Not String element in array with field name %s"), *FieldName); 525 | } 526 | 527 | StringArray.Add((*It)->AsString()); 528 | } 529 | 530 | return StringArray; 531 | } 532 | 533 | void UVaRestJsonObject::SetStringArrayField(const FString& FieldName, const TArray& StringArray) 534 | { 535 | if (FieldName.IsEmpty()) 536 | { 537 | return; 538 | } 539 | 540 | TArray> EntriesArray; 541 | for (auto String : StringArray) 542 | { 543 | EntriesArray.Add(MakeShareable(new FJsonValueString(String))); 544 | } 545 | 546 | JsonObj->SetArrayField(FieldName, EntriesArray); 547 | } 548 | 549 | TArray UVaRestJsonObject::GetBoolArrayField(const FString& FieldName) const 550 | { 551 | TArray BoolArray; 552 | if (!JsonObj->HasTypedField(FieldName) || FieldName.IsEmpty()) 553 | { 554 | UE_LOG(LogVaRest, Warning, TEXT("%s: No field with name %s of type Array"), *VA_FUNC_LINE, *FieldName); 555 | return BoolArray; 556 | } 557 | 558 | const TArray> JsonArrayValues = JsonObj->GetArrayField(FieldName); 559 | for (TArray>::TConstIterator It(JsonArrayValues); It; ++It) 560 | { 561 | const auto Value = (*It).Get(); 562 | if (Value->Type != EJson::Boolean) 563 | { 564 | UE_LOG(LogVaRest, Error, TEXT("Not Boolean element in array with field name %s"), *FieldName); 565 | } 566 | 567 | BoolArray.Add((*It)->AsBool()); 568 | } 569 | 570 | return BoolArray; 571 | } 572 | 573 | void UVaRestJsonObject::SetBoolArrayField(const FString& FieldName, const TArray& BoolArray) 574 | { 575 | if (FieldName.IsEmpty()) 576 | { 577 | return; 578 | } 579 | 580 | TArray> EntriesArray; 581 | for (auto Boolean : BoolArray) 582 | { 583 | EntriesArray.Add(MakeShareable(new FJsonValueBoolean(Boolean))); 584 | } 585 | 586 | JsonObj->SetArrayField(FieldName, EntriesArray); 587 | } 588 | 589 | TArray UVaRestJsonObject::GetObjectArrayField(const FString& FieldName) const 590 | { 591 | TArray OutArray; 592 | if (!JsonObj->HasTypedField(FieldName) || FieldName.IsEmpty()) 593 | { 594 | UE_LOG(LogVaRest, Warning, TEXT("%s: No field with name %s of type Array"), *VA_FUNC_LINE, *FieldName); 595 | return OutArray; 596 | } 597 | 598 | TArray> ValArray = JsonObj->GetArrayField(FieldName); 599 | for (const auto& Value : ValArray) 600 | { 601 | if (Value->Type != EJson::Object) 602 | { 603 | UE_LOG(LogVaRest, Error, TEXT("Not Object element in array with field name %s"), *FieldName); 604 | } 605 | 606 | TSharedPtr NewObj = Value->AsObject(); 607 | 608 | UVaRestJsonObject* NewJson = NewObject(); 609 | NewJson->SetRootObject(NewObj); 610 | 611 | OutArray.Add(NewJson); 612 | } 613 | 614 | return OutArray; 615 | } 616 | 617 | void UVaRestJsonObject::SetObjectArrayField(const FString& FieldName, const TArray& ObjectArray) 618 | { 619 | if (FieldName.IsEmpty()) 620 | { 621 | return; 622 | } 623 | 624 | TArray> EntriesArray; 625 | for (auto Value : ObjectArray) 626 | { 627 | if (Value == nullptr) 628 | continue; 629 | 630 | EntriesArray.Add(MakeShareable(new FJsonValueObject(Value->GetRootObject()))); 631 | } 632 | 633 | JsonObj->SetArrayField(FieldName, EntriesArray); 634 | } 635 | 636 | ////////////////////////////////////////////////////////////////////////// 637 | // Deserialize 638 | 639 | int32 UVaRestJsonObject::DeserializeFromUTF8Bytes(const ANSICHAR* Bytes, int32 Size) 640 | { 641 | FJSONReader Reader; 642 | 643 | const ANSICHAR* EndByte = Bytes + Size; 644 | while (Bytes < EndByte) 645 | { 646 | TCHAR Char = FUtf8Helper::CodepointFromUtf8(Bytes, EndByte - Bytes); 647 | if (Char > 0xFFFF) 648 | { 649 | Char = UNICODE_BOGUS_CHAR_CODEPOINT; 650 | } 651 | 652 | if (!Reader.Read(Char)) 653 | { 654 | break; 655 | } 656 | } 657 | 658 | SetRootObject(Reader.State.Root); 659 | return Reader.State.Size; 660 | } 661 | 662 | int32 UVaRestJsonObject::DeserializeFromTCHARBytes(const TCHAR* Bytes, int32 Size) 663 | { 664 | FJSONReader Reader; 665 | 666 | int32 i = 0; 667 | while (i < Size) 668 | { 669 | if (!Reader.Read(Bytes[i++])) 670 | { 671 | break; 672 | } 673 | } 674 | 675 | SetRootObject(Reader.State.Root); 676 | return Reader.State.Size; 677 | } 678 | 679 | void UVaRestJsonObject::DecodeFromArchive(TUniquePtr& Reader) 680 | { 681 | FArchive& Ar = (*Reader.Get()); 682 | uint8 SymbolBytes[2]; 683 | 684 | // Read first two bytes 685 | Ar << SymbolBytes[0]; 686 | Ar << SymbolBytes[1]; 687 | 688 | bool bIsIntelByteOrder = true; 689 | 690 | if (SymbolBytes[0] == 0xff && SymbolBytes[1] == 0xfe) 691 | { 692 | // Unicode Intel byte order. Less 1 for the FFFE header, additional 1 for null terminator. 693 | bIsIntelByteOrder = true; 694 | } 695 | else if (SymbolBytes[0] == 0xfe && SymbolBytes[1] == 0xff) 696 | { 697 | // Unicode non-Intel byte order. Less 1 for the FFFE header, additional 1 for null terminator. 698 | bIsIntelByteOrder = false; 699 | } 700 | 701 | FJSONReader JsonReader; 702 | TCHAR Char; 703 | 704 | while (!Ar.AtEnd()) 705 | { 706 | Ar << SymbolBytes[0]; 707 | 708 | if (Ar.AtEnd()) 709 | { 710 | break; 711 | } 712 | 713 | Ar << SymbolBytes[1]; 714 | 715 | if (bIsIntelByteOrder) 716 | { 717 | Char = CharCast(static_cast(static_cast(SymbolBytes[0]) + static_cast(SymbolBytes[1]) * 256)); 718 | } 719 | else 720 | { 721 | Char = CharCast(static_cast(static_cast(SymbolBytes[1]) + static_cast(SymbolBytes[0]) * 256)); 722 | } 723 | 724 | if (!JsonReader.Read(Char)) 725 | { 726 | break; 727 | } 728 | } 729 | 730 | SetRootObject(JsonReader.State.Root); 731 | 732 | if (!Ar.Close()) 733 | { 734 | UE_LOG(LogVaRest, Error, TEXT("UVaRestJsonObject::DecodeFromArchive: Error! Can't close file!")); 735 | } 736 | } 737 | 738 | ////////////////////////////////////////////////////////////////////////// 739 | // Serialize 740 | 741 | bool UVaRestJsonObject::WriteToFile(const FString& Path) const 742 | { 743 | TUniquePtr FileWriter(IFileManager::Get().CreateFileWriter(*Path)); 744 | if (!FileWriter) 745 | { 746 | return false; 747 | } 748 | 749 | FArchive& Ar = *FileWriter.Get(); 750 | 751 | UCS2CHAR BOM = UNICODE_BOM; 752 | Ar.Serialize(&BOM, sizeof(UCS2CHAR)); 753 | 754 | FString Str = FString(TEXT("{")); 755 | WriteStringToArchive(Ar, *Str, Str.Len()); 756 | 757 | int32 ElementCount = 0; 758 | FJSONWriter JsonWriter; 759 | for (auto JsonObjectValuePair : JsonObj->Values) 760 | { 761 | Str = FString(TEXT("\"")); 762 | WriteStringToArchive(Ar, *Str, Str.Len()); 763 | 764 | const TCHAR* BufferPtr = *JsonObjectValuePair.Key; 765 | for (int i = 0; i < JsonObjectValuePair.Key.Len(); ++i) 766 | { 767 | Str = FString(1, &BufferPtr[i]); 768 | WriteStringToArchive(Ar, *Str, Str.Len()); 769 | } 770 | 771 | Str = FString(TEXT("\"")); 772 | WriteStringToArchive(Ar, *Str, Str.Len()); 773 | 774 | Str = FString(TEXT(":")); 775 | WriteStringToArchive(Ar, *Str, Str.Len()); 776 | 777 | ++ElementCount; 778 | 779 | JsonWriter.Write(JsonObjectValuePair.Value, FileWriter.Get(), ElementCount >= JsonObj->Values.Num()); 780 | } 781 | 782 | Str = FString(TEXT("}")); 783 | WriteStringToArchive(Ar, *Str, Str.Len()); 784 | 785 | FileWriter->Close(); 786 | 787 | return true; 788 | } 789 | 790 | bool UVaRestJsonObject::WriteToFilePath(const FString& Path, const bool bIsRelativeToProjectDir) 791 | { 792 | return WriteToFile(bIsRelativeToProjectDir ? FPaths::ProjectDir() / Path : Path); 793 | } 794 | 795 | bool UVaRestJsonObject::WriteStringToArchive(FArchive& Ar, const TCHAR* StrPtr, int64 Len) 796 | { 797 | const auto Src = StringCast(StrPtr, Len); 798 | Ar.Serialize(const_cast(Src.Get()), Src.Length() * sizeof(UCS2CHAR)); 799 | 800 | return true; 801 | } 802 | -------------------------------------------------------------------------------- /Source/VaRest/Private/VaRestJsonParser.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2019 Mail.Ru Group. All Rights Reserved. 2 | 3 | #include "VaRestJsonParser.h" 4 | 5 | #include "VaRestJsonObject.h" 6 | 7 | #include "Dom/JsonObject.h" 8 | #include "Dom/JsonValue.h" 9 | 10 | uint32 FUtf8Helper::CodepointFromUtf8(const ANSICHAR*& SourceString, const uint32 SourceLengthRemaining) 11 | { 12 | checkSlow(SourceLengthRemaining > 0); 13 | 14 | const ANSICHAR* OctetPtr = SourceString; 15 | 16 | uint32 Codepoint = 0; 17 | uint32 Octet = (uint32)((uint8)*SourceString); 18 | uint32 Octet2, Octet3, Octet4; 19 | 20 | if (Octet < 128) // one octet char: 0 to 127 21 | { 22 | ++SourceString; // skip to next possible start of codepoint. 23 | return Octet; 24 | } 25 | else if (Octet < 192) // bad (starts with 10xxxxxx). 26 | { 27 | // Apparently each of these is supposed to be flagged as a bogus 28 | // char, instead of just resyncing to the next valid codepoint. 29 | ++SourceString; // skip to next possible start of codepoint. 30 | return UNICODE_BOGUS_CHAR_CODEPOINT; 31 | } 32 | else if (Octet < 224) // two octets 33 | { 34 | // Ensure our string has enough characters to read from 35 | if (SourceLengthRemaining < 2) 36 | { 37 | // Skip to end and write out a single char (we always have room for at least 1 char) 38 | SourceString += SourceLengthRemaining; 39 | return UNICODE_BOGUS_CHAR_CODEPOINT; 40 | } 41 | 42 | Octet -= (128 + 64); 43 | Octet2 = (uint32)((uint8) * (++OctetPtr)); 44 | if ((Octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx? 45 | { 46 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 47 | return UNICODE_BOGUS_CHAR_CODEPOINT; 48 | } 49 | 50 | Codepoint = ((Octet << 6) | (Octet2 - 128)); 51 | if ((Codepoint >= 0x80) && (Codepoint <= 0x7FF)) 52 | { 53 | SourceString += 2; // skip to next possible start of codepoint. 54 | return Codepoint; 55 | } 56 | } 57 | else if (Octet < 240) // three octets 58 | { 59 | // Ensure our string has enough characters to read from 60 | if (SourceLengthRemaining < 3) 61 | { 62 | // Skip to end and write out a single char (we always have room for at least 1 char) 63 | SourceString += SourceLengthRemaining; 64 | return UNICODE_BOGUS_CHAR_CODEPOINT; 65 | } 66 | 67 | Octet -= (128 + 64 + 32); 68 | Octet2 = (uint32)((uint8) * (++OctetPtr)); 69 | if ((Octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx? 70 | { 71 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 72 | return UNICODE_BOGUS_CHAR_CODEPOINT; 73 | } 74 | 75 | Octet3 = (uint32)((uint8) * (++OctetPtr)); 76 | if ((Octet3 & (128 + 64)) != 128) // Format isn't 10xxxxxx? 77 | { 78 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 79 | return UNICODE_BOGUS_CHAR_CODEPOINT; 80 | } 81 | 82 | Codepoint = (((Octet << 12)) | ((Octet2 - 128) << 6) | ((Octet3 - 128))); 83 | 84 | // UTF-8 characters cannot be in the UTF-16 surrogates range 85 | if (StringConv::IsHighSurrogate(Codepoint) || StringConv::IsLowSurrogate(Codepoint)) 86 | { 87 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 88 | return UNICODE_BOGUS_CHAR_CODEPOINT; 89 | } 90 | 91 | // 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge. 92 | if ((Codepoint >= 0x800) && (Codepoint <= 0xFFFD)) 93 | { 94 | SourceString += 3; // skip to next possible start of codepoint. 95 | return Codepoint; 96 | } 97 | } 98 | else if (Octet < 248) // four octets 99 | { 100 | // Ensure our string has enough characters to read from 101 | if (SourceLengthRemaining < 4) 102 | { 103 | // Skip to end and write out a single char (we always have room for at least 1 char) 104 | SourceString += SourceLengthRemaining; 105 | return UNICODE_BOGUS_CHAR_CODEPOINT; 106 | } 107 | 108 | Octet -= (128 + 64 + 32 + 16); 109 | Octet2 = (uint32)((uint8) * (++OctetPtr)); 110 | if ((Octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx? 111 | { 112 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 113 | return UNICODE_BOGUS_CHAR_CODEPOINT; 114 | } 115 | 116 | Octet3 = (uint32)((uint8) * (++OctetPtr)); 117 | if ((Octet3 & (128 + 64)) != 128) // Format isn't 10xxxxxx? 118 | { 119 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 120 | return UNICODE_BOGUS_CHAR_CODEPOINT; 121 | } 122 | 123 | Octet4 = (uint32)((uint8) * (++OctetPtr)); 124 | if ((Octet4 & (128 + 64)) != 128) // Format isn't 10xxxxxx? 125 | { 126 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 127 | return UNICODE_BOGUS_CHAR_CODEPOINT; 128 | } 129 | 130 | Codepoint = (((Octet << 18)) | ((Octet2 - 128) << 12) | 131 | ((Octet3 - 128) << 6) | ((Octet4 - 128))); 132 | if ((Codepoint >= 0x10000) && (Codepoint <= 0x10FFFF)) 133 | { 134 | SourceString += 4; // skip to next possible start of codepoint. 135 | return Codepoint; 136 | } 137 | } 138 | // Five and six octet sequences became illegal in rfc3629. 139 | // We throw the codepoint away, but parse them to make sure we move 140 | // ahead the right number of bytes and don't overflow the buffer. 141 | else if (Octet < 252) // five octets 142 | { 143 | // Ensure our string has enough characters to read from 144 | if (SourceLengthRemaining < 5) 145 | { 146 | // Skip to end and write out a single char (we always have room for at least 1 char) 147 | SourceString += SourceLengthRemaining; 148 | return UNICODE_BOGUS_CHAR_CODEPOINT; 149 | } 150 | 151 | Octet = (uint32)((uint8) * (++OctetPtr)); 152 | if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? 153 | { 154 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 155 | return UNICODE_BOGUS_CHAR_CODEPOINT; 156 | } 157 | 158 | Octet = (uint32)((uint8) * (++OctetPtr)); 159 | if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? 160 | { 161 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 162 | return UNICODE_BOGUS_CHAR_CODEPOINT; 163 | } 164 | 165 | Octet = (uint32)((uint8) * (++OctetPtr)); 166 | if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? 167 | { 168 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 169 | return UNICODE_BOGUS_CHAR_CODEPOINT; 170 | } 171 | 172 | Octet = (uint32)((uint8) * (++OctetPtr)); 173 | if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? 174 | { 175 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 176 | return UNICODE_BOGUS_CHAR_CODEPOINT; 177 | } 178 | 179 | SourceString += 5; // skip to next possible start of codepoint. 180 | return UNICODE_BOGUS_CHAR_CODEPOINT; 181 | } 182 | 183 | else // six octets 184 | { 185 | // Ensure our string has enough characters to read from 186 | if (SourceLengthRemaining < 6) 187 | { 188 | // Skip to end and write out a single char (we always have room for at least 1 char) 189 | SourceString += SourceLengthRemaining; 190 | return UNICODE_BOGUS_CHAR_CODEPOINT; 191 | } 192 | 193 | Octet = (uint32)((uint8) * (++OctetPtr)); 194 | if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? 195 | { 196 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 197 | return UNICODE_BOGUS_CHAR_CODEPOINT; 198 | } 199 | 200 | Octet = (uint32)((uint8) * (++OctetPtr)); 201 | if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? 202 | { 203 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 204 | return UNICODE_BOGUS_CHAR_CODEPOINT; 205 | } 206 | 207 | Octet = (uint32)((uint8) * (++OctetPtr)); 208 | if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? 209 | { 210 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 211 | return UNICODE_BOGUS_CHAR_CODEPOINT; 212 | } 213 | 214 | Octet = (uint32)((uint8) * (++OctetPtr)); 215 | if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? 216 | { 217 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 218 | return UNICODE_BOGUS_CHAR_CODEPOINT; 219 | } 220 | 221 | Octet = (uint32)((uint8) * (++OctetPtr)); 222 | if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? 223 | { 224 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 225 | return UNICODE_BOGUS_CHAR_CODEPOINT; 226 | } 227 | 228 | SourceString += 6; // skip to next possible start of codepoint. 229 | return UNICODE_BOGUS_CHAR_CODEPOINT; 230 | } 231 | 232 | ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. 233 | return UNICODE_BOGUS_CHAR_CODEPOINT; // catch everything else. 234 | } 235 | 236 | FJSONState::FJSONState() 237 | : Notation(EJSONNotation::NONE) 238 | , bEscape(false) 239 | , bError(false) 240 | , Quote(UNICODE_BOGUS_CHAR_CODEPOINT) 241 | { 242 | Key.Reserve(1024); 243 | Data.Reserve(4096); 244 | 245 | Root = TSharedPtr(nullptr); 246 | 247 | Objects.Reserve(64); 248 | Tokens.Reserve(64); 249 | 250 | Tokens.Add(EJSONToken::ROOT); 251 | 252 | Size = 0; 253 | } 254 | 255 | EJSONToken FJSONState::GetToken(int32 Index) 256 | { 257 | return Tokens.Num() > Index ? Tokens.Last(Index) : EJSONToken::ERROR; 258 | } 259 | 260 | bool FJSONState::CheckTokens(EJSONToken T1) 261 | { 262 | return T1 == GetToken(0); 263 | } 264 | 265 | bool FJSONState::CheckTokens(EJSONToken T1, EJSONToken T2) 266 | { 267 | return T1 == GetToken(1) && T2 == GetToken(0); 268 | } 269 | 270 | bool FJSONState::CheckTokens(EJSONToken T1, EJSONToken T2, EJSONToken T3) 271 | { 272 | return T1 == GetToken(2) && T2 == GetToken(1) && T3 == GetToken(0); 273 | } 274 | 275 | void FJSONState::PopToken(int32 Num) 276 | { 277 | if (Num > 0) 278 | { 279 | if (Tokens.Num() >= Num) 280 | { 281 | Tokens.RemoveAt(Tokens.Num() - Num, Num, false); 282 | } 283 | else 284 | { 285 | bError = true; 286 | } 287 | } 288 | 289 | Notation = EJSONNotation::NONE; 290 | } 291 | 292 | void FJSONState::PopObject() 293 | { 294 | if (Objects.Num() > 0) 295 | { 296 | const auto Object = Objects.Pop(false); 297 | if (Object->Type == EJson::Object) 298 | { 299 | return; 300 | } 301 | } 302 | 303 | bError = true; 304 | } 305 | 306 | void FJSONState::PopArray() 307 | { 308 | if (Objects.Num() > 0) 309 | { 310 | const auto Object = Objects.Pop(false); 311 | if (Object->Type == EJson::Array) 312 | { 313 | return; 314 | } 315 | } 316 | 317 | bError = true; 318 | } 319 | 320 | void FJSONState::PopValue(bool bCheckType) 321 | { 322 | if (Objects.Num() > 0) 323 | { 324 | const auto Value = Objects.Last(0); 325 | if (Value->Type == EJson::Object || Value->Type == EJson::Array) 326 | { 327 | if (bCheckType) 328 | { 329 | bError = true; 330 | } 331 | } 332 | else 333 | { 334 | Objects.Pop(false); 335 | if (Objects.Num() > 0) 336 | { 337 | switch (Value->Type) 338 | { 339 | case EJson::Null: 340 | { 341 | const auto LowerCase = Data.ToLower(); 342 | if (LowerCase != TEXT("null")) 343 | { 344 | bError = true; 345 | } 346 | break; 347 | } 348 | case EJson::String: 349 | { 350 | FJsonValueNonConstString* JsonValueString = ((FJsonValueNonConstString*)Value.Get()); 351 | JsonValueString->AsNonConstString() = Data; 352 | JsonValueString->AsNonConstString().Shrink(); 353 | Size += JsonValueString->AsNonConstString().GetAllocatedSize(); 354 | break; 355 | } 356 | case EJson::Number: 357 | { 358 | const FString LowerCase = Data.ToLower(); 359 | int32 ePosition = INDEX_NONE; 360 | LowerCase.FindChar('e', ePosition); 361 | if (ePosition == INDEX_NONE) 362 | { 363 | if (LowerCase.IsNumeric()) 364 | { 365 | ((FJsonValueNonConstNumber*)Value.Get())->AsNonConstNumber() = FCString::Atod(*LowerCase); 366 | } 367 | else 368 | { 369 | bError = true; 370 | } 371 | } 372 | else if (LowerCase.Len() > ePosition + 2) 373 | { 374 | const FString Left = LowerCase.Left(ePosition); 375 | const FString Rigth = LowerCase.Right(LowerCase.Len() - ePosition - 1); 376 | if (Left.IsNumeric() && Rigth.IsNumeric()) 377 | { 378 | ((FJsonValueNonConstNumber*)Value.Get())->AsNonConstNumber() = FCString::Atod(*Left) * FMath::Pow(10.f, FCString::Atoi(*Rigth)); 379 | } 380 | else 381 | { 382 | bError = true; 383 | } 384 | } 385 | else 386 | { 387 | bError = true; 388 | } 389 | break; 390 | } 391 | case EJson::Boolean: 392 | { 393 | const auto LowerCase = Data.ToLower(); 394 | if (LowerCase == TEXT("true")) 395 | { 396 | ((FJsonValueNonConstBoolean*)Value.Get())->AsNonConstBool() = true; 397 | } 398 | else if (LowerCase == TEXT("false")) 399 | { 400 | ((FJsonValueNonConstBoolean*)Value.Get())->AsNonConstBool() = false; 401 | } 402 | else 403 | { 404 | bError = true; 405 | } 406 | break; 407 | } 408 | default: 409 | { 410 | bError = true; 411 | return; 412 | } 413 | } 414 | 415 | ClearData(); 416 | 417 | const auto Container = Objects.Last(0); 418 | if (Container->Type == EJson::Object) 419 | { 420 | if (Key.Len() > 0) 421 | { 422 | FString KeyCopy = Key; 423 | KeyCopy.Shrink(); 424 | Container->AsObject()->SetField(KeyCopy, Value); 425 | Size += KeyCopy.GetAllocatedSize(); 426 | ClearKey(); 427 | } 428 | else 429 | { 430 | bError = true; 431 | } 432 | } 433 | else if (Container->Type == EJson::Array) 434 | { 435 | ((FJsonValueNonConstArray*)Container.Get())->AsNonConstArray().Add(Value); 436 | } 437 | else 438 | { 439 | bError = true; 440 | } 441 | } 442 | else 443 | { 444 | bError = true; 445 | } 446 | } 447 | } 448 | else 449 | { 450 | bError = true; 451 | } 452 | } 453 | 454 | FJsonValue* FJSONState::GetLast() 455 | { 456 | if (Objects.Num() > 0) 457 | { 458 | return Objects.Last(0).Get(); 459 | } 460 | bError = true; 461 | return nullptr; 462 | } 463 | 464 | FJsonValueObject* FJSONState::GetObject() 465 | { 466 | FJsonValue* Value = GetLast(); 467 | if (Value != nullptr && Value->Type == EJson::Object) 468 | { 469 | return (FJsonValueObject*)Value; 470 | } 471 | bError = true; 472 | return nullptr; 473 | } 474 | 475 | FJsonValueNonConstArray* FJSONState::GetArray() 476 | { 477 | FJsonValue* Value = GetLast(); 478 | if (Value != nullptr && Value->Type == EJson::Array) 479 | { 480 | return (FJsonValueNonConstArray*)Value; 481 | } 482 | bError = true; 483 | return nullptr; 484 | } 485 | 486 | TSharedPtr FJSONState::PushObject() 487 | { 488 | TSharedPtr Result(new FJsonValueObject(MakeShared())); 489 | Objects.Add(Result); 490 | Size += sizeof(TSharedPtr) + sizeof(FJsonValueObject); 491 | return Result; 492 | } 493 | 494 | TSharedPtr FJSONState::PushObject(TSharedPtr Object) 495 | { 496 | TSharedPtr Result(new FJsonValueObject(Object)); 497 | Objects.Add(Result); 498 | Size += sizeof(TSharedPtr) + sizeof(FJsonValueObject); 499 | return Result; 500 | } 501 | 502 | TSharedPtr FJSONState::PushArray() 503 | { 504 | const TArray> Empty; 505 | TSharedPtr Result(new FJsonValueNonConstArray(Empty)); 506 | Objects.Add(Result); 507 | Size += sizeof(TSharedPtr) + sizeof(FJsonValueNonConstArray); 508 | return Result; 509 | } 510 | 511 | TSharedPtr FJSONState::PushBoolean() 512 | { 513 | TSharedPtr Result(new FJsonValueNonConstBoolean(false)); 514 | Objects.Add(Result); 515 | Size += sizeof(TSharedPtr) + sizeof(FJsonValueNonConstBoolean); 516 | return Result; 517 | } 518 | 519 | TSharedPtr FJSONState::PushNull() 520 | { 521 | TSharedPtr Result(new FJsonValueNull()); 522 | Objects.Add(Result); 523 | Size += sizeof(TSharedPtr) + sizeof(FJsonValueNull); 524 | return Result; 525 | } 526 | 527 | TSharedPtr FJSONState::PushNumber() 528 | { 529 | TSharedPtr Result(new FJsonValueNonConstNumber(0.f)); 530 | Objects.Add(Result); 531 | Size += sizeof(TSharedPtr) + sizeof(FJsonValueNonConstNumber); 532 | return Result; 533 | } 534 | 535 | TSharedPtr FJSONState::PushString() 536 | { 537 | TSharedPtr Result(new FJsonValueNonConstString(TEXT(""))); 538 | Objects.Add(Result); 539 | Size += sizeof(TSharedPtr) + sizeof(FJsonValueNonConstString); 540 | return Result; 541 | } 542 | 543 | void FJSONState::ClearData() 544 | { 545 | Data.Reset(); 546 | } 547 | 548 | void FJSONState::ClearKey() 549 | { 550 | Key.Reset(); 551 | } 552 | 553 | void FJSONState::DataToKey() 554 | { 555 | ClearKey(); 556 | Key += Data; 557 | ClearData(); 558 | } 559 | 560 | void FJSONState::Error() 561 | { 562 | bError = true; 563 | } 564 | 565 | FJSONReader::FJSONReader() 566 | { 567 | } 568 | 569 | bool FJSONReader::IsNewLine(const TCHAR& Char) 570 | { 571 | return Char == '\n'; 572 | } 573 | 574 | bool FJSONReader::IsSpace(const TCHAR& Char) 575 | { 576 | return IsNewLine(Char) || Char == ' ' || Char == '\t' || Char == '\r'; 577 | } 578 | 579 | bool FJSONReader::FindToken(const TCHAR& Char) 580 | { 581 | if (State.bEscape) 582 | { 583 | return false; 584 | } 585 | 586 | if (State.Notation != EJSONNotation::STRING) 587 | { 588 | switch (Char) 589 | { 590 | case '{': State.Tokens.Add(EJSONToken::CURLY_BEGIN); return true; 591 | case '}': State.Tokens.Add(EJSONToken::CURLY_END); return true; 592 | case '[': State.Tokens.Add(EJSONToken::SQUARE_BEGIN); return true; 593 | case ']': State.Tokens.Add(EJSONToken::SQUARE_END); return true; 594 | case ',': State.Tokens.Add(EJSONToken::COMMA); return true; 595 | case ':': State.Tokens.Add(EJSONToken::COLON); return true; 596 | } 597 | } 598 | return false; 599 | } 600 | 601 | void FJSONReader::UpdateNotation() 602 | { 603 | switch (State.GetToken()) 604 | { 605 | case EJSONToken::ROOT: 606 | { 607 | return; 608 | } 609 | case EJSONToken::CURLY_BEGIN: 610 | { 611 | if (State.CheckTokens(EJSONToken::SQUARE_BEGIN, EJSONToken::CURLY_BEGIN)) // Object in array "[{" 612 | { 613 | State.Notation = EJSONNotation::OBJECT; 614 | auto Value = State.GetArray(); 615 | if (Value != nullptr) 616 | { 617 | Value->AsNonConstArray().Add(State.PushObject()); 618 | } 619 | else 620 | { 621 | State.Error(); 622 | } 623 | } 624 | else if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON, EJSONToken::CURLY_BEGIN)) // Object in key "{:{" 625 | { 626 | if (State.Key.Len() > 0) 627 | { 628 | State.Notation = EJSONNotation::OBJECT; 629 | const auto Value = State.GetObject(); 630 | if (Value != nullptr) 631 | { 632 | Value->AsObject()->SetField(State.Key, State.PushObject()); 633 | State.ClearKey(); 634 | } 635 | else 636 | { 637 | State.Error(); 638 | } 639 | } 640 | else 641 | { 642 | State.Error(); 643 | } 644 | } 645 | else if (State.CheckTokens(EJSONToken::ROOT, EJSONToken::CURLY_BEGIN)) // Root object "{" 646 | { 647 | if (State.Root.IsValid()) 648 | { 649 | State.Error(); 650 | } 651 | else 652 | { 653 | State.Root = MakeShared(); 654 | State.PushObject(State.Root); // add root object 655 | State.Notation = EJSONNotation::OBJECT; 656 | } 657 | } 658 | else 659 | { 660 | State.Error(); 661 | } 662 | break; 663 | } 664 | case EJSONToken::CURLY_END: 665 | { 666 | if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::CURLY_END)) // Close object "{}" 667 | { 668 | State.PopToken(2); // pop "{}" 669 | State.PopObject(); // remove object 670 | } 671 | else if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON, EJSONToken::CURLY_END)) // Close object "{:}" 672 | { 673 | State.PopToken(3); // pop "{:}" 674 | State.PopValue(); // remove value 675 | State.PopObject(); // remove object 676 | } 677 | else 678 | { 679 | State.Error(); 680 | } 681 | 682 | if (State.CheckTokens(EJSONToken::COLON)) // Object in object ":" 683 | { 684 | State.PopToken(1); // pop ":" 685 | } 686 | 687 | State.Notation = EJSONNotation::SKIP; 688 | 689 | break; 690 | } 691 | case EJSONToken::SQUARE_BEGIN: 692 | { 693 | if (State.CheckTokens(EJSONToken::SQUARE_BEGIN, EJSONToken::SQUARE_BEGIN)) // Array in array "[[" 694 | { 695 | State.Notation = EJSONNotation::ARRAY; 696 | auto Value = State.GetArray(); 697 | if (Value != nullptr) 698 | { 699 | Value->AsNonConstArray().Add(State.PushArray()); 700 | } 701 | else 702 | { 703 | State.Error(); 704 | } 705 | } 706 | else if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON, EJSONToken::SQUARE_BEGIN)) // Array in key "{:[" 707 | { 708 | State.Notation = EJSONNotation::ARRAY; 709 | if (State.Key.Len() > 0) 710 | { 711 | const auto Value = State.GetObject(); 712 | if (Value != nullptr) 713 | { 714 | Value->AsObject()->SetField(State.Key, State.PushArray()); 715 | State.ClearKey(); 716 | } 717 | else 718 | { 719 | State.Error(); 720 | } 721 | } 722 | else 723 | { 724 | State.Error(); 725 | } 726 | } 727 | else if (State.CheckTokens(EJSONToken::ROOT, EJSONToken::SQUARE_BEGIN)) // Root array "{" 728 | { 729 | State.Error(); // Not support 730 | } 731 | else 732 | { 733 | State.Error(); 734 | } 735 | break; 736 | } 737 | case EJSONToken::SQUARE_END: 738 | { 739 | if (State.CheckTokens(EJSONToken::SQUARE_BEGIN, EJSONToken::SQUARE_END)) // Close array "[]" 740 | { 741 | State.PopToken(2); // remove token "[]" 742 | State.PopValue(false); // remove value if exists 743 | State.PopArray(); // remove array 744 | 745 | if (State.CheckTokens(EJSONToken::COLON)) // Array in object ":" 746 | { 747 | State.PopToken(1); // pop ":" 748 | } 749 | } 750 | else 751 | { 752 | State.Error(); 753 | } 754 | 755 | State.Notation = EJSONNotation::SKIP; 756 | 757 | break; 758 | } 759 | case EJSONToken::COMMA: 760 | { 761 | if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON, EJSONToken::COMMA)) // Next record in object "{:," 762 | { 763 | State.PopToken(2); // remove token ":," 764 | State.PopValue(false); // remove value 765 | State.Notation = EJSONNotation::OBJECT; 766 | } 767 | else if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COMMA)) // Next record in object "{," 768 | { 769 | State.PopToken(1); // remove token "," 770 | State.Notation = EJSONNotation::OBJECT; 771 | } 772 | else if (State.CheckTokens(EJSONToken::SQUARE_BEGIN, EJSONToken::COMMA)) // Next record in array "[," 773 | { 774 | State.PopToken(1); // remove token "," 775 | State.PopValue(false); // remove value 776 | State.Notation = EJSONNotation::ARRAY; 777 | } 778 | else 779 | { 780 | State.Error(); 781 | } 782 | break; 783 | } 784 | case EJSONToken::COLON: 785 | { 786 | if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON)) // Object key close "{:" 787 | { 788 | State.Notation = EJSONNotation::OBJECT; 789 | if (State.Data.Len() > 0) 790 | { 791 | State.DataToKey(); 792 | } 793 | else 794 | { 795 | State.Error(); 796 | } 797 | } 798 | else 799 | { 800 | State.Error(); 801 | } 802 | break; 803 | } 804 | case EJSONToken::ERROR: 805 | { 806 | State.Error(); 807 | break; 808 | } 809 | } 810 | 811 | if (!State.bError && State.Notation == EJSONNotation::NONE) 812 | { 813 | UpdateNotation(); 814 | } 815 | } 816 | 817 | void FJSONReader::ReadAsString(const TCHAR& Char) 818 | { 819 | if (IsNewLine(Char)) 820 | { 821 | State.Error(); 822 | return; 823 | } 824 | 825 | if (!State.bEscape && State.Quote == Char) 826 | { 827 | State.Quote = UNICODE_BOGUS_CHAR_CODEPOINT; 828 | State.Notation = EJSONNotation::SKIP; 829 | } 830 | else 831 | { 832 | if (State.bEscape) 833 | { 834 | switch (Char) 835 | { 836 | case 'n': State.Data.AppendChar('\n'); break; 837 | case 't': State.Data.AppendChar('\t'); break; 838 | default: State.Data.AppendChar(Char); break; 839 | } 840 | } 841 | else 842 | { 843 | State.Data.AppendChar(Char); 844 | } 845 | } 846 | } 847 | 848 | void FJSONReader::ReadAsStringSpecial(const TCHAR& Char) 849 | { 850 | if (IsSpace(Char) && State.Data.Len() > 0) 851 | { 852 | State.Notation = EJSONNotation::SKIP; 853 | return; 854 | } 855 | 856 | State.Data.AppendChar(Char); 857 | } 858 | 859 | void FJSONReader::ReadAsNumber(const TCHAR& Char) 860 | { 861 | if (IsSpace(Char) && State.Data.Len() > 0) 862 | { 863 | State.Notation = EJSONNotation::SKIP; 864 | return; 865 | } 866 | 867 | if ((Char >= '0' && Char <= '9') || Char == '-' || Char == '.' || Char == '+' || Char == 'e' || Char == 'E') 868 | { 869 | State.Data.AppendChar(Char); 870 | } 871 | else 872 | { 873 | State.Error(); 874 | } 875 | } 876 | 877 | void FJSONReader::ReadBasicValue(const TCHAR& Char) 878 | { 879 | switch (Char) 880 | { 881 | case 'T': 882 | case 't': 883 | case 'F': 884 | case 'f': 885 | { 886 | State.PushBoolean(); 887 | State.Notation = EJSONNotation::STRING_SPECIAL; 888 | ReadAsStringSpecial(Char); 889 | return; 890 | } 891 | case 'N': 892 | case 'n': 893 | { 894 | State.PushNull(); 895 | State.Notation = EJSONNotation::STRING_SPECIAL; 896 | ReadAsStringSpecial(Char); 897 | return; 898 | } 899 | case '\'': 900 | case '"': 901 | { 902 | State.PushString(); 903 | State.Notation = EJSONNotation::STRING; 904 | State.Quote = Char; 905 | return; 906 | } 907 | } 908 | 909 | if ((Char >= '0' && Char <= '9') || Char == '-') 910 | { 911 | State.PushNumber(); 912 | State.Notation = EJSONNotation::NUMBER; 913 | ReadAsNumber(Char); 914 | return; 915 | } 916 | } 917 | 918 | void FJSONReader::ReadAsArray(const TCHAR& Char) 919 | { 920 | if (IsSpace(Char)) 921 | { 922 | return; 923 | } 924 | ReadBasicValue(Char); 925 | } 926 | 927 | void FJSONReader::ReadAsObject(const TCHAR& Char) 928 | { 929 | if (IsSpace(Char)) 930 | { 931 | return; 932 | } 933 | 934 | if (State.CheckTokens(EJSONToken::CURLY_BEGIN)) // read key "{" 935 | { 936 | if (Char == '\'' || Char == '"') 937 | { 938 | State.Notation = EJSONNotation::STRING; 939 | State.Quote = Char; 940 | } 941 | else 942 | { 943 | State.Notation = EJSONNotation::STRING_SPECIAL; 944 | ReadAsStringSpecial(Char); 945 | } 946 | } 947 | else if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON)) // read value "{:" 948 | { 949 | ReadBasicValue(Char); 950 | } 951 | } 952 | 953 | void FJSONReader::Skip(const TCHAR& Char) 954 | { 955 | if (!IsSpace(Char)) 956 | { 957 | State.Error(); 958 | } 959 | } 960 | 961 | bool FJSONReader::Read(const TCHAR Char) 962 | { 963 | if (Char == '\\' && !State.bEscape) 964 | { 965 | State.bEscape = true; 966 | return true; 967 | } 968 | 969 | if (FindToken(Char)) 970 | { 971 | State.Notation = EJSONNotation::NONE; 972 | UpdateNotation(); 973 | return true; 974 | } 975 | 976 | switch (State.Notation) 977 | { 978 | case EJSONNotation::NONE: UpdateNotation(); break; 979 | 980 | case EJSONNotation::STRING: ReadAsString(Char); break; 981 | case EJSONNotation::STRING_SPECIAL: ReadAsStringSpecial(Char); break; 982 | case EJSONNotation::NUMBER: ReadAsNumber(Char); break; 983 | case EJSONNotation::ARRAY: ReadAsArray(Char); break; 984 | case EJSONNotation::OBJECT: ReadAsObject(Char); break; 985 | 986 | case EJSONNotation::SKIP: Skip(Char); break; 987 | } 988 | 989 | if (State.bError) 990 | { 991 | State.Root = TSharedPtr(nullptr); 992 | State.Size = 0; 993 | return false; 994 | } 995 | 996 | State.bEscape = false; 997 | 998 | return true; 999 | } 1000 | 1001 | FJSONWriter::FJSONWriter() 1002 | { 1003 | } 1004 | 1005 | bool FJSONWriter::GetStartChar(const TSharedPtr& JsonValue, FString& Str) 1006 | { 1007 | switch (JsonValue->Type) 1008 | { 1009 | case EJson::Object: 1010 | Str = FString(TEXT("{")); 1011 | break; 1012 | case EJson::Array: 1013 | Str = FString(TEXT("[")); 1014 | break; 1015 | case EJson::String: 1016 | Str = FString(TEXT("\"")); 1017 | break; 1018 | default: 1019 | return false; 1020 | break; 1021 | } 1022 | 1023 | return true; 1024 | } 1025 | 1026 | bool FJSONWriter::GetEndChar(const TSharedPtr& JsonValue, FString& Str) 1027 | { 1028 | switch (JsonValue->Type) 1029 | { 1030 | case EJson::Object: 1031 | Str = FString(TEXT("}")); 1032 | break; 1033 | case EJson::Array: 1034 | Str = FString(TEXT("]")); 1035 | break; 1036 | case EJson::String: 1037 | Str = FString(TEXT("\"")); 1038 | break; 1039 | default: 1040 | return false; 1041 | break; 1042 | } 1043 | 1044 | return true; 1045 | } 1046 | 1047 | void FJSONWriter::Write(TSharedPtr JsonValue, FArchive* Writer, bool IsLastElement) 1048 | { 1049 | FString Str; 1050 | FArchive& Ar = *Writer; 1051 | 1052 | if (GetStartChar(JsonValue, Str)) 1053 | { 1054 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1055 | } 1056 | 1057 | switch (JsonValue->Type) 1058 | { 1059 | case EJson::Object: 1060 | { 1061 | int ElementsCount = 0; 1062 | auto Values = JsonValue->AsObject()->Values; 1063 | 1064 | for (auto& ChildJsonPair : Values) 1065 | { 1066 | Str = FString(TEXT("\"")); 1067 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1068 | 1069 | const TCHAR* BufferPtr = *ChildJsonPair.Key; 1070 | for (int i = 0; i < ChildJsonPair.Key.Len(); ++i) 1071 | { 1072 | Str = FString(1, &ChildJsonPair.Key[i]); 1073 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1074 | } 1075 | 1076 | Str = FString(TEXT("\"")); 1077 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1078 | 1079 | Str = FString(TEXT(":")); 1080 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1081 | 1082 | ++ElementsCount; 1083 | 1084 | Write(ChildJsonPair.Value, Writer, ElementsCount >= Values.Num()); 1085 | } 1086 | break; 1087 | } 1088 | case EJson::Array: 1089 | { 1090 | int ElementsCount = 0; 1091 | auto Array = JsonValue->AsArray(); 1092 | 1093 | for (auto& ChildJsonValue : Array) 1094 | { 1095 | ++ElementsCount; 1096 | Write(ChildJsonValue, Writer, ElementsCount >= Array.Num()); 1097 | } 1098 | break; 1099 | } 1100 | default: 1101 | { 1102 | const FString Value = JsonValue->AsString(); 1103 | 1104 | const TCHAR* BufferPtr = *Value; 1105 | for (int i = 0; i < Value.Len(); ++i) 1106 | { 1107 | Str = FString(1, &BufferPtr[i]); 1108 | if (Str == TEXT("\"")) 1109 | { 1110 | Str = FString(TEXT("\\")); 1111 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1112 | Str = FString(1, &BufferPtr[i]); 1113 | } 1114 | if (Str == TEXT("\n")) 1115 | { 1116 | Str = FString(TEXT("\\")); 1117 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1118 | Str = FString(TEXT("n")); 1119 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1120 | Str = FString(1, &BufferPtr[i]); 1121 | } 1122 | else if (Str == TEXT("\t")) 1123 | { 1124 | Str = FString(TEXT("\\")); 1125 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1126 | Str = FString(TEXT("t")); 1127 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1128 | Str = FString(1, &BufferPtr[i]); 1129 | } 1130 | else 1131 | { 1132 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1133 | } 1134 | } 1135 | 1136 | break; 1137 | } 1138 | } 1139 | 1140 | if (GetEndChar(JsonValue, Str)) 1141 | { 1142 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1143 | } 1144 | 1145 | if (!IsLastElement) 1146 | { 1147 | Str = FString(TEXT(",")); 1148 | UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len()); 1149 | } 1150 | } 1151 | -------------------------------------------------------------------------------- /Source/VaRest/Private/VaRestJsonParser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2019 Mail.Ru Group. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Dom/JsonValue.h" 7 | 8 | struct FUtf8Helper 9 | { 10 | /** @See FUTF8ToTCHAR_Convert::CodepointFromUtf8 */ 11 | static uint32 CodepointFromUtf8(const ANSICHAR*& SourceString, const uint32 SourceLengthRemaining); 12 | }; 13 | 14 | class FJsonValueNonConstArray : public FJsonValueArray 15 | { 16 | public: 17 | FJsonValueNonConstArray(const TArray>& InArray) 18 | : FJsonValueArray(InArray) 19 | { 20 | } 21 | 22 | /** return non const array */ 23 | TArray>& AsNonConstArray() { return Value; } 24 | }; 25 | 26 | class FJsonValueNonConstBoolean : public FJsonValueBoolean 27 | { 28 | public: 29 | FJsonValueNonConstBoolean(bool InBool) 30 | : FJsonValueBoolean(InBool) 31 | { 32 | } 33 | 34 | /** return non const bool */ 35 | bool& AsNonConstBool() { return Value; } 36 | }; 37 | 38 | class FJsonValueNonConstString : public FJsonValueString 39 | { 40 | public: 41 | FJsonValueNonConstString(const FString& InString) 42 | : FJsonValueString(InString) 43 | { 44 | } 45 | 46 | /** return non const string */ 47 | FString& AsNonConstString() { return Value; } 48 | }; 49 | 50 | class FJsonValueNonConstNumber : public FJsonValueNumber 51 | { 52 | public: 53 | FJsonValueNonConstNumber(double InNumber) 54 | : FJsonValueNumber(InNumber) 55 | { 56 | } 57 | 58 | /** return non const number */ 59 | double& AsNonConstNumber() { return Value; } 60 | }; 61 | 62 | enum class EJSONNotation 63 | { 64 | NONE, 65 | STRING, 66 | STRING_SPECIAL, 67 | SKIP, 68 | NUMBER, 69 | ARRAY, 70 | OBJECT, 71 | }; 72 | 73 | enum class EJSONToken 74 | { 75 | CURLY_BEGIN, 76 | CURLY_END, 77 | SQUARE_BEGIN, 78 | SQUARE_END, 79 | COMMA, 80 | COLON, 81 | ROOT, 82 | ERROR, 83 | }; 84 | 85 | struct FJSONState 86 | { 87 | /** Key */ 88 | FString Key; 89 | 90 | /** Data */ 91 | FString Data; 92 | 93 | /** Root object */ 94 | TSharedPtr Root; 95 | 96 | /** Object list */ 97 | TArray> Objects; 98 | 99 | /** Tokens */ 100 | TArray Tokens; 101 | 102 | /** Notation */ 103 | EJSONNotation Notation; 104 | 105 | /** Current char has escape */ 106 | bool bEscape; 107 | 108 | /** Has error */ 109 | int32 bError; 110 | 111 | /** Las quote for string */ 112 | TCHAR Quote; 113 | 114 | /** Size */ 115 | int32 Size; 116 | 117 | /** Default constructor */ 118 | FJSONState(); 119 | 120 | EJSONToken GetToken(int32 Index = 0); 121 | 122 | FORCEINLINE bool CheckTokens(EJSONToken T1); 123 | 124 | FORCEINLINE bool CheckTokens(EJSONToken T1, EJSONToken T2); 125 | 126 | FORCEINLINE bool CheckTokens(EJSONToken T1, EJSONToken T2, EJSONToken T3); 127 | 128 | FORCEINLINE void PopToken(int32 Num); 129 | 130 | FORCEINLINE void PopObject(); 131 | 132 | FORCEINLINE void PopArray(); 133 | 134 | FORCEINLINE void PopValue(bool bCheckType = true); 135 | 136 | FORCEINLINE FJsonValue* GetLast(); 137 | 138 | FORCEINLINE FJsonValueObject* GetObject(); 139 | 140 | FORCEINLINE FJsonValueNonConstArray* GetArray(); 141 | 142 | FORCEINLINE TSharedPtr PushObject(); 143 | 144 | FORCEINLINE TSharedPtr PushObject(TSharedPtr Object); 145 | 146 | FORCEINLINE TSharedPtr PushArray(); 147 | 148 | FORCEINLINE TSharedPtr PushBoolean(); 149 | 150 | FORCEINLINE TSharedPtr PushNull(); 151 | 152 | FORCEINLINE TSharedPtr PushNumber(); 153 | 154 | FORCEINLINE TSharedPtr PushString(); 155 | 156 | FORCEINLINE void ClearData(); 157 | 158 | FORCEINLINE void ClearKey(); 159 | 160 | FORCEINLINE void DataToKey(); 161 | 162 | FORCEINLINE void Error(); 163 | }; 164 | 165 | struct FJSONReader 166 | { 167 | /** State */ 168 | FJSONState State; 169 | 170 | /** Default constructor */ 171 | FJSONReader(); 172 | 173 | private: 174 | FORCEINLINE bool IsNewLine(const TCHAR& Char); 175 | 176 | FORCEINLINE bool IsSpace(const TCHAR& Char); 177 | 178 | FORCEINLINE bool FindToken(const TCHAR& Char); 179 | 180 | FORCEINLINE void UpdateNotation(); 181 | 182 | FORCEINLINE void ReadAsString(const TCHAR& Char); 183 | 184 | FORCEINLINE void ReadAsStringSpecial(const TCHAR& Char); 185 | 186 | FORCEINLINE void ReadAsNumber(const TCHAR& Char); 187 | 188 | FORCEINLINE void ReadBasicValue(const TCHAR& Char); 189 | 190 | FORCEINLINE void ReadAsArray(const TCHAR& Char); 191 | 192 | FORCEINLINE void ReadAsObject(const TCHAR& Char); 193 | 194 | FORCEINLINE void Skip(const TCHAR& Char); 195 | 196 | public: 197 | bool Read(const TCHAR Char); // @Pushkin 198 | }; 199 | 200 | struct FJSONWriter 201 | { 202 | FJSONWriter(); 203 | 204 | FORCEINLINE bool GetStartChar(const TSharedPtr& JsonValue, FString& Char); 205 | 206 | FORCEINLINE bool GetEndChar(const TSharedPtr& JsonValue, FString& Char); 207 | 208 | public: 209 | void Write(TSharedPtr JsonValue, FArchive* Writer, bool IsLastElement); // @Pushkin 210 | }; 211 | -------------------------------------------------------------------------------- /Source/VaRest/Private/VaRestJsonValue.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #include "VaRestJsonValue.h" 4 | 5 | #include "VaRestDefines.h" 6 | #include "VaRestJsonObject.h" 7 | 8 | UVaRestJsonValue::UVaRestJsonValue(const FObjectInitializer& ObjectInitializer) 9 | : Super(ObjectInitializer) 10 | { 11 | } 12 | 13 | void UVaRestJsonValue::Reset() 14 | { 15 | JsonVal = nullptr; 16 | } 17 | 18 | TSharedPtr& UVaRestJsonValue::GetRootValue() 19 | { 20 | return JsonVal; 21 | } 22 | 23 | void UVaRestJsonValue::SetRootValue(TSharedPtr& JsonValue) 24 | { 25 | JsonVal = JsonValue; 26 | } 27 | 28 | ////////////////////////////////////////////////////////////////////////// 29 | // FJsonValue API 30 | 31 | EVaJson UVaRestJsonValue::GetType() const 32 | { 33 | if (!JsonVal.IsValid()) 34 | { 35 | return EVaJson::None; 36 | } 37 | 38 | switch (JsonVal->Type) 39 | { 40 | case EJson::None: 41 | return EVaJson::None; 42 | 43 | case EJson::Null: 44 | return EVaJson::Null; 45 | 46 | case EJson::String: 47 | return EVaJson::String; 48 | 49 | case EJson::Number: 50 | return EVaJson::Number; 51 | 52 | case EJson::Boolean: 53 | return EVaJson::Boolean; 54 | 55 | case EJson::Array: 56 | return EVaJson::Array; 57 | 58 | case EJson::Object: 59 | return EVaJson::Object; 60 | 61 | default: 62 | return EVaJson::None; 63 | } 64 | } 65 | 66 | FString UVaRestJsonValue::GetTypeString() const 67 | { 68 | if (!JsonVal.IsValid()) 69 | { 70 | return "None"; 71 | } 72 | 73 | switch (JsonVal->Type) 74 | { 75 | case EJson::None: 76 | return TEXT("None"); 77 | 78 | case EJson::Null: 79 | return TEXT("Null"); 80 | 81 | case EJson::String: 82 | return TEXT("String"); 83 | 84 | case EJson::Number: 85 | return TEXT("Number"); 86 | 87 | case EJson::Boolean: 88 | return TEXT("Boolean"); 89 | 90 | case EJson::Array: 91 | return TEXT("Array"); 92 | 93 | case EJson::Object: 94 | return TEXT("Object"); 95 | 96 | default: 97 | return TEXT("None"); 98 | } 99 | } 100 | 101 | bool UVaRestJsonValue::IsNull() const 102 | { 103 | if (!JsonVal.IsValid()) 104 | { 105 | return true; 106 | } 107 | 108 | return JsonVal->IsNull(); 109 | } 110 | 111 | float UVaRestJsonValue::AsNumber() const 112 | { 113 | if (!JsonVal.IsValid()) 114 | { 115 | ErrorMessage(TEXT("Number")); 116 | return 0.f; 117 | } 118 | 119 | return static_cast(JsonVal->AsNumber()); 120 | } 121 | 122 | int32 UVaRestJsonValue::AsInt32() const 123 | { 124 | if (!JsonVal.IsValid()) 125 | { 126 | ErrorMessage(TEXT("Number")); 127 | return 0.f; 128 | } 129 | 130 | return static_cast(JsonVal->AsNumber()); 131 | } 132 | 133 | int64 UVaRestJsonValue::AsInt64() const 134 | { 135 | if (!JsonVal.IsValid()) 136 | { 137 | ErrorMessage(TEXT("Number")); 138 | return 0.f; 139 | } 140 | 141 | return static_cast(JsonVal->AsNumber()); 142 | } 143 | 144 | FString UVaRestJsonValue::AsString() const 145 | { 146 | if (!JsonVal.IsValid()) 147 | { 148 | ErrorMessage(TEXT("String")); 149 | return FString(); 150 | } 151 | 152 | return JsonVal->AsString(); 153 | } 154 | 155 | bool UVaRestJsonValue::AsBool() const 156 | { 157 | if (!JsonVal.IsValid()) 158 | { 159 | ErrorMessage(TEXT("Boolean")); 160 | return false; 161 | } 162 | 163 | return JsonVal->AsBool(); 164 | } 165 | 166 | TArray UVaRestJsonValue::AsArray() const 167 | { 168 | TArray OutArray; 169 | 170 | if (!JsonVal.IsValid()) 171 | { 172 | ErrorMessage(TEXT("Array")); 173 | return OutArray; 174 | } 175 | 176 | TArray> ValArray = JsonVal->AsArray(); 177 | for (auto Value : ValArray) 178 | { 179 | UVaRestJsonValue* NewValue = NewObject(); 180 | NewValue->SetRootValue(Value); 181 | 182 | OutArray.Add(NewValue); 183 | } 184 | 185 | return OutArray; 186 | } 187 | 188 | UVaRestJsonObject* UVaRestJsonValue::AsObject() 189 | { 190 | if (!JsonVal.IsValid()) 191 | { 192 | ErrorMessage(TEXT("Object")); 193 | return nullptr; 194 | } 195 | 196 | const TSharedPtr NewObj = JsonVal->AsObject(); 197 | 198 | UVaRestJsonObject* JsonObj = NewObject(); 199 | JsonObj->SetRootObject(NewObj); 200 | 201 | return JsonObj; 202 | } 203 | 204 | ////////////////////////////////////////////////////////////////////////// 205 | // Helpers 206 | 207 | void UVaRestJsonValue::ErrorMessage(const FString& InType) const 208 | { 209 | UE_LOG(LogVaRest, Error, TEXT("Json Value of type '%s' used as a '%s'."), *GetTypeString(), *InType); 210 | } 211 | -------------------------------------------------------------------------------- /Source/VaRest/Private/VaRestLibrary.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #include "VaRestLibrary.h" 4 | 5 | #include "VaRest.h" 6 | #include "VaRestDefines.h" 7 | #include "VaRestRequestJSON.h" 8 | 9 | #include "Engine/World.h" 10 | #include "GenericPlatform/GenericPlatformHttp.h" 11 | #include "Interfaces/IPluginManager.h" 12 | #include "Misc/Base64.h" 13 | 14 | UVaRestSettings* UVaRestLibrary::GetVaRestSettings() 15 | { 16 | return FVaRestModule::Get().GetSettings(); 17 | } 18 | 19 | FString UVaRestLibrary::PercentEncode(const FString& Source) 20 | { 21 | return FGenericPlatformHttp::UrlEncode(Source); 22 | } 23 | 24 | FString UVaRestLibrary::Base64Encode(const FString& Source) 25 | { 26 | TArray ByteArray; 27 | const FTCHARToUTF8 StringSrc = FTCHARToUTF8(Source.GetCharArray().GetData()); 28 | ByteArray.Append((uint8*)StringSrc.Get(), StringSrc.Length()); 29 | 30 | return FBase64::Encode(ByteArray); 31 | } 32 | 33 | bool UVaRestLibrary::Base64Decode(const FString& Source, FString& Dest) 34 | { 35 | TArray ByteArray; 36 | const bool Success = FBase64::Decode(Source, ByteArray); 37 | 38 | const FUTF8ToTCHAR StringSrc = FUTF8ToTCHAR((const ANSICHAR*)ByteArray.GetData(), ByteArray.Num()); 39 | Dest = FString(); 40 | Dest.AppendChars(StringSrc.Get(), StringSrc.Length()); 41 | 42 | return Success; 43 | } 44 | 45 | bool UVaRestLibrary::Base64EncodeData(const TArray& Data, FString& Dest) 46 | { 47 | if (Data.Num() > 0) 48 | { 49 | Dest = FBase64::Encode(Data); 50 | return true; 51 | } 52 | 53 | Dest = FString(); 54 | return false; 55 | } 56 | 57 | bool UVaRestLibrary::Base64DecodeData(const FString& Source, TArray& Dest) 58 | { 59 | return FBase64::Decode(Source, Dest); 60 | } 61 | 62 | FString UVaRestLibrary::StringToMd5(const FString& StringToHash) 63 | { 64 | return FMD5::HashAnsiString(*StringToHash); 65 | } 66 | 67 | FString UVaRestLibrary::StringToSha1(const FString& StringToHash) 68 | { 69 | FSHA1 Sha1Gen; 70 | 71 | Sha1Gen.Update((unsigned char*)TCHAR_TO_ANSI(*StringToHash), FCString::Strlen(*StringToHash)); 72 | Sha1Gen.Final(); 73 | 74 | FString Sha1String; 75 | for (int32 i = 0; i < 20; i++) 76 | { 77 | Sha1String += FString::Printf(TEXT("%02x"), Sha1Gen.m_digest[i]); 78 | } 79 | 80 | return Sha1String; 81 | } 82 | 83 | FString UVaRestLibrary::GetVaRestVersion() 84 | { 85 | const auto PluginRef = IPluginManager::Get().FindPlugin("VaRest"); 86 | 87 | return !PluginRef.IsValid() ? FString("invalid") : PluginRef->GetDescriptor().VersionName; 88 | } 89 | 90 | FVaRestURL UVaRestLibrary::GetWorldURL(UObject* WorldContextObject) 91 | { 92 | if (WorldContextObject) 93 | { 94 | if (UWorld* World = WorldContextObject->GetWorld()) 95 | { 96 | return FVaRestURL(World->URL); 97 | } 98 | } 99 | 100 | return FVaRestURL(); 101 | } 102 | -------------------------------------------------------------------------------- /Source/VaRest/Private/VaRestRequestJSON.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #include "VaRestRequestJSON.h" 4 | 5 | #include "VaRestDefines.h" 6 | #include "VaRestJsonObject.h" 7 | #include "VaRestJsonValue.h" 8 | #include "VaRestLibrary.h" 9 | #include "VaRestSettings.h" 10 | 11 | #include "Engine/Engine.h" 12 | #include "Engine/LatentActionManager.h" 13 | #include "Engine/World.h" 14 | #include "Interfaces/IHttpResponse.h" 15 | #include "Serialization/JsonSerializer.h" 16 | #include "Serialization/JsonWriter.h" 17 | 18 | FString UVaRestRequestJSON::DeprecatedResponseString(TEXT("DEPRECATED: Please use GetResponseContentAsString() instead")); 19 | 20 | template 21 | void FVaRestLatentAction::Cancel() 22 | { 23 | UObject* Obj = Request.Get(); 24 | if (Obj != nullptr) 25 | { 26 | ((UVaRestRequestJSON*)Obj)->Cancel(); 27 | } 28 | } 29 | 30 | UVaRestRequestJSON::UVaRestRequestJSON(const class FObjectInitializer& PCIP) 31 | : Super(PCIP) 32 | , BinaryContentType(TEXT("application/octet-stream")) 33 | { 34 | ContinueAction = nullptr; 35 | 36 | RequestVerb = EVaRestRequestVerb::GET; 37 | RequestContentType = EVaRestRequestContentType::x_www_form_urlencoded_url; 38 | 39 | ResetData(); 40 | } 41 | 42 | void UVaRestRequestJSON::SetVerb(EVaRestRequestVerb Verb) 43 | { 44 | RequestVerb = Verb; 45 | } 46 | 47 | void UVaRestRequestJSON::SetCustomVerb(FString Verb) 48 | { 49 | CustomVerb = Verb; 50 | } 51 | 52 | void UVaRestRequestJSON::SetContentType(EVaRestRequestContentType ContentType) 53 | { 54 | RequestContentType = ContentType; 55 | } 56 | 57 | void UVaRestRequestJSON::SetBinaryContentType(const FString& ContentType) 58 | { 59 | BinaryContentType = ContentType; 60 | } 61 | 62 | void UVaRestRequestJSON::SetBinaryRequestContent(const TArray& Bytes) 63 | { 64 | RequestBytes = Bytes; 65 | } 66 | 67 | void UVaRestRequestJSON::SetStringRequestContent(const FString& Content) 68 | { 69 | StringRequestContent = Content; 70 | } 71 | 72 | void UVaRestRequestJSON::SetHeader(const FString& HeaderName, const FString& HeaderValue) 73 | { 74 | RequestHeaders.Add(HeaderName, HeaderValue); 75 | } 76 | 77 | ////////////////////////////////////////////////////////////////////////// 78 | // Destruction and reset 79 | 80 | void UVaRestRequestJSON::ResetData() 81 | { 82 | ResetRequestData(); 83 | ResetResponseData(); 84 | } 85 | 86 | void UVaRestRequestJSON::ResetRequestData() 87 | { 88 | if (RequestJsonObj != nullptr) 89 | { 90 | RequestJsonObj->Reset(); 91 | } 92 | else 93 | { 94 | RequestJsonObj = NewObject(); 95 | } 96 | 97 | // See issue #90 98 | // HttpRequest = FHttpModule::Get().CreateRequest(); 99 | 100 | RequestBytes.Empty(); 101 | StringRequestContent.Empty(); 102 | } 103 | 104 | void UVaRestRequestJSON::ResetResponseData() 105 | { 106 | if (ResponseJsonObj != nullptr) 107 | { 108 | ResponseJsonObj->Reset(); 109 | } 110 | else 111 | { 112 | ResponseJsonObj = NewObject(); 113 | } 114 | 115 | if (ResponseJsonValue != nullptr) 116 | { 117 | ResponseJsonValue->Reset(); 118 | } 119 | else 120 | { 121 | ResponseJsonValue = NewObject(); 122 | } 123 | 124 | ResponseHeaders.Empty(); 125 | ResponseCode = -1; 126 | ResponseSize = 0; 127 | 128 | bIsValidJsonResponse = false; 129 | 130 | // #127 Reset string to deprecated state 131 | ResponseContent = DeprecatedResponseString; 132 | 133 | ResponseBytes.Empty(); 134 | ResponseContentLength = 0; 135 | } 136 | 137 | void UVaRestRequestJSON::Cancel() 138 | { 139 | ContinueAction = nullptr; 140 | 141 | ResetResponseData(); 142 | } 143 | 144 | ////////////////////////////////////////////////////////////////////////// 145 | // JSON data accessors 146 | 147 | UVaRestJsonObject* UVaRestRequestJSON::GetRequestObject() const 148 | { 149 | check(RequestJsonObj); 150 | return RequestJsonObj; 151 | } 152 | 153 | void UVaRestRequestJSON::SetRequestObject(UVaRestJsonObject* JsonObject) 154 | { 155 | if (JsonObject == nullptr) 156 | { 157 | UE_LOG(LogVaRest, Error, TEXT("%s: Provided JsonObject is nullptr"), *VA_FUNC_LINE); 158 | return; 159 | } 160 | 161 | RequestJsonObj = JsonObject; 162 | } 163 | 164 | UVaRestJsonObject* UVaRestRequestJSON::GetResponseObject() const 165 | { 166 | check(ResponseJsonObj); 167 | return ResponseJsonObj; 168 | } 169 | 170 | void UVaRestRequestJSON::SetResponseObject(UVaRestJsonObject* JsonObject) 171 | { 172 | if (JsonObject == nullptr) 173 | { 174 | UE_LOG(LogVaRest, Error, TEXT("%s: Provided JsonObject is nullptr"), *VA_FUNC_LINE); 175 | return; 176 | } 177 | 178 | ResponseJsonObj = JsonObject; 179 | } 180 | 181 | UVaRestJsonValue* UVaRestRequestJSON::GetResponseValue() const 182 | { 183 | check(ResponseJsonValue); 184 | return ResponseJsonValue; 185 | } 186 | 187 | /////////////////////////////////////////////////////////////////////////// 188 | // Response data access 189 | 190 | FString UVaRestRequestJSON::GetURL() const 191 | { 192 | return HttpRequest->GetURL(); 193 | } 194 | 195 | EVaRestRequestVerb UVaRestRequestJSON::GetVerb() const 196 | { 197 | return RequestVerb; 198 | } 199 | 200 | EVaRestRequestStatus UVaRestRequestJSON::GetStatus() const 201 | { 202 | return EVaRestRequestStatus((uint8)HttpRequest->GetStatus()); 203 | } 204 | 205 | int32 UVaRestRequestJSON::GetResponseCode() const 206 | { 207 | return ResponseCode; 208 | } 209 | 210 | FString UVaRestRequestJSON::GetResponseHeader(const FString& HeaderName) 211 | { 212 | FString Result; 213 | 214 | FString* Header = ResponseHeaders.Find(HeaderName); 215 | if (Header != nullptr) 216 | { 217 | Result = *Header; 218 | } 219 | 220 | return Result; 221 | } 222 | 223 | TArray UVaRestRequestJSON::GetAllResponseHeaders() const 224 | { 225 | TArray Result; 226 | for (TMap::TConstIterator It(ResponseHeaders); It; ++It) 227 | { 228 | Result.Add(It.Key() + TEXT(": ") + It.Value()); 229 | } 230 | return Result; 231 | } 232 | 233 | int32 UVaRestRequestJSON::GetResponseContentLength() const 234 | { 235 | return ResponseContentLength; 236 | } 237 | 238 | const TArray& UVaRestRequestJSON::GetResponseContent() const 239 | { 240 | return ResponseBytes; 241 | } 242 | 243 | ////////////////////////////////////////////////////////////////////////// 244 | // URL processing 245 | 246 | void UVaRestRequestJSON::SetURL(const FString& Url) 247 | { 248 | // Be sure to trim URL because it can break links on iOS 249 | FString TrimmedUrl = Url; 250 | 251 | TrimmedUrl.TrimStartInline(); 252 | TrimmedUrl.TrimEndInline(); 253 | 254 | HttpRequest->SetURL(TrimmedUrl); 255 | } 256 | 257 | void UVaRestRequestJSON::ProcessURL(const FString& Url) 258 | { 259 | SetURL(Url); 260 | ProcessRequest(); 261 | } 262 | 263 | void UVaRestRequestJSON::ApplyURL(const FString& Url, UVaRestJsonObject*& Result, UObject* WorldContextObject, FLatentActionInfo LatentInfo) 264 | { 265 | // Be sure to trim URL because it can break links on iOS 266 | FString TrimmedUrl = Url; 267 | 268 | TrimmedUrl.TrimStartInline(); 269 | TrimmedUrl.TrimEndInline(); 270 | 271 | HttpRequest->SetURL(TrimmedUrl); 272 | 273 | // Prepare latent action 274 | if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject)) 275 | { 276 | FLatentActionManager& LatentActionManager = World->GetLatentActionManager(); 277 | FVaRestLatentAction* Kont = LatentActionManager.FindExistingAction>(LatentInfo.CallbackTarget, LatentInfo.UUID); 278 | 279 | if (Kont != nullptr) 280 | { 281 | Kont->Cancel(); 282 | LatentActionManager.RemoveActionsForObject(LatentInfo.CallbackTarget); 283 | } 284 | 285 | LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, ContinueAction = new FVaRestLatentAction(this, Result, LatentInfo)); 286 | } 287 | 288 | ProcessRequest(); 289 | } 290 | 291 | void UVaRestRequestJSON::ExecuteProcessRequest() 292 | { 293 | if (HttpRequest->GetURL().Len() == 0) 294 | { 295 | UE_LOG(LogVaRest, Error, TEXT("Request execution attempt with empty URL")); 296 | return; 297 | } 298 | 299 | ProcessRequest(); 300 | } 301 | 302 | void UVaRestRequestJSON::ProcessRequest() 303 | { 304 | // Force add to root once request is launched 305 | AddToRoot(); 306 | 307 | // Set verb 308 | switch (RequestVerb) 309 | { 310 | case EVaRestRequestVerb::GET: 311 | HttpRequest->SetVerb(TEXT("GET")); 312 | break; 313 | 314 | case EVaRestRequestVerb::POST: 315 | HttpRequest->SetVerb(TEXT("POST")); 316 | break; 317 | 318 | case EVaRestRequestVerb::PUT: 319 | HttpRequest->SetVerb(TEXT("PUT")); 320 | break; 321 | 322 | case EVaRestRequestVerb::DEL: 323 | HttpRequest->SetVerb(TEXT("DELETE")); 324 | break; 325 | 326 | case EVaRestRequestVerb::CUSTOM: 327 | HttpRequest->SetVerb(CustomVerb); 328 | break; 329 | 330 | default: 331 | break; 332 | } 333 | 334 | // Set content-type 335 | switch (RequestContentType) 336 | { 337 | case EVaRestRequestContentType::x_www_form_urlencoded_url: 338 | { 339 | HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/x-www-form-urlencoded")); 340 | 341 | FString UrlParams = ""; 342 | uint16 ParamIdx = 0; 343 | 344 | // Loop through all the values and prepare additional url part 345 | for (auto RequestIt = RequestJsonObj->GetRootObject()->Values.CreateIterator(); RequestIt; ++RequestIt) 346 | { 347 | FString Key = RequestIt.Key(); 348 | FString Value = RequestIt.Value().Get()->AsString(); 349 | 350 | if (!Key.IsEmpty() && !Value.IsEmpty()) 351 | { 352 | UrlParams += ParamIdx == 0 ? "?" : "&"; 353 | UrlParams += UVaRestLibrary::PercentEncode(Key) + "=" + UVaRestLibrary::PercentEncode(Value); 354 | } 355 | 356 | ParamIdx++; 357 | } 358 | 359 | // Apply params 360 | HttpRequest->SetURL(HttpRequest->GetURL() + UrlParams); 361 | 362 | // Add optional string content 363 | if (!StringRequestContent.IsEmpty()) 364 | { 365 | HttpRequest->SetContentAsString(StringRequestContent); 366 | } 367 | 368 | // Check extended log to avoid security vulnerability (#133) 369 | if (UVaRestLibrary::GetVaRestSettings()->bExtendedLog) 370 | { 371 | UE_LOG(LogVaRest, Log, TEXT("%s: Request (urlencoded): %s %s %s %s"), *VA_FUNC_LINE, *HttpRequest->GetVerb(), *HttpRequest->GetURL(), *UrlParams, *StringRequestContent); 372 | } 373 | else 374 | { 375 | UE_LOG(LogVaRest, Log, TEXT("%s: Request (urlencoded): %s %s (check bExtendedLog for additional data)"), *VA_FUNC_LINE, *HttpRequest->GetVerb(), *HttpRequest->GetURL()); 376 | } 377 | 378 | break; 379 | } 380 | case EVaRestRequestContentType::x_www_form_urlencoded_body: 381 | { 382 | HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/x-www-form-urlencoded")); 383 | 384 | FString UrlParams = ""; 385 | uint16 ParamIdx = 0; 386 | 387 | // Add optional string content 388 | if (!StringRequestContent.IsEmpty()) 389 | { 390 | UrlParams = StringRequestContent; 391 | } 392 | else 393 | { 394 | // Loop through all the values and prepare additional url part 395 | for (auto RequestIt = RequestJsonObj->GetRootObject()->Values.CreateIterator(); RequestIt; ++RequestIt) 396 | { 397 | FString Key = RequestIt.Key(); 398 | FString Value = RequestIt.Value().Get()->AsString(); 399 | 400 | if (!Key.IsEmpty() && !Value.IsEmpty()) 401 | { 402 | UrlParams += ParamIdx == 0 ? "" : "&"; 403 | UrlParams += UVaRestLibrary::PercentEncode(Key) + "=" + UVaRestLibrary::PercentEncode(Value); 404 | } 405 | 406 | ParamIdx++; 407 | } 408 | } 409 | 410 | // Apply params 411 | HttpRequest->SetContentAsString(UrlParams); 412 | 413 | // Check extended log to avoid security vulnerability (#133) 414 | if (UVaRestLibrary::GetVaRestSettings()->bExtendedLog) 415 | { 416 | UE_LOG(LogVaRest, Log, TEXT("%s: Request (url body): %s %s %s"), *VA_FUNC_LINE, *HttpRequest->GetVerb(), *HttpRequest->GetURL(), *UrlParams); 417 | } 418 | else 419 | { 420 | UE_LOG(LogVaRest, Log, TEXT("%s: Request (url body): %s %s (check bExtendedLog for additional data)"), *VA_FUNC_LINE, *HttpRequest->GetVerb(), *HttpRequest->GetURL()); 421 | } 422 | 423 | break; 424 | } 425 | case EVaRestRequestContentType::binary: 426 | { 427 | HttpRequest->SetHeader(TEXT("Content-Type"), BinaryContentType); 428 | HttpRequest->SetContent(RequestBytes); 429 | 430 | UE_LOG(LogVaRest, Log, TEXT("Request (binary): %s %s"), *HttpRequest->GetVerb(), *HttpRequest->GetURL()); 431 | 432 | break; 433 | } 434 | case EVaRestRequestContentType::json: 435 | { 436 | HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json")); 437 | 438 | // Body is ignored for get requests, so we shouldn't place json into it even if it's empty 439 | if (RequestVerb == EVaRestRequestVerb::GET) 440 | { 441 | break; 442 | } 443 | 444 | // Serialize data to json string 445 | FString OutputString; 446 | TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString); 447 | FJsonSerializer::Serialize(RequestJsonObj->GetRootObject(), Writer); 448 | 449 | // Set Json content 450 | HttpRequest->SetContentAsString(OutputString); 451 | 452 | if (UVaRestLibrary::GetVaRestSettings()->bExtendedLog) 453 | { 454 | UE_LOG(LogVaRest, Log, TEXT("Request (json): %s %s %sJSON(%s%s%s)JSON"), *HttpRequest->GetVerb(), *HttpRequest->GetURL(), LINE_TERMINATOR, LINE_TERMINATOR, *OutputString, LINE_TERMINATOR); 455 | } 456 | else 457 | { 458 | UE_LOG(LogVaRest, Log, TEXT("Request (json): %s %s (check bExtendedLog for additional data)"), *HttpRequest->GetVerb(), *HttpRequest->GetURL()); 459 | } 460 | 461 | break; 462 | } 463 | 464 | default: 465 | break; 466 | } 467 | 468 | // Apply additional headers 469 | for (TMap::TConstIterator It(RequestHeaders); It; ++It) 470 | { 471 | HttpRequest->SetHeader(It.Key(), It.Value()); 472 | } 473 | 474 | // Bind event 475 | HttpRequest->OnProcessRequestComplete().BindUObject(this, &UVaRestRequestJSON::OnProcessRequestComplete); 476 | 477 | // Execute the request 478 | HttpRequest->ProcessRequest(); 479 | } 480 | 481 | ////////////////////////////////////////////////////////////////////////// 482 | // Request callbacks 483 | 484 | void UVaRestRequestJSON::OnProcessRequestComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) 485 | { 486 | // Remove from root on completion 487 | RemoveFromRoot(); 488 | 489 | // Be sure that we have no data from previous response 490 | ResetResponseData(); 491 | 492 | // Check we have a response and save response code as int32 493 | if (Response.IsValid()) 494 | { 495 | ResponseCode = Response->GetResponseCode(); 496 | } 497 | 498 | // Check we have result to process futher 499 | if (!bWasSuccessful || !Response.IsValid()) 500 | { 501 | UE_LOG(LogVaRest, Error, TEXT("Request failed (%d): %s"), ResponseCode, *Request->GetURL()); 502 | 503 | // Broadcast the result event 504 | OnRequestFail.Broadcast(this); 505 | OnStaticRequestFail.Broadcast(this); 506 | 507 | return; 508 | } 509 | 510 | #if PLATFORM_DESKTOP 511 | // Log response state 512 | UE_LOG(LogVaRest, Log, TEXT("Response (%d): %sJSON(%s%s%s)JSON"), ResponseCode, LINE_TERMINATOR, LINE_TERMINATOR, *Response->GetContentAsString(), LINE_TERMINATOR); 513 | #endif 514 | 515 | // Process response headers 516 | TArray Headers = Response->GetAllHeaders(); 517 | for (FString Header : Headers) 518 | { 519 | FString Key; 520 | FString Value; 521 | if (Header.Split(TEXT(": "), &Key, &Value)) 522 | { 523 | ResponseHeaders.Add(Key, Value); 524 | } 525 | } 526 | 527 | if (UVaRestLibrary::GetVaRestSettings()->bUseChunkedParser) 528 | { 529 | // Try to deserialize data to JSON 530 | const TArray& Bytes = Response->GetContent(); 531 | ResponseSize = ResponseJsonObj->DeserializeFromUTF8Bytes((const ANSICHAR*)Bytes.GetData(), Bytes.Num()); 532 | 533 | // Log errors 534 | if (ResponseSize == 0) 535 | { 536 | // As we assume it's recommended way to use current class, but not the only one, 537 | // it will be the warning instead of error 538 | UE_LOG(LogVaRest, Warning, TEXT("JSON could not be decoded!")); 539 | } 540 | } 541 | else 542 | { 543 | // Use default unreal one 544 | const TSharedRef> Reader = TJsonReaderFactory<>::Create(*Response->GetContentAsString()); 545 | TSharedPtr OutJsonValue; 546 | if (FJsonSerializer::Deserialize(Reader, OutJsonValue)) 547 | { 548 | ResponseJsonValue->SetRootValue(OutJsonValue); 549 | 550 | if (ResponseJsonValue->GetType() == EVaJson::Object) 551 | { 552 | ResponseJsonObj->SetRootObject(ResponseJsonValue->GetRootValue()->AsObject()); 553 | ResponseSize = Response->GetContentLength(); 554 | } 555 | } 556 | } 557 | 558 | // Decide whether the request was successful 559 | bIsValidJsonResponse = bWasSuccessful && (ResponseSize > 0); 560 | 561 | if (!bIsValidJsonResponse) 562 | { 563 | // Save response data as a string 564 | ResponseContent = Response->GetContentAsString(); 565 | ResponseSize = ResponseContent.GetAllocatedSize(); 566 | 567 | ResponseBytes = Response->GetContent(); 568 | ResponseContentLength = Response->GetContentLength(); 569 | } 570 | 571 | // Broadcast the result events on next tick 572 | OnRequestComplete.Broadcast(this); 573 | OnStaticRequestComplete.Broadcast(this); 574 | 575 | // Finish the latent action 576 | if (ContinueAction) 577 | { 578 | FVaRestLatentAction* K = ContinueAction; 579 | ContinueAction = nullptr; 580 | 581 | K->Call(ResponseJsonObj); 582 | } 583 | } 584 | 585 | ////////////////////////////////////////////////////////////////////////// 586 | // Tags 587 | 588 | void UVaRestRequestJSON::AddTag(FName Tag) 589 | { 590 | if (Tag != NAME_None) 591 | { 592 | Tags.AddUnique(Tag); 593 | } 594 | } 595 | 596 | int32 UVaRestRequestJSON::RemoveTag(FName Tag) 597 | { 598 | return Tags.Remove(Tag); 599 | } 600 | 601 | bool UVaRestRequestJSON::HasTag(FName Tag) const 602 | { 603 | return (Tag != NAME_None) && Tags.Contains(Tag); 604 | } 605 | 606 | ////////////////////////////////////////////////////////////////////////// 607 | // Data 608 | 609 | FString UVaRestRequestJSON::GetResponseContentAsString(bool bCacheResponseContent) 610 | { 611 | // Check we have valid json response 612 | if (!bIsValidJsonResponse) 613 | { 614 | // We've cached response content in OnProcessRequestComplete() 615 | return ResponseContent; 616 | } 617 | 618 | // Check we have valid response object 619 | if (!ResponseJsonObj || !ResponseJsonObj->IsValidLowLevel()) 620 | { 621 | // Discard previous cached string if we had one 622 | ResponseContent = DeprecatedResponseString; 623 | 624 | return TEXT("Invalid response"); 625 | } 626 | 627 | // Check if we should re-genetate it in runtime 628 | if (!bCacheResponseContent) 629 | { 630 | UE_LOG(LogVaRest, Warning, TEXT("%s: Use of uncashed getter could be slow"), *VA_FUNC_LINE); 631 | return ResponseJsonObj->EncodeJson(); 632 | } 633 | 634 | // Check that we haven't cached content yet 635 | if (ResponseContent == DeprecatedResponseString) 636 | { 637 | UE_LOG(LogVaRest, Warning, TEXT("%s: Response content string is cached"), *VA_FUNC_LINE); 638 | ResponseContent = ResponseJsonObj->EncodeJson(); 639 | } 640 | 641 | // Return previously cached content now 642 | return ResponseContent; 643 | } 644 | -------------------------------------------------------------------------------- /Source/VaRest/Private/VaRestSettings.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #include "VaRestSettings.h" 4 | 5 | UVaRestSettings::UVaRestSettings(const FObjectInitializer& ObjectInitializer) 6 | : Super(ObjectInitializer) 7 | { 8 | bExtendedLog = false; 9 | bUseChunkedParser = false; 10 | } 11 | -------------------------------------------------------------------------------- /Source/VaRest/Private/VaRestSubsystem.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2020 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #include "VaRestSubsystem.h" 4 | 5 | #include "VaRestDefines.h" 6 | #include "VaRestJsonObject.h" 7 | #include "VaRestJsonValue.h" 8 | 9 | #include "Misc/FileHelper.h" 10 | #include "Misc/Paths.h" 11 | #include "Serialization/JsonReader.h" 12 | #include "Serialization/JsonSerializer.h" 13 | #include "Subsystems/SubsystemBlueprintLibrary.h" 14 | 15 | UVaRestSubsystem::UVaRestSubsystem() 16 | : UEngineSubsystem() 17 | { 18 | } 19 | 20 | void UVaRestSubsystem::Initialize(FSubsystemCollectionBase& Collection) 21 | { 22 | Super::Initialize(Collection); 23 | 24 | UE_LOG(LogVaRest, Log, TEXT("%s: VaRest subsystem initialized"), *VA_FUNC_LINE); 25 | } 26 | 27 | void UVaRestSubsystem::Deinitialize() 28 | { 29 | // Do nothing for now 30 | Super::Deinitialize(); 31 | } 32 | 33 | void UVaRestSubsystem::CallURL(const FString& URL, EVaRestRequestVerb Verb, EVaRestRequestContentType ContentType, UVaRestJsonObject* VaRestJson, const FVaRestCallDelegate& Callback) 34 | { 35 | // Check we have valid data json 36 | if (VaRestJson == nullptr) 37 | { 38 | VaRestJson = ConstructVaRestJsonObject(); 39 | } 40 | 41 | UVaRestRequestJSON* Request = ConstructVaRestRequest(); 42 | 43 | Request->SetVerb(Verb); 44 | Request->SetContentType(ContentType); 45 | Request->SetRequestObject(VaRestJson); 46 | 47 | FVaRestCallResponse Response; 48 | Response.Request = Request; 49 | Response.Callback = Callback; 50 | 51 | Response.CompleteDelegateHandle = Request->OnStaticRequestComplete.AddUObject(this, &UVaRestSubsystem::OnCallComplete); 52 | Response.FailDelegateHandle = Request->OnStaticRequestFail.AddUObject(this, &UVaRestSubsystem::OnCallComplete); 53 | 54 | RequestMap.Add(Request, Response); 55 | 56 | Request->ResetResponseData(); 57 | Request->ProcessURL(URL); 58 | } 59 | 60 | void UVaRestSubsystem::OnCallComplete(UVaRestRequestJSON* Request) 61 | { 62 | if (!RequestMap.Contains(Request)) 63 | { 64 | return; 65 | } 66 | 67 | const auto Response = RequestMap.Find(Request); 68 | Request->OnStaticRequestComplete.Remove(Response->CompleteDelegateHandle); 69 | Request->OnStaticRequestFail.Remove(Response->FailDelegateHandle); 70 | 71 | Response->Callback.ExecuteIfBound(Request); 72 | Response->Request = nullptr; 73 | RequestMap.Remove(Request); 74 | } 75 | 76 | UVaRestRequestJSON* UVaRestSubsystem::ConstructVaRestRequest() 77 | { 78 | return NewObject(this); 79 | } 80 | 81 | UVaRestRequestJSON* UVaRestSubsystem::ConstructVaRestRequestExt(EVaRestRequestVerb Verb, EVaRestRequestContentType ContentType) 82 | { 83 | UVaRestRequestJSON* Request = ConstructVaRestRequest(); 84 | 85 | Request->SetVerb(Verb); 86 | Request->SetContentType(ContentType); 87 | 88 | return Request; 89 | } 90 | 91 | UVaRestJsonObject* UVaRestSubsystem::ConstructVaRestJsonObject() 92 | { 93 | return NewObject(this); 94 | } 95 | 96 | UVaRestJsonObject* UVaRestSubsystem::StaticConstructVaRestJsonObject() 97 | { 98 | auto SelfSystem = CastChecked(USubsystemBlueprintLibrary::GetEngineSubsystem(UVaRestSubsystem::StaticClass()), ECastCheckedType::NullChecked); 99 | return SelfSystem->ConstructVaRestJsonObject(); 100 | } 101 | 102 | UVaRestJsonValue* UVaRestSubsystem::ConstructJsonValueNumber(float Number) 103 | { 104 | TSharedPtr NewVal = MakeShareable(new FJsonValueNumber(Number)); 105 | 106 | UVaRestJsonValue* NewValue = NewObject(this); 107 | NewValue->SetRootValue(NewVal); 108 | 109 | return NewValue; 110 | } 111 | 112 | UVaRestJsonValue* UVaRestSubsystem::ConstructJsonValueString(const FString& StringValue) 113 | { 114 | TSharedPtr NewVal = MakeShareable(new FJsonValueString(StringValue)); 115 | 116 | UVaRestJsonValue* NewValue = NewObject(this); 117 | NewValue->SetRootValue(NewVal); 118 | 119 | return NewValue; 120 | } 121 | 122 | UVaRestJsonValue* UVaRestSubsystem::ConstructJsonValueBool(bool InValue) 123 | { 124 | TSharedPtr NewVal = MakeShareable(new FJsonValueBoolean(InValue)); 125 | 126 | UVaRestJsonValue* NewValue = NewObject(this); 127 | NewValue->SetRootValue(NewVal); 128 | 129 | return NewValue; 130 | } 131 | 132 | UVaRestJsonValue* UVaRestSubsystem::ConstructJsonValueArray(const TArray& InArray) 133 | { 134 | // Prepare data array to create new value 135 | TArray> ValueArray; 136 | for (auto InVal : InArray) 137 | { 138 | ValueArray.Add(InVal->GetRootValue()); 139 | } 140 | 141 | TSharedPtr NewVal = MakeShareable(new FJsonValueArray(ValueArray)); 142 | 143 | UVaRestJsonValue* NewValue = NewObject(this); 144 | NewValue->SetRootValue(NewVal); 145 | 146 | return NewValue; 147 | } 148 | 149 | UVaRestJsonValue* UVaRestSubsystem::ConstructJsonValueObject(UVaRestJsonObject* JsonObject) 150 | { 151 | TSharedPtr NewVal = MakeShareable(new FJsonValueObject(JsonObject->GetRootObject())); 152 | 153 | UVaRestJsonValue* NewValue = NewObject(this); 154 | NewValue->SetRootValue(NewVal); 155 | 156 | return NewValue; 157 | } 158 | 159 | UVaRestJsonValue* UVaRestSubsystem::ConstructJsonValue(const TSharedPtr& InValue) 160 | { 161 | TSharedPtr NewVal = InValue; 162 | 163 | UVaRestJsonValue* NewValue = NewObject(this); 164 | NewValue->SetRootValue(NewVal); 165 | 166 | return NewValue; 167 | } 168 | 169 | UVaRestJsonValue* UVaRestSubsystem::DecodeJsonValue(const FString& JsonString) 170 | { 171 | const TSharedRef> Reader = TJsonReaderFactory<>::Create(*JsonString); 172 | TSharedPtr OutJsonValue; 173 | if (FJsonSerializer::Deserialize(Reader, OutJsonValue)) 174 | { 175 | return ConstructJsonValue(OutJsonValue); 176 | } 177 | 178 | return nullptr; 179 | } 180 | 181 | UVaRestJsonObject* UVaRestSubsystem::DecodeJsonObject(const FString& JsonString) 182 | { 183 | const TSharedRef> Reader = TJsonReaderFactory<>::Create(*JsonString); 184 | TSharedPtr OutJsonObj; 185 | if (FJsonSerializer::Deserialize(Reader, OutJsonObj)) 186 | { 187 | auto NewJsonObj = NewObject(this); 188 | NewJsonObj->SetRootObject(OutJsonObj); 189 | return NewJsonObj; 190 | } 191 | 192 | return nullptr; 193 | } 194 | 195 | class UVaRestJsonObject* UVaRestSubsystem::LoadJsonFromFile(const FString& Path, const bool bIsRelativeToContentDir) 196 | { 197 | auto* Json = ConstructVaRestJsonObject(); 198 | 199 | FString JSONString; 200 | if (FFileHelper::LoadFileToString(JSONString, *(bIsRelativeToContentDir ? FPaths::ProjectContentDir() / Path : Path))) 201 | { 202 | if (Json->DecodeJson(JSONString)) 203 | { 204 | return Json; 205 | } 206 | else 207 | { 208 | UE_LOG(LogVaRest, Error, TEXT("%s: Can't decode json from file %s"), *VA_FUNC_LINE, *Path); 209 | } 210 | } 211 | else 212 | { 213 | UE_LOG(LogVaRest, Error, TEXT("%s: Can't open file %s"), *VA_FUNC_LINE, *Path); 214 | } 215 | 216 | return nullptr; 217 | } 218 | -------------------------------------------------------------------------------- /Source/VaRest/Public/VaRest.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Modules/ModuleManager.h" 6 | 7 | class UVaRestSettings; 8 | 9 | class FVaRestModule : public IModuleInterface 10 | { 11 | public: 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | 16 | /** 17 | * Singleton-like access to this module's interface. This is just for convenience! 18 | * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. 19 | * 20 | * @return Returns singleton instance, loading the module on demand if needed 21 | */ 22 | static inline FVaRestModule& Get() 23 | { 24 | return FModuleManager::LoadModuleChecked("VaRest"); 25 | } 26 | 27 | /** 28 | * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. 29 | * 30 | * @return True if the module is loaded and ready to use 31 | */ 32 | static inline bool IsAvailable() 33 | { 34 | return FModuleManager::Get().IsModuleLoaded("VaRest"); 35 | } 36 | 37 | /** Getter for internal settings object to support runtime configuration changes */ 38 | UVaRestSettings* GetSettings() const; 39 | 40 | protected: 41 | /** Module settings */ 42 | UVaRestSettings* ModuleSettings; 43 | }; 44 | -------------------------------------------------------------------------------- /Source/VaRest/Public/VaRestDefines.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Runtime/Launch/Resources/Version.h" 6 | 7 | #include "CoreMinimal.h" 8 | 9 | #include "Logging/LogCategory.h" 10 | #include "Logging/LogMacros.h" 11 | #include "Logging/LogVerbosity.h" 12 | 13 | // You should place include statements to your module's private header files here. You only need to 14 | // add includes for headers that are used in most of your module's source files though. 15 | 16 | DECLARE_LOG_CATEGORY_EXTERN(LogVaRest, Log, All); 17 | 18 | #define VA_FUNC (FString(__FUNCTION__)) // Current Class Name + Function Name where this is called 19 | #define VA_LINE (FString::FromInt(__LINE__)) // Current Line Number in the code where this is called 20 | #define VA_FUNC_LINE (VA_FUNC + "(" + VA_LINE + ")") // Current Class and Line Number where this is called! 21 | -------------------------------------------------------------------------------- /Source/VaRest/Public/VaRestJsonObject.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "VaRestDefines.h" 6 | 7 | #include "Dom/JsonObject.h" 8 | #include "Templates/UnrealTypeTraits.h" 9 | 10 | #include "VaRestJsonObject.generated.h" 11 | 12 | class UVaRestJsonValue; 13 | 14 | /** 15 | * Blueprintable FJsonObject wrapper 16 | */ 17 | UCLASS(BlueprintType, Blueprintable) 18 | class VAREST_API UVaRestJsonObject : public UObject 19 | { 20 | GENERATED_UCLASS_BODY() 21 | 22 | public: 23 | /** Reset all internal data */ 24 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 25 | void Reset(); 26 | 27 | /** Get the root Json object */ 28 | TSharedRef& GetRootObject(); 29 | 30 | /** Set the root Json object */ 31 | void SetRootObject(const TSharedPtr& JsonObject); 32 | 33 | ////////////////////////////////////////////////////////////////////////// 34 | // Serialization 35 | 36 | /** Serialize Json to string (formatted with line breaks) */ 37 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 38 | FString EncodeJson() const; 39 | 40 | /** Serialize Json to string (single string without line breaks) */ 41 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 42 | FString EncodeJsonToSingleString() const; 43 | 44 | /** Construct Json object from string */ 45 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 46 | bool DecodeJson(const FString& JsonString, bool bUseIncrementalParser = true); 47 | 48 | ////////////////////////////////////////////////////////////////////////// 49 | // FJsonObject API 50 | 51 | /** Gets the type of value as string for a given field */ 52 | UFUNCTION(BlueprintPure, Category = "VaRest|Json") 53 | FString GetFieldTypeString(const FString& FieldName) const; 54 | 55 | /** Returns a list of field names that exist in the object */ 56 | UFUNCTION(BlueprintPure, Category = "VaRest|Json") 57 | TArray GetFieldNames() const; 58 | 59 | /** Checks to see if the FieldName exists in the object */ 60 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 61 | bool HasField(const FString& FieldName) const; 62 | 63 | /** Remove field named FieldName */ 64 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 65 | void RemoveField(const FString& FieldName); 66 | 67 | /** Get the field named FieldName as a JsonValue */ 68 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 69 | UVaRestJsonValue* GetField(const FString& FieldName) const; 70 | 71 | /** Add a field named FieldName with a Value */ 72 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 73 | void SetField(const FString& FieldName, UVaRestJsonValue* JsonValue); 74 | 75 | /** Get the field named FieldName as a Json Array */ 76 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 77 | TArray GetArrayField(const FString& FieldName) const; 78 | 79 | /** Set an ObjectField named FieldName and value of Json Array */ 80 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 81 | void SetArrayField(const FString& FieldName, const TArray& InArray); 82 | 83 | /** Adds all of the fields from one json object to this one */ 84 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 85 | void MergeJsonObject(UVaRestJsonObject* InJsonObject, bool Overwrite); 86 | 87 | ////////////////////////////////////////////////////////////////////////// 88 | // FJsonObject API Helpers (easy to use with simple Json objects) 89 | 90 | /** Get the field named FieldName as a number. Ensures that the field is present and is of type Json number. 91 | * Attn.!! float used instead of double to make the function blueprintable! */ 92 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 93 | float GetNumberField(const FString& FieldName) const; 94 | 95 | /** DEPRECATED Attn.!! float used instead of double to make the function blueprintable! */ 96 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 97 | void SetNumberField(const FString& FieldName, float Number); 98 | 99 | /** Add a field named FieldName with Number as value */ 100 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 101 | void SetNumberFieldDouble(const FString& FieldName, double Number); 102 | 103 | /** Get the field named FieldName as an Integer. Ensures that the field is present and is of type Json number. */ 104 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 105 | int32 GetIntegerField(const FString& FieldName) const; 106 | 107 | /** Add a field named FieldName with Integer as value. */ 108 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 109 | void SetIntegerField(const FString& FieldName, int32 Number); 110 | 111 | /** Get the field named FieldName as an Int64. Ensures that the field is present and is of type Json number. */ 112 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 113 | int64 GetInt64Field(const FString& FieldName) const; 114 | 115 | /** Add a field named FieldName with Int64 as value. */ 116 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 117 | void SetInt64Field(const FString& FieldName, int64 Number); 118 | 119 | /** Get the field named FieldName as a string. */ 120 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 121 | FString GetStringField(const FString& FieldName) const; 122 | 123 | /** Add a field named FieldName with value of StringValue */ 124 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 125 | void SetStringField(const FString& FieldName, const FString& StringValue); 126 | 127 | /** Get the field named FieldName as a boolean. */ 128 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 129 | bool GetBoolField(const FString& FieldName) const; 130 | 131 | /** Set a boolean field named FieldName and value of InValue */ 132 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 133 | void SetBoolField(const FString& FieldName, bool InValue); 134 | 135 | /** Get the field named FieldName as a Json object. */ 136 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 137 | UVaRestJsonObject* GetObjectField(const FString& FieldName) const; 138 | 139 | /** Set an ObjectField named FieldName and value of JsonObject */ 140 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 141 | void SetObjectField(const FString& FieldName, UVaRestJsonObject* JsonObject); 142 | 143 | /** Set a map of fields with String values */ 144 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 145 | void SetMapFields_string(const TMap& Fields); 146 | 147 | /** Set a map of fields with uint8 values */ 148 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 149 | void SetMapFields_uint8(const TMap& Fields); 150 | 151 | /** Set a map of fields with int32 values */ 152 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 153 | void SetMapFields_int32(const TMap& Fields); 154 | 155 | /** Set a map of fields with int64 values */ 156 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 157 | void SetMapFields_int64(const TMap& Fields); 158 | 159 | /** Set a map of fields with bool values */ 160 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 161 | void SetMapFields_bool(const TMap& Fields); 162 | 163 | private: 164 | /** Internal implementation for setting map fields. */ 165 | template 166 | void SetMapFields_Impl(const TMap& Fields) 167 | { 168 | for (auto& field : Fields) 169 | { 170 | // No need to support all int types as they're not supported by BP 171 | if (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) 172 | { 173 | SetNumberField(field.Key, field.Value); 174 | } 175 | else if (std::is_same_v) 176 | { 177 | SetBoolField(field.Key, (bool)field.Value); 178 | } 179 | } 180 | } 181 | 182 | /** Internal implementation to get number arrays of different types */ 183 | template 184 | TArray GetTypeArrayField(const FString& FieldName) const 185 | { 186 | TArray NumberArray; 187 | if (!JsonObj->HasTypedField(FieldName) || FieldName.IsEmpty()) 188 | { 189 | UE_LOG(LogVaRest, Warning, TEXT("%s: No field with name %s of type Array"), *VA_FUNC_LINE, *FieldName); 190 | return NumberArray; 191 | } 192 | 193 | const TArray> JsonArrayValues = JsonObj->GetArrayField(FieldName); 194 | for (TArray>::TConstIterator It(JsonArrayValues); It; ++It) 195 | { 196 | const auto Value = (*It).Get(); 197 | if (Value->Type != EJson::Number) 198 | { 199 | UE_LOG(LogVaRest, Error, TEXT("Not Number element in array with field name %s"), *FieldName); 200 | } 201 | 202 | NumberArray.Add((*It)->AsNumber()); 203 | } 204 | 205 | return NumberArray; 206 | } 207 | 208 | ////////////////////////////////////////////////////////////////////////// 209 | // Array fields helpers (uniform arrays) 210 | 211 | public: 212 | /** Get the field named FieldName as a Number Array. Use it only if you're sure that array is uniform! 213 | * Attn.!! float used instead of double to make the function blueprintable! */ 214 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 215 | TArray GetNumberArrayField(const FString& FieldName) const; 216 | 217 | /** Get the field named FieldName as a Number Array. Use it only if you're sure that array is uniform! */ 218 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 219 | TArray GetIntegerArrayField(const FString& FieldName) const; 220 | 221 | /** DEPRECATED Attn.!! float used instead of double to make the function blueprintable! */ 222 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 223 | void SetNumberArrayField(const FString& FieldName, const TArray& NumberArray); 224 | 225 | /** Set an ObjectField named FieldName and value of Number Array */ 226 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 227 | void SetNumberArrayFieldDouble(const FString& FieldName, const TArray& NumberArray); 228 | 229 | /** Get the field named FieldName as a String Array. Use it only if you're sure that array is uniform! */ 230 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 231 | TArray GetStringArrayField(const FString& FieldName) const; 232 | 233 | /** Set an ObjectField named FieldName and value of String Array */ 234 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 235 | void SetStringArrayField(const FString& FieldName, const TArray& StringArray); 236 | 237 | /** Get the field named FieldName as a Bool Array. Use it only if you're sure that array is uniform! */ 238 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 239 | TArray GetBoolArrayField(const FString& FieldName) const; 240 | 241 | /** Set an ObjectField named FieldName and value of Bool Array */ 242 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 243 | void SetBoolArrayField(const FString& FieldName, const TArray& BoolArray); 244 | 245 | /** Get the field named FieldName as an Object Array. Use it only if you're sure that array is uniform! */ 246 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 247 | TArray GetObjectArrayField(const FString& FieldName) const; 248 | 249 | /** Set an ObjectField named FieldName and value of Ob Array */ 250 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 251 | void SetObjectArrayField(const FString& FieldName, const TArray& ObjectArray); 252 | 253 | ////////////////////////////////////////////////////////////////////////// 254 | // Deserialize 255 | 256 | public: 257 | /** Deserialize byte content to json */ 258 | int32 DeserializeFromUTF8Bytes(const ANSICHAR* Bytes, int32 Size); 259 | 260 | /** Deserialize byte content to json */ 261 | int32 DeserializeFromTCHARBytes(const TCHAR* Bytes, int32 Size); 262 | 263 | /** Deserialize byte stream from reader */ 264 | void DecodeFromArchive(TUniquePtr& Reader); 265 | 266 | ////////////////////////////////////////////////////////////////////////// 267 | // Serialize 268 | 269 | public: 270 | /** Save json to file */ 271 | bool WriteToFile(const FString& Path) const; 272 | 273 | /** 274 | * Blueprint Save json to filepath 275 | * 276 | * @param bIsRelativeToProjectDir If set to 'false' path is treated as absolute 277 | */ 278 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 279 | bool WriteToFilePath(const FString& Path, const bool bIsRelativeToProjectDir = true); 280 | 281 | static bool WriteStringToArchive(FArchive& Ar, const TCHAR* StrPtr, int64 Len); 282 | 283 | ////////////////////////////////////////////////////////////////////////// 284 | // Data 285 | 286 | private: 287 | /** Internal JSON data */ 288 | TSharedRef JsonObj; 289 | }; 290 | -------------------------------------------------------------------------------- /Source/VaRest/Public/VaRestJsonValue.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "VaRestJsonValue.generated.h" 6 | 7 | class UVaRestJsonObject; 8 | class FJsonValue; 9 | 10 | /** 11 | * Represents all the types a Json Value can be. 12 | */ 13 | UENUM(BlueprintType) 14 | enum class EVaJson : uint8 15 | { 16 | None, 17 | Null, 18 | String, 19 | Number, 20 | Boolean, 21 | Array, 22 | Object, 23 | }; 24 | 25 | /** 26 | * Blueprintable FJsonValue wrapper 27 | */ 28 | UCLASS(BlueprintType, Blueprintable) 29 | class VAREST_API UVaRestJsonValue : public UObject 30 | { 31 | GENERATED_UCLASS_BODY() 32 | 33 | public: 34 | /** Reset all internal data */ 35 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 36 | void Reset(); 37 | 38 | /** Get the root Json value */ 39 | TSharedPtr& GetRootValue(); 40 | 41 | /** Set the root Json value */ 42 | void SetRootValue(TSharedPtr& JsonValue); 43 | 44 | ////////////////////////////////////////////////////////////////////////// 45 | // FJsonValue API 46 | 47 | /** Get type of Json value (Enum) */ 48 | UFUNCTION(BlueprintPure, Category = "VaRest|Json") 49 | EVaJson GetType() const; 50 | 51 | /** Get type of Json value (String) */ 52 | UFUNCTION(BlueprintPure, Category = "VaRest|Json") 53 | FString GetTypeString() const; 54 | 55 | /** Returns true if this value is a 'null' */ 56 | UFUNCTION(BlueprintPure, Category = "VaRest|Json") 57 | bool IsNull() const; 58 | 59 | /** Returns this value as a double, throwing an error if this is not an Json Number 60 | * Attn.!! float used instead of double to make the function blueprintable! */ 61 | UFUNCTION(BlueprintPure, Category = "VaRest|Json") 62 | float AsNumber() const; 63 | 64 | /** Returns this value as a int32, throwing an error if this is not an Json Number */ 65 | UFUNCTION(BlueprintPure, Category = "VaRest|Json") 66 | int32 AsInt32() const; 67 | 68 | /** Returns this value as a int64, throwing an error if this is not an Json Number */ 69 | UFUNCTION(BlueprintPure, Category = "VaRest|Json") 70 | int64 AsInt64() const; 71 | 72 | /** Returns this value as a string, throwing an error if this is not an Json String */ 73 | UFUNCTION(BlueprintPure, Category = "VaRest|Json") 74 | FString AsString() const; 75 | 76 | /** Returns this value as a boolean, throwing an error if this is not an Json Bool */ 77 | UFUNCTION(BlueprintPure, Category = "VaRest|Json") 78 | bool AsBool() const; 79 | 80 | /** Returns this value as an array, throwing an error if this is not an Json Array */ 81 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 82 | TArray AsArray() const; 83 | 84 | /** Returns this value as an object, throwing an error if this is not an Json Object */ 85 | UFUNCTION(BlueprintCallable, Category = "VaRest|Json") 86 | UVaRestJsonObject* AsObject(); 87 | 88 | ////////////////////////////////////////////////////////////////////////// 89 | // Data 90 | 91 | private: 92 | /** Internal JSON data */ 93 | TSharedPtr JsonVal; 94 | 95 | ////////////////////////////////////////////////////////////////////////// 96 | // Helpers 97 | 98 | protected: 99 | /** Simple error logger */ 100 | void ErrorMessage(const FString& InType) const; 101 | }; 102 | -------------------------------------------------------------------------------- /Source/VaRest/Public/VaRestLibrary.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Kismet/BlueprintFunctionLibrary.h" 6 | 7 | #include "VaRestTypes.h" 8 | 9 | #include "VaRestLibrary.generated.h" 10 | 11 | class UVaRestSettings; 12 | 13 | /** 14 | * Useful tools for REST communications 15 | */ 16 | UCLASS() 17 | class VAREST_API UVaRestLibrary : public UBlueprintFunctionLibrary 18 | { 19 | GENERATED_BODY() 20 | 21 | ////////////////////////////////////////////////////////////////////////// 22 | // Data Accessors 23 | public: 24 | /** Direct access to the plugin settings */ 25 | UFUNCTION(BlueprintPure, Category = "VaRest|Common") 26 | static UVaRestSettings* GetVaRestSettings(); 27 | 28 | ////////////////////////////////////////////////////////////////////////// 29 | // Helpers 30 | 31 | public: 32 | /** Applies percent-encoding to text */ 33 | UFUNCTION(BlueprintPure, Category = "VaRest|Utility") 34 | static FString PercentEncode(const FString& Source); 35 | 36 | /** 37 | * Encodes a FString into a Base64 string 38 | * 39 | * @param Source The string data to convert 40 | * @return A string that encodes the binary data in a way that can be safely transmitted via various Internet protocols 41 | */ 42 | UFUNCTION(BlueprintPure, Category = "VaRest|Utility", meta = (DisplayName = "Base64 Encode")) 43 | static FString Base64Encode(const FString& Source); 44 | 45 | /** 46 | * Decodes a Base64 string into a FString 47 | * 48 | * @param Source The stringified data to convert 49 | * @param Dest The out buffer that will be filled with the decoded data 50 | * @return True if the buffer was decoded, false if it failed to decode 51 | */ 52 | UFUNCTION(BlueprintPure, Category = "VaRest|Utility", meta = (DisplayName = "Base64 Decode")) 53 | static bool Base64Decode(const FString& Source, FString& Dest); 54 | 55 | /** 56 | * Encodes a byte array into a Base64 string 57 | * 58 | * @param Dara The data to convert 59 | * @return A string that encodes the binary data in a way that can be safely transmitted via various Internet protocols 60 | */ 61 | UFUNCTION(BlueprintPure, Category = "VaRest|Utility", meta = (DisplayName = "Base64 Encode Data")) 62 | static bool Base64EncodeData(const TArray& Data, FString& Dest); 63 | 64 | /** 65 | * Decodes a Base64 string into a byte array 66 | * 67 | * @param Source The stringified data to convert 68 | * @param Dest The out buffer that will be filled with the decoded data 69 | * @return True if the buffer was decoded, false if it failed to decode 70 | */ 71 | UFUNCTION(BlueprintPure, Category = "VaRest|Utility", meta = (DisplayName = "Base64 Decode Data")) 72 | static bool Base64DecodeData(const FString& Source, TArray& Dest); 73 | 74 | /** 75 | * Helper to perform the very common case of hashing an ASCII string into a hex representation. 76 | * 77 | * @param String Hex representation of the hash (32 lower-case hex digits) 78 | */ 79 | UFUNCTION(BlueprintPure, Category = "VaRest|Utility", meta = (DisplayName = "String to MD5")) 80 | static FString StringToMd5(const FString& StringToHash); 81 | 82 | /** 83 | * Helper to perform the SHA1 hash operation on string. 84 | */ 85 | UFUNCTION(BlueprintPure, Category = "VaRest|Utility", meta = (DisplayName = "String to SHA1")) 86 | static FString StringToSha1(const FString& StringToHash); 87 | 88 | /** 89 | * Helper method to convert a status code from HTTP to an enum for easier readability 90 | */ 91 | UFUNCTION(BlueprintPure, Category = "VaRest|Utility", meta = (DisplayName = "HTTP Status Int To Enum")) 92 | static FORCEINLINE EVaRestHttpStatusCode::Type HTTPStatusIntToEnum(int32 StatusCode) { return (EVaRestHttpStatusCode::Type)StatusCode; } 93 | 94 | /** 95 | * Get the plugin's version 96 | */ 97 | UFUNCTION(BlueprintPure, Category = "VaRest|Utility", meta = (DisplayName = "Get VaRest Version")) 98 | static FString GetVaRestVersion(); 99 | 100 | ////////////////////////////////////////////////////////////////////////// 101 | // Common Network Helpers 102 | 103 | public: 104 | /** 105 | * Get the URL that was used when loading this World 106 | */ 107 | UFUNCTION(BlueprintPure, Category = "VaRest|Utility", meta = (WorldContext = "WorldContextObject")) 108 | static FVaRestURL GetWorldURL(UObject* WorldContextObject); 109 | }; 110 | -------------------------------------------------------------------------------- /Source/VaRest/Public/VaRestRequestJSON.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Engine/LatentActionManager.h" 6 | #include "Http.h" 7 | #include "HttpModule.h" 8 | #include "Interfaces/IHttpRequest.h" 9 | #include "LatentActions.h" 10 | 11 | #include "VaRestTypes.h" 12 | 13 | #include "VaRestRequestJSON.generated.h" 14 | 15 | class UVaRestJsonValue; 16 | class UVaRestJsonObject; 17 | class UVaRestSettings; 18 | 19 | /** 20 | * @author Original latent action class by https://github.com/unktomi 21 | */ 22 | template 23 | class FVaRestLatentAction : public FPendingLatentAction 24 | { 25 | public: 26 | virtual void Call(const T& Value) 27 | { 28 | Result = Value; 29 | Called = true; 30 | } 31 | 32 | void operator()(const T& Value) 33 | { 34 | Call(Value); 35 | } 36 | 37 | void Cancel(); 38 | 39 | FVaRestLatentAction(FWeakObjectPtr RequestObj, T& ResultParam, const FLatentActionInfo& LatentInfo) 40 | : Called(false) 41 | , Request(RequestObj) 42 | , ExecutionFunction(LatentInfo.ExecutionFunction) 43 | , OutputLink(LatentInfo.Linkage) 44 | , CallbackTarget(LatentInfo.CallbackTarget) 45 | , Result(ResultParam) 46 | { 47 | } 48 | 49 | virtual void UpdateOperation(FLatentResponse& Response) override 50 | { 51 | Response.FinishAndTriggerIf(Called, ExecutionFunction, OutputLink, CallbackTarget); 52 | } 53 | 54 | virtual void NotifyObjectDestroyed() 55 | { 56 | Cancel(); 57 | } 58 | 59 | virtual void NotifyActionAborted() 60 | { 61 | Cancel(); 62 | } 63 | 64 | private: 65 | bool Called; 66 | FWeakObjectPtr Request; 67 | 68 | public: 69 | const FName ExecutionFunction; 70 | const int32 OutputLink; 71 | const FWeakObjectPtr CallbackTarget; 72 | T& Result; 73 | }; 74 | 75 | /** Generate a delegates for callback events */ 76 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestComplete, class UVaRestRequestJSON*, Request); 77 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestFail, class UVaRestRequestJSON*, Request); 78 | 79 | DECLARE_MULTICAST_DELEGATE_OneParam(FOnStaticRequestComplete, class UVaRestRequestJSON*); 80 | DECLARE_MULTICAST_DELEGATE_OneParam(FOnStaticRequestFail, class UVaRestRequestJSON*); 81 | 82 | /** 83 | * General helper class http requests via blueprints 84 | */ 85 | UCLASS(BlueprintType, Blueprintable) 86 | class VAREST_API UVaRestRequestJSON : public UObject 87 | { 88 | GENERATED_UCLASS_BODY() 89 | 90 | public: 91 | ////////////////////////////////////////////////////////////////////////// 92 | // Construction 93 | 94 | /** Set verb to the request */ 95 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 96 | void SetVerb(EVaRestRequestVerb Verb); 97 | 98 | /** Set custom verb to the request */ 99 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 100 | void SetCustomVerb(FString Verb); 101 | 102 | /** Set content type to the request. If you're using the x-www-form-urlencoded, 103 | * params/constaints should be defined as key=ValueString pairs from Json data */ 104 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 105 | void SetContentType(EVaRestRequestContentType ContentType); 106 | 107 | /** Set content type of the request for binary post data */ 108 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 109 | void SetBinaryContentType(const FString& ContentType); 110 | 111 | /** Set content of the request for binary post data */ 112 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 113 | void SetBinaryRequestContent(const TArray& Content); 114 | 115 | /** Set content of the request as a plain string */ 116 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 117 | void SetStringRequestContent(const FString& Content); 118 | 119 | /** Sets optional header info */ 120 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 121 | void SetHeader(const FString& HeaderName, const FString& HeaderValue); 122 | 123 | ////////////////////////////////////////////////////////////////////////// 124 | // Destruction and reset 125 | 126 | /** Reset all internal saved data */ 127 | UFUNCTION(BlueprintCallable, Category = "VaRest|Utility") 128 | void ResetData(); 129 | 130 | /** Reset saved request data */ 131 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 132 | void ResetRequestData(); 133 | 134 | /** Reset saved response data */ 135 | UFUNCTION(BlueprintCallable, Category = "VaRest|Response") 136 | void ResetResponseData(); 137 | 138 | /** Cancel latent response waiting */ 139 | UFUNCTION(BlueprintCallable, Category = "VaRest|Response") 140 | void Cancel(); 141 | 142 | ////////////////////////////////////////////////////////////////////////// 143 | // JSON data accessors 144 | 145 | /** Get the Request Json object */ 146 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 147 | UVaRestJsonObject* GetRequestObject() const; 148 | 149 | /** Set the Request Json object */ 150 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 151 | void SetRequestObject(UVaRestJsonObject* JsonObject); 152 | 153 | /** Get the Response Json object */ 154 | UFUNCTION(BlueprintCallable, Category = "VaRest|Response") 155 | UVaRestJsonObject* GetResponseObject() const; 156 | 157 | /** Set the Response Json object */ 158 | UFUNCTION(BlueprintCallable, Category = "VaRest|Response") 159 | void SetResponseObject(UVaRestJsonObject* JsonObject); 160 | 161 | /** Get the Response Json value */ 162 | UFUNCTION(BlueprintCallable, Category = "VaRest|Response") 163 | UVaRestJsonValue* GetResponseValue() const; 164 | 165 | /////////////////////////////////////////////////////////////////////////// 166 | // Request/response data access 167 | 168 | /** Get url of http request */ 169 | UFUNCTION(BlueprintPure, Category = "VaRest|Request") 170 | FString GetURL() const; 171 | 172 | /** Get verb to the request */ 173 | UFUNCTION(BlueprintPure, Category = "VaRest|Request") 174 | EVaRestRequestVerb GetVerb() const; 175 | 176 | /** Get status of http request */ 177 | UFUNCTION(BlueprintPure, Category = "VaRest|Request") 178 | EVaRestRequestStatus GetStatus() const; 179 | 180 | /** Get the response code of the last query */ 181 | UFUNCTION(BlueprintPure, Category = "VaRest|Response") 182 | int32 GetResponseCode() const; 183 | 184 | /** Get value of desired response header */ 185 | UFUNCTION(BlueprintPure, Category = "VaRest|Response") 186 | FString GetResponseHeader(const FString& HeaderName); 187 | 188 | /** Get list of all response headers */ 189 | UFUNCTION(BlueprintPure, Category = "VaRest|Response") 190 | TArray GetAllResponseHeaders() const; 191 | 192 | /** 193 | * Shortcut to get the Content-Length header value. Will not always return non-zero. 194 | * If you want the real length of the payload, get the payload and check it's length. 195 | * 196 | * @return the content length (if available) 197 | */ 198 | UFUNCTION(BlueprintPure, Category = "VaRest|Response") 199 | int32 GetResponseContentLength() const; 200 | 201 | /** 202 | * Get the content payload of the request or response. 203 | * 204 | * @param Content - array that will be filled with the content. 205 | */ 206 | UFUNCTION(BlueprintPure, Category = "VaRest|Response") 207 | const TArray& GetResponseContent() const; 208 | 209 | ////////////////////////////////////////////////////////////////////////// 210 | // URL processing 211 | 212 | public: 213 | /** Setting request URL */ 214 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 215 | void SetURL(const FString& Url = TEXT("http://alyamkin.com")); 216 | 217 | /** Open URL with current setup */ 218 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 219 | virtual void ProcessURL(const FString& Url = TEXT("http://alyamkin.com")); 220 | 221 | /** Open URL in latent mode */ 222 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request", meta = (Latent, LatentInfo = "LatentInfo", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject")) 223 | virtual void ApplyURL(const FString& Url, UVaRestJsonObject*& Result, UObject* WorldContextObject, struct FLatentActionInfo LatentInfo); 224 | 225 | /** Check URL and execute process request */ 226 | UFUNCTION(BlueprintCallable, Category = "VaRest|Request") 227 | virtual void ExecuteProcessRequest(); 228 | 229 | protected: 230 | /** Apply current internal setup to request and process it */ 231 | void ProcessRequest(); 232 | 233 | ////////////////////////////////////////////////////////////////////////// 234 | // Request callbacks 235 | 236 | private: 237 | /** Internal bind function for the IHTTPRequest::OnProcessRequestCompleted() event */ 238 | void OnProcessRequestComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); 239 | 240 | public: 241 | /** Event occured when the request has been completed */ 242 | UPROPERTY(BlueprintAssignable, Category = "VaRest|Event") 243 | FOnRequestComplete OnRequestComplete; 244 | 245 | /** Event occured when the request wasn't successfull */ 246 | UPROPERTY(BlueprintAssignable, Category = "VaRest|Event") 247 | FOnRequestFail OnRequestFail; 248 | 249 | /** Event occured when the request has been completed */ 250 | FOnStaticRequestComplete OnStaticRequestComplete; 251 | 252 | /** Event occured when the request wasn't successfull */ 253 | FOnStaticRequestFail OnStaticRequestFail; 254 | 255 | ////////////////////////////////////////////////////////////////////////// 256 | // Tags 257 | 258 | public: 259 | /** Add tag to this request */ 260 | UFUNCTION(BlueprintCallable, Category = "VaRest|Utility") 261 | void AddTag(FName Tag); 262 | 263 | /** 264 | * Remove tag from this request 265 | * 266 | * @return Number of removed elements 267 | */ 268 | UFUNCTION(BlueprintCallable, Category = "VaRest|Utility") 269 | int32 RemoveTag(FName Tag); 270 | 271 | /** See if this request contains the supplied tag */ 272 | UFUNCTION(BlueprintCallable, Category = "VaRest|Utility") 273 | bool HasTag(FName Tag) const; 274 | 275 | protected: 276 | /** Array of tags that can be used for grouping and categorizing */ 277 | TArray Tags; 278 | 279 | ////////////////////////////////////////////////////////////////////////// 280 | // Data 281 | 282 | public: 283 | /** 284 | * Get request response stored as a string 285 | * @param bCacheResponseContent - Set true if you plan to use it few times to prevent deserialization each time 286 | */ 287 | UFUNCTION(BlueprintCallable, Category = "VaRest|Response") 288 | FString GetResponseContentAsString(bool bCacheResponseContent = true); 289 | 290 | public: 291 | /** Response size */ 292 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "VaRest|Response") 293 | int32 ResponseSize; 294 | 295 | /** DEPRECATED: Please use GetResponseContentAsString() instead */ 296 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "VaRest|Response") 297 | FString ResponseContent; 298 | 299 | /** Is the response valid JSON? */ 300 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "VaRest|Response") 301 | bool bIsValidJsonResponse; 302 | 303 | protected: 304 | /** Default value for deprecated ResponseContent variable */ 305 | static FString DeprecatedResponseString; 306 | 307 | protected: 308 | /** Latent action helper */ 309 | FVaRestLatentAction* ContinueAction; 310 | 311 | /** Internal request data stored as JSON */ 312 | UPROPERTY() 313 | UVaRestJsonObject* RequestJsonObj; 314 | 315 | TArray RequestBytes; 316 | FString BinaryContentType; 317 | 318 | /** Raw response storage */ 319 | TArray ResponseBytes; 320 | int32 ResponseContentLength; 321 | 322 | /** Used for special cases when used wants to have plain string data in request. 323 | * Attn.! Content-type x-www-form-urlencoded only. */ 324 | FString StringRequestContent; 325 | 326 | /** Response data stored as JSON */ 327 | UPROPERTY() 328 | UVaRestJsonObject* ResponseJsonObj; 329 | 330 | /** Response data stored as JSON value */ 331 | UPROPERTY() 332 | UVaRestJsonValue* ResponseJsonValue; 333 | 334 | /** Verb for making request (GET,POST,etc) */ 335 | EVaRestRequestVerb RequestVerb; 336 | 337 | /** Content type to be applied for request */ 338 | EVaRestRequestContentType RequestContentType; 339 | 340 | /** Mapping of header section to values. Used to generate final header string for request */ 341 | TMap RequestHeaders; 342 | 343 | /** Cached key/value header pairs. Parsed once request completes */ 344 | TMap ResponseHeaders; 345 | 346 | /** Http Response code */ 347 | int32 ResponseCode; 348 | 349 | /** Custom verb that will be used with RequestContentType == CUSTOM */ 350 | FString CustomVerb; 351 | 352 | /** Request we're currently processing */ 353 | TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); 354 | 355 | public: 356 | /** Returns reference to internal request object */ 357 | TSharedRef GetHttpRequest() const { return HttpRequest; }; 358 | }; 359 | -------------------------------------------------------------------------------- /Source/VaRest/Public/VaRestSettings.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "VaRestSettings.generated.h" 6 | 7 | UCLASS(config = Engine, defaultconfig) 8 | class VAREST_API UVaRestSettings : public UObject 9 | { 10 | GENERATED_UCLASS_BODY() 11 | 12 | public: 13 | /** You can disable request content logging to avoid security vulnerability */ 14 | UPROPERTY(Config, EditAnywhere, Category = "VaRest") 15 | bool bExtendedLog; 16 | 17 | /** Use custom chunked parses (best for memory, but has issues with hex-encoded utf-8) */ 18 | UPROPERTY(Config, EditAnywhere, Category = "VaRest") 19 | bool bUseChunkedParser; 20 | }; 21 | -------------------------------------------------------------------------------- /Source/VaRest/Public/VaRestSubsystem.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2020 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "VaRestJsonObject.h" 6 | #include "VaRestJsonValue.h" 7 | #include "VaRestRequestJSON.h" 8 | 9 | #include "Subsystems/EngineSubsystem.h" 10 | 11 | #include "VaRestSubsystem.generated.h" 12 | 13 | DECLARE_DYNAMIC_DELEGATE_OneParam(FVaRestCallDelegate, UVaRestRequestJSON*, Request); 14 | 15 | USTRUCT() 16 | struct FVaRestCallResponse 17 | { 18 | GENERATED_BODY() 19 | 20 | UPROPERTY() 21 | UVaRestRequestJSON* Request; 22 | 23 | UPROPERTY() 24 | FVaRestCallDelegate Callback; 25 | 26 | FDelegateHandle CompleteDelegateHandle; 27 | FDelegateHandle FailDelegateHandle; 28 | 29 | FVaRestCallResponse() 30 | : Request(nullptr) 31 | { 32 | } 33 | }; 34 | 35 | UCLASS() 36 | class VAREST_API UVaRestSubsystem : public UEngineSubsystem 37 | { 38 | GENERATED_BODY() 39 | 40 | public: 41 | UVaRestSubsystem(); 42 | 43 | // Begin USubsystem 44 | virtual void Initialize(FSubsystemCollectionBase& Collection) override; 45 | virtual void Deinitialize() override; 46 | // End USubsystem 47 | 48 | ////////////////////////////////////////////////////////////////////////// 49 | // Easy URL processing 50 | 51 | public: 52 | /** Easy way to process http requests */ 53 | UFUNCTION(BlueprintCallable, Category = "VaRest|Utility") 54 | void CallURL(const FString& URL, EVaRestRequestVerb Verb, EVaRestRequestContentType ContentType, UVaRestJsonObject* VaRestJson, const FVaRestCallDelegate& Callback); 55 | 56 | /** Called when URL is processed (one for both success/unsuccess events)*/ 57 | void OnCallComplete(UVaRestRequestJSON* Request); 58 | 59 | protected: 60 | UPROPERTY() 61 | TMap RequestMap; 62 | 63 | ////////////////////////////////////////////////////////////////////////// 64 | // Construction helpers 65 | 66 | public: 67 | /** Creates new request (totally empty) */ 68 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Construct Json Request (Empty)"), Category = "VaRest|Subsystem") 69 | UVaRestRequestJSON* ConstructVaRestRequest(); 70 | 71 | /** Creates new request with defined verb and content type */ 72 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Construct Json Request"), Category = "VaRest|Subsystem") 73 | UVaRestRequestJSON* ConstructVaRestRequestExt(EVaRestRequestVerb Verb, EVaRestRequestContentType ContentType); 74 | 75 | /** Create new Json object */ 76 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Construct Json Object"), Category = "VaRest|Subsystem") 77 | UVaRestJsonObject* ConstructVaRestJsonObject(); 78 | 79 | /** Create new Json object (static one for MakeJson node, hack for #293) */ 80 | UFUNCTION() 81 | static UVaRestJsonObject* StaticConstructVaRestJsonObject(); 82 | 83 | /** Create new Json Number value 84 | * Attn.!! float used instead of double to make the function blueprintable! */ 85 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Construct Json Number Value"), Category = "VaRest|Subsystem") 86 | UVaRestJsonValue* ConstructJsonValueNumber(float Number); 87 | 88 | /** Create new Json String value */ 89 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Construct Json String Value"), Category = "VaRest|Subsystem") 90 | UVaRestJsonValue* ConstructJsonValueString(const FString& StringValue); 91 | 92 | /** Create new Json Bool value */ 93 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Construct Json Bool Value"), Category = "VaRest|Subsystem") 94 | UVaRestJsonValue* ConstructJsonValueBool(bool InValue); 95 | 96 | /** Create new Json Array value */ 97 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Construct Json Array Value"), Category = "VaRest|Subsystem") 98 | UVaRestJsonValue* ConstructJsonValueArray(const TArray& InArray); 99 | 100 | /** Create new Json Object value */ 101 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Construct Json Object Value"), Category = "VaRest|Subsystem") 102 | UVaRestJsonValue* ConstructJsonValueObject(UVaRestJsonObject* JsonObject); 103 | 104 | /** Create new Json value from FJsonValue (to be used from VaRestJsonObject) */ 105 | UVaRestJsonValue* ConstructJsonValue(const TSharedPtr& InValue); 106 | 107 | ////////////////////////////////////////////////////////////////////////// 108 | // Serialization 109 | 110 | public: 111 | /** Construct Json value from string */ 112 | UFUNCTION(BlueprintCallable, Category = "VaRest|Subsystem") 113 | UVaRestJsonValue* DecodeJsonValue(const FString& JsonString); 114 | 115 | /** Construct Json object from string */ 116 | UFUNCTION(BlueprintCallable, Category = "VaRest|Subsystem") 117 | UVaRestJsonObject* DecodeJsonObject(const FString& JsonString); 118 | 119 | ////////////////////////////////////////////////////////////////////////// 120 | // File system integration 121 | 122 | public: 123 | /** 124 | * Load JSON from formatted text file 125 | * @param bIsRelativeToContentDir if set to 'false' path is treated as absolute 126 | */ 127 | UFUNCTION(BlueprintCallable, Category = "VaRest|Utility") 128 | UVaRestJsonObject* LoadJsonFromFile(const FString& Path, const bool bIsRelativeToContentDir = true); 129 | }; 130 | -------------------------------------------------------------------------------- /Source/VaRest/Public/VaRestTypes.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Engine/EngineBaseTypes.h" 6 | 7 | #include "VaRestTypes.generated.h" 8 | 9 | /** Verb (GET, PUT, POST) used by the request */ 10 | UENUM(BlueprintType) 11 | enum class EVaRestRequestVerb : uint8 12 | { 13 | GET, 14 | POST, 15 | PUT, 16 | DEL UMETA(DisplayName = "DELETE"), 17 | /** Set CUSTOM verb by SetCustomVerb() function */ 18 | CUSTOM 19 | }; 20 | 21 | // clang-format off 22 | /** Content type (json, urlencoded, etc.) used by the request */ 23 | UENUM(BlueprintType) 24 | enum class EVaRestRequestContentType : uint8 25 | { 26 | x_www_form_urlencoded_url UMETA(DisplayName = "x-www-form-urlencoded (URL)"), 27 | x_www_form_urlencoded_body UMETA(DisplayName = "x-www-form-urlencoded (Request Body)"), 28 | json, 29 | binary 30 | }; 31 | // clang-format on 32 | 33 | /** Enumerates the current state of an Http request */ 34 | UENUM(BlueprintType) 35 | enum class EVaRestRequestStatus : uint8 36 | { 37 | /** Has not been started via ProcessRequest() */ 38 | NotStarted, 39 | /** Currently being ticked and processed */ 40 | Processing, 41 | /** Finished but failed */ 42 | Failed, 43 | /** Failed because it was unable to connect (safe to retry) */ 44 | Failed_ConnectionError, 45 | /** Finished and was successful */ 46 | Succeeded 47 | }; 48 | 49 | // Taken from Interfaces/IHttpResponse.h (had to make BlueprintType :/) 50 | UENUM(BlueprintType) 51 | namespace EVaRestHttpStatusCode 52 | { 53 | /** 54 | * Response codes that can come back from an Http request 55 | */ 56 | enum Type 57 | { 58 | // status code not set yet 59 | Unknown = 0 UMETA(DisplayName = "Unknown = 0"), 60 | // the request can be continued. 61 | Continue = 100 UMETA(DisplayName = "Continue = 100"), 62 | // the server has switched protocols in an upgrade header. 63 | SwitchProtocol = 101 UMETA(DisplayName = "SwitchProtocol = 101"), 64 | // the request completed successfully. 65 | Ok = 200 UMETA(DisplayName = "Ok = 200"), 66 | // the request has been fulfilled and resulted in the creation of a new resource. 67 | Created = 201 UMETA(DisplayName = "Created = 201"), 68 | // the request has been accepted for processing, but the processing has not been completed. 69 | Accepted = 202 UMETA(DisplayName = "Accepted = 202"), 70 | // the returned meta information in the entity-header is not the definitive set available from the origin server. 71 | Partial = 203 UMETA(DisplayName = "Partial = 203"), 72 | // the server has fulfilled the request, but there is no new information to send back. 73 | NoContent = 204 UMETA(DisplayName = "NoContent = 204"), 74 | // the request has been completed, and the client program should reset the document view that caused the request to be sent to allow the user to easily initiate another input action. 75 | ResetContent = 205 UMETA(DisplayName = "ResetContent = 205"), 76 | // the server has fulfilled the partial get request for the resource. 77 | PartialContent = 206 UMETA(DisplayName = "PartialContent = 206"), 78 | // the server couldn't decide what to return. 79 | Ambiguous = 300 UMETA(DisplayName = "Ambiguous = 300"), 80 | // the requested resource has been assigned to a new permanent uri (uniform resource identifier), and any future references to this resource should be done using one of the returned uris. 81 | Moved = 301 UMETA(DisplayName = "Moved = 301"), 82 | // the requested resource resides temporarily under a different uri (uniform resource identifier). 83 | Redirect = 302 UMETA(DisplayName = "Redirect = 302"), 84 | // the response to the request can be found under a different uri (uniform resource identifier) and should be retrieved using a get http verb on that resource. 85 | RedirectMethod = 303 UMETA(DisplayName = "RedirectMethod = 303"), 86 | // the requested resource has not been modified. 87 | NotModified = 304 UMETA(DisplayName = "NotModified = 304"), 88 | // the requested resource must be accessed through the proxy given by the location field. 89 | UseProxy = 305 UMETA(DisplayName = "UseProxy = 305"), 90 | // the redirected request keeps the same http verb. http/1.1 behavior. 91 | RedirectKeepVerb = 307 UMETA(DisplayName = "RedirectKeepVerb = 307"), 92 | // the request could not be processed by the server due to invalid syntax. 93 | BadRequest = 400 UMETA(DisplayName = "BadRequest = 400"), 94 | // the requested resource requires user authentication. 95 | Denied = 401 UMETA(DisplayName = "Denied = 401"), 96 | // not currently implemented in the http protocol. 97 | PaymentReq = 402 UMETA(DisplayName = "PaymentReq = 402"), 98 | // the server understood the request, but is refusing to fulfill it. 99 | Forbidden = 403 UMETA(DisplayName = "Forbidden = 403"), 100 | // the server has not found anything matching the requested uri (uniform resource identifier). 101 | NotFound = 404 UMETA(DisplayName = "NotFound = 404"), 102 | // the http verb used is not allowed. 103 | BadMethod = 405 UMETA(DisplayName = "BadMethod = 405"), 104 | // no responses acceptable to the client were found. 105 | NoneAcceptable = 406 UMETA(DisplayName = "NoneAcceptable = 406"), 106 | // proxy authentication required. 107 | ProxyAuthReq = 407 UMETA(DisplayName = "ProxyAuthReq = 407"), 108 | // the server timed out waiting for the request. 109 | RequestTimeout = 408 UMETA(DisplayName = "RequestTimeout = 408"), 110 | // the request could not be completed due to a conflict with the current state of the resource. the user should resubmit with more information. 111 | Conflict = 409 UMETA(DisplayName = "Conflict = 409"), 112 | // the requested resource is no longer available at the server, and no forwarding address is known. 113 | Gone = 410 UMETA(DisplayName = "Gone = 410"), 114 | // the server refuses to accept the request without a defined content length. 115 | LengthRequired = 411 UMETA(DisplayName = "LengthRequired = 411"), 116 | // the precondition given in one or more of the request header fields evaluated to false when it was tested on the server. 117 | PrecondFailed = 412 UMETA(DisplayName = "PrecondFailed = 412"), 118 | // the server is refusing to process a request because the request entity is larger than the server is willing or able to process. 119 | RequestTooLarge = 413 UMETA(DisplayName = "RequestTooLarge = 413"), 120 | // the server is refusing to service the request because the request uri (uniform resource identifier) is longer than the server is willing to interpret. 121 | UriTooLong = 414 UMETA(DisplayName = "UriTooLong = 414"), 122 | // the server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method. 123 | UnsupportedMedia = 415 UMETA(DisplayName = "UnsupportedMedia = 415"), 124 | // too many requests, the server is throttling 125 | TooManyRequests = 429 UMETA(DisplayName = "TooManyRequests = 429"), 126 | // the request should be retried after doing the appropriate action. 127 | RetryWith = 449 UMETA(DisplayName = "RetryWith = 449"), 128 | // the server encountered an unexpected condition that prevented it from fulfilling the request. 129 | ServerError = 500 UMETA(DisplayName = "ServerError = 500"), 130 | // the server does not support the functionality required to fulfill the request. 131 | NotSupported = 501 UMETA(DisplayName = "NotSupported = 501"), 132 | // the server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request. 133 | BadGateway = 502 UMETA(DisplayName = "BadGateway = 502"), 134 | // the service is temporarily overloaded. 135 | ServiceUnavail = 503 UMETA(DisplayName = "ServiceUnavail = 503"), 136 | // the request was timed out waiting for a gateway. 137 | GatewayTimeout = 504 UMETA(DisplayName = "GatewayTimeout = 504"), 138 | // the server does not support, or refuses to support, the http protocol version that was used in the request message. 139 | VersionNotSup = 505 UMETA(DisplayName = "VersionNotSup = 505") 140 | }; 141 | } // namespace EVaRestHttpStatusCode 142 | 143 | /** 144 | * FURL structure wrapper for BP access 145 | */ 146 | USTRUCT(BlueprintType) 147 | struct VAREST_API FVaRestURL 148 | { 149 | GENERATED_BODY() 150 | 151 | /** Protocol, i.e. "unreal" or "http" */ 152 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "URL") 153 | FString Protocol; 154 | 155 | /** Optional hostname, i.e. "204.157.115.40" or "unreal.epicgames.com", blank if local. */ 156 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "URL") 157 | FString Host; 158 | 159 | /** Optional host port */ 160 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "URL") 161 | int32 Port; 162 | 163 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "URL") 164 | int32 Valid; 165 | 166 | /** Map name, i.e. "SkyCity", default is "Entry" */ 167 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "URL") 168 | FString Map; 169 | 170 | /** Optional place to download Map if client does not possess it */ 171 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "URL") 172 | FString RedirectURL; 173 | 174 | /** Options */ 175 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "URL") 176 | TArray Op; 177 | 178 | /** Portal to enter through, default is "" */ 179 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "URL") 180 | FString Portal; 181 | 182 | FVaRestURL() 183 | : Port(0) 184 | , Valid(0) 185 | { 186 | } 187 | 188 | FVaRestURL(FURL& InUrl) 189 | : Protocol(InUrl.Protocol) 190 | , Host(InUrl.Host) 191 | , Port(InUrl.Port) 192 | , Valid(InUrl.Valid) 193 | , Map(InUrl.Map) 194 | , RedirectURL(InUrl.RedirectURL) 195 | , Op(InUrl.Op) 196 | , Portal(InUrl.Portal) 197 | { 198 | } 199 | }; 200 | -------------------------------------------------------------------------------- /Source/VaRest/VaRest.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | using System.IO; 4 | 5 | namespace UnrealBuildTool.Rules 6 | { 7 | public class VaRest : ModuleRules 8 | { 9 | public VaRest(ReadOnlyTargetRules Target) : base(Target) 10 | { 11 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 12 | PrecompileForTargets = PrecompileTargetsType.Any; 13 | DefaultBuildSettings = BuildSettingsVersion.V5; 14 | 15 | PrivateIncludePaths.AddRange( 16 | new string[] { 17 | "VaRest/Private", 18 | // ... add other private include paths required here ... 19 | }); 20 | 21 | PublicDependencyModuleNames.AddRange( 22 | new string[] 23 | { 24 | "Core", 25 | "CoreUObject", 26 | "Engine", 27 | "HTTP", 28 | "Json", 29 | "Projects" // Required by IPluginManager etc (used to get plugin information) 30 | // ... add other public dependencies that you statically link with here ... 31 | }); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/VaRestEditor/Private/VaRestEditor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #include "VaRestEditor.h" 4 | 5 | #include "Modules/ModuleManager.h" 6 | 7 | #define LOCTEXT_NAMESPACE "FVaRestEditorModule" 8 | 9 | void FVaRestEditorModule::StartupModule() 10 | { 11 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 12 | } 13 | 14 | void FVaRestEditorModule::ShutdownModule() 15 | { 16 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 17 | // we call this function before unloading the module. 18 | } 19 | 20 | #undef LOCTEXT_NAMESPACE 21 | 22 | IMPLEMENT_MODULE(FVaRestEditorModule, VaRestEditor) 23 | -------------------------------------------------------------------------------- /Source/VaRestEditor/Private/VaRest_BreakJson.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | // Original code by https://github.com/unktomi 3 | 4 | #include "VaRest_BreakJson.h" 5 | 6 | #include "BlueprintActionDatabaseRegistrar.h" 7 | #include "BlueprintNodeSpawner.h" 8 | #include "EdGraph/EdGraph.h" 9 | #include "EdGraph/EdGraphNodeUtils.h" // for FNodeTextCache 10 | #include "EdGraphSchema_K2.h" 11 | #include "EdGraphUtilities.h" 12 | #include "EditorCategoryUtils.h" 13 | #include "KismetCompiler.h" 14 | #include "Runtime/Launch/Resources/Version.h" 15 | 16 | #define LOCTEXT_NAMESPACE "VaRest_BreakJson" 17 | 18 | class FKCHandler_BreakJson : public FNodeHandlingFunctor 19 | { 20 | 21 | public: 22 | FKCHandler_BreakJson(FKismetCompilerContext& InCompilerContext) 23 | : FNodeHandlingFunctor(InCompilerContext) 24 | { 25 | } 26 | 27 | virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override 28 | { 29 | UEdGraphPin* InputPin = nullptr; 30 | 31 | for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) 32 | { 33 | UEdGraphPin* Pin = Node->Pins[PinIndex]; 34 | if (Pin && (EGPD_Input == Pin->Direction)) 35 | { 36 | InputPin = Pin; 37 | break; 38 | } 39 | } 40 | 41 | UEdGraphPin* InNet = FEdGraphUtilities::GetNetFromPin(InputPin); 42 | UClass* Class = Cast(StaticLoadObject(UClass::StaticClass(), nullptr, TEXT("class'VaRest.VaRestJsonObject'"))); 43 | 44 | FBPTerminal** SourceTerm = Context.NetMap.Find(InNet); 45 | if (SourceTerm == nullptr) 46 | { 47 | return; 48 | } 49 | 50 | for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) 51 | { 52 | UEdGraphPin* Pin = Node->Pins[PinIndex]; 53 | if (Pin && (EGPD_Output == Pin->Direction)) 54 | { 55 | if (Pin->LinkedTo.Num() < 1) 56 | { 57 | continue; 58 | } 59 | 60 | FBPTerminal** Target = Context.NetMap.Find(Pin); 61 | 62 | const FName& FieldName = Pin->PinName; 63 | const FName& FieldType = Pin->PinType.PinCategory; 64 | 65 | FBPTerminal* FieldNameTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); 66 | FieldNameTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_String; 67 | FieldNameTerm->SourcePin = Pin; 68 | 69 | FieldNameTerm->Name = FieldName.ToString(); 70 | FieldNameTerm->TextLiteral = FText::FromName(FieldName); 71 | 72 | FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node); 73 | FName FunctionName; 74 | 75 | const bool bIsArray = Pin->PinType.ContainerType == EPinContainerType::Array; 76 | if (FieldType == CompilerContext.GetSchema()->PC_Boolean) 77 | { 78 | FunctionName = bIsArray ? TEXT("GetBoolArrayField") : TEXT("GetBoolField"); 79 | } 80 | else if (FieldType == CompilerContext.GetSchema()->PC_Real) 81 | { 82 | FunctionName = bIsArray ? TEXT("GetNumberArrayField") : TEXT("GetNumberField"); 83 | } 84 | else if (FieldType == CompilerContext.GetSchema()->PC_String) 85 | { 86 | FunctionName = bIsArray ? TEXT("GetStringArrayField") : TEXT("GetStringField"); 87 | } 88 | else if (FieldType == CompilerContext.GetSchema()->PC_Object) 89 | { 90 | FunctionName = bIsArray ? TEXT("GetObjectArrayField") : TEXT("GetObjectField"); 91 | } 92 | else 93 | { 94 | continue; 95 | } 96 | 97 | UFunction* FunctionPtr = Class->FindFunctionByName(FunctionName); 98 | Statement.Type = KCST_CallFunction; 99 | Statement.FunctionToCall = FunctionPtr; 100 | Statement.FunctionContext = *SourceTerm; 101 | Statement.bIsParentContext = false; 102 | Statement.LHS = *Target; 103 | Statement.RHS.Add(FieldNameTerm); 104 | } 105 | } 106 | } 107 | 108 | FBPTerminal* RegisterInputTerm(FKismetFunctionContext& Context, UVaRest_BreakJson* Node) 109 | { 110 | // Find input pin 111 | UEdGraphPin* InputPin = nullptr; 112 | for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) 113 | { 114 | UEdGraphPin* Pin = Node->Pins[PinIndex]; 115 | if (Pin && (EGPD_Input == Pin->Direction)) 116 | { 117 | InputPin = Pin; 118 | break; 119 | } 120 | } 121 | check(NULL != InputPin); 122 | 123 | // Find structure source net 124 | UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(InputPin); 125 | FBPTerminal** TermPtr = Context.NetMap.Find(Net); 126 | 127 | if (!TermPtr) 128 | { 129 | FBPTerminal* Term = Context.CreateLocalTerminalFromPinAutoChooseScope(Net, Context.NetNameMap->MakeValidName(Net)); 130 | 131 | Context.NetMap.Add(Net, Term); 132 | 133 | return Term; 134 | } 135 | 136 | return *TermPtr; 137 | } 138 | 139 | void RegisterOutputTerm(FKismetFunctionContext& Context, UEdGraphPin* OutputPin, FBPTerminal* ContextTerm) 140 | { 141 | FBPTerminal* Term = Context.CreateLocalTerminalFromPinAutoChooseScope(OutputPin, Context.NetNameMap->MakeValidName(OutputPin)); 142 | Context.NetMap.Add(OutputPin, Term); 143 | } 144 | 145 | virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* InNode) override 146 | { 147 | UVaRest_BreakJson* Node = Cast(InNode); 148 | FNodeHandlingFunctor::RegisterNets(Context, Node); 149 | 150 | check(NULL != Node); 151 | 152 | if (FBPTerminal* StructContextTerm = RegisterInputTerm(Context, Node)) 153 | { 154 | for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) 155 | { 156 | UEdGraphPin* Pin = Node->Pins[PinIndex]; 157 | if (nullptr != Pin && EGPD_Output == Pin->Direction) 158 | { 159 | RegisterOutputTerm(Context, Pin, StructContextTerm); 160 | } 161 | } 162 | } 163 | } 164 | }; 165 | 166 | /** 167 | * Main node class 168 | */ 169 | UVaRest_BreakJson::UVaRest_BreakJson(const FObjectInitializer& ObjectInitializer) 170 | : Super(ObjectInitializer) 171 | { 172 | } 173 | 174 | FNodeHandlingFunctor* UVaRest_BreakJson::CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const 175 | { 176 | return new FKCHandler_BreakJson(CompilerContext); 177 | } 178 | 179 | void UVaRest_BreakJson::AllocateDefaultPins() 180 | { 181 | const UEdGraphSchema_K2* K2Schema = GetDefault(); 182 | 183 | UClass* Class = Cast(StaticLoadObject(UClass::StaticClass(), nullptr, TEXT("class'VaRest.VaRestJsonObject'"))); 184 | UEdGraphPin* Pin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(""), Class, TEXT("Target")); 185 | 186 | K2Schema->SetPinAutogeneratedDefaultValueBasedOnType(Pin); 187 | 188 | CreateProjectionPins(Pin); 189 | } 190 | 191 | FLinearColor UVaRest_BreakJson::GetNodeTitleColor() const 192 | { 193 | return FLinearColor(255.0f, 255.0f, 0.0f); 194 | } 195 | 196 | void UVaRest_BreakJson::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) 197 | { 198 | const FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None; 199 | if (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(UVaRest_BreakJson, Outputs) || 200 | PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FVaRest_NamedType, Name) || 201 | PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FVaRest_NamedType, Type) || 202 | PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FVaRest_NamedType, bIsArray)) 203 | { 204 | ReconstructNode(); 205 | GetGraph()->NotifyGraphChanged(); 206 | } 207 | 208 | Super::PostEditChangeProperty(PropertyChangedEvent); 209 | } 210 | 211 | void UVaRest_BreakJson::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const 212 | { 213 | // actions get registered under specific object-keys; the idea is that 214 | // actions might have to be updated (or deleted) if their object-key is 215 | // mutated (or removed)... here we use the node's class (so if the node 216 | // type disappears, then the action should go with it) 217 | UClass* ActionKey = GetClass(); 218 | 219 | // to keep from needlessly instantiating a UBlueprintNodeSpawner, first 220 | // check to make sure that the registrar is looking for actions of this type 221 | // (could be regenerating actions for a specific asset, and therefore the 222 | // registrar would only accept actions corresponding to that asset) 223 | if (ActionRegistrar.IsOpenForRegistration(ActionKey)) 224 | { 225 | UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); 226 | check(NodeSpawner != nullptr); 227 | 228 | ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner); 229 | } 230 | } 231 | 232 | FText UVaRest_BreakJson::GetMenuCategory() const 233 | { 234 | static FNodeTextCache CachedCategory; 235 | 236 | if (CachedCategory.IsOutOfDate(this)) 237 | { 238 | // FText::Format() is slow, so we cache this to save on performance 239 | CachedCategory.SetCachedText(FEditorCategoryUtils::BuildCategoryString(FCommonEditorCategory::Utilities, LOCTEXT("ActionMenuCategory", "VaRest")), this); 240 | } 241 | return CachedCategory; 242 | } 243 | 244 | void UVaRest_BreakJson::CreateProjectionPins(UEdGraphPin* Source) 245 | { 246 | const UEdGraphSchema_K2* K2Schema = GetDefault(); 247 | UClass* Class = Cast(StaticLoadObject(UClass::StaticClass(), nullptr, TEXT("class'VaRest.VaRestJsonObject'"))); 248 | 249 | for (TArray::TIterator it(Outputs); it; ++it) 250 | { 251 | FName Type; 252 | FName SubCategory; 253 | 254 | UObject* Subtype = nullptr; 255 | 256 | switch ((*it).Type) 257 | { 258 | case EVaRest_JsonType::JSON_Bool: 259 | Type = K2Schema->PC_Boolean; 260 | break; 261 | 262 | case EVaRest_JsonType::JSON_Number: 263 | Type = K2Schema->PC_Real; 264 | SubCategory = K2Schema->PC_Double; 265 | break; 266 | 267 | case EVaRest_JsonType::JSON_String: 268 | Type = K2Schema->PC_String; 269 | break; 270 | 271 | case EVaRest_JsonType::JSON_Object: 272 | Type = K2Schema->PC_Object; 273 | Subtype = Class; 274 | break; 275 | } 276 | 277 | UEdGraphNode::FCreatePinParams OutputPinParams; 278 | OutputPinParams.ContainerType = (*it).bIsArray ? EPinContainerType::Array : EPinContainerType::None; 279 | UEdGraphPin* OutputPin = CreatePin(EGPD_Output, Type, SubCategory, Subtype, (*it).Name, OutputPinParams); 280 | } 281 | } 282 | 283 | FText UVaRest_BreakJson::GetNodeTitle(ENodeTitleType::Type TitleType) const 284 | { 285 | return LOCTEXT("VaRest_Break_Json.NodeTitle", "Break Json"); 286 | } 287 | 288 | class FKCHandler_MakeJson : public FNodeHandlingFunctor 289 | { 290 | 291 | public: 292 | FKCHandler_MakeJson(FKismetCompilerContext& InCompilerContext) 293 | : FNodeHandlingFunctor(InCompilerContext) 294 | { 295 | } 296 | 297 | virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override 298 | { 299 | UEdGraphPin* OutputPin = nullptr; 300 | 301 | for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) 302 | { 303 | UEdGraphPin* Pin = Node->Pins[PinIndex]; 304 | if (Pin && (EGPD_Output == Pin->Direction)) 305 | { 306 | OutputPin = Pin; 307 | break; 308 | } 309 | } 310 | 311 | UClass* Class = Cast(StaticLoadObject(UClass::StaticClass(), nullptr, TEXT("class'VaRest.VaRestJsonObject'"))); 312 | 313 | FBPTerminal** TargetTerm = Context.NetMap.Find(OutputPin); 314 | if (TargetTerm == nullptr) 315 | { 316 | return; 317 | } 318 | 319 | { 320 | UClass* SubsystemClass = Cast(StaticLoadObject(UClass::StaticClass(), nullptr, TEXT("class'VaRest.VaRestSubsystem'"))); 321 | 322 | const FName FunctionName = TEXT("StaticConstructVaRestJsonObject"); 323 | UFunction* FunctionPtr = SubsystemClass->FindFunctionByName(FunctionName); 324 | FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node); 325 | Statement.Type = KCST_CallFunction; 326 | Statement.FunctionToCall = FunctionPtr; 327 | Statement.FunctionContext = nullptr; 328 | Statement.bIsParentContext = false; 329 | Statement.LHS = *TargetTerm; 330 | FBPTerminal* NullTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); 331 | NullTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Object; 332 | NullTerm->ObjectLiteral = nullptr; 333 | NullTerm->SourcePin = OutputPin; 334 | Statement.RHS.Add(NullTerm); 335 | } 336 | 337 | for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) 338 | { 339 | UEdGraphPin* Pin = Node->Pins[PinIndex]; 340 | if (Pin && (EGPD_Input == Pin->Direction)) 341 | { 342 | FBPTerminal** Source = Context.NetMap.Find(FEdGraphUtilities::GetNetFromPin(Pin)); 343 | 344 | const FName& FieldName = Pin->PinName; 345 | const FName& FieldType = Pin->PinType.PinCategory; 346 | 347 | FBPTerminal* FieldNameTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); 348 | FieldNameTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_String; 349 | FieldNameTerm->SourcePin = Pin; 350 | 351 | FieldNameTerm->Name = FieldName.ToString(); 352 | FieldNameTerm->TextLiteral = FText::FromName(FieldName); 353 | 354 | FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node); 355 | FName FunctionName; 356 | 357 | const bool bIsArray = Pin->PinType.ContainerType == EPinContainerType::Array; 358 | if (FieldType == CompilerContext.GetSchema()->PC_Boolean) 359 | { 360 | FunctionName = bIsArray ? TEXT("SetBoolArrayField") : TEXT("SetBoolField"); 361 | } 362 | else if (FieldType == CompilerContext.GetSchema()->PC_Real) 363 | { 364 | FunctionName = bIsArray ? TEXT("SetNumberArrayFieldDouble") : TEXT("SetNumberFieldDouble"); 365 | } 366 | else if (FieldType == CompilerContext.GetSchema()->PC_String) 367 | { 368 | FunctionName = bIsArray ? TEXT("SetStringArrayField") : TEXT("SetStringField"); 369 | } 370 | else if (FieldType == CompilerContext.GetSchema()->PC_Object) 371 | { 372 | FunctionName = bIsArray ? TEXT("SetObjectArrayField") : TEXT("SetObjectField"); 373 | } 374 | else 375 | { 376 | continue; 377 | } 378 | 379 | UFunction* FunctionPtr = Class->FindFunctionByName(FunctionName); 380 | Statement.Type = KCST_CallFunction; 381 | Statement.FunctionToCall = FunctionPtr; 382 | Statement.FunctionContext = *TargetTerm; 383 | Statement.bIsParentContext = false; 384 | Statement.LHS = nullptr; 385 | Statement.RHS.Add(FieldNameTerm); 386 | Statement.RHS.Add(*Source); 387 | } 388 | } 389 | } 390 | 391 | FBPTerminal* RegisterInputTerm(FKismetFunctionContext& Context, UEdGraphPin* InputPin) 392 | { 393 | // Find structure source net 394 | UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(InputPin); 395 | FBPTerminal** TermPtr = Context.NetMap.Find(Net); 396 | 397 | if (!TermPtr) 398 | { 399 | FBPTerminal* Term = Context.CreateLocalTerminalFromPinAutoChooseScope(Net, Context.NetNameMap->MakeValidName(Net)); 400 | 401 | Context.NetMap.Add(Net, Term); 402 | 403 | return Term; 404 | } 405 | 406 | return *TermPtr; 407 | } 408 | 409 | void RegisterOutputTerm(FKismetFunctionContext& Context, UEdGraphPin* OutputPin) 410 | { 411 | FBPTerminal* Term = Context.CreateLocalTerminalFromPinAutoChooseScope(OutputPin, Context.NetNameMap->MakeValidName(OutputPin)); 412 | Context.NetMap.Add(OutputPin, Term); 413 | } 414 | 415 | virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* InNode) override 416 | { 417 | UVaRest_MakeJson* Node = Cast(InNode); 418 | FNodeHandlingFunctor::RegisterNets(Context, Node); 419 | 420 | check(NULL != Node); 421 | { 422 | for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) 423 | { 424 | UEdGraphPin* Pin = Node->Pins[PinIndex]; 425 | if (EGPD_Output == Pin->Direction) 426 | { 427 | RegisterOutputTerm(Context, Pin); 428 | } 429 | else 430 | { 431 | RegisterInputTerm(Context, Pin); 432 | } 433 | } 434 | } 435 | } 436 | }; 437 | 438 | /** 439 | * Main node class 440 | */ 441 | UVaRest_MakeJson::UVaRest_MakeJson(const FObjectInitializer& ObjectInitializer) 442 | : Super(ObjectInitializer) 443 | { 444 | } 445 | 446 | FNodeHandlingFunctor* UVaRest_MakeJson::CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const 447 | { 448 | return new FKCHandler_MakeJson(CompilerContext); 449 | } 450 | 451 | void UVaRest_MakeJson::AllocateDefaultPins() 452 | { 453 | const UEdGraphSchema_K2* K2Schema = GetDefault(); 454 | 455 | UClass* Class = Cast(StaticLoadObject(UClass::StaticClass(), nullptr, TEXT("class'VaRest.VaRestJsonObject'"))); 456 | UEdGraphPin* Pin = CreatePin(EGPD_Output, K2Schema->PC_Object, TEXT(""), Class, TEXT("Target")); 457 | 458 | K2Schema->SetPinAutogeneratedDefaultValueBasedOnType(Pin); 459 | 460 | CreateProjectionPins(Pin); 461 | } 462 | 463 | FLinearColor UVaRest_MakeJson::GetNodeTitleColor() const 464 | { 465 | return FLinearColor(255.0f, 255.0f, 0.0f); 466 | } 467 | 468 | void UVaRest_MakeJson::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) 469 | { 470 | const FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None; 471 | if (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(UVaRest_MakeJson, Inputs) || 472 | PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FVaRest_NamedType, Name) || 473 | PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FVaRest_NamedType, Type) || 474 | PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FVaRest_NamedType, bIsArray)) 475 | { 476 | ReconstructNode(); 477 | GetGraph()->NotifyGraphChanged(); 478 | } 479 | 480 | Super::PostEditChangeProperty(PropertyChangedEvent); 481 | } 482 | 483 | void UVaRest_MakeJson::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const 484 | { 485 | // actions get registered under specific object-keys; the idea is that 486 | // actions might have to be updated (or deleted) if their object-key is 487 | // mutated (or removed)... here we use the node's class (so if the node 488 | // type disappears, then the action should go with it) 489 | UClass* ActionKey = GetClass(); 490 | 491 | // to keep from needlessly instantiating a UBlueprintNodeSpawner, first 492 | // check to make sure that the registrar is looking for actions of this type 493 | // (could be regenerating actions for a specific asset, and therefore the 494 | // registrar would only accept actions corresponding to that asset) 495 | if (ActionRegistrar.IsOpenForRegistration(ActionKey)) 496 | { 497 | UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); 498 | check(NodeSpawner != nullptr); 499 | 500 | ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner); 501 | } 502 | } 503 | 504 | FText UVaRest_MakeJson::GetMenuCategory() const 505 | { 506 | static FNodeTextCache CachedCategory; 507 | 508 | if (CachedCategory.IsOutOfDate(this)) 509 | { 510 | // FText::Format() is slow, so we cache this to save on performance 511 | CachedCategory.SetCachedText(FEditorCategoryUtils::BuildCategoryString(FCommonEditorCategory::Utilities, LOCTEXT("ActionMenuCategory", "VaRest")), this); 512 | } 513 | return CachedCategory; 514 | } 515 | 516 | void UVaRest_MakeJson::CreateProjectionPins(UEdGraphPin* Source) 517 | { 518 | const UEdGraphSchema_K2* K2Schema = GetDefault(); 519 | UClass* Class = Cast(StaticLoadObject(UClass::StaticClass(), nullptr, TEXT("class'VaRest.VaRestJsonObject'"))); 520 | 521 | for (TArray::TIterator it(Inputs); it; ++it) 522 | { 523 | FName Type; 524 | FName SubCategory; 525 | UObject* Subtype = nullptr; 526 | 527 | switch ((*it).Type) 528 | { 529 | case EVaRest_JsonType::JSON_Bool: 530 | Type = K2Schema->PC_Boolean; 531 | break; 532 | 533 | case EVaRest_JsonType::JSON_Number: 534 | Type = K2Schema->PC_Real; 535 | SubCategory = K2Schema->PC_Double; 536 | break; 537 | 538 | case EVaRest_JsonType::JSON_String: 539 | Type = K2Schema->PC_String; 540 | break; 541 | 542 | case EVaRest_JsonType::JSON_Object: 543 | Type = K2Schema->PC_Object; 544 | Subtype = Class; 545 | break; 546 | } 547 | 548 | UEdGraphNode::FCreatePinParams InputPinParams; 549 | InputPinParams.ContainerType = (*it).bIsArray ? EPinContainerType::Array : EPinContainerType::None; 550 | UEdGraphPin* InputPin = CreatePin(EGPD_Input, Type, SubCategory, Subtype, (*it).Name, InputPinParams); 551 | 552 | InputPin->SetSavePinIfOrphaned(false); 553 | } 554 | } 555 | 556 | FText UVaRest_MakeJson::GetNodeTitle(ENodeTitleType::Type TitleType) const 557 | { 558 | return LOCTEXT("VaRest_Make_Json.NodeTitle", "Make Json"); 559 | } 560 | 561 | #undef LOCTEXT_NAMESPACE 562 | -------------------------------------------------------------------------------- /Source/VaRestEditor/Public/VaRestEditor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Modules/ModuleInterface.h" 6 | 7 | class FVaRestEditorModule : public IModuleInterface 8 | { 9 | 10 | public: 11 | /** IModuleInterface implementation */ 12 | virtual void StartupModule() override; 13 | virtual void ShutdownModule() override; 14 | }; 15 | -------------------------------------------------------------------------------- /Source/VaRestEditor/Public/VaRest_BreakJson.h: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | // Original code by https://github.com/unktomi 3 | 4 | #pragma once 5 | 6 | #include "Runtime/Launch/Resources/Version.h" 7 | 8 | #include "CoreMinimal.h" 9 | #include "K2Node.h" 10 | 11 | #include "VaRest_BreakJson.generated.h" 12 | 13 | UENUM(BlueprintType) 14 | enum class EVaRest_JsonType : uint8 15 | { 16 | // JSON_Null UMETA(DisplayName = "Null"), 17 | JSON_Bool UMETA(DisplayName = "Boolean"), 18 | JSON_Number UMETA(DisplayName = "Number"), 19 | JSON_String UMETA(DisplayName = "String"), 20 | JSON_Object UMETA(DisplayName = "Object") 21 | }; 22 | 23 | USTRUCT(BlueprintType) 24 | struct FVaRest_NamedType 25 | { 26 | GENERATED_BODY() 27 | 28 | UPROPERTY(EditAnywhere, Category = NamedType) 29 | FName Name; 30 | 31 | UPROPERTY(EditAnywhere, Category = NamedType) 32 | EVaRest_JsonType Type; 33 | 34 | UPROPERTY(EditAnywhere, Category = NamedType) 35 | bool bIsArray; 36 | 37 | FVaRest_NamedType() 38 | : Type(EVaRest_JsonType::JSON_String) 39 | , bIsArray(false) 40 | { 41 | } 42 | }; 43 | 44 | UCLASS(BlueprintType, Blueprintable) 45 | class VARESTEDITOR_API UVaRest_MakeJson : public UK2Node 46 | { 47 | GENERATED_UCLASS_BODY() 48 | 49 | public: 50 | // Begin UEdGraphNode interface. 51 | virtual void AllocateDefaultPins() override; 52 | virtual FLinearColor GetNodeTitleColor() const override; 53 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; 54 | // End UEdGraphNode interface. 55 | 56 | // Begin UK2Node interface 57 | virtual bool IsNodePure() const { return true; } 58 | virtual bool ShouldShowNodeProperties() const { return true; } 59 | void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override; 60 | virtual FText GetMenuCategory() const override; 61 | virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; 62 | virtual class FNodeHandlingFunctor* CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const override; 63 | // End UK2Node interface. 64 | 65 | protected: 66 | virtual void CreateProjectionPins(UEdGraphPin* Source); 67 | 68 | public: 69 | UPROPERTY(EditAnywhere, Category = PinOptions) 70 | TArray Inputs; 71 | }; 72 | 73 | UCLASS(BlueprintType, Blueprintable) 74 | class VARESTEDITOR_API UVaRest_BreakJson : public UK2Node 75 | { 76 | GENERATED_UCLASS_BODY() 77 | 78 | public: 79 | // Begin UEdGraphNode interface. 80 | virtual void AllocateDefaultPins() override; 81 | virtual FLinearColor GetNodeTitleColor() const override; 82 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; 83 | // End UEdGraphNode interface. 84 | 85 | // Begin UK2Node interface 86 | virtual bool IsNodePure() const { return true; } 87 | virtual bool ShouldShowNodeProperties() const { return true; } 88 | void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override; 89 | virtual FText GetMenuCategory() const override; 90 | virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override; 91 | virtual class FNodeHandlingFunctor* CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const override; 92 | // End UK2Node interface. 93 | 94 | protected: 95 | virtual void CreateProjectionPins(UEdGraphPin* Source); 96 | 97 | public: 98 | UPROPERTY(EditAnywhere, Category = PinOptions) 99 | TArray Outputs; 100 | }; 101 | -------------------------------------------------------------------------------- /Source/VaRestEditor/VaRestEditor.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2019 Vladimir Alyamkin. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class VaRestEditor : ModuleRules 6 | { 7 | public VaRestEditor(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | DefaultBuildSettings = BuildSettingsVersion.V5; 11 | 12 | PrivateIncludePaths.AddRange( 13 | new string[] { 14 | "VaRestEditor/Private", 15 | 16 | // ... add other private include paths required here ... 17 | }); 18 | 19 | 20 | PublicDependencyModuleNames.AddRange( 21 | new string[] 22 | { 23 | "Core", 24 | "VaRest" 25 | 26 | // ... add other public dependencies that you statically link with here ... 27 | }); 28 | 29 | 30 | PrivateDependencyModuleNames.AddRange( 31 | new string[] 32 | { 33 | "CoreUObject", 34 | "Engine", 35 | "Slate", 36 | "SlateCore", 37 | "InputCore", 38 | "AssetTools", 39 | "UnrealEd", // for FAssetEditorManager 40 | "KismetWidgets", 41 | "KismetCompiler", 42 | "BlueprintGraph", 43 | "GraphEditor", 44 | "Kismet", // for FWorkflowCentricApplication 45 | "PropertyEditor", 46 | "EditorStyle", 47 | "Sequencer", 48 | "DetailCustomizations", 49 | "Settings", 50 | "RenderCore" 51 | }); 52 | 53 | 54 | DynamicallyLoadedModuleNames.AddRange( 55 | new string[] 56 | { 57 | // ... add any modules that your module loads dynamically here ... 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /VaRest.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion" : 3, 3 | "FriendlyName" : "VaRest", 4 | "Version" : 32, 5 | "VersionName" : "1.1-r34", 6 | "CreatedBy" : "Vladimir Alyamkin", 7 | "CreatedByURL" : "https://ufna.dev", 8 | "EngineVersion" : "5.4.0", 9 | "Description" : "Plugin that makes REST (JSON) server communication easy to use", 10 | "Category" : "Network", 11 | "DocsURL": "https://bit.ly/VaRest-Docs", 12 | "MarketplaceURL" : "com.epicgames.launcher://ue/marketplace/content/e47be161e7a24e928560290abd5dcc4f", 13 | "Modules" : 14 | [ 15 | { 16 | "Name" : "VaRest", 17 | "Type" : "Runtime", 18 | "LoadingPhase": "PreDefault" 19 | }, 20 | { 21 | "Name": "VaRestEditor", 22 | "Type": "UncookedOnly" 23 | } 24 | ] 25 | } 26 | --------------------------------------------------------------------------------