Game detail and play are one portal page: a stage with the screenshot
and a Play button (or Enter) that loads the game in place, controls
under it, details, plays, rating, tags, favorite and add to playlist,
and related games.
Scaling the display up relied on full screen scaling the movie, but games
that set StageScaleMode.NO_SCALE stay at their native size in full screen,
leaving a small game against black. At the exact stage size, full screen
fits every scale mode and the browser scales the stream.
A game is streamable when its platform is allowed (STREAM_PLATFORMS,
default Java, Shockwave and Flash) and its launcher resolves under the same
rules fp-command applies. The display is sized from what it launches:
Director movies including Afterburner .dcr and pre-7 VWCF configs, SWF
headers scaled up to fit 1280x960, a page's largest embed, or a projector's
stage. Dimensions are rounded up to even for H.264.
fp-command applies the launcher's rules to any application path: execs.json
mappings, .bat to .sh, and .exe under Wine, resolving paths case-insensitively
and splitting launch commands as the launcher's shell command would. Windows
programs run from a writable per-session mirror of the install, with Director
Prefs and Flash saves linked into the synced saves. OldCPUSimulator, which
hangs under Wine, becomes cpulimit; Flash projectors go full screen; and
Flashpoint Navigator's toolbars are hidden. This replaces the LocalGames case.
Chilled color scheme as swappable tokens, Fredoka wordmark with a
configurable tagline, genre categories, search and random hotkeys.
The homepage offers a Game of the Day, a Time Machine with a date
picker, recently played games and Flashpoint playlist shelves.
Mature games are hidden unless opted in from the user menu, stored
in the browser and the account. Browse lists default to a seeded
shuffle and accept release date ranges.
The streaming client in a streamed game's iframe shares the app's origin and
keeps its settings in localStorage under its page address, which includes
the session. Those keys changed during play, so they were attributed to the
game and a new batch joined its save every session. Exclude them, and drop
any already recorded when gathering a save.
Saves games keep in the browser now sync with the signed-in player's
account: Ruffle SharedObjects and HTML5 localStorage, IndexedDB databases
(exported and imported whole, typed arrays and blobs included), and streamed
games' save files. Games share the app's origin, so storage that changes
while a game runs is attributed to it. Before a game starts, the account's
save is loaded unless this browser has a newer one; changes upload as they
happen; and a Saves dialog in the player restores any of the last 10, keeping
the save it replaces as a backup.
Adds game_save_snapshots and /api/game-saves for copies of the saves games
keep in a player's browser. An upload matching the current save is a no-op,
the newest 10 snapshots per user and game are kept as backups, and saves may
be up to 10 MB, so the router parses its own bodies past the app's 1 MB JSON
limit.
A proxy that sets the username header twice, as when Authentik's nginx
snippet and the documented lines are both present, made Node join the values
into "brad, brad" and create an account of that name instead of signing in
brad. Read each occurrence separately: identical repeats count once, and
conflicting ones are refused. The docs now note Authentik's snippet already
sets the headers.
On load, a signed-out visitor first asks the backend to sign them in as the
user an authenticating proxy vouches for, before guest mode or a login
redirect takes over; an explicit logout skips this for the rest of the tab.
Settings gains a password card for signed-in users, where a proxy-signed-in
user can set a password without the current one to log in without the
proxy.
With TRUSTED_HEADER_USERNAME and TRUSTED_PROXIES set, POST
/api/auth/trusted-login signs in the user an authenticating reverse proxy
(such as Authentik forward auth) names in that header, believing it only on
connections from the proxy addresses. Users are matched by username, so an
existing account is adopted; otherwise one is created, the first as admin
as with registration. New accounts get a random password, and a user the
proxy vouches for can set their own password without the current one, to log
in when reached without the proxy.
Flash games Ruffle can play now also report streamable, and the player
offers a switch to the original Flash Player streamed from the server.
Flash games launched from a page, which need the browser plugin, stream
instead of loading a page that can't play, and search no longer hides them
while streaming is enabled.
fp-launch runs a LocalGames .exe with Flashpoint's bundled Wine from a
writable copy, linking its Prefs directory, where Director projectors save,
into the synced saves directory. Players without a save start from the Prefs
the game ships with. The image adds the X, font and PulseAudio libraries the
bundled Wine loads.
Games the backend reports as streamable get a Play button. The player starts
a stream session, embeds the stream, and keeps the game's saves in
IndexedDB: sent when the session starts and updated as the server pushes
changes.
Games on other platforms (Java applets first) can be played through a
session manager that streams them from a server-side container. When
STREAMING_API_URL and STREAMING_API_TOKEN are set, game details and launch
data report whether a game is streamable, and POST /games/:id/stream-session
starts a session.
Creates a streamer container per player on flashpoint-web's request, sized
to the game's applet, proxies its Selkies stream under /stream/<id>/, and
removes it when the player disconnects. Saves under /config/saves are
seeded from the browser at start and pushed back over server-sent events.
The JSObject stand-in kept cookies in memory, so saves were lost when the
game restarted. Write them to a file the session manager can sync, with the
policy granting only the agent jar access to that directory.
A Java agent removes them when the viewer window opens, so the stream
shows only the applet. JSObject moves into the agent jar, which puts it on
the class path applets load from.
appletviewer has no netscape.javascript.JSObject, so applets that touch
JavaScript fail to load, and Java 8 denies applets the URLPermission they
need to fetch through an HTTP proxy. Add a JSObject stand-in with a working
document.cookie and a policy granting HTTP URL access.
Runs a Flashpoint game natively in a container and streams it to the
browser. fp-launch follows the Linux launcher's sequence: start the game
server, mount the game's zip, and run startJava.sh through the proxy.
GET /api/games/:id/launch only checked games.read via
validateSharedGameAccess, so an authenticated user whose role lacked
games.play could still mount and download games through the API.
When guest access is enabled, guests now receive games.play alongside
games.read and playlists.read, on both the backend guest fallback
(optionalAuth, sharedAccessAuth) and the frontend guest session. Play
statistics stay authenticated-only.
Ruffle publishes tagged releases and daily nightlies into a single
GitHub feed. The updater took whichever release came first carrying a
web-selfhosted.zip, so a stable release landing at the top of the feed
was installed on hosts tracking nightlies -- and because version
comparison normalised everything to a build date, the resulting semver
never matched, leaving the host reporting an update forever.
Selection now filters on GitHub's prerelease flag, and comparison
splits by channel: build date for nightlies, numeric parts for stable.
The channel lives in the ruffle.channel system setting (default
stable) and governs both manual installs and the scheduled job.
Switching channels records the choice and reports the target build;
nothing is downloaded until the install is requested.
Stable releases are months apart and can fall outside the recent page
the changelog is built from, so that channel falls back to the
latest-release endpoint.