bloxfa.st
Docs

Get started

Project layout

What lives in src/shared, src/server and src/client, and how data gets from the server to the UI.

All the game code is in boilerplate/src/, split by where it runs. Rojo puts shared/ in ReplicatedStorage, server/ in ServerScriptService and client/ in StarterPlayerScripts.

src/shared

Code and config that both sides load. Players can read everything here, so nothing secret goes in it.

File What it holds
Config.ts Currencies, products, passes, multipliers, boosts, items, variants, inventory limits, the index, social rewards, daily and playtime rewards, onboarding steps, leaderboard, admins, audio, DataStore names, trading
EggsConfig.ts Eggs: contents, weights, prices, pity, Robux products
WheelConfig.ts Spin wheel prizes, weights, free spin timer, spin products
QuestsConfig.ts Quest boards, achievements, the Goal type and goalText
SeasonConfig.ts Season pass settings and the list of seasons
Remotes.ts Every remote in the game, with argument checks and rate limits
Net.ts The typed remote wrappers, the check helpers and the rate limiter
Attributes.ts Names of the player attributes the server sets for the UI
Items.ts Item instance types, item origins, merge order, item bonuses
Odds.ts Weighted odds, rolling, and percentage formatting
Gacha.ts Egg and wheel odds with luck, pity and once-only items, and egg prices
Prices.ts Robux prices from GetProductInfoAsync, cached
LiveEvents.ts Which EVENT_MULTIPLIERS are running
EventBus.ts A small typed event bus
Time.ts UTC day and week helpers
demo/ Demo hub layout (layout.json, Layout.ts), pet looks, the demo season and the demo's one remote. See The demo world

src/server

One module per system. Each exports a start(), and Main.server.ts calls them in order:

Policy.start();
Multipliers.start();
Gamepasses.start();
Currency.start();
Boosts.start();
Inventory.start();
Purchases.start();
// ...
Season.start();
Rebirth.start();
Admin.start();

PlayerData.start();

PlayerData.start() goes last. Systems register PlayerData.onLoad handlers inside their own start(), so by the time anyone's data loads, every system is listening. If you add a system, call its start() above PlayerData.start().

Subfolders:

  • server/data/: PlayerData.ts (load, save, session lock), Schema.ts (saved shape, defaults, migrations), Retry.ts (retries with backoff).
  • server/social/: Group.ts, Friends.ts, Referrals.ts.
  • server/trading/: Trading.ts (sessions and the commit), Rules.ts, TradeLog.ts, History.ts, Switch.ts.
  • server/demo/: builds the demo hub (see The demo world).

server/CodesConfig.ts holds the promo codes. It's on the server so players can't read the list.

src/client

  • Main.client.ts mounts the UI, starts audio and chat tags, and opens the daily reward window once data loads.
  • State.ts turns player attributes into Fusion values and holds the window manager (State.panels).
  • Theme.ts has every color, font, size and timing the UI uses.
  • ui/kit/ is the component kit: buttons, windows, cards, bars, tabs, toasts, tooltips.
  • ui/*.ts is one file per window (Shop, Eggs, Quests, SeasonPass and so on), plus Hud.ts, which builds the HUD and mounts every window.
  • Marketplace.ts, Audio.ts, Onboarding.ts, ChatTags.ts, Claimable.ts, Synced.ts and Inventory.ts are client helpers.
  • demo/: client side of the demo hub (pickup text, prompts, pets, boards).

The demo world

DEMO.enabled in Config.ts (true by default) makes the server build a small hub with a stand for each system. The code is in src/server/demo/, src/client/demo/ and src/shared/demo/, and Main.server.ts and Main.client.ts start it on lines marked // demo. Nothing else depends on it. The demo has one remote of its own, in shared/demo/Remotes.ts; it's the only remote outside shared/Remotes.ts.

To remove it for your own game, delete those three folders, the DEMO block at the end of Config.ts, and the // demo lines in the two Main files. To keep the code but not build the hub, set DEMO.enabled to false.

How state reaches the UI

The server owns all player data. When a player joins, PlayerData loads their save and runs every onLoad handler. Those handlers set attributes on the Player, using the names in shared/Attributes.ts:

export const Attr = {
	/** True once the player's saved data has loaded. */
	Loaded: "DataLoaded",
	/** The coins balance. Same as currencyAttr("coins"). */
	Coins: "Currency_coins",
	DailyStreak: "DailyStreak",
	// ...

Attributes replicate to the client on their own. client/State.ts wraps each one in a Fusion value:

export const loaded = attribute(Attr.Loaded, false);
export const coins = attribute(Attr.Coins, 0);
export const dailyReady = attribute(Attr.DailyReady, false);

So when the server changes a balance, the HUD updates with no extra remote. Larger state that doesn't fit in an attribute (the inventory, quest boards, the index, the season pass) goes over a serverEvent such as InventoryChanged or QuestsChanged, and the client asks for a first copy with a clientFunction such as GetInventory.

The client asks the server to do things through the remotes in shared/Remotes.ts. The server checks every argument and rate-limits every remote before a handler runs (see Remotes).

Game events

server/Events.ts is an in-process event bus for things that happen in the game: currencyEarned, itemAdded, eggOpened, wheelSpun, rebirthed, questClaimed, purchase and others. Systems fire them, and quests, achievements and the index listen. server/Goals.ts turns these events into quest and achievement progress.