Skip to content

Commit 82806c2

Browse files
authored
Merge pull request #3341 from EasyRPG-NewFeatures/JSON-UPDATES
PROCESS JSON COMMAND - New Features
2 parents a923496 + 3d6c422 commit 82806c2

File tree

11 files changed

+749
-82
lines changed

11 files changed

+749
-82
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,13 +961,15 @@ player_find_package(NAME lhasa
961961
# json support
962962
if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
963963
player_find_package(NAME nlohmann_json
964+
VERSION 3.9.1
964965
DEFINITION HAVE_NLOHMANN_JSON
965966
TARGET nlohmann_json::nlohmann_json
966967
REQUIRED
967968
)
968969
else()
969970
option(PLAYER_WITH_NLOHMANN_JSON "Support processing of JSON files" ON)
970971
player_find_package(NAME nlohmann_json
972+
VERSION 3.9.1
971973
CONDITION PLAYER_WITH_NLOHMANN_JSON
972974
DEFINITION HAVE_NLOHMANN_JSON
973975
TARGET nlohmann_json::nlohmann_json

Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,7 @@ test_runner_SOURCES = \
760760
tests/game_player_input.cpp \
761761
tests/game_player_pan.cpp \
762762
tests/game_player_savecount.cpp \
763+
tests/json.cpp \
763764
tests/mock_game.cpp \
764765
tests/mock_game.h \
765766
tests/move_route.cpp \

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ AS_IF([test "$with_freetype" = "yes"],[
9999
EP_PKG_CHECK([HARFBUZZ],[harfbuzz],[Custom Font text shaping.])
100100
])
101101
EP_PKG_CHECK([LHASA],[liblhasa],[Support running games in lzh archives.])
102-
EP_PKG_CHECK([NLOHMANN_JSON],[nlohmann_json],[Support processing of JSON files.])
102+
EP_PKG_CHECK([NLOHMANN_JSON],[nlohmann_json >= 3.9.1],[Support processing of JSON files.])
103103

104104
AC_ARG_WITH([audio],[AS_HELP_STRING([--without-audio], [Disable audio support. @<:@default=on@:>@])])
105105
AS_IF([test "x$with_audio" != "xno"],[

src/game_interpreter.cpp

Lines changed: 184 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5368,16 +5368,82 @@ bool Game_Interpreter::CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& c
53685368
return true;
53695369
#else
53705370

5371+
// Helper lambda for getting values from variables
5372+
auto get_var_value = [](int var_type, int var_id) -> std::string {
5373+
switch (var_type) {
5374+
case 0: // Switch
5375+
return std::to_string(Main_Data::game_switches->Get(var_id));
5376+
case 1: // Variable
5377+
return std::to_string(Main_Data::game_variables->Get(var_id));
5378+
case 2: // String
5379+
return ToString(Main_Data::game_strings->Get(var_id));
5380+
default:
5381+
Output::Warning("CommandEasyRpgProcessJson: Unsupported var_type {}", var_type);
5382+
return {};
5383+
}
5384+
};
5385+
5386+
// Helper lambda for setting values to variables
5387+
auto set_var_value = [](int var_type, int var_id, const std::string& value) -> bool {
5388+
switch (var_type) {
5389+
case 0: // Switch
5390+
Main_Data::game_switches->Set(var_id, atoi(value.c_str()) != 0);
5391+
break;
5392+
case 1: // Variable
5393+
Main_Data::game_variables->Set(var_id, atoi(value.c_str()));
5394+
break;
5395+
case 2: // String
5396+
Main_Data::game_strings->Asg(var_id, value);
5397+
break;
5398+
default:
5399+
Output::Warning("CommandEasyRpgProcessJson: Unsupported var_type {}", var_type);
5400+
return false;
5401+
}
5402+
return true;
5403+
};
5404+
53715405
int operation = ValueOrVariable(com.parameters[0], com.parameters[1]);
5372-
int source_var_id = ValueOrVariable(com.parameters[2], com.parameters[3]);
5406+
5407+
json* json_data = nullptr;
5408+
std::optional<json> json_data_imm;
5409+
5410+
int source_var_id = -1;
5411+
5412+
int pos = 0;
5413+
std::string json_path = Main_Data::game_strings->GetWithModeAndPos(com.string, com.parameters[8], com.parameters[9], pos, *Main_Data::game_variables);
5414+
5415+
if (com.parameters[2] == 0) {
5416+
std::string json_str = Main_Data::game_strings->GetWithModeAndPos(com.string, com.parameters[2], com.parameters[3], pos, *Main_Data::game_variables);
5417+
json_data_imm = Json_Helper::Parse(json_str);
5418+
if (json_data_imm) {
5419+
json_data = &*json_data_imm;
5420+
} else {
5421+
Output::Warning("JSON Parse error for {}", json_str);
5422+
return true;
5423+
}
5424+
} else {
5425+
source_var_id = ValueOrVariable(com.parameters[2] - 1, com.parameters[3]);
5426+
json_data = Main_Data::game_strings->ParseJson(source_var_id);
5427+
5428+
if (!json_data) {
5429+
Output::Warning("JSON Parse error for {}", Main_Data::game_strings->Get(source_var_id));
5430+
return true;
5431+
}
5432+
}
5433+
53735434
int target_var_type = ValueOrVariable(com.parameters[4], com.parameters[5]);
53745435
int target_var_id = ValueOrVariable(com.parameters[6], com.parameters[7]);
53755436

5376-
std::string json_path = ToString(CommandStringOrVariable(com, 8, 9));
5377-
auto* json_data = Main_Data::game_strings->ParseJson(source_var_id);
5437+
int flags = com.parameters[10];
5438+
bool pretty_print = (flags & 4) == 4;
5439+
5440+
if ((flags & 1) == 1) { // parse command codes
5441+
json_path = Game_Strings::Extract(json_path, false);
5442+
} else if ((flags & 2) == 2) { // parse command codes, numbers as hex
5443+
json_path = Game_Strings::Extract(json_path, true);
5444+
}
53785445

5379-
if (!json_data) {
5380-
Output::Warning("JSON Parse error for {}", Main_Data::game_strings->Get(source_var_id));
5446+
if (!Json_Helper::CheckJsonPointer(json_path)) {
53815447
return true;
53825448
}
53835449

@@ -5388,54 +5454,133 @@ bool Game_Interpreter::CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& c
53885454

53895455
std::optional<std::string> result;
53905456

5391-
if (operation == 0) { // Get operation: Extract a value from JSON data
5457+
switch (operation) {
5458+
case 0: { // Get operation: Extract a value from JSON data
53925459
result = Json_Helper::GetValue(*json_data, json_path);
5393-
53945460
if (result) {
5395-
switch (target_var_type) {
5396-
case 0: // Switch
5397-
Main_Data::game_switches->Set(target_var_id, atoi(result->c_str()) != 0);
5398-
break;
5399-
case 1: // Variable
5400-
Main_Data::game_variables->Set(target_var_id, atoi(result->c_str()));
5401-
break;
5402-
case 2: // String
5403-
Main_Data::game_strings->Asg({ target_var_id }, *result);
5404-
break;
5405-
default:
5406-
Output::Warning("CommandEasyRpgProcessJson: Unsupported target_var_type {}", operation);
5407-
return true;
5408-
}
5461+
set_var_value(target_var_type, target_var_id, *result);
54095462
}
5463+
break;
54105464
}
5411-
else if (operation == 1) { // Set operation: Update JSON data with a new value
5412-
std::string new_value;
5413-
5414-
switch (target_var_type) {
5415-
case 0: // Switch
5416-
new_value = std::to_string(Main_Data::game_switches->Get(target_var_id));
5417-
break;
5418-
case 1: // Variable
5419-
new_value = std::to_string(Main_Data::game_variables->Get(target_var_id));
5420-
break;
5421-
case 2: // String
5422-
new_value = ToString(Main_Data::game_strings->Get(target_var_id));
5423-
break;
5424-
default:
5425-
Output::Warning("CommandEasyRpgProcessJson: Unsupported target_var_type {}", operation);
5465+
case 1: { // Set operation: Update JSON data with a new value
5466+
if (json_data_imm) {
5467+
Output::Warning("CommandEasyRpgProcessJson: Cannot modify constant JSON string");
54265468
return true;
54275469
}
54285470

5471+
std::string new_value = get_var_value(target_var_type, target_var_id);
54295472
result = Json_Helper::SetValue(*json_data, json_path, new_value);
5430-
54315473
if (result) {
5432-
Main_Data::game_strings->Asg({ source_var_id }, *result);
5474+
Main_Data::game_strings->Asg(source_var_id, *result);
5475+
}
5476+
break;
5477+
}
5478+
case 2: { // GetLength operation
5479+
size_t length = Json_Helper::GetLength(*json_data, json_path);
5480+
std::string length_str;
5481+
if (target_var_type == 0) {
5482+
// For switches, true if length > 0
5483+
length_str = (length > 0) ? "1" : "0";
5484+
}
5485+
else {
5486+
length_str = std::to_string(length);
5487+
}
5488+
set_var_value(target_var_type, target_var_id, length_str);
5489+
break;
5490+
}
5491+
case 3: { // GetKeys operation
5492+
bool create_keys_obj = (flags & 8) == 8;
5493+
auto keys = Json_Helper::GetKeys(*json_data, json_path);
5494+
std::string keys_str;
5495+
for (size_t i = 0; i < keys.size(); ++i) {
5496+
if (i > 0) keys_str += ",";
5497+
keys_str += "\"" + (keys)[i] + "\"";
5498+
}
5499+
std::string json_str = "[" + keys_str + "]";
5500+
if (create_keys_obj) {
5501+
json_str = fmt::format(R"({{ "keys": {} }})", json_str);
5502+
}
5503+
set_var_value(target_var_type, target_var_id, json_str);
5504+
break;
5505+
}
5506+
case 4: { // GetType operation
5507+
std::string type = Json_Helper::GetType(*json_data, json_path);
5508+
std::string value;
5509+
if (target_var_type == 0) {
5510+
// For switches, true if it exists and not null
5511+
value = (!type.empty() && type != "null");
5512+
}
5513+
else if (target_var_type == 1) {
5514+
// For variables, numeric code for type
5515+
int type_code = 0;
5516+
if (type == "object") type_code = 1;
5517+
else if (type == "array") type_code = 2;
5518+
else if (type == "string") type_code = 3;
5519+
else if (type == "number") type_code = 4;
5520+
else if (type == "boolean") type_code = 5;
5521+
else if (type == "null") type_code = 6;
5522+
value = std::to_string(type_code);
5523+
}
5524+
else {
5525+
value = type;
5526+
}
5527+
set_var_value(target_var_type, target_var_id, value);
5528+
break;
5529+
}
5530+
case 5: { // Remove operation: Remove value from JSON data
5531+
if (json_data_imm) {
5532+
Output::Warning("CommandEasyRpgProcessJson: Cannot modify constant JSON string");
5533+
return true;
5534+
}
5535+
5536+
std::string result = Json_Helper::RemoveValue(*json_data, json_path);
5537+
if (!result.empty()) {
5538+
Main_Data::game_strings->Asg(source_var_id, result);
5539+
}
5540+
break;
5541+
}
5542+
case 6: { // Push operation: Add value to end of array
5543+
if (json_data_imm) {
5544+
Output::Warning("CommandEasyRpgProcessJson: Cannot modify constant JSON string");
5545+
return true;
5546+
}
5547+
5548+
std::string value = get_var_value(target_var_type, target_var_id);
5549+
std::string result = Json_Helper::PushValue(*json_data, json_path, value);
5550+
if (!result.empty()) {
5551+
Main_Data::game_strings->Asg(source_var_id, result);
54335552
}
5553+
break;
5554+
}
5555+
case 7: { // Pop operation: Remove and return last element of array
5556+
auto [json_obj, element] = Json_Helper::PopValue(*json_data, json_path);
5557+
if (!json_obj.empty()) {
5558+
// Set popped value to target variable
5559+
set_var_value(target_var_type, target_var_id, element);
5560+
// Update source with modified JSON after pop
5561+
if (!json_data_imm) {
5562+
Main_Data::game_strings->Asg(source_var_id, json_obj);
5563+
}
5564+
}
5565+
break;
54345566
}
5435-
else {
5567+
case 8: { // Contains operation: Check if path exists
5568+
bool exists = Json_Helper::Contains(*json_data, json_path);
5569+
set_var_value(target_var_type, target_var_id, exists ? "1" : "0");
5570+
break;
5571+
}
5572+
default:
54365573
Output::Warning("CommandEasyRpgProcessJson: Invalid Operation {}", operation);
54375574
}
54385575

5576+
if (target_var_type == 2 && pretty_print == 1) { // Only works with strings
5577+
std::string target_str = ToString(Main_Data::game_strings->Get(target_var_id));
5578+
if (auto parsed_json = Json_Helper::Parse(target_str)) {
5579+
std::string formatted = Json_Helper::PrettyPrint(*parsed_json, 2);
5580+
Main_Data::game_strings->Asg({ target_var_id }, formatted);
5581+
}
5582+
}
5583+
54395584
return true;
54405585

54415586
#endif // !HAVE_NLOHMANN_JSON

src/game_strings.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ void Game_Strings::WarnGet(int id) const {
3838
}
3939

4040
#ifdef HAVE_NLOHMANN_JSON
41-
nlohmann::json* Game_Strings::ParseJson(int id) {
41+
nlohmann::ordered_json* Game_Strings::ParseJson(int id) {
4242
auto it = _json_cache.find(id);
4343
if (it != _json_cache.end()) {
4444
return &(it->second);

src/game_strings.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class Game_Strings {
7878
std::string GetWithModeAndPos(StringView str_data, int mode, int arg, int& pos, const Game_Variables& variables);
7979

8080
#ifdef HAVE_NLOHMANN_JSON
81-
nlohmann::json* ParseJson(int id);
81+
nlohmann::ordered_json* ParseJson(int id);
8282
#endif
8383

8484
StringView Asg(Str_Params params, StringView string);
@@ -114,7 +114,7 @@ class Game_Strings {
114114
mutable int _warnings = max_warnings;
115115

116116
#ifdef HAVE_NLOHMANN_JSON
117-
std::unordered_map<int, nlohmann::json> _json_cache;
117+
std::unordered_map<int, nlohmann::ordered_json> _json_cache;
118118
#endif
119119
};
120120

0 commit comments

Comments
 (0)