https://gitlab.synchro.net/main/sbbs/-/commit/7b224665c1128790d1568987
Modified Files:
src/doors/syncduke/syncduke.h syncduke_config.c syncduke_door.c syncduke_input.c syncduke_io.c syncduke_plat.c xtrn/syncduke/syncduke.example.ini
Log Message:
syncduke: end the game when the player has stopped playing
The fifth and last phase-2 door to adopt the shared idle-user clock. Same reasoning as the siblings: the BBS's own "Maximum Inactivity" cannot see a DSR-paced door, since that counter is reset by any socket read and frame
pacing makes the terminal answer ~10x/second on its own. Presence has to be judged on real input, which only the door can tell apart from its own pace-acks.
Warn-then-terminate, countdown on the bottom row, same wording as the
siblings, 10-minute default. Configured by -i<seconds> or the door's own
[idle] section; the argument wins when given, so a launcher can pass -i0 to excuse an exempt user. Unset is NOT off, which the shipped template now
says.
THE CLOCK IS ARMED LAZILY, ON FIRST MAIN-TIME USE, and that is not a
stylistic choice. Arming at the end of syncduke_door_resolve() looks right
-- the resolve is lazy, and it is where -i is parsed -- but it is wrong: syncduke_door_splash() calls syncduke_door_socket(), which resolves, and
splash runs inside the PRE-MAIN CONSTRUCTOR. syncduke.ini is read by syncduke_config_init(), a constructor in a different translation unit, so
the two race with undefined order. The door's won, the ini had not been
read, and every install would have silently used the compile-time default
with [idle] ignored entirely. Arming on first use puts it after every constructor, where both sources are known.
The activity hook goes in three places and deliberately not in the obvious
one. press() (legacy bytes), hold_press() and hold_release() (native kitty/evdev) are hooked; their common rawq_push() is not, because syncduke_input_expire() drives it with synthesized key-ups -- which would
forge activity for a player who has already walked away. hold_release()
wakes the clock but never CONSUMES: swallowing a release would leave that
key stuck down in the engine. Mouse events count too, since mouse-steer lets someone play for a long stretch with no keystroke.
Input that answers the countdown is consumed rather than delivered, so
"press any key" cannot also fire a weapon or open a menu.
-i is matched on an ALL-DIGIT suffix, both when parsed and when stripped -- deliberately tighter than this door's own -s/-t rule, so no Duke word option beginning with -i could ever be swallowed by it.
This door had no unified exit check to hang the timeout on, so one now rides
in _nextpage() beside the session time limit: the single place already
reached on every presented frame. It restores the terminal before leaving, rather than dropping the player on a wedged screen.
The countdown does not get its own writer. It becomes the Ctrl-S strip's content while showing, so it lands on the row that strip already owns. When
it clears, the row is wiped and a full repaint forced -- the sixel tier never repaints that reserved row on its own, so a bare wipe would leave it blank.
Verified offline (-i fires to the millisecond, -i0 never does, the countdown ticks down on the wire with correct plurals, the ini alone works with no -i, and an explicit -i overrides it) and on a live node.
Co-Authored-By: Claude Opus 4.8 (1M context) <
noreply@anthropic.com>
---
þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net