#include #include "loader/component_loader.hpp" #include "gsc.hpp" #include "game/scripting/array.hpp" #include #include #include namespace bank { namespace { utils::bank::outbox spool(const std::string& server, const std::string& endpoint) { return {"tws-bank/t6", server, endpoint}; } // Only fixed operation names are printed, never exception text or paths // from private configuration. Per-operation state has bounded memory. void storage_result(size_t operation, bool failed) { struct status { bool failed = false; std::chrono::steady_clock::time_point logged{}; unsigned suppressed = 0; }; static std::array states; constexpr const char* names[] = {"load", "write", "remove"}; auto& state = states[operation]; const auto now = std::chrono::steady_clock::now(); if (failed && state.failed && now - state.logged < std::chrono::seconds(30)) { ++state.suppressed; return; } if (failed || state.failed) printf("[tws_bank] native_storage operation=%s outcome=%s suppressed=%u\n", names[operation], failed ? "storage_failure" : "recovered", state.suppressed); state.failed = failed; state.logged = now; state.suppressed = 0; } } class component final : public component_interface { public: void on_startup([[maybe_unused]] plugin::plugin* plugin) override { gsc::function::add("bankversion", []() { return std::string("1.2.0"); }); gsc::function::add("bankload", [](const std::string& server, const std::string& endpoint) -> scripting::script_value { try { scripting::array entries; for (const auto& value : spool(server, endpoint).load()) { scripting::array entry; entry["id"] = value.id; entry["decision"] = value.decision; entry["body"] = value.body; entries.emplace_back(entry); } storage_result(0, false); return entries; } catch (...) { storage_result(0, true); return {}; } }); gsc::function::add("bankstore", [](const std::string& server, const std::string& endpoint, const std::string& body) { try { spool(server, endpoint).store(body); storage_result(1, false); return true; } catch (...) { storage_result(1, true); return false; } }); gsc::function::add("bankremove", [](const std::string& server, const std::string& endpoint, const std::string& id) { try { spool(server, endpoint).erase(id); storage_result(2, false); return true; } catch (...) { storage_result(2, true); return false; } }); gsc::function::add("bankresponse", [](const std::string& response) { const auto ack = utils::bank::parse_acknowledgement(response); scripting::array result; result["valid"] = ack.valid; result["reason"] = ack.reason; result["state"] = ack.state; result["code"] = ack.code; result["request_id"] = ack.id; result["amount"] = ack.amount; result["balance"] = ack.balance; return result; }); } }; } REGISTER_COMPONENT(bank::component)