World Events - #289
Open
jptaylor wants to merge 6 commits into
Open
Conversation
|
Preview deployed: https://gradient-bang-m7zdoqs0x-daily-co.vercel.app |
Time-bounded competitive events joined via secret code at character creation. Event-scoped leaderboard with 1-min cache, frozen results on event end, and 48h post-event visibility. New session-aware ranking endpoint so in-game rank reflects event standing automatically.
- Separate dialog leaderboard data from session poll data so scope toggle actually refetches from the correct endpoint - Wire up setPlayerEvent in login flow (login endpoint now returns event membership, Title.tsx sets it on character select) - Fail character creation if event participant insert fails (clean up character + ship on failure since join is only possible at creation) - Surface errors in world_events_list instead of silent HTTP 200 with empty data - Add territory to world events top players (endpoint, types, and UI) - World events client cache TTL set to 1 minute
- Add TTL to dialog leaderboard data (5min global, 1min event) so reopening the dialog refetches stale data instead of showing it indefinitely - Return event_title from character create endpoint so newly created event characters get setPlayerEvent called immediately from the create handler, without needing a re-login
CreateCharacter already sets playerEvent from the create response. Title.tsx was unconditionally re-deriving it from the characters store, which doesn't contain the just-created character, clearing the event. Now only does the store lookup for existing character selection.
CreateCharacter now calls setPlayerEvent(null) for non-event characters, clearing any stale event state from a previously selected character and resetting leaderboardScope back to global.
jptaylor
force-pushed
the
jpt/world-events
branch
from
April 15, 2026 16:54
ac3eae3 to
059da29
Compare
|
Preview deployed: https://gradient-bang-3dwkf3md1-daily-co.vercel.app |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
World Events let you run time-bounded competitions inside Gradient Bang. An admin creates an event in the database with a title, description, join code, start/end dates, and a visibility window. Players enter the join code during character creation to link that character to the event — one event per character, join-at-creation only. While the event runs, participants are ranked against each other across all four leaderboard categories. When the event ends, a cron job freezes the leaderboard into a snapshot blob. The event remains visible (with frozen results) until the visibility window expires, typically 48 hours after ending.
On the client side, if your character is in an event, your in-game rank badge automatically reflects event standing (the session polling endpoint resolves scope server-side). The leaderboard dialog shows a scope toggle between the event and global views, and a "World Events" tab lists all visible events with top players per category. The join code is never shown publicly — events are discoverable but joining requires the code, which can be distributed via external channels (e.g. a Product Hunt page linked from the event description).
Changes
Database:
world_eventstable (title, description, join code, dates, frozen results blob, image/link URLs)world_event_participantsjunction with unique-per-character constraintleaderboard_cachewithevent_idcolumn for event-scoped cachingvalidate_event_join_code()andfreeze_world_event_results()functionsEdge functions:
user_character_create— accepts optionalevent_join_code, validates early, inserts late, rolls back on failure, returnsevent_id+event_titleleaderboard_resources— accepts optionalevent_id, 1-min TTL for event cache, returns frozen blob for ended eventsleaderboard_session— new session-aware endpoint, resolves event vs global by character_idworld_events_list— new public endpoint, returns visible events with top 3 per category across all four leaderboard dimensionsuser_character_listandlogin— return event membership per characterClient:
CreateCharacter— "Have an event code?" disclosure input, setsplayerEventfrom create response (or clears it for non-event characters)usePlayerRank— pollsleaderboard_sessionwith character_id instead of rawleaderboard_resourcesLeaderboardPanel— scope toggle (event name / Global), World Events tab with event cards, frozen results indicatorplayerEvent,leaderboardScope,leaderboardDialogDatawith timestamp,worldEventsTitle— setsplayerEventon character select, skips overwrite for new characters