@rune-kit/rune 2.2.0 → 2.2.1

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 (144) hide show
  1. package/README.md +40 -34
  2. package/compiler/__tests__/pack-split.test.js +145 -0
  3. package/compiler/bin/rune.js +0 -5
  4. package/compiler/doctor.js +42 -0
  5. package/compiler/emitter.js +27 -4
  6. package/compiler/parser.js +41 -3
  7. package/compiler/transformer.js +10 -6
  8. package/compiler/transforms/compliance.js +40 -0
  9. package/extensions/ai-ml/PACK.md +38 -474
  10. package/extensions/ai-ml/skills/ai-agents.md +172 -0
  11. package/extensions/ai-ml/skills/code-sandbox.md +187 -0
  12. package/extensions/ai-ml/skills/deep-research.md +146 -0
  13. package/extensions/ai-ml/skills/embedding-search.md +66 -0
  14. package/extensions/ai-ml/skills/fine-tuning-guide.md +74 -0
  15. package/extensions/ai-ml/skills/llm-architect.md +125 -0
  16. package/extensions/ai-ml/skills/llm-integration.md +64 -0
  17. package/extensions/ai-ml/skills/prompt-patterns.md +72 -0
  18. package/extensions/ai-ml/skills/rag-patterns.md +66 -0
  19. package/extensions/ai-ml/skills/web-extraction.md +114 -0
  20. package/extensions/analytics/PACK.md +19 -484
  21. package/extensions/analytics/skills/ab-testing.md +72 -0
  22. package/extensions/analytics/skills/dashboard-patterns.md +83 -0
  23. package/extensions/analytics/skills/data-validation.md +68 -0
  24. package/extensions/analytics/skills/funnel-analysis.md +81 -0
  25. package/extensions/analytics/skills/sql-patterns.md +57 -0
  26. package/extensions/analytics/skills/statistical-analysis.md +79 -0
  27. package/extensions/analytics/skills/tracking-setup.md +71 -0
  28. package/extensions/backend/PACK.md +44 -618
  29. package/extensions/backend/skills/api-patterns.md +84 -0
  30. package/extensions/backend/skills/async-pipeline.md +193 -0
  31. package/extensions/backend/skills/auth-patterns.md +97 -0
  32. package/extensions/backend/skills/background-jobs.md +133 -0
  33. package/extensions/backend/skills/caching-patterns.md +108 -0
  34. package/extensions/backend/skills/cli-generation.md +133 -0
  35. package/extensions/backend/skills/database-patterns.md +87 -0
  36. package/extensions/backend/skills/middleware-patterns.md +104 -0
  37. package/extensions/chrome-ext/PACK.md +19 -921
  38. package/extensions/chrome-ext/skills/cws-preflight.md +143 -0
  39. package/extensions/chrome-ext/skills/cws-publish.md +104 -0
  40. package/extensions/chrome-ext/skills/ext-ai-integration.md +251 -0
  41. package/extensions/chrome-ext/skills/ext-messaging.md +139 -0
  42. package/extensions/chrome-ext/skills/ext-storage.md +133 -0
  43. package/extensions/chrome-ext/skills/mv3-scaffold.md +164 -0
  44. package/extensions/content/PACK.md +43 -335
  45. package/extensions/content/skills/blog-patterns.md +88 -0
  46. package/extensions/content/skills/cms-integration.md +131 -0
  47. package/extensions/content/skills/content-scoring.md +107 -0
  48. package/extensions/content/skills/i18n.md +83 -0
  49. package/extensions/content/skills/mdx-authoring.md +137 -0
  50. package/extensions/content/skills/reference.md +1014 -0
  51. package/extensions/content/skills/seo-patterns.md +67 -0
  52. package/extensions/content/skills/video-repurpose.md +153 -0
  53. package/extensions/devops/PACK.md +38 -457
  54. package/extensions/devops/skills/chaos-testing.md +67 -0
  55. package/extensions/devops/skills/ci-cd.md +75 -0
  56. package/extensions/devops/skills/docker.md +58 -0
  57. package/extensions/devops/skills/edge-serverless.md +163 -0
  58. package/extensions/devops/skills/infra-as-code.md +158 -0
  59. package/extensions/devops/skills/kubernetes.md +110 -0
  60. package/extensions/devops/skills/monitoring.md +57 -0
  61. package/extensions/devops/skills/server-setup.md +64 -0
  62. package/extensions/devops/skills/ssl-domain.md +42 -0
  63. package/extensions/ecommerce/PACK.md +62 -226
  64. package/extensions/ecommerce/skills/cart-system.md +79 -0
  65. package/extensions/ecommerce/skills/inventory-mgmt.md +102 -0
  66. package/extensions/ecommerce/skills/order-management.md +126 -0
  67. package/extensions/ecommerce/skills/payment-integration.md +472 -0
  68. package/extensions/ecommerce/skills/shopify-dev.md +69 -0
  69. package/extensions/ecommerce/skills/subscription-billing.md +93 -0
  70. package/extensions/ecommerce/skills/tax-compliance.md +117 -0
  71. package/extensions/gamedev/PACK.md +66 -317
  72. package/extensions/gamedev/skills/asset-pipeline.md +74 -0
  73. package/extensions/gamedev/skills/audio-system.md +129 -0
  74. package/extensions/gamedev/skills/camera-system.md +87 -0
  75. package/extensions/gamedev/skills/ecs.md +98 -0
  76. package/extensions/gamedev/skills/game-loops.md +72 -0
  77. package/extensions/gamedev/skills/input-system.md +199 -0
  78. package/extensions/gamedev/skills/multiplayer.md +180 -0
  79. package/extensions/gamedev/skills/particles.md +105 -0
  80. package/extensions/gamedev/skills/physics-engine.md +89 -0
  81. package/extensions/gamedev/skills/scene-management.md +146 -0
  82. package/extensions/gamedev/skills/threejs-patterns.md +90 -0
  83. package/extensions/gamedev/skills/webgl.md +71 -0
  84. package/extensions/mobile/PACK.md +56 -223
  85. package/extensions/mobile/skills/app-store-connect.md +152 -0
  86. package/extensions/mobile/skills/app-store-prep.md +66 -0
  87. package/extensions/mobile/skills/deep-linking.md +109 -0
  88. package/extensions/mobile/skills/flutter.md +60 -0
  89. package/extensions/mobile/skills/ios-build-pipeline.md +142 -0
  90. package/extensions/mobile/skills/native-bridge.md +66 -0
  91. package/extensions/mobile/skills/ota-updates.md +97 -0
  92. package/extensions/mobile/skills/push-notifications.md +111 -0
  93. package/extensions/mobile/skills/react-native.md +82 -0
  94. package/extensions/saas/PACK.md +26 -720
  95. package/extensions/saas/skills/billing-integration.md +121 -0
  96. package/extensions/saas/skills/feature-flags.md +130 -0
  97. package/extensions/saas/skills/multi-tenant.md +103 -0
  98. package/extensions/saas/skills/onboarding-flow.md +139 -0
  99. package/extensions/saas/skills/subscription-flow.md +95 -0
  100. package/extensions/saas/skills/team-management.md +144 -0
  101. package/extensions/security/PACK.md +10 -448
  102. package/extensions/security/skills/api-security.md +140 -0
  103. package/extensions/security/skills/compliance.md +68 -0
  104. package/extensions/security/skills/owasp-audit.md +64 -0
  105. package/extensions/security/skills/pentest-patterns.md +77 -0
  106. package/extensions/security/skills/secret-mgmt.md +65 -0
  107. package/extensions/security/skills/supply-chain.md +65 -0
  108. package/extensions/trading/PACK.md +18 -535
  109. package/extensions/trading/skills/chart-components.md +55 -0
  110. package/extensions/trading/skills/experiment-loop.md +125 -0
  111. package/extensions/trading/skills/fintech-patterns.md +47 -0
  112. package/extensions/trading/skills/indicator-library.md +58 -0
  113. package/extensions/trading/skills/quant-analysis.md +111 -0
  114. package/extensions/trading/skills/realtime-data.md +58 -0
  115. package/extensions/trading/skills/trade-logic.md +104 -0
  116. package/extensions/ui/PACK.md +34 -853
  117. package/extensions/ui/skills/a11y-audit.md +91 -0
  118. package/extensions/ui/skills/animation-patterns.md +106 -0
  119. package/extensions/ui/skills/component-patterns.md +75 -0
  120. package/extensions/ui/skills/design-decision.md +98 -0
  121. package/extensions/ui/skills/design-system.md +68 -0
  122. package/extensions/ui/skills/landing-patterns.md +155 -0
  123. package/extensions/ui/skills/palette-picker.md +162 -0
  124. package/extensions/ui/skills/react-health.md +90 -0
  125. package/extensions/ui/skills/type-system.md +125 -0
  126. package/extensions/ui/skills/web-vitals.md +153 -0
  127. package/extensions/zalo/PACK.md +117 -0
  128. package/extensions/zalo/skills/zalo-oa-mcp.md +317 -0
  129. package/extensions/zalo/skills/zalo-oa-messaging.md +429 -0
  130. package/extensions/zalo/skills/zalo-oa-setup.md +236 -0
  131. package/extensions/zalo/skills/zalo-oa-webhook.md +189 -0
  132. package/extensions/zalo/skills/zalo-personal-messaging.md +194 -0
  133. package/extensions/zalo/skills/zalo-personal-setup.md +153 -0
  134. package/extensions/zalo/skills/zalo-rate-guard.md +219 -0
  135. package/package.json +1 -1
  136. package/skills/brainstorm/SKILL.md +63 -1
  137. package/skills/cook/SKILL.md +84 -5
  138. package/skills/mcp-builder/SKILL.md +48 -1
  139. package/skills/review/SKILL.md +42 -5
  140. package/skills/review-intake/SKILL.md +17 -1
  141. package/skills/skill-router/SKILL.md +89 -7
  142. package/skills/team/SKILL.md +24 -1
  143. package/skills/test/SKILL.md +18 -0
  144. package/skills/verification/SKILL.md +40 -1
@@ -0,0 +1,180 @@
1
+ ---
2
+ name: "multiplayer"
3
+ pack: "@rune/gamedev"
4
+ description: "WebSocket game server and client prediction — authoritative server model, client-side prediction, reconciliation, entity interpolation, and lag compensation."
5
+ model: sonnet
6
+ tools: [Read, Edit, Write, Grep, Glob, Bash]
7
+ ---
8
+
9
+ # multiplayer
10
+
11
+ WebSocket game server and client prediction — authoritative server model, client-side prediction, reconciliation, entity interpolation, and lag compensation.
12
+
13
+ Authoritative server model: server owns game state, clients send inputs only. Never trust client position.
14
+
15
+ #### Workflow
16
+
17
+ **Step 1 — Assess multiplayer architecture**
18
+ Use Grep to find existing WebSocket code: `WebSocket`, `socket.io`, `ws`, `onmessage`, `postMessage`. Determine: is there a server or is this client-only? What is the tick rate?
19
+
20
+ **Step 2 — Implement authoritative server**
21
+ Emit: Node.js WebSocket server with fixed-tick update (20Hz sufficient), input queue per player, server-side physics/movement, state broadcast.
22
+
23
+ **Step 3 — Implement client prediction**
24
+ Emit: local input application before server confirmation, pending input buffer, reconciliation on server update, smooth interpolation for remote entities.
25
+
26
+ **Step 4 — Add lag compensation**
27
+ Emit: snapshot buffer for entity interpolation (render ~100ms behind server), client prediction with rollback on desync.
28
+
29
+ #### WebSocket Game Server Pattern
30
+
31
+ ```typescript
32
+ // Server (Node.js + ws) — authoritative game server
33
+ import { WebSocketServer, WebSocket } from 'ws';
34
+
35
+ interface PlayerInput { seq: number; keys: { up: boolean; down: boolean; left: boolean; right: boolean }; }
36
+ interface PlayerState { id: string; x: number; y: number; vx: number; vy: number; }
37
+
38
+ const wss = new WebSocketServer({ port: 3001 });
39
+ const players = new Map<string, PlayerState>();
40
+ const inputQueues = new Map<string, PlayerInput[]>();
41
+
42
+ wss.on('connection', (ws: WebSocket, req) => {
43
+ const id = crypto.randomUUID();
44
+ players.set(id, { id, x: 0, y: 0, vx: 0, vy: 0 });
45
+ inputQueues.set(id, []);
46
+
47
+ ws.send(JSON.stringify({ type: 'init', id, state: Object.fromEntries(players) }));
48
+ broadcast({ type: 'player_joined', id });
49
+
50
+ ws.on('message', (raw) => {
51
+ const msg = JSON.parse(raw.toString());
52
+ if (msg.type === 'input') {
53
+ inputQueues.get(id)?.push(msg.input);
54
+ }
55
+ });
56
+
57
+ ws.on('close', () => {
58
+ players.delete(id);
59
+ inputQueues.delete(id);
60
+ broadcast({ type: 'player_left', id });
61
+ });
62
+ });
63
+
64
+ // Fixed-tick server update (20Hz is sufficient for authoritative server)
65
+ const TICK_MS = 50;
66
+ setInterval(() => {
67
+ inputQueues.forEach((queue, id) => {
68
+ const player = players.get(id)!;
69
+ const input = queue.shift(); // process one input per tick
70
+ if (input) applyInput(player, input);
71
+ integratePhysics(player);
72
+ });
73
+
74
+ broadcast({ type: 'state_update', tick: Date.now(), players: Object.fromEntries(players) });
75
+ }, TICK_MS);
76
+
77
+ function broadcast(msg: object) {
78
+ const data = JSON.stringify(msg);
79
+ wss.clients.forEach(c => c.readyState === WebSocket.OPEN && c.send(data));
80
+ }
81
+
82
+ function applyInput(p: PlayerState, input: PlayerInput) {
83
+ const speed = 200;
84
+ p.vx = (input.keys.right ? 1 : 0) - (input.keys.left ? 1 : 0);
85
+ p.vy = (input.keys.down ? 1 : 0) - (input.keys.up ? 1 : 0);
86
+ const len = Math.hypot(p.vx, p.vy);
87
+ if (len > 0) { p.vx = (p.vx / len) * speed; p.vy = (p.vy / len) * speed; }
88
+ }
89
+
90
+ function integratePhysics(p: PlayerState) {
91
+ p.x += p.vx * (TICK_MS / 1000);
92
+ p.y += p.vy * (TICK_MS / 1000);
93
+ }
94
+ ```
95
+
96
+ #### Client Prediction & Reconciliation
97
+
98
+ ```typescript
99
+ // Client — predict locally, reconcile on server update
100
+ class NetworkedPlayer {
101
+ private pendingInputs: { seq: number; input: PlayerInput; }[] = [];
102
+ private seq = 0;
103
+ localState: PlayerState;
104
+ serverState: PlayerState;
105
+
106
+ constructor(initial: PlayerState) {
107
+ this.localState = { ...initial };
108
+ this.serverState = { ...initial };
109
+ }
110
+
111
+ sendInput(keys: PlayerInput['keys'], ws: WebSocket) {
112
+ const input: PlayerInput = { seq: ++this.seq, keys };
113
+ this.pendingInputs.push({ seq: this.seq, input });
114
+ ws.send(JSON.stringify({ type: 'input', input }));
115
+
116
+ // Apply immediately (client prediction)
117
+ applyInputToState(this.localState, keys);
118
+ }
119
+
120
+ reconcile(serverUpdate: PlayerState & { lastProcessedSeq: number }) {
121
+ this.serverState = serverUpdate;
122
+
123
+ // Remove acknowledged inputs
124
+ this.pendingInputs = this.pendingInputs.filter(p => p.seq > serverUpdate.lastProcessedSeq);
125
+
126
+ // Reapply unacknowledged inputs on top of server state
127
+ this.localState = { ...serverUpdate };
128
+ for (const { input } of this.pendingInputs) {
129
+ applyInputToState(this.localState, input.keys);
130
+ }
131
+ }
132
+ }
133
+
134
+ function applyInputToState(state: PlayerState, keys: PlayerInput['keys']) {
135
+ const dt = 1 / 60;
136
+ const speed = 200;
137
+ state.x += ((keys.right ? 1 : 0) - (keys.left ? 1 : 0)) * speed * dt;
138
+ state.y += ((keys.down ? 1 : 0) - (keys.up ? 1 : 0)) * speed * dt;
139
+ }
140
+ ```
141
+
142
+ #### Lag Compensation & Entity Interpolation
143
+
144
+ ```typescript
145
+ // Interpolate remote entities between server snapshots (smooth movement, ~100ms behind)
146
+ interface Snapshot { tick: number; timestamp: number; entities: Map<string, PlayerState>; }
147
+
148
+ class EntityInterpolator {
149
+ private buffer: Snapshot[] = [];
150
+ private readonly delay = 100; // ms behind server
151
+
152
+ addSnapshot(snapshot: Snapshot) {
153
+ this.buffer.push(snapshot);
154
+ // Keep only last 1 second of snapshots
155
+ const cutoff = Date.now() - 1000;
156
+ this.buffer = this.buffer.filter(s => s.timestamp > cutoff);
157
+ }
158
+
159
+ getInterpolatedState(entityId: string): PlayerState | null {
160
+ const renderTime = Date.now() - this.delay;
161
+
162
+ // Find the two snapshots bracketing renderTime
163
+ const newer = this.buffer.find(s => s.timestamp >= renderTime);
164
+ const older = this.buffer.slice().reverse().find(s => s.timestamp < renderTime);
165
+
166
+ if (!older || !newer) return newer?.entities.get(entityId) ?? null;
167
+
168
+ const t = (renderTime - older.timestamp) / (newer.timestamp - older.timestamp);
169
+ const a = older.entities.get(entityId);
170
+ const b = newer.entities.get(entityId);
171
+ if (!a || !b) return null;
172
+
173
+ return {
174
+ ...a,
175
+ x: a.x + (b.x - a.x) * t,
176
+ y: a.y + (b.y - a.y) * t,
177
+ };
178
+ }
179
+ }
180
+ ```
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: "particles"
3
+ pack: "@rune/gamedev"
4
+ description: "GPU particle system with WebGL instancing — object-pooled particles, emission presets, and performance-safe rendering for 10k+ particles."
5
+ model: sonnet
6
+ tools: [Read, Edit, Write, Grep, Glob, Bash]
7
+ ---
8
+
9
+ # particles
10
+
11
+ GPU particle system with WebGL instancing — object-pooled particles, emission presets, and performance-safe rendering for 10k+ particles.
12
+
13
+ #### Workflow
14
+
15
+ **Step 1 — Detect particle usage**
16
+ Use Grep to find particle code: `particles`, `emitter`, `ParticleSystem`, `createBufferSource`. Understand current implementation: CPU vs GPU, pooling strategy, and draw call count.
17
+
18
+ **Step 2 — Audit particle performance**
19
+ Check for: new particle objects created on emit (GC pressure), no object pool, drawing each particle as a separate draw call, no life/alpha fade.
20
+
21
+ **Step 3 — Emit particle system**
22
+ Emit: ParticleSystem with pre-allocated pool, update/render separation, emitter presets for common effects (explosion, sparks, smoke).
23
+
24
+ #### GPU Particles with WebGL Instancing
25
+
26
+ ```typescript
27
+ // GPU particle system — update on CPU, render 10k+ particles via instancing
28
+ interface Particle {
29
+ x: number; y: number;
30
+ vx: number; vy: number;
31
+ life: number; maxLife: number;
32
+ size: number;
33
+ r: number; g: number; b: number; a: number;
34
+ }
35
+
36
+ class ParticleSystem {
37
+ private particles: Particle[] = [];
38
+ private pool: Particle[] = [];
39
+ private readonly maxParticles: number;
40
+
41
+ constructor(maxParticles = 5000) {
42
+ this.maxParticles = maxParticles;
43
+ // Pre-allocate pool
44
+ for (let i = 0; i < maxParticles; i++) {
45
+ this.pool.push({ x:0,y:0,vx:0,vy:0,life:0,maxLife:1,size:4,r:1,g:1,b:1,a:1 });
46
+ }
47
+ }
48
+
49
+ emit(x: number, y: number, count: number, config: Partial<Particle> = {}) {
50
+ for (let i = 0; i < count && this.particles.length < this.maxParticles; i++) {
51
+ const p = this.pool.pop() ?? { x:0,y:0,vx:0,vy:0,life:0,maxLife:1,size:4,r:1,g:1,b:1,a:1 };
52
+ const angle = Math.random() * Math.PI * 2;
53
+ const speed = 50 + Math.random() * 150;
54
+ Object.assign(p, {
55
+ x, y,
56
+ vx: Math.cos(angle) * speed,
57
+ vy: Math.sin(angle) * speed,
58
+ life: 1, maxLife: 0.5 + Math.random() * 1.5,
59
+ size: 3 + Math.random() * 5,
60
+ r: 1, g: 0.5, b: 0.1, a: 1,
61
+ ...config,
62
+ });
63
+ this.particles.push(p);
64
+ }
65
+ }
66
+
67
+ update(dt: number) {
68
+ const gravity = 200;
69
+ for (let i = this.particles.length - 1; i >= 0; i--) {
70
+ const p = this.particles[i];
71
+ p.x += p.vx * dt;
72
+ p.y += p.vy * dt;
73
+ p.vy += gravity * dt;
74
+ p.life -= dt / p.maxLife;
75
+ p.a = Math.max(0, p.life);
76
+ if (p.life <= 0) {
77
+ this.pool.push(p);
78
+ this.particles.splice(i, 1);
79
+ }
80
+ }
81
+ }
82
+
83
+ // Render with Canvas 2D (swap with WebGL instancing for >10k particles)
84
+ render(ctx: CanvasRenderingContext2D) {
85
+ for (const p of this.particles) {
86
+ ctx.globalAlpha = p.a;
87
+ ctx.fillStyle = `rgb(${p.r * 255 | 0},${p.g * 255 | 0},${p.b * 255 | 0})`;
88
+ ctx.beginPath();
89
+ ctx.arc(p.x, p.y, p.size * p.life, 0, Math.PI * 2);
90
+ ctx.fill();
91
+ }
92
+ ctx.globalAlpha = 1;
93
+ }
94
+ }
95
+
96
+ // Emitter presets
97
+ const EMITTER_PRESETS = {
98
+ explosion: (x: number, y: number, sys: ParticleSystem) =>
99
+ sys.emit(x, y, 80, { r: 1, g: 0.4, b: 0 }),
100
+ sparks: (x: number, y: number, sys: ParticleSystem) =>
101
+ sys.emit(x, y, 20, { r: 1, g: 0.9, b: 0.2, size: 2, maxLife: 0.8 }),
102
+ smoke: (x: number, y: number, sys: ParticleSystem) =>
103
+ sys.emit(x, y, 10, { r: 0.5, g: 0.5, b: 0.5, size: 12, maxLife: 3, vx: 0, vy: -30 }),
104
+ };
105
+ ```
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: "physics-engine"
3
+ pack: "@rune/gamedev"
4
+ description: "Physics integration — Rapier.js, rigid bodies, constraints, raycasting, collision callbacks, deterministic simulation."
5
+ model: sonnet
6
+ tools: [Read, Edit, Write, Grep, Glob, Bash]
7
+ ---
8
+
9
+ # physics-engine
10
+
11
+ Physics integration — Rapier.js, rigid bodies, constraints, raycasting, collision callbacks, deterministic simulation.
12
+
13
+ #### Workflow
14
+
15
+ **Step 1 — Detect physics setup**
16
+ Use Grep to find physics libraries: `rapier`, `cannon`, `ammo`, `@dimforge/rapier3d`, `RigidBody`, `Collider`. Read physics initialization and body creation to understand: engine choice, world configuration, and collision handling.
17
+
18
+ **Step 2 — Audit physics configuration**
19
+ Check for: physics step tied to render frame (non-deterministic), missing collision groups (everything collides with everything), no sleep threshold (wasted CPU on static objects), raycasts without max distance (expensive), and missing body cleanup on entity destroy.
20
+
21
+ **Step 3 — Emit optimized physics**
22
+ Emit: Rapier.js (WASM, deterministic) setup with proper collision groups, sleep thresholds, event-driven collision callbacks, and raycasting utility.
23
+
24
+ #### Example
25
+
26
+ ```typescript
27
+ // Rapier.js (WASM) — setup with collision groups and raycasting
28
+ import RAPIER from '@dimforge/rapier3d-compat';
29
+
30
+ await RAPIER.init();
31
+ const world = new RAPIER.World({ x: 0, y: -9.81, z: 0 });
32
+
33
+ // Collision groups: player=0x0001, enemy=0x0002, ground=0x0004, projectile=0x0008
34
+ const GROUPS = { PLAYER: 0x0001, ENEMY: 0x0002, GROUND: 0x0004, PROJECTILE: 0x0008 };
35
+
36
+ // Ground — static, collides with everything
37
+ const groundBody = world.createRigidBody(RAPIER.RigidBodyDesc.fixed().setTranslation(0, 0, 0));
38
+ world.createCollider(
39
+ RAPIER.ColliderDesc.cuboid(50, 0.1, 50)
40
+ .setCollisionGroups((GROUPS.GROUND << 16) | 0xFFFF),
41
+ groundBody,
42
+ );
43
+
44
+ // Player — dynamic, collides with ground + enemy (not own projectiles)
45
+ const playerBody = world.createRigidBody(RAPIER.RigidBodyDesc.dynamic().setTranslation(0, 5, 0));
46
+ world.createCollider(
47
+ RAPIER.ColliderDesc.capsule(0.5, 0.3)
48
+ .setCollisionGroups((GROUPS.PLAYER << 16) | (GROUPS.GROUND | GROUPS.ENEMY))
49
+ .setActiveEvents(RAPIER.ActiveEvents.COLLISION_EVENTS),
50
+ playerBody,
51
+ );
52
+
53
+ // Raycast utility
54
+ function raycast(origin: RAPIER.Vector3, direction: RAPIER.Vector3, maxDist = 100) {
55
+ const ray = new RAPIER.Ray(origin, direction);
56
+ const hit = world.castRay(ray, maxDist, true);
57
+ if (hit) {
58
+ const point = ray.pointAt(hit.timeOfImpact);
59
+ return { point, normal: hit.normal, collider: hit.collider };
60
+ }
61
+ return null;
62
+ }
63
+ ```
64
+
65
+ #### Collision Event Handling
66
+
67
+ ```typescript
68
+ // Event-driven collision callbacks — no polling
69
+ const eventQueue = new RAPIER.EventQueue(true);
70
+
71
+ // In fixed update step:
72
+ world.step(eventQueue);
73
+
74
+ eventQueue.drainCollisionEvents((handle1, handle2, started) => {
75
+ const body1 = world.getRigidBody(world.getCollider(handle1).parent()!);
76
+ const body2 = world.getRigidBody(world.getCollider(handle2).parent()!);
77
+
78
+ const entity1 = entityMap.get(body1.handle);
79
+ const entity2 = entityMap.get(body2.handle);
80
+
81
+ if (started) {
82
+ entity1?.onCollisionEnter(entity2);
83
+ entity2?.onCollisionEnter(entity1);
84
+ } else {
85
+ entity1?.onCollisionExit(entity2);
86
+ entity2?.onCollisionExit(entity1);
87
+ }
88
+ });
89
+ ```
@@ -0,0 +1,146 @@
1
+ ---
2
+ name: "scene-management"
3
+ pack: "@rune/gamedev"
4
+ description: "Scene transitions, preloading, serialization — stack-based SceneManager with fade transitions, asset preloading before enter, and level JSON serialization."
5
+ model: sonnet
6
+ tools: [Read, Edit, Write, Grep, Glob, Bash]
7
+ ---
8
+
9
+ # scene-management
10
+
11
+ Scene transitions, preloading, serialization — stack-based SceneManager with fade transitions, asset preloading before enter, and level JSON serialization.
12
+
13
+ #### Workflow
14
+
15
+ **Step 1 — Detect scene structure**
16
+ Use Grep to find scene code: `Scene`, `GameState`, `StateMachine`, `push`, `pop`, `replace`. Read to understand: how scenes transition, whether assets are preloaded, and how scene data is persisted.
17
+
18
+ **Step 2 — Audit scene management**
19
+ Check for: abrupt scene switches with no transition (jarring UX), assets loaded mid-scene causing stutters, no scene stack (can't return to previous state), missing cleanup on exit.
20
+
21
+ **Step 3 — Emit SceneManager**
22
+ Emit: stack-based SceneManager with fade in/out, asset preloading tied to scene.assets[], push/pop/replace operations, and level serialization/deserialization.
23
+
24
+ #### Scene Stack with Transitions and Preloading
25
+
26
+ ```typescript
27
+ // Scene manager — stack-based, asset preloading, fade transitions
28
+ interface Scene {
29
+ name: string;
30
+ assets: string[]; // asset keys to preload before entering
31
+ onEnter(data?: unknown): void;
32
+ onExit(): void;
33
+ update(dt: number): void;
34
+ render(ctx: CanvasRenderingContext2D): void;
35
+ }
36
+
37
+ class SceneManager {
38
+ private stack: Scene[] = [];
39
+ private loader: AssetLoader;
40
+ private transitioning = false;
41
+ private fadeAlpha = 0;
42
+ private fadeDir: 1 | -1 = 1;
43
+
44
+ constructor(loader: AssetLoader) { this.loader = loader; }
45
+
46
+ get current(): Scene | undefined { return this.stack[this.stack.length - 1]; }
47
+
48
+ async push(scene: Scene, data?: unknown) {
49
+ if (this.transitioning) return;
50
+ this.transitioning = true;
51
+
52
+ await this.fadeOut();
53
+ await this.preloadScene(scene);
54
+ this.stack.push(scene);
55
+ scene.onEnter(data);
56
+ await this.fadeIn();
57
+
58
+ this.transitioning = false;
59
+ }
60
+
61
+ async pop() {
62
+ if (this.transitioning || this.stack.length <= 1) return;
63
+ this.transitioning = true;
64
+
65
+ await this.fadeOut();
66
+ this.current?.onExit();
67
+ this.stack.pop();
68
+ await this.fadeIn();
69
+
70
+ this.transitioning = false;
71
+ }
72
+
73
+ async replace(scene: Scene, data?: unknown) {
74
+ if (this.transitioning) return;
75
+ this.transitioning = true;
76
+
77
+ await this.fadeOut();
78
+ this.current?.onExit();
79
+ this.stack.pop();
80
+ await this.preloadScene(scene);
81
+ this.stack.push(scene);
82
+ scene.onEnter(data);
83
+ await this.fadeIn();
84
+
85
+ this.transitioning = false;
86
+ }
87
+
88
+ private async preloadScene(scene: Scene) {
89
+ // Preload only assets not already cached
90
+ await Promise.all(scene.assets.map(key => this.loader.load(key, `/assets/${key}`)));
91
+ }
92
+
93
+ renderTransition(ctx: CanvasRenderingContext2D, w: number, h: number) {
94
+ if (this.fadeAlpha > 0) {
95
+ ctx.save();
96
+ ctx.globalAlpha = this.fadeAlpha;
97
+ ctx.fillStyle = '#000';
98
+ ctx.fillRect(0, 0, w, h);
99
+ ctx.restore();
100
+ }
101
+ }
102
+
103
+ private fadeOut(): Promise<void> {
104
+ return new Promise(resolve => {
105
+ this.fadeDir = 1;
106
+ const interval = setInterval(() => {
107
+ this.fadeAlpha = Math.min(1, this.fadeAlpha + 0.05);
108
+ if (this.fadeAlpha >= 1) { clearInterval(interval); resolve(); }
109
+ }, 16);
110
+ });
111
+ }
112
+
113
+ private fadeIn(): Promise<void> {
114
+ return new Promise(resolve => {
115
+ this.fadeDir = -1;
116
+ const interval = setInterval(() => {
117
+ this.fadeAlpha = Math.max(0, this.fadeAlpha - 0.05);
118
+ if (this.fadeAlpha <= 0) { clearInterval(interval); resolve(); }
119
+ }, 16);
120
+ });
121
+ }
122
+ }
123
+
124
+ // Level serialization — save/load level state as JSON
125
+ interface LevelData {
126
+ name: string;
127
+ entities: Array<{ type: string; x: number; y: number; props: Record<string, unknown> }>;
128
+ tilemap: number[][];
129
+ }
130
+
131
+ function serializeLevel(world: World): LevelData {
132
+ const entities: LevelData['entities'] = [];
133
+ for (const id of world.query(Position)) {
134
+ const pos = world.getComponent(id, Position)!;
135
+ entities.push({ type: 'generic', x: pos.x, y: pos.y, props: {} });
136
+ }
137
+ return { name: 'level1', entities, tilemap: [] };
138
+ }
139
+
140
+ function deserializeLevel(data: LevelData, world: World) {
141
+ for (const e of data.entities) {
142
+ const id = world.createEntity();
143
+ world.addComponent(id, new Position(e.x, e.y));
144
+ }
145
+ }
146
+ ```
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: "threejs-patterns"
3
+ pack: "@rune/gamedev"
4
+ description: "Three.js patterns — scene setup, React Three Fiber integration, PBR materials, post-processing, performance optimization."
5
+ model: sonnet
6
+ tools: [Read, Edit, Write, Grep, Glob, Bash]
7
+ ---
8
+
9
+ # threejs-patterns
10
+
11
+ Three.js patterns — scene setup, React Three Fiber integration, PBR materials, post-processing, performance optimization.
12
+
13
+ #### Workflow
14
+
15
+ **Step 1 — Detect Three.js setup**
16
+ Use Grep to find Three.js usage: `THREE.`, `useThree`, `useFrame`, `Canvas`, `@react-three/fiber`, `@react-three/drei`. Read the main scene file to understand: renderer setup, scene graph structure, camera type, and lighting model.
17
+
18
+ **Step 2 — Audit performance**
19
+ Check for: objects created inside `useFrame` (GC pressure), missing `dispose()` on unmount (memory leak), no frustum culling on large scenes, textures without power-of-two dimensions, unoptimized geometry (too many draw calls), and missing LOD for distant objects.
20
+
21
+ **Step 3 — Emit optimized scene**
22
+ Emit: properly structured R3F scene with declarative lights, memoized geometries, disposal on unmount, instanced meshes for repeated objects, and post-processing pipeline.
23
+
24
+ #### Example
25
+
26
+ ```tsx
27
+ // React Three Fiber — optimized scene with instancing and post-processing
28
+ import { Canvas, useFrame } from '@react-three/fiber';
29
+ import { OrbitControls, Environment, useGLTF, Instances, Instance } from '@react-three/drei';
30
+ import { EffectComposer, Bloom, Vignette } from '@react-three/postprocessing';
31
+ import { useRef, useMemo } from 'react';
32
+
33
+ function InstancedTrees({ count = 500 }) {
34
+ const positions = useMemo(() =>
35
+ Array.from({ length: count }, () => [
36
+ (Math.random() - 0.5) * 100,
37
+ 0,
38
+ (Math.random() - 0.5) * 100,
39
+ ] as [number, number, number]),
40
+ [count]);
41
+
42
+ return (
43
+ <Instances limit={count}>
44
+ <cylinderGeometry args={[0.2, 0.4, 3]} />
45
+ <meshStandardMaterial color="#4a7c59" />
46
+ {positions.map((pos, i) => <Instance key={i} position={pos} />)}
47
+ </Instances>
48
+ );
49
+ }
50
+
51
+ function GameScene() {
52
+ return (
53
+ <Canvas camera={{ position: [0, 10, 20], fov: 60 }} gl={{ antialias: true }}>
54
+ <ambientLight intensity={0.3} />
55
+ <directionalLight position={[10, 20, 10]} intensity={1} castShadow />
56
+ <Environment preset="sunset" />
57
+ <InstancedTrees count={500} />
58
+ <OrbitControls maxPolarAngle={Math.PI / 2.2} />
59
+ <EffectComposer>
60
+ <Bloom intensity={0.3} luminanceThreshold={0.8} />
61
+ <Vignette offset={0.3} darkness={0.6} />
62
+ </EffectComposer>
63
+ </Canvas>
64
+ );
65
+ }
66
+ ```
67
+
68
+ #### LOD (Level of Detail) Pattern
69
+
70
+ ```typescript
71
+ import * as THREE from 'three';
72
+
73
+ // Swap geometry based on camera distance
74
+ function buildLODMesh(
75
+ highGeo: THREE.BufferGeometry,
76
+ midGeo: THREE.BufferGeometry,
77
+ lowGeo: THREE.BufferGeometry,
78
+ material: THREE.Material,
79
+ ): THREE.LOD {
80
+ const lod = new THREE.LOD();
81
+ lod.addLevel(new THREE.Mesh(highGeo, material), 0); // < 20 units
82
+ lod.addLevel(new THREE.Mesh(midGeo, material), 20); // 20–80 units
83
+ lod.addLevel(new THREE.Mesh(lowGeo, material), 80); // > 80 units
84
+ return lod;
85
+ }
86
+
87
+ // In render loop — LOD auto-updates based on camera distance
88
+ // scene.add(buildLODMesh(highGeo, midGeo, lowGeo, mat));
89
+ // lod.update(camera); // call once per frame
90
+ ```
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: "webgl"
3
+ pack: "@rune/gamedev"
4
+ description: "WebGL patterns — shader programming, GLSL, buffer management, texture handling, instanced rendering."
5
+ model: sonnet
6
+ tools: [Read, Edit, Write, Grep, Glob, Bash]
7
+ ---
8
+
9
+ # webgl
10
+
11
+ WebGL patterns — shader programming, GLSL, buffer management, texture handling, instanced rendering.
12
+
13
+ #### Workflow
14
+
15
+ **Step 1 — Detect WebGL usage**
16
+ Use Grep to find WebGL code: `getContext('webgl`, `gl.createShader`, `gl.createProgram`, `*.glsl`, `*.vert`, `*.frag`. Read shader files and GL initialization to understand: WebGL version, shader complexity, and buffer strategy.
17
+
18
+ **Step 2 — Audit shader and buffer efficiency**
19
+ Check for: uniforms set every frame that don't change (use UBO), separate draw calls for identical geometry (use instancing), textures not using mipmaps, missing `gl.deleteBuffer`/`gl.deleteTexture` cleanup, and shaders with expensive per-fragment branching.
20
+
21
+ **Step 3 — Emit optimized WebGL code**
22
+ Emit: WebGL2 setup with proper context attributes, VAO-based buffer management, instanced rendering for repeated geometry, and GLSL shaders with documented inputs/outputs.
23
+
24
+ #### Example
25
+
26
+ ```glsl
27
+ // Vertex shader — instanced rendering with per-instance transform
28
+ #version 300 es
29
+ layout(location = 0) in vec3 aPosition;
30
+ layout(location = 1) in vec3 aNormal;
31
+ layout(location = 2) in mat4 aInstanceMatrix; // per-instance (locations 2-5)
32
+
33
+ uniform mat4 uViewProjection;
34
+
35
+ out vec3 vNormal;
36
+ out vec3 vWorldPos;
37
+
38
+ void main() {
39
+ vec4 worldPos = aInstanceMatrix * vec4(aPosition, 1.0);
40
+ vWorldPos = worldPos.xyz;
41
+ vNormal = mat3(transpose(inverse(aInstanceMatrix))) * aNormal;
42
+ gl_Position = uViewProjection * worldPos;
43
+ }
44
+ ```
45
+
46
+ ```glsl
47
+ // Fragment shader — PBR-lite with single directional light
48
+ #version 300 es
49
+ precision highp float;
50
+
51
+ in vec3 vNormal;
52
+ in vec3 vWorldPos;
53
+ out vec4 fragColor;
54
+
55
+ uniform vec3 uLightDir;
56
+ uniform vec3 uCameraPos;
57
+ uniform vec3 uBaseColor;
58
+
59
+ void main() {
60
+ vec3 N = normalize(vNormal);
61
+ vec3 L = normalize(uLightDir);
62
+ vec3 V = normalize(uCameraPos - vWorldPos);
63
+ vec3 H = normalize(L + V);
64
+
65
+ float diffuse = max(dot(N, L), 0.0);
66
+ float specular = pow(max(dot(N, H), 0.0), 32.0);
67
+ vec3 ambient = uBaseColor * 0.15;
68
+
69
+ fragColor = vec4(ambient + uBaseColor * diffuse + vec3(specular * 0.5), 1.0);
70
+ }
71
+ ```