Merge branch 'develop' of https://github.com/h1-mod/h1-mod into develop
This commit is contained in:
commit
c2a0b667bd
2
deps/GSL
vendored
2
deps/GSL
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 849f7ceaf7876286aa663b4f0157b4542090fe90
|
Subproject commit 991fa6682e819590c695f00c6b880548e55fa914
|
2
deps/curl
vendored
2
deps/curl
vendored
@ -1 +1 @@
|
|||||||
Subproject commit b82eb72d8097c93313329a4aecd41317a293f506
|
Subproject commit 68fa9bf3f5d7b4fcbb57619f70cb4aabb79a51f6
|
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
@ -1 +1 @@
|
|||||||
Subproject commit e10c62ec18a8b7689adcc3076e40c5a9ef1de3b6
|
Subproject commit e8e678e1018615536f5b9cd07654d36449a28ac2
|
2
deps/protobuf
vendored
2
deps/protobuf
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 7c896ce8194619f2f9625bd115086f97b1cf7129
|
Subproject commit 57786d126249b5ed4f42b579047941805e742949
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 19f8551627d2a89b910d961fc9c7f626f3af7b21
|
Subproject commit 04f42ceca40f73e2978b50e93806c2a18c1281fc
|
@ -609,10 +609,12 @@ namespace command
|
|||||||
|
|
||||||
add_commands_generic();
|
add_commands_generic();
|
||||||
|
|
||||||
gsc::function::add("executecommand", []()
|
gsc::function::add("executecommand", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
const auto cmd = gsc::get_argument(0).as<std::string>();
|
const auto cmd = args[0].as<std::string>();
|
||||||
command::execute(cmd, true);
|
command::execute(cmd, true);
|
||||||
|
|
||||||
|
return scripting::script_value{};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,9 +225,9 @@ namespace dedicated
|
|||||||
}), true);
|
}), true);
|
||||||
|
|
||||||
// return 0 so the game doesn't override the cfg
|
// return 0 so the game doesn't override the cfg
|
||||||
gsc::function::add("isusingmatchrulesdata", []()
|
gsc::function::add("isusingmatchrulesdata", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
game::Scr_AddInt(0);
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// delay console commands until the initialization is done // COULDN'T FOUND
|
// delay console commands until the initialization is done // COULDN'T FOUND
|
||||||
|
@ -26,11 +26,13 @@
|
|||||||
|
|
||||||
namespace gsc
|
namespace gsc
|
||||||
{
|
{
|
||||||
void* func_table[0x1000]{};
|
builtin_function func_table[0x1000]{};
|
||||||
void* meth_table[0x1000]{};
|
builtin_method meth_table[0x1000]{};
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
utils::hook::detour scr_emit_function_hook;
|
||||||
|
|
||||||
game::dvar_t* developer_script = nullptr;
|
game::dvar_t* developer_script = nullptr;
|
||||||
|
|
||||||
auto compiler = ::gsc::compiler();
|
auto compiler = ::gsc::compiler();
|
||||||
@ -42,8 +44,8 @@ namespace gsc
|
|||||||
std::unordered_map<std::string, unsigned int> init_handles;
|
std::unordered_map<std::string, unsigned int> init_handles;
|
||||||
std::unordered_map<std::string, game::ScriptFile*> loaded_scripts;
|
std::unordered_map<std::string, game::ScriptFile*> loaded_scripts;
|
||||||
|
|
||||||
std::unordered_map<builtin_function, unsigned int> functions;
|
std::unordered_map<unsigned int, script_function> functions;
|
||||||
std::unordered_map<builtin_method, unsigned int> methods;
|
std::unordered_map<unsigned int, script_method> methods;
|
||||||
|
|
||||||
std::optional<std::string> gsc_error;
|
std::optional<std::string> gsc_error;
|
||||||
|
|
||||||
@ -52,6 +54,8 @@ namespace gsc
|
|||||||
auto function_id_start = 0x30A;
|
auto function_id_start = 0x30A;
|
||||||
auto method_id_start = 0x8586;
|
auto method_id_start = 0x8586;
|
||||||
|
|
||||||
|
game::scr_entref_t saved_ent_ref;
|
||||||
|
|
||||||
std::string unknown_function_error{};
|
std::string unknown_function_error{};
|
||||||
unsigned int current_filename{};
|
unsigned int current_filename{};
|
||||||
|
|
||||||
@ -349,11 +353,16 @@ namespace gsc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void builtin_call_error(const std::string& error_str)
|
std::uint16_t get_function_id()
|
||||||
{
|
{
|
||||||
const auto pos = game::scr_function_stack->pos;
|
const auto pos = game::scr_function_stack->pos;
|
||||||
const auto function_id = *reinterpret_cast<std::uint16_t*>(
|
return *reinterpret_cast<std::uint16_t*>(
|
||||||
reinterpret_cast<size_t>(pos - 2));
|
reinterpret_cast<size_t>(pos - 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void builtin_call_error(const std::string& error_str)
|
||||||
|
{
|
||||||
|
const auto function_id = get_function_id();
|
||||||
|
|
||||||
if (function_id > 0x1000)
|
if (function_id > 0x1000)
|
||||||
{
|
{
|
||||||
@ -367,11 +376,12 @@ namespace gsc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* vm_error_stub(void* a1)
|
void vm_error_stub(void* a1)
|
||||||
{
|
{
|
||||||
if (!developer_script->current.enabled && !force_error_print)
|
if (!developer_script->current.enabled && !force_error_print)
|
||||||
{
|
{
|
||||||
return utils::hook::invoke<void*>(SELECT_VALUE(0x415C90_b, 0x59DDA0_b), a1);
|
utils::hook::invoke<void>(SELECT_VALUE(0x415C90_b, 0x59DDA0_b), a1);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console::warn("*********** script runtime error *************\n");
|
console::warn("*********** script runtime error *************\n");
|
||||||
@ -405,7 +415,7 @@ namespace gsc
|
|||||||
|
|
||||||
print_callstack();
|
print_callstack();
|
||||||
console::warn("**********************************************\n");
|
console::warn("**********************************************\n");
|
||||||
return utils::hook::invoke<void*>(SELECT_VALUE(0x415C90_b, 0x59DDA0_b), a1);
|
utils::hook::invoke<void*>(SELECT_VALUE(0x415C90_b, 0x59DDA0_b), a1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_unknown_function_error(const char* code_pos)
|
void get_unknown_function_error(const char* code_pos)
|
||||||
@ -471,31 +481,49 @@ namespace gsc
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_gsc_functions_stub(void* a1, void* a2)
|
void scr_emit_function_stub(unsigned int filename, unsigned int thread_name, char* code_pos)
|
||||||
{
|
{
|
||||||
utils::hook::invoke<void>(SELECT_VALUE(0x2E0F50_b, 0x1CE010_b), a1, a2);
|
current_filename = filename;
|
||||||
for (const auto& function : functions)
|
scr_emit_function_hook.invoke<void>(filename, thread_name, code_pos);
|
||||||
{
|
|
||||||
game::Scr_RegisterFunction(function.first, 0, function.second);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_gsc_methods_stub(void* a1, void* a2)
|
function_args get_arguments()
|
||||||
{
|
{
|
||||||
utils::hook::invoke<void>(SELECT_VALUE(0x2E0FB0_b, 0x1CE120_b), a1, a2);
|
std::vector<scripting::script_value> args;
|
||||||
for (const auto& method : methods)
|
|
||||||
|
for (auto i = 0; static_cast<unsigned int>(i) < game::scr_VmPub->outparamcount; ++i)
|
||||||
{
|
{
|
||||||
game::Scr_RegisterFunction(method.first, 0, method.second);
|
const auto value = game::scr_VmPub->top[-i];
|
||||||
|
args.push_back(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute_custom_function(builtin_function function)
|
void return_value(const scripting::script_value& value)
|
||||||
|
{
|
||||||
|
if (game::scr_VmPub->outparamcount)
|
||||||
|
{
|
||||||
|
game::Scr_ClearOutParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
scripting::push_value(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_custom_function(const uint16_t id)
|
||||||
{
|
{
|
||||||
auto error = false;
|
auto error = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
function();
|
const auto& function = functions[id];
|
||||||
|
const auto result = function(get_arguments());
|
||||||
|
const auto type = result.get_raw().type;
|
||||||
|
|
||||||
|
if (type)
|
||||||
|
{
|
||||||
|
return_value(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
@ -510,13 +538,20 @@ namespace gsc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute_custom_method(scripting::script_function method, game::scr_entref_t ent_ref)
|
void call_custom_method(const uint16_t id)
|
||||||
{
|
{
|
||||||
auto error = false;
|
auto error = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
method(ent_ref);
|
const auto& method = methods[id];
|
||||||
|
const auto result = method(saved_ent_ref, get_arguments());
|
||||||
|
const auto type = result.get_raw().type;
|
||||||
|
|
||||||
|
if (type)
|
||||||
|
{
|
||||||
|
return_value(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
@ -533,10 +568,8 @@ namespace gsc
|
|||||||
|
|
||||||
void vm_call_builtin_function_stub(builtin_function function)
|
void vm_call_builtin_function_stub(builtin_function function)
|
||||||
{
|
{
|
||||||
auto is_custom_function = false;
|
const auto function_id = get_function_id();
|
||||||
{
|
const auto is_custom_function = functions.find(function_id) != functions.end();
|
||||||
is_custom_function = functions.find(function) != functions.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_custom_function)
|
if (!is_custom_function)
|
||||||
{
|
{
|
||||||
@ -544,15 +577,30 @@ namespace gsc
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
execute_custom_function(function);
|
call_custom_function(function_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::hook::detour scr_emit_function_hook;
|
game::scr_entref_t get_entity_id_stub(unsigned int ent_id)
|
||||||
void scr_emit_function_stub(unsigned int filename, unsigned int thread_name, char* code_pos)
|
|
||||||
{
|
{
|
||||||
current_filename = filename;
|
const auto ref = game::Scr_GetEntityIdRef(ent_id);
|
||||||
scr_emit_function_hook.invoke<void>(filename, thread_name, code_pos);
|
saved_ent_ref = ref;
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm_call_builtin_method_stub(builtin_method method)
|
||||||
|
{
|
||||||
|
const auto function_id = get_function_id();
|
||||||
|
const auto is_custom_function = methods.find(function_id) != methods.end();
|
||||||
|
|
||||||
|
if (!is_custom_function)
|
||||||
|
{
|
||||||
|
method(saved_ent_ref);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
call_custom_method(function_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -597,52 +645,67 @@ namespace gsc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scripting::script_value get_argument(int index)
|
|
||||||
{
|
|
||||||
if (index >= static_cast<int>(game::scr_VmPub->outparamcount))
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return game::scr_VmPub->top[-index];
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace function
|
namespace function
|
||||||
{
|
{
|
||||||
void add(const std::string& name, builtin_function function)
|
void add(const std::string& name, script_function function)
|
||||||
{
|
{
|
||||||
if (xsk::gsc::h1::resolver::find_function(name))
|
if (xsk::gsc::h1::resolver::find_function(name))
|
||||||
{
|
{
|
||||||
const auto id = xsk::gsc::h1::resolver::function_id(name);
|
const auto id = xsk::gsc::h1::resolver::function_id(name);
|
||||||
functions[function] = id;
|
functions[id] = function;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto id = ++function_id_start;
|
const auto id = ++function_id_start;
|
||||||
xsk::gsc::h1::resolver::add_function(name, static_cast<std::uint16_t>(id));
|
xsk::gsc::h1::resolver::add_function(name, static_cast<std::uint16_t>(id));
|
||||||
functions[function] = id;
|
functions[id] = function;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace method
|
namespace method
|
||||||
{
|
{
|
||||||
void add(const std::string& name, builtin_method method)
|
void add(const std::string& name, script_method method)
|
||||||
{
|
{
|
||||||
if (xsk::gsc::h1::resolver::find_method(name))
|
if (xsk::gsc::h1::resolver::find_method(name))
|
||||||
{
|
{
|
||||||
const auto id = xsk::gsc::h1::resolver::method_id(name);
|
const auto id = xsk::gsc::h1::resolver::method_id(name);
|
||||||
methods[method] = id;
|
methods[id] = method;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto id = ++method_id_start;
|
const auto id = ++method_id_start;
|
||||||
xsk::gsc::h1::resolver::add_method(name, static_cast<std::uint16_t>(id));
|
xsk::gsc::h1::resolver::add_method(name, static_cast<std::uint16_t>(id));
|
||||||
methods[method] = id;
|
methods[id] = method;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function_args::function_args(std::vector<scripting::script_value> values)
|
||||||
|
: values_(values)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int function_args::size() const
|
||||||
|
{
|
||||||
|
return static_cast<unsigned int>(this->values_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<scripting::script_value> function_args::get_raw() const
|
||||||
|
{
|
||||||
|
return this->values_;
|
||||||
|
}
|
||||||
|
|
||||||
|
scripting::value_wrap function_args::get(const int index) const
|
||||||
|
{
|
||||||
|
if (index >= this->values_.size())
|
||||||
|
{
|
||||||
|
throw std::runtime_error(utils::string::va("parameter %d does not exist", index));
|
||||||
|
}
|
||||||
|
|
||||||
|
return { this->values_[index], index };
|
||||||
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -693,9 +756,6 @@ namespace gsc
|
|||||||
utils::hook::call(SELECT_VALUE(0x3BD75A_b, 0x50473A_b), find_variable_stub);
|
utils::hook::call(SELECT_VALUE(0x3BD75A_b, 0x50473A_b), find_variable_stub);
|
||||||
scr_emit_function_hook.create(SELECT_VALUE(0x3BD680_b, 0x504660_b), scr_emit_function_stub);
|
scr_emit_function_hook.create(SELECT_VALUE(0x3BD680_b, 0x504660_b), scr_emit_function_stub);
|
||||||
|
|
||||||
utils::hook::call(SELECT_VALUE(0x3BDC4F_b, 0x504C7F_b), register_gsc_functions_stub);
|
|
||||||
utils::hook::call(SELECT_VALUE(0x3BDC5B_b, 0x504C8B_b), register_gsc_methods_stub);
|
|
||||||
|
|
||||||
utils::hook::set<uint32_t>(SELECT_VALUE(0x3BD86C_b, 0x50484C_b), 0x1000); // change builtin func count
|
utils::hook::set<uint32_t>(SELECT_VALUE(0x3BD86C_b, 0x50484C_b), 0x1000); // change builtin func count
|
||||||
|
|
||||||
utils::hook::set<uint32_t>(SELECT_VALUE(0x3BD872_b, 0x504852_b) + 4,
|
utils::hook::set<uint32_t>(SELECT_VALUE(0x3BD872_b, 0x504852_b) + 4,
|
||||||
@ -712,47 +772,62 @@ namespace gsc
|
|||||||
utils::hook::inject(SELECT_VALUE(0x3BDC36_b, 0x504C66_b) + 3, &meth_table);
|
utils::hook::inject(SELECT_VALUE(0x3BDC36_b, 0x504C66_b) + 3, &meth_table);
|
||||||
utils::hook::set<uint32_t>(SELECT_VALUE(0x3BDC3F_b, 0x504C6F_b), sizeof(meth_table));
|
utils::hook::set<uint32_t>(SELECT_VALUE(0x3BDC3F_b, 0x504C6F_b), sizeof(meth_table));
|
||||||
|
|
||||||
|
/*
|
||||||
|
trying to do a jump hook to push the ent reference (if it exists) and the builtin function/methods works, but
|
||||||
|
if longjmp is called because of a runtime error in our custom functions/methods, then the game just kinda dies
|
||||||
|
or gets incredibly slow but will eventually load. for functions, the workaround is easy but for methods, we still
|
||||||
|
have to remember the entity that called the method so the workaround is just hooking the Scr_GetEntityIdRef call
|
||||||
|
*/
|
||||||
utils::hook::nop(SELECT_VALUE(0x3CB723_b, 0x512783_b), 8);
|
utils::hook::nop(SELECT_VALUE(0x3CB723_b, 0x512783_b), 8);
|
||||||
utils::hook::call(SELECT_VALUE(0x3CB723_b, 0x512783_b), vm_call_builtin_function_stub);
|
utils::hook::call(SELECT_VALUE(0x3CB723_b, 0x512783_b), vm_call_builtin_function_stub);
|
||||||
|
|
||||||
function::add("print", []()
|
utils::hook::call(SELECT_VALUE(0x3CBA12_b, 0x512A72_b), get_entity_id_stub);
|
||||||
|
utils::hook::nop(SELECT_VALUE(0x3CBA46_b, 0x512AA6_b), 6);
|
||||||
|
utils::hook::nop(SELECT_VALUE(0x3CBA4E_b, 0x512AAE_b), 2);
|
||||||
|
utils::hook::call(SELECT_VALUE(0x3CBA46_b, 0x512AA6_b), vm_call_builtin_method_stub);
|
||||||
|
|
||||||
|
function::add("print", [](const function_args& args)
|
||||||
{
|
{
|
||||||
const auto num = game::Scr_GetNumParam();
|
|
||||||
std::string buffer{};
|
std::string buffer{};
|
||||||
|
|
||||||
for (auto i = 0; i < num; i++)
|
for (auto i = 0u; i < args.size(); ++i)
|
||||||
{
|
{
|
||||||
const auto str = game::Scr_GetString(i);
|
const auto str = args[i].as<std::string>();
|
||||||
buffer.append(str);
|
buffer.append(str);
|
||||||
buffer.append("\t");
|
buffer.append("\t");
|
||||||
}
|
}
|
||||||
|
|
||||||
console::info("%s\n", buffer.data());
|
console::info("%s\n", buffer.data());
|
||||||
|
|
||||||
|
return scripting::script_value{};
|
||||||
});
|
});
|
||||||
|
|
||||||
function::add("assert", []()
|
function::add("assert", [](const function_args& args)
|
||||||
{
|
{
|
||||||
const auto expr = get_argument(0).as<int>();
|
const auto expr = args[0].as<int>();
|
||||||
if (!expr)
|
if (!expr)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("assert fail");
|
throw std::runtime_error("assert fail");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return scripting::script_value{};
|
||||||
});
|
});
|
||||||
|
|
||||||
function::add("assertex", []()
|
function::add("assertex", [](const function_args& args)
|
||||||
{
|
{
|
||||||
const auto expr = get_argument(0).as<int>();
|
const auto expr = args[0].as<int>();
|
||||||
if (!expr)
|
if (!expr)
|
||||||
{
|
{
|
||||||
const auto error = get_argument(1).as<std::string>();
|
const auto error = args[1].as<std::string>();
|
||||||
throw std::runtime_error(error);
|
throw std::runtime_error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return scripting::script_value{};
|
||||||
});
|
});
|
||||||
|
|
||||||
function::add("replacefunc", []()
|
function::add("replacefunc", [](const function_args& args)
|
||||||
{
|
{
|
||||||
const auto what = get_argument(0).get_raw();
|
const auto what = args[0].get_raw();
|
||||||
const auto with = get_argument(1).get_raw();
|
const auto with = args[1].get_raw();
|
||||||
|
|
||||||
if (what.type != game::SCRIPT_FUNCTION)
|
if (what.type != game::SCRIPT_FUNCTION)
|
||||||
{
|
{
|
||||||
@ -765,26 +840,29 @@ namespace gsc
|
|||||||
}
|
}
|
||||||
|
|
||||||
logfile::set_gsc_hook(what.u.codePosValue, with.u.codePosValue);
|
logfile::set_gsc_hook(what.u.codePosValue, with.u.codePosValue);
|
||||||
|
|
||||||
|
return scripting::script_value{};
|
||||||
});
|
});
|
||||||
|
|
||||||
function::add("toupper", []()
|
function::add("toupper", [](const function_args& args)
|
||||||
{
|
{
|
||||||
const auto string = get_argument(0).as<std::string>();
|
const auto string = args[0].as<std::string>();
|
||||||
game::Scr_AddString(utils::string::to_upper(string).data());
|
return utils::string::to_upper(string);
|
||||||
});
|
});
|
||||||
|
|
||||||
function::add("logprint", []()
|
function::add("logprint", [](const function_args& args)
|
||||||
{
|
{
|
||||||
std::string buffer{};
|
std::string buffer{};
|
||||||
|
|
||||||
const auto params = game::Scr_GetNumParam();
|
for (auto i = 0u; i < args.size(); ++i)
|
||||||
for (auto i = 0; i < params; i++)
|
|
||||||
{
|
{
|
||||||
const auto string = game::Scr_GetString(i);
|
const auto string = args[i].as<std::string>();
|
||||||
buffer.append(string);
|
buffer.append(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
game::G_LogPrintf("%s", buffer.data());
|
game::G_LogPrintf("%s", buffer.data());
|
||||||
|
|
||||||
|
return scripting::script_value{};
|
||||||
});
|
});
|
||||||
|
|
||||||
scripting::on_shutdown([](int free_scripts)
|
scripting::on_shutdown([](int free_scripts)
|
||||||
|
@ -4,26 +4,44 @@
|
|||||||
|
|
||||||
namespace gsc
|
namespace gsc
|
||||||
{
|
{
|
||||||
|
class function_args
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
function_args(std::vector<scripting::script_value>);
|
||||||
|
|
||||||
|
unsigned int size() const;
|
||||||
|
std::vector<scripting::script_value> get_raw() const;
|
||||||
|
scripting::value_wrap get(const int index) const;
|
||||||
|
|
||||||
|
scripting::value_wrap operator[](const int index) const
|
||||||
|
{
|
||||||
|
return this->get(index);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::vector<scripting::script_value> values_;
|
||||||
|
};
|
||||||
|
|
||||||
using builtin_function = void(*)();
|
using builtin_function = void(*)();
|
||||||
using builtin_method = void(*)(game::scr_entref_t);
|
using builtin_method = void(*)(game::scr_entref_t);
|
||||||
|
|
||||||
extern void* func_table[0x1000];
|
using script_function = std::function<scripting::script_value(const function_args&)>;
|
||||||
extern void* meth_table[0x1000];
|
using script_method = std::function<scripting::script_value(const game::scr_entref_t, const function_args&)>;
|
||||||
|
|
||||||
|
extern builtin_function func_table[0x1000];
|
||||||
|
extern builtin_method meth_table[0x1000];
|
||||||
|
|
||||||
game::ScriptFile* find_script(game::XAssetType /*type*/, const char* name, int /*allow_create_default*/);
|
game::ScriptFile* find_script(game::XAssetType /*type*/, const char* name, int /*allow_create_default*/);
|
||||||
|
|
||||||
void load_main_handles();
|
void load_main_handles();
|
||||||
void load_init_handles();
|
void load_init_handles();
|
||||||
|
|
||||||
scripting::script_value get_argument(int index);
|
|
||||||
|
|
||||||
namespace function
|
namespace function
|
||||||
{
|
{
|
||||||
void add(const std::string& name, builtin_function function);
|
void add(const std::string& name, script_function function);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace method
|
namespace method
|
||||||
{
|
{
|
||||||
void add(const std::string& name, builtin_method function);
|
void add(const std::string& name, script_method function);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ namespace io
|
|||||||
{
|
{
|
||||||
if (path.generic_string().find("..") != std::string::npos)
|
if (path.generic_string().find("..") != std::string::npos)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("io: directory traversal is not allowed");
|
throw std::runtime_error("directory traversal is not allowed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ namespace io
|
|||||||
return (fs_game_path / path).generic_string();
|
return (fs_game_path / path).generic_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("io: fs_game is not properly defined");
|
throw std::runtime_error("fs_game is not properly defined");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,60 +47,59 @@ namespace io
|
|||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void post_unpack() override
|
||||||
{
|
{
|
||||||
gsc::function::add("fileexists", []()
|
gsc::function::add("fileexists", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
const auto path = convert_path(gsc::get_argument(0).as<std::string>());
|
const auto path = convert_path(args[0].as<std::string>());
|
||||||
game::Scr_AddBool(utils::io::file_exists(path));
|
return utils::io::file_exists(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
gsc::function::add("writefile", []()
|
gsc::function::add("writefile", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
const auto path = convert_path(gsc::get_argument(0).as<std::string>());
|
const auto path = convert_path(args[0].as<std::string>());
|
||||||
const auto data = gsc::get_argument(1).as<std::string>();
|
const auto data = args[1].as<std::string>();
|
||||||
const auto params = game::Scr_GetNumParam();
|
|
||||||
|
|
||||||
auto append = false;
|
auto append = false;
|
||||||
if (params > 2)
|
if (args.size() > 2u)
|
||||||
{
|
{
|
||||||
append = gsc::get_argument(2).as<bool>();
|
append = args[2].as<bool>();
|
||||||
}
|
}
|
||||||
|
|
||||||
game::Scr_AddBool(utils::io::write_file(path, data, append));
|
return utils::io::write_file(path, data, append);
|
||||||
});
|
});
|
||||||
|
|
||||||
gsc::function::add("readfile", []()
|
gsc::function::add("readfile", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
const auto path = convert_path(gsc::get_argument(0).as<std::string>());
|
const auto path = convert_path(args[0].as<std::string>());
|
||||||
game::Scr_AddString(utils::io::read_file(path).data());
|
return utils::io::read_file(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
gsc::function::add("filesize", []()
|
gsc::function::add("filesize", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
const auto path = convert_path(gsc::get_argument(0).as<std::string>());
|
const auto path = convert_path(args[0].as<std::string>());
|
||||||
game::Scr_AddInt(static_cast<uint32_t>(utils::io::file_size(path)));
|
return static_cast<uint32_t>(utils::io::file_size(path));
|
||||||
});
|
});
|
||||||
|
|
||||||
gsc::function::add("createdirectory", []()
|
gsc::function::add("createdirectory", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
const auto path = convert_path(gsc::get_argument(0).as<std::string>());
|
const auto path = convert_path(args[0].as<std::string>());
|
||||||
game::Scr_AddBool(utils::io::create_directory(path));
|
return utils::io::create_directory(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
gsc::function::add("directoryexists", []()
|
gsc::function::add("directoryexists", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
const auto path = convert_path(gsc::get_argument(0).as<std::string>());
|
const auto path = convert_path(args[0].as<std::string>());
|
||||||
game::Scr_AddBool(utils::io::directory_exists(path));
|
return utils::io::directory_exists(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
gsc::function::add("directoryisempty", []()
|
gsc::function::add("directoryisempty", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
const auto path = convert_path(gsc::get_argument(0).as<std::string>());
|
const auto path = convert_path(args[0].as<std::string>());
|
||||||
game::Scr_AddBool(utils::io::directory_is_empty(path));
|
return utils::io::directory_is_empty(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
gsc::function::add("listfiles", []()
|
gsc::function::add("listfiles", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
const auto path = convert_path(gsc::get_argument(0).as<std::string>());
|
const auto path = convert_path(args[0].as<std::string>());
|
||||||
const auto files = utils::io::list_files(path);
|
const auto files = utils::io::list_files(path);
|
||||||
|
|
||||||
scripting::array array{};
|
scripting::array array{};
|
||||||
@ -109,20 +108,22 @@ namespace io
|
|||||||
array.push(file);
|
array.push(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
scripting::push_value(array);
|
return array;
|
||||||
});
|
});
|
||||||
|
|
||||||
gsc::function::add("copyfolder", []()
|
gsc::function::add("copyfolder", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
const auto source = convert_path(gsc::get_argument(0).as<std::string>());
|
const auto source = convert_path(args[0].as<std::string>());
|
||||||
const auto target = convert_path(gsc::get_argument(1).as<std::string>());
|
const auto target = convert_path(args[1].as<std::string>());
|
||||||
utils::io::copy_folder(source, target);
|
utils::io::copy_folder(source, target);
|
||||||
|
|
||||||
|
return scripting::script_value{};
|
||||||
});
|
});
|
||||||
|
|
||||||
gsc::function::add("removefile", []()
|
gsc::function::add("removefile", [](const gsc::function_args& args)
|
||||||
{
|
{
|
||||||
const auto path = convert_path(gsc::get_argument(0).as<std::string>());
|
const auto path = convert_path(args[0].as<std::string>());
|
||||||
game::Scr_AddBool(utils::io::remove_file(path));
|
return utils::io::remove_file(path);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "game/dvars.hpp"
|
||||||
|
|
||||||
#include "party.hpp"
|
#include "party.hpp"
|
||||||
#include "console.hpp"
|
#include "console.hpp"
|
||||||
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "game/dvars.hpp"
|
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
namespace logger
|
namespace logger
|
||||||
@ -15,48 +14,42 @@ namespace logger
|
|||||||
{
|
{
|
||||||
utils::hook::detour com_error_hook;
|
utils::hook::detour com_error_hook;
|
||||||
|
|
||||||
|
const game::dvar_t* logger_dev = nullptr;
|
||||||
|
|
||||||
void print_error(const char* msg, ...)
|
void print_error(const char* msg, ...)
|
||||||
{
|
{
|
||||||
char buffer[2048];
|
char buffer[2048]{};
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
|
|
||||||
|
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
console::error(buffer);
|
console::error("%s", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_com_error(int, const char* msg, ...)
|
void print_com_error(int, const char* msg, ...)
|
||||||
{
|
{
|
||||||
char buffer[2048];
|
char buffer[2048]{};
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
|
|
||||||
|
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
console::error(buffer);
|
console::error("%s", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void com_error_stub(const int error, const char* msg, ...)
|
void com_error_stub(const int error, const char* msg, ...)
|
||||||
{
|
{
|
||||||
char buffer[2048];
|
char buffer[2048]{};
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
{
|
va_start(ap, msg);
|
||||||
va_list ap;
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
va_start(ap, msg);
|
va_end(ap);
|
||||||
|
|
||||||
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
|
console::error("Error: %s\n", buffer);
|
||||||
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
console::error("Error: %s\n", buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
party::clear_sv_motd(); // clear sv_motd on error if it exists
|
party::clear_sv_motd(); // clear sv_motd on error if it exists
|
||||||
|
|
||||||
@ -65,50 +58,43 @@ namespace logger
|
|||||||
|
|
||||||
void print_warning(const char* msg, ...)
|
void print_warning(const char* msg, ...)
|
||||||
{
|
{
|
||||||
char buffer[2048];
|
char buffer[2048]{};
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
|
|
||||||
|
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
console::warn(buffer);
|
console::warn("%s", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(const char* msg, ...)
|
void print(const char* msg, ...)
|
||||||
{
|
{
|
||||||
char buffer[2048];
|
char buffer[2048]{};
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
|
|
||||||
|
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
console::info(buffer);
|
console::info("%s", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_dev(const char* msg, ...)
|
void print_dev(const char* msg, ...)
|
||||||
{
|
{
|
||||||
static auto* enabled = dvars::register_bool("logger_dev", false, game::DVAR_FLAG_SAVED, "Print dev stuff");
|
if (!logger_dev->current.enabled)
|
||||||
if (!enabled->current.enabled)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char buffer[2048];
|
char buffer[2048]{};
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
|
|
||||||
|
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
console::info(buffer);
|
console::info("%s", buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +112,8 @@ namespace logger
|
|||||||
}
|
}
|
||||||
|
|
||||||
com_error_hook.create(game::Com_Error, com_error_stub);
|
com_error_hook.create(game::Com_Error, com_error_stub);
|
||||||
|
|
||||||
|
logger_dev = dvars::register_bool("logger_dev", false, game::DVAR_FLAG_SAVED, "Print dev stuff");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,12 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const value_wrap& value)
|
||||||
|
: value_(value.get_raw())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
script_value::script_value(const int value)
|
script_value::script_value(const int value)
|
||||||
{
|
{
|
||||||
game::VariableValue variable{};
|
game::VariableValue variable{};
|
||||||
@ -292,6 +298,12 @@ namespace scripting
|
|||||||
return this->value_.get();
|
return this->value_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value_wrap::value_wrap(const scripting::script_value& value, int argument_index)
|
||||||
|
: value_(value)
|
||||||
|
, argument_index_(argument_index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
std::string script_value::to_string() const
|
std::string script_value::to_string() const
|
||||||
{
|
{
|
||||||
if (this->is<int>())
|
if (this->is<int>())
|
||||||
|
@ -9,12 +9,14 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
class entity;
|
class entity;
|
||||||
class array;
|
class array;
|
||||||
|
class value_wrap;
|
||||||
|
|
||||||
class script_value
|
class script_value
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
script_value() = default;
|
script_value() = default;
|
||||||
script_value(const game::VariableValue& value);
|
script_value(const game::VariableValue& value);
|
||||||
|
script_value(const value_wrap& value);
|
||||||
|
|
||||||
script_value(int value);
|
script_value(int value);
|
||||||
script_value(unsigned int value);
|
script_value(unsigned int value);
|
||||||
@ -53,6 +55,20 @@ namespace scripting
|
|||||||
return get<T>();
|
return get<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename I = int>
|
||||||
|
T* as_ptr()
|
||||||
|
{
|
||||||
|
|
||||||
|
const auto value = this->as<I>();
|
||||||
|
|
||||||
|
if (!value)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<T*>(value);
|
||||||
|
}
|
||||||
|
|
||||||
const game::VariableValue& get_raw() const;
|
const game::VariableValue& get_raw() const;
|
||||||
|
|
||||||
variable_value value_{};
|
variable_value value_{};
|
||||||
@ -62,4 +78,50 @@ namespace scripting
|
|||||||
T get() const;
|
T get() const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class value_wrap
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
value_wrap(const scripting::script_value& value, int argument_index);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T as() const
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return this->value_.as<T>();
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(utils::string::va("parameter %d %s", this->argument_index_, e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename I = int>
|
||||||
|
T* as_ptr()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return this->value_.as_ptr<T>();
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(utils::string::va("parameter %d %s", this->argument_index_, e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T is() const
|
||||||
|
{
|
||||||
|
return this->value_.is<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const game::VariableValue& get_raw() const
|
||||||
|
{
|
||||||
|
return this->value_.get_raw();
|
||||||
|
}
|
||||||
|
|
||||||
|
int argument_index_{};
|
||||||
|
scripting::script_value value_;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user