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 "loader/component_loader.hpp"
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
|
|
||||||
#include "script_extension.hpp"
|
|
||||||
#include "script_error.hpp"
|
#include "script_error.hpp"
|
||||||
|
#include "script_extension.hpp"
|
||||||
|
#include "script_loading.hpp"
|
||||||
|
|
||||||
#include "component/scripting.hpp"
|
#include "component/scripting.hpp"
|
||||||
|
|
||||||
|
#include "component/console/console.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
#include <utils/string.hpp>
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
#include "error_info_map.hpp"
|
||||||
|
|
||||||
using namespace utils::string;
|
using namespace utils::string;
|
||||||
|
|
||||||
namespace gsc
|
namespace gsc
|
||||||
@ -23,6 +28,9 @@ namespace gsc
|
|||||||
|
|
||||||
std::string unknown_function_error;
|
std::string unknown_function_error;
|
||||||
|
|
||||||
|
bool force_error_print = false;
|
||||||
|
std::optional<std::string> gsc_error_msg;
|
||||||
|
|
||||||
std::array<const char*, 27> var_typename =
|
std::array<const char*, 27> var_typename =
|
||||||
{
|
{
|
||||||
"undefined",
|
"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)
|
std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
|
||||||
{
|
{
|
||||||
for (const auto& file : scripting::script_function_table_sort)
|
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_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(0xBFCF3A_b, compile_error_stub); // CompileError (LinkFile)
|
||||||
utils::hook::call(0xBFCF86_b, compile_error_stub); // ^
|
utils::hook::call(0xBFCF86_b, compile_error_stub); // ^
|
||||||
utils::hook::call(0xBFD06F_b, find_variable_stub); // Scr_EmitFunction
|
utils::hook::call(0xBFD06F_b, find_variable_stub); // Scr_EmitFunction
|
||||||
|
@ -2,5 +2,7 @@
|
|||||||
|
|
||||||
namespace gsc
|
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);
|
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 function_id_start = 806;
|
||||||
std::uint16_t method_id_start = 1483 + 0x8000;
|
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_function func_table[func_table_count]{};
|
||||||
builtin_method meth_table[meth_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_function> functions;
|
||||||
std::unordered_map<std::uint16_t, script_method> methods;
|
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;
|
std::unordered_map<const char*, const char*> vm_execute_hooks;
|
||||||
const char* target_function = nullptr;
|
const char* target_function = nullptr;
|
||||||
|
|
||||||
@ -61,16 +55,6 @@ namespace gsc
|
|||||||
scripting::push_value(value);
|
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)
|
void execute_custom_function(const std::uint16_t id)
|
||||||
{
|
{
|
||||||
try
|
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();
|
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 (function_id > func_table_count)
|
if (custom)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
|
execute_custom_method(custom_function_id, ent_ref);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console::warn("*********** script runtime error *************\n");
|
builtin_method meth = meth_table[function_id - 0x8000];
|
||||||
|
if (meth == nullptr)
|
||||||
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);
|
scr_error(utils::string::va("builtin method \"%s\" doesn't exist", gsc_ctx->meth_name(custom_function_id).data()), true);
|
||||||
}
|
return;
|
||||||
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;
|
meth(ent_ref);
|
||||||
gsc_error_msg = {};
|
|
||||||
|
|
||||||
print_callstack();
|
|
||||||
console::warn("**********************************************\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
a.jmp(0xC0E8F9_b);
|
||||||
{
|
|
||||||
//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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(const function_args& args)
|
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
|
namespace function
|
||||||
{
|
{
|
||||||
void add(const std::string& name, script_function function)
|
void add(const std::string& name, script_function function)
|
||||||
@ -358,39 +271,11 @@ namespace gsc
|
|||||||
return {this->values_[index], index};
|
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 pos = game::scr_function_stack->pos;
|
||||||
const auto custom = methods.contains(custom_function_id);
|
return *reinterpret_cast<std::uint16_t*>(
|
||||||
if (custom)
|
reinterpret_cast<size_t>(pos - 2));
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class extension final : public component_interface
|
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::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::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);
|
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_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&)>;
|
||||||
|
|
||||||
extern builtin_function func_table[0x1000];
|
constexpr size_t func_table_count = 0x1000;
|
||||||
extern builtin_method meth_table[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;
|
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
|
namespace function
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user