@pkmn/engine 0.1.0-dev.84cc1a02 → 0.1.0-dev.89cc1c98
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 +98 -59
- package/build/pkg/addon.d.ts +7 -0
- package/build/pkg/addon.js +112 -0
- package/build/pkg/addon.js.map +1 -0
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +27 -5
- package/build/pkg/data/protocol.json +69 -12
- package/build/pkg/data.d.ts +3 -4
- package/build/pkg/data.js +3 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +5 -4
- package/build/pkg/gen1.js +49 -33
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +15 -12
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +4 -2
- package/build/pkg/protocol.js +60 -39
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +400 -0
- package/build/test/display.js.map +1 -0
- package/build/tools/debug.js +133 -0
- package/build/tools/debug.js.map +1 -0
- package/build.zig +103 -87
- package/package.json +72 -40
- package/src/bin/install-pkmn-engine +55 -31
- package/src/bin/pkmn-debug +62 -0
- package/src/lib/binding/node.zig +47 -142
- package/src/lib/binding/wasm.zig +6 -4
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +19 -31
- package/src/lib/common/debug.zig +21 -11
- package/src/lib/common/js.zig +118 -0
- package/src/lib/common/optional.zig +67 -0
- package/src/lib/common/options.zig +12 -3
- package/src/lib/common/protocol.zig +788 -246
- package/src/lib/common/rational.zig +353 -0
- package/src/lib/common/rng.zig +15 -15
- package/src/lib/common/util.zig +12 -0
- package/src/lib/gen1/README.md +297 -181
- package/src/lib/gen1/calc.zig +426 -0
- package/src/lib/gen1/chance.zig +1060 -0
- package/src/lib/gen1/data/moves.zig +202 -190
- package/src/lib/gen1/data/species.zig +4 -4
- package/src/lib/gen1/data/types.zig +10 -10
- package/src/lib/gen1/data.zig +90 -76
- package/src/lib/gen1/helpers.zig +577 -32
- package/src/lib/gen1/mechanics.zig +1048 -712
- package/src/lib/gen2/README.md +106 -85
- package/src/lib/gen2/calc.zig +143 -0
- package/src/lib/gen2/chance.zig +1314 -0
- package/src/lib/gen2/data/items.zig +14 -20
- package/src/lib/gen2/data/moves.zig +664 -570
- package/src/lib/gen2/data/species.zig +2 -2
- package/src/lib/gen2/data/types.zig +137 -34
- package/src/lib/gen2/data.zig +125 -89
- package/src/lib/gen2/generated.zig +857 -0
- package/src/lib/gen2/helpers.zig +16 -22
- package/src/lib/gen2/mechanics.zig +2998 -12
- package/src/lib/pkmn.zig +68 -9
- package/src/test/display/pkmn.html.tmpl +368 -0
|
@@ -10,13 +10,15 @@ const path = require('path');
|
|
|
10
10
|
const {pipeline} = require('stream/promises');
|
|
11
11
|
const tty = require('tty');
|
|
12
12
|
|
|
13
|
-
const ZIG_VERSION = {major: 0, minor: 11, patch: 0
|
|
13
|
+
const ZIG_VERSION = {major: 0, minor: 11, patch: 0};
|
|
14
14
|
const DEV = ZIG_VERSION.dev ? `-dev.${ZIG_VERSION.dev}` : '';
|
|
15
15
|
const VERSION = `${ZIG_VERSION.major}.${ZIG_VERSION.minor}.${ZIG_VERSION.patch}${DEV}`;
|
|
16
16
|
const INDEX = 'https://ziglang.org/download/index.json';
|
|
17
17
|
const ROOT = path.resolve(__dirname, '..', '..');
|
|
18
|
+
let SILENT = false;
|
|
18
19
|
|
|
19
20
|
const sh = (cmd, args) => execFileSync(cmd, args, {encoding: 'utf8', cwd: ROOT});
|
|
21
|
+
const log = (...args) => { if (!SILENT) console.log(...args); };
|
|
20
22
|
|
|
21
23
|
// This is hardly the browser fetch API, but it works for our primitive needs
|
|
22
24
|
const fetch = url => new Promise((resolve, reject) => {
|
|
@@ -61,7 +63,7 @@ const unpack7 = (input, output) => new Promise((resolve, reject) => {
|
|
|
61
63
|
require.resolve('7zip-min');
|
|
62
64
|
} catch (err) {
|
|
63
65
|
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
64
|
-
|
|
66
|
+
log('Installing 7zip-min package locally to unpack the archive...');
|
|
65
67
|
sh('npm', ['install', '7zip-min', '--no-audit', '--no-save']);
|
|
66
68
|
}
|
|
67
69
|
|
|
@@ -83,7 +85,7 @@ const unpack = async (input, output) => {
|
|
|
83
85
|
sh('tar', ['xf', input, '-C', output]);
|
|
84
86
|
return;
|
|
85
87
|
} catch {
|
|
86
|
-
|
|
88
|
+
log('tar command not found, falling back to 7z');
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
// Try to use a 7z binary if we can, and only install 7zip-min as a last resort
|
|
@@ -120,10 +122,10 @@ const find = zig => {
|
|
|
120
122
|
(!ZIG_VERSION.dev || (dev && dev >= ZIG_VERSION.dev)));
|
|
121
123
|
|
|
122
124
|
if (compatible) {
|
|
123
|
-
|
|
125
|
+
log('Found existing compatible Zig executable:', zig_exe);
|
|
124
126
|
return zig_exe;
|
|
125
127
|
} else {
|
|
126
|
-
|
|
128
|
+
log(`Existing Zig executable is not compatible (${version} < ${VERSION}):`, zig_exe);
|
|
127
129
|
}
|
|
128
130
|
} catch (err) {
|
|
129
131
|
if (err.code !== 'ENOENT') throw err;
|
|
@@ -168,11 +170,11 @@ const install = async () => {
|
|
|
168
170
|
let base = path.basename(url);
|
|
169
171
|
const archive = path.join(dir, path.basename(url));
|
|
170
172
|
|
|
171
|
-
|
|
173
|
+
log(`Downloading Zig tarball from ${url} to ${archive}`);
|
|
172
174
|
try {
|
|
173
175
|
await download(url, archive, release.shasum);
|
|
174
176
|
|
|
175
|
-
|
|
177
|
+
log(`Extracting tarball and installing to ${dir}`);
|
|
176
178
|
await unpack(archive, dir);
|
|
177
179
|
if (archive.endsWith('.zip')) {
|
|
178
180
|
base = base.slice(0, -4);
|
|
@@ -187,17 +189,17 @@ const install = async () => {
|
|
|
187
189
|
} catch (err) {
|
|
188
190
|
if (err.code !== 'ENOENT') throw err;
|
|
189
191
|
}
|
|
190
|
-
|
|
192
|
+
log(`Zig${version === 'master' ? '' : ` v${version}`} executable:`, zig,
|
|
191
193
|
'(this version of Zig will be retained to speed up future updates)');
|
|
192
194
|
const [pre, post] = tty.isatty(1) ? ['\x1b[1m\x1b[35m', '\x1b[0m'] : ['', ''];
|
|
193
|
-
|
|
195
|
+
log(
|
|
194
196
|
`${pre}Consider moving the ${path.dirname(zig)} directory and adding it to your PATH`,
|
|
195
197
|
`to use Zig on other projects, or delete this if you wish to save space.${post}`
|
|
196
198
|
);
|
|
197
199
|
|
|
198
200
|
return zig;
|
|
199
201
|
} finally {
|
|
200
|
-
|
|
202
|
+
log(`Cleaning up ${archive}`);
|
|
201
203
|
try { fs.rmSync(archive); } catch {}
|
|
202
204
|
}
|
|
203
205
|
};
|
|
@@ -206,17 +208,17 @@ const install = async () => {
|
|
|
206
208
|
const downloadHeaders = async () => {
|
|
207
209
|
const include = path.resolve(process.execPath, '..', '..', 'include', 'node');
|
|
208
210
|
if (fs.existsSync(path.join(include, 'node.h'))) {
|
|
209
|
-
|
|
211
|
+
log('Node headers:', include);
|
|
210
212
|
return include;
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
const dir = path.join(ROOT, 'build', 'include');
|
|
214
216
|
const dest = path.join(dir, 'node');
|
|
215
217
|
if (fs.existsSync(path.join(dest, 'node.h'))) {
|
|
216
|
-
|
|
218
|
+
log('Node headers:', dest);
|
|
217
219
|
return dest;
|
|
218
220
|
} else {
|
|
219
|
-
|
|
221
|
+
log(`Could not find Node headers at ${include}, downloading...`);
|
|
220
222
|
}
|
|
221
223
|
|
|
222
224
|
try {
|
|
@@ -229,14 +231,14 @@ const downloadHeaders = async () => {
|
|
|
229
231
|
const targz = path.join(dir, path.basename(url));
|
|
230
232
|
const unpacked = path.join(dir, `node-${process.version}`, 'include');
|
|
231
233
|
|
|
232
|
-
|
|
234
|
+
log(`Downloading Node headers from ${url} to ${targz}`);
|
|
233
235
|
|
|
234
236
|
try {
|
|
235
237
|
await download(url, targz);
|
|
236
238
|
await unpack(targz, dir);
|
|
237
239
|
fs.renameSync(path.join(unpacked, 'node'), dest);
|
|
238
240
|
|
|
239
|
-
|
|
241
|
+
log('Node headers:', dest);
|
|
240
242
|
|
|
241
243
|
return dest;
|
|
242
244
|
} finally {
|
|
@@ -248,7 +250,7 @@ const downloadHeaders = async () => {
|
|
|
248
250
|
const downloadImportLib = async () => {
|
|
249
251
|
const library = path.resolve(process.execPath, '..', 'node.lib');
|
|
250
252
|
if (fs.existsSync(path.join(library, 'node.h'))) {
|
|
251
|
-
|
|
253
|
+
log('Node import library:', library);
|
|
252
254
|
return library;
|
|
253
255
|
}
|
|
254
256
|
|
|
@@ -256,10 +258,10 @@ const downloadImportLib = async () => {
|
|
|
256
258
|
const url = new URL('./win-x64/node.lib', process.release.headersUrl).href;
|
|
257
259
|
const dest = path.join(dir, path.basename(url));
|
|
258
260
|
if (fs.existsSync(path.join(dest))) {
|
|
259
|
-
|
|
261
|
+
log('Node import library:', dest);
|
|
260
262
|
return dest;
|
|
261
263
|
} else {
|
|
262
|
-
|
|
264
|
+
log(`Could not find Node import library at ${library}, downloading...`);
|
|
263
265
|
}
|
|
264
266
|
|
|
265
267
|
try {
|
|
@@ -268,24 +270,41 @@ const downloadImportLib = async () => {
|
|
|
268
270
|
if (err.code !== 'EEXIST') throw err;
|
|
269
271
|
}
|
|
270
272
|
|
|
271
|
-
|
|
273
|
+
log(`Downloading Node import library from ${url} to ${dest}`);
|
|
272
274
|
await download(url, dest);
|
|
273
|
-
|
|
275
|
+
log('Node import library:', dest);
|
|
274
276
|
|
|
275
277
|
return dest;
|
|
276
278
|
};
|
|
277
279
|
|
|
278
280
|
(async () => {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
281
|
+
let only = false;
|
|
282
|
+
let options = undefined;
|
|
283
|
+
let error = false;
|
|
284
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
285
|
+
switch (process.argv[i]) {
|
|
286
|
+
case "--silent": {
|
|
287
|
+
SILENT = true;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
case "--zig": {
|
|
291
|
+
if (options || only) error = true;
|
|
292
|
+
only = true;
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
default: {
|
|
296
|
+
if (options || only || !process.argv[i].startsWith('--options=')) error = true;
|
|
297
|
+
options = process.argv[i].slice(10).split(' ');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (error) {
|
|
283
302
|
console.error('Invalid arguments');
|
|
284
303
|
process.exit(1);
|
|
285
304
|
}
|
|
286
305
|
|
|
287
306
|
const zig = await install();
|
|
288
|
-
if (
|
|
307
|
+
if (only) process.exit(0);
|
|
289
308
|
|
|
290
309
|
let tmp;
|
|
291
310
|
try {
|
|
@@ -317,14 +336,18 @@ const downloadImportLib = async () => {
|
|
|
317
336
|
node = true;
|
|
318
337
|
if (!options) {
|
|
319
338
|
const addon = require(copy);
|
|
320
|
-
if (addon.options.showdown) opts.push('-Dshowdown');
|
|
321
|
-
if (addon.options.
|
|
339
|
+
if (addon.engine.options.showdown) opts.push('-Dshowdown');
|
|
340
|
+
if (addon.engine.options.log) opts.push('-Dlog');
|
|
341
|
+
if (addon.engine.options.chance) opts.push('-Dchance');
|
|
342
|
+
if (addon.engine.options.calc) opts.push('-Dcalc');
|
|
322
343
|
}
|
|
323
344
|
} else if (!options) {
|
|
324
345
|
const addon = await WebAssembly.instantiate(fs.readFileSync(copy));
|
|
325
346
|
const values = new Uint8Array(addon.instance.exports.memory.buffer);
|
|
326
347
|
if (values[addon.instance.exports.SHOWDOWN.value]) opts.push('-Dshowdown');
|
|
327
|
-
if (values[addon.instance.exports.
|
|
348
|
+
if (values[addon.instance.exports.LOG.value]) opts.push('-Dlog');
|
|
349
|
+
if (values[addon.instance.exports.CHANCE.value]) opts.push('-Dchance');
|
|
350
|
+
if (values[addon.instance.exports.CALC.value]) opts.push('-Dcalc');
|
|
328
351
|
}
|
|
329
352
|
const key = `${type} ${opts.sort().join(' ')}`;
|
|
330
353
|
if (!configs.has(key)) configs.set(key, {type, options: opts});
|
|
@@ -335,8 +358,9 @@ const downloadImportLib = async () => {
|
|
|
335
358
|
// either the default options or whatever was passed in
|
|
336
359
|
if (!configs.size) {
|
|
337
360
|
node = true;
|
|
338
|
-
|
|
339
|
-
configs.set('
|
|
361
|
+
const DEFAULT = ['-Dshowdown'];
|
|
362
|
+
configs.set('node', {type: 'node', options: options || DEFAULT});
|
|
363
|
+
configs.set('wasm', {type: 'wasm', options: options || DEFAULT});
|
|
340
364
|
}
|
|
341
365
|
|
|
342
366
|
const headers = node ? await downloadHeaders() : undefined;
|
|
@@ -355,7 +379,7 @@ const downloadImportLib = async () => {
|
|
|
355
379
|
opts.includes('-Dshowdown') || opts.includes('-Dshowdown=true') ? '-showdown' : '';
|
|
356
380
|
const flags = opts.length ? ` (${opts.join(' ')})` : '';
|
|
357
381
|
const artifact = path.join('build', 'lib', `pkmn${suffix}.${config.type}`);
|
|
358
|
-
|
|
382
|
+
log(`Building ${artifact}${flags}`);
|
|
359
383
|
sh(zig, args);
|
|
360
384
|
// TODO: ziglang/zig#14647 broke emit_to so we get to rename the file ourselves...
|
|
361
385
|
if (config.type === 'node') {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
try {
|
|
5
|
+
require('source-map-support').install();
|
|
6
|
+
} catch (err) {
|
|
7
|
+
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const {execFileSync} = require('child_process');
|
|
11
|
+
const {readFileSync} = require('fs');
|
|
12
|
+
const {join, resolve} = require('path');
|
|
13
|
+
|
|
14
|
+
const ROOT = resolve(__dirname, '..', '..');
|
|
15
|
+
const packageJSON = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
|
|
16
|
+
|
|
17
|
+
const missing = [];
|
|
18
|
+
for (const dep of ['@pkmn/img', 'html-minifier', 'mustache']) {
|
|
19
|
+
try {
|
|
20
|
+
require.resolve(dep);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
if (e.code !== 'MODULE_NOT_FOUND') throw e;
|
|
23
|
+
missing.push(`${dep}@${packageJSON.devDependencies[dep]}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let Dex = undefined;
|
|
28
|
+
try {
|
|
29
|
+
Dex = require('@pkmn/sim').Dex;
|
|
30
|
+
} catch (err) {
|
|
31
|
+
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
32
|
+
try {
|
|
33
|
+
Dex = require('@pkmn/dex').Dex;
|
|
34
|
+
} catch (e) {
|
|
35
|
+
if (e.code !== 'MODULE_NOT_FOUND') throw e;
|
|
36
|
+
missing.push(`@pkmn/dex@${packageJSON.dependencies['@pkmn/data']}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (missing.length) {
|
|
41
|
+
if (+/^v(\d+)/.exec(process.version)[1] < 16) {
|
|
42
|
+
console.error('pkmn-debug requires Node v16+');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
execFileSync('npm', ['install', ...missing, '--global'], {encoding: 'utf8'});
|
|
47
|
+
|
|
48
|
+
const node_modules = execFileSync('npm', ['root', '--global'], {encoding: 'utf8'}).trim();
|
|
49
|
+
if (!process.env.NODE_PATH || !process.env.NODE_PATH.includes(node_modules)) {
|
|
50
|
+
console.error(`NODE_PATH must include npm's global node_modules directory: '${node_modules}'`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const {Generations} = require('@pkmn/data');
|
|
56
|
+
|
|
57
|
+
const {run} = require('../../build/tools/debug');
|
|
58
|
+
|
|
59
|
+
run(new Generations(Dex)).catch(err => {
|
|
60
|
+
console.error(err.message);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
});
|
package/src/lib/binding/node.zig
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const pkmn = @import("../pkmn.zig");
|
|
3
3
|
|
|
4
|
+
const js = @import("../common/js.zig");
|
|
5
|
+
|
|
4
6
|
const assert = std.debug.assert;
|
|
5
7
|
|
|
6
8
|
const c = @cImport({
|
|
@@ -10,38 +12,49 @@ const c = @cImport({
|
|
|
10
12
|
|
|
11
13
|
export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi_value {
|
|
12
14
|
const properties = [_]c.napi_property_descriptor{
|
|
13
|
-
Property.init("
|
|
14
|
-
Property.init("bindings", .{ .value = bindings(env) }),
|
|
15
|
+
js.Property.init("engine", .{ .value = register(env) }),
|
|
15
16
|
};
|
|
16
17
|
assert(c.napi_define_properties(env, exports, properties.len, &properties) == c.napi_ok);
|
|
17
18
|
return exports;
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
pub fn register(env: c.napi_env) c.napi_value {
|
|
22
|
+
var object = js.Object.init(env);
|
|
23
|
+
const properties = [_]c.napi_property_descriptor{
|
|
24
|
+
js.Property.init("options", .{ .value = options(env) }),
|
|
25
|
+
js.Property.init("bindings", .{ .value = bindings(env) }),
|
|
26
|
+
};
|
|
27
|
+
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
28
|
+
return object;
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
fn options(env: c.napi_env) c.napi_value {
|
|
21
|
-
var object = Object.init(env);
|
|
32
|
+
var object = js.Object.init(env);
|
|
22
33
|
const properties = [_]c.napi_property_descriptor{
|
|
23
|
-
Property.init("showdown", .{ .value = Boolean.init(env, pkmn.options.showdown) }),
|
|
24
|
-
Property.init("
|
|
34
|
+
js.Property.init("showdown", .{ .value = js.Boolean.init(env, pkmn.options.showdown) }),
|
|
35
|
+
js.Property.init("log", .{ .value = js.Boolean.init(env, pkmn.options.log) }),
|
|
36
|
+
js.Property.init("chance", .{ .value = js.Boolean.init(env, pkmn.options.chance) }),
|
|
37
|
+
js.Property.init("calc", .{ .value = js.Boolean.init(env, pkmn.options.calc) }),
|
|
25
38
|
};
|
|
26
39
|
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
27
40
|
return object;
|
|
28
41
|
}
|
|
29
42
|
|
|
30
43
|
fn bindings(env: c.napi_env) c.napi_value {
|
|
31
|
-
var array = Array.init(env, .{ .length = 1 });
|
|
32
|
-
Array.set(env, array, 0, bind(env, pkmn.gen1));
|
|
44
|
+
var array = js.Array.init(env, .{ .length = 1 });
|
|
45
|
+
js.Array.set(env, array, 0, bind(env, pkmn.gen1));
|
|
33
46
|
return array;
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
fn bind(env: c.napi_env, gen: anytype) c.napi_value {
|
|
37
|
-
const
|
|
38
|
-
const logs_size = @intCast(
|
|
39
|
-
var object = Object.init(env);
|
|
50
|
+
const choices_size: u32 = @intCast(gen.CHOICES_SIZE);
|
|
51
|
+
const logs_size: u32 = @intCast(gen.LOGS_SIZE);
|
|
52
|
+
var object = js.Object.init(env);
|
|
40
53
|
const properties = [_]c.napi_property_descriptor{
|
|
41
|
-
Property.init("
|
|
42
|
-
Property.init("LOGS_SIZE", .{ .value = Number.init(env, logs_size) }),
|
|
43
|
-
Property.init("update", .{ .method = update(gen) }),
|
|
44
|
-
Property.init("choices", .{ .method = choices(gen) }),
|
|
54
|
+
js.Property.init("CHOICES_SIZE", .{ .value = js.Number.init(env, choices_size) }),
|
|
55
|
+
js.Property.init("LOGS_SIZE", .{ .value = js.Number.init(env, logs_size) }),
|
|
56
|
+
js.Property.init("update", .{ .method = update(gen) }),
|
|
57
|
+
js.Property.init("choices", .{ .method = choices(gen) }),
|
|
45
58
|
};
|
|
46
59
|
assert(c.napi_define_properties(env, object, properties.len, &properties) == c.napi_ok);
|
|
47
60
|
return object;
|
|
@@ -61,28 +74,32 @@ fn update(gen: anytype) c.napi_callback {
|
|
|
61
74
|
assert(len == @sizeOf(gen.Battle(gen.PRNG)));
|
|
62
75
|
assert(data != null);
|
|
63
76
|
|
|
64
|
-
var
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const c2 = @bitCast(pkmn.Choice, Number.get(env, argv[2], u8));
|
|
77
|
+
var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
|
|
78
|
+
const c1: pkmn.Choice = @bitCast(js.Number.get(env, argv[1], u8));
|
|
79
|
+
const c2: pkmn.Choice = @bitCast(js.Number.get(env, argv[2], u8));
|
|
68
80
|
|
|
69
81
|
var vtype: c.napi_valuetype = undefined;
|
|
70
82
|
assert(c.napi_typeof(env, argv[3], &vtype) == c.napi_ok);
|
|
71
83
|
const result = switch (vtype) {
|
|
72
|
-
c.napi_undefined, c.napi_null => battle.update(c1, c2,
|
|
84
|
+
c.napi_undefined, c.napi_null => battle.update(c1, c2, &gen.NULL),
|
|
73
85
|
else => result: {
|
|
74
86
|
assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
|
|
75
87
|
assert(len == gen.LOGS_SIZE);
|
|
76
88
|
assert(data != null);
|
|
77
89
|
|
|
78
|
-
var buf = @
|
|
79
|
-
var stream
|
|
80
|
-
|
|
81
|
-
|
|
90
|
+
var buf = @as([*]u8, @ptrCast(data.?))[0..gen.LOGS_SIZE];
|
|
91
|
+
var stream: pkmn.protocol.ByteStream = .{ .buffer = buf };
|
|
92
|
+
// TODO: extract out
|
|
93
|
+
var opts = pkmn.battle.options(
|
|
94
|
+
pkmn.protocol.FixedLog{ .writer = stream.writer() },
|
|
95
|
+
gen.chance.NULL,
|
|
96
|
+
gen.calc.NULL,
|
|
97
|
+
);
|
|
98
|
+
break :result battle.update(c1, c2, &opts);
|
|
82
99
|
},
|
|
83
100
|
} catch unreachable;
|
|
84
101
|
|
|
85
|
-
return Number.init(env, @
|
|
102
|
+
return js.Number.init(env, @as(u8, @bitCast(result)));
|
|
86
103
|
}
|
|
87
104
|
}.call;
|
|
88
105
|
}
|
|
@@ -101,129 +118,17 @@ fn choices(gen: anytype) c.napi_callback {
|
|
|
101
118
|
assert(len == @sizeOf(gen.Battle(gen.PRNG)));
|
|
102
119
|
assert(data != null);
|
|
103
120
|
|
|
104
|
-
var
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const player = @intToEnum(pkmn.Player, Number.get(env, argv[1], u8));
|
|
108
|
-
const request = @intToEnum(pkmn.Choice.Type, Number.get(env, argv[2], u8));
|
|
121
|
+
var battle: *gen.Battle(gen.PRNG) = @alignCast(@ptrCast(data.?));
|
|
122
|
+
const player: pkmn.Player = @enumFromInt(js.Number.get(env, argv[1], u8));
|
|
123
|
+
const request: pkmn.Choice.Type = @enumFromInt(js.Number.get(env, argv[2], u8));
|
|
109
124
|
|
|
110
125
|
assert(c.napi_get_arraybuffer_info(env, argv[3], &data, &len) == c.napi_ok);
|
|
111
|
-
assert(len == gen.
|
|
126
|
+
assert(len == gen.CHOICES_SIZE);
|
|
112
127
|
assert(data != null);
|
|
113
128
|
|
|
114
|
-
var out = @
|
|
129
|
+
var out = @as([*]pkmn.Choice, @ptrCast(data.?))[0..gen.CHOICES_SIZE];
|
|
115
130
|
const n = battle.choices(player, request, out);
|
|
116
|
-
return Number.init(env, @
|
|
131
|
+
return js.Number.init(env, @as(u8, @bitCast(n)));
|
|
117
132
|
}
|
|
118
133
|
}.call;
|
|
119
134
|
}
|
|
120
|
-
|
|
121
|
-
const Array = struct {
|
|
122
|
-
fn init(env: c.napi_env, o: struct { length: ?usize }) c.napi_value {
|
|
123
|
-
var result: c.napi_value = undefined;
|
|
124
|
-
assert(c.napi_ok == if (o.length) |n|
|
|
125
|
-
c.napi_create_array_with_length(env, n, &result)
|
|
126
|
-
else
|
|
127
|
-
c.napi_create_array(env, &result));
|
|
128
|
-
return result;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
fn set(env: c.napi_env, array: c.napi_value, index: u32, value: c.napi_value) void {
|
|
132
|
-
assert(c.napi_set_element(env, array, index, value) == c.napi_ok);
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const Boolean = struct {
|
|
137
|
-
fn init(env: c.napi_env, value: bool) c.napi_value {
|
|
138
|
-
var result: c.napi_value = undefined;
|
|
139
|
-
assert(c.napi_get_boolean(env, value, &result) == c.napi_ok);
|
|
140
|
-
return result;
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const Number = struct {
|
|
145
|
-
fn init(env: c.napi_env, value: anytype) c.napi_value {
|
|
146
|
-
const T = @TypeOf(value);
|
|
147
|
-
var result: c.napi_value = undefined;
|
|
148
|
-
assert(c.napi_ok == switch (@typeInfo(T)) {
|
|
149
|
-
.Int => |info| switch (info.bits) {
|
|
150
|
-
0...32 => switch (info.signedness) {
|
|
151
|
-
.signed => c.napi_create_int32(env, @as(i32, value), &result),
|
|
152
|
-
.unsigned => c.napi_create_uint32(env, @as(u32, value), &result),
|
|
153
|
-
},
|
|
154
|
-
33...52 => c.napi_create_int64(env, @as(i64, value), &result),
|
|
155
|
-
else => @compileError("int can't be represented as JS number"),
|
|
156
|
-
},
|
|
157
|
-
else => @compileError("expected number, got: " ++ @typeName(T)),
|
|
158
|
-
});
|
|
159
|
-
return result;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
fn get(env: c.napi_env, value: c.napi_value, comptime T: type) T {
|
|
163
|
-
switch (@typeInfo(T)) {
|
|
164
|
-
.Int => |info| switch (info.bits) {
|
|
165
|
-
0...32 => switch (info.signedness) {
|
|
166
|
-
.signed => {
|
|
167
|
-
var result: i32 = undefined;
|
|
168
|
-
assert(c.napi_get_value_int32(env, value, &result) == c.napi_ok);
|
|
169
|
-
return if (info.bits == 32) result else @intCast(T, result);
|
|
170
|
-
},
|
|
171
|
-
.unsigned => {
|
|
172
|
-
var result: u32 = undefined;
|
|
173
|
-
assert(c.napi_get_value_uint32(env, value, &result) == c.napi_ok);
|
|
174
|
-
return if (info.bits == 32) result else @intCast(T, result);
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
33...63 => {
|
|
178
|
-
var result: i64 = undefined;
|
|
179
|
-
assert(c.napi_get_value_int64(env, value, &result) == c.napi_ok);
|
|
180
|
-
return @intCast(T, result);
|
|
181
|
-
},
|
|
182
|
-
else => {
|
|
183
|
-
var result: i64 = undefined;
|
|
184
|
-
assert(c.napi_get_value_int64(env, value, &result) == c.napi_ok);
|
|
185
|
-
return switch (info.signedness) {
|
|
186
|
-
.signed => @as(T, value),
|
|
187
|
-
.unsigned => if (0 <= value) @intCast(T, value) else unreachable,
|
|
188
|
-
};
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
else => @compileError("expected number, got: " ++ @typeName(T)),
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const Object = struct {
|
|
197
|
-
fn init(env: c.napi_env) c.napi_value {
|
|
198
|
-
var result: c.napi_value = undefined;
|
|
199
|
-
assert(c.napi_create_object(env, &result) == c.napi_ok);
|
|
200
|
-
return result;
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const Property = union(enum) {
|
|
205
|
-
method: c.napi_callback,
|
|
206
|
-
value: c.napi_value,
|
|
207
|
-
|
|
208
|
-
fn init(comptime name: [:0]const u8, property: Property) c.napi_property_descriptor {
|
|
209
|
-
return .{
|
|
210
|
-
.utf8name = name,
|
|
211
|
-
.name = null,
|
|
212
|
-
.method = switch (property) {
|
|
213
|
-
.method => |m| m,
|
|
214
|
-
.value => null,
|
|
215
|
-
},
|
|
216
|
-
.getter = null,
|
|
217
|
-
.setter = null,
|
|
218
|
-
.value = switch (property) {
|
|
219
|
-
.method => null,
|
|
220
|
-
.value => |v| v,
|
|
221
|
-
},
|
|
222
|
-
.attributes = switch (property) {
|
|
223
|
-
.method => c.napi_default,
|
|
224
|
-
.value => c.napi_enumerable,
|
|
225
|
-
},
|
|
226
|
-
.data = null,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
};
|
package/src/lib/binding/wasm.zig
CHANGED
|
@@ -2,9 +2,11 @@ const std = @import("std");
|
|
|
2
2
|
const pkmn = @import("../pkmn.zig");
|
|
3
3
|
|
|
4
4
|
export const SHOWDOWN = pkmn.options.showdown;
|
|
5
|
-
export const
|
|
5
|
+
export const LOG = pkmn.options.log;
|
|
6
|
+
export const CHANCE = pkmn.options.chance;
|
|
7
|
+
export const CALC = pkmn.options.calc;
|
|
6
8
|
|
|
7
|
-
export const
|
|
8
|
-
std.math.ceilPowerOfTwo(u32, @
|
|
9
|
+
export const GEN1_CHOICES_SIZE =
|
|
10
|
+
std.math.ceilPowerOfTwo(u32, @as(u32, @intCast(pkmn.gen1.CHOICES_SIZE))) catch unreachable;
|
|
9
11
|
export const GEN1_LOGS_SIZE =
|
|
10
|
-
std.math.ceilPowerOfTwo(u32, @
|
|
12
|
+
std.math.ceilPowerOfTwo(u32, @as(u32, @intCast(pkmn.gen1.LOGS_SIZE))) catch unreachable;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/// Stores state required by optional features to the engine that can be passed to a generation's
|
|
2
|
+
/// `Battle.update` function. All generations generically support protocol logging (enabled via
|
|
3
|
+
/// `-Dlog`), chance actions and probability tracking (enabled via `-Dchance`), and damage
|
|
4
|
+
/// computation and chance overrides (`-Dcalc`), though the concrete types for these features are
|
|
5
|
+
/// generation dependent.
|
|
6
|
+
pub fn Options(comptime Log: type, comptime Chance: type, comptime Calc: type) type {
|
|
7
|
+
return struct { log: Log, chance: Chance, calc: Calc };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/// Helper to create a battle `Options` object with the provided `log`, `chance`, and `calc`.
|
|
11
|
+
pub fn options(
|
|
12
|
+
log: anytype,
|
|
13
|
+
chance: anytype,
|
|
14
|
+
calc: anytype,
|
|
15
|
+
) Options(@TypeOf(log), @TypeOf(chance), @TypeOf(calc)) {
|
|
16
|
+
return .{ .log = log, .chance = chance, .calc = calc };
|
|
17
|
+
}
|