@rune-kit/rune 2.2.0 → 2.2.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.
- package/README.md +72 -40
- package/compiler/__tests__/pack-split.test.js +145 -0
- package/compiler/bin/rune.js +26 -9
- package/compiler/doctor.js +42 -0
- package/compiler/emitter.js +27 -4
- package/compiler/parser.js +41 -3
- package/compiler/transformer.js +10 -6
- package/compiler/transforms/compliance.js +40 -0
- package/docs/ANTIGRAVITY-GAP-ANALYSIS.md +369 -0
- package/docs/ARCHITECTURE.md +332 -0
- package/docs/COMMUNITY-PACKS.md +109 -0
- package/docs/CONTRIBUTING-L4.md +215 -0
- package/docs/CROSS-IDE-ANALYSIS.md +164 -0
- package/docs/EXTENSION-TEMPLATE.md +108 -0
- package/docs/MESH-RULES.md +34 -0
- package/docs/MULTI-PLATFORM.md +804 -0
- package/docs/SKILL-DEPTH-AUDIT.md +191 -0
- package/docs/SKILL-TEMPLATE.md +72 -0
- package/docs/TRADE-MATRIX.md +327 -0
- package/docs/VERSIONING.md +91 -0
- package/docs/VISION.md +263 -0
- package/docs/assets/demo-subtitles.srt +215 -0
- package/docs/assets/end-card.html +276 -0
- package/docs/assets/mesh-diagram.html +654 -0
- package/docs/assets/thumbnail.html +295 -0
- package/docs/guides/cli.md +403 -0
- package/docs/guides/index.html +1346 -0
- package/docs/index.html +674 -0
- package/docs/references/claudekit-analysis.md +414 -0
- package/docs/references/voltagent-analysis.md +189 -0
- package/docs/script.js +277 -0
- package/docs/skills/index.html +832 -0
- package/docs/style.css +583 -0
- package/docs/video-demo-plan.md +172 -0
- package/extensions/ai-ml/PACK.md +38 -474
- package/extensions/ai-ml/skills/ai-agents.md +172 -0
- package/extensions/ai-ml/skills/code-sandbox.md +187 -0
- package/extensions/ai-ml/skills/deep-research.md +146 -0
- package/extensions/ai-ml/skills/embedding-search.md +66 -0
- package/extensions/ai-ml/skills/fine-tuning-guide.md +74 -0
- package/extensions/ai-ml/skills/llm-architect.md +125 -0
- package/extensions/ai-ml/skills/llm-integration.md +64 -0
- package/extensions/ai-ml/skills/prompt-patterns.md +72 -0
- package/extensions/ai-ml/skills/rag-patterns.md +66 -0
- package/extensions/ai-ml/skills/web-extraction.md +114 -0
- package/extensions/analytics/PACK.md +19 -484
- package/extensions/analytics/skills/ab-testing.md +72 -0
- package/extensions/analytics/skills/dashboard-patterns.md +83 -0
- package/extensions/analytics/skills/data-validation.md +68 -0
- package/extensions/analytics/skills/funnel-analysis.md +81 -0
- package/extensions/analytics/skills/sql-patterns.md +57 -0
- package/extensions/analytics/skills/statistical-analysis.md +79 -0
- package/extensions/analytics/skills/tracking-setup.md +71 -0
- package/extensions/backend/PACK.md +44 -618
- package/extensions/backend/skills/api-patterns.md +84 -0
- package/extensions/backend/skills/async-pipeline.md +193 -0
- package/extensions/backend/skills/auth-patterns.md +97 -0
- package/extensions/backend/skills/background-jobs.md +133 -0
- package/extensions/backend/skills/caching-patterns.md +108 -0
- package/extensions/backend/skills/cli-generation.md +133 -0
- package/extensions/backend/skills/database-patterns.md +87 -0
- package/extensions/backend/skills/middleware-patterns.md +104 -0
- package/extensions/chrome-ext/PACK.md +19 -921
- package/extensions/chrome-ext/skills/cws-preflight.md +143 -0
- package/extensions/chrome-ext/skills/cws-publish.md +104 -0
- package/extensions/chrome-ext/skills/ext-ai-integration.md +251 -0
- package/extensions/chrome-ext/skills/ext-messaging.md +139 -0
- package/extensions/chrome-ext/skills/ext-storage.md +133 -0
- package/extensions/chrome-ext/skills/mv3-scaffold.md +164 -0
- package/extensions/content/PACK.md +43 -335
- package/extensions/content/skills/blog-patterns.md +88 -0
- package/extensions/content/skills/cms-integration.md +131 -0
- package/extensions/content/skills/content-scoring.md +107 -0
- package/extensions/content/skills/i18n.md +83 -0
- package/extensions/content/skills/mdx-authoring.md +137 -0
- package/extensions/content/skills/reference.md +1014 -0
- package/extensions/content/skills/seo-patterns.md +67 -0
- package/extensions/content/skills/video-repurpose.md +153 -0
- package/extensions/devops/PACK.md +38 -457
- package/extensions/devops/skills/chaos-testing.md +67 -0
- package/extensions/devops/skills/ci-cd.md +75 -0
- package/extensions/devops/skills/docker.md +58 -0
- package/extensions/devops/skills/edge-serverless.md +163 -0
- package/extensions/devops/skills/infra-as-code.md +158 -0
- package/extensions/devops/skills/kubernetes.md +110 -0
- package/extensions/devops/skills/monitoring.md +57 -0
- package/extensions/devops/skills/server-setup.md +64 -0
- package/extensions/devops/skills/ssl-domain.md +42 -0
- package/extensions/ecommerce/PACK.md +62 -226
- package/extensions/ecommerce/skills/cart-system.md +79 -0
- package/extensions/ecommerce/skills/inventory-mgmt.md +102 -0
- package/extensions/ecommerce/skills/order-management.md +126 -0
- package/extensions/ecommerce/skills/payment-integration.md +472 -0
- package/extensions/ecommerce/skills/shopify-dev.md +69 -0
- package/extensions/ecommerce/skills/subscription-billing.md +93 -0
- package/extensions/ecommerce/skills/tax-compliance.md +117 -0
- package/extensions/gamedev/PACK.md +66 -317
- package/extensions/gamedev/skills/asset-pipeline.md +74 -0
- package/extensions/gamedev/skills/audio-system.md +129 -0
- package/extensions/gamedev/skills/camera-system.md +87 -0
- package/extensions/gamedev/skills/ecs.md +98 -0
- package/extensions/gamedev/skills/game-loops.md +72 -0
- package/extensions/gamedev/skills/input-system.md +199 -0
- package/extensions/gamedev/skills/multiplayer.md +180 -0
- package/extensions/gamedev/skills/particles.md +105 -0
- package/extensions/gamedev/skills/physics-engine.md +89 -0
- package/extensions/gamedev/skills/scene-management.md +146 -0
- package/extensions/gamedev/skills/threejs-patterns.md +90 -0
- package/extensions/gamedev/skills/webgl.md +71 -0
- package/extensions/mobile/PACK.md +56 -223
- package/extensions/mobile/skills/app-store-connect.md +152 -0
- package/extensions/mobile/skills/app-store-prep.md +66 -0
- package/extensions/mobile/skills/deep-linking.md +109 -0
- package/extensions/mobile/skills/flutter.md +60 -0
- package/extensions/mobile/skills/ios-build-pipeline.md +142 -0
- package/extensions/mobile/skills/native-bridge.md +66 -0
- package/extensions/mobile/skills/ota-updates.md +97 -0
- package/extensions/mobile/skills/push-notifications.md +111 -0
- package/extensions/mobile/skills/react-native.md +82 -0
- package/extensions/saas/PACK.md +26 -720
- package/extensions/saas/skills/billing-integration.md +121 -0
- package/extensions/saas/skills/feature-flags.md +130 -0
- package/extensions/saas/skills/multi-tenant.md +103 -0
- package/extensions/saas/skills/onboarding-flow.md +139 -0
- package/extensions/saas/skills/subscription-flow.md +95 -0
- package/extensions/saas/skills/team-management.md +144 -0
- package/extensions/security/PACK.md +10 -448
- package/extensions/security/skills/api-security.md +140 -0
- package/extensions/security/skills/compliance.md +68 -0
- package/extensions/security/skills/owasp-audit.md +64 -0
- package/extensions/security/skills/pentest-patterns.md +77 -0
- package/extensions/security/skills/secret-mgmt.md +65 -0
- package/extensions/security/skills/supply-chain.md +65 -0
- package/extensions/trading/PACK.md +18 -535
- package/extensions/trading/skills/chart-components.md +55 -0
- package/extensions/trading/skills/experiment-loop.md +125 -0
- package/extensions/trading/skills/fintech-patterns.md +47 -0
- package/extensions/trading/skills/indicator-library.md +58 -0
- package/extensions/trading/skills/quant-analysis.md +111 -0
- package/extensions/trading/skills/realtime-data.md +58 -0
- package/extensions/trading/skills/trade-logic.md +104 -0
- package/extensions/ui/PACK.md +36 -853
- package/extensions/ui/skills/a11y-audit.md +91 -0
- package/extensions/ui/skills/animation-patterns.md +106 -0
- package/extensions/ui/skills/component-patterns.md +75 -0
- package/extensions/ui/skills/design-decision.md +108 -0
- package/extensions/ui/skills/design-system.md +68 -0
- package/extensions/ui/skills/landing-patterns.md +155 -0
- package/extensions/ui/skills/palette-picker.md +173 -0
- package/extensions/ui/skills/react-health.md +90 -0
- package/extensions/ui/skills/type-system.md +125 -0
- package/extensions/ui/skills/web-vitals.md +153 -0
- package/extensions/zalo/PACK.md +117 -0
- package/extensions/zalo/skills/zalo-oa-mcp.md +317 -0
- package/extensions/zalo/skills/zalo-oa-messaging.md +429 -0
- package/extensions/zalo/skills/zalo-oa-setup.md +236 -0
- package/extensions/zalo/skills/zalo-oa-webhook.md +189 -0
- package/extensions/zalo/skills/zalo-personal-messaging.md +194 -0
- package/extensions/zalo/skills/zalo-personal-setup.md +153 -0
- package/extensions/zalo/skills/zalo-rate-guard.md +219 -0
- package/hooks/.gitkeep +0 -0
- package/hooks/auto-format/index.cjs +48 -0
- package/hooks/context-watch/index.cjs +68 -0
- package/hooks/hooks.json +99 -0
- package/hooks/metrics-collector/index.cjs +42 -0
- package/hooks/post-session-reflect/index.cjs +153 -0
- package/hooks/pre-compact/index.cjs +95 -0
- package/hooks/pre-tool-guard/index.cjs +68 -0
- package/hooks/run-hook +17 -0
- package/hooks/run-hook.cjs +16 -0
- package/hooks/run-hook.cmd +1 -0
- package/hooks/secrets-scan/index.cjs +100 -0
- package/hooks/session-start/index.cjs +65 -0
- package/hooks/typecheck/index.cjs +65 -0
- package/package.json +9 -4
- package/references/ui-pro-max-data/LICENSE-UI-PRO-MAX +21 -0
- package/references/ui-pro-max-data/charts.csv +26 -0
- package/references/ui-pro-max-data/colors.csv +162 -0
- package/references/ui-pro-max-data/styles.csv +85 -0
- package/references/ui-pro-max-data/typography.csv +74 -0
- package/references/ui-pro-max-data/ui-reasoning.csv +162 -0
- package/references/ui-pro-max-data/ux-guidelines.csv +100 -0
- package/skills/ba/SKILL.md +10 -0
- package/skills/brainstorm/SKILL.md +63 -1
- package/skills/completion-gate/SKILL.md +34 -1
- package/skills/context-engine/SKILL.md +13 -0
- package/skills/cook/SKILL.md +155 -5
- package/skills/debug/SKILL.md +56 -1
- package/skills/design/SKILL.md +11 -0
- package/skills/fix/SKILL.md +26 -1
- package/skills/mcp-builder/SKILL.md +48 -1
- package/skills/plan/SKILL.md +23 -6
- package/skills/review/SKILL.md +44 -5
- package/skills/review-intake/SKILL.md +17 -1
- package/skills/skill-forge/SKILL.md +38 -3
- package/skills/skill-router/SKILL.md +89 -7
- package/skills/team/SKILL.md +24 -1
- package/skills/test/SKILL.md +28 -1
- package/skills/verification/SKILL.md +98 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "tax-compliance"
|
|
3
|
+
pack: "@rune/ecommerce"
|
|
4
|
+
description: "Tax calculation — sales tax API integration, VAT for EU, digital goods tax, tax-inclusive pricing, audit trail."
|
|
5
|
+
model: sonnet
|
|
6
|
+
tools: [Read, Edit, Write, Grep, Glob, Bash]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# tax-compliance
|
|
10
|
+
|
|
11
|
+
Tax calculation — sales tax API integration, VAT for EU, digital goods tax, tax-inclusive pricing, audit trail.
|
|
12
|
+
|
|
13
|
+
#### Workflow
|
|
14
|
+
|
|
15
|
+
**Step 1 — Detect tax setup**
|
|
16
|
+
Use Grep to find: `tax`, `vat`, `taxjar`, `avalara`, `tax_rate`, `taxAmount`, `tax_exempt`. Check if tax calculation exists and where it happens (cart time vs checkout time).
|
|
17
|
+
|
|
18
|
+
**Step 2 — Audit tax accuracy**
|
|
19
|
+
Check for:
|
|
20
|
+
- Tax calculated at cart time but not recalculated at checkout (rate may have changed, or user changed shipping address)
|
|
21
|
+
- Hardcoded tax rates instead of API-based calculation (rates change; nexus rules are complex)
|
|
22
|
+
- Missing tax on digital goods (many US states and all EU countries tax digital products)
|
|
23
|
+
- EU VAT: must charge buyer's country VAT rate for B2C digital sales (not seller's country)
|
|
24
|
+
- Tax-inclusive vs tax-exclusive display: must be consistent and clearly labeled
|
|
25
|
+
- No tax audit trail: amounts, rates, and jurisdiction must be stored per order for compliance
|
|
26
|
+
- Missing tax exemption handling (B2B customers with valid VAT number or tax-exempt certificate)
|
|
27
|
+
|
|
28
|
+
**Step 3 — Emit tax patterns**
|
|
29
|
+
Emit: tax calculation at checkout time (not cart time), API-based rate lookup, EU VAT reverse charge for B2B, and tax audit trail per order line item.
|
|
30
|
+
|
|
31
|
+
#### Example
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// Tax calculation at CHECKOUT time (not cart time) — rates may change
|
|
35
|
+
interface TaxLineItem {
|
|
36
|
+
productId: string;
|
|
37
|
+
amount: number;
|
|
38
|
+
quantity: number;
|
|
39
|
+
taxCode: string; // Product tax code (e.g., 'txcd_10000000' for general goods)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function calculateTax(
|
|
43
|
+
items: TaxLineItem[],
|
|
44
|
+
shippingAddress: Address,
|
|
45
|
+
customerTaxExempt: boolean
|
|
46
|
+
): Promise<TaxResult> {
|
|
47
|
+
if (customerTaxExempt) {
|
|
48
|
+
return { totalTax: 0, lineItems: items.map(i => ({ ...i, tax: 0, rate: 0 })) };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Use tax API — never hardcode rates
|
|
52
|
+
const calculation = await stripe.tax.calculations.create({
|
|
53
|
+
currency: 'usd',
|
|
54
|
+
line_items: items.map(item => ({
|
|
55
|
+
amount: item.amount * item.quantity,
|
|
56
|
+
reference: item.productId,
|
|
57
|
+
tax_code: item.taxCode,
|
|
58
|
+
})),
|
|
59
|
+
customer_details: {
|
|
60
|
+
address: {
|
|
61
|
+
line1: shippingAddress.line1,
|
|
62
|
+
city: shippingAddress.city,
|
|
63
|
+
state: shippingAddress.state,
|
|
64
|
+
postal_code: shippingAddress.postalCode,
|
|
65
|
+
country: shippingAddress.country,
|
|
66
|
+
},
|
|
67
|
+
address_source: 'shipping',
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
totalTax: calculation.tax_amount_exclusive,
|
|
73
|
+
lineItems: calculation.line_items.data.map(li => ({
|
|
74
|
+
productId: li.reference,
|
|
75
|
+
tax: li.amount_tax,
|
|
76
|
+
rate: li.tax_breakdown?.[0]?.rate ?? 0,
|
|
77
|
+
jurisdiction: li.tax_breakdown?.[0]?.jurisdiction?.display_name ?? 'Unknown',
|
|
78
|
+
})),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// EU VAT validation — B2B reverse charge
|
|
83
|
+
async function validateEuVat(vatNumber: string, buyerCountry: string): Promise<boolean> {
|
|
84
|
+
// Use VIES (VAT Information Exchange System) API
|
|
85
|
+
const res = await fetch(
|
|
86
|
+
`https://ec.europa.eu/taxation_customs/vies/rest-api/ms/${buyerCountry}/vat/${vatNumber.replace(/^[A-Z]{2}/, '')}`
|
|
87
|
+
);
|
|
88
|
+
const data = await res.json();
|
|
89
|
+
return data.isValid === true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Store tax audit trail per order (required for compliance)
|
|
93
|
+
interface OrderTaxRecord {
|
|
94
|
+
orderId: string;
|
|
95
|
+
lineItemId: string;
|
|
96
|
+
taxAmount: number;
|
|
97
|
+
taxRate: number;
|
|
98
|
+
jurisdiction: string;
|
|
99
|
+
calculatedAt: Date;
|
|
100
|
+
taxApiTransactionId: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Commit tax record immediately at payment creation — never calculate retroactively
|
|
104
|
+
async function commitTaxRecord(orderId: string, calculation: TaxResult, txnId: string) {
|
|
105
|
+
await prisma.orderTaxRecord.createMany({
|
|
106
|
+
data: calculation.lineItems.map(li => ({
|
|
107
|
+
orderId,
|
|
108
|
+
lineItemId: li.productId,
|
|
109
|
+
taxAmount: li.tax,
|
|
110
|
+
taxRate: li.rate,
|
|
111
|
+
jurisdiction: li.jurisdiction,
|
|
112
|
+
calculatedAt: new Date(),
|
|
113
|
+
taxApiTransactionId: txnId,
|
|
114
|
+
})),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
```
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "@rune/gamedev"
|
|
3
|
-
description: Game development patterns — Three.js, WebGL, game loops, physics engines, and
|
|
3
|
+
description: Game development patterns — Three.js, WebGL, game loops, physics engines, asset pipelines, multiplayer, audio, input, ECS, particles, camera, and scene management.
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.2.0"
|
|
7
7
|
layer: L4
|
|
8
8
|
price: "$12"
|
|
9
9
|
target: Game developers
|
|
10
|
+
format: split
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
# @rune/gamedev
|
|
13
14
|
|
|
14
15
|
## Purpose
|
|
15
16
|
|
|
16
|
-
Web game development hits performance walls that traditional web apps never encounter: 60fps render loops that stutter on garbage collection, physics simulations that diverge between clients, shaders that work on desktop but fail on mobile GPUs, and asset loading that blocks the first frame for 10 seconds. This pack provides patterns for the full web game stack — rendering, simulation, physics, and
|
|
17
|
+
Web game development hits performance walls that traditional web apps never encounter: 60fps render loops that stutter on garbage collection, physics simulations that diverge between clients, shaders that work on desktop but fail on mobile GPUs, and asset loading that blocks the first frame for 10 seconds. This pack provides patterns for the full web game stack — rendering, simulation, physics, assets, multiplayer, audio, input, ECS, particles, camera, and scene management — each optimized for the unique constraints of real-time interactive applications running in a browser.
|
|
17
18
|
|
|
18
19
|
## Triggers
|
|
19
20
|
|
|
@@ -23,329 +24,62 @@ Web game development hits performance walls that traditional web apps never enco
|
|
|
23
24
|
- `/rune game-loops` — implement or audit game loop architecture
|
|
24
25
|
- `/rune physics-engine` — set up or optimize physics simulation
|
|
25
26
|
- `/rune asset-pipeline` — optimize asset loading and management
|
|
27
|
+
- `/rune multiplayer` — WebSocket game server and client prediction
|
|
28
|
+
- `/rune audio-system` — Web Audio API, spatial audio, SFX management
|
|
29
|
+
- `/rune input-system` — keyboard/mouse/gamepad/touch input handling
|
|
30
|
+
- `/rune ecs` — Entity Component System architecture
|
|
31
|
+
- `/rune particles` — GPU particle system with WebGL
|
|
32
|
+
- `/rune camera-system` — follow camera, screen shake, zoom
|
|
33
|
+
- `/rune scene-management` — scene transitions, preloading, serialization
|
|
26
34
|
- Called by `cook` (L1) when game development task detected
|
|
27
35
|
|
|
28
36
|
## Skills Included
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Three.js
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<cylinderGeometry args={[0.2, 0.4, 3]} />
|
|
66
|
-
<meshStandardMaterial color="#4a7c59" />
|
|
67
|
-
{positions.map((pos, i) => <Instance key={i} position={pos} />)}
|
|
68
|
-
</Instances>
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function GameScene() {
|
|
73
|
-
return (
|
|
74
|
-
<Canvas camera={{ position: [0, 10, 20], fov: 60 }} gl={{ antialias: true }}>
|
|
75
|
-
<ambientLight intensity={0.3} />
|
|
76
|
-
<directionalLight position={[10, 20, 10]} intensity={1} castShadow />
|
|
77
|
-
<Environment preset="sunset" />
|
|
78
|
-
<InstancedTrees count={500} />
|
|
79
|
-
<OrbitControls maxPolarAngle={Math.PI / 2.2} />
|
|
80
|
-
<EffectComposer>
|
|
81
|
-
<Bloom intensity={0.3} luminanceThreshold={0.8} />
|
|
82
|
-
<Vignette offset={0.3} darkness={0.6} />
|
|
83
|
-
</EffectComposer>
|
|
84
|
-
</Canvas>
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
### webgl
|
|
92
|
-
|
|
93
|
-
WebGL patterns — shader programming, GLSL, buffer management, texture handling, instanced rendering.
|
|
94
|
-
|
|
95
|
-
#### Workflow
|
|
96
|
-
|
|
97
|
-
**Step 1 — Detect WebGL usage**
|
|
98
|
-
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.
|
|
99
|
-
|
|
100
|
-
**Step 2 — Audit shader and buffer efficiency**
|
|
101
|
-
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.
|
|
102
|
-
|
|
103
|
-
**Step 3 — Emit optimized WebGL code**
|
|
104
|
-
Emit: WebGL2 setup with proper context attributes, VAO-based buffer management, instanced rendering for repeated geometry, and GLSL shaders with documented inputs/outputs.
|
|
105
|
-
|
|
106
|
-
#### Example
|
|
107
|
-
|
|
108
|
-
```glsl
|
|
109
|
-
// Vertex shader — instanced rendering with per-instance transform
|
|
110
|
-
#version 300 es
|
|
111
|
-
layout(location = 0) in vec3 aPosition;
|
|
112
|
-
layout(location = 1) in vec3 aNormal;
|
|
113
|
-
layout(location = 2) in mat4 aInstanceMatrix; // per-instance (locations 2-5)
|
|
114
|
-
|
|
115
|
-
uniform mat4 uViewProjection;
|
|
116
|
-
|
|
117
|
-
out vec3 vNormal;
|
|
118
|
-
out vec3 vWorldPos;
|
|
119
|
-
|
|
120
|
-
void main() {
|
|
121
|
-
vec4 worldPos = aInstanceMatrix * vec4(aPosition, 1.0);
|
|
122
|
-
vWorldPos = worldPos.xyz;
|
|
123
|
-
vNormal = mat3(transpose(inverse(aInstanceMatrix))) * aNormal;
|
|
124
|
-
gl_Position = uViewProjection * worldPos;
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
```glsl
|
|
129
|
-
// Fragment shader — PBR-lite with single directional light
|
|
130
|
-
#version 300 es
|
|
131
|
-
precision highp float;
|
|
132
|
-
|
|
133
|
-
in vec3 vNormal;
|
|
134
|
-
in vec3 vWorldPos;
|
|
135
|
-
out vec4 fragColor;
|
|
136
|
-
|
|
137
|
-
uniform vec3 uLightDir;
|
|
138
|
-
uniform vec3 uCameraPos;
|
|
139
|
-
uniform vec3 uBaseColor;
|
|
140
|
-
|
|
141
|
-
void main() {
|
|
142
|
-
vec3 N = normalize(vNormal);
|
|
143
|
-
vec3 L = normalize(uLightDir);
|
|
144
|
-
vec3 V = normalize(uCameraPos - vWorldPos);
|
|
145
|
-
vec3 H = normalize(L + V);
|
|
146
|
-
|
|
147
|
-
float diffuse = max(dot(N, L), 0.0);
|
|
148
|
-
float specular = pow(max(dot(N, H), 0.0), 32.0);
|
|
149
|
-
vec3 ambient = uBaseColor * 0.15;
|
|
150
|
-
|
|
151
|
-
fragColor = vec4(ambient + uBaseColor * diffuse + vec3(specular * 0.5), 1.0);
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
---
|
|
156
|
-
|
|
157
|
-
### game-loops
|
|
158
|
-
|
|
159
|
-
Game loop architecture — fixed timestep, interpolation, input handling, state machines, ECS.
|
|
160
|
-
|
|
161
|
-
#### Workflow
|
|
162
|
-
|
|
163
|
-
**Step 1 — Detect game loop pattern**
|
|
164
|
-
Use Grep to find loop code: `requestAnimationFrame`, `setInterval.*16`, `update`, `fixedUpdate`, `deltaTime`, `gameLoop`. Read the main loop to understand: timestep strategy, update/render separation, and input handling.
|
|
165
|
-
|
|
166
|
-
**Step 2 — Audit loop correctness**
|
|
167
|
-
Check for: variable timestep physics (non-deterministic), no accumulator for fixed update (physics tied to framerate), input polled inside render (inconsistent), missing interpolation between fixed steps (visual stuttering), and no frame budget monitoring.
|
|
168
|
-
|
|
169
|
-
**Step 3 — Emit fixed timestep loop**
|
|
170
|
-
Emit: fixed timestep (60Hz) with accumulator, interpolation for smooth rendering, decoupled input handler, and frame budget monitoring.
|
|
171
|
-
|
|
172
|
-
#### Example
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
// Fixed timestep game loop with interpolation
|
|
176
|
-
const TICK_RATE = 60;
|
|
177
|
-
const TICK_DURATION = 1000 / TICK_RATE;
|
|
178
|
-
|
|
179
|
-
class GameLoop {
|
|
180
|
-
private accumulator = 0;
|
|
181
|
-
private previousTime = 0;
|
|
182
|
-
private running = false;
|
|
183
|
-
|
|
184
|
-
constructor(
|
|
185
|
-
private update: (dt: number) => void, // fixed timestep logic
|
|
186
|
-
private render: (alpha: number) => void, // interpolated rendering
|
|
187
|
-
) {}
|
|
188
|
-
|
|
189
|
-
start() {
|
|
190
|
-
this.running = true;
|
|
191
|
-
this.previousTime = performance.now();
|
|
192
|
-
requestAnimationFrame(this.tick);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
private tick = (currentTime: number) => {
|
|
196
|
-
if (!this.running) return;
|
|
197
|
-
const elapsed = Math.min(currentTime - this.previousTime, 250); // cap spiral of death
|
|
198
|
-
this.previousTime = currentTime;
|
|
199
|
-
this.accumulator += elapsed;
|
|
200
|
-
|
|
201
|
-
while (this.accumulator >= TICK_DURATION) {
|
|
202
|
-
this.update(TICK_DURATION / 1000); // dt in seconds
|
|
203
|
-
this.accumulator -= TICK_DURATION;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const alpha = this.accumulator / TICK_DURATION; // interpolation factor [0, 1)
|
|
207
|
-
this.render(alpha);
|
|
208
|
-
requestAnimationFrame(this.tick);
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
stop() { this.running = false; }
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Usage
|
|
215
|
-
const loop = new GameLoop(
|
|
216
|
-
(dt) => { world.step(dt); entities.forEach(e => e.update(dt)); },
|
|
217
|
-
(alpha) => { renderer.render(scene, camera, alpha); },
|
|
218
|
-
);
|
|
219
|
-
loop.start();
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
---
|
|
223
|
-
|
|
224
|
-
### physics-engine
|
|
225
|
-
|
|
226
|
-
Physics integration — Rapier.js, rigid bodies, constraints, raycasting, collision callbacks, deterministic simulation.
|
|
227
|
-
|
|
228
|
-
#### Workflow
|
|
229
|
-
|
|
230
|
-
**Step 1 — Detect physics setup**
|
|
231
|
-
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.
|
|
232
|
-
|
|
233
|
-
**Step 2 — Audit physics configuration**
|
|
234
|
-
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.
|
|
235
|
-
|
|
236
|
-
**Step 3 — Emit optimized physics**
|
|
237
|
-
Emit: Rapier.js (WASM, deterministic) setup with proper collision groups, sleep thresholds, event-driven collision callbacks, and raycasting utility.
|
|
238
|
-
|
|
239
|
-
#### Example
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
// Rapier.js (WASM) — setup with collision groups and raycasting
|
|
243
|
-
import RAPIER from '@dimforge/rapier3d-compat';
|
|
244
|
-
|
|
245
|
-
await RAPIER.init();
|
|
246
|
-
const world = new RAPIER.World({ x: 0, y: -9.81, z: 0 });
|
|
247
|
-
|
|
248
|
-
// Collision groups: player=0x0001, enemy=0x0002, ground=0x0004, projectile=0x0008
|
|
249
|
-
const GROUPS = { PLAYER: 0x0001, ENEMY: 0x0002, GROUND: 0x0004, PROJECTILE: 0x0008 };
|
|
250
|
-
|
|
251
|
-
// Ground — static, collides with everything
|
|
252
|
-
const groundBody = world.createRigidBody(RAPIER.RigidBodyDesc.fixed().setTranslation(0, 0, 0));
|
|
253
|
-
world.createCollider(
|
|
254
|
-
RAPIER.ColliderDesc.cuboid(50, 0.1, 50)
|
|
255
|
-
.setCollisionGroups((GROUPS.GROUND << 16) | 0xFFFF),
|
|
256
|
-
groundBody,
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
// Player — dynamic, collides with ground + enemy (not own projectiles)
|
|
260
|
-
const playerBody = world.createRigidBody(RAPIER.RigidBodyDesc.dynamic().setTranslation(0, 5, 0));
|
|
261
|
-
world.createCollider(
|
|
262
|
-
RAPIER.ColliderDesc.capsule(0.5, 0.3)
|
|
263
|
-
.setCollisionGroups((GROUPS.PLAYER << 16) | (GROUPS.GROUND | GROUPS.ENEMY))
|
|
264
|
-
.setActiveEvents(RAPIER.ActiveEvents.COLLISION_EVENTS),
|
|
265
|
-
playerBody,
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
// Raycast utility
|
|
269
|
-
function raycast(origin: RAPIER.Vector3, direction: RAPIER.Vector3, maxDist = 100) {
|
|
270
|
-
const ray = new RAPIER.Ray(origin, direction);
|
|
271
|
-
const hit = world.castRay(ray, maxDist, true);
|
|
272
|
-
if (hit) {
|
|
273
|
-
const point = ray.pointAt(hit.timeOfImpact);
|
|
274
|
-
return { point, normal: hit.normal, collider: hit.collider };
|
|
275
|
-
}
|
|
276
|
-
return null;
|
|
277
|
-
}
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
---
|
|
281
|
-
|
|
282
|
-
### asset-pipeline
|
|
283
|
-
|
|
284
|
-
Game asset pipeline — glTF loading, texture compression, audio management, asset manifest, preloading.
|
|
285
|
-
|
|
286
|
-
#### Workflow
|
|
287
|
-
|
|
288
|
-
**Step 1 — Detect asset strategy**
|
|
289
|
-
Use Glob to find asset files: `*.gltf`, `*.glb`, `*.ktx2`, `*.basis`, `*.png` in `assets/` or `public/`. Use Grep to find loaders: `GLTFLoader`, `TextureLoader`, `KTX2Loader`, `Howler`, `Audio`. Read the loading code to understand: preloading strategy, compression, and caching.
|
|
290
|
-
|
|
291
|
-
**Step 2 — Audit asset efficiency**
|
|
292
|
-
Check for: uncompressed textures (PNG/JPG instead of KTX2/Basis), glTF without Draco compression, no asset manifest (scattered inline paths), missing preloader (assets load mid-gameplay causing stutters), audio files in WAV format (use OGG/MP3), and no LOD variants for 3D models.
|
|
293
|
-
|
|
294
|
-
**Step 3 — Emit asset pipeline**
|
|
295
|
-
Emit: asset manifest with typed entries, preloader with progress tracking, glTF loader with Draco decoder, KTX2 texture loader, and audio manager with Howler.js.
|
|
296
|
-
|
|
297
|
-
#### Example
|
|
298
|
-
|
|
299
|
-
```typescript
|
|
300
|
-
// Asset manifest + preloader with progress tracking
|
|
301
|
-
interface AssetManifest {
|
|
302
|
-
models: Record<string, { url: string; draco?: boolean }>;
|
|
303
|
-
textures: Record<string, { url: string; format: 'ktx2' | 'png' }>;
|
|
304
|
-
audio: Record<string, { url: string; volume?: number; loop?: boolean }>;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const MANIFEST: AssetManifest = {
|
|
308
|
-
models: {
|
|
309
|
-
player: { url: '/assets/player.glb', draco: true },
|
|
310
|
-
level1: { url: '/assets/level1.glb', draco: true },
|
|
311
|
-
},
|
|
312
|
-
textures: {
|
|
313
|
-
terrain: { url: '/assets/terrain.ktx2', format: 'ktx2' },
|
|
314
|
-
},
|
|
315
|
-
audio: {
|
|
316
|
-
bgm: { url: '/assets/bgm.ogg', volume: 0.5, loop: true },
|
|
317
|
-
jump: { url: '/assets/jump.ogg', volume: 0.8 },
|
|
318
|
-
},
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
class AssetLoader {
|
|
322
|
-
private loaded = 0;
|
|
323
|
-
private total = 0;
|
|
324
|
-
|
|
325
|
-
async loadAll(manifest: AssetManifest, onProgress: (pct: number) => void) {
|
|
326
|
-
const entries = [
|
|
327
|
-
...Object.values(manifest.models),
|
|
328
|
-
...Object.values(manifest.textures),
|
|
329
|
-
...Object.values(manifest.audio),
|
|
330
|
-
];
|
|
331
|
-
this.total = entries.length;
|
|
332
|
-
|
|
333
|
-
await Promise.all(entries.map(async (entry) => {
|
|
334
|
-
await fetch(entry.url); // preload into browser cache
|
|
335
|
-
this.loaded++;
|
|
336
|
-
onProgress(this.loaded / this.total);
|
|
337
|
-
}));
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
---
|
|
38
|
+
| Skill | Model | Description |
|
|
39
|
+
|-------|-------|-------------|
|
|
40
|
+
| [threejs-patterns](skills/threejs-patterns.md) | sonnet | Three.js scene, React Three Fiber, PBR materials, LOD, post-processing, instanced rendering, and disposal patterns. |
|
|
41
|
+
| [webgl](skills/webgl.md) | sonnet | Raw WebGL2, GLSL shaders, VAO buffer management, instanced rendering, and texture handling. |
|
|
42
|
+
| [game-loops](skills/game-loops.md) | sonnet | Fixed timestep with accumulator, interpolation for smooth rendering, decoupled input handler, and frame budget monitoring. |
|
|
43
|
+
| [physics-engine](skills/physics-engine.md) | sonnet | Rapier.js (WASM) setup with collision groups, sleep thresholds, event-driven collision callbacks, and raycasting. |
|
|
44
|
+
| [asset-pipeline](skills/asset-pipeline.md) | sonnet | glTF/Draco loading, KTX2 texture compression, typed asset manifest, preloader with progress tracking. |
|
|
45
|
+
| [multiplayer](skills/multiplayer.md) | sonnet | Authoritative WebSocket game server, client-side prediction, reconciliation, entity interpolation, and lag compensation. |
|
|
46
|
+
| [audio-system](skills/audio-system.md) | sonnet | Web Audio API AudioManager — spatial audio, music crossfade, SFX pooling, browser autoplay policy handling. |
|
|
47
|
+
| [input-system](skills/input-system.md) | sonnet | Unified keyboard/mouse/gamepad/touch input with action mapping, input buffering, coyote time, and virtual joystick. |
|
|
48
|
+
| [ecs](skills/ecs.md) | sonnet | Lightweight archetype-based ECS — dense component storage, query-based entity iteration, and pure system functions. |
|
|
49
|
+
| [particles](skills/particles.md) | sonnet | Object-pooled CPU particle system with WebGL instancing path for 10k+ particles and emitter presets. |
|
|
50
|
+
| [camera-system](skills/camera-system.md) | sonnet | 2D camera with smooth lerp follow, dead zone, screen shake decay, and zoom-to target. |
|
|
51
|
+
| [scene-management](skills/scene-management.md) | sonnet | Stack-based SceneManager with fade transitions, asset preloading before enter, and level JSON serialization. |
|
|
52
|
+
|
|
53
|
+
## Common Workflows
|
|
54
|
+
|
|
55
|
+
| Workflow | Skills Involved | Typical Trigger |
|
|
56
|
+
|----------|----------------|----------------|
|
|
57
|
+
| 2D platformer bootstrap | game-loops → physics-engine → input-system → camera-system | new Phaser/PixiJS project |
|
|
58
|
+
| 3D world with NPCs | threejs-patterns → ecs → physics-engine → camera-system | Three.js/R3F project |
|
|
59
|
+
| Multiplayer action game | game-loops → multiplayer → physics-engine → input-system | real-time PvP feature |
|
|
60
|
+
| Mobile game port | asset-pipeline → input-system → camera-system → game-loops | add touch controls |
|
|
61
|
+
| VFX & atmosphere | particles → webgl → threejs-patterns → audio-system | visual polish sprint |
|
|
62
|
+
| Game level editor | scene-management → asset-pipeline → ecs → camera-system | tooling sprint |
|
|
63
|
+
| Performance audit | game-loops → webgl → particles → asset-pipeline | frame rate complaints |
|
|
64
|
+
|
|
65
|
+
## Cross-Pack Connections
|
|
66
|
+
|
|
67
|
+
| Target Pack | Connection | Use Case |
|
|
68
|
+
|-------------|-----------|----------|
|
|
69
|
+
| **@rune/ui** | HUD components, inventory screens, pause menus, leaderboard overlays | Health bars, minimap, skill cooldowns, settings modal |
|
|
70
|
+
| **@rune/backend** | REST/WebSocket API for leaderboards, save data, player accounts, matchmaking | POST `/scores`, GET `/leaderboard`, save game state to DB |
|
|
71
|
+
| **@rune/analytics** | Player telemetry — session length, death locations, skill usage heatmaps | `analytics.track('player_died', { x, y, cause })` |
|
|
72
|
+
| **@rune/ai-ml** | NPC behavior trees, pathfinding ML, procedural content, cheat detection | A* pathfinding, trained NPC models, PCG level generation |
|
|
343
73
|
|
|
344
74
|
## Connections
|
|
345
75
|
|
|
346
76
|
```
|
|
347
77
|
Calls → perf (L2): frame budget and rendering performance audit
|
|
348
78
|
Calls → asset-creator (L3): generate placeholder assets and sprites
|
|
79
|
+
Calls → @rune/ui: HUD, inventory, menus, overlays
|
|
80
|
+
Calls → @rune/backend: leaderboards, save data, player accounts, matchmaking
|
|
81
|
+
Calls → @rune/analytics: player telemetry and session tracking
|
|
82
|
+
Calls → @rune/ai-ml: NPC behavior, procedural content, cheat detection
|
|
349
83
|
Called By ← cook (L1): when game development task detected
|
|
350
84
|
Called By ← review (L2): when game code under review
|
|
351
85
|
```
|
|
@@ -367,6 +101,9 @@ Called By ← review (L2): when game code under review
|
|
|
367
101
|
3. MUST NOT create objects inside the render loop — allocate outside, reuse inside.
|
|
368
102
|
4. MUST test on target minimum hardware (mobile GPU) not just development machine.
|
|
369
103
|
5. MUST use compressed asset formats (Draco for geometry, KTX2/Basis for textures) — raw assets cause unacceptable load times.
|
|
104
|
+
6. MUST use authoritative server model for multiplayer — never trust client position data.
|
|
105
|
+
7. MUST resume AudioContext on user gesture — browsers block autoplay audio.
|
|
106
|
+
8. MUST call `input.flush()` at end of each fixed tick — prevents justPressed persisting across frames.
|
|
370
107
|
|
|
371
108
|
## Sharp Edges
|
|
372
109
|
|
|
@@ -378,6 +115,10 @@ Called By ← review (L2): when game code under review
|
|
|
378
115
|
| Shader compiles on first use causing frame drop (shader cache miss) | MEDIUM | Pre-warm shaders during loading screen; use `renderer.compile(scene, camera)` |
|
|
379
116
|
| Asset loading blocks first frame (white screen for 5+ seconds) | HIGH | Implement progressive loading with preloader UI; prioritize visible assets |
|
|
380
117
|
| Mobile GPU fails on desktop-quality shaders (WebGL context lost) | HIGH | Detect GPU tier with `detect-gpu`; provide shader LOD variants |
|
|
118
|
+
| Multiplayer client trusts own position — speed hack trivial | CRITICAL | Server is authoritative; client sends inputs only, reconciles with server state |
|
|
119
|
+
| AudioContext locked until user gesture — no music on load | MEDIUM | Resume AudioContext in first click/keydown handler; show muted indicator |
|
|
120
|
+
| Gamepad axes not zeroed when gamepad disconnects | LOW | Set axes to 0 in gamepaddisconnected handler |
|
|
121
|
+
| Input justPressed persists to next frame if flush() skipped | HIGH | Always flush at end of fixed update, not render |
|
|
381
122
|
|
|
382
123
|
## Done When
|
|
383
124
|
|
|
@@ -386,8 +127,16 @@ Called By ← review (L2): when game code under review
|
|
|
386
127
|
- All GPU resources properly disposed on cleanup
|
|
387
128
|
- Assets compressed and preloaded with progress indicator
|
|
388
129
|
- Game loop decouples update from render with interpolation
|
|
130
|
+
- Multiplayer: server authoritative, client predicts + reconciles
|
|
131
|
+
- Audio: spatial SFX + crossfade music, resumable after user gesture
|
|
132
|
+
- Input: keyboard/mouse/gamepad/touch unified, buffered, rebindable
|
|
133
|
+
- ECS: entities/components/systems cleanly separated, query-based
|
|
134
|
+
- Particles: pooled, no GC spikes, emitter presets for common FX
|
|
135
|
+
- Camera: smooth follow, dead zone, screen shake on impact
|
|
136
|
+
- Scenes: transition with fade, preload assets before enter
|
|
137
|
+
- Performance: quadtree spatial queries, frame budget monitoring active
|
|
389
138
|
- Structured report emitted for each skill invoked
|
|
390
139
|
|
|
391
140
|
## Cost Profile
|
|
392
141
|
|
|
393
|
-
~10,000–20,000 tokens per full pack run (all
|
|
142
|
+
~10,000–20,000 tokens per full pack run (all skills). Individual skill: ~2,000–4,000 tokens. Sonnet default. Use haiku for asset detection scans and grep passes; sonnet for physics config, shader optimization, and multiplayer architecture; escalate to opus for full game architecture decisions spanning multiple systems.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "asset-pipeline"
|
|
3
|
+
pack: "@rune/gamedev"
|
|
4
|
+
description: "Game asset pipeline — glTF loading, texture compression, audio management, asset manifest, preloading."
|
|
5
|
+
model: sonnet
|
|
6
|
+
tools: [Read, Edit, Write, Grep, Glob, Bash]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# asset-pipeline
|
|
10
|
+
|
|
11
|
+
Game asset pipeline — glTF loading, texture compression, audio management, asset manifest, preloading.
|
|
12
|
+
|
|
13
|
+
#### Workflow
|
|
14
|
+
|
|
15
|
+
**Step 1 — Detect asset strategy**
|
|
16
|
+
Use Glob to find asset files: `*.gltf`, `*.glb`, `*.ktx2`, `*.basis`, `*.png` in `assets/` or `public/`. Use Grep to find loaders: `GLTFLoader`, `TextureLoader`, `KTX2Loader`, `Howler`, `Audio`. Read the loading code to understand: preloading strategy, compression, and caching.
|
|
17
|
+
|
|
18
|
+
**Step 2 — Audit asset efficiency**
|
|
19
|
+
Check for: uncompressed textures (PNG/JPG instead of KTX2/Basis), glTF without Draco compression, no asset manifest (scattered inline paths), missing preloader (assets load mid-gameplay causing stutters), audio files in WAV format (use OGG/MP3), and no LOD variants for 3D models.
|
|
20
|
+
|
|
21
|
+
**Step 3 — Emit asset pipeline**
|
|
22
|
+
Emit: asset manifest with typed entries, preloader with progress tracking, glTF loader with Draco decoder, KTX2 texture loader, and audio manager with Howler.js.
|
|
23
|
+
|
|
24
|
+
#### Example
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// Asset manifest + preloader with progress tracking
|
|
28
|
+
interface AssetManifest {
|
|
29
|
+
models: Record<string, { url: string; draco?: boolean }>;
|
|
30
|
+
textures: Record<string, { url: string; format: 'ktx2' | 'png' }>;
|
|
31
|
+
audio: Record<string, { url: string; volume?: number; loop?: boolean }>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const MANIFEST: AssetManifest = {
|
|
35
|
+
models: {
|
|
36
|
+
player: { url: '/assets/player.glb', draco: true },
|
|
37
|
+
level1: { url: '/assets/level1.glb', draco: true },
|
|
38
|
+
},
|
|
39
|
+
textures: {
|
|
40
|
+
terrain: { url: '/assets/terrain.ktx2', format: 'ktx2' },
|
|
41
|
+
},
|
|
42
|
+
audio: {
|
|
43
|
+
bgm: { url: '/assets/bgm.ogg', volume: 0.5, loop: true },
|
|
44
|
+
jump: { url: '/assets/jump.ogg', volume: 0.8 },
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
class AssetLoader {
|
|
49
|
+
private loaded = 0;
|
|
50
|
+
private total = 0;
|
|
51
|
+
private cache = new Map<string, unknown>();
|
|
52
|
+
|
|
53
|
+
async loadAll(manifest: AssetManifest, onProgress: (pct: number) => void) {
|
|
54
|
+
const entries = [
|
|
55
|
+
...Object.entries(manifest.models).map(([k, v]) => ({ key: k, ...v, type: 'model' })),
|
|
56
|
+
...Object.entries(manifest.textures).map(([k, v]) => ({ key: k, ...v, type: 'texture' })),
|
|
57
|
+
...Object.entries(manifest.audio).map(([k, v]) => ({ key: k, ...v, type: 'audio' })),
|
|
58
|
+
];
|
|
59
|
+
this.total = entries.length;
|
|
60
|
+
|
|
61
|
+
await Promise.all(entries.map(async (entry) => {
|
|
62
|
+
await fetch(entry.url); // preload into browser cache
|
|
63
|
+
this.loaded++;
|
|
64
|
+
onProgress(this.loaded / this.total);
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get<T>(key: string): T {
|
|
69
|
+
const asset = this.cache.get(key);
|
|
70
|
+
if (!asset) throw new Error(`Asset not loaded: ${key}`);
|
|
71
|
+
return asset as T;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|