refactor(json): migrate from nlohmann to rapidjson

Replace nlohmann::json with rapidjson across localization, hitbox manager, server world, and sensitive filter. Add json_utils helper for converting rapidjson documents to maps, and improve parse error handling.
This commit is contained in:
2026-08-04 13:17:45 +08:00
parent ef3be12b64
commit 5e7c6ec891
8 changed files with 132 additions and 33 deletions

View File

@@ -0,0 +1,8 @@
#include <rapidjson/document.h>
#include <string>
#include <unordered_map>
namespace Tools {
std::unordered_map<std::string, std::string>
doc_to_map(const rapidjson::Document& doc);
}

View File

@@ -1,11 +1,13 @@
#pragma once #pragma once
#include <nlohmann/json.hpp> #include <rapidjson/document.h>
#include <unordered_map>
#include <vector>
namespace Cubed { namespace Cubed {
class SensitiveFilter { class SensitiveFilter {
public: public:
SensitiveFilter(); SensitiveFilter();
void load(const nlohmann::json& j); void load(const rapidjson::Document& doc);
std::string filter(std::string_view text); std::string filter(std::string_view text);
private: private:

View File

@@ -103,4 +103,5 @@ target_sources(${PROJECT_NAME}
gameplay/server_entity_manager.cpp gameplay/server_entity_manager.cpp
gameplay/client_entity_manager.cpp gameplay/client_entity_manager.cpp
gameplay/systems/wander_ai_system.cpp gameplay/systems/wander_ai_system.cpp
tools/json_utils.cpp
) )

View File

@@ -5,9 +5,10 @@
#include "Cubed/tools/log.hpp" #include "Cubed/tools/log.hpp"
#include "Cubed/tools/name_space.hpp" #include "Cubed/tools/name_space.hpp"
#include <nlohmann/json.hpp> #include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
namespace fs = std::filesystem; namespace fs = std::filesystem;
using nlohmann::json; using namespace rapidjson;
namespace Cubed { namespace Cubed {
HitboxManager::HitboxManager() { HitboxManager::HitboxManager() {
@@ -84,21 +85,30 @@ HitboxManager::Handle HitboxManager::load(std::string_view name) {
glm::vec3 center; glm::vec3 center;
glm::vec3 half; glm::vec3 half;
std::ifstream s{p}; std::ifstream s{p};
json j = json::parse(s); IStreamWrapper isw(s);
if (j.contains("boxes")) { Document doc;
const json* box = &j["boxes"]; doc.ParseStream(isw);
if (box->is_array() && !box->empty()) { if (doc.HasParseError()) {
box = &(*box)[0]; auto code = doc.GetParseError();
}
if (box->contains("center")) { throw std::runtime_error(
center.x = (*box)["center"].at(0).get<float>(); std::format("Parse {} failed, error code {}", p.string(),
center.y = (*box)["center"].at(1).get<float>(); static_cast<int>(code)));
center.z = (*box)["center"].at(2).get<float>(); }
} if (doc.HasMember("boxes")) {
if (box->contains("half")) { const Value& box = doc["boxes"];
half.x = (*box)["half"].at(0).get<float>(); if (box.IsArray()) {
half.y = (*box)["half"].at(1).get<float>(); const Value& b = box[0];
half.z = (*box)["half"].at(2).get<float>(); if (b.HasMember("center")) {
center.x = b["center"][0].GetFloat();
center.y = b["center"][1].GetFloat();
center.z = b["center"][2].GetFloat();
}
if (b.HasMember("half")) {
half.x = b["half"][0].GetFloat();
half.y = b["half"][1].GetFloat();
half.z = b["half"][2].GetFloat();
}
} }
} }
{ {

View File

@@ -8,14 +8,15 @@
#include "Cubed/tools/net_utils.hpp" #include "Cubed/tools/net_utils.hpp"
#include "Cubed/tools/uuid.hpp" #include "Cubed/tools/uuid.hpp"
#include <nlohmann/json.hpp>
#include <ranges> #include <ranges>
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <utility> #include <utility>
using namespace std::chrono; using namespace std::chrono;
using namespace std::chrono_literals; using namespace std::chrono_literals;
using namespace google::protobuf; using namespace google::protobuf;
using namespace rapidjson;
namespace fs = std::filesystem; namespace fs = std::filesystem;
using nlohmann::json;
namespace Cubed { namespace Cubed {
ServerWorld::ServerWorld(Config& config) ServerWorld::ServerWorld(Config& config)
: m_config(config), m_entity_manager(*this) {} : m_config(config), m_entity_manager(*this) {}
@@ -212,8 +213,13 @@ void ServerWorld::init_world() {
try { try {
fs::path path = std::format("{}SensitiveLexicon.json", ASSETS_PATH); fs::path path = std::format("{}SensitiveLexicon.json", ASSETS_PATH);
std::ifstream s{path}; std::ifstream s{path};
json j = json::parse(s); if (!s.is_open()) {
m_filter.load(j); throw std::runtime_error("can't open SensitiveLexicon.json");
}
IStreamWrapper isw(s);
Document doc;
doc.ParseStream(isw);
m_filter.load(doc);
} catch (const std::exception& e) { } catch (const std::exception& e) {
Logger::error("Load SensitiveLexicon.json Fail"); Logger::error("Load SensitiveLexicon.json Fail");
m_enable_filter = false; m_enable_filter = false;

View File

@@ -1,12 +1,14 @@
#include "Cubed/localization.hpp" #include "Cubed/localization.hpp"
#include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/json_utils.hpp"
#include "Cubed/tools/log.hpp" #include "Cubed/tools/log.hpp"
#include <fstream> #include <fstream>
#include <nlohmann/json.hpp> #include <rapidjson/document.h>
using json = nlohmann::json; #include <rapidjson/istreamwrapper.h>
namespace fs = std::filesystem; namespace fs = std::filesystem;
using namespace rapidjson;
namespace Cubed { namespace Cubed {
Localization::Localization() {} Localization::Localization() {}
Localization& Localization::instance() { Localization& Localization::instance() {
@@ -25,10 +27,19 @@ void Localization::load_language(std::string_view language) {
return; return;
} }
try { try {
json j = json::parse(file); IStreamWrapper isw(file);
Document doc;
doc.ParseStream(isw);
if (doc.HasParseError()) {
auto code = doc.GetParseError();
throw std::runtime_error(
std::format("Parse {} failed, error code {}", path,
static_cast<int>(code)));
}
m_translations.clear(); m_translations.clear();
j.get_to(m_translations); m_translations = Tools::doc_to_map(doc);
} catch (const json::parse_error& e) { } catch (const std::exception& e) {
Logger::error("JSON syntax error: {}", e.what()); Logger::error("JSON syntax error: {}", e.what());
} }
} }

54
src/tools/json_utils.cpp Normal file
View File

@@ -0,0 +1,54 @@
#include "Cubed/tools/json_utils.hpp"
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <string>
#include <unordered_map>
namespace Tools {
using rapidjson::Document;
using rapidjson::Value;
namespace {
std::string serialize_value(const Value& v, rapidjson::StringBuffer& buf) {
rapidjson::Writer<rapidjson::StringBuffer> w(buf);
v.Accept(w);
return buf.GetString();
}
} // namespace
std::unordered_map<std::string, std::string> doc_to_map(const Document& doc) {
std::unordered_map<std::string, std::string> m;
if (!doc.IsObject())
return m;
for (auto it = doc.MemberBegin(); it != doc.MemberEnd(); ++it) {
const std::string KEY(it->name.GetString(), it->name.GetStringLength());
const Value& v = it->value;
std::string val;
if (v.IsString()) {
val.assign(v.GetString(), v.GetStringLength());
} else if (v.IsInt()) {
val = std::to_string(v.GetInt());
} else if (v.IsUint()) {
val = std::to_string(v.GetUint());
} else if (v.IsInt64()) {
val = std::to_string(v.GetInt64());
} else if (v.IsUint64()) {
val = std::to_string(v.GetUint64());
} else if (v.IsDouble()) {
val = std::to_string(v.GetDouble());
} else if (v.IsBool()) {
val = v.GetBool() ? "true" : "false";
} else if (v.IsNull()) {
val = "null";
} else {
rapidjson::StringBuffer buf;
val = serialize_value(v, buf);
}
m.emplace(KEY, val);
}
return m;
}
} // namespace Tools

View File

@@ -2,18 +2,25 @@
#include <queue> #include <queue>
#include <utf8cpp/utf8.h> #include <utf8cpp/utf8.h>
using nlohmann::json; using namespace rapidjson;
namespace Cubed { namespace Cubed {
SensitiveFilter::SensitiveFilter() { SensitiveFilter::SensitiveFilter() {
}; };
void SensitiveFilter::load(const nlohmann::json& j) { void SensitiveFilter::load(const Document& doc) {
trie.clear(); trie.clear();
trie.emplace_back(); trie.emplace_back();
for (const auto& item : j["words"]) { if (!doc.HasMember("words")) {
return;
auto str = item.get<std::string>(); }
const Value& words = doc["words"];
for (SizeType i = 0; i < words.Size(); ++i) {
const auto& w = words[i];
if (!w.IsString()) {
continue;
}
std::string str(w.GetString(), w.GetStringLength());
std::u32string word; std::u32string word;
utf8::utf8to32(str.begin(), str.end(), std::back_inserter(word)); utf8::utf8to32(str.begin(), str.end(), std::back_inserter(word));
insert(word); insert(word);