These moved from the Coffey-Labs org to jcoffey-dev, where they belong.
GitHub redirects a transferred repository, so nothing was broken by
leaving them -- but a source link that lands via a redirect is still
naming an owner that no longer holds the code, and the redirect only
lasts as long as nobody creates a repository of the same name in the
old place.
Only the seven repositories that actually moved are rewritten. Every
other Coffey-Labs URL is left alone: ihasmail, cairnobs and the rest
are still there.
The leaderboard started in this repository because when it was written there
was one game. It was already being treated as shared -- games.jcoffey.dev
calls it "the shared leaderboard" in its compose file and nginx proxies it at
a site-wide /api/ rather than under /lemonade/ -- but it was still this game's
code, and the second game would have meant a second container, a second volume
and a second database to back up for every game after that.
It lives in https://github.com/Coffey-Labs/games-scores now, which is its own
repository rather than either game's. A shared board belongs to no game, and
leaving it here would have made every future game depend on the lemonade stand
for its leaderboard.
What changes here is smaller than the diff suggests. server/ goes, and with it
scripts/dev-all.mjs and two package scripts. The client names the game on every
call and is otherwise untouched: rows still come back as assets, days, glasses
and broke, because the shared service hands each game its own field names back.
Nothing about the deployed board moves. The volume keeps its name, the rows are
migrated in place on first boot, and the original table is kept alongside as
scores_lemonade_v1 rather than dropped -- it is the only copy of a board real
people are on. Bundles already in browsers keep working too: a post with no
game at all is treated as this one, deliberately, until this build has been out
long enough that nobody is running the old one.
Build clean.
The hub page and the games.jcoffey.dev deployment move to their own
private repository; this one keeps only the game, its leaderboard and a
generic compose that runs both anywhere. No deployment details remain
here, and no site references.
A SOURCE link now sits on every screen. AGPL section 13 entitles anyone
playing this over a network to the source of what they are playing, and
a link in the footer is how that offer gets made.
The high score table moves out of the browser and into a service, so
every player is on one board. It is a Node process with SQLite, no build
step and no native modules - node:sqlite ships with the runtime and Node
runs the TypeScript directly.
Everything in a request is treated as hostile: names forced to a
printable subset, every number range-checked, and scores refused if they
could not have happened in the days claimed. Bodies capped, submissions
rate limited per address. It still cannot prove a score is real, and
server/README.md says so plainly rather than implying otherwise.
The game degrades properly without it: the board says it cannot reach
town, posting happens behind the closing standings, and play is
untouched.
Deployment is three containers behind the host's nginx - the hub at /,
the game at /lemonade/, the scores service at /api/ - so the game keeps
its own container and a second game is just another service.
Licensing: AGPL-3.0-or-later, Affero because the leaderboard is a network
service. NOTICE.md credits Bob Jamison and Charlie Kellner, records that
this is clean-room work, and is honest about the one thing it is not:
some on-screen wording is quoted from the original and cannot be
licensed by us.
The street can now get better as well as worse. The summer fair brings
the town out and lifts what they will pay; a rival on the next corner
takes a share and lingers. Both show in the art and in the crowd. Over
500 seasons a player who reads the briefing survives every time and
finishes around $25.80; one who ignores it goes broke 70% of the time.
Faithful day loop - weather report, cost of lemonade rising over the
summer, glasses/signs/price decisions, thunderstorms, heat waves and
street crews - with the $$ LEMONSVILLE DAILY FINANCIAL REPORT $$ laid
out the way it printed.
The simulation is kept out of React so a season can be run headlessly:
a careful player compounds $2.00 into roughly $22 over twelve days,
while an all-in player goes broke about seven times in ten.
Weather scenes are pixel rectangles in SVG and the music is a small
chiptune engine on the Web Audio API, so there are no binary assets.
The screen is locked to a 4:3 tube and never scrolls.