Pin minqlx to fixed SHA, add damage-event patch - #145
Conversation
Pins the minqlx clone to a known-good upstream commit instead of a floating master, and backports the damage event/dispatcher from the mgaertne/minqlx fork as a local patch, consistent with the existing reset-accuracy patch mechanism. Verified locally: pinned SHA + both patches apply with no rejects and compile cleanly.
There was a problem hiding this comment.
PR Review: Pin minqlx build to fixed SHA + damage-event patch
Strengths
- Good reproducibility fix (
rebuild_minqlx.yml:32,setup_host.yml:529): Pinning to a specific commit SHA eliminates non-deterministic builds caused by a floatingmaster. The old TODO comment is cleanly removed. damageadded tono_debug(_events.py:33): Damage events fire on every hit; excluding them from the debug log prevents log flooding, consistent with howframeandstatsare handled.- GIL handling is correct (
python_dispatchers.c:296–314):PyGILState_Ensure/Releasewraps the Python call properly;Py_XDECREFsafely handles a NULL result from a failedPyObject_CallFunction. - Documentation is thorough:
docs/technical.mddocuments the patch mechanism and both patch filenames, andreleases.md/version.jsonare updated consistently.
Issues
Critical (Must Fix)
ql-assets/patches/minqlx-damage-event.patch → hooks.c line ~258 (patch hunk +28)
attacker_id = attacker - g_entities;attacker can legally be NULL in Quake 3 game code (world/environmental damage, trigger damage). Subtracting a null pointer from a non-null pointer is undefined behavior in C (C11 §6.5.6). On x86-64 the result is an enormous integer, and whether DamageDispatcher's attacker_id >= 0 guard catches it is implementation-defined. In the Python handler the attacker_id in range(0, 64) check would likely keep the raw garbage integer out of Player(), but the UB itself can cause miscompiles with aggressive optimizers.
Fix:
attacker_id = attacker ? (attacker - g_entities) : -1;This mirrors how the existing KamikazeExplodeDispatcher pattern handles optional entities and makes the DamageDispatcher dispatch path deterministic.
Important (Should Fix)
SHA duplicated across two playbooks (rebuild_minqlx.yml:10, setup_host.yml:12)
minqlx_git_version is defined independently in both files. If someone bumps the SHA in one playbook and forgets the other, the two build paths silently diverge—setup_host (initial provision) and rebuild_minqlx (day-2 rebuild) will produce different binaries from the same repo. This is exactly the class of bug the SHA pin is meant to prevent.
Fix: Extract to a shared group_vars file (e.g., ansible/group_vars/all/minqlx.yml) or a dedicated vars_files include so there is one source of truth.
No Python-layer tests for handle_damage (python/minqlx/_handlers.py:435–447)
The new handle_damage function has two non-trivial behaviors: it guards target_id and attacker_id with range(0, 64) before constructing Player objects, and it swallows all exceptions. The existing test suite covers other handlers (e.g., handle_kamikaze_explode). A NULL attacker (raw integer, not in range) and a mid-range player id (within range) should both be exercised.
Minor (Nice to Have)
Trailing whitespace in patch (python_dispatchers.c hunk, line after Py_XDECREF(result);)
A trailing space is present after Py_XDECREF(result); in the dispatcher. Minor, but it will show up in future whitespace-sensitive diffs.
depth: 1 with a commit SHA (rebuild_minqlx.yml:33, setup_host.yml:530)
Ansible's git module resolves this by issuing git fetch --depth 1 origin <sha>, which requires the SHA to be advertised by the remote (i.e., reachable from a branch tip). This works on GitHub today, but is not guaranteed to work if the upstream repo is later force-pushed or the commit is no longer reachable. A tag on the pinned commit (or dropping depth: 1 when a SHA is specified) would be more robust.
Assessment
Ready to merge? No — with fixes.
Reasoning: The NULL-attacker pointer arithmetic in hooks.c is undefined behavior that could silently misbehave depending on compiler optimization level; it has a one-line fix. The SHA duplication is an operational risk that undermines the reproducibility goal of the PR. Both should be addressed before merge.
…e patch attacker can legally be NULL (environmental damage) so attacker - g_entities was undefined behavior; guard it and fall back to -1, matching the existing "no valid attacker" convention DamageDispatcher already expects.
Summary
fbdd915185337791d8e209dc4b686a1ee60d3721onMinoMino/minqlx) instead of a floatingmaster, in bothsetup_host.ymlandrebuild_minqlx.yml.ql-assets/patches/minqlx-damage-event.patch, backporting thedamageevent/dispatcher (plus expandedDAMAGE_*C flag constants) from themgaertne/minqlxfork's 11-commit diff — verified to be the only functional change in that diff.docs/technical.md.Compatibility
Checked
qlsm_plugins/(force_rate, highfps, improved_timer, nullmove, ql_netfix, spec_switch_guard) — none referencedamage,is_chatting, orPlayerState, so this is a non-breaking, purely additive change for existing plugins.Test plan
MinoMino/minqlxat the pinned SHA, applied bothql-assets/patches/*.patchfiles in the same glob order the playbooks use, confirmed zero.rejfilesmakeagainst the patched tree — build succeeds,bin/minqlx.x64.soproduced