diff --git a/Core/Game/Actors/Mesh/StaticMesh.cpp b/Core/Game/Actors/Mesh/StaticMesh.cpp index f1081fc..89af642 100644 --- a/Core/Game/Actors/Mesh/StaticMesh.cpp +++ b/Core/Game/Actors/Mesh/StaticMesh.cpp @@ -1,7 +1,7 @@ #include "StaticMesh.hpp" -#include "Log/Log.h" -#include "Game/SaveMap/SaveMap.h" +#include "Log/Log.hpp" +#include "Game/SaveMap/SaveMap.hpp" StaticMesh::StaticMesh() { diff --git a/Core/Game/SaveMap/SaveMap.cpp b/Core/Game/SaveMap/SaveMap.cpp index 426baf8..17a8c6c 100644 --- a/Core/Game/SaveMap/SaveMap.cpp +++ b/Core/Game/SaveMap/SaveMap.cpp @@ -32,63 +32,64 @@ SaveMap::SaveMap(const char* class_name) : class_name(class_name) SaveMap::SaveMap(const std::string& json_data) { - for (size_t i = 0; i < json_data.length() - 1;) + std::string json_data_blank = CleaningJSON(json_data); + for (size_t i = 0; i < json_data_blank.length() - 1;) { - size_t key_begin = i = json_data.find_first_of('\"', i) + 1; - size_t key_end = i = json_data.find_first_of('\"', i); + size_t key_begin = i = json_data_blank.find_first_of('\"', i) + 1; + size_t key_end = i = json_data_blank.find_first_of('\"', i); - std::string name = json_data.substr(key_begin, key_end - key_begin); + std::string name = json_data_blank.substr(key_begin, key_end - key_begin); if (class_name.empty()) { - size_t begin = json_data.find(':', i) + 2; - size_t end = i = json_data.find('\"', begin); + size_t begin = json_data_blank.find(':', i) + 2; + size_t end = i = json_data_blank.find('\"', begin); i++; - class_name = json_data.substr(begin, end - begin); + class_name = json_data_blank.substr(begin, end - begin); continue; } if (name[0] == 'i') { - size_t begin = json_data.find(':', i) + 2; - size_t end = i = json_data.find_first_of(",}", i); + size_t begin = json_data_blank.find(':', i) + 1; + size_t end = i = json_data_blank.find_first_of(",}", i); i++; - std::string value = json_data.substr(begin, end - begin - 1); + std::string value = json_data_blank.substr(begin, end - begin); save_long[name.c_str() + 1] = std::stoll(value); } else if (name[0] == 'd') { - size_t begin = json_data.find(':', i) + 2; - size_t end = i = json_data.find_first_of(",}", i); + size_t begin = json_data_blank.find(':', i) + 1; + size_t end = i = json_data_blank.find_first_of(",}", i); i++; - std::string value = json_data.substr(begin, end - begin - 1); + std::string value = json_data_blank.substr(begin, end - begin); save_double[name.c_str() + 1] = std::stod(value); } else if (name[0] == 's') { - size_t begin = json_data.find(':', i) + 2; - size_t end = i = json_data.find('\"', begin); + size_t begin = json_data_blank.find(':', i) + 2; + size_t end = i = json_data_blank.find('\"', begin); i++; std::string value; if (begin != end) - value = json_data.substr(begin, end - begin); + value = json_data_blank.substr(begin, end - begin); else value = ""; save_string[name.c_str() + 1] = value; } else if (name[0] == 'o') { - size_t begin = i = json_data.find('{', i); + size_t begin = i = json_data_blank.find('{', i); int open = 1, close = 0; while (close < open) { i++; - if (json_data[i] == '{') + if (json_data_blank[i] == '{') open++; - else if (json_data[i] == '}') + else if (json_data_blank[i] == '}') close++; } size_t end = i; i++; - std::string object_json = json_data.substr(begin, end - begin + 1); + std::string object_json = json_data_blank.substr(begin, end - begin + 1); begin = object_json.find(':') + 2; end = object_json.find('\"', begin); @@ -102,20 +103,20 @@ SaveMap::SaveMap(const std::string& json_data) { std::string key = name.substr(2, name.length() - 2); - size_t begin_arr = i = json_data.find('[', i); + size_t begin_arr = i = json_data_blank.find('[', i); int open = 1, close = 0; while (close < open) { i++; - if (json_data[i] == '[') + if (json_data_blank[i] == '[') open++; - else if (json_data[i] == ']') + else if (json_data_blank[i] == ']') close++; } size_t end_arr = i; i++; - std::string arr_data = json_data.substr(begin_arr, end_arr - begin_arr + 1); + std::string arr_data = json_data_blank.substr(begin_arr, end_arr - begin_arr + 1); if (name[1] == 'i') { save_vector_integer[key] = std::vector(); @@ -155,6 +156,8 @@ SaveMap::SaveMap(const std::string& json_data) while (j < arr_data.length()) { size_t begin = arr_data.find('\"', j) + 1; + if (begin == std::string::npos + 1) + break; size_t end = j = arr_data.find('\"', begin); std::string value; if (begin != end) @@ -162,7 +165,7 @@ SaveMap::SaveMap(const std::string& json_data) else value = ""; vector_strings.push_back(value); - j += 3; + ++j; } } else if (name[1] == 'o') { @@ -206,20 +209,20 @@ SaveMap::SaveMap(const std::string& json_data) { std::string key = name.substr(2, name.length() - 2); - size_t begin_arr = i = json_data.find('[', i); + size_t begin_arr = i = json_data_blank.find('[', i); int open = 1, close = 0; while (close < open) { i++; - if (json_data[i] == '[') + if (json_data_blank[i] == '[') open++; - else if (json_data[i] == ']') + else if (json_data_blank[i] == ']') close++; } size_t end_arr = i; i++; - std::string arr_data = json_data.substr(begin_arr, end_arr - begin_arr + 1); + std::string arr_data = json_data_blank.substr(begin_arr, end_arr - begin_arr + 1); if (name[1] == 'i') { save_list_integer[key] = std::list(); @@ -304,21 +307,21 @@ SaveMap::SaveMap(const std::string& json_data) } } else if (name == "Parent parameters") { - size_t parent_begin = i = json_data.find('{', i); + size_t parent_begin = i = json_data_blank.find('{', i); int open = 1, close = 0; while (close < open) { i++; - if (json_data[i] == '{') + if (json_data_blank[i] == '{') open++; - else if (json_data[i] == '}') + else if (json_data_blank[i] == '}') close++; } size_t parent_end = i; i++; - std::string str = json_data.substr(parent_begin, parent_end - parent_begin + 1); + std::string str = json_data_blank.substr(parent_begin, parent_end - parent_begin + 1); parent_ = std::make_shared(str); } } @@ -448,9 +451,9 @@ double SaveMap::GetDouble(const char* name) return save_double[name]; } -const char* SaveMap::GetString(const char* name) +std::string SaveMap::GetString(const char* name) { - return save_string[name].c_str(); + return save_string[name]; } ISave* SaveMap::GetObject(const char* name) @@ -634,3 +637,20 @@ std::shared_ptr SaveMap::connect_to(const std::shared_ptr& par this->parent_ = parent; return std::shared_ptr(this); } + +std::string SaveMap::CleaningJSON(const std::string& json_data) +{ + std::string blank_str; + blank_str.reserve(json_data.length()); + + bool open_str = false; + for (auto i : json_data) + { + if (i == '\"') + open_str = !open_str; + if (open_str || (i != ' ' && i != '\t' && i != '\n' && i != '\r')) + blank_str += i; + } + + return blank_str; +} diff --git a/Core/Game/SaveMap/SaveMap.hpp b/Core/Game/SaveMap/SaveMap.hpp index 7115f64..e20abd1 100644 --- a/Core/Game/SaveMap/SaveMap.hpp +++ b/Core/Game/SaveMap/SaveMap.hpp @@ -60,7 +60,7 @@ public: // Методы получения значенй long long GetInteger(const char* name); double GetDouble(const char* name); - const char* GetString(const char* name); + std::string GetString(const char* name); ISave* GetObject(const char* name); std::vector& GetVectorInteger(const char* name); std::vector& GetVectorDouble(const char* name); @@ -78,4 +78,6 @@ public: std::string serialize() noexcept; std::shared_ptr connect_to(const std::shared_ptr& parent); + + static std::string CleaningJSON(const std::string& json_data); }; diff --git a/TestGame/Worlds/TestWorld.world b/TestGame/Worlds/TestWorld.world index 9291601..b63c26a 100644 --- a/TestGame/Worlds/TestWorld.world +++ b/TestGame/Worlds/TestWorld.world @@ -1 +1 @@ -{"Class name":"TestWorld","dtime":5.000000,"Parent parameters":{"Class name":"World","loActors":[{"Class name":"StaticMesh","Parent parameters":{"Class name":"Mesh","smodel_name_":"TestModel","Parent parameters":{"Class name":"Actor","oloc":{"Class name":"Vector3D","dx":"0.0","dy":"0.0","dz":"0.0"},"orot":{"Class name":"Vector3D","dx":"0.0","dy":"0.0","dz":"0.0"},"oscale":{"Class name":"Vector3D","dx":"0.0","dy":"0.0","dz":"0.0"},"tags":[]}}}]}} \ No newline at end of file +{"Class name":"TestWorld","dtime":5.000000,"Parent parameters":{"Class name":"World","loActors":[{"Class name":"StaticMesh","Parent parameters":{"Class name":"Mesh","smodel_name_":"TestModel","Parent parameters":{"Class name":"Actor","oloc":{"Class name":"Vector3D","dx":0.0,"dy":0.0,"dz":0.0},"orot":{"Class name":"Vector3D","dx":0.0,"dy":0.0,"dz":0.0},"oscale":{"Class name":"Vector3D","dx":0.0,"dy":0.0,"dz":0.0},"tags":[]}}}]}} \ No newline at end of file diff --git a/TestGame/main_config.conf b/TestGame/main_config.conf index 1edecc2..e2d7189 100644 --- a/TestGame/main_config.conf +++ b/TestGame/main_config.conf @@ -1 +1 @@ -{"Class name":"MainConfig","dmin_memory_size":"4096.0","imin_CPU_count":"1","sgame_name":"Test Game","sbase_world":"TestWorld"} \ No newline at end of file +{"Class name":"MainConfig","dmin_memory_size":4096.0,"imin_CPU_count":1,"sgame_name":"Test Game","sbase_world":"TestWorld"} \ No newline at end of file diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 025c18c..8e0ad46 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1,3 +1,4 @@ +set(CMAKE_CXX_STANDARD 17) include(FetchContent) FetchContent_Declare( googletest @@ -9,7 +10,13 @@ FetchContent_MakeAvailable(googletest) enable_testing() -file(GLOB SRC "UStringTest.cpp" "${PROJECT_SOURCE_DIR}/Core/Types/UString.cpp") +file(GLOB SRC + "UStringTest.cpp" + "SaveMapTest.cpp" + + "${PROJECT_SOURCE_DIR}/Core/Types/UString.cpp" + "${PROJECT_SOURCE_DIR}/Core/Game/SaveMap/SaveMap.cpp" +) add_executable(Tests ${SRC}) target_link_libraries(Tests PRIVATE diff --git a/Tests/SaveMapTest.cpp b/Tests/SaveMapTest.cpp new file mode 100644 index 0000000..29fa958 --- /dev/null +++ b/Tests/SaveMapTest.cpp @@ -0,0 +1,185 @@ +#include +#include + +#include "Game/ObjectFactory.hpp" +#include "Game/SaveMap/SaveMap.hpp" + +class CustomObject : public ISave +{ +public: + int num; + std::string str; + double dbl; + + std::shared_ptr save() override + { + return std::make_shared("CustomObject")->SaveInteger("num", num)->SaveString("str", str)->SaveDouble("dbl", dbl); + } + void load(std::shared_ptr save) override + { + num = save->GetInteger("num"); + str = save->GetString("str"); + dbl = save->GetDouble("dbl"); + } + + bool operator==(const CustomObject& obj) const + { + return num == obj.num && str == obj.str && dbl == obj.dbl; + } +}; + +FACTORIES_LIST { + GENERATE_FACTORY_OBJECT(CustomObject) +}; +// A compatibility plug +std::vector base_object_factories = {}; + +TEST(SaveMapTest, check_clean) +{ + std::string start_json = +"{\n" +"\t\"id\": 1,\n" +"\t\"name\": \"Ivan Ivanov\",\n" +"\t\"age\": 28,\n" +"\t\"email\": \"ivan@example.com\",\n" +"\t\"roles\": [\"user\", \"editor\", \"moderator\"]\n" +"}"; + std::string end_json = "{\"id\":1,\"name\":\"Ivan Ivanov\",\"age\":28,\"email\":\"ivan@example.com\",\"roles\":[\"user\",\"editor\",\"moderator\"]}"; + + EXPECT_EQ(SaveMap::CleaningJSON(start_json), end_json); +} + +TEST(SaveMapTest, check_load_base_type) +{ + std::string json = +"{\n" + "\t\"Class name\": \"test class\",\n" + "\t\"iTestInt\": 123,\n" + "\t\"dTestDouble\": 123.56,\n" + "\t\"sTestString\": \"string str\",\n" + "\t\"viTestVectorInt\": [\n" + "\t\t1,\n" + "\t\t2,\n" + "\t\t3\n" + "\t],\n" + "\t\"vdTestVectorDouble\": [\n" + "\t\t1.2,\n" + "\t\t3.4,\n" + "\t\t5.6\n" + "\t],\n" + "\t\t\"vsTestVectorString\": [\n" + "\t\t\"str 1\",\n" + "\t\t\"2 str\",\n" + "\t\t\"str 3 str\"\n" + "\t]\n" +"}"; + + std::vector test_vector_int = { 1, 2, 3 }; + std::vector test_vector_double = { 1.2, 3.4, 5.6 }; + std::vector test_vector_string = { "str 1", "2 str", "str 3 str" }; + SaveMap save_map(json); + + EXPECT_EQ(save_map.GetInteger("TestInt"), 123); + EXPECT_EQ(save_map.GetDouble("TestDouble"), 123.56); + EXPECT_EQ(save_map.GetString("TestString"), "string str"); + + EXPECT_EQ(save_map.GetVectorInteger("TestVectorInt").size(), 3); + EXPECT_EQ(save_map.GetVectorDouble("TestVectorDouble").size(), 3); + EXPECT_EQ(save_map.GetVectorString("TestVectorString").size(), 3); + for (auto i = 0; i < 3; i++) + { + EXPECT_EQ(test_vector_int[i], save_map.GetVectorInteger("TestVectorInt")[i]); + EXPECT_EQ(test_vector_double[i], save_map.GetVectorDouble("TestVectorDouble")[i]); + EXPECT_EQ(test_vector_string[i], save_map.GetVectorString("TestVectorString")[i]); + } +} + +TEST(SaveMapTest, check_load_with_custom_type) +{ + std::string json = + "{\n" + "\t\"Class name\": \"test class\",\n" + "\t\"iTestInt\": 123,\n" + "\t\"dTestDouble\": 123.56,\n" + "\t\"sTestString\": \"string str\",\n" + "\t\"viTestVectorInt\": [\n" + "\t\t1,\n" + "\t\t2,\n" + "\t\t3\n" + "\t],\n" + "\t\"vdTestVectorDouble\": [\n" + "\t\t1.2,\n" + "\t\t3.4,\n" + "\t\t5.6\n" + "\t],\n" + "\t\t\"vsTestVectorString\": [\n" + "\t\t\"str 1\",\n" + "\t\t\"2 str\",\n" + "\t\t\"str 3 str\"\n" + "\t],\n" + "\t\"oCustomObject\": {\n" + "\t\t\"Class name\": \"CustomObject\",\n" + "\t\t\"inum\": 123,\n" + "\"sstr\": \"str123\",\n" + "\t\t\"ddbl\": 123.45\n" + "\t},\n" + "\"voCustomObjects\": [" + "{" + "\"Class name\": \"CustomObject\"," + "\"inum\": 123," + "\"sstr\": \"str123\"," + "\"ddbl\": 123.45" + "}," + "{" + "\"Class name\": \"CustomObject\"," + "\"inum\": 321," + "\"sstr\": \"str321\"," + "\"ddbl\": 543.21" + "}" + "]" +"}"; + + std::vector test_vector_int = { 1, 2, 3 }; + std::vector test_vector_double = { 1.2, 3.4, 5.6 }; + std::vector test_vector_string = { "str 1", "2 str", "str 3 str" }; + SaveMap save_map(json); + + EXPECT_EQ(save_map.GetInteger("TestInt"), 123); + EXPECT_EQ(save_map.GetDouble("TestDouble"), 123.56); + EXPECT_EQ(save_map.GetString("TestString"), "string str"); + + EXPECT_EQ(save_map.GetVectorInteger("TestVectorInt").size(), 3); + EXPECT_EQ(save_map.GetVectorDouble("TestVectorDouble").size(), 3); + EXPECT_EQ(save_map.GetVectorString("TestVectorString").size(), 3); + for (auto i = 0; i < 3; i++) + { + EXPECT_EQ(test_vector_int[i], save_map.GetVectorInteger("TestVectorInt")[i]); + EXPECT_EQ(test_vector_double[i], save_map.GetVectorDouble("TestVectorDouble")[i]); + EXPECT_EQ(test_vector_string[i], save_map.GetVectorString("TestVectorString")[i]); + } + + CustomObject* obj = reinterpret_cast(save_map.GetObject("CustomObject")); + CustomObject test_obj; + test_obj.num = 123; + test_obj.str = "str123"; + test_obj.dbl = 123.45; + EXPECT_EQ(*obj, test_obj); + + std::vector vec_obj = reinterpret_cast&>(save_map.GetVectorObject("CustomObjects")); + std::vector vec_obj_test = { + new CustomObject, + new CustomObject, + }; + vec_obj_test[0]->num = 123; + vec_obj_test[0]->str = "str123"; + vec_obj_test[0]->dbl = 123.45; + vec_obj_test[1]->num = 321; + vec_obj_test[1]->str = "str321"; + vec_obj_test[1]->dbl = 543.21; + for (auto i = 0; i < 2; i++) + { + EXPECT_EQ(*vec_obj[i], *vec_obj_test[i]); + delete vec_obj[i]; + delete vec_obj_test[i]; + } +} \ No newline at end of file