create-airjam 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/index.js +11 -3
  2. package/package.json +6 -3
  3. package/templates/pong/.env.example +11 -0
  4. package/templates/pong/.env.local +10 -0
  5. package/templates/pong/AI_INSTRUCTIONS.md +44 -0
  6. package/templates/pong/README.md +111 -0
  7. package/templates/pong/airjam-docs/getting-started/architecture/page.md +165 -0
  8. package/templates/pong/airjam-docs/getting-started/game-ideas/page.md +114 -0
  9. package/templates/pong/airjam-docs/getting-started/introduction/page.md +122 -0
  10. package/templates/pong/airjam-docs/how-it-works/host-system/page.md +241 -0
  11. package/templates/pong/airjam-docs/sdk/hooks/page.md +403 -0
  12. package/templates/pong/airjam-docs/sdk/input-system/page.md +336 -0
  13. package/templates/pong/airjam-docs/sdk/networked-state/page.md +575 -0
  14. package/templates/pong/dist/assets/index-B9l0NKly.js +269 -0
  15. package/templates/pong/dist/assets/index-CHKqdIQG.css +1 -0
  16. package/templates/pong/dist/index.html +14 -0
  17. package/templates/pong/eslint.config.js +33 -0
  18. package/templates/pong/index.html +6 -1
  19. package/templates/pong/node_modules/.bin/air-jam-server +17 -0
  20. package/templates/pong/node_modules/.bin/eslint +17 -0
  21. package/templates/pong/node_modules/.bin/eslint-config-prettier +17 -0
  22. package/templates/pong/node_modules/.bin/jiti +17 -0
  23. package/templates/pong/node_modules/.bin/tsc +17 -0
  24. package/templates/pong/node_modules/.bin/tsserver +17 -0
  25. package/templates/pong/node_modules/.bin/tsx +17 -0
  26. package/templates/pong/node_modules/.bin/vite +17 -0
  27. package/templates/pong/node_modules/.vite/deps/@air-jam_sdk.js +66143 -0
  28. package/templates/pong/node_modules/.vite/deps/@air-jam_sdk.js.map +7 -0
  29. package/templates/pong/node_modules/.vite/deps/_metadata.json +73 -0
  30. package/templates/pong/node_modules/.vite/deps/chunk-3TUQC5ZT.js +292 -0
  31. package/templates/pong/node_modules/.vite/deps/chunk-3TUQC5ZT.js.map +7 -0
  32. package/templates/pong/node_modules/.vite/deps/chunk-DC5AMYBS.js +38 -0
  33. package/templates/pong/node_modules/.vite/deps/chunk-DC5AMYBS.js.map +7 -0
  34. package/templates/pong/node_modules/.vite/deps/chunk-QUPSG5AV.js +280 -0
  35. package/templates/pong/node_modules/.vite/deps/chunk-QUPSG5AV.js.map +7 -0
  36. package/templates/pong/node_modules/.vite/deps/chunk-TYOCAO5S.js +13810 -0
  37. package/templates/pong/node_modules/.vite/deps/chunk-TYOCAO5S.js.map +7 -0
  38. package/templates/pong/node_modules/.vite/deps/chunk-YG4BJP3V.js +1004 -0
  39. package/templates/pong/node_modules/.vite/deps/chunk-YG4BJP3V.js.map +7 -0
  40. package/templates/pong/node_modules/.vite/deps/package.json +3 -0
  41. package/templates/pong/node_modules/.vite/deps/react-dom.js +6 -0
  42. package/templates/pong/node_modules/.vite/deps/react-dom.js.map +7 -0
  43. package/templates/pong/node_modules/.vite/deps/react-dom_client.js +20217 -0
  44. package/templates/pong/node_modules/.vite/deps/react-dom_client.js.map +7 -0
  45. package/templates/pong/node_modules/.vite/deps/react-router-dom.js +13900 -0
  46. package/templates/pong/node_modules/.vite/deps/react-router-dom.js.map +7 -0
  47. package/templates/pong/node_modules/.vite/deps/react.js +5 -0
  48. package/templates/pong/node_modules/.vite/deps/react.js.map +7 -0
  49. package/templates/pong/node_modules/.vite/deps/react_jsx-dev-runtime.js +278 -0
  50. package/templates/pong/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
  51. package/templates/pong/node_modules/.vite/deps/react_jsx-runtime.js +6 -0
  52. package/templates/pong/node_modules/.vite/deps/react_jsx-runtime.js.map +7 -0
  53. package/templates/pong/node_modules/.vite/deps/zod.js +476 -0
  54. package/templates/pong/node_modules/.vite/deps/zod.js.map +7 -0
  55. package/templates/pong/package.json +12 -1
  56. package/templates/pong/src/App.tsx +2 -2
  57. package/templates/pong/src/controller-view.tsx +143 -0
  58. package/templates/pong/src/host-view.tsx +401 -0
  59. package/templates/pong/src/main.tsx +2 -1
  60. package/templates/pong/src/store.ts +80 -0
  61. package/templates/pong/tsconfig.json +3 -2
  62. package/templates/pong/vite.config.ts +3 -0
  63. package/templates/pong/src/ControllerView.tsx +0 -64
  64. package/templates/pong/src/HostView.tsx +0 -148
@@ -0,0 +1,336 @@
1
+ # Input System
2
+
3
+ The Air Jam input system provides type-safe, validated, and latched input handling. This page explains how input flows from controllers to your game and how to configure it for optimal performance.
4
+
5
+ ## Input Flow
6
+
7
+ ## Configuration
8
+
9
+ Configure input handling in the `AirJamProvider`:
10
+
11
+ ```tsx filename="src/App.tsx"
12
+ import { AirJamProvider } from "@air-jam/sdk";
13
+ import { z } from "zod";
14
+
15
+ // Define your input schema with Zod
16
+ const gameInputSchema = z.object({
17
+ vector: z.object({
18
+ x: z.number(),
19
+ y: z.number(),
20
+ }),
21
+ action: z.boolean(),
22
+ ability: z.boolean(),
23
+ timestamp: z.number(),
24
+ });
25
+
26
+ // Type is automatically inferred
27
+ type GameInput = z.infer<typeof gameInputSchema>;
28
+
29
+ <AirJamProvider
30
+ input={{
31
+ // Schema for validation and type inference
32
+ schema: gameInputSchema,
33
+
34
+ // Latching configuration
35
+ latch: {
36
+ booleanFields: ["action", "ability"],
37
+ vectorFields: ["vector"],
38
+ },
39
+ }}
40
+ >
41
+ <App />
42
+ </AirJamProvider>;
43
+ ```
44
+
45
+ ## Schema Validation
46
+
47
+ When you provide a Zod schema, all incoming input is validated:
48
+
49
+ ```tsx
50
+ // ✅ Valid input - passes through
51
+ {
52
+ vector: { x: 0.5, y: -0.3 },
53
+ action: true,
54
+ ability: false,
55
+ timestamp: 1703123456789
56
+ }
57
+
58
+ // ❌ Invalid input - returns undefined, logs warning
59
+ {
60
+ vector: { x: "not a number", y: 0 }, // Type mismatch
61
+ action: true,
62
+ // missing required fields
63
+ }
64
+ ```
65
+
66
+ **Benefits of Schema Validation:**
67
+
68
+ - Catch malformed input early
69
+ - TypeScript knows exact input shape
70
+ - Protect game logic from invalid data
71
+ - Helpful development warnings
72
+
73
+ ## Input Latching
74
+
75
+ ### The Problem
76
+
77
+ Game loops typically run at 60fps, but network events arrive asynchronously. Without latching, quick button taps can be missed:
78
+
79
+ ```
80
+ Frame 1: getInput() → action: false
81
+ [Network: action: true arrives]
82
+ [Network: action: false arrives]
83
+ Frame 2: getInput() → action: false ← Tap missed!
84
+ ```
85
+
86
+ ### The Solution
87
+
88
+ Latching "holds" true values until they're consumed:
89
+
90
+ ```
91
+ Frame 1: getInput() → action: false
92
+ [Network: action: true arrives]
93
+ [Network: action: false arrives]
94
+ Frame 2: getInput() → action: true ← Tap captured!
95
+ Frame 3: getInput() → action: false ← Auto-reset
96
+ ```
97
+
98
+ ### Boolean Latching
99
+
100
+ For buttons and triggers:
101
+
102
+ ```tsx filename="src/App.tsx"
103
+ latch: {
104
+ booleanFields: ["action", "ability", "jump", "fire"],
105
+ }
106
+ ```
107
+
108
+ **Behavior:**
109
+
110
+ - When field becomes `true`, it stays `true` until consumed
111
+ - After `getInput()` reads it, it resets to actual current value
112
+ - Rapid taps (even between frames) are never missed
113
+
114
+ ### Vector Latching
115
+
116
+ For joysticks and directional inputs:
117
+
118
+ ```tsx filename="src/App.tsx"
119
+ latch: {
120
+ vectorFields: ["vector", "aim", "movement"],
121
+ }
122
+ ```
123
+
124
+ **Behavior:**
125
+
126
+ - Non-zero vectors are kept for one frame after release
127
+ - Quick stick flicks register in the game loop
128
+ - Prevents "dead zone" issues with fast movements
129
+
130
+ ### Example: Without vs With Latching
131
+
132
+ **Without latching (missed inputs):**
133
+
134
+ ```
135
+ Player rapidly taps fire button
136
+ Frame 1: action: false (tap happened between frames)
137
+ Frame 2: action: false (another tap missed)
138
+ Frame 3: action: true (finally caught one)
139
+ Result: Player fires once instead of 3 times
140
+ ```
141
+
142
+ **With latching (all inputs captured):**
143
+
144
+ ```
145
+ Player rapidly taps fire button
146
+ Frame 1: action: true (first tap latched)
147
+ Frame 2: action: true (second tap latched)
148
+ Frame 3: action: true (third tap latched)
149
+ Result: Player fires 3 times as expected
150
+ ```
151
+
152
+ ## Reading Input
153
+
154
+ ### In the Main Host Component
155
+
156
+ ```tsx filename="src/components/HostView.tsx"
157
+ const host = useAirJamHost();
158
+
159
+ useFrame(() => {
160
+ host.players.forEach((player) => {
161
+ const input = host.getInput(player.id);
162
+ if (!input) return;
163
+
164
+ // Input is fully typed!
165
+ movePlayer(player.id, input.vector);
166
+
167
+ if (input.action) {
168
+ playerShoot(player.id);
169
+ }
170
+ });
171
+ });
172
+ ```
173
+
174
+ ### In Performance-Critical Components
175
+
176
+ Use `useGetInput()` to avoid re-renders:
177
+
178
+ ```tsx filename="src/components/Ship.tsx"
179
+ import { useGetInput } from "@air-jam/sdk";
180
+
181
+ const Ship = ({ playerId }: { playerId: string }) => {
182
+ // No store subscription = no re-renders
183
+ const getInput = useGetInput<typeof gameInputSchema>();
184
+
185
+ useFrame(() => {
186
+ const input = getInput(playerId);
187
+ // Update ship...
188
+ });
189
+
190
+ return <mesh>...</mesh>;
191
+ };
192
+ ```
193
+
194
+ ## Best Practices
195
+
196
+ ### 1. Define Schema Once
197
+
198
+ Create your schema in a shared file:
199
+
200
+ ```tsx filename="src/game/types.ts"
201
+ import { z } from "zod";
202
+
203
+ export const gameInputSchema = z.object({
204
+ vector: z.object({ x: z.number(), y: z.number() }),
205
+ action: z.boolean(),
206
+ ability: z.boolean(),
207
+ timestamp: z.number(),
208
+ });
209
+
210
+ export type GameInput = z.infer<typeof gameInputSchema>;
211
+ ```
212
+
213
+ ### 2. Latch All Interactive Fields
214
+
215
+ Always latch buttons and sticks:
216
+
217
+ ```tsx filename="src/App.tsx"
218
+ latch: {
219
+ booleanFields: ["action", "ability", "jump", "fire", "menu"],
220
+ vectorFields: ["vector", "aim"],
221
+ }
222
+ ```
223
+
224
+ ### 3. Use Lightweight Hooks in Game Objects
225
+
226
+ ```tsx filename="src/components/Ship.tsx"
227
+ // ❌ Don't do this (causes re-renders)
228
+ const Ship = () => {
229
+ const host = useAirJamHost(); // Re-renders on connection changes
230
+ // ...
231
+ };
232
+
233
+ // ✅ Do this instead
234
+ const Ship = () => {
235
+ const getInput = useGetInput(); // Stable, no re-renders
236
+ // ...
237
+ };
238
+ ```
239
+
240
+ ### 4. Handle Missing Input Gracefully
241
+
242
+ ```tsx filename="src/components/Player.tsx"
243
+ const input = getInput(playerId);
244
+
245
+ // Always check if input exists
246
+ if (!input) {
247
+ // Player just connected, input not yet received
248
+ return;
249
+ }
250
+
251
+ // Now safely use input
252
+ movePlayer(input.vector);
253
+ ```
254
+
255
+ ### 5. Include Timestamps for Advanced Physics
256
+
257
+ ```tsx filename="src/game/types.ts"
258
+ const inputSchema = z.object({
259
+ // ...
260
+ timestamp: z.number(), // Client-side timestamp
261
+ });
262
+
263
+ // Use for input prediction, lag compensation, etc.
264
+ const inputAge = Date.now() - input.timestamp;
265
+ ```
266
+
267
+ ## Debugging Input
268
+
269
+ ### Console Logging
270
+
271
+ ```tsx filename="src/components/DebugView.tsx"
272
+ useFrame(() => {
273
+ const input = getInput(playerId);
274
+ console.log(`Player ${playerId}:`, input);
275
+ });
276
+ ```
277
+
278
+ ### Visual Debug
279
+
280
+ ```tsx filename="src/components/InputDebugger.tsx"
281
+ const InputDebugger = ({ playerId }: { playerId: string }) => {
282
+ const getInput = useGetInput();
283
+ const [display, setDisplay] = useState("");
284
+
285
+ useEffect(() => {
286
+ const interval = setInterval(() => {
287
+ const input = getInput(playerId);
288
+ setDisplay(JSON.stringify(input, null, 2));
289
+ }, 100);
290
+ return () => clearInterval(interval);
291
+ }, [playerId, getInput]);
292
+
293
+ return <pre style={{ position: "fixed", top: 0, left: 0 }}>{display}</pre>;
294
+ };
295
+ ```
296
+
297
+ ## Common Issues
298
+
299
+ ### Input Not Received
300
+
301
+ 1. **Check connection status:**
302
+
303
+ ```tsx filename="src/components/HostView.tsx"
304
+ console.log(host.connectionStatus); // Should be "connected"
305
+ ```
306
+
307
+ 2. **Verify room code matches:**
308
+
309
+ ```tsx filename="src/components/HostView.tsx"
310
+ console.log("Host room:", host.roomId);
311
+ // Should match controller's room
312
+ ```
313
+
314
+ 3. **Check schema validation:**
315
+ ```tsx filename="src/components/HostView.tsx"
316
+ // Look for warnings in console:
317
+ // [InputManager] Invalid input for controller XXX: [errors]
318
+ ```
319
+
320
+ ### Input Feels Laggy
321
+
322
+ 1. **Use `useGetInput()` in game objects** (not `useAirJamHost`)
323
+ 2. **Check network latency** (input includes timestamp)
324
+ 3. **Verify latching is configured** for buttons/sticks
325
+
326
+ ### Buttons Missed
327
+
328
+ 1. **Enable boolean latching:**
329
+
330
+ ```tsx filename="src/App.tsx"
331
+ latch: {
332
+ booleanFields: ["action", "ability"],
333
+ }
334
+ ```
335
+
336
+ 2. **Ensure game loop runs consistently** (60fps target)