koishipro-core.js 1.0.10 → 1.1.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/README.md +248 -38
- package/dist/index.cjs +126 -257
- package/dist/index.cjs.map +3 -3
- package/dist/index.mjs +135 -259
- package/dist/index.mjs.map +3 -3
- package/dist/src/adapters/ocgcore-parsers.d.ts +11 -5
- package/dist/src/ocgcore-duel.d.ts +1 -1
- package/dist/src/structs/card-data.d.ts +5 -3
- package/dist/src/types/card-data.d.ts +1 -3
- package/dist/src/types/ocgcore-params.d.ts +6 -6
- package/dist/src/types/ocgcore-results.d.ts +6 -77
- package/dist/vendor/libocgcore.shared.d.ts +7 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,9 +7,10 @@ helpers for loading scripts/cards.
|
|
|
7
7
|
## Features
|
|
8
8
|
- Load ocgcore WASM (CJS or ESM) and create duels
|
|
9
9
|
- TypeScript-friendly wrapper around duel APIs
|
|
10
|
-
-
|
|
10
|
+
- **All query methods and process messages use [`ygopro-msg-encode`](https://github.com/purerosefallen/ygopro-msg-encode) for typed message parsing**
|
|
11
|
+
- Script readers (Map/Zip/Dir) for loading Lua scripts
|
|
11
12
|
- SQL.js card reader helper
|
|
12
|
-
- Replay (YRP/YRP2) playback
|
|
13
|
+
- Replay (YRP/YRP2) playback with step-by-step control
|
|
13
14
|
- Constants auto-generated from upstream YGOPro sources
|
|
14
15
|
|
|
15
16
|
## Install
|
|
@@ -50,67 +51,276 @@ wrapper
|
|
|
50
51
|
});
|
|
51
52
|
|
|
52
53
|
const duel = wrapper.createDuel(1234);
|
|
53
|
-
duel.setPlayerInfo({
|
|
54
|
-
duel.setPlayerInfo({
|
|
54
|
+
duel.setPlayerInfo({ player: 0, lp: 8000, startHand: 5, drawCount: 1 });
|
|
55
|
+
duel.setPlayerInfo({ player: 1, lp: 8000, startHand: 5, drawCount: 1 });
|
|
55
56
|
duel.startDuel(0);
|
|
56
57
|
|
|
57
|
-
const { raw, status } = duel.process();
|
|
58
|
-
console.log(
|
|
58
|
+
const { raw, status, message } = duel.process();
|
|
59
|
+
console.log('Raw bytes:', raw);
|
|
60
|
+
console.log('Status:', status);
|
|
61
|
+
console.log('Parsed message:', message); // YGOProMsgBase instance from ygopro-msg-encode
|
|
59
62
|
|
|
60
63
|
duel.endDuel();
|
|
61
64
|
wrapper.finalize();
|
|
62
65
|
```
|
|
63
66
|
|
|
64
67
|
## Replay Playback
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
|
|
69
|
+
### playYrp (One-shot)
|
|
70
|
+
`playYrp` replays a `.yrp/.yrp2` file and returns all messages at once:
|
|
68
71
|
|
|
69
72
|
```ts
|
|
70
73
|
import { createOcgcoreWrapper, playYrp } from 'koishipro-core.js';
|
|
71
|
-
import { YGOProYrp } from 'ygopro-yrp-encode';
|
|
72
74
|
|
|
73
75
|
const wrapper = await createOcgcoreWrapper();
|
|
74
|
-
|
|
75
|
-
// ...setScriptReader / setCardReader / setMessageHandler...
|
|
76
|
+
// ...setScriptReader / setCardReader...
|
|
76
77
|
|
|
77
78
|
const yrpBytes = await fetch('/replay.yrp').then((r) => r.arrayBuffer());
|
|
78
79
|
const messages = playYrp(wrapper, new Uint8Array(yrpBytes));
|
|
79
|
-
console.log('
|
|
80
|
+
console.log('Total messages:', messages.length);
|
|
80
81
|
```
|
|
81
82
|
|
|
83
|
+
### playYrpStep (Generator)
|
|
84
|
+
`playYrpStep` gives you step-by-step control over replay execution. Use this when you need to:
|
|
85
|
+
- Query game state during replay
|
|
86
|
+
- Inspect individual messages and their parsed objects
|
|
87
|
+
- Control replay execution flow
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { createOcgcoreWrapper, playYrpStep } from 'koishipro-core.js';
|
|
91
|
+
import { YGOProMsgNewTurn } from 'ygopro-msg-encode';
|
|
92
|
+
|
|
93
|
+
const wrapper = await createOcgcoreWrapper();
|
|
94
|
+
// ...setScriptReader / setCardReader...
|
|
95
|
+
|
|
96
|
+
const yrpBytes = await fetch('/replay.yrp').then((r) => r.arrayBuffer());
|
|
97
|
+
|
|
98
|
+
for (const { duel, result } of playYrpStep(wrapper, new Uint8Array(yrpBytes))) {
|
|
99
|
+
// Access parsed message (from ygopro-msg-encode)
|
|
100
|
+
if (result.message instanceof YGOProMsgNewTurn) {
|
|
101
|
+
console.log('New turn started!');
|
|
102
|
+
|
|
103
|
+
// Query game state at this point
|
|
104
|
+
const fieldInfo = duel.queryFieldInfo();
|
|
105
|
+
console.log('Player 0 LP:', fieldInfo.field.players[0].lp);
|
|
106
|
+
|
|
107
|
+
const mzoneCards = duel.queryFieldCard({
|
|
108
|
+
player: 0,
|
|
109
|
+
location: LOCATION_MZONE,
|
|
110
|
+
queryFlag: QUERY_CODE | QUERY_ATTACK | QUERY_DEFENSE,
|
|
111
|
+
});
|
|
112
|
+
console.log('Monster zone cards:', mzoneCards.cards);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Access raw bytes if needed
|
|
116
|
+
console.log('Raw message bytes:', result.raw);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
## API Reference
|
|
122
|
+
|
|
123
|
+
### Core Classes
|
|
124
|
+
|
|
125
|
+
#### `createOcgcoreWrapper(options?): Promise<OcgcoreWrapper>`
|
|
126
|
+
Load the ocgcore WASM module and return an `OcgcoreWrapper`.
|
|
127
|
+
|
|
128
|
+
**Options:**
|
|
129
|
+
- `scriptBufferSize?: number` - Buffer size for script loading (default: 0x100000)
|
|
130
|
+
- `logBufferSize?: number` - Buffer size for log messages (default: 1024)
|
|
131
|
+
|
|
132
|
+
#### `OcgcoreWrapper`
|
|
133
|
+
Manages the WASM module, script/card/message handlers, and duel creation.
|
|
134
|
+
|
|
135
|
+
**Methods:**
|
|
136
|
+
- `setScriptReader(reader: ScriptReader, reset?: boolean): this`
|
|
137
|
+
Register a script reader. Multiple readers are tried in order (fallback).
|
|
138
|
+
- `setCardReader(reader: CardReader, reset?: boolean): this`
|
|
139
|
+
Register a card reader. Multiple readers are tried in order (fallback).
|
|
140
|
+
- `setMessageHandler(handler: MessageHandler, reset?: boolean): this`
|
|
141
|
+
Register a message handler for debug/error messages (fan-out pattern).
|
|
142
|
+
- `createDuel(seed: number): OcgcoreDuel`
|
|
143
|
+
Create a new duel with a single seed.
|
|
144
|
+
- `createDuelV2(seedSequence: number[]): OcgcoreDuel`
|
|
145
|
+
Create a new duel with a seed sequence (YRP2 format).
|
|
146
|
+
- `finalize(): void`
|
|
147
|
+
Clean up all allocated resources. Call this before discarding the wrapper.
|
|
82
148
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- `
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
149
|
+
#### `OcgcoreDuel`
|
|
150
|
+
Represents a single duel instance with full lifecycle management.
|
|
151
|
+
|
|
152
|
+
**Core Methods:**
|
|
153
|
+
- `startDuel(options: number | OcgcoreStartDuelOptions): void`
|
|
154
|
+
Start the duel with specified options (duel rules, shuffle mode, etc.).
|
|
155
|
+
- `process(): OcgcoreProcessResult`
|
|
156
|
+
Process the next game event. Returns `{ raw: Uint8Array, status: number, message?: YGOProMsgBase }`.
|
|
157
|
+
**The `message` field contains the parsed message from `ygopro-msg-encode`.**
|
|
158
|
+
- `setResponse(response: Uint8Array): void`
|
|
159
|
+
Provide a response to the engine (player action).
|
|
160
|
+
- `setResponseInt(value: number): void`
|
|
161
|
+
Provide an integer response.
|
|
162
|
+
- `endDuel(): void`
|
|
163
|
+
End the duel and clean up resources.
|
|
164
|
+
|
|
165
|
+
**Card Management:**
|
|
166
|
+
- `newCard(card: OcgcoreNewCardParams): void`
|
|
167
|
+
Add a card to the duel.
|
|
168
|
+
- `newTagCard(card: OcgcoreNewTagCardParams): void`
|
|
169
|
+
Add a tag duel card.
|
|
170
|
+
|
|
171
|
+
**Query Methods (All return `ygopro-msg-encode` objects):**
|
|
172
|
+
- `queryCard(query: OcgcoreQueryCardParams): OcgcoreCardQueryResult`
|
|
173
|
+
Query information about a single card.
|
|
174
|
+
**Returns `{ card: CardQuery | null }` from `ygopro-msg-encode`.**
|
|
175
|
+
|
|
176
|
+
- `queryFieldCard(query: OcgcoreQueryFieldCardParams): OcgcoreFieldCardQueryResult`
|
|
177
|
+
Query all cards in a location.
|
|
178
|
+
**Returns `{ cards: CardQuery[] }` from `ygopro-msg-encode`.**
|
|
179
|
+
|
|
180
|
+
- `queryFieldInfo(): OcgcoreFieldInfoResult`
|
|
181
|
+
Query the entire field state.
|
|
182
|
+
**Returns `{ field: YGOProMsgReloadField }` from `ygopro-msg-encode`.**
|
|
183
|
+
|
|
184
|
+
- `queryFieldCount(query: OcgcoreQueryFieldCountParams): number`
|
|
185
|
+
Get the number of cards in a location.
|
|
186
|
+
|
|
187
|
+
**Player Info:**
|
|
188
|
+
- `setPlayerInfo(info: OcgcoreSetPlayerInfoParams): void`
|
|
189
|
+
Set initial player state (LP, hand size, draw count).
|
|
190
|
+
|
|
191
|
+
**Script Preloading:**
|
|
192
|
+
- `preloadScript(scriptPath: string): void`
|
|
193
|
+
Preload a Lua script before duel starts.
|
|
194
|
+
|
|
195
|
+
**Registry (Key-Value Storage):**
|
|
196
|
+
- `setRegistryValue(key: string, value: string): void`
|
|
197
|
+
- `getRegistryValue(key: string): OcgcoreRegistryValueResult`
|
|
198
|
+
- `getRegistryKeys(): OcgcoreRegistryKeysResult`
|
|
199
|
+
- `dumpRegistry(): OcgcoreRegistryDumpResult`
|
|
200
|
+
- `loadRegistry(input: Uint8Array): void`
|
|
201
|
+
- `clearRegistry(): void`
|
|
92
202
|
|
|
93
203
|
### Script Readers
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
204
|
+
|
|
205
|
+
#### `MapReader(...maps: Map<string, string | Uint8Array>[]): ScriptReader`
|
|
206
|
+
Resolve Lua scripts from one or more Maps with fallback order.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
const scripts = new Map([
|
|
210
|
+
['c12345.lua', 'function c12345.initial_effect(c) end'],
|
|
211
|
+
]);
|
|
212
|
+
wrapper.setScriptReader(MapReader(scripts));
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### `ZipReader(...zipBytes: Uint8Array[]): Promise<ScriptReader>`
|
|
216
|
+
Load all `.lua` files from one or more zips.
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
const zipBytes = await fetch('/scripts.zip').then(r => r.arrayBuffer());
|
|
220
|
+
wrapper.setScriptReader(await ZipReader(new Uint8Array(zipBytes)));
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### `DirReader(...dirs: string[]): ScriptReader`
|
|
224
|
+
Node-only directory reader with fallback order.
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
wrapper.setScriptReader(DirReader('./ygopro-scripts', './custom-scripts'));
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Replay Functions
|
|
231
|
+
|
|
232
|
+
#### `playYrp(wrapper: OcgcoreWrapper, yrpOrBytes: YGOProYrp | Uint8Array): Uint8Array[]`
|
|
233
|
+
Run a complete replay and return all messages as raw bytes.
|
|
234
|
+
|
|
235
|
+
**Parameters:**
|
|
236
|
+
- `wrapper`: Initialized `OcgcoreWrapper` with script/card readers configured
|
|
237
|
+
- `yrpOrBytes`: `YGOProYrp` instance or raw `.yrp/.yrp2` bytes
|
|
238
|
+
|
|
239
|
+
**Returns:** Array of raw message bytes
|
|
240
|
+
|
|
241
|
+
**Throws:** `'Got MSG_RETRY'` if a retry message is encountered
|
|
242
|
+
|
|
243
|
+
#### `playYrpStep(wrapper: OcgcoreWrapper, yrpOrBytes: YGOProYrp | Uint8Array): Generator<{ duel: OcgcoreDuel, result: OcgcoreProcessResult }>`
|
|
244
|
+
Step through a replay with full control over execution.
|
|
245
|
+
|
|
246
|
+
**Yields:**
|
|
247
|
+
- `duel`: Current `OcgcoreDuel` instance (use for queries)
|
|
248
|
+
- `result`: Process result with `{ raw, status, message? }` where `message` is from `ygopro-msg-encode`
|
|
249
|
+
|
|
250
|
+
**Example:**
|
|
251
|
+
```ts
|
|
252
|
+
for (const { duel, result } of playYrpStep(wrapper, yrpBytes)) {
|
|
253
|
+
if (result.message) {
|
|
254
|
+
console.log('Message type:', result.message.constructor.name);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Query game state at any point
|
|
258
|
+
const fieldInfo = duel.queryFieldInfo();
|
|
259
|
+
const handCards = duel.queryFieldCard({
|
|
260
|
+
player: 0,
|
|
261
|
+
location: LOCATION_HAND,
|
|
262
|
+
queryFlag: QUERY_CODE
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
```
|
|
106
266
|
|
|
107
267
|
### Card Reader
|
|
108
|
-
|
|
109
|
-
|
|
268
|
+
|
|
269
|
+
#### `createSqljsCardReader(...dbs: Database[]): CardReader`
|
|
270
|
+
Build a `CardReader` from one or more SQL.js databases with fallback order.
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import initSqlJs from 'sql.js';
|
|
274
|
+
|
|
275
|
+
const SQL = await initSqlJs();
|
|
276
|
+
const db1 = new SQL.Database(officialCards);
|
|
277
|
+
const db2 = new SQL.Database(customCards);
|
|
278
|
+
|
|
279
|
+
// Try db1 first, fallback to db2
|
|
280
|
+
wrapper.setCardReader(createSqljsCardReader(db1, db2));
|
|
281
|
+
```
|
|
110
282
|
|
|
111
283
|
### Constants
|
|
112
|
-
|
|
113
|
-
|
|
284
|
+
|
|
285
|
+
#### `OcgcoreCommonConstants`
|
|
286
|
+
Message types and query flags (e.g., `MSG_NEW_TURN`, `QUERY_CODE`, `QUERY_ATTACK`).
|
|
287
|
+
|
|
288
|
+
#### `OcgcoreScriptConstants`
|
|
289
|
+
Game constants (e.g., `LOCATION_MZONE`, `POS_FACEUP_ATTACK`, `TYPE_MONSTER`).
|
|
290
|
+
|
|
291
|
+
### Integration with ygopro-msg-encode
|
|
292
|
+
|
|
293
|
+
All query methods and `process()` return typed objects from [`ygopro-msg-encode`](https://github.com/purerosefallen/ygopro-msg-encode):
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
import { YGOProMsgNewTurn, CardQuery } from 'ygopro-msg-encode';
|
|
297
|
+
|
|
298
|
+
// Process returns parsed messages
|
|
299
|
+
const { message } = duel.process();
|
|
300
|
+
if (message instanceof YGOProMsgNewTurn) {
|
|
301
|
+
console.log('Turn player:', message.player);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Query methods return CardQuery objects
|
|
305
|
+
const { card } = duel.queryCard({
|
|
306
|
+
player: 0,
|
|
307
|
+
location: LOCATION_MZONE,
|
|
308
|
+
sequence: 0,
|
|
309
|
+
queryFlag: QUERY_CODE | QUERY_ATTACK
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
if (card) {
|
|
313
|
+
console.log('Card code:', card.code);
|
|
314
|
+
console.log('Attack:', card.attack);
|
|
315
|
+
console.log('Position:', card.position);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// queryFieldInfo returns YGOProMsgReloadField
|
|
319
|
+
const { field } = duel.queryFieldInfo();
|
|
320
|
+
console.log('Duel rule:', field.duelRule);
|
|
321
|
+
console.log('Player 0 LP:', field.players[0].lp);
|
|
322
|
+
console.log('Player 0 hand count:', field.players[0].handCount);
|
|
323
|
+
```
|
|
114
324
|
|
|
115
325
|
## Build / Scripts
|
|
116
326
|
- `npm run build` – build CJS + ESM + types
|