Vendor pinned T5 and T6 plugin sources under the new plugins layout. Add native HTTP deadlines, status and timing diagnostics, telemetry integration, and build/test tooling.
320 lines
6.9 KiB
C++
320 lines
6.9 KiB
C++
#include <stdinc.hpp>
|
|
#include "loader/component_loader.hpp"
|
|
|
|
#include "game/scripting/event.hpp"
|
|
#include "game/scripting/execution.hpp"
|
|
#include "game/scripting/array.hpp"
|
|
|
|
#include "gsc.hpp"
|
|
#include "json.hpp"
|
|
#include "scripting.hpp"
|
|
|
|
#include <utils/io.hpp>
|
|
|
|
namespace json
|
|
{
|
|
namespace
|
|
{
|
|
constexpr const auto max_str_len = 0x5000u;
|
|
|
|
struct dump_context_t
|
|
{
|
|
std::unordered_set<std::uint32_t> visited_objects;
|
|
};
|
|
|
|
nlohmann::json gsc_to_json(const scripting::script_value& value, bool print_id, dump_context_t& ctx);
|
|
|
|
nlohmann::json array_to_json(const scripting::array& array, bool print_id, dump_context_t& ctx)
|
|
{
|
|
if (array.size() == 0)
|
|
{
|
|
return nlohmann::json::array();
|
|
}
|
|
|
|
nlohmann::json obj;
|
|
|
|
auto string_indexed = -1;
|
|
const auto keys = array.get_keys();
|
|
|
|
for (const auto& key : keys)
|
|
{
|
|
const auto is_int = key.is<int>();
|
|
const auto is_string = key.is<std::string>();
|
|
|
|
if (string_indexed == -1)
|
|
{
|
|
string_indexed = is_string;
|
|
}
|
|
|
|
if (!string_indexed && is_int)
|
|
{
|
|
const auto index = key.as<int>();
|
|
obj[index] = gsc_to_json(array[index], print_id, ctx);
|
|
}
|
|
else if (string_indexed && is_string)
|
|
{
|
|
const auto key_str = key.as<std::string>();
|
|
obj.emplace(key_str, gsc_to_json(array[key_str], print_id, ctx));
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
nlohmann::json object_to_json(const scripting::object& object, bool print_id, dump_context_t& ctx)
|
|
{
|
|
const auto id = object.get_entity_id();
|
|
if (ctx.visited_objects.find(id) != ctx.visited_objects.end())
|
|
{
|
|
return utils::string::va("[struct reference %i]", id);
|
|
}
|
|
|
|
ctx.visited_objects.insert(id);
|
|
auto obj = nlohmann::json::object();
|
|
|
|
if (print_id)
|
|
{
|
|
obj["__id"] = id;
|
|
}
|
|
|
|
const auto keys = object.get_keys();
|
|
for (const auto& key : keys)
|
|
{
|
|
if (key == "__id")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
obj.emplace(key, gsc_to_json(object[key], print_id, ctx));
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
nlohmann::json vector_to_array(const float* value)
|
|
{
|
|
nlohmann::json obj;
|
|
obj.push_back(value[0]);
|
|
obj.push_back(value[1]);
|
|
obj.push_back(value[2]);
|
|
return obj;
|
|
}
|
|
|
|
nlohmann::json gsc_to_json(const scripting::script_value& value, bool print_id, dump_context_t& ctx)
|
|
{
|
|
const auto& variable = value.get_raw();
|
|
|
|
if (value.is<int>())
|
|
{
|
|
return value.as<int>();
|
|
}
|
|
|
|
if (value.is<float>())
|
|
{
|
|
return value.as<float>();
|
|
}
|
|
|
|
if (value.is<std::string>())
|
|
{
|
|
return value.as<std::string>();
|
|
}
|
|
|
|
if (value.is<scripting::vector>())
|
|
{
|
|
return vector_to_array(variable.u.vectorValue);
|
|
}
|
|
|
|
if (value.is<scripting::object>())
|
|
{
|
|
return object_to_json(variable.u.uintValue, print_id, ctx);
|
|
}
|
|
|
|
if (value.is<scripting::array>())
|
|
{
|
|
return array_to_json(variable.u.uintValue, print_id, ctx);
|
|
}
|
|
|
|
if (value.is<scripting::entity>())
|
|
{
|
|
return object_to_json(variable.u.uintValue, print_id, ctx);
|
|
}
|
|
|
|
if (value.is<scripting::function>())
|
|
{
|
|
const auto function = value.as<scripting::function>();
|
|
return utils::string::va("[[ %s ]]", function.get_name().data());
|
|
}
|
|
|
|
if (variable.type == game::SCRIPT_NONE)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (variable.type == game::SCRIPT_CODEPOS)
|
|
{
|
|
const auto function = scripting::find_function(variable.u.codePosValue);
|
|
return utils::string::va("[codepos: %s]", function.data());
|
|
}
|
|
|
|
if (variable.type == game::SCRIPT_END)
|
|
{
|
|
const auto function = scripting::find_function(variable.u.codePosValue);
|
|
return utils::string::va("[precodepos: %s]", function.data());
|
|
}
|
|
|
|
return utils::string::va("[%s]", value.type_name().data());
|
|
}
|
|
|
|
scripting::script_value json_to_gsc(nlohmann::json obj)
|
|
{
|
|
const auto type = obj.type();
|
|
|
|
switch (type)
|
|
{
|
|
case (nlohmann::detail::value_t::boolean):
|
|
return obj.get<bool>();
|
|
case (nlohmann::detail::value_t::number_integer):
|
|
case (nlohmann::detail::value_t::number_unsigned):
|
|
return obj.get<int>();
|
|
case (nlohmann::detail::value_t::number_float):
|
|
return obj.get<float>();
|
|
case (nlohmann::detail::value_t::string):
|
|
return obj.get<std::string>();
|
|
case (nlohmann::detail::value_t::array):
|
|
{
|
|
scripting::array array;
|
|
|
|
for (const auto& [key, value] : obj.items())
|
|
{
|
|
array.emplace_back(json_to_gsc(value));
|
|
}
|
|
|
|
return array.get_raw();
|
|
}
|
|
case (nlohmann::detail::value_t::object):
|
|
{
|
|
scripting::array array;
|
|
|
|
for (const auto& [key, value] : obj.items())
|
|
{
|
|
array[key] = json_to_gsc(value);
|
|
}
|
|
|
|
return array.get_raw();
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
}
|
|
|
|
std::string gsc_to_string(const scripting::script_value& value)
|
|
{
|
|
dump_context_t ctx;
|
|
return gsc_to_json(value, false, ctx).dump();
|
|
}
|
|
|
|
class component final : public component_interface
|
|
{
|
|
public:
|
|
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
|
{
|
|
gsc::function::add_multiple([](const scripting::variadic_args& args)
|
|
{
|
|
scripting::array array;
|
|
|
|
for (auto i = 0u; i < args.size(); i += 2)
|
|
{
|
|
if (i >= args.size() - 1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const auto key = args[i].as<std::string>();
|
|
array[key] = args[i + 1];
|
|
}
|
|
|
|
return array;
|
|
}, "createmap", "json::create_map");
|
|
|
|
gsc::function::add_multiple([](const std::string& json)
|
|
{
|
|
const auto obj = nlohmann::json::parse(json);
|
|
return json_to_gsc(obj);
|
|
}, "jsonparse", "json::parse");
|
|
|
|
gsc::function::add_multiple([](const scripting::script_value& value, const scripting::variadic_args& args)
|
|
{
|
|
auto indent = -1;
|
|
auto print_id = false;
|
|
|
|
if (args.size() > 0)
|
|
{
|
|
indent = args[0].as<int>();
|
|
}
|
|
|
|
if (args.size() > 1)
|
|
{
|
|
print_id = args[1].as<bool>();
|
|
}
|
|
|
|
dump_context_t ctx;
|
|
const auto json = gsc_to_json(value, print_id, ctx);
|
|
auto dump = json.dump(indent);
|
|
|
|
if (dump.size() > max_str_len)
|
|
{
|
|
printf("^3WARNING: json dump longer than %i bytes, truncating!", max_str_len);
|
|
dump.resize(max_str_len);
|
|
}
|
|
|
|
return dump;
|
|
}, "jsonserialize", "json::serialize");
|
|
|
|
gsc::function::add_multiple([](const std::string& file, const scripting::script_value& value, const scripting::variadic_args& va)
|
|
{
|
|
std::string file_name = file;
|
|
|
|
auto indent = -1;
|
|
auto print_id = false;
|
|
|
|
if (!file_name.ends_with(".json"))
|
|
{
|
|
file_name.append(".json");
|
|
}
|
|
|
|
if (va.size() > 0)
|
|
{
|
|
indent = va[0].as<int>();
|
|
}
|
|
|
|
if (va.size() > 1)
|
|
{
|
|
print_id = va[1].as<bool>();
|
|
}
|
|
|
|
dump_context_t ctx;
|
|
const auto json = gsc_to_json(value, print_id, ctx);
|
|
const auto dump = json.dump(indent);
|
|
|
|
return utils::io::write_file(file_name, dump);
|
|
}, "jsondump", "json::dump");
|
|
|
|
gsc::function::add_multiple([](const scripting::variadic_args& args)
|
|
{
|
|
std::string buffer;
|
|
|
|
for (const auto& arg : args)
|
|
{
|
|
buffer.append(gsc_to_string(arg));
|
|
buffer.append("\t");
|
|
}
|
|
|
|
printf("%s\n", buffer.data());
|
|
}, "jsonprint", "json::print");
|
|
}
|
|
};
|
|
}
|
|
|
|
REGISTER_COMPONENT(json::component)
|