Forest Logo
search
package_2

geode

By @qvyra

Roblox

Mirrored from Wally

Geode

A layered data framework for Roblox. Geode puts DataStoreService, MemoryStoreService and MessagingService behind one API, with guards, reactive data and replication on top.

Geode is in early development. The API will change without notice until 1.0.

Quick start

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Geode = require(ReplicatedStorage.Packages.Geode)

local PlayerData = Geode.players({
	name = "PlayerData",
	template = { coins = 0, xp = 0, level = 1, inventory = {} },
	replicate = { coins = "owner", level = "public" },
})

PlayerData:onLoaded(function(player, profile)
	profile:increment("coins", 100)
end)

Each player's data loads when they join and is saved and released when they leave or the server shuts down. Until it loads, PlayerData:get(player) reads the template, and isLoaded, waitFor and onLoaded tell you when the real data is in. In Studio, data stays in memory unless you ask for a separate scope or live data. PlayerData.store is the profile store underneath, with every option visible, for when you need more control.

On the client, the replicated paths arrive as atoms:

local client = Geode.Client.start()
client:atom("coins"):subscribe(function(coins)
	print("coins", coins)
end)

Install

With Wally, add Geode to wally.toml and run wally install:

[dependencies]
Geode = "qvyra/geode@0.4.0"

A roblox-ts package is coming. The typings for the whole API already live in types/.

The idea

Polish       Geode.players and Geode.setup: player data with the lifecycle handled
Stores       Profile, Shared, Ephemeral, Ranked, Matchmaker, plus premades
Primitives   scheduler, schema, transactions, leases, guards, bundles, reactions
Adapters     DataStore, MemoryStore, MessagingService, remotes, plus mocks

Each layer only uses the public API of the one below it. Start at the top and drop down a level whenever you need more control.

DataStore is always the source of truth. MemoryStore and MessagingService only make things faster or help servers coordinate, and correctness never depends on them. Every write from every source goes through one transaction pipeline, so guards, reactions, replication and history all see the same changes.

What's inside

  • Profiles: session locked player data with autosave, fast server hops, migrations, receipts that never grant twice, a mailbox for offline players and erasure on request
  • Shared stores: data any server writes, with the whole pipeline run inside UpdateAsync
  • MemoryStore: validated hash maps, sorted maps and queues, plus presence, locks with fencing tokens, counters, rate limits and job queues
  • Matchmaking and leaderboards: one elected matcher per queue, all-time and windowed boards
  • Replication: per path visibility, diffs batched once per frame, client atoms and rate limited remote ops
  • History and tooling: version history, rollback through the server holding a profile, an audit log and a telemetry bus

The guides and the API reference are at qvyra.github.io/geode. DECISIONS.md records why things are built the way they are, and example/ holds a small game that uses most of it.

Development

You need Rokit and Node.js.

rokit install
npm ci
lune run check

lune run check runs formatting, linting, type checking and tests, the same as CI. lune run fix applies the formatters. lune run test <filter> runs only the spec files whose path contains the filter. See CONTRIBUTING.md before opening a pull request.

License

MIT, see LICENSE.

Package Details

Install command (Click to copy)


Version

0.4.0

License

MIT

check_circle

Safe for commercial use

Automated license review — not legal advice.