mirror of
https://github.com/fedddddd/iw5-gsc-utils.git
synced 2025-07-03 09:41:51 +00:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
705ad50d65 | |||
e8cad54293 | |||
a548e9d04b | |||
d4c8a7ce2c | |||
ba290e0f7c | |||
234f510e98 | |||
8224ca3b57 | |||
a19c2761c8 | |||
739ea2a7f0 | |||
6c99991429 | |||
c9543dfbe4 | |||
76842b4bce | |||
7139e8d3c4 | |||
6f53ed5d87 | |||
063caa51f9 | |||
9ed8626067 | |||
cb0725bad1 | |||
44886c8f9f | |||
bdfd37de35 | |||
c49ca5a8cd | |||
b1a29ded7d | |||
e5083cd228 |
11
README.md
11
README.md
@ -43,6 +43,15 @@ This plugin adds some useful functions/methods to IW5's GSC VM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
# Player
|
||||||
|
* `say(message)`: Prints a message to all players' chat.
|
||||||
|
|
||||||
|
* `self tell(message)`: Prints a message to the player's chat.
|
||||||
|
* `self setName(name)`: Sets a player's name.
|
||||||
|
* `self resetName(name)`: Resets a player's name to its original.
|
||||||
|
* `self setClantag(name)`: Sets a player's clantag.
|
||||||
|
* `self resetClantag(name)`: Resets a player's clantag to its original.
|
||||||
|
* `self removeClantag(name)`: Removes a player's clantag.
|
||||||
# IO
|
# IO
|
||||||
|
|
||||||
The basepath for all IO functions is `Plutonium/storage/iw5`
|
The basepath for all IO functions is `Plutonium/storage/iw5`
|
||||||
@ -217,6 +226,6 @@ The basepath for all IO functions is `Plutonium/storage/iw5`
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
* `jsonPrint(...)`: Prints values as json.
|
||||||
# Credits
|
# Credits
|
||||||
* [xensik](https://github.com/xensik)
|
* [xensik](https://github.com/xensik)
|
||||||
|
2
deps/GSL
vendored
2
deps/GSL
vendored
Submodule deps/GSL updated: c1cbb41b42...c31a9ad5e8
@ -14,8 +14,6 @@
|
|||||||
|
|
||||||
namespace gsc
|
namespace gsc
|
||||||
{
|
{
|
||||||
std::unordered_map<const char*, const char*> replaced_functions;
|
|
||||||
|
|
||||||
function_args::function_args(std::vector<scripting::script_value> values)
|
function_args::function_args(std::vector<scripting::script_value> values)
|
||||||
: values_(values)
|
: values_(values)
|
||||||
{
|
{
|
||||||
@ -109,7 +107,7 @@ namespace gsc
|
|||||||
{
|
{
|
||||||
if (id < 0x200)
|
if (id < 0x200)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<builtin_function*>(0x1D6EB34)[id]();
|
return reinterpret_cast<builtin_function*>(0x20689DD8)[id]();
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -135,7 +133,7 @@ namespace gsc
|
|||||||
{
|
{
|
||||||
if (id < 0x8400)
|
if (id < 0x8400)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<builtin_method*>(0x1D4F258)[id](ent);
|
return reinterpret_cast<builtin_method*>(0x2068A5A8)[id - 0x8000](ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -201,62 +199,6 @@ namespace gsc
|
|||||||
retn
|
retn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* replaced_pos = 0;
|
|
||||||
|
|
||||||
void get_replaced_pos(const char* pos)
|
|
||||||
{
|
|
||||||
if (replaced_functions.find(pos) != replaced_functions.end())
|
|
||||||
{
|
|
||||||
replaced_pos = replaced_functions[pos];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(naked) void vm_execute_stub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
pushad
|
|
||||||
push esi
|
|
||||||
call get_replaced_pos
|
|
||||||
pop esi
|
|
||||||
popad
|
|
||||||
|
|
||||||
cmp replaced_pos, 0
|
|
||||||
jne set_pos
|
|
||||||
|
|
||||||
movzx eax, byte ptr[esi]
|
|
||||||
inc esi
|
|
||||||
|
|
||||||
jmp loc_1
|
|
||||||
loc_1:
|
|
||||||
mov [ebp - 0x18], eax
|
|
||||||
mov [ebp - 0x8], esi
|
|
||||||
|
|
||||||
push ecx
|
|
||||||
|
|
||||||
mov ecx, 0x20B8E28
|
|
||||||
mov [ecx], eax
|
|
||||||
|
|
||||||
mov ecx, 0x20B4A5C
|
|
||||||
mov[ecx], esi
|
|
||||||
|
|
||||||
pop ecx
|
|
||||||
|
|
||||||
cmp eax, 0x98
|
|
||||||
|
|
||||||
push 0x56B740
|
|
||||||
retn
|
|
||||||
set_pos:
|
|
||||||
mov esi, replaced_pos
|
|
||||||
mov replaced_pos, 0
|
|
||||||
|
|
||||||
movzx eax, byte ptr[esi]
|
|
||||||
inc esi
|
|
||||||
|
|
||||||
jmp loc_1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace function
|
namespace function
|
||||||
@ -292,28 +234,11 @@ namespace gsc
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
function::add("replacefunc", [](const function_args& args) -> scripting::script_value
|
|
||||||
{
|
|
||||||
const auto what = args[0].as<scripting::function>();
|
|
||||||
const auto with = args[1].as<scripting::function>();
|
|
||||||
|
|
||||||
replaced_functions[what.get_pos()] = with.get_pos();
|
|
||||||
|
|
||||||
return {};
|
|
||||||
});
|
|
||||||
|
|
||||||
function::add("addcommand", [](const function_args& args) -> scripting::script_value
|
function::add("addcommand", [](const function_args& args) -> scripting::script_value
|
||||||
{
|
{
|
||||||
const auto name = args[0].as<std::string>();
|
const auto name = args[0].as<std::string>();
|
||||||
const auto function = args[1].get_raw();
|
const auto function = args[1].as<scripting::function>();
|
||||||
|
command::add_script_command(name, [function](const command::params& params)
|
||||||
if (function.type != game::SCRIPT_FUNCTION)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Invalid type");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto pos = function.u.codePosValue;
|
|
||||||
command::add_script_command(name, [pos](const command::params& params)
|
|
||||||
{
|
{
|
||||||
scripting::array array;
|
scripting::array array;
|
||||||
for (auto i = 0; i < params.size(); i++)
|
for (auto i = 0; i < params.size(); i++)
|
||||||
@ -321,7 +246,7 @@ namespace gsc
|
|||||||
array.push(params[i]);
|
array.push(params[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
scripting::exec_ent_thread(*game::levelEntityId, pos, {array.get_raw()});
|
function({array.get_raw()});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@ -350,9 +275,29 @@ namespace gsc
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
method::add("specialtymarathon", [](const game::scr_entref_t ent, const function_args& args) -> scripting::script_value
|
||||||
|
{
|
||||||
|
if (ent.classnum != 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid entity");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto num = ent.entnum;
|
||||||
|
const auto toggle = args[0].as<int>();
|
||||||
|
|
||||||
|
auto g_client = game::g_entities[num].client;
|
||||||
|
auto playerState = &g_client->ps;
|
||||||
|
auto flags = playerState->perks[0];
|
||||||
|
|
||||||
|
playerState->perks[0] = toggle
|
||||||
|
? flags | 0x4000u
|
||||||
|
: flags & ~0x4000u;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
utils::hook::jump(0x56C8EB, call_builtin_stub);
|
utils::hook::jump(0x56C8EB, call_builtin_stub);
|
||||||
utils::hook::jump(0x56CBDC, call_builtin_method_stub);
|
utils::hook::jump(0x56CBDC, call_builtin_method_stub);
|
||||||
utils::hook::jump(0x56B726, vm_execute_stub);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,13 @@
|
|||||||
|
|
||||||
namespace gsc
|
namespace gsc
|
||||||
{
|
{
|
||||||
extern std::unordered_map<const char*, const char*> replaced_functions;
|
|
||||||
|
|
||||||
class function_args
|
class function_args
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
function_args(std::vector<scripting::script_value>);
|
function_args(std::vector<scripting::script_value>);
|
||||||
|
|
||||||
unsigned int function_args::size() const;
|
unsigned int size() const;
|
||||||
std::vector<scripting::script_value> function_args::get_raw() const;
|
std::vector<scripting::script_value> get_raw() const;
|
||||||
scripting::script_value get(const int index) const;
|
scripting::script_value get(const int index) const;
|
||||||
|
|
||||||
scripting::script_value operator[](const int index) const
|
scripting::script_value operator[](const int index) const
|
||||||
|
@ -150,7 +150,7 @@ namespace io
|
|||||||
array.push(file);
|
array.push(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return array.get_raw();
|
return array;
|
||||||
});
|
});
|
||||||
|
|
||||||
gsc::function::add("copyfolder", [](const gsc::function_args& args)
|
gsc::function::add("copyfolder", [](const gsc::function_args& args)
|
||||||
@ -161,6 +161,26 @@ namespace io
|
|||||||
|
|
||||||
return scripting::script_value{};
|
return scripting::script_value{};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gsc::function::add("httpget", [](const gsc::function_args& args) -> scripting::script_value
|
||||||
|
{
|
||||||
|
const auto url = args[0].as<std::string>();
|
||||||
|
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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ namespace json
|
|||||||
array[key] = args[i + 1];
|
array[key] = args[i + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
return array.get_raw();
|
return array;
|
||||||
});
|
});
|
||||||
|
|
||||||
gsc::function::add("jsonparse", [](const gsc::function_args& args)
|
gsc::function::add("jsonparse", [](const gsc::function_args& args)
|
||||||
|
@ -1,73 +1,128 @@
|
|||||||
#include <stdinc.hpp>
|
#include <stdinc.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
namespace scheduler
|
namespace scheduler
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::queue<std::function<void()>> tasks;
|
|
||||||
|
|
||||||
struct task
|
struct task
|
||||||
{
|
{
|
||||||
std::function<bool()> handler;
|
std::function<bool()> handler{};
|
||||||
std::chrono::milliseconds interval{};
|
std::chrono::milliseconds interval{};
|
||||||
std::chrono::high_resolution_clock::time_point last_call{};
|
std::chrono::high_resolution_clock::time_point last_call{};
|
||||||
};
|
};
|
||||||
|
|
||||||
utils::concurrent_list<task> callbacks;
|
using task_list = std::vector<task>;
|
||||||
|
|
||||||
|
class task_pipeline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void add(task&& task)
|
||||||
|
{
|
||||||
|
new_callbacks_.access([&task](task_list& tasks)
|
||||||
|
{
|
||||||
|
tasks.emplace_back(std::move(task));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void execute()
|
void execute()
|
||||||
{
|
{
|
||||||
for (auto callback : callbacks)
|
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 now = std::chrono::high_resolution_clock::now();
|
||||||
const auto diff = now - callback->last_call;
|
const auto diff = now - i->last_call;
|
||||||
|
|
||||||
if (diff < callback->interval) continue;
|
if (diff < i->interval)
|
||||||
|
|
||||||
callback->last_call = now;
|
|
||||||
|
|
||||||
const auto res = callback->handler();
|
|
||||||
if (res)
|
|
||||||
{
|
{
|
||||||
callbacks.remove(callback);
|
++i;
|
||||||
}
|
continue;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void server_frame()
|
i->last_call = now;
|
||||||
|
|
||||||
|
const auto res = i->handler();
|
||||||
|
if (res == cond_end)
|
||||||
|
{
|
||||||
|
i = tasks.erase(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
utils::concurrency::container<task_list> new_callbacks_;
|
||||||
|
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
||||||
|
|
||||||
|
void merge_callbacks()
|
||||||
|
{
|
||||||
|
callbacks_.access([&](task_list& tasks)
|
||||||
|
{
|
||||||
|
new_callbacks_.access([&](task_list& new_tasks)
|
||||||
|
{
|
||||||
|
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()), std::move_iterator<task_list::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_stub()
|
||||||
{
|
{
|
||||||
execute();
|
|
||||||
reinterpret_cast<void (*)()>(0x50C1E0)();
|
reinterpret_cast<void (*)()>(0x50C1E0)();
|
||||||
|
execute(pipeline::server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void schedule(const std::function<bool()>& callback, const std::chrono::milliseconds delay)
|
void schedule(const std::function<bool()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
{
|
{
|
||||||
|
assert(type >= 0 && type < pipeline::count);
|
||||||
|
|
||||||
task task;
|
task task;
|
||||||
task.handler = callback;
|
task.handler = callback;
|
||||||
task.interval = delay;
|
task.interval = delay;
|
||||||
task.last_call = std::chrono::high_resolution_clock::now();
|
task.last_call = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
callbacks.add(task);
|
pipelines[type].add(std::move(task));
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop(const std::function<void()>& callback, const std::chrono::milliseconds delay)
|
void loop(const std::function<void()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
{
|
{
|
||||||
schedule([callback]()
|
schedule([callback]()
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
return false;
|
return cond_continue;
|
||||||
}, delay);
|
}, type, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
void once(const std::function<void()>& callback, const std::chrono::milliseconds delay)
|
void once(const std::function<void()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
{
|
{
|
||||||
schedule([callback]()
|
schedule([callback]()
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
return true;
|
return cond_end;
|
||||||
}, delay);
|
}, type, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
@ -75,7 +130,16 @@ namespace scheduler
|
|||||||
public:
|
public:
|
||||||
void post_unpack() override
|
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,20 @@
|
|||||||
|
|
||||||
namespace scheduler
|
namespace scheduler
|
||||||
{
|
{
|
||||||
void schedule(const std::function<bool()>& callback, std::chrono::milliseconds delay = 0ms);
|
enum pipeline
|
||||||
void loop(const std::function<void()>& callback, std::chrono::milliseconds delay = 0ms);
|
{
|
||||||
void once(const std::function<void()>& callback, std::chrono::milliseconds delay = 0ms);
|
server,
|
||||||
|
async,
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const bool cond_continue = false;
|
||||||
|
static const bool cond_end = true;
|
||||||
|
|
||||||
|
void schedule(const std::function<bool()>& callback, pipeline type = pipeline::server,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
|
void loop(const std::function<void()>& callback, pipeline type = pipeline::server,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
|
void once(const std::function<void()>& callback, pipeline type = pipeline::server,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,6 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
userinfo::clear_overrides();
|
userinfo::clear_overrides();
|
||||||
command::clear_script_commands();
|
command::clear_script_commands();
|
||||||
gsc::replaced_functions.clear();
|
|
||||||
g_shutdown_game_hook.invoke<void>(free_scripts);
|
g_shutdown_game_hook.invoke<void>(free_scripts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,16 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
|||||||
{
|
{
|
||||||
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
||||||
{
|
{
|
||||||
|
const auto value = *reinterpret_cast<DWORD*>(0x20800000);
|
||||||
|
if (value != 0xB9C9566)
|
||||||
|
{
|
||||||
|
printf("\x1b[31m\n**************************************************************************************\n\n");
|
||||||
|
printf("This version of \x1b[33miw5-gsc-utils\x1b[31m is outdated.\n");
|
||||||
|
printf("Download the latest dll from here:\x1b[34m https://github.com/fedddddd/iw5-gsc-utils/releases \x1b[31m\n");
|
||||||
|
printf("\n**************************************************************************************\n\n\x1b[37m");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
component_loader::post_unpack();
|
component_loader::post_unpack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,19 +40,32 @@ namespace scripting
|
|||||||
this->value_ = value;
|
this->value_ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
array::array(unsigned int id)
|
array::array(const unsigned int id)
|
||||||
: id_(id)
|
: id_(id)
|
||||||
{
|
{
|
||||||
|
this->add();
|
||||||
|
}
|
||||||
|
|
||||||
|
array::array(const array& other) : array(other.id_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
array::array(array&& other) noexcept
|
||||||
|
{
|
||||||
|
this->id_ = other.id_;
|
||||||
|
other.id_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
array::array()
|
array::array()
|
||||||
{
|
{
|
||||||
this->id_ = make_array();
|
this->id_ = make_array();
|
||||||
|
this->add();
|
||||||
}
|
}
|
||||||
|
|
||||||
array::array(std::vector<script_value> values)
|
array::array(std::vector<script_value> values)
|
||||||
{
|
{
|
||||||
this->id_ = make_array();
|
this->id_ = make_array();
|
||||||
|
this->add();
|
||||||
|
|
||||||
for (const auto& value : values)
|
for (const auto& value : values)
|
||||||
{
|
{
|
||||||
@ -63,6 +76,7 @@ namespace scripting
|
|||||||
array::array(std::unordered_map<std::string, script_value> values)
|
array::array(std::unordered_map<std::string, script_value> values)
|
||||||
{
|
{
|
||||||
this->id_ = make_array();
|
this->id_ = make_array();
|
||||||
|
this->add();
|
||||||
|
|
||||||
for (const auto& value : values)
|
for (const auto& value : values)
|
||||||
{
|
{
|
||||||
@ -70,6 +84,51 @@ namespace scripting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
array::~array()
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
array& array::operator=(const array& other)
|
||||||
|
{
|
||||||
|
if (&other != this)
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
this->id_ = other.id_;
|
||||||
|
this->add();
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
array& array::operator=(array&& other) noexcept
|
||||||
|
{
|
||||||
|
if (&other != this)
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
this->id_ = other.id_;
|
||||||
|
other.id_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void array::add() const
|
||||||
|
{
|
||||||
|
if (this->id_)
|
||||||
|
{
|
||||||
|
game::AddRefToValue(game::SCRIPT_OBJECT, {static_cast<int>(this->id_)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void array::release() const
|
||||||
|
{
|
||||||
|
if (this->id_)
|
||||||
|
{
|
||||||
|
game::RemoveRefToValue(game::SCRIPT_OBJECT, {static_cast<int>(this->id_)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<array_key> array::get_keys() const
|
std::vector<array_key> array::get_keys() const
|
||||||
{
|
{
|
||||||
std::vector<array_key> result;
|
std::vector<array_key> result;
|
||||||
@ -147,10 +206,22 @@ namespace scripting
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
script_value array::get(const array_key& key) const
|
||||||
|
{
|
||||||
|
if (key.is_integer)
|
||||||
|
{
|
||||||
|
return this->get(key.index);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return this->get(key.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
script_value array::get(const std::string& key) const
|
script_value array::get(const std::string& key) const
|
||||||
{
|
{
|
||||||
const auto string_value = game::SL_GetString(key.data(), 0);
|
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)
|
if (!variable_id)
|
||||||
{
|
{
|
||||||
@ -167,7 +238,7 @@ namespace scripting
|
|||||||
|
|
||||||
script_value array::get(const unsigned int index) const
|
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)
|
if (!variable_id)
|
||||||
{
|
{
|
||||||
@ -182,6 +253,18 @@ namespace scripting
|
|||||||
return variable;
|
return variable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void array::set(const array_key& key, const script_value& value) const
|
||||||
|
{
|
||||||
|
if (key.is_integer)
|
||||||
|
{
|
||||||
|
this->set(key.index, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->set(key.key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void array::set(const std::string& key, const script_value& _value) const
|
void array::set(const std::string& key, const script_value& _value) const
|
||||||
{
|
{
|
||||||
const auto value = _value.get_raw();
|
const auto value = _value.get_raw();
|
||||||
|
@ -7,15 +7,15 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
bool is_string = false;
|
bool is_string = false;
|
||||||
bool is_integer = false;
|
bool is_integer = false;
|
||||||
int index{};
|
unsigned int index{};
|
||||||
std::string key{};
|
std::string key{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class array_value : public script_value
|
class array_value : public script_value
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
array_value(unsigned int parent_id, unsigned int id);
|
array_value(unsigned int, unsigned int);
|
||||||
void array_value::operator=(const script_value& value);
|
void operator=(const script_value&);
|
||||||
private:
|
private:
|
||||||
unsigned int id_;
|
unsigned int id_;
|
||||||
unsigned int parent_id_;
|
unsigned int parent_id_;
|
||||||
@ -25,21 +25,32 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
array();
|
array();
|
||||||
array(unsigned int);
|
array(const unsigned int);
|
||||||
|
|
||||||
array(std::vector<script_value>);
|
array(std::vector<script_value>);
|
||||||
array(std::unordered_map<std::string, script_value>);
|
array(std::unordered_map<std::string, script_value>);
|
||||||
|
|
||||||
|
array(const array& other);
|
||||||
|
array(array&& other) noexcept;
|
||||||
|
|
||||||
|
~array();
|
||||||
|
|
||||||
|
array& operator=(const array& other);
|
||||||
|
array& operator=(array&& other) noexcept;
|
||||||
|
|
||||||
std::vector<array_key> get_keys() const;
|
std::vector<array_key> get_keys() const;
|
||||||
unsigned int size() const;
|
unsigned int size() const;
|
||||||
|
|
||||||
unsigned int push(script_value) const;
|
unsigned int push(script_value) const;
|
||||||
void erase(const unsigned int index) const;
|
void erase(const unsigned int) const;
|
||||||
void erase(const std::string& key) const;
|
void erase(const std::string&) const;
|
||||||
script_value pop() const;
|
script_value pop() const;
|
||||||
|
|
||||||
|
script_value get(const array_key&) const;
|
||||||
script_value get(const std::string&) const;
|
script_value get(const std::string&) const;
|
||||||
script_value get(const unsigned int) const;
|
script_value get(const unsigned int) const;
|
||||||
|
|
||||||
|
void set(const array_key&, const script_value&) const;
|
||||||
void set(const std::string&, const script_value&) const;
|
void set(const std::string&, const script_value&) const;
|
||||||
void set(const unsigned int, const script_value&) const;
|
void set(const unsigned int, const script_value&) const;
|
||||||
|
|
||||||
@ -50,16 +61,32 @@ namespace scripting
|
|||||||
|
|
||||||
entity get_raw() const;
|
entity get_raw() const;
|
||||||
|
|
||||||
array_value array::operator[](const int index) const
|
array_value operator[](const int index) const
|
||||||
{
|
{
|
||||||
return {this->id_, this->get_value_id(index)};
|
return {this->id_, this->get_value_id(index)};
|
||||||
}
|
}
|
||||||
|
|
||||||
array_value array::operator[](const std::string& key) const
|
array_value operator[](const std::string& key) const
|
||||||
{
|
{
|
||||||
return {this->id_, this->get_value_id(key)};
|
return {this->id_, this->get_value_id(key)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
array_value operator[](const array_key& key) const
|
||||||
|
{
|
||||||
|
if (key.is_integer)
|
||||||
|
{
|
||||||
|
return {this->id_, this->get_value_id(key.index)};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return {this->id_, this->get_value_id(key.key)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void add() const;
|
||||||
|
void release() const;
|
||||||
|
|
||||||
unsigned int id_;
|
unsigned int id_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -283,4 +283,14 @@ namespace scripting
|
|||||||
|
|
||||||
return index;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
@ -36,4 +36,5 @@ namespace scripting
|
|||||||
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments);
|
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments);
|
||||||
|
|
||||||
unsigned int make_array();
|
unsigned int make_array();
|
||||||
|
unsigned int make_object();
|
||||||
}
|
}
|
||||||
|
@ -59,15 +59,15 @@ namespace scripting
|
|||||||
|
|
||||||
script_function get_function_by_index(const unsigned index)
|
script_function get_function_by_index(const unsigned index)
|
||||||
{
|
{
|
||||||
static const auto function_table = 0x1D6EB34;
|
static const auto function_table = 0x20689DD8;
|
||||||
static const auto method_table = 0x1D4F258;
|
static const auto method_table = 0x2068A5A8;
|
||||||
|
|
||||||
if (index < 0x1C7)
|
if (index < 0x1C7)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<script_function*>(function_table)[index];
|
return reinterpret_cast<script_function*>(function_table)[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
return reinterpret_cast<script_function*>(method_table)[index];
|
return reinterpret_cast<script_function*>(method_table)[index - 0x8000];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +103,24 @@ namespace scripting
|
|||||||
this->value_ = variable;
|
this->value_ = variable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const array& value)
|
||||||
|
{
|
||||||
|
game::VariableValue variable{};
|
||||||
|
variable.type = game::SCRIPT_OBJECT;
|
||||||
|
variable.u.pointerValue = value.get_entity_id();
|
||||||
|
|
||||||
|
this->value_ = variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const function& value)
|
||||||
|
{
|
||||||
|
game::VariableValue variable{};
|
||||||
|
variable.type = game::SCRIPT_OBJECT;
|
||||||
|
variable.u.codePosValue = value.get_pos();
|
||||||
|
|
||||||
|
this->value_ = variable;
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
* Integer
|
* Integer
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
namespace scripting
|
namespace scripting
|
||||||
{
|
{
|
||||||
class entity;
|
class entity;
|
||||||
|
class array;
|
||||||
|
class function;
|
||||||
|
|
||||||
class script_value
|
class script_value
|
||||||
{
|
{
|
||||||
@ -29,6 +31,10 @@ namespace scripting
|
|||||||
|
|
||||||
script_value(const vector& value);
|
script_value(const vector& value);
|
||||||
|
|
||||||
|
script_value(const array& value);
|
||||||
|
|
||||||
|
script_value(const function& value);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool is() const;
|
bool is() const;
|
||||||
|
|
||||||
@ -43,15 +49,15 @@ namespace scripting
|
|||||||
return get<T>();
|
return get<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, typename I = int>
|
||||||
T* as_ptr()
|
T* as_ptr()
|
||||||
{
|
{
|
||||||
if (!this->is<int>())
|
if (!this->is<I>())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Invalid type");
|
throw std::runtime_error("Invalid type");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto value = this->get<int>();
|
const auto value = this->get<I>();
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
{
|
{
|
||||||
|
@ -303,8 +303,23 @@ namespace game
|
|||||||
dvar_t* hashNext;
|
dvar_t* hashNext;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct playerState_s
|
||||||
|
{
|
||||||
|
char __pad0[1260];
|
||||||
|
unsigned int perks[2];
|
||||||
|
unsigned int perkSlots[9];
|
||||||
|
char __pad1[11752];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gclient_s
|
||||||
|
{
|
||||||
|
playerState_s ps;
|
||||||
|
};
|
||||||
|
|
||||||
struct gentity_s
|
struct gentity_s
|
||||||
{
|
{
|
||||||
int entnum;
|
int entnum;
|
||||||
|
char __pad0[340];
|
||||||
|
gclient_s* client;
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -74,13 +74,12 @@ namespace game
|
|||||||
|
|
||||||
WEAK symbol<scr_classStruct_t*> g_classMap{0x8B4300};
|
WEAK symbol<scr_classStruct_t*> g_classMap{0x8B4300};
|
||||||
|
|
||||||
WEAK symbol<gentity_s> g_entities{0x0};
|
WEAK symbol<gentity_s> g_entities{0x1A66E28};
|
||||||
WEAK symbol<unsigned int> levelEntityId{0x208E1A4};
|
WEAK symbol<unsigned int> levelEntityId{0x208E1A4};
|
||||||
|
|
||||||
namespace plutonium
|
namespace plutonium
|
||||||
{
|
{
|
||||||
WEAK symbol<std::unordered_map<std::string, std::uint16_t>> function_map_rev{0x20589F68};
|
WEAK symbol<std::unordered_map<std::string, std::uint16_t>> function_map_rev{0x20691228};
|
||||||
WEAK symbol<std::unordered_map<std::string, std::uint16_t>> method_map_rev{0x20589F88};
|
WEAK symbol<std::unordered_map<std::string, std::uint16_t>> method_map_rev{0x20691248};
|
||||||
WEAK symbol<std::unordered_map<std::string, std::uint16_t>> token_map_rev{0x20589FC8};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
#pragma warning(disable: 4018)
|
#pragma warning(disable: 4018)
|
||||||
#pragma warning(disable: 4146)
|
#pragma warning(disable: 4146)
|
||||||
|
#pragma warning(disable: 4129)
|
||||||
#pragma warning(disable: 4244)
|
#pragma warning(disable: 4244)
|
||||||
#pragma warning(disable: 4267)
|
#pragma warning(disable: 4267)
|
||||||
|
#pragma warning(disable: 4996)
|
||||||
#pragma warning(disable: 26812)
|
#pragma warning(disable: 26812)
|
||||||
|
|
||||||
#define DLL_EXPORT extern "C" __declspec(dllexport)
|
#define DLL_EXPORT extern "C" __declspec(dllexport)
|
||||||
@ -25,6 +27,9 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <csetjmp>
|
#include <csetjmp>
|
||||||
|
#include <atlcomcli.h>
|
||||||
|
|
||||||
|
#pragma comment(lib, "urlmon.lib")
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
@ -36,6 +41,8 @@ using namespace std::literals;
|
|||||||
#include "utils/hook.hpp"
|
#include "utils/hook.hpp"
|
||||||
#include "utils/concurrent_list.hpp"
|
#include "utils/concurrent_list.hpp"
|
||||||
#include "utils/io.hpp"
|
#include "utils/io.hpp"
|
||||||
|
#include "utils/concurrency.hpp"
|
||||||
|
#include "utils/http.hpp"
|
||||||
|
|
||||||
#include "game/structs.hpp"
|
#include "game/structs.hpp"
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
46
src/utils/concurrency.hpp
Normal file
46
src/utils/concurrency.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace utils::concurrency
|
||||||
|
{
|
||||||
|
template <typename T, typename MutexType = std::mutex>
|
||||||
|
class container
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access(F&& accessor) const
|
||||||
|
{
|
||||||
|
std::lock_guard<MutexType> _{mutex_};
|
||||||
|
return accessor(object_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access(F&& accessor)
|
||||||
|
{
|
||||||
|
std::lock_guard<MutexType> _{mutex_};
|
||||||
|
return accessor(object_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access_with_lock(F&& accessor) const
|
||||||
|
{
|
||||||
|
std::unique_lock<MutexType> lock{mutex_};
|
||||||
|
return accessor(object_, lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access_with_lock(F&& accessor)
|
||||||
|
{
|
||||||
|
std::unique_lock<MutexType> lock{mutex_};
|
||||||
|
return accessor(object_, lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
T& get_raw() { return object_; }
|
||||||
|
const T& get_raw() const { return object_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable MutexType mutex_{};
|
||||||
|
T object_{};
|
||||||
|
};
|
||||||
|
}
|
47
src/utils/http.cpp
Normal file
47
src/utils/http.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "http.hpp"
|
||||||
|
|
||||||
|
namespace utils::http
|
||||||
|
{
|
||||||
|
std::optional<std::string> get_data(const std::string& url)
|
||||||
|
{
|
||||||
|
CComPtr<IStream> 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<std::optional<std::string>> get_data_async(const std::string& url)
|
||||||
|
{
|
||||||
|
return std::async(std::launch::async, [url]()
|
||||||
|
{
|
||||||
|
return get_data(url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
11
src/utils/http.hpp
Normal file
11
src/utils/http.hpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
namespace utils::http
|
||||||
|
{
|
||||||
|
std::optional<std::string> get_data(const std::string& url);
|
||||||
|
std::future<std::optional<std::string>> get_data_async(const std::string& url);
|
||||||
|
}
|
Reference in New Issue
Block a user