Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Command 2055 - Process JSON Code #3215

Merged
merged 6 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ add_library(${PROJECT_NAME} OBJECT
src/input_source.h
src/instrumentation.cpp
src/instrumentation.h
src/json_helper.cpp
src/json_helper.h
src/keys.h
src/main_data.cpp
src/main_data.h
Expand Down
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ libeasyrpg_player_a_SOURCES = \
src/input_source.h \
src/instrumentation.cpp \
src/instrumentation.h \
src/json_helper.cpp \
src/json_helper.h \
src/keys.h \
src/main_data.cpp \
src/main_data.h \
Expand Down
91 changes: 88 additions & 3 deletions src/game_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "game_screen.h"
#include "game_interpreter_control_variables.h"
#include "game_windows.h"
#include "json_helper.h"
#include "maniac_patch.h"
#include "spriteset_map.h"
#include "sprite_character.h"
Expand Down Expand Up @@ -789,9 +790,11 @@ bool Game_Interpreter::ExecuteCommand(lcf::rpg::EventCommand const& com) {
return CommandManiacCallCommand(com);
case Cmd::EasyRpg_SetInterpreterFlag:
return CommandEasyRpgSetInterpreterFlag(com);
case static_cast<Cmd>(2056): //EasyRPG_CloneMapEvent
case Cmd::EasyRpg_ProcessJson:
return CommandEasyRpgProcessJson(com);
case Cmd::EasyRpg_CloneMapEvent:
return CommandEasyRpgCloneMapEvent(com);
case static_cast<Cmd>(2057): //EasyRPG_DestroyMapEvent
case Cmd::EasyRpg_DestroyMapEvent:
return CommandEasyRpgDestroyMapEvent(com);
default:
return true;
Expand Down Expand Up @@ -5055,13 +5058,95 @@ bool Game_Interpreter::CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand c
Player::game_config.patch_key_patch.Set(flag_value);
if (flag_name == "rpg2k3-cmds" || flag_name == "rpg2k3-commands")
Player::game_config.patch_rpg2k3_commands.Set(flag_value);

if (flag_name == "rpg2k-battle")
lcf::Data::system.easyrpg_use_rpg2k_battle_system = flag_value;

return true;
}

bool Game_Interpreter::CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& com) {
if (!Player::HasEasyRpgExtensions()) {
return true;
}

#ifndef HAVE_NLOHMANN_JSON
Output::Warning("CommandEasyRpgProcessJson: JSON not supported on this platform");
return true;
#else

int operation = ValueOrVariable(com.parameters[0], com.parameters[1]);
int source_var_id = ValueOrVariable(com.parameters[2], com.parameters[3]);
int target_var_type = ValueOrVariable(com.parameters[4], com.parameters[5]);
int target_var_id = ValueOrVariable(com.parameters[6], com.parameters[7]);

std::string json_path = ToString(CommandStringOrVariable(com, 8, 9));
auto* json_data = Main_Data::game_strings->ParseJson(source_var_id);

if (!json_data) {
Output::Warning("JSON Parse error for {}", Main_Data::game_strings->Get(source_var_id));
return true;
}

if (target_var_type == 2 && !Player::IsPatchManiac()) {
Output::Warning("CommandEasyRpgProcessJson: String operations require Maniac Patch support");
return true;
}

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

if (operation == 0) { // Get operation: Extract a value from JSON data
result = Json_Helper::GetValue(*json_data, json_path);

if (result) {
switch (target_var_type) {
case 0: // Switch
Main_Data::game_switches->Set(target_var_id, atoi(result->c_str()) != 0);
break;
case 1: // Variable
Main_Data::game_variables->Set(target_var_id, atoi(result->c_str()));
break;
case 2: // String
Main_Data::game_strings->Asg({ target_var_id }, *result);
break;
default:
Output::Warning("CommandEasyRpgProcessJson: Unsupported target_var_type {}", operation);
return true;
}
}
}
else if (operation == 1) { // Set operation: Update JSON data with a new value
std::string new_value;

switch (target_var_type) {
case 0: // Switch
new_value = std::to_string(Main_Data::game_switches->Get(target_var_id));
break;
case 1: // Variable
new_value = std::to_string(Main_Data::game_variables->Get(target_var_id));
break;
case 2: // String
new_value = ToString(Main_Data::game_strings->Get(target_var_id));
break;
default:
Output::Warning("CommandEasyRpgProcessJson: Unsupported target_var_type {}", operation);
return true;
}

result = Json_Helper::SetValue(*json_data, json_path, new_value);

if (result) {
Main_Data::game_strings->Asg({ source_var_id }, *result);
}
}
else {
Output::Warning("CommandEasyRpgProcessJson: Invalid Operation {}", operation);
}

return true;

#endif // !HAVE_NLOHMANN_JSON
}

bool Game_Interpreter::CommandEasyRpgCloneMapEvent(lcf::rpg::EventCommand const& com) {
if (!Player::HasEasyRpgExtensions()) {
return true;
Expand Down
1 change: 1 addition & 0 deletions src/game_interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ class Game_Interpreter : public Game_BaseInterpreterContext
bool CommandManiacControlStrings(lcf::rpg::EventCommand const& com);
bool CommandManiacCallCommand(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgSetInterpreterFlag(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgProcessJson(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgCloneMapEvent(lcf::rpg::EventCommand const& com);
bool CommandEasyRpgDestroyMapEvent(lcf::rpg::EventCommand const& com);

Expand Down
23 changes: 23 additions & 0 deletions src/game_strings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,34 @@
#include "player.h"
#include "utils.h"

#ifdef HAVE_NLOHMANN_JSON
#include "json_helper.h"
#endif

void Game_Strings::WarnGet(int id) const {
Output::Debug("Invalid read strvar[{}]!", id);
--_warnings;
}

#ifdef HAVE_NLOHMANN_JSON
nlohmann::json* Game_Strings::ParseJson(int id) {
auto it = _json_cache.find(id);
if (it != _json_cache.end()) {
return &(it->second);
}

auto str = ToString(Get(id));
auto res = Json_Helper::Parse(str);

if (!res) {
return nullptr;
} else {
_json_cache[id] = *res;
return &_json_cache[id];
}
}
#endif

StringView Game_Strings::Asg(Str_Params params, StringView string) {
Set(params, string);
return Get(params.string_id);
Expand Down
28 changes: 26 additions & 2 deletions src/game_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
* along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
*/

// Headers
// Headers
#include "system.h"
#include <cstdint>
#include <string>
#include <lcf/data.h>
Expand All @@ -27,6 +28,10 @@
#include "player.h"
#include "string_view.h"

#ifdef HAVE_NLOHMANN_JSON
#include <nlohmann/json.hpp>
#endif

/**
* Game_Strings class.
*/
Expand Down Expand Up @@ -59,6 +64,10 @@ class Game_Strings {
StringView GetWithMode(StringView str_data, int mode, int arg, const Game_Variables& variables) const;
StringView GetWithModeAndPos(StringView str_data, int mode, int arg, int* pos, const Game_Variables& variables);

#ifdef HAVE_NLOHMANN_JSON
nlohmann::json* ParseJson(int id);
#endif

StringView Asg(Str_Params params, StringView string);
StringView Cat(Str_Params params, StringView string);
int ToNum(Str_Params params, int var_id, Game_Variables& variables);
Expand All @@ -85,8 +94,11 @@ class Game_Strings {

Strings_t _strings;
mutable int _warnings = max_warnings;
};

#ifdef HAVE_NLOHMANN_JSON
std::unordered_map<int, nlohmann::json> _json_cache;
#endif
};

inline void Game_Strings::Set(Str_Params params, StringView string) {
if (params.string_id <= 0) {
Expand All @@ -108,10 +120,18 @@ inline void Game_Strings::Set(Str_Params params, StringView string) {
} else {
it->second = ins_string;
}

#ifdef HAVE_NLOHMANN_JSON
_json_cache.erase(params.string_id);
#endif
}

inline void Game_Strings::SetData(Strings_t s) {
_strings = std::move(s);

#ifdef HAVE_NLOHMANN_JSON
_json_cache.clear();
#endif
}

inline void Game_Strings::SetData(const std::vector<lcf::DBString>& s) {
Expand All @@ -122,6 +142,10 @@ inline void Game_Strings::SetData(const std::vector<lcf::DBString>& s) {
}
++i;
}

#ifdef HAVE_NLOHMANN_JSON
_json_cache.clear();
#endif
}

inline const Game_Strings::Strings_t& Game_Strings::GetData() const {
Expand Down
100 changes: 100 additions & 0 deletions src/json_helper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* This file is part of EasyRPG Player.
*
* EasyRPG Player is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EasyRPG Player is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
*/

#include "json_helper.h"

#ifdef HAVE_NLOHMANN_JSON

#include "output.h"
#include <nlohmann/json.hpp>
#include <sstream>
#include <unordered_map>
#include "string_view.h"

using json = nlohmann::json;

namespace {
std::string GetValueAsString(const json& json_obj) {
std::string result;

if (json_obj.is_string()) {
result = json_obj.get<std::string>();
}
else if (json_obj.is_number_integer()) {
result = std::to_string(json_obj.get<int>());
}
else if (json_obj.is_number_float()) {
result = std::to_string(json_obj.get<float>());
}
else if (json_obj.is_boolean()) {
result = json_obj.get<bool>() ? "true" : "false";
}
else {
result = json_obj.dump();
}

return result;
}
}

namespace Json_Helper {
std::optional<nlohmann::json> Parse(std::string_view json_data) {
json json_obj = json::parse(json_data, nullptr, false);
if (json_obj.is_discarded()) {
return {};
}

return json_obj;
}

std::optional<std::string> GetValue(nlohmann::json& json_obj, std::string_view json_path) {
json::json_pointer ptr((std::string(json_path)));

if (ptr.empty()) {
Output::Warning("JSON: Bad json pointer {}", json_path);
return {};
}

if (!json_obj.contains(ptr)) {
return "";
}

return GetValueAsString(json_obj[ptr]);
}

std::string SetValue(nlohmann::json& json_obj, std::string_view json_path, std::string_view value) {
json::json_pointer ptr((std::string(json_path)));

if (ptr.empty()) {
Output::Warning("JSON: Bad json pointer {}", json_path);
return {};
}

json obj_value = json::parse(value, nullptr, false);

if (obj_value.is_discarded()) {
// If parsing fails, treat it as a string value
json_obj[ptr] = std::string(value);
} else {
json_obj[ptr] = obj_value;
}

return json_obj.dump();
}
}

#endif // HAVE_NLOHMANN_JSON
36 changes: 36 additions & 0 deletions src/json_helper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* This file is part of EasyRPG Player.
*
* EasyRPG Player is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EasyRPG Player is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef JSON_HELPER_H
#define JSON_HELPER_H

#include "system.h"

#ifdef HAVE_NLOHMANN_JSON

#include <optional>
#include <string_view>
#include <nlohmann/json.hpp>

namespace Json_Helper {
std::optional<nlohmann::json> Parse(std::string_view json_data);
std::optional<std::string> GetValue(nlohmann::json& json_obj, std::string_view json_path);
std::string SetValue(nlohmann::json& json_obj, std::string_view json_path, std::string_view value);
}

#endif // HAVE_NLOHMANN_JSON
#endif // JSON_HELPER_H
Loading