@littlepartytime/sdk 2.2.2 → 2.3.0
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/GAME_DEV_GUIDE.md +105 -35
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/interfaces.d.ts +10 -4
- package/dist/interfaces.d.ts.map +1 -1
- package/package.json +1 -1
package/GAME_DEV_GUIDE.md
CHANGED
|
@@ -23,10 +23,10 @@ my-game/
|
|
|
23
23
|
├── vitest.config.ts # Test configuration
|
|
24
24
|
├── tsconfig.json
|
|
25
25
|
├── assets/ # Game images and custom assets
|
|
26
|
-
│ ├── icon.png # 1:1 (
|
|
27
|
-
│ ├── banner.
|
|
28
|
-
│ ├── cover.
|
|
29
|
-
│ ├── splash.
|
|
26
|
+
│ ├── icon.png # 1:1 (512x512+) - game list icon
|
|
27
|
+
│ ├── banner.webp # 16:9 (1280x720+) - lobby banner
|
|
28
|
+
│ ├── cover.webp # 21:9 (1260x540+) - store/featured cover
|
|
29
|
+
│ ├── splash.webp # 9:21 (1080x2520+) - loading screen
|
|
30
30
|
│ ├── cards/ # Custom game assets (optional)
|
|
31
31
|
│ │ ├── king.png
|
|
32
32
|
│ │ └── queen.png
|
|
@@ -76,16 +76,18 @@ const config: GameConfig = {
|
|
|
76
76
|
name: "My Game", // Display name (Chinese preferred for CN users)
|
|
77
77
|
description: "...", // Brief description
|
|
78
78
|
assets: {
|
|
79
|
-
icon: "assets/icon.png",
|
|
80
|
-
banner: "assets/banner.
|
|
81
|
-
cover: "assets/cover.
|
|
82
|
-
splash: "assets/splash.
|
|
79
|
+
icon: "assets/icon.png", // 1:1 (512x512+), PNG recommended
|
|
80
|
+
banner: "assets/banner.webp", // 16:9 (1280x720+), WebP recommended
|
|
81
|
+
cover: "assets/cover.webp", // 21:9 (1260x540+), WebP recommended
|
|
82
|
+
splash: "assets/splash.webp", // 9:21 (1080x2520+), WebP recommended
|
|
83
83
|
},
|
|
84
84
|
minPlayers: 2,
|
|
85
85
|
maxPlayers: 6,
|
|
86
86
|
tags: ["strategy", "card"],
|
|
87
87
|
version: "1.0.0",
|
|
88
88
|
sdkVersion: "2.0.0",
|
|
89
|
+
// Optional: include a demo site in the game package
|
|
90
|
+
// demo: { title: "My Game — Demo" },
|
|
89
91
|
};
|
|
90
92
|
|
|
91
93
|
export default config;
|
|
@@ -430,9 +432,33 @@ Features:
|
|
|
430
432
|
- **Action log**: See all actions sent by players in real-time
|
|
431
433
|
- **Game over detection**: Automatically detects when `isGameOver()` returns true and displays results
|
|
432
434
|
- **Reset**: Re-initialize the game at any time
|
|
435
|
+
- **Screenshot**: Capture the game content area (without the phone frame) as a PNG file
|
|
433
436
|
|
|
434
437
|
This enables a rapid development cycle: **edit code -> refresh browser -> test immediately**.
|
|
435
438
|
|
|
439
|
+
### Screenshot
|
|
440
|
+
|
|
441
|
+
A **Screenshot** button floats at the bottom of the phone frame on the Preview page. Clicking it captures the game's safe area (390×751 px, 2× retina resolution) as a PNG and downloads it immediately. The phone bezel, Dynamic Island, and Home Indicator are excluded.
|
|
442
|
+
|
|
443
|
+
The same capture function is also exposed as a JavaScript API on the page, so LLMs and Playwright scripts can call it programmatically:
|
|
444
|
+
|
|
445
|
+
```javascript
|
|
446
|
+
// Via Playwright evaluate (e.g. in a test or from an AI agent):
|
|
447
|
+
const dataUrl = await page.evaluate(() => window.__devkit__.captureScreen());
|
|
448
|
+
// dataUrl is a "data:image/png;base64,..." string — pass it to a vision model or save it.
|
|
449
|
+
|
|
450
|
+
// Save to file in Node.js:
|
|
451
|
+
const base64 = dataUrl.replace(/^data:image\/png;base64,/, '');
|
|
452
|
+
fs.writeFileSync('screenshot.png', Buffer.from(base64, 'base64'));
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
Alternatively, Playwright's built-in element screenshot can target the game area directly using the stable test ID:
|
|
456
|
+
|
|
457
|
+
```javascript
|
|
458
|
+
const el = page.getByTestId('game-screen');
|
|
459
|
+
await el.screenshot({ path: 'screenshot.png' });
|
|
460
|
+
```
|
|
461
|
+
|
|
436
462
|
### Multiplayer Page
|
|
437
463
|
|
|
438
464
|
The Play page runs your game with real Socket.IO multiplayer:
|
|
@@ -535,6 +561,16 @@ await preview.startGame(); // Host clicks "Start Game"
|
|
|
535
561
|
await preview.stop(); // Clean up browser + server
|
|
536
562
|
```
|
|
537
563
|
|
|
564
|
+
To capture a screenshot of the game area during a test, use either approach:
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
// Option A: via window.__devkit__ (returns base64 data URL)
|
|
568
|
+
const dataUrl = await page.evaluate(() => window.__devkit__.captureScreen());
|
|
569
|
+
|
|
570
|
+
// Option B: via Playwright element screenshot (saves directly to file)
|
|
571
|
+
await page.getByTestId('game-screen').screenshot({ path: 'screenshot.png' });
|
|
572
|
+
```
|
|
573
|
+
|
|
538
574
|
### Excluding E2E Tests from Unit Test Runs
|
|
539
575
|
|
|
540
576
|
E2E tests are slower and require playwright. Exclude them from the default `vitest run`:
|
|
@@ -572,8 +608,9 @@ This command:
|
|
|
572
608
|
3. Reads `GameConfig` from the built engine to extract metadata
|
|
573
609
|
4. Validates the required image assets (format, dimensions, aspect ratio)
|
|
574
610
|
5. Validates `rules.md` exists and is non-empty
|
|
575
|
-
6.
|
|
576
|
-
7.
|
|
611
|
+
6. If `GameConfig.demo` is set, validates that `demo/dist/index.html` exists
|
|
612
|
+
7. Generates `manifest.json` from your config
|
|
613
|
+
8. Creates a `.zip` file in the `dist/` directory containing code, manifest, rules, images, and optional demo site
|
|
577
614
|
|
|
578
615
|
### Build Output
|
|
579
616
|
|
|
@@ -594,15 +631,17 @@ The `.zip` upload package contains:
|
|
|
594
631
|
├── rules.md # Game rules
|
|
595
632
|
├── bundle.js # Client-side bundle
|
|
596
633
|
├── engine.cjs # Server-side engine
|
|
597
|
-
├── icon.png
|
|
598
|
-
├── banner.png
|
|
599
|
-
├── cover.png
|
|
600
|
-
├── splash.png
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
634
|
+
├── icon.png|.webp # 1:1 game icon
|
|
635
|
+
├── banner.png|.webp # 16:9 banner
|
|
636
|
+
├── cover.png|.webp # 21:9 cover
|
|
637
|
+
├── splash.png|.webp # 9:21 splash screen
|
|
638
|
+
├── assets/ # Custom game assets (if any)
|
|
639
|
+
│ ├── cards/
|
|
640
|
+
│ │ └── king.png
|
|
641
|
+
│ └── sounds/
|
|
642
|
+
│ └── flip.mp3
|
|
643
|
+
└── demo/ # Demo site (optional, only if GameConfig.demo is set)
|
|
644
|
+
└── index.html
|
|
606
645
|
```
|
|
607
646
|
|
|
608
647
|
### Configuration
|
|
@@ -657,21 +696,21 @@ export default defineConfig({
|
|
|
657
696
|
|
|
658
697
|
Every game must include 4 images in the `assets/` directory. These are validated and packaged by the `pack` command.
|
|
659
698
|
|
|
660
|
-
| Image | Aspect Ratio |
|
|
661
|
-
|
|
662
|
-
| `icon.png` | 1:1 |
|
|
663
|
-
| `banner.png` | 16:9 |
|
|
664
|
-
| `cover.png` | 21:9 |
|
|
665
|
-
| `splash.png` | 9:21 |
|
|
699
|
+
| Image | Aspect Ratio | Recommended Size | Purpose |
|
|
700
|
+
|-------|-------------|-----------------|---------|
|
|
701
|
+
| `icon.png` | 1:1 | 512x512 | Game list icon |
|
|
702
|
+
| `banner.png` or `.webp` | 16:9 | 1280x720 | Lobby banner |
|
|
703
|
+
| `cover.png` or `.webp` | 21:9 | 1260x540 | Store/featured cover |
|
|
704
|
+
| `splash.png` or `.webp` | 9:21 | 1080x2520 | Loading/splash screen |
|
|
666
705
|
|
|
667
706
|
**Rules:**
|
|
668
|
-
- **Formats**: PNG or WebP only
|
|
707
|
+
- **Formats**: PNG or WebP only. Recommendation: use PNG for icon (small, sharp edges), WebP for banner/cover/splash (photo-like content, 60-80% smaller than PNG)
|
|
669
708
|
- **Aspect ratio**: Must match exactly (1% tolerance)
|
|
670
|
-
- **
|
|
671
|
-
- **File size**: Warning if any image exceeds 2MB
|
|
672
|
-
- **Paths**: Referenced in `GameConfig.assets` as relative paths from the project root
|
|
709
|
+
- **Dimensions**: Must meet or exceed the recommended size. Exact match preferred; larger with correct ratio also accepted
|
|
710
|
+
- **File size**: Warning if any image exceeds 2MB (WebP typically avoids this even at splash resolution)
|
|
711
|
+
- **Paths**: Referenced in `GameConfig.assets` as relative paths from the project root (e.g., `"assets/banner.webp"`)
|
|
673
712
|
|
|
674
|
-
The `pack` command reads these images, validates them, and includes them in the zip with canonical names (`icon
|
|
713
|
+
The `pack` command reads these images, validates them, and includes them in the zip with canonical names (`icon`, `banner`, `cover`, `splash`) preserving the original file extension (`.png` or `.webp`).
|
|
675
714
|
|
|
676
715
|
### Custom Game Assets
|
|
677
716
|
|
|
@@ -679,10 +718,10 @@ For assets used inside your game UI (card images, sound effects, fonts, etc.), p
|
|
|
679
718
|
|
|
680
719
|
```
|
|
681
720
|
assets/
|
|
682
|
-
├── icon.png # Platform display images (root level, required)
|
|
683
|
-
├── banner.
|
|
684
|
-
├── cover.
|
|
685
|
-
├── splash.
|
|
721
|
+
├── icon.png # Platform display images (root level, required, .png or .webp)
|
|
722
|
+
├── banner.webp
|
|
723
|
+
├── cover.webp
|
|
724
|
+
├── splash.webp
|
|
686
725
|
├── cards/ # Custom game assets (subdirectories)
|
|
687
726
|
│ ├── king.png
|
|
688
727
|
│ └── queen.png
|
|
@@ -897,8 +936,39 @@ Use these CSS variables in your inline styles for consistent theming:
|
|
|
897
936
|
| Border | `--border-default` | `border: "1px solid var(--border-default)"` |
|
|
898
937
|
| Success | `--success` | `color: "var(--success)"` |
|
|
899
938
|
| Error | `--error` | `color: "var(--error)"` |
|
|
939
|
+
| Body font (sans-serif) | `--font-body` | `fontFamily: "var(--font-body)"` |
|
|
940
|
+
| Serif font | `--font-serif` | `fontFamily: "var(--font-serif)"` |
|
|
900
941
|
| Display font | `--font-display` | `fontFamily: "var(--font-display)"` |
|
|
901
|
-
|
|
942
|
+
|
|
943
|
+
### Platform Fonts
|
|
944
|
+
|
|
945
|
+
The platform self-hosts **Noto Sans SC** (sans-serif) and **Noto Serif SC** (serif) via `next/font/google`, available to all games inside the sandbox container.
|
|
946
|
+
|
|
947
|
+
| CSS Variable | Font | Type |
|
|
948
|
+
|---|---|---|
|
|
949
|
+
| `--font-body` | Noto Sans SC | sans-serif (default — inherited automatically) |
|
|
950
|
+
| `--font-serif` | Noto Serif SC | serif |
|
|
951
|
+
| `--font-display` | Comfortaa | display / title |
|
|
952
|
+
|
|
953
|
+
Games inherit `--font-body` (Noto Sans SC) by default — no action needed for sans-serif text.
|
|
954
|
+
|
|
955
|
+
To use the serif font:
|
|
956
|
+
|
|
957
|
+
```tsx
|
|
958
|
+
<p style={{ fontFamily: "var(--font-serif)" }}>衬线体文本</p>
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
**Do NOT reference fonts by name.** The platform uses hashed `font-family` names internally, so literal font names will not match the self-hosted font files:
|
|
962
|
+
|
|
963
|
+
```css
|
|
964
|
+
/* ✅ Works */
|
|
965
|
+
font-family: var(--font-body);
|
|
966
|
+
font-family: var(--font-serif);
|
|
967
|
+
|
|
968
|
+
/* ❌ Does NOT work — won't match the self-hosted font */
|
|
969
|
+
font-family: 'Noto Sans SC', sans-serif;
|
|
970
|
+
font-family: 'Noto Serif SC', serif;
|
|
971
|
+
```
|
|
902
972
|
|
|
903
973
|
## Platform Runtime Constraints
|
|
904
974
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export type { Player, GameAction, GameResult, Platform, HapticImpactStyle, HapticNotificationType, DeviceTilt, DeviceCapabilities, } from './types';
|
|
2
|
-
export type { PlayerState, GameState, GameAssets, GameConfig, GameEngine, GameRendererProps } from './interfaces';
|
|
2
|
+
export type { PlayerState, GameState, GameAssets, GameDemo, GameConfig, GameEngine, GameRendererProps } from './interfaces';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,MAAM,EACN,UAAU,EACV,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,sBAAsB,EACtB,UAAU,EACV,kBAAkB,GACnB,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,MAAM,EACN,UAAU,EACV,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,sBAAsB,EACtB,UAAU,EACV,kBAAkB,GACnB,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -9,15 +9,19 @@ export interface GameState {
|
|
|
9
9
|
data: Record<string, unknown>;
|
|
10
10
|
}
|
|
11
11
|
export interface GameAssets {
|
|
12
|
-
/** 1:1 game list icon (
|
|
12
|
+
/** 1:1 game list icon (512x512+ recommended, .png or .webp) */
|
|
13
13
|
icon: string;
|
|
14
|
-
/** 16:9 lobby banner */
|
|
14
|
+
/** 16:9 lobby banner (1280x720+ recommended, .png or .webp) */
|
|
15
15
|
banner: string;
|
|
16
|
-
/** 21:9 store/featured cover */
|
|
16
|
+
/** 21:9 store/featured cover (1260x540+ recommended, .png or .webp) */
|
|
17
17
|
cover: string;
|
|
18
|
-
/** 9:21 loading/splash screen */
|
|
18
|
+
/** 9:21 loading/splash screen (1080x2520+ recommended, .png or .webp) */
|
|
19
19
|
splash: string;
|
|
20
20
|
}
|
|
21
|
+
export interface GameDemo {
|
|
22
|
+
/** Title shown on the demo page (e.g. "奶酪大盗 — 游戏演示") */
|
|
23
|
+
title: string;
|
|
24
|
+
}
|
|
21
25
|
export interface GameConfig {
|
|
22
26
|
name: string;
|
|
23
27
|
description: string;
|
|
@@ -28,6 +32,8 @@ export interface GameConfig {
|
|
|
28
32
|
version: string;
|
|
29
33
|
sdkVersion: string;
|
|
30
34
|
price?: number;
|
|
35
|
+
/** Optional demo site metadata. When present, `lpt-dev-kit pack` will look for `demo/dist/` and include it in the ZIP. */
|
|
36
|
+
demo?: GameDemo;
|
|
31
37
|
}
|
|
32
38
|
export interface GameEngine {
|
|
33
39
|
init(players: Player[], options?: Record<string, unknown>): GameState;
|
package/dist/interfaces.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExE,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB
|
|
1
|
+
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExE,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0HAA0H;IAC1H,IAAI,CAAC,EAAE,QAAQ,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IACtE,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IAChF;;;;;;;;;OASG;IACH,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC;IACtC,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CAAC;IACxC,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CACvE;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;CAC3B"}
|
package/package.json
CHANGED