Merge branch 'master' of https://github.com/project-bo4/shield-development
This commit is contained in:
commit
6f784abc1f
@ -1,6 +1,7 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "definitions/game.hpp"
|
#include "definitions/game.hpp"
|
||||||
#include "component/scheduler.hpp"
|
#include "component/scheduler.hpp"
|
||||||
|
#include "component/gsc_custom.hpp"
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
#include <utilities/hook.hpp>
|
#include <utilities/hook.hpp>
|
||||||
@ -73,6 +74,12 @@ namespace debugging
|
|||||||
|
|
||||||
void sys_error_stub(uint32_t code, const char* message)
|
void sys_error_stub(uint32_t code, const char* message)
|
||||||
{
|
{
|
||||||
|
if (code == gsc_custom::linking_error)
|
||||||
|
{
|
||||||
|
gsc_custom::find_linking_issues();
|
||||||
|
return; // converted to runtime error to avoid the crash
|
||||||
|
}
|
||||||
|
|
||||||
const char* error_message = runtime_errors::get_error_message(code);
|
const char* error_message = runtime_errors::get_error_message(code);
|
||||||
|
|
||||||
if (error_message)
|
if (error_message)
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "gsc_custom.hpp"
|
#include "gsc_custom.hpp"
|
||||||
#include "gsc_funcs.hpp"
|
#include "gsc_funcs.hpp"
|
||||||
|
#include "hashes.hpp"
|
||||||
#include "definitions/game.hpp"
|
#include "definitions/game.hpp"
|
||||||
|
#include "definitions/xassets.hpp"
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
#include <utilities/hook.hpp>
|
#include <utilities/hook.hpp>
|
||||||
#include <utilities/json_config.hpp>
|
#include <utilities/json_config.hpp>
|
||||||
|
#include <utilities/string.hpp>
|
||||||
|
|
||||||
namespace gsc_custom
|
namespace gsc_custom
|
||||||
{
|
{
|
||||||
@ -208,6 +211,20 @@ namespace gsc_custom
|
|||||||
inst_data.emplace_back(info);
|
inst_data.emplace_back(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vm_op_custom_devblock(game::scriptInstance_t inst, game::function_stack_t* fs_0, game::ScrVmContext_t* vmc, bool* terminate)
|
||||||
|
{
|
||||||
|
byte* base = align_ptr<int16_t>(fs_0->pos);
|
||||||
|
int16_t delta = *(int16_t*)base;
|
||||||
|
|
||||||
|
fs_0->pos = base + 2;
|
||||||
|
|
||||||
|
if (!gsc_funcs::enable_dev_blocks) {
|
||||||
|
// default action, jump after the dev block
|
||||||
|
fs_0->pos = fs_0->pos + delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void vm_op_custom_lazylink(game::scriptInstance_t inst, game::function_stack_t* fs_0, game::ScrVmContext_t* vmc, bool* terminate)
|
void vm_op_custom_lazylink(game::scriptInstance_t inst, game::function_stack_t* fs_0, game::ScrVmContext_t* vmc, bool* terminate)
|
||||||
{
|
{
|
||||||
byte* base = align_ptr<uint32_t>(fs_0->pos);
|
byte* base = align_ptr<uint32_t>(fs_0->pos);
|
||||||
@ -290,7 +307,212 @@ namespace gsc_custom
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void find_linking_issues()
|
||||||
|
{
|
||||||
|
logger::write(logger::LOG_TYPE_ERROR, "Linking error detected, searching cause...");
|
||||||
|
|
||||||
|
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> availables{};
|
||||||
|
for (size_t _inst = 0; _inst < game::SCRIPTINSTANCE_MAX; _inst++)
|
||||||
|
{
|
||||||
|
size_t error{};
|
||||||
|
game::scriptInstance_t inst = (game::scriptInstance_t)_inst;
|
||||||
|
for (size_t obj = 0; obj < game::gObjFileInfoCount[inst]; obj++)
|
||||||
|
{
|
||||||
|
game::objFileInfo_t& info = (*game::gObjFileInfo)[inst][obj];
|
||||||
|
|
||||||
|
if (!info.activeVersion)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::GSC_OBJ* prime_obj = info.activeVersion;
|
||||||
|
|
||||||
|
availables.clear();
|
||||||
|
|
||||||
|
// load exports
|
||||||
|
game::GSC_EXPORT_ITEM* exports = (game::GSC_EXPORT_ITEM*)(prime_obj->magic + prime_obj->exports_offset);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < prime_obj->exports_count; i++)
|
||||||
|
{
|
||||||
|
availables[exports[i].name_space].insert(exports[i].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load using exports
|
||||||
|
int64_t* usings = (int64_t*)(prime_obj->magic + prime_obj->include_offset);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < prime_obj->include_count; i++)
|
||||||
|
{
|
||||||
|
game::BO4_AssetRef_t ref{ usings[i], 0 };
|
||||||
|
xassets::scriptparsetree_header* spt = xassets::DB_FindXAssetHeader(xassets::ASSET_TYPE_SCRIPTPARSETREE, &ref, false, -1).scriptparsetree;
|
||||||
|
|
||||||
|
if (!spt || !spt->buffer)
|
||||||
|
{
|
||||||
|
error++;
|
||||||
|
logger::write(logger::LOG_TYPE_ERROR, "[%s] Can't find #using %s in %s", inst ? "CSC" : "GSC", hashes::lookup_tmp("script", ref.hash), hashes::lookup_tmp("script", prime_obj->name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::GSC_EXPORT_ITEM* exports_using = (game::GSC_EXPORT_ITEM*)(spt->buffer->magic + spt->buffer->exports_offset);
|
||||||
|
|
||||||
|
for (size_t j = 0; j < spt->buffer->exports_count; j++)
|
||||||
|
{
|
||||||
|
if (exports_using[j].flags & game::GSC_EXPORT_FLAGS::GEF_PRIVATE)
|
||||||
|
{
|
||||||
|
continue; // can't import private exports
|
||||||
|
}
|
||||||
|
availables[exports_using[j].name_space].insert(exports_using[j].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
game::GSC_IMPORT_ITEM* imports = (game::GSC_IMPORT_ITEM*)(prime_obj->magic + prime_obj->imports_offset);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < prime_obj->imports_count; i++)
|
||||||
|
{
|
||||||
|
game::GSC_IMPORT_ITEM* imp = imports;
|
||||||
|
|
||||||
|
uint32_t* locations = reinterpret_cast<uint32_t*>(imp + 1);
|
||||||
|
imports = reinterpret_cast<game::GSC_IMPORT_ITEM*>(locations + imp->num_address);
|
||||||
|
|
||||||
|
if (imp->flags & game::GSC_IMPORT_FLAGS::GIF_DEV_CALL)
|
||||||
|
{
|
||||||
|
// ignore dev calls
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto itn = availables.find(imp->name_space);
|
||||||
|
|
||||||
|
if (itn != availables.end() && itn->second.contains(imp->name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte import_type = imp->flags & game::GSC_IMPORT_FLAGS::GIF_CALLTYPE_MASK;
|
||||||
|
|
||||||
|
// search builtin calls
|
||||||
|
if ((imp->flags & game::GSC_IMPORT_FLAGS::GIF_GET_CALL) != 0 || imp->name_space == 0xC1243180 || imp->name_space == 0x222276A9)
|
||||||
|
{
|
||||||
|
|
||||||
|
int type{};
|
||||||
|
int ignored{};
|
||||||
|
if (import_type == game::GSC_IMPORT_FLAGS::GIF_FUNC_METHOD || import_type == game::GSC_IMPORT_FLAGS::GIF_FUNCTION)
|
||||||
|
{
|
||||||
|
// &func or func()
|
||||||
|
if (inst)
|
||||||
|
{
|
||||||
|
if (game::CScr_GetFunction(imp->name, &type, &ignored, &ignored) && !type)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (game::Scr_GetFunction(imp->name, &type, &ignored, &ignored) && !type)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import_type == game::GSC_IMPORT_FLAGS::GIF_FUNC_METHOD || import_type == game::GSC_IMPORT_FLAGS::GIF_METHOD)
|
||||||
|
{
|
||||||
|
// &meth or <x> meth()
|
||||||
|
if (inst)
|
||||||
|
{
|
||||||
|
if (game::CScr_GetMethod(imp->name, &type, &ignored, &ignored) && !type)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (game::Scr_GetMethod(imp->name, &type, &ignored, &ignored) && !type)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* func;
|
||||||
|
|
||||||
|
if ((imp->flags & game::GSC_IMPORT_FLAGS::GIF_GET_CALL) != 0 || imp->name_space == 0xC1243180 || imp->name_space == 0x222276A9)
|
||||||
|
{
|
||||||
|
func = hashes::lookup_tmp("function", imp->name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
func = utilities::string::va("%s::%s", hashes::lookup_tmp("namespace", imp->name_space), hashes::lookup_tmp("function", imp->name));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* prefix;
|
||||||
|
|
||||||
|
switch (import_type)
|
||||||
|
{
|
||||||
|
case game::GIF_FUNC_METHOD:
|
||||||
|
prefix = "&";
|
||||||
|
break;
|
||||||
|
case game::GIF_FUNCTION:
|
||||||
|
case game::GIF_METHOD:
|
||||||
|
prefix = "";
|
||||||
|
break;
|
||||||
|
case game::GIF_FUNCTION_THREAD:
|
||||||
|
case game::GIF_METHOD_THREAD:
|
||||||
|
prefix = "thread ";
|
||||||
|
break;
|
||||||
|
case game::GIF_FUNCTION_CHILDTHREAD:
|
||||||
|
case game::GIF_METHOD_CHILDTHREAD:
|
||||||
|
prefix = "childthread ";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
prefix = "<error>";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger::write(logger::LOG_TYPE_ERROR, "[%s] Unknown import %s%s in %s",
|
||||||
|
inst ? "CSC" : "GSC", prefix, func, hashes::lookup_tmp("script", prime_obj->name)
|
||||||
|
);
|
||||||
|
for (size_t j = 0; j < imp->num_address; j++)
|
||||||
|
{
|
||||||
|
const char* scriptname{};
|
||||||
|
int32_t sloc{};
|
||||||
|
int32_t crc{};
|
||||||
|
int32_t vm{};
|
||||||
|
game::Scr_GetGscExportInfo(inst, prime_obj->magic + locations[j], &scriptname, &sloc, &crc, &vm);
|
||||||
|
if (scriptname)
|
||||||
|
{
|
||||||
|
logger::write(logger::LOG_TYPE_ERROR, "[%s] at %s", inst ? "CSC" : "GSC", scriptname);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger::write(logger::LOG_TYPE_ERROR, "[%s] at %s@%lx", inst ? "CSC" : "GSC", hashes::lookup_tmp("script", prime_obj->name), locations[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
// convert the error to a terminal error to avoid a game crash
|
||||||
|
gsc_funcs::gsc_error("Find %lld GSC Linking error(s), see logs for more details", inst, true, error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Can't find the error, we crash the server by default
|
||||||
|
gsc_funcs::gsc_error("GSC Linking error, see logs for more details", game::SCRIPTINSTANCE_SERVER, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void patch_linking_sys_error()
|
||||||
|
{
|
||||||
|
auto scr_get_gsc_obj = 0x142748BB0_g;
|
||||||
|
|
||||||
|
// skip the error and the autoexec
|
||||||
|
// 1C1 = syserr start
|
||||||
|
// 19F = end
|
||||||
|
utilities::hook::jump(scr_get_gsc_obj + 0x1C1, scr_get_gsc_obj + 0x19F);
|
||||||
|
}
|
||||||
|
|
||||||
utilities::hook::detour scr_get_gsc_obj_hook;
|
utilities::hook::detour scr_get_gsc_obj_hook;
|
||||||
|
utilities::hook::detour gsc_obj_resolve_hook;
|
||||||
void scr_get_gsc_obj_stub(game::scriptInstance_t inst, game::BO4_AssetRef_t* name, bool runScript)
|
void scr_get_gsc_obj_stub(game::scriptInstance_t inst, game::BO4_AssetRef_t* name, bool runScript)
|
||||||
{
|
{
|
||||||
if (game::gObjFileInfoCount[inst] == 0)
|
if (game::gObjFileInfoCount[inst] == 0)
|
||||||
@ -305,16 +527,63 @@ namespace gsc_custom
|
|||||||
gsc_custom::link_detours(inst);
|
gsc_custom::link_detours(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t gsc_obj_resolve_stub(game::scriptInstance_t inst, game::GSC_OBJ* prime_obj)
|
||||||
|
{
|
||||||
|
game::GSC_IMPORT_ITEM* import_item = prime_obj->get_imports();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < prime_obj->imports_count; i++)
|
||||||
|
{
|
||||||
|
if ((import_item->flags & game::GIF_SHIELD_DEV_BLOCK_FUNC) != 0)
|
||||||
|
{
|
||||||
|
// enable or disable this dev import
|
||||||
|
if (gsc_funcs::enable_dev_blocks)
|
||||||
|
{
|
||||||
|
import_item->flags &= ~game::GIF_DEV_CALL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
import_item->flags |= game::GIF_DEV_CALL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// goto to the next element after the addresses
|
||||||
|
uint32_t* addresses = reinterpret_cast<uint32_t*>(import_item + 1);
|
||||||
|
import_item = reinterpret_cast<game::GSC_IMPORT_ITEM*>(addresses + import_item->num_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dv_func_back = gsc_funcs::enable_dev_func;
|
||||||
|
|
||||||
|
if (gsc_funcs::enable_dev_blocks)
|
||||||
|
{
|
||||||
|
gsc_funcs::enable_dev_func = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ret = gsc_obj_resolve_hook.invoke<int32_t>(inst, prime_obj);
|
||||||
|
|
||||||
|
gsc_funcs::enable_dev_func = dv_func_back;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void post_unpack() override
|
void post_unpack() override
|
||||||
{
|
{
|
||||||
// t8compiler custom opcode
|
// t8compiler custom opcode
|
||||||
game::gVmOpJumpTable[0x16] = vm_op_custom_lazylink;
|
game::gVmOpJumpTable[lazylink_opcode] = vm_op_custom_lazylink;
|
||||||
|
game::gVmOpJumpTable[shield_devblock_opcode] = vm_op_custom_devblock;
|
||||||
|
|
||||||
// group gsc link
|
// group gsc link
|
||||||
scr_get_gsc_obj_hook.create(0x142748BB0_g, scr_get_gsc_obj_stub);
|
scr_get_gsc_obj_hook.create(0x142748BB0_g, scr_get_gsc_obj_stub);
|
||||||
|
|
||||||
|
if (gsc_funcs::enable_dev_blocks)
|
||||||
|
{
|
||||||
|
gsc_obj_resolve_hook.create(0x142746A30_g, gsc_obj_resolve_stub);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
patch_linking_sys_error();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
namespace gsc_custom
|
namespace gsc_custom
|
||||||
{
|
{
|
||||||
|
constexpr uint32_t linking_error = 1670707254;
|
||||||
|
constexpr uint16_t lazylink_opcode = 0x16;
|
||||||
|
constexpr uint16_t shield_devblock_opcode = 0x21;
|
||||||
|
|
||||||
enum gsic_field_type
|
enum gsic_field_type
|
||||||
{
|
{
|
||||||
GSIC_FIELD_DETOUR = 0
|
GSIC_FIELD_DETOUR = 0
|
||||||
@ -22,8 +26,10 @@ namespace gsc_custom
|
|||||||
struct gsic_info
|
struct gsic_info
|
||||||
{
|
{
|
||||||
bool sync{};
|
bool sync{};
|
||||||
|
bool dev_blocks{};
|
||||||
std::vector<gsic_detour> detours{};
|
std::vector<gsic_detour> detours{};
|
||||||
};
|
};
|
||||||
|
|
||||||
void sync_gsic(game::scriptInstance_t inst, gsic_info& info);
|
void sync_gsic(game::scriptInstance_t inst, gsic_info& info);
|
||||||
|
void find_linking_issues();
|
||||||
}
|
}
|
@ -947,6 +947,59 @@ namespace gsc_funcs
|
|||||||
|
|
||||||
shield_from_json_push_struct(inst, doc);
|
shield_from_json_push_struct(inst, doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_debug_command(game::scriptInstance_t inst)
|
||||||
|
{
|
||||||
|
int localclientnum;
|
||||||
|
const char* cmd;
|
||||||
|
|
||||||
|
if (inst == game::SCRIPTINSTANCE_CLIENT)
|
||||||
|
{
|
||||||
|
// client, using param
|
||||||
|
localclientnum = (int)game::ScrVm_GetInt(inst, 0);
|
||||||
|
cmd = game::ScrVm_GetString(inst, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// server, using primary
|
||||||
|
localclientnum = game::Com_LocalClients_GetPrimary();
|
||||||
|
cmd = game::ScrVm_GetString(inst, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
game::Cbuf_AddText(localclientnum, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hash_lookup(game::scriptInstance_t inst)
|
||||||
|
{
|
||||||
|
game::ScrVarType_t type = game::ScrVm_GetType(inst, 0);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case game::TYPE_STRING:
|
||||||
|
game::ScrVm_AddConstString(inst, game::ScrVm_GetConstString(inst, 0));
|
||||||
|
return;
|
||||||
|
case game::TYPE_HASH:
|
||||||
|
{
|
||||||
|
game::BO4_AssetRef_t hash;
|
||||||
|
const char* lookup = hashes::lookup(game::ScrVm_GetHash(&hash, inst, 0)->hash);
|
||||||
|
|
||||||
|
if (lookup)
|
||||||
|
{
|
||||||
|
game::ScrVm_AddString(inst, lookup);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// can't find value, return base hash
|
||||||
|
game::ScrVm_AddHash(inst, &hash);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
gsc_error("invalid hash param, a hash or a string should be used, received: %s", inst, false, game::var_typename[type]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
game::BO4_BuiltinFunctionDef custom_functions_gsc[] =
|
game::BO4_BuiltinFunctionDef custom_functions_gsc[] =
|
||||||
{
|
{
|
||||||
@ -1047,6 +1100,13 @@ namespace gsc_funcs
|
|||||||
.max_args = 2,
|
.max_args = 2,
|
||||||
.actionFunc = shield_to_json,
|
.actionFunc = shield_to_json,
|
||||||
.type = 0
|
.type = 0
|
||||||
|
},
|
||||||
|
{// ShieldHashLookup(hash hash) -> string
|
||||||
|
.canonId = canon_hash("ShieldHashLookup"),
|
||||||
|
.min_args = 1,
|
||||||
|
.max_args = 1,
|
||||||
|
.actionFunc = hash_lookup,
|
||||||
|
.type = 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
game::BO4_BuiltinFunctionDef custom_functions_csc[] =
|
game::BO4_BuiltinFunctionDef custom_functions_csc[] =
|
||||||
@ -1141,6 +1201,13 @@ namespace gsc_funcs
|
|||||||
.max_args = 2,
|
.max_args = 2,
|
||||||
.actionFunc = shield_to_json,
|
.actionFunc = shield_to_json,
|
||||||
.type = 0
|
.type = 0
|
||||||
|
},
|
||||||
|
{// ShieldHashLookup(hash hash) -> string
|
||||||
|
.canonId = canon_hash("ShieldHashLookup"),
|
||||||
|
.min_args = 1,
|
||||||
|
.max_args = 1,
|
||||||
|
.actionFunc = hash_lookup,
|
||||||
|
.type = 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1175,6 +1242,7 @@ namespace gsc_funcs
|
|||||||
|
|
||||||
|
|
||||||
bool enable_dev_func = false;
|
bool enable_dev_func = false;
|
||||||
|
bool enable_dev_blocks = false;
|
||||||
|
|
||||||
utilities::hook::detour scr_get_function_reverse_lookup;
|
utilities::hook::detour scr_get_function_reverse_lookup;
|
||||||
utilities::hook::detour cscr_get_function_reverse_lookup;
|
utilities::hook::detour cscr_get_function_reverse_lookup;
|
||||||
@ -1394,7 +1462,7 @@ namespace gsc_funcs
|
|||||||
|
|
||||||
game::GSC_OBJ* obj = info.activeVersion;
|
game::GSC_OBJ* obj = info.activeVersion;
|
||||||
|
|
||||||
if (codepos >= obj->magic + obj->start_data && codepos < obj->magic + obj->start_data + obj->data_length)
|
if (codepos >= obj->magic + obj->cseg_offset && codepos < obj->magic + obj->cseg_offset + obj->cseg_size)
|
||||||
{
|
{
|
||||||
script_obj = obj;
|
script_obj = obj;
|
||||||
break;
|
break;
|
||||||
@ -1461,10 +1529,19 @@ namespace gsc_funcs
|
|||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
void pre_start() override
|
||||||
|
{
|
||||||
|
// enable dev functions
|
||||||
|
enable_dev_func = utilities::json_config::ReadBoolean("gsc", "dev_funcs", false);
|
||||||
|
// enable custom compiled dev blocks
|
||||||
|
enable_dev_blocks = utilities::json_config::ReadBoolean("gsc", "dev_blocks", false);
|
||||||
|
}
|
||||||
|
|
||||||
void post_unpack() override
|
void post_unpack() override
|
||||||
{
|
{
|
||||||
// enable dev functions still available in the game
|
// replace nulled function references
|
||||||
enable_dev_func = utilities::json_config::ReadBoolean("gsc", "dev_funcs", false);
|
reinterpret_cast<game::BO4_BuiltinFunctionDef*>(0x144ED5D90_g)->actionFunc = add_debug_command; // csc
|
||||||
|
reinterpret_cast<game::BO4_BuiltinFunctionDef*>(0x1449BAD60_g)->actionFunc = add_debug_command; // gsc
|
||||||
|
|
||||||
scr_get_function_reverse_lookup.create(0x1433AF8A0_g, scr_get_function_reverse_lookup_stub);
|
scr_get_function_reverse_lookup.create(0x1433AF8A0_g, scr_get_function_reverse_lookup_stub);
|
||||||
cscr_get_function_reverse_lookup.create(0x141F132A0_g, cscr_get_function_reverse_lookup_stub);
|
cscr_get_function_reverse_lookup.create(0x141F132A0_g, cscr_get_function_reverse_lookup_stub);
|
||||||
|
@ -7,6 +7,7 @@ namespace gsc_funcs
|
|||||||
constexpr auto serious_custom_func_name = "SeriousCustom";
|
constexpr auto serious_custom_func_name = "SeriousCustom";
|
||||||
|
|
||||||
extern bool enable_dev_func;
|
extern bool enable_dev_func;
|
||||||
|
extern bool enable_dev_blocks;
|
||||||
|
|
||||||
uint32_t canon_hash(const char* str);
|
uint32_t canon_hash(const char* str);
|
||||||
uint32_t canon_hash_pattern(const char* str);
|
uint32_t canon_hash_pattern(const char* str);
|
||||||
|
@ -6,8 +6,9 @@
|
|||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
#include "definitions/xassets.hpp"
|
|
||||||
#include "definitions/game.hpp"
|
#include "definitions/game.hpp"
|
||||||
|
#include "definitions/xassets.hpp"
|
||||||
|
#include "definitions/scripting.hpp"
|
||||||
|
|
||||||
#include <utilities/io.hpp>
|
#include <utilities/io.hpp>
|
||||||
#include <utilities/hook.hpp>
|
#include <utilities/hook.hpp>
|
||||||
@ -22,6 +23,13 @@ namespace mods {
|
|||||||
std::filesystem::path mod_dir = "project-bo4/mods";
|
std::filesystem::path mod_dir = "project-bo4/mods";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
template<typename T>
|
||||||
|
inline byte* align_ptr(byte* ptr)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<byte*>((reinterpret_cast<uintptr_t>(ptr) + sizeof(T) - 1) & ~(sizeof(T) - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct raw_file
|
struct raw_file
|
||||||
{
|
{
|
||||||
xassets::raw_file_header header{};
|
xassets::raw_file_header header{};
|
||||||
@ -144,6 +152,89 @@ namespace mods {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool load_acts_debug()
|
||||||
|
{
|
||||||
|
if ((data.length() < sizeof(game::GSC_OBJ) + sizeof(game::acts_debug::MAGIC))
|
||||||
|
|| *reinterpret_cast<decltype(game::acts_debug::MAGIC)*>(&data[sizeof(game::GSC_OBJ)]) != game::acts_debug::MAGIC)
|
||||||
|
{
|
||||||
|
return true; // too small to be a debug file
|
||||||
|
}
|
||||||
|
game::acts_debug::GSC_ACTS_DEBUG& dbg = *reinterpret_cast<game::acts_debug::GSC_ACTS_DEBUG*>(data.data() + sizeof(game::GSC_OBJ));
|
||||||
|
|
||||||
|
|
||||||
|
logger::write(logger::LOG_TYPE_DEBUG, "loading acts debug file v%x.%llx", dbg.version, dbg.actsVersion);
|
||||||
|
|
||||||
|
byte* ptr = (byte*)data.data();
|
||||||
|
|
||||||
|
if (dbg.has_feature(game::acts_debug::ADF_STRING))
|
||||||
|
{
|
||||||
|
char* start = data.data();
|
||||||
|
for (uint32_t* strs = dbg.get_strings(ptr); strs != dbg.get_strings_end(ptr); strs++)
|
||||||
|
{
|
||||||
|
const char* val = data.data() + *strs;
|
||||||
|
hashes::add_hash(fnv1a::generate_hash(val), val);
|
||||||
|
hashes::add_hash(gsc_funcs::canon_hash(val), val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbg.has_feature(game::acts_debug::ADF_DETOUR))
|
||||||
|
{
|
||||||
|
for (game::acts_debug::GSC_ACTS_DETOUR* detours = dbg.get_detours(ptr); detours != dbg.get_detours_end(ptr); detours++)
|
||||||
|
{
|
||||||
|
gsc_custom::gsic_detour& detour = gsic.detours.emplace_back();
|
||||||
|
|
||||||
|
detour.fixup_name = 0;
|
||||||
|
detour.replace_namespace = detours->name_space;
|
||||||
|
detour.replace_function = detours->name;
|
||||||
|
detour.fixup_offset = detours->location;
|
||||||
|
detour.fixup_size = detours->size;
|
||||||
|
detour.target_script = detours->script;
|
||||||
|
|
||||||
|
logger::write(logger::LOG_TYPE_DEBUG, std::format(
|
||||||
|
"read detour : namespace_{:x}<script_{:x}>::function_{:x} / offset=0x{:x}+0x{:x}",
|
||||||
|
detour.replace_namespace, detour.target_script, detour.replace_function,
|
||||||
|
detour.fixup_offset, detour.fixup_size
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gsc_funcs::enable_dev_blocks)
|
||||||
|
{
|
||||||
|
if (dbg.has_feature(game::acts_debug::ADF_DEVBLOCK_BEGIN))
|
||||||
|
{
|
||||||
|
if (dbg.devblock_count)
|
||||||
|
{
|
||||||
|
gsic.dev_blocks = true;
|
||||||
|
|
||||||
|
// we need to patch the dev function imports to load them
|
||||||
|
|
||||||
|
game::GSC_OBJ* prime_obj = (game::GSC_OBJ*)data.data();
|
||||||
|
|
||||||
|
game::GSC_IMPORT_ITEM* import_item = prime_obj->get_imports();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < prime_obj->imports_count; i++)
|
||||||
|
{
|
||||||
|
// mark this function for the custom linker
|
||||||
|
if ((import_item->flags & game::GIF_DEV_CALL) != 0)
|
||||||
|
{
|
||||||
|
import_item->flags |= game::GIF_SHIELD_DEV_BLOCK_FUNC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// goto to the next element after the addresses
|
||||||
|
uint32_t* addresses = reinterpret_cast<uint32_t*>(import_item + 1);
|
||||||
|
import_item = reinterpret_cast<game::GSC_IMPORT_ITEM*>(addresses + import_item->num_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint32_t* devblock = dbg.get_devblocks(ptr); devblock != dbg.get_devblocks_end(ptr); devblock++)
|
||||||
|
{
|
||||||
|
byte* base = align_ptr<uint16_t>(ptr + *devblock);
|
||||||
|
*(uint16_t*)base = gsc_custom::shield_devblock_opcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
struct lua_file
|
struct lua_file
|
||||||
{
|
{
|
||||||
@ -427,17 +518,23 @@ namespace mods {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp.gsic.detours.size())
|
|
||||||
{
|
|
||||||
logger::write(logger::LOG_TYPE_DEBUG, std::format("loaded {} detours", tmp.gsic.detours.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tmp.data.length() < sizeof(game::GSC_OBJ) || *reinterpret_cast<uint64_t*>(tmp.data.data()) != gsc_magic)
|
if (tmp.data.length() < sizeof(game::GSC_OBJ) || *reinterpret_cast<uint64_t*>(tmp.data.data()) != gsc_magic)
|
||||||
{
|
{
|
||||||
logger::write(logger::LOG_TYPE_ERROR, std::format("bad scriptparsetree magic in {} for mod {}", spt_path.string(), mod_name));
|
logger::write(logger::LOG_TYPE_ERROR, std::format("bad scriptparsetree magic in {} for mod {}", spt_path.string(), mod_name));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tmp.load_acts_debug())
|
||||||
|
{
|
||||||
|
logger::write(logger::LOG_TYPE_ERROR, std::format("error when reading ACTS DEBUG header of {} for mod {}", spt_path.string(), mod_name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp.gsic.detours.size())
|
||||||
|
{
|
||||||
|
logger::write(logger::LOG_TYPE_DEBUG, std::format("loaded {} detours", tmp.gsic.detours.size()));
|
||||||
|
}
|
||||||
|
|
||||||
// after this point we assume that the GSC file is well formatted
|
// after this point we assume that the GSC file is well formatted
|
||||||
|
|
||||||
game::GSC_OBJ* script_obj = reinterpret_cast<game::GSC_OBJ*>(tmp.data.data());
|
game::GSC_OBJ* script_obj = reinterpret_cast<game::GSC_OBJ*>(tmp.data.data());
|
||||||
|
@ -491,6 +491,7 @@ namespace game
|
|||||||
// Cmd
|
// Cmd
|
||||||
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{ 0x143CDE880_g };
|
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{ 0x143CDE880_g };
|
||||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* buffer)> Cbuf_ExecuteBuffer{ 0x143CDEBE0_g };
|
WEAK symbol<void(int localClientNum, int controllerIndex, const char* buffer)> Cbuf_ExecuteBuffer{ 0x143CDEBE0_g };
|
||||||
|
WEAK symbol<int()> Com_LocalClients_GetPrimary{ 0x142893AF0_g };
|
||||||
|
|
||||||
WEAK symbol<void(BO4_AssetRef_t* cmdName, xcommand_t function, cmd_function_t* allocedCmd)> Cmd_AddCommandInternal{ 0x143CDEE80_g };
|
WEAK symbol<void(BO4_AssetRef_t* cmdName, xcommand_t function, cmd_function_t* allocedCmd)> Cmd_AddCommandInternal{ 0x143CDEE80_g };
|
||||||
WEAK symbol<void()> Cbuf_AddServerText_f{ 0x143CDE870_g };
|
WEAK symbol<void()> Cbuf_AddServerText_f{ 0x143CDE870_g };
|
||||||
@ -569,6 +570,7 @@ namespace game
|
|||||||
WEAK symbol<BuiltinFunction(uint32_t canonId, int* type, int* min_args, int* max_args)> Scr_GetFunction{ 0x1433AF840_g };
|
WEAK symbol<BuiltinFunction(uint32_t canonId, int* type, int* min_args, int* max_args)> Scr_GetFunction{ 0x1433AF840_g };
|
||||||
WEAK symbol<void*(uint32_t canonId, int* type, int* min_args, int* max_args)> CScr_GetMethod{ 0x141F13650_g };
|
WEAK symbol<void*(uint32_t canonId, int* type, int* min_args, int* max_args)> CScr_GetMethod{ 0x141F13650_g };
|
||||||
WEAK symbol<void*(uint32_t canonId, int* type, int* min_args, int* max_args)> Scr_GetMethod{ 0x1433AFC20_g };
|
WEAK symbol<void*(uint32_t canonId, int* type, int* min_args, int* max_args)> Scr_GetMethod{ 0x1433AFC20_g };
|
||||||
|
WEAK symbol<void(game::scriptInstance_t inst, byte* codepos, const char** scriptname, int32_t* sloc, int32_t* crc, int32_t* vm)> Scr_GetGscExportInfo{ 0x142748550_g };
|
||||||
|
|
||||||
WEAK symbol<void(uint64_t code, scriptInstance_t inst, char* unused, bool terminal)> ScrVm_Error{ 0x142770330_g };
|
WEAK symbol<void(uint64_t code, scriptInstance_t inst, char* unused, bool terminal)> ScrVm_Error{ 0x142770330_g };
|
||||||
WEAK symbol<BO4_scrVarPub> scrVarPub{ 0x148307880_g };
|
WEAK symbol<BO4_scrVarPub> scrVarPub{ 0x148307880_g };
|
||||||
|
@ -34,7 +34,7 @@ namespace game
|
|||||||
int32_t include_offset;
|
int32_t include_offset;
|
||||||
uint16_t string_count;
|
uint16_t string_count;
|
||||||
uint16_t exports_count;
|
uint16_t exports_count;
|
||||||
int32_t start_data;
|
int32_t cseg_offset;
|
||||||
int32_t string_offset;
|
int32_t string_offset;
|
||||||
int16_t imports_count;
|
int16_t imports_count;
|
||||||
uint16_t fixup_count;
|
uint16_t fixup_count;
|
||||||
@ -48,7 +48,7 @@ namespace game
|
|||||||
int32_t script_size;
|
int32_t script_size;
|
||||||
int32_t requires_implements_offset;
|
int32_t requires_implements_offset;
|
||||||
int32_t ukn50;
|
int32_t ukn50;
|
||||||
int32_t data_length;
|
int32_t cseg_size;
|
||||||
uint16_t include_count;
|
uint16_t include_count;
|
||||||
byte ukn5a;
|
byte ukn5a;
|
||||||
byte requires_implements_count;
|
byte requires_implements_count;
|
||||||
@ -283,4 +283,157 @@ namespace game
|
|||||||
int refCount;
|
int refCount;
|
||||||
uint32_t groupId;
|
uint32_t groupId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum GSC_EXPORT_FLAGS : byte
|
||||||
|
{
|
||||||
|
GEF_LINKED = 0x01,
|
||||||
|
GEF_AUTOEXEC = 0x02,
|
||||||
|
GEF_PRIVATE = 0x04,
|
||||||
|
GEF_CLASS_MEMBER = 0x08,
|
||||||
|
GEF_CLASS_DESTRUCTOR = 0x10,
|
||||||
|
GEF_VE = 0x20,
|
||||||
|
GEF_EVENT = 0x40,
|
||||||
|
GEF_CLASS_LINKED = 0x80,
|
||||||
|
GEF_CLASS_VTABLE = 0x86
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GSC_IMPORT_FLAGS : byte
|
||||||
|
{
|
||||||
|
GIF_FUNC_METHOD = 0x1,
|
||||||
|
GIF_FUNCTION = 0x2,
|
||||||
|
GIF_FUNCTION_THREAD = 0x3,
|
||||||
|
GIF_FUNCTION_CHILDTHREAD = 0x4,
|
||||||
|
GIF_METHOD = 0x5,
|
||||||
|
GIF_METHOD_THREAD = 0x6,
|
||||||
|
GIF_METHOD_CHILDTHREAD = 0x7,
|
||||||
|
GIF_CALLTYPE_MASK = 0xF,
|
||||||
|
GIF_DEV_CALL = 0x10,
|
||||||
|
GIF_GET_CALL = 0x20,
|
||||||
|
|
||||||
|
GIF_SHIELD_DEV_BLOCK_FUNC = 0x80,
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace acts_debug
|
||||||
|
{
|
||||||
|
constexpr uint64_t MAGIC = 0x0d0a42444124;
|
||||||
|
constexpr byte CURRENT_VERSION = 0x12;
|
||||||
|
|
||||||
|
enum GSC_ACTS_DEBUG_FEATURES : byte
|
||||||
|
{
|
||||||
|
ADF_STRING = 0x10,
|
||||||
|
ADF_DETOUR = 0x11,
|
||||||
|
ADF_DEVBLOCK_BEGIN = 0x12,
|
||||||
|
ADF_LAZYLINK = 0x12,
|
||||||
|
ADF_CRC_LOC = 0x13,
|
||||||
|
ADF_DEVSTRING = 0x14,
|
||||||
|
ADF_LINES = 0x15,
|
||||||
|
ADF_FILES = 0x15,
|
||||||
|
ADF_FLAGS = 0x15,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GSC_ACTS_DEBUG_FLAGS : uint32_t
|
||||||
|
{
|
||||||
|
ADFG_OBFUSCATED = 1 << 0,
|
||||||
|
ADFG_DEBUG = 1 << 1,
|
||||||
|
ADFG_CLIENT = 1 << 2,
|
||||||
|
ADFG_PLATFORM_SHIFT = 3,
|
||||||
|
ADFG_PLATFORM_MASK = 0xF << ADFG_PLATFORM_SHIFT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GSC_ACTS_DETOUR
|
||||||
|
{
|
||||||
|
uint64_t name_space;
|
||||||
|
uint64_t name;
|
||||||
|
uint64_t script;
|
||||||
|
uint32_t location;
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GSC_ACTS_LAZYLINK
|
||||||
|
{
|
||||||
|
uint64_t name_space;
|
||||||
|
uint64_t name;
|
||||||
|
uint64_t script;
|
||||||
|
uint32_t num_address;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GSC_ACTS_DEVSTRING
|
||||||
|
{
|
||||||
|
uint32_t string;
|
||||||
|
uint32_t num_address;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GSC_ACTS_LINES
|
||||||
|
{
|
||||||
|
uint32_t start;
|
||||||
|
uint32_t end;
|
||||||
|
size_t lineNum;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GSC_ACTS_FILES
|
||||||
|
{
|
||||||
|
uint32_t filename;
|
||||||
|
size_t lineStart;
|
||||||
|
size_t lineEnd;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct GSC_ACTS_DEBUG
|
||||||
|
{
|
||||||
|
byte magic[sizeof(MAGIC)];
|
||||||
|
byte version;
|
||||||
|
uint32_t flags;
|
||||||
|
uint64_t actsVersion;
|
||||||
|
uint32_t strings_offset{};
|
||||||
|
uint32_t strings_count{};
|
||||||
|
uint32_t detour_offset{};
|
||||||
|
uint32_t detour_count{};
|
||||||
|
uint32_t devblock_offset{};
|
||||||
|
uint32_t devblock_count{};
|
||||||
|
uint32_t lazylink_offset{};
|
||||||
|
uint32_t lazylink_count{};
|
||||||
|
uint32_t crc_offset{};
|
||||||
|
uint32_t devstrings_offset{};
|
||||||
|
uint32_t devstrings_count{};
|
||||||
|
uint32_t lines_offset{};
|
||||||
|
uint32_t lines_count{};
|
||||||
|
uint32_t files_offset{};
|
||||||
|
uint32_t files_count{};
|
||||||
|
|
||||||
|
constexpr bool has_feature(GSC_ACTS_DEBUG_FEATURES feature) const
|
||||||
|
{
|
||||||
|
return version >= feature;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline GSC_ACTS_DETOUR* get_detours(byte* start)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<GSC_ACTS_DETOUR*>(start + detour_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline GSC_ACTS_DETOUR* get_detours_end(byte* start)
|
||||||
|
{
|
||||||
|
return get_detours(start) + detour_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t* get_devblocks(byte* start)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<uint32_t*>(start + devblock_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t* get_devblocks_end(byte* start)
|
||||||
|
{
|
||||||
|
return get_devblocks(start) + devblock_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t* get_strings(byte* start)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<uint32_t*>(start + strings_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t* get_strings_end(byte* start)
|
||||||
|
{
|
||||||
|
return get_strings(start) + strings_count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
@ -6529,7 +6529,127 @@ namespace variables
|
|||||||
"quit",
|
"quit",
|
||||||
"Shutdown the Game [Com_Quit_f]",
|
"Shutdown the Game [Com_Quit_f]",
|
||||||
0x1DEE6107B26F8BB6
|
0x1DEE6107B26F8BB6
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"gts",
|
||||||
|
"Set gametype setting, Usage: gts <path> [<value>]",
|
||||||
|
0x55155818fad5c0df
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gametype_setting",
|
||||||
|
"Set gametype setting, Usage: gametype_setting <path> [<value>]",
|
||||||
|
0x46fb4ee1a7c51bcc
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"set",
|
||||||
|
"Set dvar value, Usage: set <variable> <value>",
|
||||||
|
0x23b87195ce20e23
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setdvartotime",
|
||||||
|
"Set dvar value to time, Usage: setdvartotime <variable>",
|
||||||
|
0x1cc7ab7a4c8a4e7a
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reset",
|
||||||
|
"Reset dvar value, Usage: reset <variable>",
|
||||||
|
0xf136ff2c086b760
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lobby_errorshutdown",
|
||||||
|
"Shutdown lobby with error",
|
||||||
|
0x6a3a118a63f519a4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lobby_reload",
|
||||||
|
"Reload lobby",
|
||||||
|
0x67ac003bb1f9cfc9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"map",
|
||||||
|
"Load map, Usage: map <map>",
|
||||||
|
0x80f5919176d2d91
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"devmap",
|
||||||
|
"Load map in dev mode, Usage: devmap <map>",
|
||||||
|
0x7e4ea29057c5f962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gametype",
|
||||||
|
"Set the gametype, Usage: gametype <gametype>",
|
||||||
|
0x6792c1f90c3a5c7f
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"exec",
|
||||||
|
"Exec file, Usage: exec <config>",
|
||||||
|
0x67c75e608ea39b5c
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"disconnect",
|
||||||
|
"Disconnect from server",
|
||||||
|
0x5926cb82738c446b
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reconnect",
|
||||||
|
"Reconnect to localhost",
|
||||||
|
0x31ab1e1fcc28efe6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hostmigration_start",
|
||||||
|
"Start host migration",
|
||||||
|
0x6bf6f7867a2b3202
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setperk",
|
||||||
|
"Set a perk, Usage: setperk <perk>",
|
||||||
|
0x262eebca5ebf646b
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"killserver",
|
||||||
|
"Shutdown the server",
|
||||||
|
0x64fb81c94800815e
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"switchmaps",
|
||||||
|
"Switch now to the map",
|
||||||
|
0x182ae2b46edd075a
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"msload",
|
||||||
|
"Start loading map switch, Usage: msload <map>",
|
||||||
|
0xddcb9fb38629fcd
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mspreload",
|
||||||
|
"Start loading map switch, Usage: mspreload <map>",
|
||||||
|
0x291b1ea6866a7cfa
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fast_restart",
|
||||||
|
"Fast restart",
|
||||||
|
0x506e7e29c6d674bb
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"full_restart",
|
||||||
|
"Full restart",
|
||||||
|
0x7cba6d063fbd73d0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"clientkick_for_reason",
|
||||||
|
"Kick client with a reason, Usage: clientkick_for_reason <client number> <reason loc string>",
|
||||||
|
0x7086b005b6eb2871
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"clientkick",
|
||||||
|
"Kick client, Usage: clientkick <client number>",
|
||||||
|
0x3e30e01add5a0b78
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"clearpsdata",
|
||||||
|
"Clear PS data",
|
||||||
|
0x2e8816c78dd363c7
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<const char*> get_dvars_list()
|
std::vector<const char*> get_dvars_list()
|
||||||
|
Loading…
Reference in New Issue
Block a user