Repository navigation
RustyNES v3.4.1 — "Beacon" (the hosted lobby live (the TURN relay held off), the browser RetroAchievements build fixed, version-named MiSTer release assets, and the MiSTer save engine's cartridge-change defects and v3.4.0's two build findings fixed)
RustyNES v3.4.1 — "Beacon"
A patch release on v3.4.0 "Waypoint". It puts the hosted netplay services live and points the browser and mobile builds at them, fixes the browser RetroAchievements build, names the MiSTer release assets by version, fixes the MiSTer core's save engine on a cartridge change and the build warning v3.4.0's bitstreams shipped past, fixes which binary the profile-guided Linux build ships, and takes every dependency to its newest compatible version. Nothing in the emulation core changes: movies, netplay sessions and save states from v3.4.0 are compatible (epoch 5, protocol 9).
Added
- The hosted lobby is live. v3.4.0 shipped the hosted services as code; the lobby now runs on the maintainer's Cloudflare account. The WebAssembly demo and the Android and iOS builds made by this project's CI are compiled with its address, so a room code works with no setup. (Corrected 2026-10-20: this line first named the desktop binaries too. Native desktop netplay does not use the hosted lobby: it connects directly by address, as before, and nothing in the desktop binaries reads the hosted address.) The TURN relay is deployed but switched off (see "The hosted services' live checks" below): sessions connect directly, which covers most home networks, and two players both behind strict NATs cannot connect until it returns. On Android and iOS a fresh install now hosts and joins room codes without a relay URL. A
hosted_base_urlin the config overrides the address; F-Droid's own builds and plain source builds do not carry it. What the service keeps is inPRIVACY.md.
Changed
- Dependencies at their newest compatible versions: Rust 1.99.1 (the libretro buildbot with it), every Cargo lockfile (the three open Dependabot updates folded in), the hosted Worker's toolchain (TypeScript 7, vitest 5, wrangler 4.151), Python 3.15 for the handbook build, and the newest pins of the CI actions.
- MiSTer release assets are version-named:
RustyNES_MiSTer-v3.4.1.rbf(the off-die headline, needs the SDRAM add-on) andRustyNES_MiSTer-v3.4.1-lite.rbf(on-die). MiSTer's updater still installs the dated files from the RustyNES_MiSTer repository'sreleases/. Every earlier release's bitstreams were renamed the same way (same bytes, same digests) and their notes updated.
Fixed
- The browser RetroAchievements build loads rcheevos. Three packaging faults each stopped it: an empty feature attribute in the web page made
trunk buildignore--featuresand--no-default-features(the documented lightweight canvas build came out as the 12.6 MB default build; it is now 1.3 MB), the glue imported rcheevos from a directory trunk does not copy it to, and the rcheevos module had no ES-module export. Web RetroAchievements stays off on the demo until RetroAchievements answers the project's integration request, and the hosted RA proxy is deployed switched off for the same reason. - The profile-guided Linux binary. When the PGO gate promoted, the Linux download it replaced was a test build: a later stage rebuilt the binary at the same path without the
fullfeatures (RetroAchievements, scripting, HD packs). The optimized binary is now the one shipped when the gate promotes, and it must be thefullbuild to be promoted. This release's gate did not promote (the PGO build measured 5.05% slower than plain), so the Linux download is the plain build. (Corrected 2026-10-20: the promotion check first required the hosted base, which a native binary never contains; it now checks for the full build's RetroAchievements client.) The release history shows one promotion, v2.9.6: its Linux download lacks the RetroAchievements client and is rebuilt from the v2.9.6 tag with the full features after this release. - MiSTer save states across a cartridge change. A load interrupted by a ROM download no longer writes one byte outside its grant, and a manual save interrupted mid-write empties its slot instead of leaving one a later load accepted with a mix of two states.
- MiSTer build warnings. The Quartus width warning in the MMC2/MMC4 CHR selection (the one
release-rbf.shrefused v3.4.0 on) and the Zapper aim's constant ninth bit are fixed, with no change in behaviour or save-state layout. The width warning came from the CHR register index, not the slot index a first attempt changed; the release check caught it and the bitstreams were rebuilt. - Android and iOS netplay settings describe the hosted defaults, a custom relay must be a
ws://orwss://URL on both, and clearing the field returns to the hosted lobby. - Code scanning. The PPU's emphasis table is an ordinary Rust module rather than an
include!d expression, which CodeQL's Rust extractor could not parse (101 of its 138 warnings).
The hosted services' live checks
Health check ok; the lobby test against the live socket passed; a credential request returns a 3-hour credential, the seventh request from one address is refused, a GET is refused, and the kill switch answers "TURN disabled" and restores; a browser gathers relay candidates with a freshly minted credential.
Why the relay is off. The Worker asks Cloudflare for credentials that expire after 3 hours, and Cloudflare documents that an expired credential is refused. Tested on 2026-10-19, it is not: a 3-hour credential 2 h 38 min past its expiry and 10-minute test credentials past theirs all still obtained a relay, including ones the Worker had revoked through Cloudflare's own revoke API (the revoke calls succeeded), while a made-up credential was correctly refused. Cloudflare's FAQ says an expired credential stops being billed at once, but that was not measured, so the maintainer chose to keep the relay off until it is. This release adds the revocation anyway: the Worker revokes every credential at the end of its lifetime, retrying failures and never dropping one silently, so the relay can be switched back on with one setting once the behaviour is understood.
Privacy policy (effective 2026-10-19). With the relay on, the service keeps each relay credential's username (a random identifier the provider issues, not linked to you or your address) until the credential's lifetime ends and its revocation is confirmed, at most 48 hours after that, so that it can revoke it. The secret half is never stored. With the relay off, as shipped, nothing is kept. PRIVACY.md has the full text.
Still open, and the maintainer's: two browsers through the lobby, a relayed session across two NATs, the RetroAchievements unlock (held for the integration request), and the 30-day traffic reading.
The MiSTer core
- Release candidate, not hardware-verified. No hardware has run either bitstream.
- New bitstreams at fitter seed 2, built 2026-10-19 (BUILD_DATE 261019), one compile each. At the maintainer's direction this release ran no seed sweep and no co-simulation ladder: the RTL changes are confined to the save engine's cartridge-change paths, the MMC2/MMC4 CHR selection and the Zapper aim's width, and their own gates and mutants were run instead (below).
- off-die
RustyNES_MiSTer-v3.4.1.rbf(the headline), md5474fa68f3f7888aaa6214e74aace2aa8: setup +0.277 ns, hold +0.068 ns over all four timing corners; the SDRAM read capture +0.426 ns setup and +1.189 ns hold, assuming zero board delay; - on-die
RustyNES_MiSTer-v3.4.1-lite.rbf, md5ca92523115ee9d864ce20c13a66a7eb7: setup +0.151 ns, hold +0.024 ns. The hold margin is thin; v3.4.0's was +0.117 ns at the same seed, and a sweep was not run.
- off-die
release-rbf.shpassed every check on both builds this time, the Quartus width warning included (0 occurrences), and staged the dated files.- The save engine:
ss-engine-gategains the cartridge-change cases (a load abandoned on a byte-write edge; a save abandoned while streaming the scan chain and while streaming memory), and both fixes' mutants are caught. The MMC2/MMC4 frame gates match on every pixel and their bus gates on every checkpoint, and a mutant that swaps the new CHR select is caught. - The save-state layout is unchanged (fingerprints on-die
cb630ede, off-die5eaf5eb4), so v3.4.0 states load. - The tag on the RustyNES_MiSTer repository is a lightweight tag made by the release, not a signed annotated tag as before: the signing key was out of reach for this release.
Verification
cargo test --workspace --features test-roms --no-fail-fast: 3,550 passed, 0 failed, 13 ignored across 172 test binaries; withcommercial-romsadded, 3,801 passed, 0 failed, 18 ignored across 182 (at148ee28b, on Rust 1.99.1). AccuracyCoin 146/146, nestest 0-diff, and the epoch fingerprint holds at epoch 5: nothing in this release moves a frame, a sample or a bus cycle. The PPU emphasis table moved into a module byte for byte; its generator reproduces the committed file exactly.- Every clippy feature set the project gates (
-D warnings), both WebAssembly targets, theno_stdbuild and rustdoc are clean on 1.99.1. The hosted Worker's suite: 122 passed, TypeScript 7 clean; each new behaviour was shown to fail first, and its mutants are caught. - The iOS app was compiled once on CI for this release, at
3a6f97e9, with the Swift netplay changes. Later commits changed two of its hint strings, a label and the relay-URL check (a WebSocket URL must now name a host), which were not compiled again (Apple builds are run by hand). Android builds on every pull request.
Install
- Download the pre-built binaries for Linux, macOS, and Windows below.
- The MiSTer bitstreams are attached below as a release candidate, not hardware-verified:
RustyNES_MiSTer-v3.4.1.rbf(off-die, needs SDRAM) andRustyNES_MiSTer-v3.4.1-lite.rbf(on-die). - The WebAssembly build is live at doublegate.github.io/RustyNES.
- The RetroArch core is in RetroArch's Online Updater on the platforms the libretro buildbot publishes to.
- Licensed under GPL-3.0-or-later.