• exec/load/syncretro_lobby

    From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Friday, July 17, 2026 02:57:00
    https://gitlab.synchro.net/main/sbbs/-/commit/9396232fcb7ac80e9b0844cc
    Modified Files:
    exec/load/syncretro_lobby.js xtrn/syncdoom/lobby.js xtrn/syncduke/lobby.js xtrn/zmachine/zmachine.js
    Log Message:
    doors: log what the user actually played, not just the lobby

    The Terminal Server logs "Executing external program: <name>" when it spawns
    an xtrn entry, so a door installed as its own entry names the game in the
    node log. A door reached through a JS lobby does not: the log named the
    lobby, and the WAD set / map / cartridge / story picked inside it went unrecorded.

    Log the pick at each lobby's launch choke point, via bbs.logline() with the same "X-" code the server's own line uses, so one grep finds both:

    X- Executing external program: Z-Machine
    X- Playing Z-machine story: Ad Verbum

    syncdoom's sd_play() now takes the picked wadset rather than the flattened argument array -- every call site was already passing sd_wadset_args(cfg, ws) and discarding the ws the log line needs, so moving that call inside shortens all five. The zmachine chooser stashes the resolved IFDB title for the log;
    the -file path has no chooser, so it prettifies the filename the way the chooser would have. syncretro reuses the parsed title and console label it already builds for the who's-online status.

    Only the player's own launch logs. syncdoom's detached dedicated server (-spawnserver) stays silent -- it is not a play.

    Verified live on node 11: SyncDOOM, SyncDuke, SyncNES, SyncIvision and
    zmachine each produced their line in a real session.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Friday, July 17, 2026 02:57:00
    https://gitlab.synchro.net/main/sbbs/-/commit/90df3d16a49a242ce5e905c0
    Modified Files:
    exec/load/syncretro_lobby.js xtrn/syncivision/test_lobby_headless.js Log Message:
    syncretro: page the lobby with '+'/'-', find with 'F'

    '+' pages forward like N and '-' back like P: they sit next to each other on the keyboard -- and on the numeric keypad, under the hand that just typed a cartridge number -- so they read as forward/back without being told. Both are below '0' in ASCII, so neither can be mistaken for the start of a cartridge number.

    Find is now 'F', which names itself the way Next/Prev/Quit already do; '/' stays bound as an alias. The prompt names F and leaves the aliases off, as it already does for Enter/PgUp/PgDn/Home/End -- the row has to stay inside 80 columns.

    Also repairs the headless lobby test, which was red before any of this:

    - Its bbs stub had no logline(), so the play-logging line added by the
    earlier commit in this batch threw on the launch path. Stub it, and cover
    the line it writes.
    - It asserted the board prints "Most played" or "[1-". Neither string has
    ever appeared in the lobby, and the board cannot paint at all on that
    first pass -- the harness refuses to run unless the plays file is absent.
    Assert the footer, which is always drawn.
    - It asserted the door binary is quoted. The lobby deliberately leaves it
    bare: external() only reads the command as an absolute path when it starts
    with the drive letter or '/', and a leading quote defeats that. The check
    also pinned the binary to a flat door path, but the "<os>-<arch>" sub-dir
    is optional -- never present on Windows, and on *nix only when it is
    populated -- so either layout is legal and neither can be hard-coded.
    Assert the contract instead: leading token, absolute, unquoted, ending in
    the binary and its %. specifier.

    The new keys are asserted against the page the lobby draws, so an unbound key would leave the count unmoved and fail. Verified against the live 186-ROM Intellivision install, which paginates; the harness needs the install dir.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Friday, July 17, 2026 02:57:00
    https://gitlab.synchro.net/main/sbbs/-/commit/c4720dc2b85bd3775856c69e
    Modified Files:
    exec/load/syncretro_lobby.js xtrn/syncivision/syncretro.example.ini test_lobby_headless.js xtrn/syncnes/syncretro.example.ini
    Log Message:
    syncretro: play a sysop-supplied sound on lobby entry

    SyncDuke and SyncDOOM play a one-shot sound when a player enters the lobby,
    via game_lobby.js's shared enter_sound(); the SyncRetro consoles did not. syncretro_lobby.js already loads game_lobby.js, so this is the same one-line call the siblings make, plus the [lobby] section read that feeds it. Both consoles inherit it from the one shared call site.

    Nothing ships: silent until a sysop sets [lobby] enter_sound, and silent on a terminal that cannot decode audio files. The templates document the key with one caveat the siblings don't need -- SyncDuke can tell you to extract a quip from your own GRP and SyncDOOM a blast from your own WAD, but a cartridge has nothing to extract: a console's sound is generated by its sound chip as the game runs, so it is nowhere in the ROM as data.

    The headless harness gains a stub console that can ANSWER the libsndfile
    probe, so the test drives the real path: a configured sound is uploaded,
    loaded and queued when the terminal decodes audio, and nothing is queued when it doesn't. It calls the helper directly with its own cfg rather than writing
    a [lobby] key into the sysop's live syncretro.ini to test itself.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Tuesday, July 21, 2026 01:41:00
    https://gitlab.synchro.net/main/sbbs/-/commit/88cf67b5761d0cff1c95aa76
    Modified Files:
    exec/load/syncretro_lobby.js
    Log Message:
    syncretro lobby: pass the ROM relative to the door directory

    The door command line is assembled by the BBS into a buffer that truncates silently at 260 characters, and the ROM is the LAST argument on it -- so the ROM is what disappears when the line overflows, producing a door that reports its content missing while the BBS log shows that content's full path.

    Passing "roms\pacman.zip" instead of "s:\sbbs\xtrn\syncarcade\roms\pacman.zip" gives 24 characters back, taking a SyncArcade line from ~240 to ~216 and roughly doubling the margin. That covers a long game title: "1943: The Battle of Midway" costs 19 characters more than "Pac-Man" and previously came within
    a few bytes of the cliff.

    Safe because the BBS starts the door IN the door's directory -- Synchronet passes startup_dir to CreateProcess as the working directory on Windows and chdir()s to it before exec on *nix -- and sr_resolve_file() tries the name as-given (against that cwd) before absolutizing, all of it before the door chdir()s into the per-user sandbox. It is not a new dependency: the door already locates syncretro.ini, its core and its BIOS relative to the same
    cwd, so a door started elsewhere could not run at all.

    Built from rules.dir rather than a hardcoded "roms" so a sysop who moves the ROM directory keeps working -- the lobby names the directory it actually scanned, which also side-steps the door's own bare-name fallback being hardcoded to roms/.

    Verified against the installed door: galaga, dkong and mspacman all load from
    a relative argument, and the derived content path is identical to the absolute form's.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Thursday, July 23, 2026 00:08:00
    https://gitlab.synchro.net/main/sbbs/-/commit/185d73c468ba23574f01c1f3
    Modified Files:
    exec/load/syncretro_lobby.js src/doors/syncretro/main.c syncretro.h syncretro_config.c syncretro_door.c syncretro_input.c xtrn/syncarcade/syncretro.example.ini xtrn/syncivision/syncretro.example.ini xtrn/syncnes/syncretro.example.ini
    Log Message:
    syncretro: end the game when the player has stopped playing

    The BBS cannot do this for a graphical door. Synchronet's per-program "Maximum Inactivity" is enforced against a counter that any successful socket read resets, and this door's frame pacing makes the terminal answer roughly ten times a second on its own, so the counter never climbs. A sysop who sets that value sees it accepted and sees it do nothing.

    So the door judges presence itself, on the one signal the pacing cannot forge: a real keystroke. sr_key_apply() feeds the shared termgfx idle clock, and nothing else does -- deliberately not the CSI dispatcher, which also sees the terminal's pace-acks and capability replies, and would report a departed user as present forever.

    Once per frame the door polls the clock. Inside the warning window it paints a countdown toast on the bottom row, re-armed each second so it stays up; any
    key clears it and restarts the full threshold, with no penalty for having been warned. At the threshold it latches, and sr_door_should_exit() ends the
    session through the same path the -t time limit already used.

    The countdown says "terminating", not "disconnecting": this ends the GAME and hands the player back to the BBS, it does not drop the call. Once they are at
    a normal BBS prompt the BBS's own inactivity timer applies again -- and that one does work, because nothing is pacing the link with DSR any more.

    Two ways to configure it, and BOTH work with no JS anywhere in the path, because this door also runs under other BBS software via DOOR32.SYS:

    - -i<seconds> on the command line. The lobby passes it, having evaluated the
    exempt ARS (which needs a user record the door cannot see), and passes -i0
    explicitly for an exempt user so the exemption positively overrides the ini. - [idle] timeout / warn in the door's own syncretro.ini, which on a
    non-Synchronet install is the only mechanism there is.

    The argument wins when given, mirroring how -t beats the drop file.

    Two things worth knowing for the next door that adopts this:

    - The clock is armed after sr_config_apply(), not beside the -t deadline in
    sr_door_setup(). Setup runs before the ini is read, so arming there would
    read a threshold of zero and silently leave every no-lobby install with no
    timeout -- on exactly the path where the ini is the only way to set one.
    - -i gets a predicate beside the -s and -t ones rather than an inline test,
    because sr_door_sanitize_argv() has to strip precisely what the parse
    consumed. A second spelling of that rule is how the two drift apart, and an
    unstripped -i900 would reach the core's argv.

    Verified headlessly through capture mode against a real core: -i fires at its threshold to the second, -i0 never fires, the ini alone fires with no -i present, an explicit -i0 overrides an ini timeout, and a "5s" style duration parses. The countdown's on-screen behavior was confirmed on a live node, which capture mode cannot show -- it stubs the toast out.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Thursday, July 23, 2026 00:08:00
    https://gitlab.synchro.net/main/sbbs/-/commit/5bd9a9251416f85e3f649284
    Modified Files:
    exec/load/syncretro_lobby.js src/doors/syncretro/syncretro.h syncretro_config.c syncretro_door.c syncretro_input.c syncretro_io.c xtrn/syncarcade/syncretro.example.ini xtrn/syncivision/syncretro.example.ini xtrn/syncnes/syncretro.example.ini
    Log Message:
    syncretro: default the idle timeout to 10 minutes, and let "any key" mean it

    Two changes on top of the idle-user detection added earlier in this batch,
    both found by testing at a real terminal.

    DEFAULT. The timeout was opt-in: no configuration meant no idle detection at all, so the feature would sit dormant on almost every install. It now defaults to 10 minutes -- lenient on purpose, because a player reading a hint screen, watching an attract loop or answering the door is not gone, and a false termination costs far more than a node held a few extra minutes. Setting `timeout = 0` still disables it outright, and "unset" no longer means "off", which the shipped ini templates now say explicitly.

    The number lives in two places that MUST agree, because each governs a path
    the other cannot see: SR_IDLE_DEFAULT in syncretro_config.c is the no-lobby (DOOR32.SYS / other-BBS) default, and SYNCRETRO_IDLE_DEFAULT in the lobby is the Synchronet one, since the lobby always passes -i. Both carry a comment pointing at the other.

    "PRESS ANY KEY" NOW MEANS ANY KEY. The countdown invites a keystroke, and the likeliest key a player reaches for is the space bar -- which every controller profile binds to pause. Answering the countdown therefore paused the game and raised the pause/legend screen, which is not what the prompt offered; any
    other bound key would likewise have fired its action for a player who only meant "I am still here".

    The key that answers a countdown is now consumed: it resets the clock, retires the countdown, and is not dispatched further. Only the press edge is
    swallowed, so the release still falls through and no key is left stuck down, and this happens only while the countdown is actually on screen -- space at
    any other moment still pauses exactly as before.

    Retiring the countdown immediately also hands the shared bottom row straight back to the Ctrl-S stats strip rather than leaving it covered for the rest of the toast's dwell.

    Verified by driving the door over -stdio with an injected keystroke: the countdown appears, the space is consumed with no pause screen, the clock
    resets far enough to survive the original deadline, and the countdown reopens
    a full threshold later. A control run with idle disabled confirms space still pauses normally, and one with no keystroke confirms the legend is not drawn on its own.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net