yeow-api 0.3.0 → 0.3.5
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/package.json +1 -1
- package/src/command.ts +7 -17
- package/src/core.ts +6 -0
- package/src/event.ts +35 -21
- package/src/player.ts +9 -1
- package/src/util.ts +77 -0
package/package.json
CHANGED
package/src/command.ts
CHANGED
|
@@ -79,23 +79,13 @@ export function registerCommand(name: string, options: CommandOptions, taskOptio
|
|
|
79
79
|
} else {
|
|
80
80
|
const result = completerFn(sender, data.args);
|
|
81
81
|
if (result && typeof result.then === 'function') {
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
},
|
|
91
|
-
() => {
|
|
92
|
-
$send('task', {
|
|
93
|
-
type: 'command.tabComplete',
|
|
94
|
-
params: { callbackId: compCbId, completions: [] },
|
|
95
|
-
cb: '',
|
|
96
|
-
});
|
|
97
|
-
},
|
|
98
|
-
);
|
|
82
|
+
// 自动模式不等待 Promise(与事件处理一致):收到 Promise 视为无补全,
|
|
83
|
+
// 立即释放(空补全);异步补全请使用手动模式 complete(res)
|
|
84
|
+
$send('task', {
|
|
85
|
+
type: 'command.tabComplete',
|
|
86
|
+
params: { callbackId: compCbId, completions: [] },
|
|
87
|
+
cb: '',
|
|
88
|
+
});
|
|
99
89
|
} else {
|
|
100
90
|
$send('task', {
|
|
101
91
|
type: 'command.tabComplete',
|
package/src/core.ts
CHANGED
|
@@ -103,3 +103,9 @@ export { registerPermission } from './permission.js';
|
|
|
103
103
|
export type { Permission, PermissionOptions, PermissionDefault } from './permission.js';
|
|
104
104
|
export { createWorker, Worker, onMessage, postMessage } from './worker.js';
|
|
105
105
|
export type { WorkerOptions } from './worker.js';
|
|
106
|
+
export {
|
|
107
|
+
stringToBytes, stringToBytesAsync,
|
|
108
|
+
bytesToString, bytesToStringAsync,
|
|
109
|
+
gzipCompress, gzipCompressSync,
|
|
110
|
+
gzipDecompress, gzipDecompressSync,
|
|
111
|
+
} from './util.js';
|
package/src/event.ts
CHANGED
|
@@ -358,11 +358,12 @@ function loc(raw: Record<string, unknown> | null): Location | null {
|
|
|
358
358
|
);
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
-
function adaptEvent<K extends keyof EventMap>(type: K, data: RawEvent): EventMap[K] {
|
|
362
|
-
|
|
361
|
+
function adaptEvent<K extends keyof EventMap>(type: K, data: RawEvent): { event: EventMap[K]; mods: Record<string, unknown> } {
|
|
362
|
+
// 初始值:原始数据(跳过 _ 前缀内部字段),player/from/to/respawnLocation 适配为对象
|
|
363
|
+
const initial: Record<string, unknown> = {};
|
|
363
364
|
for (const key of Object.keys(data)) {
|
|
364
365
|
if (key.startsWith('_')) continue;
|
|
365
|
-
|
|
366
|
+
initial[key] = data[key];
|
|
366
367
|
}
|
|
367
368
|
const hasPlayer = [
|
|
368
369
|
'playerJoin', 'playerQuit', 'playerChat', 'playerMove',
|
|
@@ -375,13 +376,34 @@ function adaptEvent<K extends keyof EventMap>(type: K, data: RawEvent): EventMap
|
|
|
375
376
|
'playerAdvancementDone', 'playerToggleSneak', 'playerToggleFlight',
|
|
376
377
|
'inventoryClick', 'playerResourcePackStatus',
|
|
377
378
|
] as K[];
|
|
378
|
-
|
|
379
|
-
|
|
379
|
+
// 直接以 uuid 构造 Player(零往返);name 在首次访问时惰性获取(见 Player.name)
|
|
380
|
+
if (hasPlayer.includes(type) && data.player) initial.player = new Player(data.player as string);
|
|
381
|
+
if (data.from) initial.from = loc(data.from as Record<string, unknown>);
|
|
382
|
+
if (data.to) initial.to = loc(data.to as Record<string, unknown>);
|
|
383
|
+
if (data.respawnLocation) initial.respawnLocation = loc(data.respawnLocation as Record<string, unknown>);
|
|
384
|
+
|
|
385
|
+
// 修改收集:所有字段经 getter/setter——handler 直接赋值(e.xxx = ...)即记录为回写 mods。
|
|
386
|
+
// cancelled 单独处理(仅可取消事件暴露;读取语义保持原状:未设置时返回 false)。
|
|
387
|
+
const mods: Record<string, unknown> = {};
|
|
388
|
+
const wrap: Record<string, unknown> = {};
|
|
389
|
+
for (const key of Object.keys(initial)) {
|
|
390
|
+
if (key === 'cancelled') continue;
|
|
391
|
+
Object.defineProperty(wrap, key, {
|
|
392
|
+
get: () => (key in mods ? mods[key] : initial[key]),
|
|
393
|
+
set: (v: unknown) => { mods[key] = v; },
|
|
394
|
+
enumerable: true,
|
|
395
|
+
configurable: true,
|
|
396
|
+
});
|
|
380
397
|
}
|
|
381
|
-
if (data.
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
398
|
+
if (data._cancellable) {
|
|
399
|
+
Object.defineProperty(wrap, 'cancelled', {
|
|
400
|
+
get: () => (mods.cancelled as boolean) || false,
|
|
401
|
+
set: (v: boolean) => { mods.cancelled = v; },
|
|
402
|
+
enumerable: true,
|
|
403
|
+
configurable: true,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
return { event: wrap as unknown as EventMap[K], mods };
|
|
385
407
|
}
|
|
386
408
|
|
|
387
409
|
// ── Event Subscription ─────────────────────────────────────────────
|
|
@@ -411,18 +433,8 @@ export function eventOn<K extends keyof EventMap>(
|
|
|
411
433
|
const pluginName = __plugin?.name || 'unknown';
|
|
412
434
|
|
|
413
435
|
const cbId = _registerCallback((data: RawEvent) => {
|
|
414
|
-
const wrapped = adaptEvent(eventType, data);
|
|
436
|
+
const { event: wrapped, mods: mutatedMods } = adaptEvent(eventType, data);
|
|
415
437
|
const eventId = data?._eventId;
|
|
416
|
-
const cancellable = data?._cancellable;
|
|
417
|
-
const localMods: { cancelled?: boolean } = {};
|
|
418
|
-
|
|
419
|
-
if (cancellable) {
|
|
420
|
-
Object.defineProperty(wrapped, 'cancelled', {
|
|
421
|
-
get: () => localMods.cancelled || false,
|
|
422
|
-
set: (v: boolean) => { localMods.cancelled = v; },
|
|
423
|
-
enumerable: true,
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
438
|
|
|
427
439
|
if (manualRelease) {
|
|
428
440
|
const complete = (result?: Record<string, unknown>) => {
|
|
@@ -437,10 +449,12 @@ export function eventOn<K extends keyof EventMap>(
|
|
|
437
449
|
}
|
|
438
450
|
|
|
439
451
|
const result = (handler as EventHandler<typeof eventType>)(wrapped);
|
|
452
|
+
// 回写合并:返回值(mods)优先合并,事件参数直接赋值(e.xxx = ...)覆盖之——
|
|
453
|
+
// 返回 Promise 时视为无修改(Promise 不展开),事件立即释放
|
|
440
454
|
const mods: Record<string, unknown> = {
|
|
441
455
|
...(result && typeof result === 'object' ? result : {}),
|
|
456
|
+
...mutatedMods,
|
|
442
457
|
};
|
|
443
|
-
if (localMods.cancelled) mods.cancelled = true;
|
|
444
458
|
$send('task', { type: 'event.complete', params: { eventId, mods }, cb: '' });
|
|
445
459
|
}, { persistent: true });
|
|
446
460
|
|
package/src/player.ts
CHANGED
|
@@ -32,7 +32,15 @@ export class Player {
|
|
|
32
32
|
|
|
33
33
|
constructor(public readonly uuid: string, private _name?: string) {}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
/** 玩家名。未提供时首次访问惰性同步获取并缓存(仅一次往返)。 */
|
|
36
|
+
get name(): string {
|
|
37
|
+
if (this._name === undefined) {
|
|
38
|
+
let n = '';
|
|
39
|
+
try { n = call<PlayerData>('player.get', { identifier: this.uuid })?.name ?? ''; } catch { /* 离线/异常 → '' */ }
|
|
40
|
+
this._name = n;
|
|
41
|
+
}
|
|
42
|
+
return this._name;
|
|
43
|
+
}
|
|
36
44
|
|
|
37
45
|
/** 玩家物品栏(统一 Inventory 容器抽象)。 */
|
|
38
46
|
get inventory(): Inventory {
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// util 通道:gzip 压缩/解压 + UTF-8 ↔ 字节转换。
|
|
2
|
+
//
|
|
3
|
+
// 协议层字节数据以 base64 字符串承载(引擎原生 Uint8Array.toBase64()/fromBase64()
|
|
4
|
+
// 负责转换)——本模块输入输出一律 Uint8Array / string,**不暴露 base64**。
|
|
5
|
+
// encode/decode 的语义是 buffer ↔ 字符串;无流式接口(一次性整体处理)。
|
|
6
|
+
|
|
7
|
+
/** util 通道同步调用:err → 抛 Error。 */
|
|
8
|
+
function send<T>(t: string, p: Record<string, unknown>): T {
|
|
9
|
+
const r = $send('util', { t, p }) as T | { err?: string } | null;
|
|
10
|
+
if (r == null) return undefined as T;
|
|
11
|
+
if ((r as { err?: string }).err) throw new Error((r as { err?: string }).err);
|
|
12
|
+
return r as T;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** util 通道异步调用(ioExecutor 上执行,不阻塞 JS 线程)。 */
|
|
16
|
+
function sendAsync<T>(t: string, p: Record<string, unknown>): Promise<T> {
|
|
17
|
+
return new Promise<T>((resolve, reject) => {
|
|
18
|
+
const cbId = _registerCallback((r: T | { err?: string }) => {
|
|
19
|
+
if ((r as { err?: string })?.err) reject(new Error((r as { err?: string }).err));
|
|
20
|
+
else resolve(r as T);
|
|
21
|
+
});
|
|
22
|
+
$send('util', { t, p, cb: cbId });
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 输入规范化:string 视为 UTF-8 文本(经 encode.utf8 字节化);Uint8Array 直接转换。 */
|
|
27
|
+
function toB64(data: Uint8Array | string): string {
|
|
28
|
+
return typeof data === 'string'
|
|
29
|
+
? send<{ data: string }>('encode.utf8', { data }).data
|
|
30
|
+
: data.toBase64();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── 字符串 ↔ 字节(UTF-8)────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
/** UTF-8 字符串 → 字节(同步)。 */
|
|
36
|
+
export function stringToBytes(text: string): Uint8Array {
|
|
37
|
+
return Uint8Array.fromBase64(send<{ data: string }>('encode.utf8', { data: text }).data);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 字节 → UTF-8 字符串(同步;非法序列替换为 U+FFFD,不抛错)。 */
|
|
41
|
+
export function bytesToString(bytes: Uint8Array): string {
|
|
42
|
+
return send<{ data: string }>('decode.utf8', { data: bytes.toBase64() }).data;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** UTF-8 字符串 → 字节(异步,ioExecutor 执行)。 */
|
|
46
|
+
export function stringToBytesAsync(text: string): Promise<Uint8Array> {
|
|
47
|
+
return sendAsync<{ data: string }>('encode.utf8', { data: text }).then((r) => Uint8Array.fromBase64(r.data));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 字节 → UTF-8 字符串(异步)。 */
|
|
51
|
+
export function bytesToStringAsync(bytes: Uint8Array): Promise<string> {
|
|
52
|
+
return sendAsync<{ data: string }>('decode.utf8', { data: bytes.toBase64() }).then((r) => r.data);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── gzip ──────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
/** gzip 压缩(level 0-9,默认引擎默认级别)。输入 string 视为 UTF-8 文本。 */
|
|
58
|
+
export function gzipCompress(data: Uint8Array | string, level?: number): Promise<Uint8Array> {
|
|
59
|
+
return sendAsync<{ data: string }>('gzip.compress', { data: toB64(data), level })
|
|
60
|
+
.then((r) => Uint8Array.fromBase64(r.data));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** gzip 压缩(同步版)。 */
|
|
64
|
+
export function gzipCompressSync(data: Uint8Array | string, level?: number): Uint8Array {
|
|
65
|
+
return Uint8Array.fromBase64(send<{ data: string }>('gzip.compress', { data: toB64(data), level }).data);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** gzip 解压(输出上限 256 MiB,超限报错——防压缩炸弹)。 */
|
|
69
|
+
export function gzipDecompress(data: Uint8Array | string): Promise<Uint8Array> {
|
|
70
|
+
return sendAsync<{ data: string }>('gzip.decompress', { data: toB64(data) })
|
|
71
|
+
.then((r) => Uint8Array.fromBase64(r.data));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** gzip 解压(同步版)。 */
|
|
75
|
+
export function gzipDecompressSync(data: Uint8Array | string): Uint8Array {
|
|
76
|
+
return Uint8Array.fromBase64(send<{ data: string }>('gzip.decompress', { data: toB64(data) }).data);
|
|
77
|
+
}
|