scripting changes
This commit is contained in:
parent
74ea8fb55f
commit
9197b8123f
6984
src/client/component/gsc/error_info_map.hpp
Normal file
6984
src/client/component/gsc/error_info_map.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,14 +3,19 @@
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "script_extension.hpp"
|
||||
#include "script_error.hpp"
|
||||
#include "script_extension.hpp"
|
||||
#include "script_loading.hpp"
|
||||
|
||||
#include "component/scripting.hpp"
|
||||
|
||||
#include "component/console/console.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "error_info_map.hpp"
|
||||
|
||||
using namespace utils::string;
|
||||
|
||||
namespace gsc
|
||||
@ -23,6 +28,9 @@ namespace gsc
|
||||
|
||||
std::string unknown_function_error;
|
||||
|
||||
bool force_error_print = false;
|
||||
std::optional<std::string> gsc_error_msg;
|
||||
|
||||
std::array<const char*, 27> var_typename =
|
||||
{
|
||||
"undefined",
|
||||
@ -281,6 +289,128 @@ namespace gsc
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour scr_error_internal_hook;
|
||||
|
||||
std::optional<std::string> get_opcode_name(const std::uint8_t opcode)
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto index = gsc_ctx->opcode_enum(opcode);
|
||||
return { gsc_ctx->opcode_name(index) };
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void builtin_call_error(const std::string& error)
|
||||
{
|
||||
const auto function_id = get_function_id();
|
||||
|
||||
if (function_id > func_table_count)
|
||||
{
|
||||
console::warn("in call to builtin method \"%s\"%s", gsc_ctx->meth_name(function_id).data(), error.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
console::warn("in call to builtin function \"%s\"%s", gsc_ctx->func_name(function_id).data(), error.data());
|
||||
}
|
||||
}
|
||||
|
||||
void print_callstack()
|
||||
{
|
||||
for (auto frame = game::scr_VmPub->function_frame; frame != game::scr_VmPub->function_frame_start; --frame)
|
||||
{
|
||||
const auto pos = frame == game::scr_VmPub->function_frame ? game::scr_function_stack->pos : frame->fs.pos;
|
||||
const auto function = find_function(frame->fs.pos);
|
||||
|
||||
if (function.has_value())
|
||||
{
|
||||
console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
console::warn("\tat unknown location %p\n", pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vm_error_internal()
|
||||
{
|
||||
const bool dev_script = developer_script ? developer_script->current.enabled : false;
|
||||
|
||||
if (!dev_script && !force_error_print)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
console::warn("*********** script runtime error *************\n");
|
||||
|
||||
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(0x6B22940_b);
|
||||
const std::string error_str = gsc_error_msg.has_value()
|
||||
? utils::string::va(": %s", gsc_error_msg.value().data())
|
||||
: "";
|
||||
|
||||
if ((opcode_id >= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltin0) && opcode_id <= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltin))
|
||||
|| (opcode_id >= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltinMethod0) && opcode_id <= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltinMethod)))
|
||||
{
|
||||
builtin_call_error(error_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto opcode = get_opcode_name(opcode_id);
|
||||
if (opcode.has_value())
|
||||
{
|
||||
console::warn("while processing instruction %s%s\n", opcode.value().data(), error_str.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
console::warn("while processing instruction 0x%X%s\n", opcode_id, error_str.data());
|
||||
}
|
||||
}
|
||||
|
||||
force_error_print = false;
|
||||
gsc_error_msg = {};
|
||||
|
||||
print_callstack();
|
||||
console::warn("**********************************************\n");
|
||||
}
|
||||
|
||||
void vm_error_stub(__int64 mark_pos)
|
||||
{
|
||||
vm_error_internal();
|
||||
|
||||
utils::hook::invoke<void>(0x510C80_b, mark_pos);
|
||||
}
|
||||
|
||||
void scr_error_internal_stub()
|
||||
{
|
||||
const auto ret_addr = reinterpret_cast<std::uint64_t>(_ReturnAddress());
|
||||
if (!gsc_error_msg.has_value())
|
||||
{
|
||||
const auto it = scr_error_info_map.find(ret_addr - 5);
|
||||
|
||||
if (it != scr_error_info_map.end() && !it->second.empty())
|
||||
{
|
||||
gsc_error_msg = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
scr_error_internal_hook.invoke<void>();
|
||||
}
|
||||
}
|
||||
|
||||
void scr_error(const char* error, const bool force_print)
|
||||
{
|
||||
force_error_print = force_print;
|
||||
gsc_error_msg = error;
|
||||
|
||||
game::Scr_ErrorInternal();
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
|
||||
{
|
||||
for (const auto& file : scripting::script_function_table_sort)
|
||||
@ -305,6 +435,10 @@ namespace gsc
|
||||
{
|
||||
scr_emit_function_hook.create(0xBFCF90_b, &scr_emit_function_stub);
|
||||
|
||||
scr_error_internal_hook.create(game::Scr_ErrorInternal, scr_error_internal_stub);
|
||||
|
||||
utils::hook::call(0xC0F8C1_b, vm_error_stub); // LargeLocalResetToMark
|
||||
|
||||
utils::hook::call(0xBFCF3A_b, compile_error_stub); // CompileError (LinkFile)
|
||||
utils::hook::call(0xBFCF86_b, compile_error_stub); // ^
|
||||
utils::hook::call(0xBFD06F_b, find_variable_stub); // Scr_EmitFunction
|
||||
|
@ -2,5 +2,7 @@
|
||||
|
||||
namespace gsc
|
||||
{
|
||||
void scr_error(const char* error, const bool force_print = false);
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> find_function(const char* pos);
|
||||
}
|
@ -19,9 +19,6 @@ namespace gsc
|
||||
std::uint16_t function_id_start = 806;
|
||||
std::uint16_t method_id_start = 1483 + 0x8000;
|
||||
|
||||
constexpr size_t func_table_count = 0x1000;
|
||||
constexpr size_t meth_table_count = 0x1000;
|
||||
|
||||
builtin_function func_table[func_table_count]{};
|
||||
builtin_method meth_table[meth_table_count]{};
|
||||
|
||||
@ -32,9 +29,6 @@ namespace gsc
|
||||
std::unordered_map<std::uint16_t, script_function> functions;
|
||||
std::unordered_map<std::uint16_t, script_method> methods;
|
||||
|
||||
bool force_error_print = false;
|
||||
std::optional<std::string> gsc_error_msg;
|
||||
|
||||
std::unordered_map<const char*, const char*> vm_execute_hooks;
|
||||
const char* target_function = nullptr;
|
||||
|
||||
@ -61,16 +55,6 @@ namespace gsc
|
||||
scripting::push_value(value);
|
||||
}
|
||||
|
||||
std::uint16_t get_function_id()
|
||||
{
|
||||
const auto pos = game::scr_function_stack->pos;
|
||||
return *reinterpret_cast<std::uint16_t*>(
|
||||
reinterpret_cast<size_t>(pos - 2));
|
||||
}
|
||||
|
||||
void nullstub_func() {}
|
||||
void nullstub_meth(game::scr_entref_t) {}
|
||||
|
||||
void execute_custom_function(const std::uint16_t id)
|
||||
{
|
||||
try
|
||||
@ -130,102 +114,39 @@ namespace gsc
|
||||
}
|
||||
}
|
||||
|
||||
void builtin_call_error(const std::string& error)
|
||||
void vm_call_builtin_method_internal(game::scr_entref_t ent_ref, int function_id)
|
||||
{
|
||||
const auto function_id = get_function_id();
|
||||
|
||||
if (function_id > func_table_count)
|
||||
{
|
||||
console::warn("in call to builtin method \"%s\"%s", gsc_ctx->meth_name(function_id).data(), error.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
console::warn("in call to builtin function \"%s\"%s", gsc_ctx->func_name(function_id).data(), error.data());
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> get_opcode_name(const std::uint8_t opcode)
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto index = gsc_ctx->opcode_enum(opcode);
|
||||
return { gsc_ctx->opcode_name(index) };
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void print_callstack()
|
||||
{
|
||||
for (auto frame = game::scr_VmPub->function_frame; frame != game::scr_VmPub->function_frame_start; --frame)
|
||||
{
|
||||
const auto pos = frame == game::scr_VmPub->function_frame ? game::scr_function_stack->pos : frame->fs.pos;
|
||||
const auto function = find_function(frame->fs.pos);
|
||||
|
||||
if (function.has_value())
|
||||
{
|
||||
console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
console::warn("\tat unknown location %p\n", pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vm_error_internal()
|
||||
{
|
||||
const bool dev_script = developer_script ? developer_script->current.enabled : false;
|
||||
|
||||
if (!dev_script && !force_error_print)
|
||||
const auto custom_function_id = static_cast<std::uint16_t>(function_id); // cast for gsc-tool & our custom method map
|
||||
const auto custom = methods.contains(custom_function_id);
|
||||
if (custom)
|
||||
{
|
||||
execute_custom_method(custom_function_id, ent_ref);
|
||||
return;
|
||||
}
|
||||
|
||||
console::warn("*********** script runtime error *************\n");
|
||||
|
||||
const auto opcode_id = *reinterpret_cast<std::uint8_t*>(0x6B22940_b);
|
||||
const std::string error_str = gsc_error_msg.has_value()
|
||||
? utils::string::va(": %s", gsc_error_msg.value().data())
|
||||
: "";
|
||||
|
||||
if ((opcode_id >= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltin0) && opcode_id <= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltin))
|
||||
|| (opcode_id >= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltinMethod0) && opcode_id <= gsc_ctx->opcode_id(xsk::gsc::opcode::OP_CallBuiltinMethod)))
|
||||
builtin_method meth = meth_table[function_id - 0x8000];
|
||||
if (meth == nullptr)
|
||||
{
|
||||
builtin_call_error(error_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto opcode = get_opcode_name(opcode_id);
|
||||
if (opcode.has_value())
|
||||
{
|
||||
console::warn("while processing instruction %s%s\n", opcode.value().data(), error_str.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
console::warn("while processing instruction 0x%X%s\n", opcode_id, error_str.data());
|
||||
}
|
||||
scr_error(utils::string::va("builtin method \"%s\" doesn't exist", gsc_ctx->meth_name(custom_function_id).data()), true);
|
||||
return;
|
||||
}
|
||||
|
||||
force_error_print = false;
|
||||
gsc_error_msg = {};
|
||||
|
||||
print_callstack();
|
||||
console::warn("**********************************************\n");
|
||||
meth(ent_ref);
|
||||
}
|
||||
|
||||
void vm_error_stub(__int64 mark_pos)
|
||||
void vm_call_builtin_method_stub(utils::hook::assembler& a)
|
||||
{
|
||||
vm_error_internal();
|
||||
//a.pushad64();
|
||||
a.push(rdx);
|
||||
a.push(ecx);
|
||||
a.mov(rdx, rdi); // function id is stored in rdi
|
||||
a.mov(ecx, ebx); // ent ref is stored in ebx
|
||||
a.call(vm_call_builtin_method_internal);
|
||||
a.pop(rdx);
|
||||
a.pop(ecx);
|
||||
//a.popad64();
|
||||
|
||||
if (!game::CL_IsGameClientActive(0))
|
||||
{
|
||||
//return game::Com_Error(game::errorParm::ERR_SCRIPT_DROP, gsc_error_msg.has_value() ? gsc_error_msg.value().data() : "Fatal script error");
|
||||
}
|
||||
|
||||
utils::hook::invoke<void>(0x510C80_b, mark_pos);
|
||||
a.jmp(0xC0E8F9_b);
|
||||
}
|
||||
|
||||
void print(const function_args& args)
|
||||
@ -289,14 +210,6 @@ namespace gsc
|
||||
}
|
||||
}
|
||||
|
||||
void scr_error(const char* error, const bool force_print)
|
||||
{
|
||||
force_error_print = force_print;
|
||||
gsc_error_msg = error;
|
||||
|
||||
game::Scr_ErrorInternal();
|
||||
}
|
||||
|
||||
namespace function
|
||||
{
|
||||
void add(const std::string& name, script_function function)
|
||||
@ -358,39 +271,11 @@ namespace gsc
|
||||
return {this->values_[index], index};
|
||||
}
|
||||
|
||||
void vm_call_builtin_method_internal(game::scr_entref_t ent_ref, int function_id)
|
||||
std::uint16_t get_function_id()
|
||||
{
|
||||
const auto custom_function_id = static_cast<std::uint16_t>(function_id); // cast for gsc-tool & our custom method map
|
||||
const auto custom = methods.contains(custom_function_id);
|
||||
if (custom)
|
||||
{
|
||||
execute_custom_method(custom_function_id, ent_ref);
|
||||
return;
|
||||
}
|
||||
|
||||
builtin_method meth = meth_table[function_id - 0x8000];
|
||||
if (meth == nullptr)
|
||||
{
|
||||
scr_error(utils::string::va("builtin method \"%s\" doesn't exist", gsc_ctx->meth_name(custom_function_id).data()), true);
|
||||
return;
|
||||
}
|
||||
|
||||
meth(ent_ref);
|
||||
}
|
||||
|
||||
void vm_call_builtin_method_stub(utils::hook::assembler& a)
|
||||
{
|
||||
//a.pushad64();
|
||||
a.push(rdx);
|
||||
a.push(ecx);
|
||||
a.mov(rdx, rdi); // function id is stored in rdi
|
||||
a.mov(ecx, ebx); // ent ref is stored in ebx
|
||||
a.call(vm_call_builtin_method_internal);
|
||||
a.pop(rdx);
|
||||
a.pop(ecx);
|
||||
//a.popad64();
|
||||
|
||||
a.jmp(0xC0E8F9_b);
|
||||
const auto pos = game::scr_function_stack->pos;
|
||||
return *reinterpret_cast<std::uint16_t*>(
|
||||
reinterpret_cast<size_t>(pos - 2));
|
||||
}
|
||||
|
||||
class extension final : public component_interface
|
||||
@ -413,8 +298,6 @@ namespace gsc
|
||||
utils::hook::nop(0xC0E8EB_b, 14); // nop the lea & call at the end of call_builtin_method
|
||||
utils::hook::jump(0xC0E8EB_b, utils::hook::assemble(vm_call_builtin_method_stub), true);
|
||||
|
||||
utils::hook::call(0xC0F8C1_b, vm_error_stub); // LargeLocalResetToMark
|
||||
|
||||
utils::hook::jump(0xC0D0A4_b, utils::hook::assemble(vm_execute_stub), true);
|
||||
|
||||
/*
|
||||
|
@ -29,12 +29,15 @@ namespace gsc
|
||||
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&)>;
|
||||
|
||||
extern builtin_function func_table[0x1000];
|
||||
extern builtin_method meth_table[0x1000];
|
||||
constexpr size_t func_table_count = 0x1000;
|
||||
constexpr size_t meth_table_count = 0x1000;
|
||||
|
||||
extern builtin_function func_table[func_table_count];
|
||||
extern builtin_method meth_table[meth_table_count];
|
||||
|
||||
extern const game::dvar_t* developer_script;
|
||||
|
||||
void scr_error(const char* error, const bool force_print = false);
|
||||
std::uint16_t get_function_id();
|
||||
|
||||
namespace function
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user