diff --git a/src/client/component/http.cpp b/src/client/component/http.cpp new file mode 100644 index 00000000..c92335da --- /dev/null +++ b/src/client/component/http.cpp @@ -0,0 +1,173 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/structs.hpp" +#include "game/game.hpp" + +#include "http.hpp" +#include "console.hpp" +#include "scheduler.hpp" +#include "scripting.hpp" + +#include +#include + +namespace http +{ + std::unordered_map active_requests{}; + uint64_t request_id{}; + + unsigned int http_get(const std::string& url) + { + const auto id = request_id++; + active_requests[id] = true; + + const auto object = scripting::array{}; + const auto object_id = object.get_entity_id(); + + scheduler::once([id, object_id, url]() + { + const auto data = utils::http::get_data(url); + scheduler::once([id, object_id, data]() + { + if (active_requests.find(id) == active_requests.end()) + { + return; + } + + if (!data.has_value()) + { + scripting::notify(object_id, "done", {{}, false, "Unknown error"}); + } + + const auto& result = data.value(); + const auto code = result.code; + const auto error = curl_easy_strerror(code); + + if (code != CURLE_OK) + { + scripting::notify(object_id, "done", {{}, false, error}); + } + + if (result.buffer.size() >= 0x5000) + { + console::warn("^3WARNING: HTTP result size bigger than 20480 bytes (%i), truncating!", static_cast(result.buffer.size())); + } + + scripting::notify(object_id, "done", {result.buffer.substr(0, 0x5000), true}); + }, scheduler::pipeline::server); + }, scheduler::pipeline::async); + + return object_id; + } + + unsigned int http_request(const std::string& url, const sol::variadic_args& va) + { + const auto id = request_id++; + active_requests[id] = true; + + const auto object = scripting::array{}; + const auto object_id = object.get_entity_id(); + + std::string fields_string{}; + std::unordered_map headers_map{}; + + if (va.size() > 0) + { + const scripting::array options = va[0].as(); + + const auto fields = options["parameters"]; + const auto body = options["body"]; + const auto headers = options["headers"]; + + if (fields.is()) + { + const auto fields_ = fields.as(); + const auto keys = fields_.get_keys(); + + for (const auto& key : keys) + { + if (!key.is()) + { + continue; + } + + const auto key_ = key.as(); + const auto value = fields_[key].to_string(); + fields_string += key_ + "=" + value + "&"; + } + } + + if (body.is()) + { + fields_string = body.as(); + } + + if (headers.is()) + { + const auto headers_ = headers.as(); + const auto keys = headers_.get_keys(); + + for (const auto& key : keys) + { + if (!key.is()) + { + continue; + } + + const auto key_ = key.as(); + const auto value = headers_[key].to_string(); + + headers_map[key_] = value; + } + } + } + + scheduler::once([id, object_id, url]() + { + const auto data = utils::http::get_data(url); + scheduler::once([id, object_id, data]() + { + if (active_requests.find(id) == active_requests.end()) + { + return; + } + + if (!data.has_value()) + { + scripting::notify(object_id, "done", {{}, false, "Unknown error"}); + } + + const auto& result = data.value(); + const auto code = result.code; + const auto error = curl_easy_strerror(code); + + if (code != CURLE_OK) + { + scripting::notify(object_id, "done", { {}, false, error }); + } + + if (result.buffer.size() >= 0x5000) + { + console::warn("^3WARNING: HTTP result size bigger than 20480 bytes (%i), truncating!", static_cast(result.buffer.size())); + } + + scripting::notify(object_id, "done", {result.buffer.substr(0, 0x5000), true}); + }, scheduler::pipeline::server); + }, scheduler::pipeline::async); + + return object_id; + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + scripting::on_shutdown([]() + { + active_requests.clear(); + }); + } + }; +} diff --git a/src/client/component/http.hpp b/src/client/component/http.hpp new file mode 100644 index 00000000..19408787 --- /dev/null +++ b/src/client/component/http.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "game/scripting/lua/value_conversion.hpp" +#include "game/scripting/array.hpp" +#include "game/scripting/execution.hpp" + +namespace http +{ + unsigned int http_get(const std::string& url); + unsigned int http_request(const std::string& url, const sol::variadic_args& va); +} diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp index efb81f41..9bcd2bc9 100644 --- a/src/client/component/scripting.cpp +++ b/src/client/component/scripting.cpp @@ -44,6 +44,8 @@ namespace scripting game::dvar_t* g_dump_scripts; + std::vector> shutdown_callbacks; + void vm_notify_stub(const unsigned int notify_list_owner_id, const game::scr_string_t string_value, game::VariableValue* top) { @@ -94,6 +96,11 @@ namespace scripting script_function_table.clear(); } + for (const auto& callback : shutdown_callbacks) + { + callback(); + } + scripting::notify(*game::levelEntityId, "shutdownGame_called", {1}); lua::engine::stop(); return g_shutdown_game_hook.invoke(free_scripts); @@ -162,6 +169,11 @@ namespace scripting } } + void on_shutdown(const std::function& callback) + { + shutdown_callbacks.push_back(callback); + } + class component final : public component_interface { public: diff --git a/src/client/component/scripting.hpp b/src/client/component/scripting.hpp index 5794bff2..226b275c 100644 --- a/src/client/component/scripting.hpp +++ b/src/client/component/scripting.hpp @@ -8,4 +8,6 @@ namespace scripting extern std::unordered_map> fields_table; extern std::unordered_map> script_function_table; extern utils::concurrency::container shared_table; + + void on_shutdown(const std::function& callback); } \ No newline at end of file diff --git a/src/client/game/scripting/array.hpp b/src/client/game/scripting/array.hpp index e78ace3b..64a9269b 100644 --- a/src/client/game/scripting/array.hpp +++ b/src/client/game/scripting/array.hpp @@ -76,6 +76,8 @@ namespace scripting { return { this->id_, this->get_value_id(key.as()) }; } + + throw std::runtime_error("Invalid key type"); } private: diff --git a/src/client/game/scripting/lua/context.cpp b/src/client/game/scripting/lua/context.cpp index 42140a86..e84cb59f 100644 --- a/src/client/game/scripting/lua/context.cpp +++ b/src/client/game/scripting/lua/context.cpp @@ -7,12 +7,15 @@ #include "../functions.hpp" #include "../../../component/command.hpp" +#include "../../../component/http.hpp" #include "../../../component/logfile.hpp" #include "../../../component/scripting.hpp" #include "../../../component/fastfiles.hpp" +#include "../../../component/scheduler.hpp" #include #include +#include namespace scripting::lua { @@ -567,6 +570,20 @@ namespace scripting::lua return sol::lua_value{s, sol::lua_nil}; } }; + + game_type["httpget"] = [](const game& game, const sol::this_state s, + const std::string& url) + { + const auto request = http::http_get(url); + return convert(s, scripting::entity{request}); + }; + + game_type["httprequest"] = [](const game& game, const sol::this_state s, + const std::string& url, sol::variadic_args va) + { + const auto request = http::http_request(url, va); + return convert(s, scripting::entity{request}); + }; } } diff --git a/src/client/game/scripting/script_value.cpp b/src/client/game/scripting/script_value.cpp index b99d831f..371693c0 100644 --- a/src/client/game/scripting/script_value.cpp +++ b/src/client/game/scripting/script_value.cpp @@ -2,6 +2,7 @@ #include "script_value.hpp" #include "entity.hpp" #include "array.hpp" +#include "functions.hpp" namespace scripting { @@ -290,4 +291,39 @@ namespace scripting { return this->value_.get(); } + + std::string script_value::to_string() const + { + if (this->is()) + { + return utils::string::va("%i", this->as()); + } + + if (this->is()) + { + return utils::string::va("%f", this->as()); + } + + if (this->is()) + { + return this->as(); + } + + if (this->is()) + { + const auto vec = this->as(); + return utils::string::va("(%g, %g, %g)", + vec.get_x(), + vec.get_y(), + vec.get_z() + ); + } + + if (this->is>()) + { + return utils::string::va("[[ function ]]"); + } + + return this->type_name(); + } } diff --git a/src/client/game/scripting/script_value.hpp b/src/client/game/scripting/script_value.hpp index 27cae79f..d20a01c2 100644 --- a/src/client/game/scripting/script_value.hpp +++ b/src/client/game/scripting/script_value.hpp @@ -3,6 +3,8 @@ #include "variable_value.hpp" #include "vector.hpp" +#include + namespace scripting { class entity; @@ -32,6 +34,14 @@ namespace scripting template bool is() const; + // was gonna do this but no clue if this is the same on H1 so just return string (https://github.com/fedddddd/t6-gsc-utils/blob/main/src/game/scripting/script_value.hpp#L18) + std::string type_name() const + { + return utils::string::va("%s", this->get_raw().type); + } + + std::string to_string() const; + template T as() const {