Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added new hook, handler and dispatcher for any damage events happening #120

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
39 changes: 39 additions & 0 deletions hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,38 @@ void __cdecl My_G_StartKamikaze(gentity_t* ent) {
if (client_id != -1)
KamikazeExplodeDispatcher(client_id, is_used_on_demand);
}

void __cdecl My_G_Damage(
gentity_t* target, // entity that is being damaged
gentity_t* inflictor, // entity that is causing the damage
gentity_t* attacker, // entity that caused the inflictor to damage targ
vec3_t dir, // direction of the attack for knockback
vec3_t point, // point at which the damage is being inflicted, used for headshots
int damage, // amount of damage being inflicted
int dflags, // these flags are used to control how T_Damage works
int mod // means_of_death indicator
) {
int target_id;
int attacker_id = -1;

G_Damage(target, inflictor, attacker, dir, point, damage, dflags, mod);

if (!target) {
return;
}

if (!target->client) {
return;
}

target_id = target->client->ps.clientNum;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if target->client is spectating, ps.clientNum is the player who is being spectated.


if (attacker && attacker->client) {
attacker_id = attacker->client->ps.clientNum;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

}

DamageDispatcher(target_id, attacker_id, damage, dflags, mod);
}
#endif

// Hook static functions. Can be done before program even runs.
Expand Down Expand Up @@ -342,6 +374,13 @@ void HookVm(void) {
DebugPrint("ERROR: Failed to hook ClientSpawn: %d\n", res);
failed = 1;
}
count++;

res = Hook((void*)G_Damage, My_G_Damage, (void*)&G_Damage);
if (res) {
DebugPrint("ERROR: Failed to hook G_Damage: %d\n", res);
failed = 1;
}
count++;

if (failed) {
Expand Down
2 changes: 2 additions & 0 deletions pyminqlx.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ extern PyObject* client_spawn_handler;

extern PyObject* kamikaze_use_handler;
extern PyObject* kamikaze_explode_handler;
extern PyObject* damage_handler;

// Custom console command handler. These are commands added through Python that can be used
// from the console or using RCON.
Expand Down Expand Up @@ -91,5 +92,6 @@ void ClientSpawnDispatcher(int client_id);

void KamikazeUseDispatcher(int client_id);
void KamikazeExplodeDispatcher(int client_id, int is_used_on_demand);
void DamageDispatcher(int target_id, int attacker_id, int damage, int dflags, int mod);

#endif /* PYMINQLX_H */
11 changes: 10 additions & 1 deletion python/minqlx/_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class EventDispatcher:
to hook into events by registering an event handler.

"""
no_debug = ("frame", "set_configstring", "stats", "server_command", "death", "kill", "command", "console_print")
no_debug = ("frame", "set_configstring", "stats", "server_command", "death", "kill", "command", "console_print", "damage")
need_zmq_stats_enabled = False

def __init__(self):
Expand Down Expand Up @@ -581,6 +581,14 @@ class KamikazeExplodeDispatcher(EventDispatcher):
def dispatch(self, player, is_used_on_demand):
return super().dispatch(player, is_used_on_demand)

class DamageDispatcher(EventDispatcher):
"""Event that goes off when someone is inflicted with damage."""

name = "damage"
need_zmq_stats_enabled = False

def dispatch(self, target, attacker, damage, dflags, mod):
return super().dispatch(target, attacker, damage, dflags, mod)

EVENT_DISPATCHERS = EventDispatcherManager()
EVENT_DISPATCHERS.add_dispatcher(ConsolePrintDispatcher)
Expand Down Expand Up @@ -615,3 +623,4 @@ def dispatch(self, player, is_used_on_demand):
EVENT_DISPATCHERS.add_dispatcher(KillDispatcher)
EVENT_DISPATCHERS.add_dispatcher(DeathDispatcher)
EVENT_DISPATCHERS.add_dispatcher(UserinfoDispatcher)
EVENT_DISPATCHERS.add_dispatcher(DamageDispatcher)
13 changes: 13 additions & 0 deletions python/minqlx/_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,18 @@ def handle_kamikaze_explode(client_id, is_used_on_demand):
minqlx.log_exception()
return True

def handle_damage(target_id, attacker_id, damage, dflags, mod):
target_player = minqlx.Player(target_id) if target_id in range(0, 64) else None
inflictor_player = minqlx.Player(attacker_id) if attacker_id is not None and attacker_id in range(0, 64) else None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need check validity of target_id and attacker_id. My_G_Damage is expected to pass valid ids

# noinspection PyBroadException
try:
minqlx.EVENT_DISPATCHERS["damage"].dispatch(
target_player, inflictor_player, damage, dflags, mod
)
except: # noqa: E722
minqlx.log_exception()
return True

def handle_console_print(text):
"""Called whenever the server prints something to the console and when rcon is used."""
try:
Expand Down Expand Up @@ -510,3 +522,4 @@ def register_handlers():

minqlx.register_handler("kamikaze_use", handle_kamikaze_use)
minqlx.register_handler("kamikaze_explode", handle_kamikaze_explode)
minqlx.register_handler("damage", handle_damage)
18 changes: 18 additions & 0 deletions python_dispatchers.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,21 @@ void KamikazeExplodeDispatcher(int client_id, int is_used_on_demand) {

PyGILState_Release(gstate);
}

void DamageDispatcher(int target_id, int attacker_id, int damage, int dflags, int mod) {
if (!damage_handler)
return; // No registered handler

PyGILState_STATE gstate = PyGILState_Ensure();

PyObject* result;
if (attacker_id >= 0) {
result = PyObject_CallFunction(damage_handler, "iiiii", target_id, attacker_id, damage, dflags, mod);
} else {
result = PyObject_CallFunction(damage_handler, "iOiii", target_id, Py_None, damage, dflags, mod);
}

Py_XDECREF(result);

PyGILState_Release(gstate);
}
21 changes: 20 additions & 1 deletion python_embed.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ PyObject* client_spawn_handler = NULL;
PyObject* kamikaze_use_handler = NULL;
PyObject* kamikaze_explode_handler = NULL;

PyObject* damage_handler = NULL;

static PyThreadState* mainstate;
static int initialized = 0;

Expand Down Expand Up @@ -71,6 +73,8 @@ static handler_t handlers[] = {
{"kamikaze_use", &kamikaze_use_handler},
{"kamikaze_explode", &kamikaze_explode_handler},

{"damage", &damage_handler},

{NULL, NULL}
};

Expand Down Expand Up @@ -117,6 +121,7 @@ static PyStructSequence_Field player_state_fields[] = {
{"powerups", "The player's powerups."},
{"holdable", "The player's holdable item."},
{"flight", "A struct sequence with flight parameters."},
{"is_chatting", "Whether the player is currently chatting."},
{"is_frozen", "Whether the player is frozen(freezetag)."},
{NULL}
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from other PR

Expand Down Expand Up @@ -757,7 +762,8 @@ static PyObject* PyMinqlx_PlayerState(PyObject* self, PyObject* args) {
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));
PyStructSequence_SetItem(state, 12, PyBool_FromLong(g_entities[client_id].client->ps.eFlags & EF_TALK != 0));
PyStructSequence_SetItem(state, 13, PyBool_FromLong(g_entities[client_id].client->ps.pm_type == 4));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is from other PR


return state;
}
Expand Down Expand Up @@ -1499,7 +1505,11 @@ void replace_item_core(gentity_t* ent, int item_id) {
static PyObject* PyMinqlx_ReplaceItems(PyObject* self, PyObject* args) {
PyObject *arg1, *arg2 ;
int entity_id = 0, item_id = 0;
#if PY_VERSION_HEX < ((3 << 24) | (7 << 16))
char *entity_classname = NULL, *item_classname = NULL;
#else
const char *entity_classname = NULL, *item_classname = NULL;
#endif
gentity_t* ent;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already merged



Expand Down Expand Up @@ -1873,6 +1883,13 @@ static PyObject* PyMinqlx_InitModule(void) {
PyModule_AddIntMacro(module, MOD_HMG);
PyModule_AddIntMacro(module, MOD_RAILGUN_HEADSHOT);

// damage flags
PyModule_AddIntMacro(module, DAMAGE_RADIUS);
PyModule_AddIntMacro(module, DAMAGE_NO_ARMOR);
PyModule_AddIntMacro(module, DAMAGE_NO_KNOCKBACK);
PyModule_AddIntMacro(module, DAMAGE_NO_PROTECTION);
PyModule_AddIntMacro(module, DAMAGE_NO_TEAM_PROTECTION);

// Initialize struct sequence types.
PyStructSequence_InitType(&player_info_type, &player_info_desc);
PyStructSequence_InitType(&player_state_type, &player_state_desc);
Expand Down Expand Up @@ -1914,7 +1931,9 @@ PyMinqlx_InitStatus_t PyMinqlx_Initialize(void) {
Py_SetProgramName(PYTHON_FILENAME);
PyImport_AppendInittab("_minqlx", &PyMinqlx_InitModule);
Py_Initialize();
#if PY_VERSION_HEX < ((3 << 24) | (7 << 16))
PyEval_InitThreads();
#endif
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already merged


// Add the main module.
PyObject* main_module = PyImport_AddModule("__main__");
Expand Down
8 changes: 7 additions & 1 deletion quake_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.

#define FL_DROPPED_ITEM 0x00001000

#define DAMAGE_NO_PROTECTION 0x00000008
// damage flags
#define DAMAGE_RADIUS 0x00000001 // damage was indirect
#define DAMAGE_NO_ARMOR 0x00000002 // armor does not protect from this damage
#define DAMAGE_NO_KNOCKBACK 0x00000004 // do not affect velocity, just view angles
#define DAMAGE_NO_PROTECTION 0x00000008 // armor, shields, invulnerability, and godmode have no effect
#define DAMAGE_NO_TEAM_PROTECTION 0x00000010 // armor, shields, invulnerability, and godmode have no effect

typedef enum {qfalse, qtrue} qboolean;
typedef unsigned char byte;
Expand Down Expand Up @@ -1565,6 +1570,7 @@ 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_Damage(gentity_t* target, gentity_t* inflictor, gentity_t* attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod);
#endif

// Custom commands added using Cmd_AddCommand during initialization.
Expand Down