Skip to content

Commit ab5d550

Browse files
committed
kv docs
ref DRE-1449
1 parent 3e97c1f commit ab5d550

1 file changed

Lines changed: 156 additions & 0 deletions

File tree

docs/guide/kv.mdx

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
sidebar_position: 999
3+
title: Key-Value (KV) Storage
4+
---
5+
6+
# Key-Value Storage
7+
8+
Dreamlab games are stateless by default, when a new instance is created the game is started fresh every time. In order
9+
to persist state between instances you can use the Key-Value (KV) API.
10+
11+
Data stored in Dreamlab KV is scoped to the project, you cannot have cross-project persistent data. KV data is also
12+
scoped per-player, and players (clients) can only read/write to their own scope. The server has its own scope, and is
13+
also able to read/write to any player scopes.
14+
15+
The KV API can be accessed by `this.game.kv` and exposes `get` / `set` / `list` / `delete` / `clear` methods. KV keys
16+
must be of `string` type, and values can be any JSON serializable value.
17+
18+
## KV Debugger
19+
20+
Attach this behavior script to a `UILayer` in the world root to debug the KV data stored in your project.
21+
22+
```tsx title="kv-debugger.tsx"
23+
import { JsonValue, rpc, UIBehavior } from "@dreamlab/engine";
24+
25+
export default class KvDebugger extends UIBehavior {
26+
#player: string = "";
27+
#players = new Set<string>();
28+
#list: Record<string, JsonValue> = {};
29+
30+
@rpc.server()
31+
async getKv(playerId: string) {
32+
if (!this.game.isServer()) throw new Error("rpc not on server");
33+
const kv = this.game.kv;
34+
35+
const [players, list] = await Promise.all([
36+
kv.info.players(),
37+
playerId === "" ? kv.server.list() : kv.player.list(playerId),
38+
]);
39+
40+
return { players: [...players], list };
41+
}
42+
43+
async #update(): Promise<void> {
44+
if (!this.game.isClient()) return;
45+
46+
const { players, list } = await this.getKv(this.#player);
47+
this.#players = new Set(players);
48+
this.#list = list;
49+
50+
this.rerender();
51+
}
52+
53+
onInitialize(): void {
54+
super.onInitialize();
55+
if (!this.game.isClient()) return;
56+
57+
const css = `
58+
div.container {
59+
width: 100%;
60+
height: 100%;
61+
display: flex;
62+
flex-direction: column;
63+
gap: 1rem;
64+
background: #252a36;
65+
padding: 1rem;
66+
67+
& h1 {
68+
margin: 0;
69+
}
70+
71+
& div.selector {
72+
display: flex;
73+
gap: 1rem;
74+
}
75+
76+
& div.data {
77+
flex-grow: 1;
78+
overflow-y: scroll;
79+
padding-bottom: 2rem;
80+
81+
& > span {
82+
font-style: italic;
83+
}
84+
85+
& > div {
86+
display: grid;
87+
grid-template-columns: max-content auto;
88+
gap: 0.4rem 1rem;
89+
90+
& > *:nth-child(1), & > *:nth-child(2) {
91+
font-weight: bold;
92+
}
93+
94+
& code, & pre {
95+
margin: 0;
96+
padding: 0;
97+
font-family: var(--font-mono), monospace;
98+
font-size: 0.9rem;
99+
}
100+
}
101+
}
102+
}
103+
`;
104+
105+
this.setCss(css);
106+
this.#update();
107+
}
108+
109+
render() {
110+
const entries = Object.entries(this.#list);
111+
112+
return (
113+
<div className="container">
114+
<h1>KV Debugger</h1>
115+
116+
<div className="selector">
117+
<label htmlFor="player">Player Selector</label>
118+
<select
119+
id="player"
120+
onChange={(ev) => {
121+
if (!ev.target) return;
122+
if (!(ev.target instanceof HTMLSelectElement)) return;
123+
124+
this.#player = ev.target.value;
125+
this.#update();
126+
}}
127+
>
128+
<option value="">[server]</option>
129+
{[...this.#players].map((player) => (
130+
<option value={player}>{player}</option>
131+
))}
132+
</select>
133+
</div>
134+
135+
<div className="data">
136+
{entries.length === 0 ? (
137+
<span>No data stored.</span>
138+
) : (
139+
<div>
140+
<span>Key</span>
141+
<span>Value</span>
142+
143+
{entries.map(([key, value]) => (
144+
<div style={{ display: "contents" }}>
145+
<code>{key}</code>
146+
<pre>{JSON.stringify(value)}</pre>
147+
</div>
148+
))}
149+
</div>
150+
)}
151+
</div>
152+
</div>
153+
);
154+
}
155+
}
156+
```

0 commit comments

Comments
 (0)