From 77e5d392bf69ad1807f83a8db9b0291595ba8226 Mon Sep 17 00:00:00 2001 From: Thomas Jones Date: Sat, 29 Jun 2024 09:47:43 +1000 Subject: [PATCH] Re-format existing files, correcting method names as I go --- commands.c | 66 +- dllmain.c | 205 ++-- hooks.c | 216 ++-- maps_parser.c | 171 +-- maps_parser.h | 22 +- misc.c | 61 +- patches.c | 59 +- patterns.h | 33 +- pyminqlxtended.h | 22 +- python_dispatchers.c | 229 ++-- python_embed.c | 1385 ++++++++++++------------- quake_common.h | 2349 +++++++++++++++++++++--------------------- simple_hook.c | 52 +- simple_hook.h | 2 +- trampoline.c | 142 ++- trampoline.h | 77 +- 16 files changed, 2575 insertions(+), 2516 deletions(-) diff --git a/commands.c b/commands.c index 0e244fb..22841ee 100644 --- a/commands.c +++ b/commands.c @@ -2,12 +2,12 @@ #define _GNU_SOURCE #endif -#include -#include #include +#include +#include -#include "quake_common.h" #include "common.h" +#include "quake_common.h" #ifndef NOPY #include "pyminqlxtended.h" @@ -26,7 +26,7 @@ void __cdecl RegularPrint(void) { } void __cdecl Slap(void) { - int dmg = 0; + int dmg = 0; int argc = Cmd_Argc(); if (argc < 2) { Com_Printf("Usage: %s [damage]\n", Cmd_Argv(0)); @@ -36,27 +36,29 @@ void __cdecl Slap(void) { if (i < 0 || i > sv_maxclients->integer) { Com_Printf("client_id must be a number between 0 and %d\n.", sv_maxclients->integer); return; - } - else if (argc > 2) + } else if (argc > 2) { dmg = atoi(Cmd_Argv(2)); - + } + if (g_entities[i].inuse && g_entities[i].health > 0) { Com_Printf("Slapping...\n"); - if (dmg) + if (dmg) { SV_SendServerCommand(NULL, "print \"%s^7 was slapped for %d damage!\n\"\n", svs->clients[i].name, dmg); - else + } else { SV_SendServerCommand(NULL, "print \"%s^7 was slapped!\n\"\n", svs->clients[i].name); + } g_entities[i].client->ps.velocity[0] += RandomFloatWithNegative() * 200.0f; g_entities[i].client->ps.velocity[1] += RandomFloatWithNegative() * 200.0f; g_entities[i].client->ps.velocity[2] += 300.0f; g_entities[i].health -= dmg; // Will be 0 if argument wasn't passed. - if (g_entities[i].health > 0) + if (g_entities[i].health > 0) { G_AddEvent(&g_entities[i], EV_PAIN, 99); // 99 health = pain100_1.wav - else + } else { G_AddEvent(&g_entities[i], EV_DEATH1, g_entities[i].s.number); - } - else + } + } else { Com_Printf("The player is currently not active.\n"); + } } void __cdecl Slay(void) { @@ -69,16 +71,15 @@ void __cdecl Slay(void) { if (i < 0 || i > sv_maxclients->integer) { Com_Printf("client_id must be a number between 0 and %d\n.", sv_maxclients->integer); return; - } - else if (g_entities[i].inuse && g_entities[i].health > 0) { + } else if (g_entities[i].inuse && g_entities[i].health > 0) { Com_Printf("Slaying player...\n"); SV_SendServerCommand(NULL, "print \"%s^7 was slain!\n\"\n", svs->clients[i].name); DebugPrint("Slaying '%s'!\n", svs->clients[i].name); - g_entities[i].health = -40; - G_AddEvent(&g_entities[i], EV_GIB_PLAYER, g_entities[i].s.number); - } - else + g_entities[i].health = -40; + G_AddEvent(&g_entities[i], EV_GIB_PLAYER, g_entities[i].s.number); + } else { Com_Printf("The player is currently not active.\n"); + } } #ifndef NOPY @@ -89,25 +90,26 @@ void __cdecl PyRcon(void) { } void __cdecl PyCommand(void) { - if (!custom_command_handler) { - return; // No registered handler. - } - PyGILState_STATE gstate = PyGILState_Ensure(); + if (!custom_command_handler) { + return; // No registered handler. + } + PyGILState_STATE gstate = PyGILState_Ensure(); - PyObject* result = PyObject_CallFunction(custom_command_handler, "s", Cmd_Args()); - if (result == Py_False) { - Com_Printf("The command failed to be executed. pyminqlxtended found no handler.\n"); - } + PyObject* result = PyObject_CallFunction(custom_command_handler, "s", Cmd_Args()); + if (result == Py_False) { + Com_Printf("The command failed to be executed. pyminqlxtended found no handler.\n"); + } - Py_XDECREF(result); - PyGILState_Release(gstate); + Py_XDECREF(result); + PyGILState_Release(gstate); } void __cdecl RestartPython(void) { Com_Printf("Restarting Python...\n"); - if (PyMinqlx_IsInitialized()) - PyMinqlx_Finalize(); - PyMinqlx_Initialize(); + if (PyMinqlxtended_IsInitialized()) { + PyMinqlxtended_Finalize(); + } + PyMinqlxtended_Initialize(); // minqlxtended initializes after the first new game starts, but since the game already // start, we manually trigger the event to make it initialize properly. NewGameDispatcher(0); diff --git a/dllmain.c b/dllmain.c index 6a8a4b5..d8d82ab 100644 --- a/dllmain.c +++ b/dllmain.c @@ -1,15 +1,15 @@ -#include -#include +#include #include #include +#include +#include #include -#include #include #include "common.h" -#include "quake_common.h" -#include "patterns.h" #include "maps_parser.h" +#include "patterns.h" +#include "quake_common.h" #ifndef NOPY #include "pyminqlxtended.h" #endif @@ -19,16 +19,16 @@ extern char* __progname; #if defined(__x86_64__) || defined(_M_X64) -const char qzeroded[] = "qzeroded.x64"; +const char qzeroded[] = "qzeroded.x64"; const char qagame_name[] = "qagamex64.so"; #elif defined(__i386) || defined(_M_IX86) -const char qzeroded[] = "qzeroded.x86"; +const char qzeroded[] = "qzeroded.x86"; const char qagame_name[] = "qagamei386.so"; #endif // Global variables. int common_initialized = 0; -int cvars_initialized = 0; +int cvars_initialized = 0; serverStatic_t* svs; Com_Printf_ptr Com_Printf; @@ -80,94 +80,106 @@ cvar_t* sv_maxclients; // TODO: Make it output everything to a file too. void DebugPrint(const char* fmt, ...) { va_list args; - va_start(args, fmt); + va_start(args, fmt); printf(DEBUG_PRINT_PREFIX); vprintf(fmt, args); - va_end(args); + va_end(args); } // TODO: Make it output everything to a file too. void DebugError(const char* fmt, const char* file, int line, const char* func, ...) { va_list args; - va_start(args, func); + va_start(args, func); fprintf(stderr, DEBUG_ERROR_FORMAT, file, line, func); vfprintf(stderr, fmt, args); - va_end(args); + va_end(args); } -#define STATIC_SEARCH(x, p, m) x = (x ## _ptr) PatternSearchModule(&module, p, m); if (x == NULL) { DebugPrint("ERROR: Unable to find " #x ".\n"); failed = 1;} else DebugPrint(#x ": %p\n", x) +#define STATIC_SEARCH(x, p, m) \ + x = (x##_ptr)PatternSearchModule(&module, p, m); \ + if (x == NULL) { \ + DebugPrint("ERROR: Unable to find " #x ".\n"); \ + failed = 1; \ + } else \ + DebugPrint(#x ": %p\n", x) static void SearchFunctions(void) { - int failed = 0; - module_info_t module; - strcpy(module.name, qzeroded); - int res = GetModuleInfo(&module); - if (res <= 0) { - DebugError("GetModuleInfo() returned %d.\n", __FILE__, __LINE__, __func__, res); - failed = 1; - } - - DebugPrint("Searching for necessary functions...\n"); - - STATIC_SEARCH(Com_Printf, PTRN_COM_PRINTF, MASK_COM_PRINTF); - STATIC_SEARCH(Cmd_AddCommand, PTRN_CMD_ADDCOMMAND, MASK_CMD_ADDCOMMAND); - STATIC_SEARCH(Cmd_Args, PTRN_CMD_ARGS, MASK_CMD_ARGS); - STATIC_SEARCH(Cmd_Argv, PTRN_CMD_ARGV, MASK_CMD_ARGV); - STATIC_SEARCH(Cmd_TokenizeString, PTRN_CMD_TOKENIZESTRING, MASK_CMD_TOKENIZESTRING); - STATIC_SEARCH(Cbuf_ExecuteText, PTRN_CBUF_EXECUTETEXT, MASK_CBUF_EXECUTETEXT); - STATIC_SEARCH(Cvar_FindVar, PTRN_CVAR_FINDVAR, MASK_CVAR_FINDVAR); - STATIC_SEARCH(Cvar_Get, PTRN_CVAR_GET, MASK_CVAR_GET); - STATIC_SEARCH(Cvar_GetLimit, PTRN_CVAR_GETLIMIT, MASK_CVAR_GETLIMIT); - STATIC_SEARCH(Cvar_Set2, PTRN_CVAR_SET2, MASK_CVAR_SET2); - STATIC_SEARCH(SV_SendServerCommand, PTRN_SV_SENDSERVERCOMMAND, MASK_SV_SENDSERVERCOMMAND); - STATIC_SEARCH(SV_ExecuteClientCommand, PTRN_SV_EXECUTECLIENTCOMMAND, MASK_SV_EXECUTECLIENTCOMMAND); - STATIC_SEARCH(SV_Shutdown, PTRN_SV_SHUTDOWN, MASK_SV_SHUTDOWN); - STATIC_SEARCH(SV_Map_f, PTRN_SV_MAP_F, MASK_SV_MAP_F); - STATIC_SEARCH(SV_ClientEnterWorld, PTRN_SV_CLIENTENTERWORLD, MASK_SV_CLIENTENTERWORLD); - STATIC_SEARCH(SV_SetConfigstring, PTRN_SV_SETCONFIGSTRING, MASK_SV_SETCONFIGSTRING); - STATIC_SEARCH(SV_GetConfigstring, PTRN_SV_GETCONFIGSTRING, MASK_SV_GETCONFIGSTRING); - STATIC_SEARCH(SV_DropClient, PTRN_SV_DROPCLIENT, MASK_SV_DROPCLIENT); - STATIC_SEARCH(Sys_SetModuleOffset, PTRN_SYS_SETMODULEOFFSET, MASK_SYS_SETMODULEOFFSET); - STATIC_SEARCH(SV_SpawnServer, PTRN_SV_SPAWNSERVER, MASK_SV_SPAWNSERVER); - STATIC_SEARCH(Cmd_ExecuteString, PTRN_CMD_EXECUTESTRING, MASK_CMD_EXECUTESTRING); - - // Cmd_Argc is really small, making it hard to search for, so we use a reference to it instead. - if (SV_Map_f != NULL) { - Cmd_Argc = (Cmd_Argc_ptr)(*(int32_t*)OFFSET_RELP_CMD_ARGC + OFFSET_RELP_CMD_ARGC + 4); - DebugPrint("Cmd_Argc: %p\n", Cmd_Argc); - } - - if (failed) { - DebugPrint("Exiting.\n"); - exit(1); - } + int failed = 0; + module_info_t module; + strcpy(module.name, qzeroded); + int res = GetModuleInfo(&module); + if (res <= 0) { + DebugError("GetModuleInfo() returned %d.\n", __FILE__, __LINE__, __func__, res); + failed = 1; + } + + DebugPrint("Searching for necessary functions...\n"); + + STATIC_SEARCH(Com_Printf, PTRN_COM_PRINTF, MASK_COM_PRINTF); + STATIC_SEARCH(Cmd_AddCommand, PTRN_CMD_ADDCOMMAND, MASK_CMD_ADDCOMMAND); + STATIC_SEARCH(Cmd_Args, PTRN_CMD_ARGS, MASK_CMD_ARGS); + STATIC_SEARCH(Cmd_Argv, PTRN_CMD_ARGV, MASK_CMD_ARGV); + STATIC_SEARCH(Cmd_TokenizeString, PTRN_CMD_TOKENIZESTRING, MASK_CMD_TOKENIZESTRING); + STATIC_SEARCH(Cbuf_ExecuteText, PTRN_CBUF_EXECUTETEXT, MASK_CBUF_EXECUTETEXT); + STATIC_SEARCH(Cvar_FindVar, PTRN_CVAR_FINDVAR, MASK_CVAR_FINDVAR); + STATIC_SEARCH(Cvar_Get, PTRN_CVAR_GET, MASK_CVAR_GET); + STATIC_SEARCH(Cvar_GetLimit, PTRN_CVAR_GETLIMIT, MASK_CVAR_GETLIMIT); + STATIC_SEARCH(Cvar_Set2, PTRN_CVAR_SET2, MASK_CVAR_SET2); + STATIC_SEARCH(SV_SendServerCommand, PTRN_SV_SENDSERVERCOMMAND, MASK_SV_SENDSERVERCOMMAND); + STATIC_SEARCH(SV_ExecuteClientCommand, PTRN_SV_EXECUTECLIENTCOMMAND, MASK_SV_EXECUTECLIENTCOMMAND); + STATIC_SEARCH(SV_Shutdown, PTRN_SV_SHUTDOWN, MASK_SV_SHUTDOWN); + STATIC_SEARCH(SV_Map_f, PTRN_SV_MAP_F, MASK_SV_MAP_F); + STATIC_SEARCH(SV_ClientEnterWorld, PTRN_SV_CLIENTENTERWORLD, MASK_SV_CLIENTENTERWORLD); + STATIC_SEARCH(SV_SetConfigstring, PTRN_SV_SETCONFIGSTRING, MASK_SV_SETCONFIGSTRING); + STATIC_SEARCH(SV_GetConfigstring, PTRN_SV_GETCONFIGSTRING, MASK_SV_GETCONFIGSTRING); + STATIC_SEARCH(SV_DropClient, PTRN_SV_DROPCLIENT, MASK_SV_DROPCLIENT); + STATIC_SEARCH(Sys_SetModuleOffset, PTRN_SYS_SETMODULEOFFSET, MASK_SYS_SETMODULEOFFSET); + STATIC_SEARCH(SV_SpawnServer, PTRN_SV_SPAWNSERVER, MASK_SV_SPAWNSERVER); + STATIC_SEARCH(Cmd_ExecuteString, PTRN_CMD_EXECUTESTRING, MASK_CMD_EXECUTESTRING); + + // Cmd_Argc is really small, making it hard to search for, so we use a reference to it instead. + if (SV_Map_f != NULL) { + Cmd_Argc = (Cmd_Argc_ptr)(*(int32_t*)OFFSET_RELP_CMD_ARGC + OFFSET_RELP_CMD_ARGC + 4); + DebugPrint("Cmd_Argc: %p\n", Cmd_Argc); + } + + if (failed) { + DebugPrint("Exiting.\n"); + exit(1); + } } -#define VM_SEARCH(x, p, m) x = (x ## _ptr) PatternSearch((void*)((pint)qagame + 0xB000), 0xB0000, p, m); if (x == NULL) { DebugPrint("ERROR: Unable to find " #x ".\n"); failed = 1;} else DebugPrint(#x ": %p\n", x) +#define VM_SEARCH(x, p, m) \ + x = (x##_ptr)PatternSearch((void*)((pint)qagame + 0xB000), 0xB0000, p, m); \ + if (x == NULL) { \ + DebugPrint("ERROR: Unable to find " #x ".\n"); \ + failed = 1; \ + } else \ + DebugPrint(#x ": %p\n", x) // NOTE: Some functions can easily and reliably be found on the VM_Call table instead. void SearchVmFunctions(void) { - int failed = 0; - - // For some reason, the module doesn't show up when reading /proc/self/maps. - // Perhaps this needs to be called later? In any case, we know exactly where - // the module is mapped, so I think this is fine. If it ever breaks, it'll - // be trivial to fix. - VM_SEARCH(G_AddEvent, PTRN_G_ADDEVENT, MASK_G_ADDEVENT); - VM_SEARCH(CheckPrivileges, PTRN_CHECKPRIVILEGES, MASK_CHECKPRIVILEGES); - VM_SEARCH(ClientConnect, PTRN_CLIENTCONNECT, MASK_CLIENTCONNECT); - VM_SEARCH(ClientSpawn, PTRN_CLIENTSPAWN, MASK_CLIENTSPAWN); - VM_SEARCH(G_Damage, PTRN_G_DAMAGE, MASK_G_DAMAGE); - VM_SEARCH(Touch_Item, PTRN_TOUCH_ITEM, MASK_TOUCH_ITEM); - VM_SEARCH(LaunchItem, PTRN_LAUNCHITEM, MASK_LAUNCHITEM); - VM_SEARCH(Drop_Item, PTRN_DROP_ITEM, MASK_DROP_ITEM); - VM_SEARCH(G_StartKamikaze, PTRN_G_STARTKAMIKAZE, MASK_G_STARTKAMIKAZE); - VM_SEARCH(G_FreeEntity, PTRN_G_FREEENTITY, MASK_G_FREEENTITY); - - if (failed) { - DebugPrint("Exiting.\n"); - exit(1); - } + int failed = 0; + + // For some reason, the module doesn't show up when reading /proc/self/maps. + // Perhaps this needs to be called later? In any case, we know exactly where + // the module is mapped, so I think this is fine. If it ever breaks, it'll + // be trivial to fix. + VM_SEARCH(G_AddEvent, PTRN_G_ADDEVENT, MASK_G_ADDEVENT); + VM_SEARCH(CheckPrivileges, PTRN_CHECKPRIVILEGES, MASK_CHECKPRIVILEGES); + VM_SEARCH(ClientConnect, PTRN_CLIENTCONNECT, MASK_CLIENTCONNECT); + VM_SEARCH(ClientSpawn, PTRN_CLIENTSPAWN, MASK_CLIENTSPAWN); + VM_SEARCH(G_Damage, PTRN_G_DAMAGE, MASK_G_DAMAGE); + VM_SEARCH(Touch_Item, PTRN_TOUCH_ITEM, MASK_TOUCH_ITEM); + VM_SEARCH(LaunchItem, PTRN_LAUNCHITEM, MASK_LAUNCHITEM); + VM_SEARCH(Drop_Item, PTRN_DROP_ITEM, MASK_DROP_ITEM); + VM_SEARCH(G_StartKamikaze, PTRN_G_STARTKAMIKAZE, MASK_G_STARTKAMIKAZE); + VM_SEARCH(G_FreeEntity, PTRN_G_FREEENTITY, MASK_G_FREEENTITY); + + if (failed) { + DebugPrint("Exiting.\n"); + exit(1); + } } // Currently called by My_Cmd_AddCommand(), since it's called at a point where we @@ -175,7 +187,7 @@ void SearchVmFunctions(void) { // point, since functions like Cmd_AddCommand need initialization first. void InitializeStatic(void) { DebugPrint("Initializing...\n"); - + // Set the seed for our RNG. srand(time(NULL)); @@ -189,10 +201,10 @@ void InitializeStatic(void) { Cmd_AddCommand("pycmd", PyCommand); Cmd_AddCommand("pyrestart", RestartPython); #endif - + #ifndef NOPY - // Initialize Python and run the main script. - PyMinqlx_InitStatus_t res = PyMinqlx_Initialize(); + // Initialize Python and run the main script. + PyMinqlxtended_InitStatus_t res = PyMinqlxtended_Initialize(); if (res != PYM_SUCCESS) { DebugPrint("Python initialization failed: %d\n", res); exit(1); @@ -207,34 +219,35 @@ void InitializeStatic(void) { void InitializeVm(void) { DebugPrint("Initializing VM pointers...\n"); #if defined(__x86_64__) || defined(_M_X64) - g_entities = (gentity_t*)(*(int32_t*)OFFSET_RELP_G_ENTITIES + OFFSET_RELP_G_ENTITIES + 4); - level = (level_locals_t*)(*(int32_t*)OFFSET_RELP_LEVEL + OFFSET_RELP_LEVEL + 4); + g_entities = (gentity_t*)(*(int32_t*)OFFSET_RELP_G_ENTITIES + OFFSET_RELP_G_ENTITIES + 4); + level = (level_locals_t*)(*(int32_t*)OFFSET_RELP_LEVEL + OFFSET_RELP_LEVEL + 4); bg_itemlist = (gitem_t*)*(int64_t*)((*(int32_t*)OFFSET_RELP_BG_ITEMLIST + OFFSET_RELP_BG_ITEMLIST + 4)); #elif defined(__i386) || defined(_M_IX86) - g_entities = (gentity_t*)(*(int32_t*)OFFSET_RELP_G_ENTITIES + 0xCEFF4 + (pint)qagame); - level = (level_locals_t*)(*(int32_t*)OFFSET_RELP_LEVEL + 0xCEFF4 + (pint)qagame); + g_entities = (gentity_t*)(*(int32_t*)OFFSET_RELP_G_ENTITIES + 0xCEFF4 + (pint)qagame); + level = (level_locals_t*)(*(int32_t*)OFFSET_RELP_LEVEL + 0xCEFF4 + (pint)qagame); bg_itemlist = (gitem_t*)*(int32_t*)((*(int32_t*)OFFSET_RELP_BG_ITEMLIST + 0xCEFF4 + (pint)qagame)); #endif - for (bg_numItems = 1; bg_itemlist[ bg_numItems ].classname; bg_numItems++); + for (bg_numItems = 1; bg_itemlist[bg_numItems].classname; bg_numItems++) + ; } // Called after the game is initialized. void InitializeCvars(void) { sv_maxclients = Cvar_FindVar("sv_maxclients"); - + cvars_initialized = 1; } -__attribute__((constructor)) -void EntryPoint(void) { - if (strcmp(__progname, qzeroded)) - return; +__attribute__((constructor)) void EntryPoint(void) { + if (strcmp(__progname, qzeroded)) { + return; + } - SearchFunctions(); + SearchFunctions(); - // Initialize some key structure pointers before hooking, since we - // might use some of the functions that could be hooked later to - // get the pointer, such as SV_SetConfigstring. + // Initialize some key structure pointers before hooking, since we + // might use some of the functions that could be hooked later to + // get the pointer, such as SV_SetConfigstring. #if defined(__x86_64__) || defined(_M_X64) // 32-bit pointer. intptr_t added to suppress warning about the casting. svs = (serverStatic_t*)(intptr_t)(*(uint32_t*)OFFSET_PP_SVS); diff --git a/hooks.c b/hooks.c index 51bb7cc..67a60f6 100644 --- a/hooks.c +++ b/hooks.c @@ -1,16 +1,16 @@ #define _GNU_SOURCE #define __STDC_FORMAT_MACROS -#include -#include -#include #include +#include +#include +#include -#include "patterns.h" #include "common.h" +#include "patches.h" +#include "patterns.h" #include "quake_common.h" #include "simple_hook.h" -#include "patches.h" #ifndef NOPY #include "pyminqlxtended.h" @@ -24,8 +24,10 @@ qboolean skipFrameDispatcher; static void SetTag(void); void __cdecl My_Cmd_AddCommand(char* cmd, void* func) { - if (!common_initialized) InitializeStatic(); - + if (!common_initialized) { + InitializeStatic(); + } + Cmd_AddCommand(cmd, func); } @@ -35,28 +37,27 @@ void __cdecl My_Sys_SetModuleOffset(char* moduleName, void* offset) { // Despite the name, it's not the actual module, but vmMain. // We use dlinfo to get the base of the module so we can properly // initialize all the pointers relative to the base. - qagame_dllentry = offset; + qagame_dllentry = offset; Dl_info dlinfo; int res = dladdr(offset, &dlinfo); if (!res) { DebugError("dladdr() failed.\n", __FILE__, __LINE__, __func__); qagame = NULL; - } - else { + } else { qagame = dlinfo.dli_fbase; } DebugPrint("Got qagame: %#010x\n", qagame); - } - else + } else { DebugPrint("Unknown module: %s\n", moduleName); - + } + Sys_SetModuleOffset(moduleName, offset); if (common_initialized) { - SearchVmFunctions(); - HookVm(); - InitializeVm(); - patch_vm(); + SearchVmFunctions(); + HookVm(); + InitializeVm(); + patch_vm(); } } @@ -69,55 +70,59 @@ void __cdecl My_G_InitGame(int levelTime, int randomSeed, int restart) { InitializeCvars(); #ifndef NOPY - if (restart) - NewGameDispatcher(restart); + if (restart) { + NewGameDispatcher(restart); + } #endif } // USED FOR PYTHON #ifndef NOPY -void __cdecl My_SV_ExecuteClientCommand(client_t *cl, char *s, qboolean clientOK) { +void __cdecl My_SV_ExecuteClientCommand(client_t* cl, char* s, qboolean clientOK) { char* res = s; if (clientOK && cl->gentity) { res = ClientCommandDispatcher(cl - svs->clients, s); - if (!res) + if (!res) { return; + } } SV_ExecuteClientCommand(cl, res, clientOK); } void __cdecl My_SV_SendServerCommand(client_t* cl, char* fmt, ...) { - va_list argptr; - char buffer[MAX_MSGLEN]; + va_list argptr; + char buffer[MAX_MSGLEN]; - va_start(argptr, fmt); - vsnprintf((char *)buffer, sizeof(buffer), fmt, argptr); - va_end(argptr); + va_start(argptr, fmt); + vsnprintf((char*)buffer, sizeof(buffer), fmt, argptr); + va_end(argptr); char* res = buffer; - if (cl && cl->gentity) - res = ServerCommandDispatcher(cl - svs->clients, buffer); - else if (cl == NULL) - res = ServerCommandDispatcher(-1, buffer); + if (cl && cl->gentity) { + res = ServerCommandDispatcher(cl - svs->clients, buffer); + } else if (cl == NULL) { + res = ServerCommandDispatcher(-1, buffer); + } - if (!res) - return; + if (!res) { + return; + } SV_SendServerCommand(cl, res); } void __cdecl My_SV_ClientEnterWorld(client_t* client, usercmd_t* cmd) { - clientState_t state = client->state; // State before we call real one. - SV_ClientEnterWorld(client, cmd); - - // gentity is NULL if map changed. - // state is CS_PRIMED only if it's the first time they connect to the server, - // otherwise the dispatcher would also go off when a game starts and such. - if (client->gentity != NULL && state == CS_PRIMED) { - ClientLoadedDispatcher(client - svs->clients); - } + clientState_t state = client->state; // State before we call real one. + SV_ClientEnterWorld(client, cmd); + + // gentity is NULL if map changed. + // state is CS_PRIMED only if it's the first time they connect to the server, + // otherwise the dispatcher would also go off when a game starts and such. + if (client->gentity != NULL && state == CS_PRIMED) { + ClientLoadedDispatcher(client - svs->clients); + } } void __cdecl My_SV_SetConfigstring(int index, char* value) { @@ -130,11 +135,14 @@ void __cdecl My_SV_SetConfigstring(int index, char* value) { return; } - if (!value) value = ""; + if (!value) { + value = ""; + } char* res = SetConfigstringDispatcher(index, value); // NULL means stop the event. - if (res) + if (res) { SV_SetConfigstring(index, res); + } } void __cdecl My_SV_DropClient(client_t* drop, const char* reason) { @@ -152,8 +160,9 @@ void __cdecl My_Com_Printf(char* fmt, ...) { char* res = ConsolePrintDispatcher(buf); // NULL means stop the event. - if (res) + if (res) { Com_Printf(buf); + } } void __cdecl My_SV_SpawnServer(char* server, qboolean killBots) { @@ -166,7 +175,7 @@ void __cdecl My_SV_SpawnServer(char* server, qboolean killBots) { NewGameDispatcher(qfalse); } -void __cdecl My_G_RunFrame(int time) { +void __cdecl My_G_RunFrame(int time) { // Dropping frames is probably not a good idea, so we don't allow cancelling. if (!skipFrameDispatcher) { @@ -178,19 +187,19 @@ void __cdecl My_G_RunFrame(int time) { } char* __cdecl My_ClientConnect(int clientNum, qboolean firstTime, qboolean isBot) { - if (firstTime) { - char* res = ClientConnectDispatcher(clientNum, isBot); - if (res && !isBot) { - return res; - } - } - - return ClientConnect(clientNum, firstTime, isBot); + if (firstTime) { + char* res = ClientConnectDispatcher(clientNum, isBot); + if (res && !isBot) { + return res; + } + } + + return ClientConnect(clientNum, firstTime, isBot); } void __cdecl My_ClientSpawn(gentity_t* ent) { ClientSpawn(ent); - + // Since we won't ever stop the real function from being called, // we trigger the event after calling the real one. This will allow // us to set weapons and such without it getting overriden later. @@ -203,43 +212,45 @@ void __cdecl My_G_StartKamikaze(gentity_t* ent) { if (ent->client) { // player activated kamikaze item ent->client->ps.eFlags &= ~EF_KAMIKAZE; - client_id = ent->client->ps.clientNum; + client_id = ent->client->ps.clientNum; is_used_on_demand = 1; } else if (ent->activator) { // dead player's body blast - client_id = ent->activator->r.ownerNum; + client_id = ent->activator->r.ownerNum; is_used_on_demand = 0; } else { // I don't know - client_id = -1; + client_id = -1; is_used_on_demand = 0; } - if (is_used_on_demand) - KamikazeUseDispatcher(client_id); + if (is_used_on_demand) { + KamikazeUseDispatcher(client_id); + } G_StartKamikaze(ent); - if (client_id != -1) + if (client_id != -1) { KamikazeExplodeDispatcher(client_id, is_used_on_demand); + } } #endif // Hook static functions. Can be done before program even runs. void HookStatic(void) { - int res, failed = 0; + int res, failed = 0; DebugPrint("Hooking...\n"); res = Hook((void*)Cmd_AddCommand, My_Cmd_AddCommand, (void*)&Cmd_AddCommand); - if (res) { - DebugPrint("ERROR: Failed to hook Cmd_AddCommand: %d\n", res); - failed = 1; - } + if (res) { + DebugPrint("ERROR: Failed to hook Cmd_AddCommand: %d\n", res); + failed = 1; + } res = Hook((void*)Sys_SetModuleOffset, My_Sys_SetModuleOffset, (void*)&Sys_SetModuleOffset); if (res) { - DebugPrint("ERROR: Failed to hook Sys_SetModuleOffset: %d\n", res); - failed = 1; - } + DebugPrint("ERROR: Failed to hook Sys_SetModuleOffset: %d\n", res); + failed = 1; + } // ============================== // ONLY NEEDED FOR PYTHON @@ -247,21 +258,21 @@ void HookStatic(void) { #ifndef NOPY res = Hook((void*)SV_ExecuteClientCommand, My_SV_ExecuteClientCommand, (void*)&SV_ExecuteClientCommand); if (res) { - DebugPrint("ERROR: Failed to hook SV_ExecuteClientCommand: %d\n", res); - failed = 1; + DebugPrint("ERROR: Failed to hook SV_ExecuteClientCommand: %d\n", res); + failed = 1; } res = Hook((void*)SV_ClientEnterWorld, My_SV_ClientEnterWorld, (void*)&SV_ClientEnterWorld); - if (res) { - DebugPrint("ERROR: Failed to hook SV_ClientEnterWorld: %d\n", res); - failed = 1; - } + if (res) { + DebugPrint("ERROR: Failed to hook SV_ClientEnterWorld: %d\n", res); + failed = 1; + } - res = Hook((void*)SV_SendServerCommand, My_SV_SendServerCommand, (void*)&SV_SendServerCommand); - if (res) { - DebugPrint("ERROR: Failed to hook SV_SendServerCommand: %d\n", res); - failed = 1; - } + res = Hook((void*)SV_SendServerCommand, My_SV_SendServerCommand, (void*)&SV_SendServerCommand); + if (res) { + DebugPrint("ERROR: Failed to hook SV_SendServerCommand: %d\n", res); + failed = 1; + } res = Hook((void*)SV_SetConfigstring, My_SV_SetConfigstring, (void*)&SV_SetConfigstring); if (res) { @@ -290,21 +301,21 @@ void HookStatic(void) { #endif if (failed) { - DebugPrint("Exiting.\n"); - exit(1); - } + DebugPrint("Exiting.\n"); + exit(1); + } } -/* +/* * Hooks VM calls. Not all use Hook, since the VM calls are stored in a table of * pointers. We simply set our function pointer to the current pointer in the table and * then replace the it with our replacement function. Just like hooking a VMT. - * + * * This must be called AFTER Sys_SetModuleOffset, since Sys_SetModuleOffset is called after * the VM DLL has been loaded, meaning the pointer we use has been set. * * PROTIP: If you can, ALWAYS use VM_Call table hooks instead of using Hook(). -*/ + */ void HookVm(void) { DebugPrint("Hooking VM functions...\n"); @@ -314,21 +325,21 @@ void HookVm(void) { pint vm_call_table = *(int32_t*)OFFSET_RELP_VM_CALL_TABLE + 0xCEFF4 + (pint)qagame; #endif - G_InitGame = *(G_InitGame_ptr*)(vm_call_table + RELOFFSET_VM_CALL_INITGAME); - *(void**)(vm_call_table + RELOFFSET_VM_CALL_INITGAME) = My_G_InitGame; + G_InitGame = *(G_InitGame_ptr*)(vm_call_table + RELOFFSET_VM_CALL_INITGAME); + *(void**)(vm_call_table + RELOFFSET_VM_CALL_INITGAME) = My_G_InitGame; - G_RunFrame = *(G_RunFrame_ptr*)(vm_call_table + RELOFFSET_VM_CALL_RUNFRAME); + G_RunFrame = *(G_RunFrame_ptr*)(vm_call_table + RELOFFSET_VM_CALL_RUNFRAME); #ifndef NOPY - *(void**)(vm_call_table + RELOFFSET_VM_CALL_RUNFRAME) = My_G_RunFrame; + *(void**)(vm_call_table + RELOFFSET_VM_CALL_RUNFRAME) = My_G_RunFrame; - int res, failed = 0, count = 0; - res = Hook((void*)ClientConnect, My_ClientConnect, (void*)&ClientConnect); - if (res) { - DebugPrint("ERROR: Failed to hook ClientConnect: %d\n", res); - failed = 1; - } - count++; + int res, failed = 0, count = 0; + res = Hook((void*)ClientConnect, My_ClientConnect, (void*)&ClientConnect); + if (res) { + DebugPrint("ERROR: Failed to hook ClientConnect: %d\n", res); + failed = 1; + } + count++; res = Hook((void*)G_StartKamikaze, My_G_StartKamikaze, (void*)&G_StartKamikaze); if (res) { @@ -344,12 +355,12 @@ void HookVm(void) { } count++; - if (failed) { - DebugPrint("Exiting.\n"); - exit(1); - } + if (failed) { + DebugPrint("Exiting.\n"); + exit(1); + } - if ( !seek_hook_slot( -count ) ) { + if (!seek_hook_slot(-count)) { DebugPrint("ERROR: Failed to rewind hook slot\nExiting.\n"); exit(1); } @@ -368,8 +379,7 @@ static void SetTag(void) { if (strlen(sv_tags->string) > 2) { // Does it already have tags? snprintf(tags, sizeof(tags), "sv_tags \"" SV_TAGS_PREFIX ",%s\"", sv_tags->string); Cbuf_ExecuteText(EXEC_INSERT, tags); - } - else { + } else { Cbuf_ExecuteText(EXEC_INSERT, "sv_tags \"" SV_TAGS_PREFIX "\""); } } diff --git a/maps_parser.c b/maps_parser.c index c28fc8c..1aeb46c 100644 --- a/maps_parser.c +++ b/maps_parser.c @@ -1,6 +1,6 @@ +#include #include #include -#include #include "maps_parser.h" @@ -13,82 +13,105 @@ const char fmt[] = ("%" SCNxPTR "-%" SCNxPTR " %s %x %x:%x %u %[^\n]"); * that specific module name. */ int GetModuleInfo(module_info_t* module_info) { - int ret = 0; - pint start, end; - int file_offset, dev_major, dev_minor, inode; - char flags[32], path[4096], linebuf[8192]; - - // Check if the name's initialized before we do anything. - if (!strlen(module_info->name)) return -1; - - FILE* fp = fopen("/proc/self/maps", "r"); - while (fgets(linebuf, sizeof(linebuf), fp) != 0) { - sscanf(linebuf, fmt, &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode, path); - - // Some pages have no module name. Ignore those. - size_t pathlen = strlen(path); - if (!pathlen) continue; - - int slash = -1; - for (size_t i = 0; i < pathlen; i++) - if (path[i] == '/') slash = i; - - // Special name such as [heap]? Ignore. - if (slash == -1) continue; - - // Check if it's the module we're interested it. - if (strcmp(module_info->name, &path[slash + 1])) continue; - - // Return error if there's an ambiguity. Could happen if two modules - // are different, but have the same filename. - // TODO: Add option to pass the path instead of name to avoid this. - if (ret && strcmp(path, module_info->path)) return -2; - - if (!ret) { // Only once. - strcpy(module_info->path, path); - } - - // Addresses - module_info->address_start[ret] = start; - module_info->address_end[ret] = end; - - // Permissions - module_info->permissions[ret] = 0; - if (flags[0] == 'r') module_info->permissions[ret] |= PG_READ; - if (flags[1] == 'w') module_info->permissions[ret] |= PG_WRITE; - if (flags[2] == 'x') module_info->permissions[ret] |= PG_EXECUTE; - if (flags[3] == 'p') module_info->permissions[ret] |= PG_PRIVATE; - if (flags[3] == 's') module_info->permissions[ret] |= PG_SHARED; - - ret++; - } - fclose(fp); - - module_info->entries = ret; - return ret; + int ret = 0; + pint start, end; + int file_offset, dev_major, dev_minor, inode; + char flags[32], path[4096], linebuf[8192]; + + // Check if the name's initialized before we do anything. + if (!strlen(module_info->name)) { + return -1; + } + + FILE* fp = fopen("/proc/self/maps", "r"); + while (fgets(linebuf, sizeof(linebuf), fp) != 0) { + sscanf(linebuf, fmt, &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode, path); + + // Some pages have no module name. Ignore those. + size_t pathlen = strlen(path); + if (!pathlen) { + continue; + } + + int slash = -1; + for (size_t i = 0; i < pathlen; i++) { + if (path[i] == '/') { + slash = i; + } + } + + // Special name such as [heap]? Ignore. + if (slash == -1) { + continue; + } + + // Check if it's the module we're interested it. + if (strcmp(module_info->name, &path[slash + 1])) { + continue; + } + + // Return error if there's an ambiguity. Could happen if two modules + // are different, but have the same filename. + // TODO: Add option to pass the path instead of name to avoid this. + if (ret && strcmp(path, module_info->path)) { + return -2; + } + + if (!ret) { // Only once. + strcpy(module_info->path, path); + } + + // Addresses + module_info->address_start[ret] = start; + module_info->address_end[ret] = end; + + // Permissions + module_info->permissions[ret] = 0; + if (flags[0] == 'r') { + module_info->permissions[ret] |= PG_READ; + } + if (flags[1] == 'w') { + module_info->permissions[ret] |= PG_WRITE; + } + if (flags[2] == 'x') { + module_info->permissions[ret] |= PG_EXECUTE; + } + if (flags[3] == 'p') { + module_info->permissions[ret] |= PG_PRIVATE; + } + if (flags[3] == 's') { + module_info->permissions[ret] |= PG_SHARED; + } + + ret++; + } + fclose(fp); + + module_info->entries = ret; + return ret; } /* void main() { - module_info_t module_info; - strcpy(module_info.name, "mapsparser"); - - int res = GetModuleInfo(&module_info); - if (!res) { - printf("Fuck this gay Earth\n"); - return; - } - else if (res < 0) { - printf("Returned: %d\n", res); - return; - } - - for (int i = 0; i < res; i++) { - printf("%s (%p-%p): %p\n", - module_info.name, - module_info.address_start[i], - module_info.address_end[i], - module_info.permissions[i]); - } + module_info_t module_info; + strcpy(module_info.name, "mapsparser"); + + int res = GetModuleInfo(&module_info); + if (!res) { + printf("Fuck this gay Earth\n"); + return; + } + else if (res < 0) { + printf("Returned: %d\n", res); + return; + } + + for (int i = 0; i < res; i++) { + printf("%s (%p-%p): %p\n", + module_info.name, + module_info.address_start[i], + module_info.address_end[i], + module_info.permissions[i]); + } } */ diff --git a/maps_parser.h b/maps_parser.h index 2b00610..e95fb94 100644 --- a/maps_parser.h +++ b/maps_parser.h @@ -20,19 +20,19 @@ typedef int32_t sint; #endif // Permission flags. The two last are mutually exclusive. -#define PG_READ 1 -#define PG_WRITE 2 -#define PG_EXECUTE 4 -#define PG_PRIVATE 8 -#define PG_SHARED 16 +#define PG_READ 1 +#define PG_WRITE 2 +#define PG_EXECUTE 4 +#define PG_PRIVATE 8 +#define PG_SHARED 16 typedef struct { - char name[512]; - char path[4096]; - int entries; - int permissions[128]; - pint address_start[128]; - pint address_end[128]; + char name[512]; + char path[4096]; + int entries; + int permissions[128]; + pint address_start[128]; + pint address_end[128]; } module_info_t; int GetModuleInfo(module_info_t* module_info); diff --git a/misc.c b/misc.c index 1ba3c76..71aab3f 100644 --- a/misc.c +++ b/misc.c @@ -2,29 +2,32 @@ #include #include "common.h" -#include "quake_common.h" #include "maps_parser.h" +#include "quake_common.h" /* Takes a 64-bit integer used as a bit field as flags for which player * has an action pending, removes the flag and returns the client ID. * The server only allows up to 64 players, so a 64-bit int covers it all. - * + * * Returns -1 if no flag is set, so use it in a loop until it does so. */ int GetPendingPlayer(uint64_t* players) { int flag = -1; // We first check if any bitfield is set. - if (!*players) return flag; - else { + if (!*players) { + return flag; + } else { for (int id = 0; id < 64; id++) { // Check bit i's flag. flag = *players & (1LL << id); // Remove the flag we checked, if present. *players &= ~flag; // If the flag was set, return client id. - if (flag) return id; + if (flag) { + return id; + } } } - + return -1; // All flags have been cleared. } @@ -35,39 +38,41 @@ void SetPendingPlayer(uint64_t* players, int client_id) { // (0.0f, 1.0f) float RandomFloat(void) { - return (float)rand()/(float)RAND_MAX; + return (float)rand() / (float)RAND_MAX; } // (-1.0f, 1.0f) float RandomFloatWithNegative(void) { - return (float)rand()/(float)(RAND_MAX/2) - 1; + return (float)rand() / (float)(RAND_MAX / 2) - 1; } void* PatternSearch(void* address, size_t length, const char* pattern, const char* mask) { - for (size_t i = 0; i < length; i++) { - for (size_t j = 0; mask[j]; j++) { - if (mask[j] == 'X' && pattern[j] != ((char*)address)[i + j]) { - break; - } - else if (mask[j + 1]) { - continue; - } + for (size_t i = 0; i < length; i++) { + for (size_t j = 0; mask[j]; j++) { + if (mask[j] == 'X' && pattern[j] != ((char*)address)[i + j]) { + break; + } else if (mask[j + 1]) { + continue; + } - return (void*)(((pint)address) + i); + return (void*)(((pint)address) + i); + } } - } - return NULL; + return NULL; } void* PatternSearchModule(module_info_t* module, const char* pattern, const char* mask) { - void* res = NULL; - for (int i = 0; i < module->entries; i++) { - if (!(module->permissions[i] & PG_READ)) continue; - size_t size = module->address_end[i] - module->address_start[i]; - res = PatternSearch((void*)module->address_start[i], size, pattern, mask); - if (res) break; - } + void* res = NULL; + for (int i = 0; i < module->entries; i++) { + if (!(module->permissions[i] & PG_READ)) { + continue; + } + size_t size = module->address_end[i] - module->address_start[i]; + res = PatternSearch((void*)module->address_start[i], size, pattern, mask); + if (res) { + break; + } + } - return res; + return res; } - diff --git a/patches.c b/patches.c index 73e4e50..5b553d5 100644 --- a/patches.c +++ b/patches.c @@ -1,42 +1,47 @@ +#include #include -#include #include -#include +#include -#include "quake_common.h" -#include "patches.h" #include "common.h" +#include "patches.h" +#include "quake_common.h" Cmd_CallVote_f_ptr Cmd_CallVote_f; int patch_by_mask(pint offset, char* pattern, char* mask) { - int res, page_size; - - page_size = sysconf(_SC_PAGESIZE); - if (page_size == -1) return errno; - res = mprotect((void*)(offset & ~(page_size-1)), page_size, PROT_READ | PROT_WRITE | PROT_EXEC); - if (res) return errno; - - for (int i=0; mask[i]; i++) { - if (mask[i] != 'X') - continue; - - *(int8_t*)(offset+i) = pattern[i]; - } - return 0; + int res, page_size; + + page_size = sysconf(_SC_PAGESIZE); + if (page_size == -1) { + return errno; + } + res = mprotect((void*)(offset & ~(page_size - 1)), page_size, PROT_READ | PROT_WRITE | PROT_EXEC); + if (res) { + return errno; + } + + for (int i = 0; mask[i]; i++) { + if (mask[i] != 'X') { + continue; + } + + *(int8_t*)(offset + i) = pattern[i]; + } + return 0; } void vote_clientkick_fix(void) { - Cmd_CallVote_f = (Cmd_CallVote_f_ptr)PatternSearch((void*)((pint)qagame + 0xB000), - 0xB0000, PTRN_CMD_CALLVOTE_F, MASK_CMD_CALLVOTE_F); - if (Cmd_CallVote_f == NULL) { - DebugPrint("WARNING: Unable to find Cmd_CallVote_f. Skipping callvote-clientkick patch...\n"); - return; - } - - patch_by_mask( ADDR_VOTE_CLIENTKICK_FIX, PTRN_VOTE_CLIENTKICK_FIX, MASK_VOTE_CLIENTKICK_FIX ); + Cmd_CallVote_f = (Cmd_CallVote_f_ptr)PatternSearch((void*)((pint)qagame + 0xB000), + 0xB0000, PTRN_CMD_CALLVOTE_F, MASK_CMD_CALLVOTE_F); + if (Cmd_CallVote_f == NULL) { + DebugPrint("WARNING: Unable to find Cmd_CallVote_f. Skipping callvote-clientkick patch...\n"); + return; + } + + patch_by_mask(ADDR_VOTE_CLIENTKICK_FIX, PTRN_VOTE_CLIENTKICK_FIX, MASK_VOTE_CLIENTKICK_FIX); } void patch_vm(void) { - vote_clientkick_fix(); + vote_clientkick_fix(); } diff --git a/patterns.h b/patterns.h index 62bc426..4e76c7f 100644 --- a/patterns.h +++ b/patterns.h @@ -91,19 +91,19 @@ #define OFFSET_RELP_CMD_ARGC ((pint)SV_Map_f + 0x81) // qagame structs and global varaibles. -#define OFFSET_RELP_VM_CALL_TABLE ((pint)qagame_dllentry + 0x3) -#define OFFSET_RELP_G_ENTITIES ((pint)G_RunFrame + 0x11B) -#define OFFSET_RELP_ADMINCOMMANDS ((pint)CheckPrivileges + 0x56) -#define OFFSET_RELP_LEVEL ((pint)G_InitGame + 0x4A1) -#define OFFSET_RELP_BG_ITEMLIST ((pint)LaunchItem + 0x2A) +#define OFFSET_RELP_VM_CALL_TABLE ((pint)qagame_dllentry + 0x3) +#define OFFSET_RELP_G_ENTITIES ((pint)G_RunFrame + 0x11B) +#define OFFSET_RELP_ADMINCOMMANDS ((pint)CheckPrivileges + 0x56) +#define OFFSET_RELP_LEVEL ((pint)G_InitGame + 0x4A1) +#define OFFSET_RELP_BG_ITEMLIST ((pint)LaunchItem + 0x2A) // Structs. -#define OFFSET_PP_SVS ((pint)SV_Shutdown + 0xAC) -#define OFFSET_RELP_SV ((pint)SV_SetConfigstring + 0xB4) +#define OFFSET_PP_SVS ((pint)SV_Shutdown + 0xAC) +#define OFFSET_RELP_SV ((pint)SV_SetConfigstring + 0xB4) // VM_Call table offsets. -#define RELOFFSET_VM_CALL_INITGAME 0x18 -#define RELOFFSET_VM_CALL_RUNFRAME 0x8 +#define RELOFFSET_VM_CALL_INITGAME 0x18 +#define RELOFFSET_VM_CALL_RUNFRAME 0x8 #elif defined(__i386) || defined(_M_IX86) @@ -111,17 +111,17 @@ #define OFFSET_RELP_CMD_ARGC ((pint)SV_Map_f + 0x8A) // qagame structs and global varaibles. -#define OFFSET_RELP_VM_CALL_TABLE ((pint)qagame_dllentry + 0x11) -#define OFFSET_RELP_G_ENTITIES ((pint)G_RunFrame + 0x143) -#define OFFSET_RELP_LEVEL ((pint)G_InitGame + 0x566) -#define OFFSET_RELP_BG_ITEMLIST ((pint)LaunchItem + 0x21) +#define OFFSET_RELP_VM_CALL_TABLE ((pint)qagame_dllentry + 0x11) +#define OFFSET_RELP_G_ENTITIES ((pint)G_RunFrame + 0x143) +#define OFFSET_RELP_LEVEL ((pint)G_InitGame + 0x566) +#define OFFSET_RELP_BG_ITEMLIST ((pint)LaunchItem + 0x21) // Structs. -#define OFFSET_PP_SVS ((pint)SV_Shutdown + 0xA0) +#define OFFSET_PP_SVS ((pint)SV_Shutdown + 0xA0) // VM_Call table offsets. -#define RELOFFSET_VM_CALL_INITGAME 0xC -#define RELOFFSET_VM_CALL_RUNFRAME 0x4 +#define RELOFFSET_VM_CALL_INITGAME 0xC +#define RELOFFSET_VM_CALL_RUNFRAME 0x4 // Generated by minfuncfind32. #define PTRN_COM_PRINTF "\x57\xba\x00\x00\x00\x00\x56\xb9\x00\x00\x00\x00\x53\x81\xec\x00\x00\x00\x00\x65\xa1\x00\x00\x00\x00\x89\x84\x24\x00\x00\x00\x00\x31\xc0\x8d\x84\x24\x00\x00\x00\x00\x89\x44\x24\x00" @@ -209,5 +209,4 @@ #endif - #endif /* PATTERNS_H */ diff --git a/pyminqlxtended.h b/pyminqlxtended.h index 865f9b5..c84edda 100644 --- a/pyminqlxtended.h +++ b/pyminqlxtended.h @@ -15,15 +15,15 @@ typedef enum { PYM_MAIN_SCRIPT_ERROR, PYM_ALREADY_INITIALIZED, PYM_NOT_INITIALIZED_ERROR -} PyMinqlx_InitStatus_t; +} PyMinqlxtended_InitStatus_t; // Used primarily in Python, but defined here and added using PyModule_AddIntMacro(). enum { RET_NONE, - RET_STOP, // Stop execution of event handlers within Python. - RET_STOP_EVENT, // Only stop the event, but let other handlers process it. - RET_STOP_ALL, // Stop execution at an engine level. SCARY STUFF! - RET_USAGE // Used for commands. Replies to the channel with a command's usage. + RET_STOP, // Stop execution of event handlers within Python. + RET_STOP_EVENT, // Only stop the event, but let other handlers process it. + RET_STOP_ALL, // Stop execution at an engine level. SCARY STUFF! + RET_USAGE // Used for commands. Replies to the channel with a command's usage. }; enum { @@ -34,18 +34,18 @@ enum { PRI_LOWEST }; -int PyMinqlx_IsInitialized(void); -PyMinqlx_InitStatus_t PyMinqlx_Initialize(void); -PyMinqlx_InitStatus_t PyMinqlx_Finalize(void); +int PyMinqlxtended_IsInitialized(void); +PyMinqlxtended_InitStatus_t PyMinqlxtended_Initialize(void); +PyMinqlxtended_InitStatus_t PyMinqlxtended_Finalize(void); /* * Event handlers. Note that we're using simple PyObject pointers, meaning it only supports * a single handler for each event. I don't see the need for multiple handlers, since you can * do that more easily in Python-level code instead of C. -*/ + */ typedef struct { - char* name; - PyObject** handler; + char* name; + PyObject** handler; } handler_t; extern PyObject* client_command_handler; extern PyObject* server_command_handler; diff --git a/python_dispatchers.c b/python_dispatchers.c index ad51e76..86848f6 100644 --- a/python_dispatchers.c +++ b/python_dispatchers.c @@ -8,24 +8,25 @@ int allow_free_client = -1; char* ClientCommandDispatcher(int client_id, char* cmd) { char* ret = cmd; static char ccmd_buf[4096]; - if (!client_command_handler) + if (!client_command_handler) { return ret; // No registered handler. - + } + PyGILState_STATE gstate = PyGILState_Ensure(); PyObject* cmd_string = PyUnicode_DecodeUTF8(cmd, strlen(cmd), "ignore"); - PyObject* result = PyObject_CallFunction(client_command_handler, "iO", client_id, cmd_string); - - if (result == NULL) + PyObject* result = PyObject_CallFunction(client_command_handler, "iO", client_id, cmd_string); + + if (result == NULL) { DebugError("PyObject_CallFunction() returned NULL.\n", - __FILE__, __LINE__, __func__); - else if (PyBool_Check(result) && result == Py_False) + __FILE__, __LINE__, __func__); + } else if (PyBool_Check(result) && result == Py_False) { ret = NULL; - else if (PyUnicode_Check(result)) { + } else if (PyUnicode_Check(result)) { strncpy(ccmd_buf, PyUnicode_AsUTF8(result), sizeof(ccmd_buf)); ret = ccmd_buf; } - + Py_XDECREF(cmd_string); Py_XDECREF(result); @@ -36,20 +37,21 @@ char* ClientCommandDispatcher(int client_id, char* cmd) { char* ServerCommandDispatcher(int client_id, char* cmd) { char* ret = cmd; static char scmd_buf[4096]; - if (!server_command_handler) + if (!server_command_handler) { return ret; // No registered handler. + } PyGILState_STATE gstate = PyGILState_Ensure(); PyObject* cmd_string = PyUnicode_DecodeUTF8(cmd, strlen(cmd), "ignore"); - PyObject* result = PyObject_CallFunction(server_command_handler, "iO", client_id, cmd_string); + PyObject* result = PyObject_CallFunction(server_command_handler, "iO", client_id, cmd_string); - if (result == NULL) + if (result == NULL) { DebugError("PyObject_CallFunction() returned NULL.\n", - __FILE__, __LINE__, __func__); - else if (PyBool_Check(result) && result == Py_False) + __FILE__, __LINE__, __func__); + } else if (PyBool_Check(result) && result == Py_False) { ret = NULL; - else if (PyUnicode_Check(result)) { + } else if (PyUnicode_Check(result)) { strncpy(scmd_buf, PyUnicode_AsUTF8(result), sizeof(scmd_buf)); ret = scmd_buf; } @@ -62,8 +64,9 @@ char* ServerCommandDispatcher(int client_id, char* cmd) { } void FrameDispatcher(void) { - if (!frame_handler) + if (!frame_handler) { return; // No registered handler. + } PyGILState_STATE gstate = PyGILState_Ensure(); @@ -76,138 +79,146 @@ void FrameDispatcher(void) { } char* ClientConnectDispatcher(int client_id, int is_bot) { - char* ret = NULL; + char* ret = NULL; static char connect_buf[4096]; - if (!client_connect_handler) - return ret; // No registered handler. - - PyGILState_STATE gstate = PyGILState_Ensure(); - - // Tell PyMinqlx_PlayerInfo it's OK to get player info for someone with CS_FREE. - allow_free_client = client_id; - PyObject* result = PyObject_CallFunction(client_connect_handler, "iO", client_id, is_bot ? Py_True : Py_False); - allow_free_client = -1; - - if (result == NULL) - DebugError("PyObject_CallFunction() returned NULL.\n", - __FILE__, __LINE__, __func__); - else if (PyBool_Check(result) && result == Py_False) - ret = "You are banned from this server."; - else if (PyUnicode_Check(result)) { - strncpy(connect_buf, PyUnicode_AsUTF8(result), sizeof(connect_buf)); + if (!client_connect_handler) { + return ret; // No registered handler. + } + + PyGILState_STATE gstate = PyGILState_Ensure(); + + // Tell PyMinqlxtended_PlayerInfo it's OK to get player info for someone with CS_FREE. + allow_free_client = client_id; + PyObject* result = PyObject_CallFunction(client_connect_handler, "iO", client_id, is_bot ? Py_True : Py_False); + allow_free_client = -1; + + if (result == NULL) { + DebugError("PyObject_CallFunction() returned NULL.\n", + __FILE__, __LINE__, __func__); + } else if (PyBool_Check(result) && result == Py_False) { + ret = "You are banned from this server."; + } else if (PyUnicode_Check(result)) { + strncpy(connect_buf, PyUnicode_AsUTF8(result), sizeof(connect_buf)); ret = connect_buf; } - Py_XDECREF(result); + Py_XDECREF(result); - PyGILState_Release(gstate); - return ret; + PyGILState_Release(gstate); + return ret; } void ClientDisconnectDispatcher(int client_id, const char* reason) { - if (!client_disconnect_handler) - return; // No registered handler. + if (!client_disconnect_handler) { + return; // No registered handler. + } - PyGILState_STATE gstate = PyGILState_Ensure(); + PyGILState_STATE gstate = PyGILState_Ensure(); - // Tell PyMinqlx_PlayerInfo it's OK to get player info for someone with CS_FREE. + // Tell PyMinqlxtended_PlayerInfo it's OK to get player info for someone with CS_FREE. allow_free_client = client_id; - PyObject* result = PyObject_CallFunction(client_disconnect_handler, "is", client_id, reason); + PyObject* result = PyObject_CallFunction(client_disconnect_handler, "is", client_id, reason); allow_free_client = -1; - - if (result == NULL) - DebugError("PyObject_CallFunction() returned NULL.\n", - __FILE__, __LINE__, __func__); - Py_XDECREF(result); + if (result == NULL) { + DebugError("PyObject_CallFunction() returned NULL.\n", + __FILE__, __LINE__, __func__); + } + + Py_XDECREF(result); - PyGILState_Release(gstate); - return; + PyGILState_Release(gstate); + return; } // Does not trigger on bots. int ClientLoadedDispatcher(int client_id) { - int ret = 1; - if (!client_loaded_handler) - return ret; // No registered handler. + int ret = 1; + if (!client_loaded_handler) { + return ret; // No registered handler. + } - PyGILState_STATE gstate = PyGILState_Ensure(); + PyGILState_STATE gstate = PyGILState_Ensure(); - PyObject* result = PyObject_CallFunction(client_loaded_handler, "i", client_id); + PyObject* result = PyObject_CallFunction(client_loaded_handler, "i", client_id); - // Only change to 0 if we got False returned to us. - if (result == NULL) { - DebugError("PyObject_CallFunction() returned NULL.\n", - __FILE__, __LINE__, __func__); - PyGILState_Release(gstate); - return ret; - } - else if (PyBool_Check(result) && result == Py_False) { - ret = 0; - } + // Only change to 0 if we got False returned to us. + if (result == NULL) { + DebugError("PyObject_CallFunction() returned NULL.\n", + __FILE__, __LINE__, __func__); + PyGILState_Release(gstate); + return ret; + } else if (PyBool_Check(result) && result == Py_False) { + ret = 0; + } - Py_XDECREF(result); + Py_XDECREF(result); - PyGILState_Release(gstate); - return ret; + PyGILState_Release(gstate); + return ret; } void NewGameDispatcher(int restart) { - if (!new_game_handler) - return; // No registered handler. + if (!new_game_handler) { + return; // No registered handler. + } - PyGILState_STATE gstate = PyGILState_Ensure(); + PyGILState_STATE gstate = PyGILState_Ensure(); - PyObject* result = PyObject_CallFunction(new_game_handler, "O", restart ? Py_True : Py_False); + PyObject* result = PyObject_CallFunction(new_game_handler, "O", restart ? Py_True : Py_False); - if (result == NULL) - DebugError("PyObject_CallFunction() returned NULL.\n", __FILE__, __LINE__, __func__); + if (result == NULL) { + DebugError("PyObject_CallFunction() returned NULL.\n", __FILE__, __LINE__, __func__); + } - Py_XDECREF(result); + Py_XDECREF(result); - PyGILState_Release(gstate); - return; + PyGILState_Release(gstate); + return; } char* SetConfigstringDispatcher(int index, char* value) { - char* ret = value; + char* ret = value; static char setcs_buf[4096]; - if (!set_configstring_handler) - return ret; // No registered handler. + if (!set_configstring_handler) { + return ret; // No registered handler. + } - PyGILState_STATE gstate = PyGILState_Ensure(); + PyGILState_STATE gstate = PyGILState_Ensure(); PyObject* value_string = PyUnicode_DecodeUTF8(value, strlen(value), "ignore"); - PyObject* result = PyObject_CallFunction(set_configstring_handler, "iO", index, value_string); - - if (result == NULL) - DebugError("PyObject_CallFunction() returned NULL.\n", - __FILE__, __LINE__, __func__); - else if (PyBool_Check(result) && result == Py_False) - ret = NULL; - else if (PyUnicode_Check(result)) { - strncpy(setcs_buf, PyUnicode_AsUTF8(result), sizeof(setcs_buf)); + PyObject* result = PyObject_CallFunction(set_configstring_handler, "iO", index, value_string); + + if (result == NULL) { + DebugError("PyObject_CallFunction() returned NULL.\n", + __FILE__, __LINE__, __func__); + } else if (PyBool_Check(result) && result == Py_False) { + ret = NULL; + } else if (PyUnicode_Check(result)) { + strncpy(setcs_buf, PyUnicode_AsUTF8(result), sizeof(setcs_buf)); ret = setcs_buf; } Py_XDECREF(value_string); - Py_XDECREF(result); + Py_XDECREF(result); - PyGILState_Release(gstate); - return ret; + PyGILState_Release(gstate); + return ret; } void RconDispatcher(const char* cmd) { - if (!rcon_handler) + if (!rcon_handler) { return; // No registered handler. + } PyGILState_STATE gstate = PyGILState_Ensure(); PyObject* result = PyObject_CallFunction(rcon_handler, "s", cmd); - if (result == NULL) + if (result == NULL) { DebugError("PyObject_CallFunction() returned NULL.\n", - __FILE__, __LINE__, __func__); + __FILE__, __LINE__, __func__); + } Py_XDECREF(result); PyGILState_Release(gstate); @@ -216,20 +227,21 @@ void RconDispatcher(const char* cmd) { char* ConsolePrintDispatcher(char* text) { char* ret = text; static char print_buf[4096]; - if (!console_print_handler) + if (!console_print_handler) { return ret; // No registered handler. + } PyGILState_STATE gstate = PyGILState_Ensure(); PyObject* text_string = PyUnicode_DecodeUTF8(text, strlen(text), "ignore"); - PyObject* result = PyObject_CallFunction(console_print_handler, "O", text_string); + PyObject* result = PyObject_CallFunction(console_print_handler, "O", text_string); - if (result == NULL) + if (result == NULL) { DebugError("PyObject_CallFunction() returned NULL.\n", - __FILE__, __LINE__, __func__); - else if (PyBool_Check(result) && result == Py_False) + __FILE__, __LINE__, __func__); + } else if (PyBool_Check(result) && result == Py_False) { ret = NULL; - else if (PyUnicode_Check(result)) { + } else if (PyUnicode_Check(result)) { strncpy(print_buf, PyUnicode_AsUTF8(result), sizeof(print_buf)); ret = print_buf; } @@ -242,8 +254,9 @@ char* ConsolePrintDispatcher(char* text) { } void ClientSpawnDispatcher(int client_id) { - if (!client_spawn_handler) + if (!client_spawn_handler) { return; // No registered handler. + } PyGILState_STATE gstate = PyGILState_Ensure(); @@ -252,7 +265,7 @@ void ClientSpawnDispatcher(int client_id) { // Only change to 0 if we got False returned to us. if (result == NULL) { DebugError("PyObject_CallFunction() returned NULL.\n", - __FILE__, __LINE__, __func__); + __FILE__, __LINE__, __func__); } Py_XDECREF(result); @@ -260,8 +273,9 @@ void ClientSpawnDispatcher(int client_id) { } void KamikazeUseDispatcher(int client_id) { - if (!kamikaze_use_handler) + if (!kamikaze_use_handler) { return; // No registered handler. + } PyGILState_STATE gstate = PyGILState_Ensure(); @@ -270,7 +284,7 @@ void KamikazeUseDispatcher(int client_id) { // Only change to 0 if we got False returned to us. if (result == NULL) { DebugError("PyObject_CallFunction() returned NULL.\n", - __FILE__, __LINE__, __func__); + __FILE__, __LINE__, __func__); } Py_XDECREF(result); @@ -278,8 +292,9 @@ void KamikazeUseDispatcher(int client_id) { } void KamikazeExplodeDispatcher(int client_id, int is_used_on_demand) { - if (!kamikaze_explode_handler) + if (!kamikaze_explode_handler) { return; // No registered handler. + } PyGILState_STATE gstate = PyGILState_Ensure(); @@ -288,7 +303,7 @@ void KamikazeExplodeDispatcher(int client_id, int is_used_on_demand) { // Only change to 0 if we got False returned to us. if (result == NULL) { DebugError("PyObject_CallFunction() returned NULL.\n", - __FILE__, __LINE__, __func__); + __FILE__, __LINE__, __func__); } Py_XDECREF(result); diff --git a/python_embed.c b/python_embed.c index 3e0904c..6ce7cfa 100644 --- a/python_embed.c +++ b/python_embed.c @@ -1,31 +1,31 @@ #include +#include #include -#include -#include +#include #include #include -#include -#include +#include +#include +#include "common.h" +#include "patterns.h" #include "pyminqlxtended.h" #include "quake_common.h" -#include "patterns.h" -#include "common.h" -PyObject* client_command_handler = NULL; -PyObject* server_command_handler = NULL; -PyObject* client_connect_handler = NULL; -PyObject* client_loaded_handler = NULL; +PyObject* client_command_handler = NULL; +PyObject* server_command_handler = NULL; +PyObject* client_connect_handler = NULL; +PyObject* client_loaded_handler = NULL; PyObject* client_disconnect_handler = NULL; -PyObject* frame_handler = NULL; -PyObject* custom_command_handler = NULL; -PyObject* new_game_handler = NULL; -PyObject* set_configstring_handler = NULL; -PyObject* rcon_handler = NULL; -PyObject* console_print_handler = NULL; -PyObject* client_spawn_handler = NULL; - -PyObject* kamikaze_use_handler = NULL; +PyObject* frame_handler = NULL; +PyObject* custom_command_handler = NULL; +PyObject* new_game_handler = NULL; +PyObject* set_configstring_handler = NULL; +PyObject* rcon_handler = NULL; +PyObject* console_print_handler = NULL; +PyObject* client_spawn_handler = NULL; + +PyObject* kamikaze_use_handler = NULL; PyObject* kamikaze_explode_handler = NULL; static PyThreadState* mainstate; @@ -36,19 +36,19 @@ static int initialized = 0; * and PyRun_String*() if the module has an error in it. It's ugly as * fuck, but other than doing this, I have no idea how to extract the * traceback. The documentation or Google doesn't help much either. -*/ -static const char loader[] = "import traceback\n" \ - "try:\n" \ - " import sys\n" \ - " sys.path.append('" CORE_MODULE "')\n" \ - " sys.path.append('.')\n" \ - " import minqlxtended\n" \ - " minqlxtended.initialize()\n" \ - " ret = True\n" \ - "except Exception as e:\n" \ - " e = traceback.format_exc().rstrip('\\n')\n" \ - " for line in e.split('\\n'): print(line)\n" \ - " ret = False\n"; + */ +static const char loader[] = "import traceback\n" + "try:\n" + " import sys\n" + " sys.path.append('" CORE_MODULE "')\n" + " sys.path.append('.')\n" + " import minqlxtended\n" + " minqlxtended.initialize()\n" + " ret = True\n" + "except Exception as e:\n" + " e = traceback.format_exc().rstrip('\\n')\n" + " for line in e.split('\\n'): print(line)\n" + " ret = False\n"; /* * The number of handlers was getting large, so instead of a bunch of @@ -56,30 +56,29 @@ static const char loader[] = "import traceback\n" \ * pairs and iterate over them instead. */ static handler_t handlers[] = { - {"client_command", &client_command_handler}, - {"server_command", &server_command_handler}, - {"frame", &frame_handler}, - {"player_connect", &client_connect_handler}, - {"player_loaded", &client_loaded_handler}, - {"player_disconnect", &client_disconnect_handler}, - {"custom_command", &custom_command_handler}, - {"new_game", &new_game_handler}, - {"set_configstring", &set_configstring_handler}, - {"rcon", &rcon_handler}, - {"console_print", &console_print_handler}, - {"player_spawn", &client_spawn_handler}, - - {"kamikaze_use", &kamikaze_use_handler}, - {"kamikaze_explode", &kamikaze_explode_handler}, - - {NULL, NULL} -}; + {"client_command", &client_command_handler}, + {"server_command", &server_command_handler}, + {"frame", &frame_handler}, + {"player_connect", &client_connect_handler}, + {"player_loaded", &client_loaded_handler}, + {"player_disconnect", &client_disconnect_handler}, + {"custom_command", &custom_command_handler}, + {"new_game", &new_game_handler}, + {"set_configstring", &set_configstring_handler}, + {"rcon", &rcon_handler}, + {"console_print", &console_print_handler}, + {"player_spawn", &client_spawn_handler}, + + {"kamikaze_use", &kamikaze_use_handler}, + {"kamikaze_explode", &kamikaze_explode_handler}, + + {NULL, NULL}}; /* * ================================================================ * Struct Sequences * ================================================================ -*/ + */ // Players static PyTypeObject player_info_type = {0}; @@ -92,15 +91,13 @@ static PyStructSequence_Field player_info_fields[] = { {"steam_id", "The player's 64-bit representation of the Steam ID."}, {"team", "The player's team."}, {"privileges", "The player's privileges."}, - {NULL} -}; + {NULL}}; static PyStructSequence_Desc player_info_desc = { "PlayerInfo", "Information about a player, such as Steam ID, name, client ID, and whatnot.", player_info_fields, - (sizeof(player_info_fields)/sizeof(PyStructSequence_Field)) - 1 -}; + (sizeof(player_info_fields) / sizeof(PyStructSequence_Field)) - 1}; // Player state static PyTypeObject player_state_type = {0}; @@ -120,15 +117,13 @@ static PyStructSequence_Field player_state_fields[] = { {"flight", "A struct sequence with flight parameters."}, {"is_frozen", "Whether the player is frozen(freezetag)."}, {"keys", "The player's keys."}, - {NULL} -}; + {NULL}}; static PyStructSequence_Desc player_state_desc = { "PlayerState", "Information about a player's state in the game.", player_state_fields, - (sizeof(player_state_fields)/sizeof(PyStructSequence_Field)) - 1 -}; + (sizeof(player_state_fields) / sizeof(PyStructSequence_Field)) - 1}; // Stats static PyTypeObject player_stats_type = {0}; @@ -141,15 +136,13 @@ static PyStructSequence_Field player_stats_fields[] = { {"damage_taken", "The player's total damage taken."}, {"time", "The time in milliseconds the player has on a team since the game started."}, {"ping", "The player's ping."}, - {NULL} -}; + {NULL}}; static PyStructSequence_Desc player_stats_desc = { "PlayerStats", "A player's score and some basic stats.", player_stats_fields, - (sizeof(player_stats_fields)/sizeof(PyStructSequence_Field)) - 1 -}; + (sizeof(player_stats_fields) / sizeof(PyStructSequence_Field)) - 1}; // Vectors static PyTypeObject vector3_type = {0}; @@ -158,51 +151,37 @@ static PyStructSequence_Field vector3_fields[] = { {"x", NULL}, {"y", NULL}, {"z", NULL}, - {NULL} -}; + {NULL}}; static PyStructSequence_Desc vector3_desc = { "Vector3", "A three-dimensional vector.", vector3_fields, - (sizeof(vector3_fields)/sizeof(PyStructSequence_Field)) - 1 -}; + (sizeof(vector3_fields) / sizeof(PyStructSequence_Field)) - 1}; // Weapons static PyTypeObject weapons_type = {0}; static PyStructSequence_Field weapons_fields[] = { - {"g", NULL}, {"mg", NULL}, {"sg", NULL}, - {"gl", NULL}, {"rl", NULL}, {"lg", NULL}, - {"rg", NULL}, {"pg", NULL}, {"bfg", NULL}, - {"gh", NULL}, {"ng", NULL}, {"pl", NULL}, - {"cg", NULL}, {"hmg", NULL}, {"hands", NULL}, - {NULL} -}; + {"g", NULL}, {"mg", NULL}, {"sg", NULL}, {"gl", NULL}, {"rl", NULL}, {"lg", NULL}, {"rg", NULL}, {"pg", NULL}, {"bfg", NULL}, {"gh", NULL}, {"ng", NULL}, {"pl", NULL}, {"cg", NULL}, {"hmg", NULL}, {"hands", NULL}, {NULL}}; static PyStructSequence_Desc weapons_desc = { "Weapons", "A struct sequence containing all the weapons in the game.", weapons_fields, - (sizeof(weapons_fields)/sizeof(PyStructSequence_Field)) - 1 -}; + (sizeof(weapons_fields) / sizeof(PyStructSequence_Field)) - 1}; // Powerups static PyTypeObject powerups_type = {0}; static PyStructSequence_Field powerups_fields[] = { - {"quad", NULL}, {"battlesuit", NULL}, - {"haste", NULL}, {"invisibility", NULL}, - {"regeneration", NULL}, {"invulnerability", NULL}, - {NULL} -}; + {"quad", NULL}, {"battlesuit", NULL}, {"haste", NULL}, {"invisibility", NULL}, {"regeneration", NULL}, {"invulnerability", NULL}, {NULL}}; static PyStructSequence_Desc powerups_desc = { "Powerups", "A struct sequence containing all the powerups in the game.", powerups_fields, - (sizeof(powerups_fields)/sizeof(PyStructSequence_Field)) - 1 -}; + (sizeof(powerups_fields) / sizeof(PyStructSequence_Field)) - 1}; // Flight static PyTypeObject flight_type = {0}; @@ -212,15 +191,13 @@ static PyStructSequence_Field flight_fields[] = { {"max_fuel", NULL}, {"thrust", NULL}, {"refuel", NULL}, - {NULL} -}; + {NULL}}; static PyStructSequence_Desc flight_desc = { "Flight", "A struct sequence containing parameters for the flight holdable item.", flight_fields, - (sizeof(flight_fields)/sizeof(PyStructSequence_Field)) - 1 -}; + (sizeof(flight_fields) / sizeof(PyStructSequence_Field)) - 1}; // Keys static PyTypeObject keys_type = {0}; @@ -229,47 +206,46 @@ static PyStructSequence_Field keys_fields[] = { {"silver", NULL}, {"gold", NULL}, {"master", NULL}, - {NULL} -}; + {NULL}}; static PyStructSequence_Desc keys_desc = { "Keys", "A struct sequence containing all the keys in the game.", keys_fields, - (sizeof(keys_fields)/sizeof(PyStructSequence_Field)) - 1 -}; + (sizeof(keys_fields) / sizeof(PyStructSequence_Field)) - 1}; /* * ================================================================ * player_info/players_info * ================================================================ -*/ + */ static PyObject* makePlayerTuple(int client_id) { PyObject *name, *team, *priv; PyObject* cid = PyLong_FromLongLong(client_id); if (g_entities[client_id].client != NULL) { - if (g_entities[client_id].client->pers.connected == CON_DISCONNECTED) + if (g_entities[client_id].client->pers.connected == CON_DISCONNECTED) { name = PyUnicode_FromString(""); - else + } else { name = PyUnicode_DecodeUTF8(g_entities[client_id].client->pers.netname, - strlen(g_entities[client_id].client->pers.netname), "ignore"); + strlen(g_entities[client_id].client->pers.netname), "ignore"); + } - if (g_entities[client_id].client->pers.connected == CON_DISCONNECTED) + if (g_entities[client_id].client->pers.connected == CON_DISCONNECTED) { team = PyLong_FromLongLong(TEAM_SPECTATOR); // Set team to spectator if not yet connected. - else + } else { team = PyLong_FromLongLong(g_entities[client_id].client->sess.sessionTeam); + } priv = PyLong_FromLongLong(g_entities[client_id].client->sess.privileges); - } - else { + } else { name = PyUnicode_FromString(""); team = PyLong_FromLongLong(TEAM_SPECTATOR); priv = PyLong_FromLongLong(-1); } - PyObject* state = PyLong_FromLongLong(svs->clients[client_id].state); + PyObject* state = PyLong_FromLongLong(svs->clients[client_id].state); PyObject* userinfo = PyUnicode_DecodeUTF8(svs->clients[client_id].userinfo, strlen(svs->clients[client_id].userinfo), "ignore"); PyObject* steam_id = PyLong_FromLongLong(svs->clients[client_id].steam_id); @@ -285,42 +261,43 @@ static PyObject* makePlayerTuple(int client_id) { return info; } -static PyObject* PyMinqlx_PlayerInfo(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_PlayerInfo(PyObject* self, PyObject* args) { int i; - if (!PyArg_ParseTuple(args, "i:player", &i)) + if (!PyArg_ParseTuple(args, "i:player", &i)) { return NULL; + } if (i < 0 || i >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - - } - else if (allow_free_client != i && svs->clients[i].state == CS_FREE) { - #ifndef NDEBUG - DebugPrint("WARNING: PyMinqlx_PlayerInfo called for CS_FREE client %d.\n", i); - #endif + } else if (allow_free_client != i && svs->clients[i].state == CS_FREE) { +#ifndef NDEBUG + DebugPrint("WARNING: PyMinqlxtended_PlayerInfo called for CS_FREE client %d.\n", i); +#endif Py_RETURN_NONE; } return makePlayerTuple(i); } -static PyObject* PyMinqlx_PlayersInfo(PyObject* self, PyObject* args) { - PyObject* ret = PyList_New(sv_maxclients->integer); +static PyObject* PyMinqlxtended_PlayersInfo(PyObject* self, PyObject* args) { + PyObject* ret = PyList_New(sv_maxclients->integer); - for (int i = 0; i < sv_maxclients->integer; i++) { - if (svs->clients[i].state == CS_FREE) { - if (PyList_SetItem(ret, i, Py_None) == -1) - return NULL; + for (int i = 0; i < sv_maxclients->integer; i++) { + if (svs->clients[i].state == CS_FREE) { + if (PyList_SetItem(ret, i, Py_None) == -1) { + return NULL; + } Py_INCREF(Py_None); continue; - } + } - if (PyList_SetItem(ret, i, makePlayerTuple(i)) == -1) - return NULL; - } + if (PyList_SetItem(ret, i, makePlayerTuple(i)) == -1) { + return NULL; + } + } return ret; } @@ -329,22 +306,22 @@ static PyObject* PyMinqlx_PlayersInfo(PyObject* self, PyObject* args) { * ================================================================ * get_userinfo * ================================================================ -*/ + */ -static PyObject* PyMinqlx_GetUserinfo(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_GetUserinfo(PyObject* self, PyObject* args) { int i; - if (!PyArg_ParseTuple(args, "i:get_userinfo", &i)) + if (!PyArg_ParseTuple(args, "i:get_userinfo", &i)) { return NULL; + } if (i < 0 || i >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - - } - else if (allow_free_client != i && svs->clients[i].state == CS_FREE) + } else if (allow_free_client != i && svs->clients[i].state == CS_FREE) { Py_RETURN_NONE; + } return PyUnicode_DecodeUTF8(svs->clients[i].userinfo, strlen(svs->clients[i].userinfo), "ignore"); } @@ -353,25 +330,25 @@ static PyObject* PyMinqlx_GetUserinfo(PyObject* self, PyObject* args) { * ================================================================ * send_server_command * ================================================================ -*/ + */ -static PyObject* PyMinqlx_SendServerCommand(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SendServerCommand(PyObject* self, PyObject* args) { PyObject* client_id; int i; char* cmd; - if (!PyArg_ParseTuple(args, "Os:send_server_command", &client_id, &cmd)) + if (!PyArg_ParseTuple(args, "Os:send_server_command", &client_id, &cmd)) { return NULL; + } if (client_id == Py_None) { My_SV_SendServerCommand(NULL, "%s\n", cmd); // Send to all. Py_RETURN_TRUE; - } - else if (PyLong_Check(client_id)) { + } else if (PyLong_Check(client_id)) { i = PyLong_AsLong(client_id); if (i >= 0 && i < sv_maxclients->integer) { - if (svs->clients[i].state != CS_ACTIVE) + if (svs->clients[i].state != CS_ACTIVE) { Py_RETURN_FALSE; - else { + } else { My_SV_SendServerCommand(&svs->clients[i], "%s\n", cmd); Py_RETURN_TRUE; } @@ -388,22 +365,23 @@ static PyObject* PyMinqlx_SendServerCommand(PyObject* self, PyObject* args) { * ================================================================ * client_command * ================================================================ -*/ + */ -static PyObject* PyMinqlx_ClientCommand(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_ClientCommand(PyObject* self, PyObject* args) { int i; char* cmd; - if (!PyArg_ParseTuple(args, "is:client_command", &i, &cmd)) + if (!PyArg_ParseTuple(args, "is:client_command", &i, &cmd)) { return NULL; + } - if (i >= 0 && i < sv_maxclients->integer) { - if (svs->clients[i].state == CS_FREE || svs->clients[i].state == CS_ZOMBIE) - Py_RETURN_FALSE; - else { - My_SV_ExecuteClientCommand(&svs->clients[i], cmd, qtrue); - Py_RETURN_TRUE; - } - } + if (i >= 0 && i < sv_maxclients->integer) { + if (svs->clients[i].state == CS_FREE || svs->clients[i].state == CS_ZOMBIE) { + Py_RETURN_FALSE; + } else { + My_SV_ExecuteClientCommand(&svs->clients[i], cmd, qtrue); + Py_RETURN_TRUE; + } + } PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d, or None.", @@ -415,12 +393,13 @@ static PyObject* PyMinqlx_ClientCommand(PyObject* self, PyObject* args) { * ================================================================ * console_command * ================================================================ -*/ + */ -static PyObject* PyMinqlx_ConsoleCommand(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_ConsoleCommand(PyObject* self, PyObject* args) { char* cmd; - if (!PyArg_ParseTuple(args, "s:console_command", &cmd)) + if (!PyArg_ParseTuple(args, "s:console_command", &cmd)) { return NULL; + } Cmd_ExecuteString(cmd); @@ -431,16 +410,17 @@ static PyObject* PyMinqlx_ConsoleCommand(PyObject* self, PyObject* args) { * ================================================================ * get_cvar * ================================================================ -*/ + */ -static PyObject* PyMinqlx_GetCvar(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_GetCvar(PyObject* self, PyObject* args) { char* name; - if (!PyArg_ParseTuple(args, "s:get_cvar", &name)) + if (!PyArg_ParseTuple(args, "s:get_cvar", &name)) { return NULL; + } cvar_t* cvar = Cvar_FindVar(name); if (cvar) { - return PyUnicode_FromString(cvar->string); + return PyUnicode_FromString(cvar->string); } Py_RETURN_NONE; @@ -450,13 +430,14 @@ static PyObject* PyMinqlx_GetCvar(PyObject* self, PyObject* args) { * ================================================================ * set_cvar * ================================================================ -*/ + */ -static PyObject* PyMinqlx_SetCvar(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetCvar(PyObject* self, PyObject* args) { char *name, *value; int flags = 0; - if (!PyArg_ParseTuple(args, "ss|i:set_cvar", &name, &value, &flags)) + if (!PyArg_ParseTuple(args, "ss|i:set_cvar", &name, &value, &flags)) { return NULL; + } cvar_t* var = Cvar_FindVar(name); if (!var) { @@ -464,10 +445,11 @@ static PyObject* PyMinqlx_SetCvar(PyObject* self, PyObject* args) { Py_RETURN_TRUE; } - if (flags == -1) + if (flags == -1) { Cvar_Set2(name, value, qtrue); - else + } else { Cvar_Set2(name, value, qfalse); + } Py_RETURN_FALSE; } @@ -476,13 +458,14 @@ static PyObject* PyMinqlx_SetCvar(PyObject* self, PyObject* args) { * ================================================================ * set_cvar_limit * ================================================================ -*/ + */ -static PyObject* PyMinqlx_SetCvarLimit(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetCvarLimit(PyObject* self, PyObject* args) { char *name, *value, *min, *max; int flags = 0; - if (!PyArg_ParseTuple(args, "ssss|i:set_cvar_limit", &name, &value, &min, &max, &flags)) + if (!PyArg_ParseTuple(args, "ssss|i:set_cvar_limit", &name, &value, &min, &max, &flags)) { return NULL; + } Cvar_GetLimit(name, value, min, max, flags); Py_RETURN_NONE; @@ -492,34 +475,33 @@ static PyObject* PyMinqlx_SetCvarLimit(PyObject* self, PyObject* args) { * ================================================================ * kick * ================================================================ -*/ + */ -static PyObject* PyMinqlx_Kick(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_Kick(PyObject* self, PyObject* args) { int i; PyObject* reason; - if (!PyArg_ParseTuple(args, "iO:kick", &i, &reason)) - return NULL; - - if (i >= 0 && i < sv_maxclients->integer) { - if (svs->clients[i].state != CS_ACTIVE) { - PyErr_Format(PyExc_ValueError, - "client_id must be None or the ID of an active player."); - return NULL; - } - else if (reason == Py_None || (PyUnicode_Check(reason) && PyUnicode_AsUTF8(reason)[0] == 0)) { - // Default kick message for None or empty strings. - My_SV_DropClient(&svs->clients[i], "was kicked."); - } - else if (PyUnicode_Check(reason)) { - My_SV_DropClient(&svs->clients[i], PyUnicode_AsUTF8(reason)); - } - } - else { - PyErr_Format(PyExc_ValueError, - "client_id needs to be a number from 0 to %d, or None.", - sv_maxclients->integer); - return NULL; - } + + if (!PyArg_ParseTuple(args, "iO:kick", &i, &reason)) { + return NULL; + } + + if (i >= 0 && i < sv_maxclients->integer) { + if (svs->clients[i].state != CS_ACTIVE) { + PyErr_Format(PyExc_ValueError, + "client_id must be None or the ID of an active player."); + return NULL; + } else if (reason == Py_None || (PyUnicode_Check(reason) && PyUnicode_AsUTF8(reason)[0] == 0)) { + // Default kick message for None or empty strings. + My_SV_DropClient(&svs->clients[i], "was kicked."); + } else if (PyUnicode_Check(reason)) { + My_SV_DropClient(&svs->clients[i], PyUnicode_AsUTF8(reason)); + } + } else { + PyErr_Format(PyExc_ValueError, + "client_id needs to be a number from 0 to %d, or None.", + sv_maxclients->integer); + return NULL; + } Py_RETURN_NONE; } @@ -528,12 +510,13 @@ static PyObject* PyMinqlx_Kick(PyObject* self, PyObject* args) { * ================================================================ * console_print * ================================================================ -*/ + */ -static PyObject* PyMinqlx_ConsolePrint(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_ConsolePrint(PyObject* self, PyObject* args) { char* text; - if (!PyArg_ParseTuple(args, "s:console_print", &text)) + if (!PyArg_ParseTuple(args, "s:console_print", &text)) { return NULL; + } My_Com_Printf("%s\n", text); @@ -544,19 +527,21 @@ static PyObject* PyMinqlx_ConsolePrint(PyObject* self, PyObject* args) { * ================================================================ * get_configstring * ================================================================ -*/ + */ -static PyObject* PyMinqlx_GetConfigstring(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_GetConfigstring(PyObject* self, PyObject* args) { int i; char csbuffer[4096]; - if (!PyArg_ParseTuple(args, "i:get_configstring", &i)) + if (!PyArg_ParseTuple(args, "i:get_configstring", &i)) { return NULL; + } + else if (i < 0 || i > MAX_CONFIGSTRINGS) { - PyErr_Format(PyExc_ValueError, - "index needs to be a number from 0 to %d.", - MAX_CONFIGSTRINGS); - return NULL; - } + PyErr_Format(PyExc_ValueError, + "index needs to be a number from 0 to %d.", + MAX_CONFIGSTRINGS); + return NULL; + } SV_GetConfigstring(i, csbuffer, sizeof(csbuffer)); return PyUnicode_DecodeUTF8(csbuffer, strlen(csbuffer), "ignore"); @@ -566,18 +551,20 @@ static PyObject* PyMinqlx_GetConfigstring(PyObject* self, PyObject* args) { * ================================================================ * set_configstring * ================================================================ -*/ + */ -static PyObject* PyMinqlx_SetConfigstring(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetConfigstring(PyObject* self, PyObject* args) { int i; char* cs; - if (!PyArg_ParseTuple(args, "is:set_configstring", &i, &cs)) + if (!PyArg_ParseTuple(args, "is:set_configstring", &i, &cs)) { return NULL; + } + else if (i < 0 || i > MAX_CONFIGSTRINGS) { - PyErr_Format(PyExc_ValueError, - "index needs to be a number from 0 to %d.", - MAX_CONFIGSTRINGS); - return NULL; + PyErr_Format(PyExc_ValueError, + "index needs to be a number from 0 to %d.", + MAX_CONFIGSTRINGS); + return NULL; } My_SV_SetConfigstring(i, cs); @@ -589,27 +576,27 @@ static PyObject* PyMinqlx_SetConfigstring(PyObject* self, PyObject* args) { * ================================================================ * force_vote * ================================================================ -*/ + */ -static PyObject* PyMinqlx_ForceVote(PyObject* self, PyObject* args) { - int pass; - if (!PyArg_ParseTuple(args, "p:force_vote", &pass)) +static PyObject* PyMinqlxtended_ForceVote(PyObject* self, PyObject* args) { + int pass; + if (!PyArg_ParseTuple(args, "p:force_vote", &pass)) { return NULL; + } if (!level->voteTime) { - // No active vote. - Py_RETURN_FALSE; - } - else if (pass && level->voteTime) { - // We tell the server every single client voted yes, making it pass in the next G_RunFrame. - for (int i = 0; i < sv_maxclients->integer; i++) { - if (svs->clients[i].state == CS_ACTIVE) - g_entities[i].client->pers.voteState = VOTE_YES; - } - } - else if (!pass && level->voteTime) { - // If we tell the server the vote is over, it'll fail it right away. - level->voteTime -= 30000; + // No active vote. + Py_RETURN_FALSE; + } else if (pass && level->voteTime) { + // We tell the server every single client voted yes, making it pass in the next G_RunFrame. + for (int i = 0; i < sv_maxclients->integer; i++) { + if (svs->clients[i].state == CS_ACTIVE) { + g_entities[i].client->pers.voteState = VOTE_YES; + } + } + } else if (!pass && level->voteTime) { + // If we tell the server the vote is over, it'll fail it right away. + level->voteTime -= 30000; } Py_RETURN_TRUE; @@ -619,12 +606,13 @@ static PyObject* PyMinqlx_ForceVote(PyObject* self, PyObject* args) { * ================================================================ * add_console_command * ================================================================ -*/ + */ -static PyObject* PyMinqlx_AddConsoleCommand(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_AddConsoleCommand(PyObject* self, PyObject* args) { char* cmd; - if (!PyArg_ParseTuple(args, "s:add_console_command", &cmd)) + if (!PyArg_ParseTuple(args, "s:add_console_command", &cmd)) { return NULL; + } Cmd_AddCommand(cmd, PyCommand); @@ -635,77 +623,78 @@ static PyObject* PyMinqlx_AddConsoleCommand(PyObject* self, PyObject* args) { * ================================================================ * register_handler * ================================================================ -*/ + */ -static PyObject* PyMinqlx_RegisterHandler(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_RegisterHandler(PyObject* self, PyObject* args) { char* event; PyObject* new_handler; if (!PyArg_ParseTuple(args, "sO:register_handler", &event, &new_handler)) { - return NULL; + return NULL; } + else if (new_handler != Py_None && !PyCallable_Check(new_handler)) { - PyErr_SetString(PyExc_TypeError, "The handler must be callable."); - return NULL; - } - - for (handler_t* h = handlers; h->name; h++) { - if (!strcmp(h->name, event)) { - Py_XDECREF(*h->handler); - if (new_handler == Py_None) - *h->handler = NULL; - else { - *h->handler = new_handler; - Py_INCREF(new_handler); - } - - Py_RETURN_NONE; - } - } - - PyErr_SetString(PyExc_ValueError, "Invalid event."); - return NULL; + PyErr_SetString(PyExc_TypeError, "The handler must be callable."); + return NULL; + } + + for (handler_t* h = handlers; h->name; h++) { + if (!strcmp(h->name, event)) { + Py_XDECREF(*h->handler); + if (new_handler == Py_None) { + *h->handler = NULL; + } else { + *h->handler = new_handler; + Py_INCREF(new_handler); + } + + Py_RETURN_NONE; + } + } + + PyErr_SetString(PyExc_ValueError, "Invalid event."); + return NULL; } /* * ================================================================ * player_state * ================================================================ -*/ + */ -static PyObject* PyMinqlx_PlayerState(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_PlayerState(PyObject* self, PyObject* args) { int client_id; - if (!PyArg_ParseTuple(args, "i:player_state", &client_id)) + if (!PyArg_ParseTuple(args, "i:player_state", &client_id)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_NONE; + } PyObject* state = PyStructSequence_New(&player_state_type); PyStructSequence_SetItem(state, 0, PyBool_FromLong(g_entities[client_id].client->ps.pm_type == 0)); PyObject* pos = PyStructSequence_New(&vector3_type); PyStructSequence_SetItem(pos, 0, - PyFloat_FromDouble(g_entities[client_id].client->ps.origin[0])); + PyFloat_FromDouble(g_entities[client_id].client->ps.origin[0])); PyStructSequence_SetItem(pos, 1, - PyFloat_FromDouble(g_entities[client_id].client->ps.origin[1])); + PyFloat_FromDouble(g_entities[client_id].client->ps.origin[1])); PyStructSequence_SetItem(pos, 2, - PyFloat_FromDouble(g_entities[client_id].client->ps.origin[2])); + PyFloat_FromDouble(g_entities[client_id].client->ps.origin[2])); PyStructSequence_SetItem(state, 1, pos); PyObject* vel = PyStructSequence_New(&vector3_type); PyStructSequence_SetItem(vel, 0, - PyFloat_FromDouble(g_entities[client_id].client->ps.velocity[0])); + PyFloat_FromDouble(g_entities[client_id].client->ps.velocity[0])); PyStructSequence_SetItem(vel, 1, - PyFloat_FromDouble(g_entities[client_id].client->ps.velocity[1])); + PyFloat_FromDouble(g_entities[client_id].client->ps.velocity[1])); PyStructSequence_SetItem(vel, 2, - PyFloat_FromDouble(g_entities[client_id].client->ps.velocity[2])); + PyFloat_FromDouble(g_entities[client_id].client->ps.velocity[2])); PyStructSequence_SetItem(state, 2, vel); PyStructSequence_SetItem(state, 3, PyLong_FromLongLong(g_entities[client_id].health)); @@ -715,10 +704,10 @@ static PyObject* PyMinqlx_PlayerState(PyObject* self, PyObject* args) { // Get weapons and ammo count. PyObject* weapons = PyStructSequence_New(&weapons_type); - PyObject* ammo = PyStructSequence_New(&weapons_type); + PyObject* ammo = PyStructSequence_New(&weapons_type); for (int i = 0; i < weapons_desc.n_in_sequence; i++) { - PyStructSequence_SetItem(weapons, i, PyBool_FromLong(g_entities[client_id].client->ps.stats[STAT_WEAPONS] & (1 << (i+1)))); - PyStructSequence_SetItem(ammo, i, PyLong_FromLongLong(g_entities[client_id].client->ps.ammo[i+1])); + PyStructSequence_SetItem(weapons, i, PyBool_FromLong(g_entities[client_id].client->ps.stats[STAT_WEAPONS] & (1 << (i + 1)))); + PyStructSequence_SetItem(ammo, i, PyLong_FromLongLong(g_entities[client_id].client->ps.ammo[i + 1])); } PyStructSequence_SetItem(state, 7, weapons); PyStructSequence_SetItem(state, 8, ammo); @@ -726,64 +715,67 @@ static PyObject* PyMinqlx_PlayerState(PyObject* self, PyObject* args) { PyObject* powerups = PyStructSequence_New(&powerups_type); int index; for (int i = 0; i < powerups_desc.n_in_sequence; i++) { - index = i+PW_QUAD; - if (index == PW_FLIGHT) // Skip flight. + index = i + PW_QUAD; + if (index == PW_FLIGHT) { // Skip flight. index = PW_INVULNERABILITY; + } int remaining = g_entities[client_id].client->ps.powerups[index]; - if (remaining) // We don't want the time, but the remaining time. + if (remaining) { // We don't want the time, but the remaining time. remaining -= level->time; + } PyStructSequence_SetItem(powerups, i, PyLong_FromLongLong(remaining)); } PyStructSequence_SetItem(state, 9, powerups); PyObject* holdable; switch (g_entities[client_id].client->ps.stats[STAT_HOLDABLE_ITEM]) { - case 0: - holdable = Py_None; - Py_INCREF(Py_None); - break; - case 27: - holdable = PyUnicode_FromString("teleporter"); - break; - case 28: - holdable = PyUnicode_FromString("medkit"); - break; - case 34: - holdable = PyUnicode_FromString("flight"); - break; - case 37: - holdable = PyUnicode_FromString("kamikaze"); - break; - case 38: - holdable = PyUnicode_FromString("portal"); - break; - case 39: - holdable = PyUnicode_FromString("invulnerability"); - break; - default: - holdable = PyUnicode_FromString("unknown"); + case 0: + holdable = Py_None; + Py_INCREF(Py_None); + break; + case 27: + holdable = PyUnicode_FromString("teleporter"); + break; + case 28: + holdable = PyUnicode_FromString("medkit"); + break; + case 34: + holdable = PyUnicode_FromString("flight"); + break; + case 37: + holdable = PyUnicode_FromString("kamikaze"); + break; + case 38: + holdable = PyUnicode_FromString("portal"); + break; + case 39: + holdable = PyUnicode_FromString("invulnerability"); + break; + default: + holdable = PyUnicode_FromString("unknown"); } PyStructSequence_SetItem(state, 10, holdable); PyObject* flight = PyStructSequence_New(&flight_type); PyStructSequence_SetItem(flight, 0, - PyLong_FromLongLong(g_entities[client_id].client->ps.stats[STAT_CUR_FLIGHT_FUEL])); + PyLong_FromLongLong(g_entities[client_id].client->ps.stats[STAT_CUR_FLIGHT_FUEL])); PyStructSequence_SetItem(flight, 1, - PyLong_FromLongLong(g_entities[client_id].client->ps.stats[STAT_MAX_FLIGHT_FUEL])); + PyLong_FromLongLong(g_entities[client_id].client->ps.stats[STAT_MAX_FLIGHT_FUEL])); PyStructSequence_SetItem(flight, 2, - PyLong_FromLongLong(g_entities[client_id].client->ps.stats[STAT_FLIGHT_THRUST])); + PyLong_FromLongLong(g_entities[client_id].client->ps.stats[STAT_FLIGHT_THRUST])); PyStructSequence_SetItem(flight, 3, - PyLong_FromLongLong(g_entities[client_id].client->ps.stats[STAT_FLIGHT_REFUEL])); + PyLong_FromLongLong(g_entities[client_id].client->ps.stats[STAT_FLIGHT_REFUEL])); PyStructSequence_SetItem(state, 11, flight); PyStructSequence_SetItem(state, 12, PyBool_FromLong(g_entities[client_id].client->ps.pm_type == 4)); PyObject* keys = PyStructSequence_New(&keys_type); for (int i = 0; i < keys_desc.n_in_sequence; i++) { - if (g_entities[client_id].client->ps.stats[STAT_KEY] & (1 << i)) + if (g_entities[client_id].client->ps.stats[STAT_KEY] & (1 << i)) { PyStructSequence_SetItem(keys, i, Py_True); - else + } else { PyStructSequence_SetItem(keys, i, Py_False); + } } PyStructSequence_SetItem(state, 13, keys); @@ -794,25 +786,24 @@ static PyObject* PyMinqlx_PlayerState(PyObject* self, PyObject* args) { * ================================================================ * player_stats * ================================================================ -*/ + */ -static PyObject* PyMinqlx_PlayerStats(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_PlayerStats(PyObject* self, PyObject* args) { int client_id; - if (!PyArg_ParseTuple(args, "i:player_stats", &client_id)) + if (!PyArg_ParseTuple(args, "i:player_stats", &client_id)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_NONE; + } PyObject* stats = PyStructSequence_New(&player_stats_type); - int score = g_entities[client_id].client->sess.sessionTeam == TEAM_SPECTATOR ? - 0 : g_entities[client_id].client->ps.persistant[PERS_ROUND_SCORE]; + int score = g_entities[client_id].client->sess.sessionTeam == TEAM_SPECTATOR ? 0 : g_entities[client_id].client->ps.persistant[PERS_ROUND_SCORE]; PyStructSequence_SetItem(stats, 0, PyLong_FromLongLong(score)); PyStructSequence_SetItem(stats, 1, PyLong_FromLongLong(g_entities[client_id].client->expandedStats.numKills)); PyStructSequence_SetItem(stats, 2, PyLong_FromLongLong(g_entities[client_id].client->expandedStats.numDeaths)); @@ -828,23 +819,22 @@ static PyObject* PyMinqlx_PlayerStats(PyObject* self, PyObject* args) { * ================================================================ * set_position * ================================================================ -*/ + */ -static PyObject* PyMinqlx_SetPosition(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetPosition(PyObject* self, PyObject* args) { int client_id; PyObject* new_position; - if (!PyArg_ParseTuple(args, "iO:set_position", &client_id, &new_position)) + if (!PyArg_ParseTuple(args, "iO:set_position", &client_id, &new_position)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; - else if (!PyObject_TypeCheck(new_position, &vector3_type)) { + } else if (!PyObject_TypeCheck(new_position, &vector3_type)) { PyErr_Format(PyExc_ValueError, "Argument must be of type minqlxtended.Vector3."); return NULL; } @@ -863,23 +853,22 @@ static PyObject* PyMinqlx_SetPosition(PyObject* self, PyObject* args) { * ================================================================ * set_velocity * ================================================================ -*/ + */ -static PyObject* PyMinqlx_SetVelocity(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetVelocity(PyObject* self, PyObject* args) { int client_id; PyObject* new_velocity; - if (!PyArg_ParseTuple(args, "iO:set_velocity", &client_id, &new_velocity)) + if (!PyArg_ParseTuple(args, "iO:set_velocity", &client_id, &new_velocity)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; - else if (!PyObject_TypeCheck(new_velocity, &vector3_type)) { + } else if (!PyObject_TypeCheck(new_velocity, &vector3_type)) { PyErr_Format(PyExc_ValueError, "Argument must be of type minqlxtended.Vector3."); return NULL; } @@ -895,23 +884,23 @@ static PyObject* PyMinqlx_SetVelocity(PyObject* self, PyObject* args) { } /* -* ================================================================ -* noclip -* ================================================================ -*/ + * ================================================================ + * noclip + * ================================================================ + */ -static PyObject* PyMinqlx_NoClip(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_NoClip(PyObject* self, PyObject* args) { int client_id, activate; - if (!PyArg_ParseTuple(args, "ip:noclip", &client_id, &activate)) + if (!PyArg_ParseTuple(args, "ip:noclip", &client_id, &activate)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; + } if ((activate && g_entities[client_id].client->noclip) || (!activate && !g_entities[client_id].client->noclip)) { // Change was made. @@ -923,71 +912,70 @@ static PyObject* PyMinqlx_NoClip(PyObject* self, PyObject* args) { } /* -* ================================================================ -* set_health -* ================================================================ -*/ + * ================================================================ + * set_health + * ================================================================ + */ -static PyObject* PyMinqlx_SetHealth(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetHealth(PyObject* self, PyObject* args) { int client_id, health; - if (!PyArg_ParseTuple(args, "ii:set_health", &client_id, &health)) + if (!PyArg_ParseTuple(args, "ii:set_health", &client_id, &health)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; + } g_entities[client_id].health = health; Py_RETURN_TRUE; } /* -* ================================================================ -* set_armor -* ================================================================ -*/ + * ================================================================ + * set_armor + * ================================================================ + */ -static PyObject* PyMinqlx_SetArmor(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetArmor(PyObject* self, PyObject* args) { int client_id, armor; - if (!PyArg_ParseTuple(args, "ii:set_armor", &client_id, &armor)) + if (!PyArg_ParseTuple(args, "ii:set_armor", &client_id, &armor)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; + } g_entities[client_id].client->ps.stats[STAT_ARMOR] = armor; Py_RETURN_TRUE; } /* -* ================================================================ -* set_weapons -* ================================================================ -*/ + * ================================================================ + * set_weapons + * ================================================================ + */ -static PyObject* PyMinqlx_SetWeapons(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetWeapons(PyObject* self, PyObject* args) { int client_id, weapon_flags = 0; PyObject* weapons; - if (!PyArg_ParseTuple(args, "iO:set_weapons", &client_id, &weapons)) + if (!PyArg_ParseTuple(args, "iO:set_weapons", &client_id, &weapons)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; - else if (!PyObject_TypeCheck(weapons, &weapons_type)) { + } else if (!PyObject_TypeCheck(weapons, &weapons_type)) { PyErr_Format(PyExc_ValueError, "Argument must be of type minqlxtended.Weapons."); return NULL; } @@ -1000,7 +988,7 @@ static PyObject* PyMinqlx_SetWeapons(PyObject* self, PyObject* args) { return NULL; } - weapon_flags |= w == Py_True ? (1 << (i+1)) : 0; + weapon_flags |= w == Py_True ? (1 << (i + 1)) : 0; } g_entities[client_id].client->ps.stats[STAT_WEAPONS] = weapon_flags; @@ -1008,24 +996,23 @@ static PyObject* PyMinqlx_SetWeapons(PyObject* self, PyObject* args) { } /* -* ================================================================ -* set_weapon -* ================================================================ -*/ + * ================================================================ + * set_weapon + * ================================================================ + */ -static PyObject* PyMinqlx_SetWeapon(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetWeapon(PyObject* self, PyObject* args) { int client_id, weapon; - if (!PyArg_ParseTuple(args, "ii:set_weapon", &client_id, &weapon)) + if (!PyArg_ParseTuple(args, "ii:set_weapon", &client_id, &weapon)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; - else if (weapon < 0 || weapon >= MAX_WEAPONS) { + } else if (weapon < 0 || weapon >= MAX_WEAPONS) { PyErr_Format(PyExc_ValueError, "Weapon must be a number from 0 to 15."); return NULL; } @@ -1035,25 +1022,24 @@ static PyObject* PyMinqlx_SetWeapon(PyObject* self, PyObject* args) { } /* -* ================================================================ -* set_ammo -* ================================================================ -*/ + * ================================================================ + * set_ammo + * ================================================================ + */ -static PyObject* PyMinqlx_SetAmmo(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetAmmo(PyObject* self, PyObject* args) { int client_id; PyObject* ammos; - if (!PyArg_ParseTuple(args, "iO:set_ammo", &client_id, &ammos)) + if (!PyArg_ParseTuple(args, "iO:set_ammo", &client_id, &ammos)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; - else if (!PyObject_TypeCheck(ammos, &weapons_type)) { + } else if (!PyObject_TypeCheck(ammos, &weapons_type)) { PyErr_Format(PyExc_ValueError, "Argument must be of type minqlxtended.Weapons."); return NULL; } @@ -1066,32 +1052,31 @@ static PyObject* PyMinqlx_SetAmmo(PyObject* self, PyObject* args) { return NULL; } - g_entities[client_id].client->ps.ammo[i+1] = PyLong_AsLong(a); + g_entities[client_id].client->ps.ammo[i + 1] = PyLong_AsLong(a); } Py_RETURN_TRUE; } /* -* ================================================================ -* set_powerups -* ================================================================ -*/ + * ================================================================ + * set_powerups + * ================================================================ + */ -static PyObject* PyMinqlx_SetPowerups(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetPowerups(PyObject* self, PyObject* args) { int client_id, t; PyObject* powerups; - if (!PyArg_ParseTuple(args, "iO:set_powerups", &client_id, &powerups)) + if (!PyArg_ParseTuple(args, "iO:set_powerups", &client_id, &powerups)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; - else if (!PyObject_TypeCheck(powerups, &powerups_type)) { + } else if (!PyObject_TypeCheck(powerups, &powerups_type)) { PyErr_Format(PyExc_ValueError, "Argument must be of type minqlxtended.Powerups."); return NULL; } @@ -1108,97 +1093,103 @@ static PyObject* PyMinqlx_SetPowerups(PyObject* self, PyObject* args) { // If i == 5, it'll modify flight, which isn't a real powerup. // We bump it up and modify invulnerability instead. - if (i+PW_QUAD == PW_FLIGHT) + if (i + PW_QUAD == PW_FLIGHT) { i = PW_INVULNERABILITY - PW_QUAD; + } t = PyLong_AsLong(powerup); if (!t) { - g_entities[client_id].client->ps.powerups[i+PW_QUAD] = 0; + g_entities[client_id].client->ps.powerups[i + PW_QUAD] = 0; continue; } - g_entities[client_id].client->ps.powerups[i+PW_QUAD] = level->time - (level->time % 1000) + t; + g_entities[client_id].client->ps.powerups[i + PW_QUAD] = level->time - (level->time % 1000) + t; } Py_RETURN_TRUE; } /* -* ================================================================ -* set_holdable -* ================================================================ -*/ + * ================================================================ + * set_holdable + * ================================================================ + */ -static PyObject* PyMinqlx_SetHoldable(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetHoldable(PyObject* self, PyObject* args) { int client_id, i; - if (!PyArg_ParseTuple(args, "ii:set_holdable", &client_id, &i)) + if (!PyArg_ParseTuple(args, "ii:set_holdable", &client_id, &i)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; + } - if (i == 37) // 37 - kamikaze + if (i == 37) { // 37 - kamikaze g_entities[client_id].client->ps.eFlags |= EF_KAMIKAZE; - else + } else { g_entities[client_id].client->ps.eFlags &= ~EF_KAMIKAZE; + } g_entities[client_id].client->ps.stats[STAT_HOLDABLE_ITEM] = i; Py_RETURN_TRUE; } /* -* ================================================================ -* drop_holdable -* ================================================================ -*/ - -void __cdecl Switch_Touch_Item(gentity_t *ent) { - ent->touch = (void*)Touch_Item; - ent->think = G_FreeEntity; + * ================================================================ + * drop_holdable + * ================================================================ + */ + +void __cdecl Switch_Touch_Item(gentity_t* ent) { + ent->touch = (void*)Touch_Item; + ent->think = G_FreeEntity; ent->nextthink = level->time + 29000; } -void __cdecl My_Touch_Item(gentity_t *ent, gentity_t *other, trace_t *trace) { - if (ent->parent == other) return; +void __cdecl My_Touch_Item(gentity_t* ent, gentity_t* other, trace_t* trace) { + if (ent->parent == other) { + return; + } Touch_Item(ent, other, trace); } -static PyObject* PyMinqlx_DropHoldable(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_DropHoldable(PyObject* self, PyObject* args) { int client_id, item; vec3_t velocity; vec_t angle; - if (!PyArg_ParseTuple(args, "i:drop_holdable", &client_id)) + if (!PyArg_ParseTuple(args, "i:drop_holdable", &client_id)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; + } // removing kamikaze flag (surrounding skulls) g_entities[client_id].client->ps.eFlags &= ~EF_KAMIKAZE; item = g_entities[client_id].client->ps.stats[STAT_HOLDABLE_ITEM]; - if (item == 0) Py_RETURN_FALSE; + if (item == 0) { + Py_RETURN_FALSE; + } - angle = g_entities[client_id].s.apos.trBase[1] * (M_PI*2 / 360); - velocity[0] = 150*cos(angle); - velocity[1] = 150*sin(angle); + angle = g_entities[client_id].s.apos.trBase[1] * (M_PI * 2 / 360); + velocity[0] = 150 * cos(angle); + velocity[1] = 150 * sin(angle); velocity[2] = 250; - gentity_t* entity = LaunchItem(bg_itemlist + item, g_entities[client_id].s.pos.trBase, velocity); - entity->touch = (void*)My_Touch_Item; - entity->parent = &g_entities[client_id]; - entity->think = Switch_Touch_Item; - entity->nextthink = level->time + 1000; + gentity_t* entity = LaunchItem(bg_itemlist + item, g_entities[client_id].s.pos.trBase, velocity); + entity->touch = (void*)My_Touch_Item; + entity->parent = &g_entities[client_id]; + entity->think = Switch_Touch_Item; + entity->nextthink = level->time + 1000; entity->s.pos.trTime = level->time - 500; // removing holdable from player entity @@ -1207,64 +1198,61 @@ static PyObject* PyMinqlx_DropHoldable(PyObject* self, PyObject* args) { Py_RETURN_TRUE; } - /* -* ================================================================ -* set_flight -* ================================================================ -*/ + * ================================================================ + * set_flight + * ================================================================ + */ -static PyObject* PyMinqlx_SetFlight(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetFlight(PyObject* self, PyObject* args) { int client_id; PyObject* flight; - if (!PyArg_ParseTuple(args, "iO:set_flight", &client_id, &flight)) + if (!PyArg_ParseTuple(args, "iO:set_flight", &client_id, &flight)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; - else if (!PyObject_TypeCheck(flight, &flight_type)) { + } else if (!PyObject_TypeCheck(flight, &flight_type)) { PyErr_Format(PyExc_ValueError, "Argument must be of type minqlxtended.Flight."); return NULL; } - for (int i = 0; i < flight_desc.n_in_sequence; i++) + for (int i = 0; i < flight_desc.n_in_sequence; i++) { if (!PyLong_Check(PyStructSequence_GetItem(flight, i))) { PyErr_Format(PyExc_ValueError, "Tuple argument %d is not an integer.", i); return NULL; } + } g_entities[client_id].client->ps.stats[STAT_CUR_FLIGHT_FUEL] = PyLong_AsLong(PyStructSequence_GetItem(flight, 0)); g_entities[client_id].client->ps.stats[STAT_MAX_FLIGHT_FUEL] = PyLong_AsLong(PyStructSequence_GetItem(flight, 1)); - g_entities[client_id].client->ps.stats[STAT_FLIGHT_THRUST] = PyLong_AsLong(PyStructSequence_GetItem(flight, 2)); - g_entities[client_id].client->ps.stats[STAT_FLIGHT_REFUEL] = PyLong_AsLong(PyStructSequence_GetItem(flight, 3)); + g_entities[client_id].client->ps.stats[STAT_FLIGHT_THRUST] = PyLong_AsLong(PyStructSequence_GetItem(flight, 2)); + g_entities[client_id].client->ps.stats[STAT_FLIGHT_REFUEL] = PyLong_AsLong(PyStructSequence_GetItem(flight, 3)); Py_RETURN_TRUE; } - /* -* ================================================================ -* set_keys -* ================================================================ -*/ -static PyObject* PyMinqlx_SetKeys(PyObject* self, PyObject* args) { + * ================================================================ + * set_keys + * ================================================================ + */ +static PyObject* PyMinqlxtended_SetKeys(PyObject* self, PyObject* args) { int client_id, key_flags = 0; PyObject* keys; - if (!PyArg_ParseTuple(args, "iO:set_keys", &client_id, &keys)) + if (!PyArg_ParseTuple(args, "iO:set_keys", &client_id, &keys)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, - "client_id needs to be a number from 0 to %d.", - sv_maxclients->integer); + "client_id needs to be a number from 0 to %d.", + sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; - else if (!PyObject_TypeCheck(keys, &keys_type)) { + } else if (!PyObject_TypeCheck(keys, &keys_type)) { PyErr_Format(PyExc_ValueError, "Argument must be of type minqlxtended.Keys."); return NULL; } @@ -1285,24 +1273,23 @@ static PyObject* PyMinqlx_SetKeys(PyObject* self, PyObject* args) { } /* -* ================================================================ -* set_invulnerability -* ================================================================ -*/ + * ================================================================ + * set_invulnerability + * ================================================================ + */ -static PyObject* PyMinqlx_SetInvulnerability(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetInvulnerability(PyObject* self, PyObject* args) { int client_id, time; - if (!PyArg_ParseTuple(args, "ii:set_invulnerability", &client_id, &time)) + if (!PyArg_ParseTuple(args, "ii:set_invulnerability", &client_id, &time)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; - else if (time <= 0) { + } else if (time <= 0) { PyErr_Format(PyExc_ValueError, "time needs to be positive integer."); return NULL; } @@ -1312,50 +1299,53 @@ static PyObject* PyMinqlx_SetInvulnerability(PyObject* self, PyObject* args) { } /* -* ================================================================ -* set_score -* ================================================================ -*/ + * ================================================================ + * set_score + * ================================================================ + */ -static PyObject* PyMinqlx_SetScore(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetScore(PyObject* self, PyObject* args) { int client_id, score; - if (!PyArg_ParseTuple(args, "ii:set_score", &client_id, &score)) + if (!PyArg_ParseTuple(args, "ii:set_score", &client_id, &score)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; + } g_entities[client_id].client->ps.persistant[PERS_ROUND_SCORE] = score; Py_RETURN_TRUE; } /* -* ================================================================ -* callvote -* ================================================================ -*/ + * ================================================================ + * callvote + * ================================================================ + */ -static PyObject* PyMinqlx_Callvote(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_Callvote(PyObject* self, PyObject* args) { char *vote, *vote_disp; int vote_time = 30; char buf[64]; - if (!PyArg_ParseTuple(args, "ss|i:callvote", &vote, &vote_disp, &vote_time)) + if (!PyArg_ParseTuple(args, "ss|i:callvote", &vote, &vote_disp, &vote_time)) { return NULL; + } strncpy(level->voteString, vote, sizeof(level->voteString)); strncpy(level->voteDisplayString, vote_disp, sizeof(level->voteDisplayString)); level->voteTime = (level->time - 30000) + vote_time * 1000; - level->voteYes = 0; - level->voteNo = 0; + level->voteYes = 0; + level->voteNo = 0; - for (int i = 0; i < sv_maxclients->integer; i++) - if (g_entities[i].client) + for (int i = 0; i < sv_maxclients->integer; i++) { + if (g_entities[i].client) { g_entities[i].client->pers.voteState = VOTE_PENDING; + } + } My_SV_SetConfigstring(CS_VOTE_STRING, level->voteDisplayString); snprintf(buf, sizeof(buf), "%d", level->voteTime); @@ -1367,42 +1357,44 @@ static PyObject* PyMinqlx_Callvote(PyObject* self, PyObject* args) { } /* -* ================================================================ -* allow_single_player -* ================================================================ -*/ + * ================================================================ + * allow_single_player + * ================================================================ + */ -static PyObject* PyMinqlx_AllowSinglePlayer(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_AllowSinglePlayer(PyObject* self, PyObject* args) { int x; - if (!PyArg_ParseTuple(args, "p:allow_single_player", &x)) + if (!PyArg_ParseTuple(args, "p:allow_single_player", &x)) { return NULL; + } - if (x) + if (x) { level->mapIsTrainingMap = qtrue; - else + } else { level->mapIsTrainingMap = qfalse; + } Py_RETURN_NONE; } /* -* ================================================================ -* player_spawn -* ================================================================ -*/ + * ================================================================ + * player_spawn + * ================================================================ + */ -static PyObject* PyMinqlx_PlayerSpawn(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_PlayerSpawn(PyObject* self, PyObject* args) { int client_id; - if (!PyArg_ParseTuple(args, "i:player_spawn", &client_id)) + if (!PyArg_ParseTuple(args, "i:player_spawn", &client_id)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; + } g_entities[client_id].client->ps.pm_type = PM_NORMAL; My_ClientSpawn(&g_entities[client_id]); @@ -1410,64 +1402,67 @@ static PyObject* PyMinqlx_PlayerSpawn(PyObject* self, PyObject* args) { } /* -* ================================================================ -* set_privileges -* ================================================================ -*/ + * ================================================================ + * set_privileges + * ================================================================ + */ -static PyObject* PyMinqlx_SetPrivileges(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SetPrivileges(PyObject* self, PyObject* args) { int client_id, priv; - if (!PyArg_ParseTuple(args, "ii:set_privileges", &client_id, &priv)) + if (!PyArg_ParseTuple(args, "ii:set_privileges", &client_id, &priv)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; + } g_entities[client_id].client->sess.privileges = priv; Py_RETURN_TRUE; } /* -* ================================================================ -* destroy_kamikaze_timers -* ================================================================ -*/ + * ================================================================ + * destroy_kamikaze_timers + * ================================================================ + */ -static PyObject* PyMinqlx_DestroyKamikazeTimers(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_DestroyKamikazeTimers(PyObject* self, PyObject* args) { int i; gentity_t* ent; for (i = 0; i < MAX_GENTITIES; i++) { ent = &g_entities[i]; - if (!ent->inuse) + if (!ent->inuse) { continue; + } // removing kamikaze skull from dead body if (ent->client && ent->health <= 0) { ent->client->ps.eFlags &= ~EF_KAMIKAZE; } - if (strcmp(ent->classname, "kamikaze timer") == 0) + if (strcmp(ent->classname, "kamikaze timer") == 0) { G_FreeEntity(ent); + } } Py_RETURN_TRUE; } /* -* ================================================================ -* spawn_item -* ================================================================ -*/ + * ================================================================ + * spawn_item + * ================================================================ + */ -static PyObject* PyMinqlx_SpawnItem(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SpawnItem(PyObject* self, PyObject* args) { int item_id, x, y, z; - if (!PyArg_ParseTuple(args, "iiii:spawn_item", &item_id, &x, &y, &z)) + if (!PyArg_ParseTuple(args, "iiii:spawn_item", &item_id, &x, &y, &z)) { return NULL; + } if (item_id < 1 || item_id >= bg_numItems) { PyErr_Format(PyExc_ValueError, "item_id needs to be a number from 1 to %d.", @@ -1475,147 +1470,149 @@ static PyObject* PyMinqlx_SpawnItem(PyObject* self, PyObject* args) { return NULL; } - vec3_t origin = {x, y, z}; + vec3_t origin = {x, y, z}; vec3_t velocity = {0}; gentity_t* ent = LaunchItem(bg_itemlist + item_id, origin, velocity); ent->nextthink = 0; - ent->think = 0; + ent->think = 0; G_AddEvent(ent, EV_ITEM_RESPAWN, 0); // make item be scaled up Py_RETURN_TRUE; } /* -* ================================================================ -* remove_dropped_items -* ================================================================ -*/ + * ================================================================ + * remove_dropped_items + * ================================================================ + */ -static PyObject* PyMinqlx_RemoveDroppedItems(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_RemoveDroppedItems(PyObject* self, PyObject* args) { int i; gentity_t* ent; for (i = 0; i < MAX_GENTITIES; i++) { ent = &g_entities[i]; - if (!ent->inuse) + if (!ent->inuse) { continue; + } - if (ent->flags & FL_DROPPED_ITEM) + if (ent->flags & FL_DROPPED_ITEM) { G_FreeEntity(ent); + } } Py_RETURN_TRUE; } /* -* ================================================================ -* slay_with_mod -* ================================================================ -*/ + * ================================================================ + * slay_with_mod + * ================================================================ + */ // it is alternative to Slay from command.c -static PyObject* PyMinqlx_SlayWithMod(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_SlayWithMod(PyObject* self, PyObject* args) { int client_id, mod; - if (!PyArg_ParseTuple(args, "ii:slay_with_mod", &client_id, &mod)) + if (!PyArg_ParseTuple(args, "ii:slay_with_mod", &client_id, &mod)) { return NULL; - else if (client_id < 0 || client_id >= sv_maxclients->integer) { + } else if (client_id < 0 || client_id >= sv_maxclients->integer) { PyErr_Format(PyExc_ValueError, "client_id needs to be a number from 0 to %d.", sv_maxclients->integer); return NULL; - } - else if (!g_entities[client_id].client) + } else if (!g_entities[client_id].client) { Py_RETURN_FALSE; - else if (g_entities[client_id].health <= 0) + } else if (g_entities[client_id].health <= 0) { Py_RETURN_TRUE; + } gentity_t* ent = &g_entities[client_id]; - int damage = g_entities[client_id].health + (mod == MOD_KAMIKAZE ? 100000 : 0); + int damage = g_entities[client_id].health + (mod == MOD_KAMIKAZE ? 100000 : 0); g_entities[client_id].client->ps.stats[STAT_ARMOR] = 0; // self damage = half damage, so multiplaying by 2 - G_Damage( ent, ent, ent, NULL, NULL, damage*2, DAMAGE_NO_PROTECTION, mod ); + G_Damage(ent, ent, ent, NULL, NULL, damage * 2, DAMAGE_NO_PROTECTION, mod); Py_RETURN_TRUE; } /* -* ================================================================ -* replace_items -* ================================================================ -*/ + * ================================================================ + * replace_items + * ================================================================ + */ void replace_item_core(gentity_t* ent, int item_id) { char csbuffer[4096]; if (item_id) { ent->s.modelindex = item_id; - ent->classname = bg_itemlist[item_id].classname; - ent->item = &bg_itemlist[item_id]; + ent->classname = bg_itemlist[item_id].classname; + ent->item = &bg_itemlist[item_id]; // this forces client to load new item SV_GetConfigstring(CS_ITEMS, csbuffer, sizeof(csbuffer)); csbuffer[item_id] = '1'; My_SV_SetConfigstring(CS_ITEMS, csbuffer); - } else { G_FreeEntity(ent); } } -static PyObject* PyMinqlx_ReplaceItems(PyObject* self, PyObject* args) { - PyObject *arg1, *arg2 ; +static PyObject* PyMinqlxtended_ReplaceItems(PyObject* self, PyObject* args) { + PyObject *arg1, *arg2; int entity_id = 0, item_id = 0; - #if PY_VERSION_HEX < ((3 << 24) | (7 << 16)) +#if PY_VERSION_HEX < ((3 << 24) | (7 << 16)) char *entity_classname = NULL, *item_classname = NULL; - #else +#else const char *entity_classname = NULL, *item_classname = NULL; - #endif +#endif gentity_t* ent; - - if (!PyArg_ParseTuple(args, "OO:replace_items", &arg1, &arg2)) + if (!PyArg_ParseTuple(args, "OO:replace_items", &arg1, &arg2)) { return NULL; + } // checking type of first arg if (PyLong_Check(arg1)) { entity_id = PyLong_AsLong(arg1); - } else if (PyUnicode_Check(arg1)) + } else if (PyUnicode_Check(arg1)) { entity_classname = PyUnicode_AsUTF8(arg1); - else { + } else { PyErr_Format(PyExc_ValueError, "entity needs to be type of int or string."); return NULL; } // checking type of second arg - if (PyLong_Check(arg2)) + if (PyLong_Check(arg2)) { item_id = PyLong_AsLong(arg2); - else if (PyUnicode_Check(arg2)) + } else if (PyUnicode_Check(arg2)) { item_classname = PyUnicode_AsUTF8(arg2); - else { + } else { PyErr_Format(PyExc_ValueError, "item needs to be type of int or string."); return NULL; } // convert second arg to item_id, if needed - int i=1; - if (item_classname == NULL) i=bg_numItems; - for (; i= bg_numItems) { - // throw error if invalid item_id - PyErr_Format(PyExc_ValueError, "item_id needs to be between 0 and %d.", bg_numItems-1); + PyErr_Format(PyExc_ValueError, "item_id needs to be between 0 and %d.", bg_numItems - 1); return NULL; } @@ -1626,7 +1623,7 @@ static PyObject* PyMinqlx_ReplaceItems(PyObject* self, PyObject* args) { // entity_id checking if (entity_id < 0 || entity_id >= MAX_GENTITIES) { - PyErr_Format(PyExc_ValueError, "entity_id needs to be between 0 and %d.", MAX_GENTITIES-1); + PyErr_Format(PyExc_ValueError, "entity_id needs to be between 0 and %d.", MAX_GENTITIES - 1); return NULL; } else if (g_entities[entity_id].inuse == 0) { PyErr_Format(PyExc_ValueError, "entity #%d is not in use.", entity_id); @@ -1639,19 +1636,20 @@ static PyObject* PyMinqlx_ReplaceItems(PyObject* self, PyObject* args) { Com_Printf("%s\n", g_entities[entity_id].classname); replace_item_core(&g_entities[entity_id], item_id); Py_RETURN_TRUE; - } else { // replacing items by entity_classname int is_entity_found = 0; - for (i=0; iinuse) + if (!ent->inuse) { continue; + } - if (ent->s.eType != ET_ITEM) + if (ent->s.eType != ET_ITEM) { continue; + } if (strcmp(ent->classname, entity_classname) == 0) { is_entity_found = 1; @@ -1659,38 +1657,41 @@ static PyObject* PyMinqlx_ReplaceItems(PyObject* self, PyObject* args) { } } - if (is_entity_found) + if (is_entity_found) { Py_RETURN_TRUE; - else - Py_RETURN_FALSE; + } + + Py_RETURN_FALSE; } } /* -* ================================================================ -* dev_print_items -* ================================================================ -*/ + * ================================================================ + * dev_print_items + * ================================================================ + */ -static PyObject* PyMinqlx_DevPrintItems(PyObject* self, PyObject* args) { +static PyObject* PyMinqlxtended_DevPrintItems(PyObject* self, PyObject* args) { gentity_t* ent; char buffer[1024], temp_buffer[1024]; int buffer_index = 0; size_t chars_written; - char format[] = "%d %s\n"; + char format[] = "%d %s\n"; qboolean is_buffer_enough = qtrue; // default results sprintf(buffer, "No items found in the map"); - for (int i=0; iinuse) + if (!ent->inuse) { continue; + } - if (ent->s.eType != ET_ITEM) + if (ent->s.eType != ET_ITEM) { continue; + } chars_written = sprintf(temp_buffer, format, i, ent->classname); if (is_buffer_enough && buffer_index + chars_written >= sizeof(buffer)) { @@ -1707,42 +1708,47 @@ static PyObject* PyMinqlx_DevPrintItems(PyObject* self, PyObject* args) { buffer_index += chars_written; } - if (is_buffer_enough) + if (is_buffer_enough) { SV_SendServerCommand(NULL, "print \"%s\"", buffer); + } Py_RETURN_NONE; } /* -* ================================================================ -* force_weapon_respawn_time -* ================================================================ -*/ + * ================================================================ + * force_weapon_respawn_time + * ================================================================ + */ -static PyObject* PyMinqlx_ForceWeaponRespawnTime(PyObject* self, PyObject* args) { - int respawn_time; +static PyObject* PyMinqlxtended_ForceWeaponRespawnTime(PyObject* self, PyObject* args) { + int respawn_time; gentity_t* ent; - - if (!PyArg_ParseTuple(args, "i:force_weapon_respawn_time", &respawn_time)) - return NULL; - - if (respawn_time < 0) { + + if (!PyArg_ParseTuple(args, "i:force_weapon_respawn_time", &respawn_time)) { + return NULL; + } + + if (respawn_time < 0) { PyErr_Format(PyExc_ValueError, "respawn time needs to be an integer 0 or greater"); return NULL; - } + } - for (int i=0; iinuse) + if (!ent->inuse) { continue; + } - if (ent->s.eType != ET_ITEM || ent->item == NULL) - continue; - - if (ent->item->giType != IT_WEAPON) - continue; - - ent->wait = respawn_time; + if (ent->s.eType != ET_ITEM || ent->item == NULL) { + continue; + } + + if (ent->item->giType != IT_WEAPON) { + continue; + } + + ent->wait = respawn_time; } Py_RETURN_TRUE; @@ -1752,117 +1758,115 @@ static PyObject* PyMinqlx_ForceWeaponRespawnTime(PyObject* self, PyObject* args) * ================================================================ * Module definition and initialization * ================================================================ -*/ + */ static PyMethodDef minqlxtendedMethods[] = { - {"player_info", PyMinqlx_PlayerInfo, METH_VARARGS, + {"player_info", PyMinqlxtended_PlayerInfo, METH_VARARGS, "Returns a dictionary with information about a player by ID."}, - {"players_info", PyMinqlx_PlayersInfo, METH_NOARGS, - "Returns a list with dictionaries with information about all the players on the server."}, - {"get_userinfo", PyMinqlx_GetUserinfo, METH_VARARGS, - "Returns a string with a player's userinfo."}, - {"send_server_command", PyMinqlx_SendServerCommand, METH_VARARGS, + {"players_info", PyMinqlxtended_PlayersInfo, METH_NOARGS, + "Returns a list with dictionaries with information about all the players on the server."}, + {"get_userinfo", PyMinqlxtended_GetUserinfo, METH_VARARGS, + "Returns a string with a player's userinfo."}, + {"send_server_command", PyMinqlxtended_SendServerCommand, METH_VARARGS, "Sends a server command to either one specific client or all the clients."}, - {"client_command", PyMinqlx_ClientCommand, METH_VARARGS, - "Tells the server to process a command from a specific client."}, - {"console_command", PyMinqlx_ConsoleCommand, METH_VARARGS, - "Executes a command as if it was executed from the server console."}, - {"get_cvar", PyMinqlx_GetCvar, METH_VARARGS, - "Gets a cvar."}, - {"set_cvar", PyMinqlx_SetCvar, METH_VARARGS, - "Sets a cvar."}, - {"set_cvar_limit", PyMinqlx_SetCvarLimit, METH_VARARGS, + {"client_command", PyMinqlxtended_ClientCommand, METH_VARARGS, + "Tells the server to process a command from a specific client."}, + {"console_command", PyMinqlxtended_ConsoleCommand, METH_VARARGS, + "Executes a command as if it was executed from the server console."}, + {"get_cvar", PyMinqlxtended_GetCvar, METH_VARARGS, + "Gets a cvar."}, + {"set_cvar", PyMinqlxtended_SetCvar, METH_VARARGS, + "Sets a cvar."}, + {"set_cvar_limit", PyMinqlxtended_SetCvarLimit, METH_VARARGS, "Sets a non-string cvar with a minimum and maximum value."}, - {"kick", PyMinqlx_Kick, METH_VARARGS, - "Kick a player and allowing the admin to supply a reason for it."}, - {"console_print", PyMinqlx_ConsolePrint, METH_VARARGS, - "Prints text on the console. If used during an RCON command, it will be printed in the player's console."}, - {"get_configstring", PyMinqlx_GetConfigstring, METH_VARARGS, - "Get a configstring."}, - {"set_configstring", PyMinqlx_SetConfigstring, METH_VARARGS, - "Sets a configstring and sends it to all the players on the server."}, - {"force_vote", PyMinqlx_ForceVote, METH_VARARGS, - "Forces the current vote to either fail or pass."}, - {"add_console_command", PyMinqlx_AddConsoleCommand, METH_VARARGS, - "Adds a console command that will be handled by Python code."}, - {"register_handler", PyMinqlx_RegisterHandler, METH_VARARGS, + {"kick", PyMinqlxtended_Kick, METH_VARARGS, + "Kick a player and allowing the admin to supply a reason for it."}, + {"console_print", PyMinqlxtended_ConsolePrint, METH_VARARGS, + "Prints text on the console. If used during an RCON command, it will be printed in the player's console."}, + {"get_configstring", PyMinqlxtended_GetConfigstring, METH_VARARGS, + "Get a configstring."}, + {"set_configstring", PyMinqlxtended_SetConfigstring, METH_VARARGS, + "Sets a configstring and sends it to all the players on the server."}, + {"force_vote", PyMinqlxtended_ForceVote, METH_VARARGS, + "Forces the current vote to either fail or pass."}, + {"add_console_command", PyMinqlxtended_AddConsoleCommand, METH_VARARGS, + "Adds a console command that will be handled by Python code."}, + {"register_handler", PyMinqlxtended_RegisterHandler, METH_VARARGS, "Register an event handler. Can be called more than once per event, but only the last one will work."}, - {"player_state", PyMinqlx_PlayerState, METH_VARARGS, + {"player_state", PyMinqlxtended_PlayerState, METH_VARARGS, "Get information about the player's state in the game."}, - {"player_stats", PyMinqlx_PlayerStats, METH_VARARGS, + {"player_stats", PyMinqlxtended_PlayerStats, METH_VARARGS, "Get some player stats."}, - {"set_position", PyMinqlx_SetPosition, METH_VARARGS, + {"set_position", PyMinqlxtended_SetPosition, METH_VARARGS, "Sets a player's position vector."}, - {"set_velocity", PyMinqlx_SetVelocity, METH_VARARGS, + {"set_velocity", PyMinqlxtended_SetVelocity, METH_VARARGS, "Sets a player's velocity vector."}, - {"noclip", PyMinqlx_NoClip, METH_VARARGS, + {"noclip", PyMinqlxtended_NoClip, METH_VARARGS, "Sets noclip for a player."}, - {"set_health", PyMinqlx_SetHealth, METH_VARARGS, + {"set_health", PyMinqlxtended_SetHealth, METH_VARARGS, "Sets a player's health."}, - {"set_armor", PyMinqlx_SetArmor, METH_VARARGS, + {"set_armor", PyMinqlxtended_SetArmor, METH_VARARGS, "Sets a player's armor."}, - {"set_weapons", PyMinqlx_SetWeapons, METH_VARARGS, + {"set_weapons", PyMinqlxtended_SetWeapons, METH_VARARGS, "Sets a player's weapons."}, - {"set_weapon", PyMinqlx_SetWeapon, METH_VARARGS, + {"set_weapon", PyMinqlxtended_SetWeapon, METH_VARARGS, "Sets a player's current weapon."}, - {"set_ammo", PyMinqlx_SetAmmo, METH_VARARGS, + {"set_ammo", PyMinqlxtended_SetAmmo, METH_VARARGS, "Sets a player's ammo."}, - {"set_powerups", PyMinqlx_SetPowerups, METH_VARARGS, + {"set_powerups", PyMinqlxtended_SetPowerups, METH_VARARGS, "Sets a player's powerups."}, - {"set_holdable", PyMinqlx_SetHoldable, METH_VARARGS, + {"set_holdable", PyMinqlxtended_SetHoldable, METH_VARARGS, "Sets a player's holdable item."}, - {"drop_holdable", PyMinqlx_DropHoldable, METH_VARARGS, + {"drop_holdable", PyMinqlxtended_DropHoldable, METH_VARARGS, "Drops player's holdable item."}, - {"set_flight", PyMinqlx_SetFlight, METH_VARARGS, + {"set_flight", PyMinqlxtended_SetFlight, METH_VARARGS, "Sets a player's flight parameters, such as current fuel, max fuel and, so on."}, - {"set_keys", PyMinqlx_SetKeys, METH_VARARGS, + {"set_keys", PyMinqlxtended_SetKeys, METH_VARARGS, "Sets a player's keys."}, - {"set_invulnerability", PyMinqlx_SetInvulnerability, METH_VARARGS, + {"set_invulnerability", PyMinqlxtended_SetInvulnerability, METH_VARARGS, "Makes player invulnerable for limited time."}, - {"set_score", PyMinqlx_SetScore, METH_VARARGS, + {"set_score", PyMinqlxtended_SetScore, METH_VARARGS, "Sets a player's score."}, - {"callvote", PyMinqlx_Callvote, METH_VARARGS, + {"callvote", PyMinqlxtended_Callvote, METH_VARARGS, "Calls a vote as if started by the server and not a player."}, - {"allow_single_player", PyMinqlx_AllowSinglePlayer, METH_VARARGS, - "Allows or disallows a game with only a single player in it to go on without forfeiting. Useful for race."}, - {"player_spawn", PyMinqlx_PlayerSpawn, METH_VARARGS, + {"allow_single_player", PyMinqlxtended_AllowSinglePlayer, METH_VARARGS, "Allows or disallows a game with only a single player in it to go on without forfeiting. Useful for race."}, - {"set_privileges", PyMinqlx_SetPrivileges, METH_VARARGS, + {"player_spawn", PyMinqlxtended_PlayerSpawn, METH_VARARGS, + "Forces the player to spawn in the map (no matter the team!)"}, + {"set_privileges", PyMinqlxtended_SetPrivileges, METH_VARARGS, "Sets a player's privileges. Does not persist."}, - {"destroy_kamikaze_timers", PyMinqlx_DestroyKamikazeTimers, METH_NOARGS, + {"destroy_kamikaze_timers", PyMinqlxtended_DestroyKamikazeTimers, METH_NOARGS, "Removes all current kamikaze timers."}, - {"spawn_item", PyMinqlx_SpawnItem, METH_VARARGS, + {"spawn_item", PyMinqlxtended_SpawnItem, METH_VARARGS, "Spawns item with specified coordinates."}, - {"remove_dropped_items", PyMinqlx_RemoveDroppedItems, METH_NOARGS, + {"remove_dropped_items", PyMinqlxtended_RemoveDroppedItems, METH_NOARGS, "Removes all dropped items."}, - {"slay_with_mod", PyMinqlx_SlayWithMod, METH_VARARGS, + {"slay_with_mod", PyMinqlxtended_SlayWithMod, METH_VARARGS, "Slay player with mean of death."}, - {"replace_items", PyMinqlx_ReplaceItems, METH_VARARGS, + {"replace_items", PyMinqlxtended_ReplaceItems, METH_VARARGS, "Replaces target entity's item with specified one."}, - {"dev_print_items", PyMinqlx_DevPrintItems, METH_NOARGS, + {"dev_print_items", PyMinqlxtended_DevPrintItems, METH_NOARGS, "Prints all items and entity numbers to server console."}, - {"force_weapon_respawn_time", PyMinqlx_ForceWeaponRespawnTime, METH_VARARGS, + {"force_weapon_respawn_time", PyMinqlxtended_ForceWeaponRespawnTime, METH_VARARGS, "Force all weapons to have a specified respawn time, overriding custom map respawn times set for them."}, - {NULL, NULL, 0, NULL} -}; + {NULL, NULL, 0, NULL}}; static PyModuleDef minqlxtendedModule = { PyModuleDef_HEAD_INIT, "minqlxtended", NULL, -1, minqlxtendedMethods, - NULL, NULL, NULL, NULL -}; + NULL, NULL, NULL, NULL}; -static PyObject* PyMinqlx_InitModule(void) { +static PyObject* PyMinqlxtended_InitModule(void) { PyObject* module = PyModule_Create(&minqlxtendedModule); // Set minqlxtended version. PyModule_AddStringConstant(module, "__version__", MINQLXTENDED_VERSION); - // Set IS_DEBUG. - #ifndef NDEBUG +// Set IS_DEBUG. +#ifndef NDEBUG PyModule_AddObject(module, "DEBUG", Py_True); - #else +#else PyModule_AddObject(module, "DEBUG", Py_False); - #endif +#endif // Set a bunch of constants. We set them here because if you define functions in Python that use module // constants as keyword defaults, we have to always make sure they're exported first, and fuck that. @@ -1976,60 +1980,59 @@ static PyObject* PyMinqlx_InitModule(void) { return module; } -int PyMinqlx_IsInitialized(void) { - return initialized; +int PyMinqlxtended_IsInitialized(void) { + return initialized; } -PyMinqlx_InitStatus_t PyMinqlx_Initialize(void) { - if (PyMinqlx_IsInitialized()) { +PyMinqlxtended_InitStatus_t PyMinqlxtended_Initialize(void) { + if (PyMinqlxtended_IsInitialized()) { DebugPrint("%s was called while already initialized!\n", __func__); return PYM_ALREADY_INITIALIZED; } DebugPrint("Initializing Python...\n"); Py_SetProgramName(PYTHON_FILENAME); - PyImport_AppendInittab("_minqlxtended", &PyMinqlx_InitModule); + PyImport_AppendInittab("_minqlxtended", &PyMinqlxtended_InitModule); Py_Initialize(); - #if PY_VERSION_HEX < ((3 << 24) | (7 << 16)) +#if PY_VERSION_HEX < ((3 << 24) | (7 << 16)) PyEval_InitThreads(); - #endif +#endif // Add the main module. PyObject* main_module = PyImport_AddModule("__main__"); - PyObject* main_dict = PyModule_GetDict(main_module); + PyObject* main_dict = PyModule_GetDict(main_module); // Run script to load pyminqlxtended. PyObject* res = PyRun_String(loader, Py_file_input, main_dict, main_dict); if (res == NULL) { - DebugPrint("PyRun_String() returned NULL. Did you modify the loader?\n"); - return PYM_MAIN_SCRIPT_ERROR; - } + DebugPrint("PyRun_String() returned NULL. Did you modify the loader?\n"); + return PYM_MAIN_SCRIPT_ERROR; + } PyObject* ret = PyDict_GetItemString(main_dict, "ret"); Py_XDECREF(ret); Py_DECREF(res); if (ret == NULL) { - DebugPrint("The loader script return value doesn't exist?\n"); - return PYM_MAIN_SCRIPT_ERROR; - } - else if (ret != Py_True) { - // No need to print anything, since the traceback should be printed already. - return PYM_MAIN_SCRIPT_ERROR; - } - - mainstate = PyEval_SaveThread(); + DebugPrint("The loader script return value doesn't exist?\n"); + return PYM_MAIN_SCRIPT_ERROR; + } else if (ret != Py_True) { + // No need to print anything, since the traceback should be printed already. + return PYM_MAIN_SCRIPT_ERROR; + } + + mainstate = PyEval_SaveThread(); initialized = 1; DebugPrint("Python initialized!\n"); return PYM_SUCCESS; } -PyMinqlx_InitStatus_t PyMinqlx_Finalize(void) { - if (!PyMinqlx_IsInitialized()) { +PyMinqlxtended_InitStatus_t PyMinqlxtended_Finalize(void) { + if (!PyMinqlxtended_IsInitialized()) { DebugPrint("%s was called before being initialized!\n", __func__); return PYM_NOT_INITIALIZED_ERROR; } for (handler_t* h = handlers; h->name; h++) { - *h->handler = NULL; - } + *h->handler = NULL; + } PyEval_RestoreThread(mainstate); Py_Finalize(); diff --git a/quake_common.h b/quake_common.h index f68ba14..eedb44f 100644 --- a/quake_common.h +++ b/quake_common.h @@ -21,102 +21,103 @@ along with this program. If not, see . * exactly the same, so there's a good number of modifications to make it * fit QL. The end of the file has a bunch of stuff I added. Might want * to refactor it. TODO. -*/ + */ #ifndef QUAKE_COMMON_H #define QUAKE_COMMON_H #include -#include "patterns.h" #include "common.h" +#include "patterns.h" -#define CS_SCORES1 6 -#define CS_SCORES2 7 -#define CS_VOTE_TIME 8 -#define CS_VOTE_STRING 9 -#define CS_VOTE_YES 10 -#define CS_VOTE_NO 11 -#define CS_ITEMS 15 +#define CS_SCORES1 6 +#define CS_SCORES2 7 +#define CS_VOTE_TIME 8 +#define CS_VOTE_STRING 9 +#define CS_VOTE_YES 10 +#define CS_VOTE_NO 11 +#define CS_ITEMS 15 #define MAX_CLIENTS 64 -#define MAX_CHALLENGES 1024 -#define MAX_MSGLEN 16384 -#define MAX_PS_EVENTS 2 -#define MAX_MAP_AREA_BYTES 32 // bit vector of area visibility +#define MAX_CHALLENGES 1024 +#define MAX_MSGLEN 16384 +#define MAX_PS_EVENTS 2 +#define MAX_MAP_AREA_BYTES 32 // bit vector of area visibility #define MAX_INFO_STRING 1024 -#define MAX_RELIABLE_COMMANDS 64 // max string commands buffered for restransmit -#define MAX_STRING_CHARS 1024 // max length of a string passed to Cmd_TokenizeString -#define MAX_NAME_LENGTH 32 // max length of a client name -#define MAX_QPATH 64 // max length of a quake game pathname -#define MAX_DOWNLOAD_WINDOW 8 // max of eight download frames -#define MAX_NETNAME 36 -#define PACKET_BACKUP 32 // number of old messages that must be kept on client and - // server for delta comrpession and ping estimation -#define PACKET_MASK (PACKET_BACKUP-1) -#define MAX_ENT_CLUSTERS 16 -#define MAX_MODELS 256 // these are sent over the net as 8 bits -#define MAX_CONFIGSTRINGS 1024 -#define GENTITYNUM_BITS 10 // don't need to send any more -#define MAX_GENTITIES (1<outgoingSequence of gamestate - int challenge; - - usercmd_t lastUsercmd; - int lastMessageNum; // for delta compression - int lastClientCommand; // reliable client message sequence - char lastClientCommandString[MAX_STRING_CHARS]; - sharedEntity_t *gentity; // SV_GentityNum(clientnum) - char name[MAX_NAME_LENGTH]; // extracted from userinfo, high bits masked - + clientState_t state; + char userinfo[MAX_INFO_STRING]; // name, etc + + char reliableCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS]; + int reliableSequence; // last added reliable message, not necesarily sent or acknowledged yet + int reliableAcknowledge; // last acknowledged reliable message + int reliableSent; // last sent reliable message, not necesarily acknowledged yet + int messageAcknowledge; + + int gamestateMessageNum; // netchan->outgoingSequence of gamestate + int challenge; + + usercmd_t lastUsercmd; + int lastMessageNum; // for delta compression + int lastClientCommand; // reliable client message sequence + char lastClientCommandString[MAX_STRING_CHARS]; + sharedEntity_t *gentity; // SV_GentityNum(clientnum) + char name[MAX_NAME_LENGTH]; // extracted from userinfo, high bits masked + // Mino: I think everything above this is correct. Below is a mess. - + // downloading - char downloadName[MAX_QPATH]; // if not empty string, we are downloading - fileHandle_t download; // file being downloaded - int downloadSize; // total bytes (can't use EOF because of paks) - int downloadCount; // bytes sent - int downloadClientBlock; // last block we sent to the client, awaiting ack - int downloadCurrentBlock; // current block number - int downloadXmitBlock; // last block we xmited - unsigned char *downloadBlocks[MAX_DOWNLOAD_WINDOW]; // the buffers for the download blocks - int downloadBlockSize[MAX_DOWNLOAD_WINDOW]; - qboolean downloadEOF; // We have sent the EOF block - int downloadSendTime; // time we last got an ack from the client - - int deltaMessage; // frame last client usercmd message - int nextReliableTime; // svs.time when another reliable command will be allowed - int lastPacketTime; // svs.time when packet was last received - int lastConnectTime; // svs.time when connection started - int nextSnapshotTime; // send another snapshot when svs.time >= nextSnapshotTime - qboolean rateDelayed; // true if nextSnapshotTime was set based on rate instead of snapshotMsec - int timeoutCount; // must timeout a few frames in a row so debugging doesn't break - clientSnapshot_t frames[PACKET_BACKUP]; // updates can be delta'd from here - int ping; - int rate; // bytes / second - int snapshotMsec; // requests a snapshot every snapshotMsec unless rate choked - int pureAuthentic; - qboolean gotCP; // TTimo - additional flag to distinguish between a bad pure checksum, and no cp command at all - netchan_t netchan; + char downloadName[MAX_QPATH]; // if not empty string, we are downloading + fileHandle_t download; // file being downloaded + int downloadSize; // total bytes (can't use EOF because of paks) + int downloadCount; // bytes sent + int downloadClientBlock; // last block we sent to the client, awaiting ack + int downloadCurrentBlock; // current block number + int downloadXmitBlock; // last block we xmited + unsigned char *downloadBlocks[MAX_DOWNLOAD_WINDOW]; // the buffers for the download blocks + int downloadBlockSize[MAX_DOWNLOAD_WINDOW]; + qboolean downloadEOF; // We have sent the EOF block + int downloadSendTime; // time we last got an ack from the client + + int deltaMessage; // frame last client usercmd message + int nextReliableTime; // svs.time when another reliable command will be allowed + int lastPacketTime; // svs.time when packet was last received + int lastConnectTime; // svs.time when connection started + int nextSnapshotTime; // send another snapshot when svs.time >= nextSnapshotTime + qboolean rateDelayed; // true if nextSnapshotTime was set based on rate instead of snapshotMsec + int timeoutCount; // must timeout a few frames in a row so debugging doesn't break + clientSnapshot_t frames[PACKET_BACKUP]; // updates can be delta'd from here + int ping; + int rate; // bytes / second + int snapshotMsec; // requests a snapshot every snapshotMsec unless rate choked + int pureAuthentic; + qboolean gotCP; // TTimo - additional flag to distinguish between a bad pure checksum, and no cp command at all + netchan_t netchan; netchan_buffer_t *netchan_start_queue; netchan_buffer_t **netchan_end_queue; - + // Mino: Holy crap. A bunch of data was added. I have no idea where it actually goes, // but this will at least correct sizeof(client_t). #if defined(__x86_64__) || defined(_M_X64) - uint8_t _unknown2[36808]; + uint8_t _unknown2[36808]; #elif defined(__i386) || defined(_M_IX86) - uint8_t _unknown2[36836]; // TODO: Outdated. + uint8_t _unknown2[36836]; // TODO: Outdated. #endif // Mino: Woohoo! How nice of them to put the SteamID last. - uint64_t steam_id; + uint64_t steam_id; } client_t; // @@ -919,194 +920,194 @@ typedef struct client_s { // typedef struct { - netadr_t adr; - int challenge; - int time; // time the last packet was sent to the autherize server - int pingTime; // time the challenge response was sent to client - int firstTime; // time the adr was first used, for authorize timeout checks - qboolean connected; + netadr_t adr; + int challenge; + int time; // time the last packet was sent to the autherize server + int pingTime; // time the challenge response was sent to client + int firstTime; // time the adr was first used, for authorize timeout checks + qboolean connected; } challenge_t; // this structure will be cleared only when the game dll changes typedef struct { - qboolean initialized; // sv_init has completed - int time; // will be strictly increasing across level changes - int snapFlagServerBit; // ^= SNAPFLAG_SERVERCOUNT every SV_SpawnServer() - client_t *clients; // [sv_maxclients->integer]; - int numSnapshotEntities; // sv_maxclients->integer*PACKET_BACKUP*MAX_PACKET_ENTITIES - int nextSnapshotEntities; // next snapshotEntities to use - entityState_t *snapshotEntities; // [numSnapshotEntities] - int nextHeartbeatTime; + qboolean initialized; // sv_init has completed + int time; // will be strictly increasing across level changes + int snapFlagServerBit; // ^= SNAPFLAG_SERVERCOUNT every SV_SpawnServer() + client_t *clients; // [sv_maxclients->integer]; + int numSnapshotEntities; // sv_maxclients->integer*PACKET_BACKUP*MAX_PACKET_ENTITIES + int nextSnapshotEntities; // next snapshotEntities to use + entityState_t *snapshotEntities; // [numSnapshotEntities] + int nextHeartbeatTime; challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting - netadr_t redirectAddress; // for rcon return messages - netadr_t authorizeAddress; // for rcon return messages + netadr_t redirectAddress; // for rcon return messages + netadr_t authorizeAddress; // for rcon return messages } serverStatic_t; typedef struct svEntity_s { struct worldSector_s *worldSector; struct svEntity_s *nextEntityInWorldSector; - - entityState_t baseline; // for delta compression of initial sighting - int numClusters; // if -1, use headnode instead - int clusternums[MAX_ENT_CLUSTERS]; - int lastCluster; // if all the clusters don't fit in clusternums - int areanum, areanum2; - int snapshotCounter; // used to prevent double adding from portal views + + entityState_t baseline; // for delta compression of initial sighting + int numClusters; // if -1, use headnode instead + int clusternums[MAX_ENT_CLUSTERS]; + int lastCluster; // if all the clusters don't fit in clusternums + int areanum, areanum2; + int snapshotCounter; // used to prevent double adding from portal views } svEntity_t; typedef struct worldSector_s { - int axis; // -1 = leaf node - float dist; - struct worldSector_s *children[2]; - svEntity_t *entities; + int axis; // -1 = leaf node + float dist; + struct worldSector_s *children[2]; + svEntity_t *entities; } worldSector_t; typedef enum { - SS_DEAD, // no map loaded - SS_LOADING, // spawning level entities - SS_GAME // actively running + SS_DEAD, // no map loaded + SS_LOADING, // spawning level entities + SS_GAME // actively running } serverState_t; typedef struct { - serverState_t state; - qboolean restarting; // if true, send configstring changes during SS_LOADING - int serverId; // changes each server start - int restartedServerId; // serverId before a map_restart - int checksumFeed; // the feed key that we use to compute the pure checksum strings + serverState_t state; + qboolean restarting; // if true, send configstring changes during SS_LOADING + int serverId; // changes each server start + int restartedServerId; // serverId before a map_restart + int checksumFeed; // the feed key that we use to compute the pure checksum strings // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475 // the serverId associated with the current checksumFeed (always <= serverId) - int checksumFeedServerId; - int snapshotCounter; // incremented for each snapshot built - int timeResidual; // <= 1000 / sv_frame->value - int nextFrameTime; // when time > nextFrameTime, process world + int checksumFeedServerId; + int snapshotCounter; // incremented for each snapshot built + int timeResidual; // <= 1000 / sv_frame->value + int nextFrameTime; // when time > nextFrameTime, process world struct cmodel_s *models[MAX_MODELS]; - char *configstrings[MAX_CONFIGSTRINGS]; - svEntity_t svEntities[MAX_GENTITIES]; + char *configstrings[MAX_CONFIGSTRINGS]; + svEntity_t svEntities[MAX_GENTITIES]; - char *entityParsePoint; // used during game VM init + char *entityParsePoint; // used during game VM init // the game virtual machine will update these on init and changes - sharedEntity_t *gentities; - int gentitySize; - int num_entities; // current number, <= MAX_GENTITIES + sharedEntity_t *gentities; + int gentitySize; + int num_entities; // current number, <= MAX_GENTITIES - playerState_t *gameClients; - int gameClientSize; // will be > sizeof(playerState_t) due to game private data + playerState_t *gameClients; + int gameClientSize; // will be > sizeof(playerState_t) due to game private data - int restartTime; + int restartTime; } server_t; typedef struct { - playerTeamStateState_t state; - int captures; - int basedefense; - int carrierdefense; - int flagrecovery; - int fragcarrier; - int assists; - int flagruntime; - int flagrunrelays; - int lasthurtcarrier; - int lastreturnedflag; - int lastfraggedcarrier; + playerTeamStateState_t state; + int captures; + int basedefense; + int carrierdefense; + int flagrecovery; + int fragcarrier; + int assists; + int flagruntime; + int flagrunrelays; + int lasthurtcarrier; + int lastreturnedflag; + int lastfraggedcarrier; } playerTeamState_t; typedef struct { - unsigned int statId; - int lastThinkTime; - int teamJoinTime; - int totalPlayTime; - int serverRank; - qboolean serverRankIsTied; - int teamRank; - qboolean teamRankIsTied; - int numKills; - int numDeaths; - int numSuicides; - int numTeamKills; - int numTeamKilled; - int numWeaponKills[16]; - int numWeaponDeaths[16]; - int shotsFired[16]; - int shotsHit[16]; - int damageDealt[16]; - int damageTaken[16]; - int powerups[16]; - int holdablePickups[7]; - int weaponPickups[16]; - int weaponUsageTime[16]; - int numCaptures; - int numAssists; - int numDefends; - int numHolyShits; - int totalDamageDealt; - int totalDamageTaken; - int previousHealth; - int previousArmor; - int numAmmoPickups; - int numFirstMegaHealthPickups; - int numMegaHealthPickups; - int megaHealthPickupTime; - int numHealthPickups; - int numFirstRedArmorPickups; - int numRedArmorPickups; - int redArmorPickupTime; - int numFirstYellowArmorPickups; - int numYellowArmorPickups; - int yellowArmorPickupTime; - int numFirstGreenArmorPickups; - int numGreenArmorPickups; - int greenArmorPickupTime; - int numQuadDamagePickups; - int numQuadDamageKills; - int numBattleSuitPickups; - int numRegenerationPickups; - int numHastePickups; - int numInvisibilityPickups; - int numRedFlagPickups; - int numBlueFlagPickups; - int numNeutralFlagPickups; - int numMedkitPickups; - int numArmorPickups; - int numDenials; - int killStreak; - int maxKillStreak; - int xp; - int domThreeFlagsTime; - int numMidairShotgunKills; + unsigned int statId; + int lastThinkTime; + int teamJoinTime; + int totalPlayTime; + int serverRank; + qboolean serverRankIsTied; + int teamRank; + qboolean teamRankIsTied; + int numKills; + int numDeaths; + int numSuicides; + int numTeamKills; + int numTeamKilled; + int numWeaponKills[16]; + int numWeaponDeaths[16]; + int shotsFired[16]; + int shotsHit[16]; + int damageDealt[16]; + int damageTaken[16]; + int powerups[16]; + int holdablePickups[7]; + int weaponPickups[16]; + int weaponUsageTime[16]; + int numCaptures; + int numAssists; + int numDefends; + int numHolyShits; + int totalDamageDealt; + int totalDamageTaken; + int previousHealth; + int previousArmor; + int numAmmoPickups; + int numFirstMegaHealthPickups; + int numMegaHealthPickups; + int megaHealthPickupTime; + int numHealthPickups; + int numFirstRedArmorPickups; + int numRedArmorPickups; + int redArmorPickupTime; + int numFirstYellowArmorPickups; + int numYellowArmorPickups; + int yellowArmorPickupTime; + int numFirstGreenArmorPickups; + int numGreenArmorPickups; + int greenArmorPickupTime; + int numQuadDamagePickups; + int numQuadDamageKills; + int numBattleSuitPickups; + int numRegenerationPickups; + int numHastePickups; + int numInvisibilityPickups; + int numRedFlagPickups; + int numBlueFlagPickups; + int numNeutralFlagPickups; + int numMedkitPickups; + int numArmorPickups; + int numDenials; + int killStreak; + int maxKillStreak; + int xp; + int domThreeFlagsTime; + int numMidairShotgunKills; } expandedStatObj_t; // client data that stays across multiple respawns, but is cleared // on each level change or team change at ClientBegin() typedef struct __attribute__((aligned(8))) { - clientConnected_t connected; - usercmd_t cmd; - qboolean localClient; - qboolean initialSpawn; - qboolean predictItemPickup; - char netname[40]; - char country[24]; - uint64_t steamId; - int maxHealth; - int voteCount; - voteState_t voteState; - int complaints; - int complaintClient; - int complaintEndTime; - int damageFromTeammates; - int damageToTeammates; - qboolean ready; - int autoaction; - int timeouts; - int enterTime; - playerTeamState_t teamState; - int damageResidual; - int inactivityTime; - int inactivityWarning; - int lastUserinfoUpdate; - int userInfoFloodInfractions; - int lastMapVoteTime; - int lastMapVoteIndex; + clientConnected_t connected; + usercmd_t cmd; + qboolean localClient; + qboolean initialSpawn; + qboolean predictItemPickup; + char netname[40]; + char country[24]; + uint64_t steamId; + int maxHealth; + int voteCount; + voteState_t voteState; + int complaints; + int complaintClient; + int complaintEndTime; + int damageFromTeammates; + int damageToTeammates; + qboolean ready; + int autoaction; + int timeouts; + int enterTime; + playerTeamState_t teamState; + int damageResidual; + int inactivityTime; + int inactivityWarning; + int lastUserinfoUpdate; + int userInfoFloodInfractions; + int lastMapVoteTime; + int lastMapVoteIndex; } clientPersistant_t; // client data that stays across multiple levels or tournament restarts @@ -1114,53 +1115,53 @@ typedef struct __attribute__((aligned(8))) { // time and reading them back at connection time. Anything added here // MUST be dealt with in G_InitSessionData() / G_ReadSessionData() / G_WriteSessionData() typedef struct { - team_t sessionTeam; - int spectatorTime; - spectatorState_t spectatorState; - int spectatorClient; - int weaponPrimary; - int wins; - int losses; - qboolean teamLeader; - privileges_t privileges; - int specOnly; - int playQueue; - qboolean updatePlayQueue; - int muted; - int prevScore; + team_t sessionTeam; + int spectatorTime; + spectatorState_t spectatorState; + int spectatorClient; + int weaponPrimary; + int wins; + int losses; + qboolean teamLeader; + privileges_t privileges; + int specOnly; + int playQueue; + qboolean updatePlayQueue; + int muted; + int prevScore; } clientSession_t; typedef struct gitem_s { - char *classname; - const char *pickup_sound; - const char *world_model[4]; - const char *premium_model[4]; - const char *icon; - const char *pickup_name; - int quantity; - itemType_t giType; - int giTag; - qboolean itemTimer; - unsigned int maskGametypeRenderSkip; - unsigned int maskGametypeForceSpawn; + char *classname; + const char *pickup_sound; + const char *world_model[4]; + const char *premium_model[4]; + const char *icon; + const char *pickup_name; + int quantity; + itemType_t giType; + int giTag; + qboolean itemTimer; + unsigned int maskGametypeRenderSkip; + unsigned int maskGametypeForceSpawn; } gitem_t; typedef enum { - ET_GENERAL, - ET_PLAYER, - ET_ITEM, - ET_MISSILE, - ET_MOVER, - ET_BEAM, - ET_PORTAL, - ET_SPEAKER, - ET_PUSH_TRIGGER, - ET_TELEPORT_TRIGGER, - ET_INVISIBLE, - ET_GRAPPLE, // grapple hooked on wall - ET_TEAM, - - ET_EVENTS // any of the EV_* events can be added freestanding + ET_GENERAL, + ET_PLAYER, + ET_ITEM, + ET_MISSILE, + ET_MOVER, + ET_BEAM, + ET_PORTAL, + ET_SPEAKER, + ET_PUSH_TRIGGER, + ET_TELEPORT_TRIGGER, + ET_INVISIBLE, + ET_GRAPPLE, // grapple hooked on wall + ET_TEAM, + + ET_EVENTS // any of the EV_* events can be added freestanding // by setting eType to ET_EVENTS + eventNum // this avoids having to set eFlags and eventNum } entityType_t; @@ -1168,352 +1169,352 @@ typedef enum { struct gclient_s; struct gentity_s { - entityState_t s; - entityShared_t r; - struct gclient_s *client; - qboolean inuse; - char *classname; - int spawnflags; - qboolean neverFree; - int flags; - char *model; - char *model2; - int freetime; - int eventTime; - qboolean freeAfterEvent; - qboolean unlinkAfterEvent; - qboolean physicsObject; - float physicsBounce; - int clipmask; - moverState_t moverState; - int soundPos1; - int sound1to2; - int sound2to1; - int soundPos2; - int soundLoop; - gentity_t *parent; - gentity_t *nextTrain; - gentity_t *prevTrain; - vec3_t pos1; - vec3_t pos2; - char *message; - char *cvar; - char *tourPointTarget; - char *tourPointTargetName; - char *noise; - int timestamp; - float angle; - char *target; - char *targetname; - char *targetShaderName; - char *targetShaderNewName; - gentity_t *target_ent; - float speed; - vec3_t movedir; - int nextthink; - void (*think)(gentity_t *); - void (*framethink)(gentity_t *); - void (*reached)(gentity_t *); - void (*blocked)(gentity_t *, gentity_t *); - void (*touch)(gentity_t *, gentity_t *); - void (*use)(gentity_t *, gentity_t *, gentity_t *); - void (*pain)(gentity_t *, gentity_t *, int); - void (*die)(gentity_t *, gentity_t *, gentity_t *, int, int); - int pain_debounce_time; - int fly_sound_debounce_time; - int health; - qboolean takedamage; - int damage; - int damageFactor; - int splashDamage; - int splashRadius; - int methodOfDeath; - int splashMethodOfDeath; - int count; - gentity_t *enemy; - gentity_t *activator; - const char *team; - gentity_t *teammaster; - gentity_t *teamchain; - int kamikazeTime; - int kamikazeShockTime; - int watertype; - int waterlevel; - int noise_index; - int bouncecount; - float wait; - float random; - int spawnTime; - const gitem_t *item; - int pickupCount; + entityState_t s; + entityShared_t r; + struct gclient_s *client; + qboolean inuse; + char *classname; + int spawnflags; + qboolean neverFree; + int flags; + char *model; + char *model2; + int freetime; + int eventTime; + qboolean freeAfterEvent; + qboolean unlinkAfterEvent; + qboolean physicsObject; + float physicsBounce; + int clipmask; + moverState_t moverState; + int soundPos1; + int sound1to2; + int sound2to1; + int soundPos2; + int soundLoop; + gentity_t *parent; + gentity_t *nextTrain; + gentity_t *prevTrain; + vec3_t pos1; + vec3_t pos2; + char *message; + char *cvar; + char *tourPointTarget; + char *tourPointTargetName; + char *noise; + int timestamp; + float angle; + char *target; + char *targetname; + char *targetShaderName; + char *targetShaderNewName; + gentity_t *target_ent; + float speed; + vec3_t movedir; + int nextthink; + void (*think)(gentity_t *); + void (*framethink)(gentity_t *); + void (*reached)(gentity_t *); + void (*blocked)(gentity_t *, gentity_t *); + void (*touch)(gentity_t *, gentity_t *); + void (*use)(gentity_t *, gentity_t *, gentity_t *); + void (*pain)(gentity_t *, gentity_t *, int); + void (*die)(gentity_t *, gentity_t *, gentity_t *, int, int); + int pain_debounce_time; + int fly_sound_debounce_time; + int health; + qboolean takedamage; + int damage; + int damageFactor; + int splashDamage; + int splashRadius; + int methodOfDeath; + int splashMethodOfDeath; + int count; + gentity_t *enemy; + gentity_t *activator; + const char *team; + gentity_t *teammaster; + gentity_t *teamchain; + int kamikazeTime; + int kamikazeShockTime; + int watertype; + int waterlevel; + int noise_index; + int bouncecount; + float wait; + float random; + int spawnTime; + const gitem_t *item; + int pickupCount; }; typedef struct { - qboolean racingActive; - int startTime; - int lastTime; - int best_race[64]; - int current_race[64]; - int currentCheckPoint; - qboolean weaponUsed; - gentity_t *nextRacePoint; - gentity_t *nextRacePoint2; + qboolean racingActive; + int startTime; + int lastTime; + int best_race[64]; + int current_race[64]; + int currentCheckPoint; + qboolean weaponUsed; + gentity_t *nextRacePoint; + gentity_t *nextRacePoint2; } raceInfo_t; // this structure is cleared on each ClientSpawn(), // except for 'client->pers' and 'client->sess' struct __attribute__((aligned(8))) gclient_s { - playerState_t ps; - clientPersistant_t pers; - clientSession_t sess; - qboolean noclip; - int lastCmdTime; - int buttons; - int oldbuttons; - int damage_armor; - int damage_blood; - vec3_t damage_from; - qboolean damage_fromWorld; - int impressiveCount; - int accuracyCount; - int accuracy_shots; - int accuracy_hits; - int lastClientKilled; - int lastKilledClient; - int lastHurtClient[2]; - int lastHurtMod[2]; - int lastHurtTime[2]; - int lastKillTime; - int lastGibTime; - int rampageCounter; - int revengeCounter[64]; - int respawnTime; - int rewardTime; - int airOutTime; - qboolean fireHeld; - gentity_t *hook; - int switchTeamTime; - int timeResidual; - int timeResidualScout; - int timeResidualArmor; - int timeResidualHealth; - int timeResidualPingPOI; - int timeResidualSpecInfo; - qboolean healthRegenActive; - qboolean armorRegenActive; - gentity_t *persistantPowerup; - int portalID; - int ammoTimes[16]; - int invulnerabilityTime; - expandedStatObj_t expandedStats; - int ignoreChatsTime; - int lastUserCmdTime; - qboolean freezePlayer; - int deferredSpawnTime; - int deferredSpawnCount; - raceInfo_t race; - int shotgunDmg[64]; - int round_shots; - int round_hits; - int round_damage; - qboolean queuedSpectatorFollow; - int queuedSpectatorClient; + playerState_t ps; + clientPersistant_t pers; + clientSession_t sess; + qboolean noclip; + int lastCmdTime; + int buttons; + int oldbuttons; + int damage_armor; + int damage_blood; + vec3_t damage_from; + qboolean damage_fromWorld; + int impressiveCount; + int accuracyCount; + int accuracy_shots; + int accuracy_hits; + int lastClientKilled; + int lastKilledClient; + int lastHurtClient[2]; + int lastHurtMod[2]; + int lastHurtTime[2]; + int lastKillTime; + int lastGibTime; + int rampageCounter; + int revengeCounter[64]; + int respawnTime; + int rewardTime; + int airOutTime; + qboolean fireHeld; + gentity_t *hook; + int switchTeamTime; + int timeResidual; + int timeResidualScout; + int timeResidualArmor; + int timeResidualHealth; + int timeResidualPingPOI; + int timeResidualSpecInfo; + qboolean healthRegenActive; + qboolean armorRegenActive; + gentity_t *persistantPowerup; + int portalID; + int ammoTimes[16]; + int invulnerabilityTime; + expandedStatObj_t expandedStats; + int ignoreChatsTime; + int lastUserCmdTime; + qboolean freezePlayer; + int deferredSpawnTime; + int deferredSpawnCount; + raceInfo_t race; + int shotgunDmg[64]; + int round_shots; + int round_hits; + int round_damage; + qboolean queuedSpectatorFollow; + int queuedSpectatorClient; }; typedef struct { - roundStateState_t eCurrent; - roundStateState_t eNext; - int tNext; - int startTime; - int turn; - int round; - team_t prevRoundWinningTeam; - qboolean touch; - qboolean capture; + roundStateState_t eCurrent; + roundStateState_t eNext; + int tNext; + int startTime; + int turn; + int round; + team_t prevRoundWinningTeam; + qboolean touch; + qboolean capture; } roundState_t; typedef struct __attribute__((aligned(8))) { - struct gclient_s *clients; - struct gentity_s *gentities; - int gentitySize; - int num_entities; - int warmupTime; - fileHandle_t logFile; - int maxclients; - int time; - int frametime; - int startTime; - int teamScores[4]; - int nextTeamInfoTime; - qboolean newSession; - qboolean restarted; - qboolean shufflePending; - int shuffleReadyTime; - int numConnectedClients; - int numNonSpectatorClients; - int numPlayingClients; - int numReadyClients; - int numReadyHumans; - int numStandardClients; - int sortedClients[64]; - int follow1; - int follow2; - int snd_fry; - int warmupModificationCount; - char voteString[1024]; - char voteDisplayString[1024]; - int voteExecuteTime; - int voteTime; - int voteYes; - int voteNo; - int pendingVoteCaller; - qboolean spawning; - int numSpawnVars; - char *spawnVars[64][2]; - int numSpawnVarChars; - char spawnVarChars[4096]; - int intermissionQueued; - int intermissionTime; - qboolean readyToExit; - qboolean votingEnded; - int exitTime; - vec3_t intermission_origin; - vec3_t intermission_angle; - qboolean locationLinked; - gentity_t *locationHead; - int timePauseBegin; - int timeOvertime; - int timeInitialPowerupSpawn; - int bodyQueIndex; - gentity_t *bodyQue[8]; - int portalSequence; - qboolean gameStatsReported; - qboolean mapIsTrainingMap; - int clientNum1stPlayer; - int clientNum2ndPlayer; - char scoreboardArchive1[1024]; - char scoreboardArchive2[1024]; - char firstScorer[40]; - char lastScorer[40]; - char lastTeamScorer[40]; - char firstFrag[40]; - vec3_t red_flag_origin; - vec3_t blue_flag_origin; - int spawnCount[4]; - int runeSpawns[5]; - int itemCount[60]; - int suddenDeathRespawnDelay; - int suddenDeathRespawnDelayLastAnnounced; - int numRedArmorPickups[4]; - int numYellowArmorPickups[4]; - int numGreenArmorPickups[4]; - int numMegaHealthPickups[4]; - int numQuadDamagePickups[4]; - int numBattleSuitPickups[4]; - int numRegenerationPickups[4]; - int numHastePickups[4]; - int numInvisibilityPickups[4]; - int quadDamagePossessionTime[4]; - int battleSuitPossessionTime[4]; - int regenerationPossessionTime[4]; - int hastePossessionTime[4]; - int invisibilityPossessionTime[4]; - int numFlagPickups[4]; - int numMedkitPickups[4]; - int flagPossessionTime[4]; - gentity_t *dominationPoints[5]; - int dominationPointCount; - int dominationPointsTallied; - int racePointCount; - qboolean disableDropWeapon; - qboolean teamShuffleActive; - int lastTeamScores[4]; - int lastTeamRoundScores[4]; - team_t attackingTeam; - roundState_t roundState; - int lastTeamCountSent; - int infectedConscript; - int lastZombieSurvivor; - int zombieScoreTime; - int lastInfectionTime; - char intermissionMapNames[3][1024]; - char intermissionMapTitles[3][1024]; - char intermissionMapConfigs[3][1024]; - int intermissionMapVotes[3]; - qboolean matchForfeited; - int allReadyTime; - qboolean notifyCvarChange; - int notifyCvarChangeTime; - int lastLeadChangeTime; - int lastLeadChangeClient; + struct gclient_s *clients; + struct gentity_s *gentities; + int gentitySize; + int num_entities; + int warmupTime; + fileHandle_t logFile; + int maxclients; + int time; + int frametime; + int startTime; + int teamScores[4]; + int nextTeamInfoTime; + qboolean newSession; + qboolean restarted; + qboolean shufflePending; + int shuffleReadyTime; + int numConnectedClients; + int numNonSpectatorClients; + int numPlayingClients; + int numReadyClients; + int numReadyHumans; + int numStandardClients; + int sortedClients[64]; + int follow1; + int follow2; + int snd_fry; + int warmupModificationCount; + char voteString[1024]; + char voteDisplayString[1024]; + int voteExecuteTime; + int voteTime; + int voteYes; + int voteNo; + int pendingVoteCaller; + qboolean spawning; + int numSpawnVars; + char *spawnVars[64][2]; + int numSpawnVarChars; + char spawnVarChars[4096]; + int intermissionQueued; + int intermissionTime; + qboolean readyToExit; + qboolean votingEnded; + int exitTime; + vec3_t intermission_origin; + vec3_t intermission_angle; + qboolean locationLinked; + gentity_t *locationHead; + int timePauseBegin; + int timeOvertime; + int timeInitialPowerupSpawn; + int bodyQueIndex; + gentity_t *bodyQue[8]; + int portalSequence; + qboolean gameStatsReported; + qboolean mapIsTrainingMap; + int clientNum1stPlayer; + int clientNum2ndPlayer; + char scoreboardArchive1[1024]; + char scoreboardArchive2[1024]; + char firstScorer[40]; + char lastScorer[40]; + char lastTeamScorer[40]; + char firstFrag[40]; + vec3_t red_flag_origin; + vec3_t blue_flag_origin; + int spawnCount[4]; + int runeSpawns[5]; + int itemCount[60]; + int suddenDeathRespawnDelay; + int suddenDeathRespawnDelayLastAnnounced; + int numRedArmorPickups[4]; + int numYellowArmorPickups[4]; + int numGreenArmorPickups[4]; + int numMegaHealthPickups[4]; + int numQuadDamagePickups[4]; + int numBattleSuitPickups[4]; + int numRegenerationPickups[4]; + int numHastePickups[4]; + int numInvisibilityPickups[4]; + int quadDamagePossessionTime[4]; + int battleSuitPossessionTime[4]; + int regenerationPossessionTime[4]; + int hastePossessionTime[4]; + int invisibilityPossessionTime[4]; + int numFlagPickups[4]; + int numMedkitPickups[4]; + int flagPossessionTime[4]; + gentity_t *dominationPoints[5]; + int dominationPointCount; + int dominationPointsTallied; + int racePointCount; + qboolean disableDropWeapon; + qboolean teamShuffleActive; + int lastTeamScores[4]; + int lastTeamRoundScores[4]; + team_t attackingTeam; + roundState_t roundState; + int lastTeamCountSent; + int infectedConscript; + int lastZombieSurvivor; + int zombieScoreTime; + int lastInfectionTime; + char intermissionMapNames[3][1024]; + char intermissionMapTitles[3][1024]; + char intermissionMapConfigs[3][1024]; + int intermissionMapVotes[3]; + qboolean matchForfeited; + int allReadyTime; + qboolean notifyCvarChange; + int notifyCvarChangeTime; + int lastLeadChangeTime; + int lastLeadChangeClient; } level_locals_t; // Some extra stuff that's not in the Q3 source. These are the commands you // get when you type ? in the console. The array has a sentinel struct, so // check "cmd" == NULL. typedef struct { - privileges_t needed_privileges; - int unknown1; - char* cmd; // The command name, e.g. "tempban". - void (*admin_func)(gentity_t* ent); - int unknown2; - int unknown3; - char* description; // Command description that gets printed when you do "?". + privileges_t needed_privileges; + int unknown1; + char *cmd; // The command name, e.g. "tempban". + void (*admin_func)(gentity_t *ent); + int unknown2; + int unknown3; + char *description; // Command description that gets printed when you do "?". } adminCmd_t; // A pointer to the qagame module in memory and its entry point. -extern void* qagame; -extern void* qagame_dllentry; +extern void *qagame; +extern void *qagame_dllentry; // Additional key struct pointers. -extern serverStatic_t* svs; -extern gentity_t* g_entities; -extern level_locals_t* level; -extern gitem_t* bg_itemlist; +extern serverStatic_t *svs; +extern gentity_t *g_entities; +extern level_locals_t *level; +extern gitem_t *bg_itemlist; extern int bg_numItems; // Cvars. -extern cvar_t* sv_maxclients; +extern cvar_t *sv_maxclients; // Internal QL function pointer types. -typedef void (__cdecl *Com_Printf_ptr)(char* fmt, ...); -typedef void (__cdecl *Cmd_AddCommand_ptr)(char* cmd, void* func); -typedef char* (__cdecl *Cmd_Args_ptr)(void); -typedef char* (__cdecl *Cmd_Argv_ptr)(int arg); -typedef int (__cdecl *Cmd_Argc_ptr)(void); -typedef void (__cdecl *Cmd_TokenizeString_ptr)(const char* text_in); -typedef void (__cdecl *Cbuf_ExecuteText_ptr)(int exec_when, const char* text); -typedef cvar_t* (__cdecl *Cvar_FindVar_ptr)(const char* var_name); -typedef cvar_t* (__cdecl *Cvar_Get_ptr)(const char* var_name, const char* var_value, int flags); -typedef cvar_t* (__cdecl *Cvar_GetLimit_ptr)(const char* var_name, const char* var_value, const char* min, const char* max, int flag); -typedef cvar_t* (__cdecl *Cvar_Set2_ptr)(const char* var_name, const char* value, qboolean force); -typedef void (__cdecl *SV_SendServerCommand_ptr)(client_t* cl, const char* fmt, ...); -typedef void (__cdecl *SV_ExecuteClientCommand_ptr)(client_t* cl, const char* s, qboolean clientOK); -typedef void (__cdecl *SV_ClientEnterWorld_ptr)(client_t *client, usercmd_t *cmd); -typedef void (__cdecl *SV_Shutdown_ptr)(char* finalmsg); -typedef void (__cdecl *SV_Map_f_ptr)(void); -typedef void (__cdecl *SV_ClientThink_ptr)(client_t* cl, usercmd_t* cmd); -typedef void (__cdecl *SV_SetConfigstring_ptr)(int index, const char* value); -typedef void (__cdecl *SV_GetConfigstring_ptr)(int index, char* buffer, int bufferSize); -typedef void (__cdecl *SV_DropClient_ptr)(client_t* drop, const char* reason); -typedef void (__cdecl *FS_Startup_ptr)(const char* gameName); -typedef void (__cdecl *Sys_SetModuleOffset_ptr)(char* moduleName, void* offset); -typedef void (__cdecl *SV_LinkEntity_ptr)(sharedEntity_t* gEnt); -typedef void (__cdecl *SV_SpawnServer_ptr)(char* server, qboolean killBots); -typedef void (__cdecl *Cmd_ExecuteString_ptr)(const char* text); +typedef void(__cdecl *Com_Printf_ptr)(char *fmt, ...); +typedef void(__cdecl *Cmd_AddCommand_ptr)(char *cmd, void *func); +typedef char *(__cdecl *Cmd_Args_ptr)(void); +typedef char *(__cdecl *Cmd_Argv_ptr)(int arg); +typedef int(__cdecl *Cmd_Argc_ptr)(void); +typedef void(__cdecl *Cmd_TokenizeString_ptr)(const char *text_in); +typedef void(__cdecl *Cbuf_ExecuteText_ptr)(int exec_when, const char *text); +typedef cvar_t *(__cdecl *Cvar_FindVar_ptr)(const char *var_name); +typedef cvar_t *(__cdecl *Cvar_Get_ptr)(const char *var_name, const char *var_value, int flags); +typedef cvar_t *(__cdecl *Cvar_GetLimit_ptr)(const char *var_name, const char *var_value, const char *min, const char *max, int flag); +typedef cvar_t *(__cdecl *Cvar_Set2_ptr)(const char *var_name, const char *value, qboolean force); +typedef void(__cdecl *SV_SendServerCommand_ptr)(client_t *cl, const char *fmt, ...); +typedef void(__cdecl *SV_ExecuteClientCommand_ptr)(client_t *cl, const char *s, qboolean clientOK); +typedef void(__cdecl *SV_ClientEnterWorld_ptr)(client_t *client, usercmd_t *cmd); +typedef void(__cdecl *SV_Shutdown_ptr)(char *finalmsg); +typedef void(__cdecl *SV_Map_f_ptr)(void); +typedef void(__cdecl *SV_ClientThink_ptr)(client_t *cl, usercmd_t *cmd); +typedef void(__cdecl *SV_SetConfigstring_ptr)(int index, const char *value); +typedef void(__cdecl *SV_GetConfigstring_ptr)(int index, char *buffer, int bufferSize); +typedef void(__cdecl *SV_DropClient_ptr)(client_t *drop, const char *reason); +typedef void(__cdecl *FS_Startup_ptr)(const char *gameName); +typedef void(__cdecl *Sys_SetModuleOffset_ptr)(char *moduleName, void *offset); +typedef void(__cdecl *SV_LinkEntity_ptr)(sharedEntity_t *gEnt); +typedef void(__cdecl *SV_SpawnServer_ptr)(char *server, qboolean killBots); +typedef void(__cdecl *Cmd_ExecuteString_ptr)(const char *text); // VM functions. -typedef void (__cdecl *G_RunFrame_ptr)(int time); -typedef void (__cdecl *G_AddEvent_ptr)(gentity_t* ent, int event, int eventParm); -typedef void (__cdecl *G_InitGame_ptr)(int levelTime, int randomSeed, int restart); -typedef int (__cdecl *CheckPrivileges_ptr)(gentity_t* ent, char* cmd); -typedef char* (__cdecl *ClientConnect_ptr)(int clientNum, qboolean firstTime, qboolean isBot); -typedef void (__cdecl *ClientSpawn_ptr)(gentity_t* ent); -typedef void (__cdecl *Cmd_CallVote_f_ptr)(gentity_t *ent); -typedef void (__cdecl *G_Damage_ptr)(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod); -typedef void (__cdecl *Touch_Item_ptr)(gentity_t *ent, gentity_t *other, trace_t *trace); -typedef gentity_t* (__cdecl *LaunchItem_ptr)(gitem_t *item, vec3_t origin, vec3_t velocity); -typedef gentity_t* (__cdecl *Drop_Item_ptr)(gentity_t *ent, gitem_t *item, float angle); -typedef void (__cdecl *G_StartKamikaze_ptr)(gentity_t *ent); -typedef void (__cdecl *G_FreeEntity_ptr)(gentity_t *ed); +typedef void(__cdecl *G_RunFrame_ptr)(int time); +typedef void(__cdecl *G_AddEvent_ptr)(gentity_t *ent, int event, int eventParm); +typedef void(__cdecl *G_InitGame_ptr)(int levelTime, int randomSeed, int restart); +typedef int(__cdecl *CheckPrivileges_ptr)(gentity_t *ent, char *cmd); +typedef char *(__cdecl *ClientConnect_ptr)(int clientNum, qboolean firstTime, qboolean isBot); +typedef void(__cdecl *ClientSpawn_ptr)(gentity_t *ent); +typedef void(__cdecl *Cmd_CallVote_f_ptr)(gentity_t *ent); +typedef void(__cdecl *G_Damage_ptr)(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod); +typedef void(__cdecl *Touch_Item_ptr)(gentity_t *ent, gentity_t *other, trace_t *trace); +typedef gentity_t *(__cdecl *LaunchItem_ptr)(gitem_t *item, vec3_t origin, vec3_t velocity); +typedef gentity_t *(__cdecl *Drop_Item_ptr)(gentity_t *ent, gitem_t *item, float angle); +typedef void(__cdecl *G_StartKamikaze_ptr)(gentity_t *ent); +typedef void(__cdecl *G_FreeEntity_ptr)(gentity_t *ed); // Some of them are initialized by Initialize(), but not all of them necessarily. extern Com_Printf_ptr Com_Printf; @@ -1531,7 +1532,7 @@ extern SV_SendServerCommand_ptr SV_SendServerCommand; extern SV_ExecuteClientCommand_ptr SV_ExecuteClientCommand; extern SV_ClientEnterWorld_ptr SV_ClientEnterWorld; extern SV_Shutdown_ptr SV_Shutdown; // Used to get svs pointer. -extern SV_Map_f_ptr SV_Map_f; // Used to get Cmd_Argc +extern SV_Map_f_ptr SV_Map_f; // Used to get Cmd_Argc extern SV_SetConfigstring_ptr SV_SetConfigstring; extern SV_GetConfigstring_ptr SV_GetConfigstring; extern SV_DropClient_ptr SV_DropClient; @@ -1554,31 +1555,31 @@ extern G_StartKamikaze_ptr G_StartKamikaze; extern G_FreeEntity_ptr G_FreeEntity; // Server replacement functions for hooks. -void __cdecl My_Cmd_AddCommand(char* cmd, void* func); -void __cdecl My_Sys_SetModuleOffset(char* moduleName, void* offset); +void __cdecl My_Cmd_AddCommand(char *cmd, void *func); +void __cdecl My_Sys_SetModuleOffset(char *moduleName, void *offset); #ifndef NOPY void __cdecl My_SV_ExecuteClientCommand(client_t *cl, char *s, qboolean clientOK); -void __cdecl My_SV_SendServerCommand(client_t* cl, char* fmt, ...); -void __cdecl My_SV_ClientEnterWorld(client_t* client, usercmd_t* cmd); -void __cdecl My_SV_SetConfigstring(int index, char* value); -void __cdecl My_SV_DropClient(client_t* drop, const char* reason); -void __cdecl My_Com_Printf(char* fmt, ...); -void __cdecl My_SV_SpawnServer(char* server, qboolean killBots); +void __cdecl My_SV_SendServerCommand(client_t *cl, char *fmt, ...); +void __cdecl My_SV_ClientEnterWorld(client_t *client, usercmd_t *cmd); +void __cdecl My_SV_SetConfigstring(int index, char *value); +void __cdecl My_SV_DropClient(client_t *drop, const char *reason); +void __cdecl My_Com_Printf(char *fmt, ...); +void __cdecl My_SV_SpawnServer(char *server, qboolean killBots); // VM replacement functions for hooks. void __cdecl My_G_RunFrame(int time); void __cdecl My_G_InitGame(int levelTime, int randomSeed, int restart); -char* __cdecl My_ClientConnect(int clientNum, qboolean firstTime, qboolean isBot); -void __cdecl My_ClientSpawn(gentity_t* ent); +char *__cdecl My_ClientConnect(int clientNum, qboolean firstTime, qboolean isBot); +void __cdecl My_ClientSpawn(gentity_t *ent); -void __cdecl My_G_StartKamikaze(gentity_t* ent); +void __cdecl My_G_StartKamikaze(gentity_t *ent); #endif // Custom commands added using Cmd_AddCommand during initialization. void __cdecl SendServerCommand(void); // "cmd" -void __cdecl CenterPrint(void); // "cp" -void __cdecl RegularPrint(void); // "p" -void __cdecl Slap(void); // "slap" -void __cdecl Slay(void); // "slay" +void __cdecl CenterPrint(void); // "cp" +void __cdecl RegularPrint(void); // "p" +void __cdecl Slap(void); // "slap" +void __cdecl Slay(void); // "slay" #ifndef NOPY // PyRcon gives the owner the ability to execute pyminqlxtended commands as if the // owner executed them. diff --git a/simple_hook.c b/simple_hook.c index cbb8ea5..2ac7820 100644 --- a/simple_hook.c +++ b/simple_hook.c @@ -1,31 +1,31 @@ -#include -#include -#include +#include "trampoline.h" #include #include -#include "trampoline.h" +#include +#include +#include #if defined(__x86_64__) || defined(_M_X64) typedef uint64_t pint; typedef int64_t sint; -#define WORST_CASE 42 -#define JUMP_SIZE sizeof(JMP_ABS) +#define WORST_CASE 42 +#define JUMP_SIZE sizeof(JMP_ABS) #elif defined(__i386) || defined(_M_IX86) typedef uint32_t pint; typedef int32_t sint; -#define WORST_CASE 29 -#define JUMP_SIZE sizeof(JMP_REL) +#define WORST_CASE 29 +#define JUMP_SIZE sizeof(JMP_REL) #endif -#define TRMPS_ARRAY_SIZE 30 +#define TRMPS_ARRAY_SIZE 30 const uint8_t NOP = 0x90; static void* trmps; static int last_trmp = 0; // trmp[TRMPS_ARRAY_SIZE] static void initializeTrampolines(void) { - trmps = mmap(NULL, (WORST_CASE * TRMPS_ARRAY_SIZE), - PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + trmps = mmap(NULL, (WORST_CASE * TRMPS_ARRAY_SIZE), + PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); } int Hook(void* target, void* replacement, void** func_ptr) { @@ -34,10 +34,11 @@ int Hook(void* target, void* replacement, void** func_ptr) { // Check if our trampoline pool has been initialized. If not, do so. if (!trmps) { - initializeTrampolines(); - } - else { // TODO: Implement a way to add and remove hooks. - if (last_trmp + 1 > TRMPS_ARRAY_SIZE) return -3; + initializeTrampolines(); + } else { // TODO: Implement a way to add and remove hooks. + if (last_trmp + 1 > TRMPS_ARRAY_SIZE) { + return -3; + } } void* trmp = (void*)((pint)trmps + last_trmp * WORST_CASE); @@ -51,9 +52,13 @@ int Hook(void* target, void* replacement, void** func_ptr) { } page_size = sysconf(_SC_PAGESIZE); - if (page_size == -1) return errno; - res = mprotect((void*)((pint)target & ~(page_size-1)), page_size, PROT_READ | PROT_WRITE | PROT_EXEC); - if (res) return errno; + if (page_size == -1) { + return errno; + } + res = mprotect((void*)((pint)target & ~(page_size - 1)), page_size, PROT_READ | PROT_WRITE | PROT_EXEC); + if (res) { + return errno; + } #if defined(__x86_64__) || defined(_M_X64) PJMP_ABS pJmp = (PJMP_ABS)target; @@ -64,11 +69,11 @@ int Hook(void* target, void* replacement, void** func_ptr) { #else PJMP_REL pJmp = (PJMP_REL)target; pJmp->opcode = 0xE9; - pJmp->operand = (pint)replacement - ( (pint)target + sizeof(JMP_REL) ); + pJmp->operand = (pint)replacement - ((pint)target + sizeof(JMP_REL)); #endif - int difference = ct.oldIPs[ ct.nIP - 1 ]; - for (int i=JUMP_SIZE; i= TRMPS_ARRAY_SIZE) ) + if ((last_trmp + offset < 0) || (last_trmp + offset >= TRMPS_ARRAY_SIZE)) { return 0; + } last_trmp += offset; return 1; diff --git a/simple_hook.h b/simple_hook.h index a63cf9e..d41ac9e 100644 --- a/simple_hook.h +++ b/simple_hook.h @@ -2,6 +2,6 @@ #define SIMPLE_HOOK_H int Hook(void* target, void* replacement, void** func_ptr); -int seek_hook_slot( int offset ); +int seek_hook_slot(int offset); #endif /* SIMPLE_HOOK_H */ diff --git a/trampoline.c b/trampoline.c index 45f8b6c..3d8419b 100644 --- a/trampoline.c +++ b/trampoline.c @@ -29,37 +29,36 @@ #include #ifndef ARRAYSIZE - #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#define ARRAYSIZE(A) (sizeof(A) / sizeof((A)[0])) #endif #if defined(_M_X64) || defined(__x86_64__) - #include "./HDE/hde64.h" - typedef hde64s HDE; - #define HDE_DISASM(code, hs) hde64_disasm(code, hs) +#include "./HDE/hde64.h" +typedef hde64s HDE; +#define HDE_DISASM(code, hs) hde64_disasm(code, hs) #else - #include "./HDE/hde32.h" - typedef hde32s HDE; - #define HDE_DISASM(code, hs) hde32_disasm(code, hs) +#include "./HDE/hde32.h" +typedef hde32s HDE; +#define HDE_DISASM(code, hs) hde32_disasm(code, hs) #endif #include "trampoline.h" #if defined(_M_X64) || defined(__x86_64__) - #define MEMORY_SLOT_SIZE 64 +#define MEMORY_SLOT_SIZE 64 #else - #define MEMORY_SLOT_SIZE 32 +#define MEMORY_SLOT_SIZE 32 #endif // Maximum size of a trampoline function. #if defined(_M_X64) || defined(__x86_64__) - #define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS)) +#define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS)) #else - #define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE +#define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE #endif //------------------------------------------------------------------------- -BOOL CreateTrampolineFunction(PTRAMPOLINE ct) -{ +BOOL CreateTrampolineFunction(PTRAMPOLINE ct) { #if defined(_M_X64) || defined(__x86_64__) CALL_ABS call = { 0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8] @@ -77,45 +76,44 @@ BOOL CreateTrampolineFunction(PTRAMPOLINE ct) }; #else CALL_REL call = { - 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx - 0x00000000 // Relative destination address + 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx + 0x00000000 // Relative destination address }; JMP_REL jmp = { - 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx - 0x00000000 // Relative destination address + 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx + 0x00000000 // Relative destination address }; JCC_REL jcc = { - 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx - 0x00000000 // Relative destination address + 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx + 0x00000000 // Relative destination address }; #endif - UINT8 oldPos = 0; - UINT8 newPos = 0; - ULONG_PTR jmpDest = 0; // Destination address of an internal jump. - BOOL finished = FALSE; // Is the function completed? + UINT8 oldPos = 0; + UINT8 newPos = 0; + ULONG_PTR jmpDest = 0; // Destination address of an internal jump. + BOOL finished = FALSE; // Is the function completed? #if defined(_M_X64) || defined(__x86_64__) - UINT8 instBuf[16]; + UINT8 instBuf[16]; #endif ct->patchAbove = FALSE; ct->nIP = 0; - do - { - HDE hs; - UINT copySize; - LPVOID pCopySrc; - ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos; + do { + HDE hs; + UINT copySize; + LPVOID pCopySrc; + ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos; ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos; copySize = HDE_DISASM((LPVOID)pOldInst, &hs); - if (hs.flags & F_ERROR) + if (hs.flags & F_ERROR) { return FALSE; + } pCopySrc = (LPVOID)pOldInst; - if (oldPos >= sizeof(jmp)) - { + if (oldPos >= sizeof(jmp)) { // The trampoline function is long enough. // Complete the function with the jump to the target function. #if defined(_M_X64) || defined(__x86_64__) @@ -129,8 +127,7 @@ BOOL CreateTrampolineFunction(PTRAMPOLINE ct) finished = TRUE; } #if defined(_M_X64) || defined(__x86_64__) - else if ((hs.modrm & 0xC7) == 0x05) - { + else if ((hs.modrm & 0xC7) == 0x05) { // Instructions using RIP relative addressing. (ModR/M = 00???101B) // Modify the RIP relative address. @@ -145,17 +142,16 @@ BOOL CreateTrampolineFunction(PTRAMPOLINE ct) pCopySrc = instBuf; // Relative address is stored at (instruction length - immediate value length - 4). - pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4); - *pRelAddr - = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len)); + pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4); + *pRelAddr = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len)); // Complete the function if JMP (FF /4). - if (hs.opcode == 0xFF && hs.modrm_reg == 4) + if (hs.opcode == 0xFF && hs.modrm_reg == 4) { finished = TRUE; + } } #endif - else if (hs.opcode == 0xE8) - { + else if (hs.opcode == 0xE8) { // Direct relative CALL ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32; #if defined(_M_X64) || defined(__x86_64__) @@ -165,26 +161,22 @@ BOOL CreateTrampolineFunction(PTRAMPOLINE ct) #endif pCopySrc = &call; copySize = sizeof(call); - } - else if ((hs.opcode & 0xFD) == 0xE9) - { + } else if ((hs.opcode & 0xFD) == 0xE9) { // Direct relative JMP (EB or E9) ULONG_PTR dest = pOldInst + hs.len; - if (hs.opcode == 0xEB) // isShort jmp + if (hs.opcode == 0xEB) { // isShort jmp dest += (INT8)hs.imm.imm8; - else + } else { dest += (INT32)hs.imm.imm32; + } // Simply copy an internal jump. - if ((ULONG_PTR)ct->pTarget <= dest - && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) - { - if (jmpDest < dest) + if ((ULONG_PTR)ct->pTarget <= dest && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) { + if (jmpDest < dest) { jmpDest = dest; - } - else - { + } + } else { #if defined(_M_X64) || defined(__x86_64__) jmp.address = (UINT64)dest; #else @@ -196,34 +188,26 @@ BOOL CreateTrampolineFunction(PTRAMPOLINE ct) // Exit the function If it is not in the branch finished = (pOldInst >= jmpDest); } - } - else if ((hs.opcode & 0xF0) == 0x70 - || (hs.opcode & 0xFC) == 0xE0 - || (hs.opcode2 & 0xF0) == 0x80) - { + } else if ((hs.opcode & 0xF0) == 0x70 || (hs.opcode & 0xFC) == 0xE0 || (hs.opcode2 & 0xF0) == 0x80) { // Direct relative Jcc ULONG_PTR dest = pOldInst + hs.len; - if ((hs.opcode & 0xF0) == 0x70 // Jcc - || (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ + if ((hs.opcode & 0xF0) == 0x70 // Jcc + || (hs.opcode & 0xFC) == 0xE0) { // LOOPNZ/LOOPZ/LOOP/JECXZ dest += (INT8)hs.imm.imm8; - else + } else { dest += (INT32)hs.imm.imm32; + } // Simply copy an internal jump. - if ((ULONG_PTR)ct->pTarget <= dest - && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) - { - if (jmpDest < dest) + if ((ULONG_PTR)ct->pTarget <= dest && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) { + if (jmpDest < dest) { jmpDest = dest; - } - else if ((hs.opcode & 0xFC) == 0xE0) - { + } + } else if ((hs.opcode & 0xFC) == 0xE0) { // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported. return FALSE; - } - else - { + } else { UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F); #if defined(_M_X64) || defined(__x86_64__) // Invert the condition in x64 mode to simplify the conditional jump logic. @@ -236,9 +220,7 @@ BOOL CreateTrampolineFunction(PTRAMPOLINE ct) pCopySrc = &jcc; copySize = sizeof(jcc); } - } - else if ((hs.opcode & 0xFE) == 0xC2) - { + } else if ((hs.opcode & 0xFE) == 0xC2) { // RET (C2 or C3) // Complete the function if not in a branch. @@ -246,16 +228,19 @@ BOOL CreateTrampolineFunction(PTRAMPOLINE ct) } // Can't alter the instruction length in a branch. - if (pOldInst < jmpDest && copySize != hs.len) + if (pOldInst < jmpDest && copySize != hs.len) { return FALSE; + } // Trampoline function is too large. - if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE) + if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE) { return FALSE; + } // Trampoline function has too many instructions. - if (ct->nIP >= ARRAYSIZE(ct->oldIPs)) + if (ct->nIP >= ARRAYSIZE(ct->oldIPs)) { return FALSE; + } ct->oldIPs[ct->nIP] = oldPos; ct->newIPs[ct->nIP] = newPos; @@ -269,8 +254,7 @@ BOOL CreateTrampolineFunction(PTRAMPOLINE ct) #endif newPos += copySize; oldPos += hs.len; - } - while (!finished); + } while (!finished); #if defined(_M_X64) || defined(__x86_64__) // Create a relay function. diff --git a/trampoline.h b/trampoline.h index d055b73..11e4090 100644 --- a/trampoline.h +++ b/trampoline.h @@ -48,73 +48,66 @@ typedef uint8_t* LPBYTE; // Structs for writing x86/x64 instructions. // 8-bit relative jump. -typedef struct _JMP_REL_SHORT -{ - UINT8 opcode; // EB xx: JMP +2+xx - UINT8 operand; +typedef struct _JMP_REL_SHORT { + UINT8 opcode; // EB xx: JMP +2+xx + UINT8 operand; } JMP_REL_SHORT, *PJMP_REL_SHORT; // 32-bit direct relative jump/call. -typedef struct _JMP_REL -{ - UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx - UINT32 operand; // Relative destination address +typedef struct _JMP_REL { + UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx + UINT32 operand; // Relative destination address } JMP_REL, *PJMP_REL, CALL_REL; // 64-bit indirect absolute jump. -typedef struct _JMP_ABS -{ - UINT8 opcode0; // FF25 00000000: JMP [+6] - UINT8 opcode1; +typedef struct _JMP_ABS { + UINT8 opcode0; // FF25 00000000: JMP [+6] + UINT8 opcode1; UINT32 dummy; - UINT64 address; // Absolute destination address + UINT64 address; // Absolute destination address } JMP_ABS, *PJMP_ABS; // 64-bit indirect absolute call. -typedef struct _CALL_ABS -{ - UINT8 opcode0; // FF15 00000002: CALL [+6] - UINT8 opcode1; +typedef struct _CALL_ABS { + UINT8 opcode0; // FF15 00000002: CALL [+6] + UINT8 opcode1; UINT32 dummy0; - UINT8 dummy1; // EB 08: JMP +10 - UINT8 dummy2; - UINT64 address; // Absolute destination address + UINT8 dummy1; // EB 08: JMP +10 + UINT8 dummy2; + UINT64 address; // Absolute destination address } CALL_ABS; // 32-bit direct relative conditional jumps. -typedef struct _JCC_REL -{ - UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx - UINT8 opcode1; - UINT32 operand; // Relative destination address +typedef struct _JCC_REL { + UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx + UINT8 opcode1; + UINT32 operand; // Relative destination address } JCC_REL; // 64bit indirect absolute conditional jumps that x64 lacks. -typedef struct _JCC_ABS -{ - UINT8 opcode; // 7* 0E: J** +16 - UINT8 dummy0; - UINT8 dummy1; // FF25 00000000: JMP [+6] - UINT8 dummy2; +typedef struct _JCC_ABS { + UINT8 opcode; // 7* 0E: J** +16 + UINT8 dummy0; + UINT8 dummy1; // FF25 00000000: JMP [+6] + UINT8 dummy2; UINT32 dummy3; - UINT64 address; // Absolute destination address + UINT64 address; // Absolute destination address } JCC_ABS; #pragma pack(pop) -typedef struct _TRAMPOLINE -{ - LPVOID pTarget; // [In] Address of the target function. - LPVOID pDetour; // [In] Address of the detour function. - LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function. +typedef struct _TRAMPOLINE { + LPVOID pTarget; // [In] Address of the target function. + LPVOID pDetour; // [In] Address of the detour function. + LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function. #if defined(_M_X64) || defined(__x86_64__) - LPVOID pRelay; // [Out] Address of the relay function. + LPVOID pRelay; // [Out] Address of the relay function. #endif - BOOL patchAbove; // [Out] Should use the hot patch area? - UINT nIP; // [Out] Number of the instruction boundaries. - UINT8 oldIPs[12]; // [Out] Instruction boundaries of the target function. - UINT8 newIPs[12]; // [Out] Instruction boundaries of the trampoline function. + BOOL patchAbove; // [Out] Should use the hot patch area? + UINT nIP; // [Out] Number of the instruction boundaries. + UINT8 oldIPs[12]; // [Out] Instruction boundaries of the target function. + UINT8 newIPs[12]; // [Out] Instruction boundaries of the trampoline function. } TRAMPOLINE, *PTRAMPOLINE; BOOL CreateTrampolineFunction(PTRAMPOLINE ct);