From b1a29ded7d1b6e03fc3d6a2f7feba68bd75afe15 Mon Sep 17 00:00:00 2001 From: Federico Cecchetto Date: Sun, 20 Jun 2021 20:04:36 +0200 Subject: [PATCH] Add httpGet function --- src/component/io.cpp | 20 +++++ src/component/scheduler.cpp | 122 +++++++++++++++++++++++-------- src/component/scheduler.hpp | 19 ++++- src/component/scripting.cpp | 2 + src/game/scripting/array.cpp | 4 +- src/game/scripting/execution.cpp | 10 +++ src/game/scripting/execution.hpp | 1 + src/stdinc.hpp | 5 ++ src/utils/concurrency.hpp | 46 ++++++++++++ src/utils/http.cpp | 47 ++++++++++++ src/utils/http.hpp | 11 +++ 11 files changed, 253 insertions(+), 34 deletions(-) create mode 100644 src/utils/concurrency.hpp create mode 100644 src/utils/http.cpp create mode 100644 src/utils/http.hpp diff --git a/src/component/io.cpp b/src/component/io.cpp index cbce74b..4530d21 100644 --- a/src/component/io.cpp +++ b/src/component/io.cpp @@ -161,6 +161,26 @@ namespace io return scripting::script_value{}; }); + + gsc::function::add("httpget", [](const gsc::function_args& args) -> scripting::script_value + { + const auto url = args[0].as(); + const auto object = scripting::entity(scripting::make_object()); + + scheduler::once([object, url]() + { + const auto result = utils::http::get_data(url.data()); + scheduler::once([object, result]() + { + const auto value = result.has_value() + ? result.value().substr(0, 0x5000) + : ""; + scripting::notify(object, "done", {value}); + }); + }, scheduler::pipeline::async); + + return object; + }); } }; } diff --git a/src/component/scheduler.cpp b/src/component/scheduler.cpp index 88f43f1..9bad71d 100644 --- a/src/component/scheduler.cpp +++ b/src/component/scheduler.cpp @@ -1,73 +1,128 @@ #include #include "loader/component_loader.hpp" +#include "scheduler.hpp" +#include "game/game.hpp" namespace scheduler { namespace { - std::queue> tasks; - struct task { - std::function handler; + std::function handler{}; std::chrono::milliseconds interval{}; std::chrono::high_resolution_clock::time_point last_call{}; }; - utils::concurrent_list callbacks; + using task_list = std::vector; - void execute() + class task_pipeline { - for (auto callback : callbacks) + public: + void add(task&& task) { - const auto now = std::chrono::high_resolution_clock::now(); - const auto diff = now - callback->last_call; - - if (diff < callback->interval) continue; - - callback->last_call = now; - - const auto res = callback->handler(); - if (res) + new_callbacks_.access([&task](task_list& tasks) { - callbacks.remove(callback); - } + tasks.emplace_back(std::move(task)); + }); } + + void execute() + { + callbacks_.access([&](task_list& tasks) + { + this->merge_callbacks(); + + for (auto i = tasks.begin(); i != tasks.end();) + { + const auto now = std::chrono::high_resolution_clock::now(); + const auto diff = now - i->last_call; + + if (diff < i->interval) + { + ++i; + continue; + } + + i->last_call = now; + + const auto res = i->handler(); + if (res == cond_end) + { + i = tasks.erase(i); + } + else + { + ++i; + } + } + }); + } + + private: + utils::concurrency::container new_callbacks_; + utils::concurrency::container callbacks_; + + void merge_callbacks() + { + callbacks_.access([&](task_list& tasks) + { + new_callbacks_.access([&](task_list& new_tasks) + { + tasks.insert(tasks.end(), std::move_iterator(new_tasks.begin()), std::move_iterator(new_tasks.end())); + new_tasks = {}; + }); + }); + } + }; + + std::thread thread; + task_pipeline pipelines[pipeline::count]; + + void execute(const pipeline type) + { + assert(type >= 0 && type < pipeline::count); + pipelines[type].execute(); } - void server_frame() + void server_frame_stub() { - execute(); reinterpret_cast(0x50C1E0)(); + execute(pipeline::server); } } - void schedule(const std::function& callback, const std::chrono::milliseconds delay) + void schedule(const std::function& callback, const pipeline type, + const std::chrono::milliseconds delay) { + assert(type >= 0 && type < pipeline::count); + task task; task.handler = callback; task.interval = delay; task.last_call = std::chrono::high_resolution_clock::now(); - callbacks.add(task); + pipelines[type].add(std::move(task)); } - void loop(const std::function& callback, const std::chrono::milliseconds delay) + void loop(const std::function& callback, const pipeline type, + const std::chrono::milliseconds delay) { schedule([callback]() { callback(); - return false; - }, delay); + return cond_continue; + }, type, delay); } - void once(const std::function& callback, const std::chrono::milliseconds delay) + void once(const std::function& callback, const pipeline type, + const std::chrono::milliseconds delay) { schedule([callback]() { callback(); - return true; - }, delay); + return cond_end; + }, type, delay); } class component final : public component_interface @@ -75,9 +130,18 @@ namespace scheduler public: void post_unpack() override { - utils::hook::call(0x50CEDC, server_frame); + thread = std::thread([]() + { + while (true) + { + execute(pipeline::async); + std::this_thread::sleep_for(10ms); + } + }); + + utils::hook::call(0x50CEDC, server_frame_stub); } }; } -REGISTER_COMPONENT(scheduler::component) \ No newline at end of file +REGISTER_COMPONENT(scheduler::component) diff --git a/src/component/scheduler.hpp b/src/component/scheduler.hpp index 4a5e5aa..cabb37e 100644 --- a/src/component/scheduler.hpp +++ b/src/component/scheduler.hpp @@ -2,7 +2,20 @@ namespace scheduler { - void schedule(const std::function& callback, std::chrono::milliseconds delay = 0ms); - void loop(const std::function& callback, std::chrono::milliseconds delay = 0ms); - void once(const std::function& callback, std::chrono::milliseconds delay = 0ms); + enum pipeline + { + server, + async, + count, + }; + + static const bool cond_continue = false; + static const bool cond_end = true; + + void schedule(const std::function& callback, pipeline type = pipeline::server, + std::chrono::milliseconds delay = 0ms); + void loop(const std::function& callback, pipeline type = pipeline::server, + std::chrono::milliseconds delay = 0ms); + void once(const std::function& callback, pipeline type = pipeline::server, + std::chrono::milliseconds delay = 0ms); } diff --git a/src/component/scripting.cpp b/src/component/scripting.cpp index 8255cf5..6b25737 100644 --- a/src/component/scripting.cpp +++ b/src/component/scripting.cpp @@ -85,6 +85,8 @@ namespace scripting { current_file = filename; + printf("%s\n", filename); + const auto file_id = atoi(filename); if (file_id) { diff --git a/src/game/scripting/array.cpp b/src/game/scripting/array.cpp index ef65854..8c96002 100644 --- a/src/game/scripting/array.cpp +++ b/src/game/scripting/array.cpp @@ -150,7 +150,7 @@ namespace scripting script_value array::get(const std::string& key) const { const auto string_value = game::SL_GetString(key.data(), 0); - const auto variable_id = game::GetVariable(this->id_, string_value); + const auto variable_id = game::FindVariable(this->id_, string_value); if (!variable_id) { @@ -167,7 +167,7 @@ namespace scripting script_value array::get(const unsigned int index) const { - const auto variable_id = game::GetVariable(this->id_, (index - 0x800000) & 0xFFFFFF); + const auto variable_id = game::FindVariable(this->id_, (index - 0x800000) & 0xFFFFFF); if (!variable_id) { diff --git a/src/game/scripting/execution.cpp b/src/game/scripting/execution.cpp index 8625390..7099cc1 100644 --- a/src/game/scripting/execution.cpp +++ b/src/game/scripting/execution.cpp @@ -283,4 +283,14 @@ namespace scripting return index; } + + unsigned int make_object() + { + unsigned int index = 0; + const auto variable = game::AllocVariable(&index); + variable->w.type = game::SCRIPT_STRUCT; + variable->u.f.prev = 0; + + return index; + } } \ No newline at end of file diff --git a/src/game/scripting/execution.hpp b/src/game/scripting/execution.hpp index f2cac08..71d8e37 100644 --- a/src/game/scripting/execution.hpp +++ b/src/game/scripting/execution.hpp @@ -36,4 +36,5 @@ namespace scripting void notify(const entity& entity, const std::string& event, const std::vector& arguments); unsigned int make_array(); + unsigned int make_object(); } diff --git a/src/stdinc.hpp b/src/stdinc.hpp index 09bc7ef..2e84379 100644 --- a/src/stdinc.hpp +++ b/src/stdinc.hpp @@ -25,6 +25,9 @@ #include #include #include +#include + +#pragma comment(lib, "urlmon.lib") using namespace std::literals; @@ -36,6 +39,8 @@ using namespace std::literals; #include "utils/hook.hpp" #include "utils/concurrent_list.hpp" #include "utils/io.hpp" +#include "utils/concurrency.hpp" +#include "utils/http.hpp" #include "game/structs.hpp" #include "game/game.hpp" \ No newline at end of file diff --git a/src/utils/concurrency.hpp b/src/utils/concurrency.hpp new file mode 100644 index 0000000..05c5d3a --- /dev/null +++ b/src/utils/concurrency.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +namespace utils::concurrency +{ + template + class container + { + public: + template + R access(F&& accessor) const + { + std::lock_guard _{mutex_}; + return accessor(object_); + } + + template + R access(F&& accessor) + { + std::lock_guard _{mutex_}; + return accessor(object_); + } + + template + R access_with_lock(F&& accessor) const + { + std::unique_lock lock{mutex_}; + return accessor(object_, lock); + } + + template + R access_with_lock(F&& accessor) + { + std::unique_lock lock{mutex_}; + return accessor(object_, lock); + } + + T& get_raw() { return object_; } + const T& get_raw() const { return object_; } + + private: + mutable MutexType mutex_{}; + T object_{}; + }; +} diff --git a/src/utils/http.cpp b/src/utils/http.cpp new file mode 100644 index 0000000..55725a2 --- /dev/null +++ b/src/utils/http.cpp @@ -0,0 +1,47 @@ +#include +#include "http.hpp" + +namespace utils::http +{ + std::optional get_data(const std::string& url) + { + CComPtr stream; + + if (FAILED(URLOpenBlockingStreamA(nullptr, url.data(), &stream, 0, nullptr))) + { + return {}; + } + + char buffer[0x1000]; + std::string result; + + HRESULT status{}; + + do + { + DWORD bytes_read = 0; + status = stream->Read(buffer, sizeof(buffer), &bytes_read); + + if (bytes_read > 0) + { + result.append(buffer, bytes_read); + } + } + while (SUCCEEDED(status) && status != S_FALSE); + + if (FAILED(status)) + { + return {}; + } + + return {result}; + } + + std::future> get_data_async(const std::string& url) + { + return std::async(std::launch::async, [url]() + { + return get_data(url); + }); + } +} diff --git a/src/utils/http.hpp b/src/utils/http.hpp new file mode 100644 index 0000000..65628a9 --- /dev/null +++ b/src/utils/http.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include + +namespace utils::http +{ + std::optional get_data(const std::string& url); + std::future> get_data_async(const std::string& url); +}