feat: baseTask 定义但留空表示不使用已生成的同名任务

例如国际服覆写不使用同名国服任务

perf: 检测任务是否循环依赖(在此之前若循环依赖会直接 runtime error (递归爆栈))

例如 `"A": { "baseTask": "A" }`
This commit is contained in:
zzyyyl
2022-11-06 01:38:48 +08:00
parent b13dabce31
commit c985cd0ca3

View File

@@ -54,9 +54,16 @@ bool asst::TaskData::parse(const json::value& json)
const auto& json_obj = json.as_object();
{
std::unordered_map<std::string_view, bool> to_be_generated;
enum TaskStatus
{
NotToBeGenerate = 0, // 已经显式生成 或 不是待显式生成 的资源
ToBeGenerate, // 待生成 的资源
Generating, // 正在生成 的资源
NotExists, // 不存在的资源
};
std::unordered_map<std::string_view, TaskStatus> task_status;
for (const std::string& name : json_obj | views::keys) {
to_be_generated[task_name_view(name)] = true;
task_status[task_name_view(name)] = ToBeGenerate;
}
auto generate_task_and_its_base = [&](const std::string& name) -> bool {
@@ -66,52 +73,65 @@ bool asst::TaskData::parse(const json::value& json)
if (task_info_ptr == nullptr) {
return false;
}
to_be_generated[task_name_view(name)] = false;
task_status[task_name_view(name)] = NotToBeGenerate;
insert_or_assign_raw_task(name, task_info_ptr);
return true;
};
std::function<bool(const std::string&, bool)> generate_fun;
// must_true 若为真,那么 return false 了就是炸了。
// 否则可能只是某个 B@A 的任务没定义 A这不是少见现象例如 Roguelike@Abandon
generate_fun = [&](const std::string& name, bool must_true) -> bool {
// 不是需要 generate 的资源(不在 tasks.json 中)
if (!to_be_generated[task_name_view(name)]) {
// 已生成(它是之前加载过的某个资源的 base
switch (task_status[task_name_view(name)]) {
case NotToBeGenerate:
// 已经显式生成 或 曾经显式生成(外服隐式引用国服资源
if (m_raw_all_tasks_info.contains(name)) {
return true;
}
// 不在 json 内且未生成(例如生成 C@B@A 时没有定义 B@A而是定义了 A
// 隐式生成的资源
if (size_t p = name.find('@'); p != std::string::npos) {
return generate_fun(name.substr(p + 1), must_true);
}
// 例如 Roguelike@Abandon 在这里如果 must_true = true会报 `Unknown task: Abandon`
task_status[name] = NotExists;
[[fallthrough]];
case NotExists:
if (must_true) {
// 必须有名字为 name 的资源
Log.error("Unknown task:", name);
}
// 不一定必须有名字为 name 的资源,例如 Roguelike@Abandon 不必有 Abandon.
return false;
case ToBeGenerate: {
task_status[name] = Generating;
const json::value& task_json = json_obj.at(name);
if (auto opt = task_json.find<std::string>("baseTask")) {
// BaseTask
std::string base = opt.value();
if (!base.empty()) {
return generate_fun(base, must_true) && generate_task(name, "", get_raw(base), task_json);
}
// `"baseTask": ""` 表示不使用已生成的同名任务
}
else if (m_raw_all_tasks_info.contains(name)) {
// 已生成(外服覆写国服资源)
return generate_task(name, "", get_raw(name), task_json);
}
// TemplateTask
if (size_t p = name.find('@'); p != std::string::npos) {
if (std::string base = name.substr(p + 1); generate_fun(base, false)) {
return generate_task(name, name.substr(0, p), get_raw(base), task_json);
}
}
return generate_task(name, "", nullptr, task_json);
}
[[unlikely]] case Generating:
Log.error("Task", name, "is generated cyclically");
return false;
[[unlikely]] default:
Log.error("Task", name, "has unknown status");
return false;
}
const json::value& task_json = json_obj.at(name);
// 已生成(例如国际服覆写国服资源)
if (m_raw_all_tasks_info.contains(name)) {
return generate_task(name, "", get_raw(name), task_json);
}
// BaseTask
if (auto opt = task_json.find<std::string>("baseTask")) {
std::string base = opt.value();
return generate_fun(base, must_true) && generate_task(name, "", get_raw(base), task_json);
}
// TemplateTask
if (size_t p = name.find('@'); p != std::string::npos) {
if (std::string base = name.substr(p + 1); generate_fun(base, false)) {
return generate_task(name, name.substr(0, p), get_raw(base), task_json);
}
// 这类任务有点多,非必要不输出(
// Log.debug("Task", name, "based on unknown task, just use task_json.");
}
return generate_task(name, "", nullptr, task_json);
};
return generate_fun(name, true);
};