HTTP functions #5

This commit is contained in:
Federico Cecchetto 2022-06-14 23:56:15 +02:00
parent 2111db394d
commit 8c0d999a2a
12 changed files with 272 additions and 35 deletions

View File

@ -57,7 +57,7 @@ namespace command
const scripting::entity player = game::Scr_GetEntityId(game::SCRIPTINSTANCE_SERVER, client_num, 0, 0);
const auto command = utils::string::to_lower(params[0]);
const auto message = params.join(1);
const auto message = params.join(1).substr(1);
for (const auto& callback : chat_callbacks)
{

View File

@ -207,6 +207,18 @@ namespace gsc
return scr_get_builtin_hook.invoke<unsigned int>(inst, func_name);
}
// Scr_NotifyId doesn't exist, Scr_NotifyNum_Internal calls FindVariableId to get the variable id from entnum, classnum & clientNum
// to not have to recreate Scr_NotifyId we simply make FindVariableId return `entnum` (which in this case will be the id) if `clientNum` == -1
unsigned int find_variable_id_stub(int inst, int entnum, unsigned int classnum, int client_num)
{
if (client_num == -1)
{
return entnum;
}
return utils::hook::invoke<unsigned int>(SELECT_VALUE(0x5E96E0, 0x40BEF0), inst, entnum, classnum, client_num);
}
}
namespace function
@ -251,6 +263,8 @@ namespace gsc
utils::hook::set<char>(SELECT_VALUE(0x9FC5C0 + 40, 0xAABA68 + 40), '\n');
utils::hook::set<char>(SELECT_VALUE(0x9FC5C0 + 41, 0xAABA68 + 41), '\0');
utils::hook::call(SELECT_VALUE(0x41D2B5, 0x416325), find_variable_id_stub);
gsc::function::add("array", [](const scripting::variadic_args& va)
{
scripting::array array{};
@ -267,6 +281,30 @@ namespace gsc
{
return value.type_name();
}, "typeof", "type");
gsc::function::add("debug::get_var_count", []()
{
auto count = 0;
if (game::environment::is_sp())
{
for (auto i = 1; i < 0x5FFE; i++)
{
const auto var = game::scr_VarGlob->variableList_mp[i];
count += var.w.status != 0;
}
}
else
{
for (auto i = 1; i < 0x7FFE; i++)
{
const auto var = game::scr_VarGlob->variableList_mp[i];
count += var.w.status != 0;
}
}
return count;
});
}
};
}

177
src/component/http.cpp Normal file
View File

@ -0,0 +1,177 @@
#include <stdinc.hpp>
#include "loader/component_loader.hpp"
#include "game/structs.hpp"
#include "game/game.hpp"
#include "gsc.hpp"
#include "scheduler.hpp"
#include "scripting.hpp"
#include <utils/http.hpp>
#include <curl/curl.h>
namespace http
{
std::unordered_map<uint64_t, bool> active_requests{};
uint64_t request_id{};
class component final : public component_interface
{
public:
void post_unpack() override
{
scripting::on_shutdown([]()
{
active_requests.clear();
});
gsc::function::add_multiple([](const std::string& url)
{
const auto id = request_id++;
active_requests[id] = true;
const auto object = scripting::object{};
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"});
return;
}
const auto& result = data.value();
const auto error = curl_easy_strerror(result.code);
if (result.code != CURLE_OK)
{
scripting::notify(object_id, "done", {{}, false, error});
return;
}
if (result.buffer.size() >= 0x5000)
{
printf("^3WARNING: http result size bigger than 20480 bytes (%i), truncating!", static_cast<int>(result.buffer.size()));
}
scripting::notify(object_id, "done", {result.buffer.substr(0, 0x5000), true});
}, scheduler::pipeline::server);
}, scheduler::pipeline::async);
return object;
}, "http::get", "httpget", "curl");
gsc::function::add("http::request", [](const std::string& url, const scripting::variadic_args& va)
{
const auto id = request_id++;
active_requests[id] = true;
const auto object = scripting::object{};
const auto object_id = object.get_entity_id();
std::string fields_string{};
std::unordered_map<std::string, std::string> headers_map{};
if (va.size() > 0)
{
const auto options = va[0].as<scripting::array>();
const auto fields = options["parameters"];
const auto body = options["body"];
const auto headers = options["headers"];
if (fields.is<scripting::array>())
{
const auto fields_ = fields.as<scripting::array>();
const auto keys = fields_.get_keys();
for (const auto& key : keys)
{
if (!key.is<std::string>())
{
continue;
}
const auto key_ = key.as<std::string>();
const auto value = fields_[key].to_string();
fields_string += key_ + "=" + value + "&";
}
}
if (body.is<std::string>())
{
fields_string = body.as<std::string>();
}
if (headers.is<scripting::array>())
{
const auto headers_ = headers.as<scripting::array>();
const auto keys = headers_.get_keys();
for (const auto& key : keys)
{
if (!key.is<std::string>())
{
continue;
}
const auto key_ = key.as<std::string>();
const auto value = headers_[key].to_string();
headers_map[key_] = value;
}
}
}
scheduler::once([id, object_id, url, fields_string, headers_map]()
{
const auto data = utils::http::get_data(url, fields_string, headers_map);
scheduler::once([data, object_id, id]
{
if (active_requests.find(id) == active_requests.end())
{
return;
}
if (!data.has_value())
{
scripting::notify(object_id, "done", {{}, false, "Unknown error"});
return;
}
const auto& result = data.value();
const auto error = curl_easy_strerror(result.code);
if (result.code != CURLE_OK)
{
scripting::notify(object_id, "done", {{}, false, error});
return;
}
if (result.buffer.size() >= 0x5000)
{
printf("^3WARNING: http result size bigger than 20480 bytes (%i), truncating!", static_cast<int>(result.buffer.size()));
}
scripting::notify(object_id, "done", {result.buffer.substr(0, 0x5000), true});
}, scheduler::pipeline::server);
}, scheduler::pipeline::async);
return object;
});
}
};
}
REGISTER_COMPONENT(http::component)

View File

@ -172,11 +172,14 @@ namespace string
gsc::function::add("print", [](const scripting::variadic_args& va)
{
std::string buffer{};
for (const auto& arg : va)
{
printf("%s\t", arg.to_string().data());
buffer.append(utils::string::va("%s\t", arg.to_string().data()));
}
printf("\n");
printf("%s\n", buffer.data());
});
gsc::function::add_multiple(utils::string::to_upper, "toupper", "string::to_upper");

View File

@ -67,6 +67,12 @@ namespace game
return utils::hook::invoke<unsigned int>(func, inst);
}
void Scr_NotifyId(scriptInstance_t inst, int /*client_num*/, unsigned int id,
unsigned int string_value, unsigned int paramcount)
{
game::Scr_NotifyNum_Internal(inst, -1, id, 0, string_value, paramcount);
}
VariableValue Scr_GetArrayIndexValue(scriptInstance_t, unsigned int name)
{
VariableValue value{};

View File

@ -65,6 +65,9 @@ namespace game
VariableValue Scr_GetArrayIndexValue(scriptInstance_t inst, unsigned int name);
unsigned int Scr_GetSelf(scriptInstance_t inst, unsigned int threadId);
void Scr_NotifyId(scriptInstance_t inst, int client_num, unsigned int id,
unsigned int string_value, unsigned int paramcount);
}
#include "symbols.hpp"

View File

@ -72,6 +72,8 @@ namespace scripting
{
return {this->id_, this->get_value_id(key.as<S>())};
}
throw std::runtime_error("Invalid key type");
}
private:

View File

@ -63,17 +63,7 @@ namespace scripting
object::object()
{
this->id_ = make_object();
}
object::object(std::unordered_map<std::string, script_value> values)
{
this->id_ = make_object();
for (const auto& value : values)
{
this->set(value.first, value.second);
}
this->id_ = game::AllocObject(game::SCRIPTINSTANCE_SERVER);
}
object::~object()
@ -157,14 +147,14 @@ namespace scripting
return game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_);
}
void object::erase(const std::string& key) const
void object::erase(const std::string& /*key*/) const
{
const auto string_value = game::SL_GetCanonicalString(key.data(), 0);
/*const auto string_value = game::SL_GetCanonicalString(key.data(), 0);
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
if (variable_id)
{
game::RemoveVariableValue(game::SCRIPTINSTANCE_SERVER, this->id_, variable_id);
}
}*/
}
script_value object::get(const std::string& /*key*/) const

View File

@ -19,8 +19,6 @@ namespace scripting
object();
object(const unsigned int);
object(std::unordered_map<std::string, script_value>);
object(const object& other);
object(object&& other) noexcept;

View File

@ -41,7 +41,7 @@ namespace game
WEAK symbol<void(scriptInstance_t inst)> Scr_ClearOutParams{0x654D10, 0x588680};
WEAK symbol<unsigned int(scriptInstance_t inst)> AllocObject{0x0, 0x0};
WEAK symbol<unsigned int(scriptInstance_t inst)> AllocObject{0x603400, 0x6970B0};
WEAK symbol<unsigned int(scriptInstance_t inst, unsigned int id)> AllocThread{0x69E140, 0x43CA60};
WEAK symbol<void(scriptInstance_t inst, unsigned int id)> RemoveRefToObject{0x5517B0, 0x698FA0};
WEAK symbol<void(scriptInstance_t inst, const float* vectorValue)> RemoveRefToVector{0x0, 0x0};
@ -69,7 +69,8 @@ namespace game
WEAK symbol<VariableValue(scriptInstance_t inst, unsigned int classnum, int entnum, int clientNum, int offset)> GetEntityFieldValue{0x0, 0x0};
WEAK symbol<void(gentity_s* ent, unsigned int stringValue, unsigned int paramcount)> Scr_Notify{0x0, 0x0};
WEAK symbol<void(scriptInstance_t inst, int clientNum, unsigned int id, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyId{0x0, 0x0};
WEAK symbol<void(scriptInstance_t inst, int clientNum, int entnum,
unsigned int classnum, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyNum_Internal{0x41D270, 0x4162E0};
WEAK symbol<void(int entnum, unsigned int classnum, unsigned int stringValue, unsigned int paramcount)> Scr_NotifyNum{0x0, 0x0};
WEAK symbol<unsigned int(const char* str, unsigned int user, scriptInstance_t inst)> SL_GetString{0x463130, 0x4B1770};

View File

@ -1,6 +1,6 @@
#include <stdinc.hpp>
#include "http.hpp"
#include <curl/curl.h>
#include <gsl/gsl>
#pragma comment(lib, "ws2_32.lib")
@ -45,7 +45,8 @@ namespace utils::http
}
}
std::optional<std::string> get_data(const std::string& url, const headers& headers, const std::function<void(size_t)>& callback)
std::optional<result> get_data(const std::string& url, const std::string& fields,
const headers& headers, const std::function<void(size_t)>& callback)
{
curl_slist* header_list = nullptr;
auto* curl = curl_easy_init();
@ -78,9 +79,27 @@ namespace utils::http
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &helper);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
if (curl_easy_perform(curl) == CURLE_OK)
if (!fields.empty())
{
return {std::move(buffer)};
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, fields.data());
}
const auto code = curl_easy_perform(curl);
if (code == CURLE_OK)
{
result result;
result.code = code;
result.buffer = std::move(buffer);
return result;
}
else
{
result result;
result.code = code;
return result;
}
if (helper.exception)
@ -90,12 +109,4 @@ namespace utils::http
return {};
}
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers)
{
return std::async(std::launch::async, [url, headers]()
{
return get_data(url, headers);
});
}
}

View File

@ -4,10 +4,18 @@
#include <optional>
#include <future>
#include <curl/curl.h>
namespace utils::http
{
struct result
{
CURLcode code;
std::string buffer;
};
using headers = std::unordered_map<std::string, std::string>;
std::optional<std::string> get_data(const std::string& url, const headers& headers = {}, const std::function<void(size_t)>& callback = {});
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers = {});
std::optional<result> get_data(const std::string& url, const std::string& fields = {},
const headers& headers = {}, const std::function<void(size_t)>& callback = {});
}