mirror of
https://github.com/fedddddd/iw5-gsc-utils.git
synced 2026-08-01 12:50:33 +00:00
changes + cleanup
This commit is contained in:
+103
-126
@@ -39,181 +39,152 @@ namespace gsc
|
|||||||
scripting::push_value(value);
|
scripting::push_value(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto field_offset_start = 0xA000;
|
auto field_offset_start = 0xA000u;
|
||||||
|
|
||||||
struct entity_field
|
std::unordered_map<unsigned int, entity_field_t> custom_fields[class_id_t::class_count];
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
std::function<scripting::script_value(unsigned int entnum)> getter;
|
|
||||||
std::function<void(unsigned int entnum, scripting::script_value)> setter;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::function<void()>> post_load_callbacks;
|
|
||||||
std::unordered_map<unsigned int, std::unordered_map<unsigned int, entity_field>> custom_fields;
|
|
||||||
|
|
||||||
void call_function(const script_function* function, const char* name)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const auto result = function->operator()(get_arguments());
|
|
||||||
const auto type = result.get_raw().type;
|
|
||||||
|
|
||||||
if (type)
|
|
||||||
{
|
|
||||||
return_value(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
printf("************** Script execution error **************");
|
|
||||||
printf("Error executing function %s:", name);
|
|
||||||
printf(" %s", e.what());
|
|
||||||
printf("****************************************************");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void call_method(const script_method* method, const char* name, game::scr_entref_t ent)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const auto result = method->operator()(ent, get_arguments());
|
|
||||||
const auto type = result.get_raw().type;
|
|
||||||
|
|
||||||
if (type)
|
|
||||||
{
|
|
||||||
return_value(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
printf("************** Script execution error **************");
|
|
||||||
printf("Error executing method %s:", name);
|
|
||||||
printf(" %s", e.what());
|
|
||||||
printf("****************************************************");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
utils::hook::detour scr_get_object_field_hook;
|
utils::hook::detour scr_get_object_field_hook;
|
||||||
|
utils::hook::detour scr_set_object_field_hook;
|
||||||
|
utils::hook::detour scr_post_load_scripts_hook;
|
||||||
|
|
||||||
|
const char* get_script_loc()
|
||||||
|
{
|
||||||
|
return "unknown location";
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_error(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
static char buffer[0x1000];
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
_vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
printf("\n"
|
||||||
|
"******* script runtime error *******\n"
|
||||||
|
"%s\n"
|
||||||
|
"\tat %s\n"
|
||||||
|
"************************************",
|
||||||
|
buffer, get_script_loc());
|
||||||
|
}
|
||||||
|
|
||||||
|
const gsc::entity_field_t* find_field(unsigned int classnum, unsigned int offset)
|
||||||
|
{
|
||||||
|
const auto& class_map = custom_fields[classnum];
|
||||||
|
const auto field_iter = class_map.find(offset);
|
||||||
|
if (field_iter == class_map.end())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &field_iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
void scr_get_object_field_stub(unsigned int classnum, int entnum, unsigned int offset)
|
void scr_get_object_field_stub(unsigned int classnum, int entnum, unsigned int offset)
|
||||||
{
|
{
|
||||||
if (custom_fields[classnum].find(offset) == custom_fields[classnum].end())
|
const auto field = find_field(classnum, offset);
|
||||||
|
if (field == nullptr)
|
||||||
{
|
{
|
||||||
return scr_get_object_field_hook.invoke<void>(classnum, entnum, offset);
|
return scr_get_object_field_hook.invoke<void>(classnum, entnum, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& field = custom_fields[classnum][offset];
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto result = field.getter(entnum);
|
const auto result = field->getter(entnum);
|
||||||
return_value(result);
|
return_value(result);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
printf("************** Script execution error **************");
|
print_error("while getting builtin field \"%s\"\n%s", field->name.data(), e.what());
|
||||||
printf("Error getting field %s:", field.name.data());
|
|
||||||
printf(" %s", e.what());
|
|
||||||
printf("****************************************************");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::hook::detour scr_set_object_field_hook;
|
|
||||||
void scr_set_object_field_stub(unsigned int classnum, int entnum, unsigned int offset)
|
void scr_set_object_field_stub(unsigned int classnum, int entnum, unsigned int offset)
|
||||||
{
|
{
|
||||||
if (custom_fields[classnum].find(offset) == custom_fields[classnum].end())
|
const auto field = find_field(classnum, offset);
|
||||||
|
if (field == nullptr)
|
||||||
{
|
{
|
||||||
return scr_set_object_field_hook.invoke<void>(classnum, entnum, offset);
|
return scr_set_object_field_hook.invoke<void>(classnum, entnum, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto args = get_arguments();
|
const auto args = get_arguments();
|
||||||
const auto& field = custom_fields[classnum][offset];
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
field.setter(entnum, args[0]);
|
field->setter(entnum, args[0]);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
printf("************** Script execution error **************");
|
print_error("while setting builtin field \"%s\"\n%s", field->name.data(), e.what());
|
||||||
printf("Error setting field %s:", field.name.data());
|
|
||||||
printf(" %s", e.what());
|
|
||||||
printf("****************************************************");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::hook::detour scr_post_load_scripts_hook;
|
|
||||||
void scr_post_load_scripts_stub()
|
void scr_post_load_scripts_stub()
|
||||||
{
|
{
|
||||||
for (const auto& callback : post_load_callbacks)
|
for (auto i = 0; i < class_id_t::class_count; i++)
|
||||||
{
|
{
|
||||||
callback();
|
const auto& class_map = custom_fields[i];
|
||||||
|
for (const auto& [offset, field] : class_map)
|
||||||
|
{
|
||||||
|
const auto str_id = game::SL_GetString(field.name.data(), 0);
|
||||||
|
const auto canon_str = game::SL_GetCanonicalString(field.name.data());
|
||||||
|
game::Scr_AddClassField(i, str_id, canon_str, offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return scr_post_load_scripts_hook.invoke<void>();
|
return scr_post_load_scripts_hook.invoke<void>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* make_function_thunk(const char* name, const script_function* func)
|
void call_function(const script_function& function, const std::string& name)
|
||||||
{
|
{
|
||||||
static std::uint8_t bytes[] =
|
try
|
||||||
{
|
{
|
||||||
0x68, 0x44, 0x33, 0x22, 0x11, // push
|
const auto result = function.operator()(get_arguments());
|
||||||
0x68, 0x44, 0x33, 0x22, 0x11, // push
|
const auto type = result.get_raw().type;
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, // call
|
|
||||||
0x83, 0xC4, 0x08, // add esp, 8
|
|
||||||
0xC3 // ret
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto stub = utils::memory::allocate_array<std::uint8_t>(sizeof(bytes));
|
if (type)
|
||||||
std::memcpy(stub, bytes, sizeof(bytes));
|
{
|
||||||
|
return_value(result);
|
||||||
utils::hook::unprotect(stub, sizeof(bytes));
|
}
|
||||||
|
}
|
||||||
*reinterpret_cast<std::size_t*>(stub + 1) = reinterpret_cast<size_t>(name);
|
catch (const std::exception& e)
|
||||||
*reinterpret_cast<std::size_t*>(stub + 6) = reinterpret_cast<size_t>(func);
|
{
|
||||||
utils::hook::call(stub + 10, call_function);
|
print_error("in call to builtin function \"%s\"\n%s", name.data(), e.what());
|
||||||
|
}
|
||||||
return stub;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void* make_method_thunk(const char* name, const script_method* func)
|
void call_method(const script_method& method, const std::string& name, game::scr_entref_t ent)
|
||||||
{
|
{
|
||||||
static std::uint8_t bytes[] =
|
try
|
||||||
{
|
{
|
||||||
0xFF, 0x74, 0x24, 0x04, // push [esp+4]
|
const auto result = method.operator()(ent, get_arguments());
|
||||||
0x68, 0x44, 0x33, 0x22, 0x11, // push
|
const auto type = result.get_raw().type;
|
||||||
0x68, 0x44, 0x33, 0x22, 0x11, // push
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, // call
|
|
||||||
0x83, 0xC4, 0x0C, // add esp, 8
|
|
||||||
0xC3 // ret
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto stub = utils::memory::allocate_array<std::uint8_t>(sizeof(bytes));
|
if (type)
|
||||||
std::memcpy(stub, bytes, sizeof(bytes));
|
{
|
||||||
|
return_value(result);
|
||||||
utils::hook::unprotect(stub, sizeof(bytes));
|
}
|
||||||
|
}
|
||||||
*reinterpret_cast<std::size_t*>(stub + 5) = reinterpret_cast<size_t>(name);
|
catch (const std::exception& e)
|
||||||
*reinterpret_cast<std::size_t*>(stub + 10) = reinterpret_cast<size_t>(func);
|
{
|
||||||
utils::hook::call(stub + 14, call_method);
|
print_error("in call to builtin method \"%s\"\n%s", name.data(), e.what());
|
||||||
|
}
|
||||||
return stub;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace field
|
namespace field
|
||||||
{
|
{
|
||||||
void add(const classid classnum, const std::string& name,
|
void add(const class_id_t classnum, const std::string& name, const field_getter_t getter, const field_setter_t& setter)
|
||||||
const std::function<scripting::script_value(unsigned int entnum)>& getter,
|
|
||||||
const std::function<void(unsigned int entnum, const scripting::script_value&)>& setter)
|
|
||||||
{
|
{
|
||||||
const auto offset = field_offset_start++;
|
const auto offset = field_offset_start++;
|
||||||
custom_fields[classnum][offset] = {name, getter, setter};
|
auto& class_map = custom_fields[classnum];
|
||||||
|
|
||||||
post_load_callbacks.push_back([=]()
|
entity_field_t field{};
|
||||||
{
|
field.name = name;
|
||||||
const auto name_str = game::SL_GetString(name.data(), 0);
|
field.getter = getter;
|
||||||
game::Scr_AddClassField(classnum, name_str, game::SL_GetCanonicalString(name.data()), offset);
|
field.setter = setter;
|
||||||
});
|
class_map.insert(std::make_pair(offset, field));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +222,7 @@ namespace gsc
|
|||||||
scr_set_object_field_hook.create(0x52BCC0, scr_set_object_field_stub);
|
scr_set_object_field_hook.create(0x52BCC0, scr_set_object_field_stub);
|
||||||
scr_post_load_scripts_hook.create(0x628B50, scr_post_load_scripts_stub);
|
scr_post_load_scripts_hook.create(0x628B50, scr_post_load_scripts_stub);
|
||||||
|
|
||||||
field::add(classid::entity, "entityflags",
|
field::add(class_id_t::class_entity, "entityflags",
|
||||||
[](unsigned int entnum) -> scripting::script_value
|
[](unsigned int entnum) -> scripting::script_value
|
||||||
{
|
{
|
||||||
const auto entity = &game::g_entities[entnum];
|
const auto entity = &game::g_entities[entnum];
|
||||||
@@ -264,7 +235,7 @@ namespace gsc
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
field::add(classid::entity, "clientflags",
|
field::add(class_id_t::class_entity, "clientflags",
|
||||||
[](unsigned int entnum) -> scripting::script_value
|
[](unsigned int entnum) -> scripting::script_value
|
||||||
{
|
{
|
||||||
const auto entity = &game::g_entities[entnum];
|
const auto entity = &game::g_entities[entnum];
|
||||||
@@ -277,13 +248,15 @@ namespace gsc
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
function::add("executecommand", [](const function_args& args) -> scripting::script_value
|
function::add("executecommand", [](const function_args& args)
|
||||||
|
-> scripting::script_value
|
||||||
{
|
{
|
||||||
game::Cbuf_AddText(0, args[0].as<const char*>());
|
game::Cbuf_AddText(0, args[0].as<const char*>());
|
||||||
return {};
|
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].as<scripting::function>();
|
const auto function = args[1].as<scripting::function>();
|
||||||
@@ -301,7 +274,8 @@ namespace gsc
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
function::add("dropallbots", [](const function_args&) -> scripting::script_value
|
function::add("dropallbots", [](const function_args&)
|
||||||
|
-> scripting::script_value
|
||||||
{
|
{
|
||||||
for (auto i = 0; i < *game::svs_clientCount; i++)
|
for (auto i = 0; i < *game::svs_clientCount; i++)
|
||||||
{
|
{
|
||||||
@@ -315,7 +289,8 @@ namespace gsc
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
method::add("specialtymarathon", [](const game::scr_entref_t ent, const function_args& args) -> scripting::script_value
|
method::add("specialtymarathon", [](const game::scr_entref_t ent, const function_args& args)
|
||||||
|
-> scripting::script_value
|
||||||
{
|
{
|
||||||
if (ent.classnum != 0)
|
if (ent.classnum != 0)
|
||||||
{
|
{
|
||||||
@@ -339,7 +314,8 @@ namespace gsc
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
method::add("isbot", [](const game::scr_entref_t ent, const function_args&) -> scripting::script_value
|
method::add("isbot", [](const game::scr_entref_t ent, const function_args&)
|
||||||
|
-> scripting::script_value
|
||||||
{
|
{
|
||||||
if (ent.classnum != 0)
|
if (ent.classnum != 0)
|
||||||
{
|
{
|
||||||
@@ -356,7 +332,8 @@ namespace gsc
|
|||||||
return game::svs_clients[client].bIsTestClient;
|
return game::svs_clients[client].bIsTestClient;
|
||||||
});
|
});
|
||||||
|
|
||||||
method::add("arecontrolsfrozen", [](const game::scr_entref_t ent, const function_args&) -> scripting::script_value
|
method::add("arecontrolsfrozen", [](const game::scr_entref_t ent, const function_args&)
|
||||||
|
-> scripting::script_value
|
||||||
{
|
{
|
||||||
if (ent.classnum != 0)
|
if (ent.classnum != 0)
|
||||||
{
|
{
|
||||||
@@ -370,7 +347,7 @@ namespace gsc
|
|||||||
throw std::runtime_error("not a player entity");
|
throw std::runtime_error("not a player entity");
|
||||||
}
|
}
|
||||||
|
|
||||||
return {(game::g_entities[client].client->flags & 4) != 0};
|
return (game::g_entities[client].client->flags & 4) != 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+49
-23
@@ -4,13 +4,23 @@
|
|||||||
|
|
||||||
namespace gsc
|
namespace gsc
|
||||||
{
|
{
|
||||||
enum classid
|
enum class_id_t
|
||||||
{
|
{
|
||||||
entity,
|
class_entity,
|
||||||
hudelem,
|
class_hudelem,
|
||||||
pathnode,
|
class_pathnode,
|
||||||
node,
|
class_node,
|
||||||
count
|
class_count
|
||||||
|
};
|
||||||
|
|
||||||
|
using field_getter_t = std::function<scripting::script_value(unsigned int entnum)>;
|
||||||
|
using field_setter_t = std::function<void(unsigned int entnum, const scripting::script_value&)>;
|
||||||
|
|
||||||
|
struct entity_field_t
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
field_getter_t getter;
|
||||||
|
field_setter_t setter;
|
||||||
};
|
};
|
||||||
|
|
||||||
class function_args
|
class function_args
|
||||||
@@ -36,39 +46,55 @@ namespace gsc
|
|||||||
using script_function = std::function<scripting::script_value(const function_args&)>;
|
using script_function = std::function<scripting::script_value(const function_args&)>;
|
||||||
using script_method = std::function<scripting::script_value(const game::scr_entref_t, const function_args&)>;
|
using script_method = std::function<scripting::script_value(const game::scr_entref_t, const function_args&)>;
|
||||||
|
|
||||||
void* make_function_thunk(const char* name, const script_function* func);
|
void call_function(const script_function& function, const std::string& name);
|
||||||
void* make_method_thunk(const char* name, const script_method* meth);
|
void call_method(const script_method& method, const std::string& name, game::scr_entref_t ent);
|
||||||
|
|
||||||
namespace function
|
namespace function
|
||||||
{
|
{
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void add(const std::string& name, F&& f)
|
void add(const std::string& n, F&& f)
|
||||||
{
|
{
|
||||||
const auto name_str = utils::memory::duplicate_string(name);
|
static auto done = false;
|
||||||
const auto func = new script_function(std::forward<F>(f));
|
assert(!done);
|
||||||
const auto thunk = reinterpret_cast<
|
done = true;
|
||||||
plutonium::sdk::interfaces::gsc::function_callback>(make_function_thunk(name_str, func));
|
|
||||||
plugin::get()->get_interface()->gsc()->register_function(name, thunk);
|
static struct
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
script_function function;
|
||||||
|
} ctx{n, f};
|
||||||
|
|
||||||
|
plugin::get()->get_interface()->gsc()->register_function(n, []()
|
||||||
|
{
|
||||||
|
call_function(ctx.function, ctx.name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace method
|
namespace method
|
||||||
{
|
{
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void add(const std::string& name, F&& f)
|
void add(const std::string& n, F&& f)
|
||||||
{
|
{
|
||||||
const auto name_str = utils::memory::duplicate_string(name);
|
static auto done = false;
|
||||||
const auto func = new script_method(std::forward<F>(f));
|
assert(!done);
|
||||||
const auto thunk = reinterpret_cast<
|
done = true;
|
||||||
plutonium::sdk::interfaces::gsc::method_callback>(make_method_thunk(name_str, func));
|
|
||||||
plugin::get()->get_interface()->gsc()->register_method(name, thunk);
|
static struct
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
script_method function;
|
||||||
|
} ctx{n, f};
|
||||||
|
|
||||||
|
plugin::get()->get_interface()->gsc()->register_method(n, [](plutonium::sdk::types::entref entref)
|
||||||
|
{
|
||||||
|
call_method(ctx.function, ctx.name, *reinterpret_cast<game::scr_entref_t*>(&entref));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace field
|
namespace field
|
||||||
{
|
{
|
||||||
void add(const classid classnum, const std::string& name,
|
void add(const class_id_t classnum, const std::string& name, const field_getter_t getter, const field_setter_t& setter);
|
||||||
const std::function<scripting::script_value(unsigned int entnum)>& getter,
|
|
||||||
const std::function<void(unsigned int entnum, const scripting::script_value&)>& setter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,6 +48,7 @@ namespace game
|
|||||||
WEAK symbol<void()> Scr_MakeArray{0x56ADE0};
|
WEAK symbol<void()> Scr_MakeArray{0x56ADE0};
|
||||||
WEAK symbol<void(unsigned int stringValue)> Scr_AddArrayStringIndexed{0x56AE70};
|
WEAK symbol<void(unsigned int stringValue)> Scr_AddArrayStringIndexed{0x56AE70};
|
||||||
WEAK symbol<void(unsigned int classnum, unsigned int name, unsigned int canonicalString, unsigned int offset)> Scr_AddClassField{0x567CD0};
|
WEAK symbol<void(unsigned int classnum, unsigned int name, unsigned int canonicalString, unsigned int offset)> Scr_AddClassField{0x567CD0};
|
||||||
|
WEAK symbol<void()> Scr_ErrorInternal{0x568FD0};
|
||||||
|
|
||||||
WEAK symbol<unsigned int(const char* str, unsigned int user)> SL_GetString{0x5649E0};
|
WEAK symbol<unsigned int(const char* str, unsigned int user)> SL_GetString{0x5649E0};
|
||||||
WEAK symbol<unsigned int(const char* str)> SL_GetCanonicalString{0x5619A0};
|
WEAK symbol<unsigned int(const char* str)> SL_GetCanonicalString{0x5619A0};
|
||||||
|
|||||||
Reference in New Issue
Block a user