Skip to content

Commit 93ea98f

Browse files
Merge branch 'master' into jlarabie/money-exchange
2 parents d0bd83a + b6b072e commit 93ea98f

55 files changed

Lines changed: 3961 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export default tseslint.config(
5252
'./templates/react-ts/tsconfig.json',
5353
'./templates/chat-react-ts/tsconfig.json',
5454
'./templates/money-exchange-react-ts/tsconfig.json',
55+
'./templates/hangman-react-ts/tsconfig.json',
5556
'./templates/basic-ts/tsconfig.json',
5657
'./templates/angular-ts/tsconfig.app.json',
5758
'./docs/tsconfig.json',

pnpm-lock.yaml

Lines changed: 77 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ packages:
44
- 'crates/bindings-typescript/case-conversion-test-client'
55
- 'templates/chat-react-ts'
66
- 'templates/money-exchange-react-ts'
7+
- 'templates/hangman-react-ts'
78
- 'templates/react-ts'
89
- 'templates/basic-ts'
10+
- 'templates/llm-chat-ts'
911
- 'templates/vue-ts'
1012
- 'templates/tanstack-ts'
1113
- 'templates/browser-ts'
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules
2+
dist
3+
*.log
4+
5+
.DS_Store
6+
7+
spacetime.local.json
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"description": "Competitive Hangman game with React and TypeScript server",
3+
"client_framework": "React",
4+
"client_lang": "typescript",
5+
"server_lang": "typescript",
6+
"tags": ["Launchpad"],
7+
"builtWith": [
8+
"react",
9+
"react-dom",
10+
"eslint",
11+
"vitejs",
12+
"eslint-plugin-react-hooks",
13+
"eslint-plugin-react-refresh",
14+
"globals",
15+
"prettier",
16+
"typescript",
17+
"typescript-eslint",
18+
"vite",
19+
"spacetimedb"
20+
]
21+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
Get a competitive SpacetimeDB Hangman game running with React and TypeScript.
2+
3+
## Prerequisites
4+
5+
- [Node.js](https://nodejs.org/) 18+ installed
6+
- [SpacetimeDB CLI](https://spacetimedb.com/install) installed
7+
8+
Install the [SpacetimeDB CLI](https://spacetimedb.com/install) before continuing.
9+
10+
---
11+
12+
## Create your project
13+
14+
Run the `spacetime dev` command to create a new project with a TypeScript SpacetimeDB module and React client.
15+
16+
This will start the local SpacetimeDB server, publish your module, generate TypeScript bindings, and start the React development server.
17+
18+
```bash
19+
spacetime dev --template hangman-react-ts
20+
```
21+
22+
## Open your app
23+
24+
Navigate to [http://localhost:5173](http://localhost:5173) to play Hangman.
25+
26+
Every player guesses the same hidden word on a private board. A round lasts 60 seconds, followed by 10 seconds of revealed results and rankings.
27+
28+
## Explore the project structure
29+
30+
Your project contains both server and client code.
31+
32+
Edit `spacetimedb/src/index.ts` to change game rules or the word list. Edit `src/App.tsx` to change the game interface.
33+
34+
```
35+
my-spacetime-app/
36+
├── spacetimedb/ # Your SpacetimeDB module
37+
│ └── src/
38+
│ └── index.ts # Game tables, reducers, and round transitions
39+
├── src/ # React frontend
40+
│ ├── App.tsx
41+
│ └── module_bindings/ # Auto-generated types
42+
└── package.json
43+
```
44+
45+
## Understand tables and reducers
46+
47+
Open `spacetimedb/src/index.ts` to see the module code. The template includes public round and result tables, private player progress, and scheduled round transitions. The `set_name` reducer registers a nickname and `guess_letter` submits one letter for the current round.
48+
49+
Tables store game state. Reducers are functions that modify data - they are the only way to write to the database.
50+
51+
```typescript
52+
export const set_name = spacetimedb.reducer(
53+
{ name: t.string() },
54+
(ctx, { name }) => {
55+
const trimmedName = name.trim();
56+
if (trimmedName.length === 0 || trimmedName.length > 20) {
57+
throw new SenderError('Names must be between 1 and 20 characters');
58+
}
59+
60+
const existing = ctx.db.player.identity.find(ctx.sender);
61+
if (existing) {
62+
ctx.db.player.identity.update({ ...existing, name: trimmedName });
63+
} else {
64+
ctx.db.player.insert({ identity: ctx.sender, name: trimmedName });
65+
}
66+
}
67+
);
68+
69+
export const guess_letter = spacetimedb.reducer(
70+
{ letter: t.string() },
71+
(ctx, { letter }) => {
72+
const guess = letter.trim().toUpperCase();
73+
if (!/^[A-Z]$/.test(guess)) {
74+
throw new SenderError('Guess one letter from A to Z');
75+
}
76+
77+
// Update this player's private progress for the active round.
78+
}
79+
);
80+
```
81+
82+
## Test with the CLI
83+
84+
Open a new terminal and navigate to your project directory. Then use the SpacetimeDB CLI to join a round, guess letters, and inspect your state.
85+
86+
```bash
87+
cd my-spacetime-app
88+
89+
# Pick a nickname before guessing
90+
spacetime call set_name '"Ada"'
91+
92+
# Guess one letter
93+
spacetime call guess_letter '"A"'
94+
95+
# Inspect the active public round and your private board view
96+
spacetime sql "SELECT * FROM current_round"
97+
spacetime sql "SELECT * FROM my_progress"
98+
```
99+
100+
## Understand round state and privacy
101+
102+
The module runs one shared competitive round at a time:
103+
104+
- `current_round` publishes the timer, difficulty, and word length. It reveals the answer only during results.
105+
- `my_progress` exposes each player only to their own masked word and guesses during an active round.
106+
- `round_result` publishes the completed round standings once the timer ends.
107+
- `transition_timer` schedules the results phase and the start of the next round.
108+
109+
## Customize the game
110+
111+
The built-in word list and round durations are at the top of `spacetimedb/src/index.ts`. Add words, adjust difficulty labels, or change the active and results durations there.
112+
113+
The React UI in `src/App.tsx` includes the gallows drawing, masked-word board, keyboard, timer, nickname form, and standings panel. Edit those components and `src/App.css` to change how the game looks and plays.
114+
115+
## Next steps
116+
117+
- Read the [TypeScript SDK Reference](https://spacetimedb.com/docs/intro/core-concepts/clients/typescript-reference) for detailed API docs
118+
- See the [Chat App Tutorial](https://spacetimedb.com/docs/intro/tutorials/chat-app) for another complete React example
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>SpacetimeDB Hangman</title>
7+
</head>
8+
<body>
9+
<div id="root"></div>
10+
<script type="module" src="/src/main.tsx"></script>
11+
</body>
12+
</html>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "@clockworklabs/hangman-react-ts",
3+
"private": true,
4+
"version": "0.0.1",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "tsc -b && vite build",
9+
"format": "prettier . --write --ignore-path ../../.prettierignore",
10+
"lint": "eslint . && prettier . --check --ignore-path ../../.prettierignore",
11+
"preview": "vite preview",
12+
"generate": "cargo run -p gen-bindings -- --out-dir src/module_bindings --module-path spacetimedb && prettier --write src/module_bindings",
13+
"spacetime:generate": "spacetime generate --lang typescript --out-dir src/module_bindings --module-path spacetimedb",
14+
"spacetime:publish:local": "spacetime publish --module-path spacetimedb --server local",
15+
"spacetime:publish": "spacetime publish --module-path spacetimedb --server maincloud"
16+
},
17+
"dependencies": {
18+
"spacetimedb": "workspace:*",
19+
"react": "^18.3.1",
20+
"react-dom": "^18.3.1"
21+
},
22+
"devDependencies": {
23+
"@eslint/js": "^9.17.0",
24+
"@types/react": "^18.3.18",
25+
"@types/react-dom": "^18.3.5",
26+
"@vitejs/plugin-react": "^5.0.2",
27+
"eslint": "^9.17.0",
28+
"eslint-plugin-react-hooks": "^5.0.0",
29+
"eslint-plugin-react-refresh": "^0.4.16",
30+
"globals": "^15.14.0",
31+
"prettier": "^3.3.3",
32+
"typescript": "~5.6.2",
33+
"typescript-eslint": "^8.18.2",
34+
"vite": "^7.1.5"
35+
}
36+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "spacetime-module",
3+
"version": "1.0.0",
4+
"description": "",
5+
"scripts": {
6+
"build": "spacetime build",
7+
"publish": "spacetime publish"
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"spacetimedb": "workspace:*"
14+
},
15+
"devDependencies": {
16+
"typescript": "~5.6.2"
17+
}
18+
}

0 commit comments

Comments
 (0)