yeow-api 0.2.31 → 0.2.33

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/src/event.ts CHANGED
@@ -2,397 +2,349 @@ import { call } from './task.js';
2
2
  import { Player } from './player.js';
3
3
  import { Location } from './location.js';
4
4
 
5
- // ── Event Typed Interfaces ─────────────────────────────────────────
5
+ // ── Event Data Interfaces ──────────────────────────────────────────
6
6
 
7
7
  export interface PlayerJoinEvent {
8
- player: Player;
9
- joinMessage: string;
8
+ player: Player;
9
+ joinMessage: string;
10
10
  }
11
-
12
11
  export interface PlayerQuitEvent {
13
- player: Player;
14
- quitMessage: string;
12
+ player: Player;
13
+ quitMessage: string;
15
14
  }
16
-
17
15
  export interface PlayerChatEvent {
18
- player: Player;
19
- message: string;
20
- format: string;
16
+ player: Player;
17
+ message: string;
18
+ format: string;
21
19
  }
22
-
23
20
  export interface PlayerMoveEvent {
24
- player: Player;
25
- from: Location;
26
- to: Location;
21
+ player: Player;
22
+ from: Location;
23
+ to: Location;
27
24
  }
28
-
29
25
  export interface PlayerInteractEvent {
30
- player: Player;
31
- action: string;
32
- material: string | null;
33
- block: { x: number; y: number; z: number; type: string } | null;
26
+ player: Player;
27
+ action: string;
28
+ material: string | null;
29
+ block: { x: number; y: number; z: number; type: string } | null;
34
30
  }
35
-
36
31
  export interface PlayerCommandEvent {
37
- player: Player;
38
- message: string;
32
+ player: Player;
33
+ message: string;
39
34
  }
40
-
41
35
  export interface PlayerDeathEvent {
42
- player: Player;
43
- deathMessage: string;
44
- deathType: string;
36
+ player: Player;
37
+ deathMessage: string;
38
+ deathType: string;
45
39
  }
46
-
47
40
  export interface PlayerRespawnEvent {
48
- player: Player;
49
- respawnLocation: Location;
41
+ player: Player;
42
+ respawnLocation: Location;
50
43
  }
51
-
52
44
  export interface PlayerDropItemEvent {
53
- player: Player;
54
- itemType: string;
55
- amount: number;
45
+ player: Player;
46
+ itemType: string;
47
+ amount: number;
56
48
  }
57
-
58
49
  export interface PlayerPickupItemEvent {
59
- player: Player;
60
- itemType: string;
61
- amount: number;
50
+ player: Player;
51
+ itemType: string;
52
+ amount: number;
62
53
  }
63
-
64
54
  export interface PlayerBucketFillEvent {
65
- player: Player;
66
- bucket: string;
55
+ player: Player;
56
+ bucket: string;
67
57
  }
68
-
69
58
  export interface PlayerBucketEmptyEvent {
70
- player: Player;
71
- bucket: string;
59
+ player: Player;
60
+ bucket: string;
72
61
  }
73
-
74
62
  export interface PlayerExpChangeEvent {
75
- player: Player;
76
- amount: number;
63
+ player: Player;
64
+ amount: number;
77
65
  }
78
-
79
66
  export interface PlayerLevelChangeEvent {
80
- player: Player;
81
- oldLevel: number;
82
- newLevel: number;
67
+ player: Player;
68
+ oldLevel: number;
69
+ newLevel: number;
83
70
  }
84
-
85
71
  export interface PlayerGameModeChangeEvent {
86
- player: Player;
87
- newGameMode: string;
72
+ player: Player;
73
+ newGameMode: string;
88
74
  }
89
-
90
75
  export interface FoodLevelChangeEvent {
91
- player: Player;
92
- oldFoodLevel: number;
93
- newFoodLevel: number;
76
+ player: Player;
77
+ oldFoodLevel: number;
78
+ newFoodLevel: number;
94
79
  }
95
-
96
80
  export interface EntityDamageEvent {
97
- entity: string;
98
- damage: number;
99
- cause: string;
100
- entityType: string;
81
+ entity: string;
82
+ damage: number;
83
+ cause: string;
84
+ entityType: string;
101
85
  }
102
-
103
86
  export interface EntityDeathEvent {
104
- entity: string;
105
- entityType: string;
106
- entityName: string;
87
+ entity: string;
88
+ entityType: string;
89
+ entityName: string;
107
90
  }
108
-
109
91
  export interface EntitySpawnEvent {
110
- entity: string;
111
- entityType: string;
112
- x: number;
113
- y: number;
114
- z: number;
115
- world: string;
92
+ entity: string;
93
+ entityType: string;
94
+ x: number;
95
+ y: number;
96
+ z: number;
97
+ world: string;
116
98
  }
117
-
118
99
  export interface ProjectileLaunchEvent {
119
- entity: string;
120
- projectileType: string;
121
- shooter?: string;
100
+ entity: string;
101
+ projectileType: string;
102
+ shooter?: string;
122
103
  }
123
-
124
104
  export interface BlockBreakEvent {
125
- player: Player;
126
- block: string;
127
- x: number;
128
- y: number;
129
- z: number;
105
+ player: Player;
106
+ block: string;
107
+ x: number;
108
+ y: number;
109
+ z: number;
130
110
  }
131
-
132
111
  export interface BlockPlaceEvent {
133
- player: Player;
134
- block: string;
135
- blockAgainst: string;
136
- x: number;
137
- y: number;
138
- z: number;
112
+ player: Player;
113
+ block: string;
114
+ blockAgainst: string;
115
+ x: number;
116
+ y: number;
117
+ z: number;
139
118
  }
140
-
141
119
  export interface InventoryOpenEvent {
142
- player: Player;
143
- inventoryType: string;
144
- title: string;
120
+ player: Player;
121
+ inventoryType: string;
122
+ title: string;
145
123
  }
146
-
147
124
  export interface InventoryCloseEvent {
148
- player: Player;
149
- inventoryType: string;
125
+ player: Player;
126
+ inventoryType: string;
150
127
  }
151
-
152
128
  export interface ServerPingEvent {
153
- address: string;
154
- numPlayers: number;
155
- maxPlayers: number;
156
- motd: string;
129
+ address: string;
130
+ numPlayers: number;
131
+ maxPlayers: number;
132
+ motd: string;
157
133
  }
158
-
159
134
  export interface PlayerTeleportEvent {
160
- player: Player;
161
- from: Location;
162
- to: Location;
163
- cause: string;
135
+ player: Player;
136
+ from: Location;
137
+ to: Location;
138
+ cause: string;
164
139
  }
165
-
166
140
  export interface PlayerItemConsumeEvent {
167
- player: Player;
168
- itemType: string;
141
+ player: Player;
142
+ itemType: string;
169
143
  }
170
-
171
144
  export interface EntityExplodeEvent {
172
- entity: string;
173
- entityType: string;
174
- x: number;
175
- y: number;
176
- z: number;
177
- blockCount: number;
145
+ entity: string;
146
+ entityType: string;
147
+ x: number;
148
+ y: number;
149
+ z: number;
150
+ blockCount: number;
178
151
  }
179
-
180
152
  export interface EntityRegainHealthEvent {
181
- entity: string;
182
- amount: number;
183
- reason: string;
153
+ entity: string;
154
+ amount: number;
155
+ reason: string;
184
156
  }
185
-
186
157
  export interface EntityTargetEvent {
187
- entity: string;
188
- target: string | null;
158
+ entity: string;
159
+ target: string | null;
189
160
  }
190
-
191
161
  export interface ProjectileHitEvent {
192
- entity: string;
193
- projectileType: string;
194
- hitEntity: string | null;
195
- hitBlock: { x: number; y: number; z: number; type: string } | null;
162
+ entity: string;
163
+ projectileType: string;
164
+ hitEntity: string | null;
165
+ hitBlock: { x: number; y: number; z: number; type: string } | null;
196
166
  }
197
-
198
167
  export interface BlockFadeEvent {
199
- block: string;
200
- x: number;
201
- y: number;
202
- z: number;
168
+ block: string;
169
+ x: number;
170
+ y: number;
171
+ z: number;
203
172
  }
204
-
205
173
  export interface BlockGrowEvent {
206
- block: string;
207
- x: number;
208
- y: number;
209
- z: number;
174
+ block: string;
175
+ x: number;
176
+ y: number;
177
+ z: number;
210
178
  }
211
-
212
179
  export interface BlockSpreadEvent {
213
- block: string;
214
- x: number;
215
- y: number;
216
- z: number;
180
+ block: string;
181
+ x: number;
182
+ y: number;
183
+ z: number;
217
184
  }
218
-
219
185
  export interface BlockExplodeEvent {
220
- block: string;
221
- x: number;
222
- y: number;
223
- z: number;
186
+ block: string;
187
+ x: number;
188
+ y: number;
189
+ z: number;
224
190
  }
225
-
226
191
  export interface ServerCommandEvent {
227
- command: string;
228
- sender: string;
192
+ command: string;
193
+ sender: string;
229
194
  }
230
195
 
231
- // ── Event Type Mapping ─────────────────────────────────────────────
232
-
233
196
  type EventMap = {
234
- playerJoin: PlayerJoinEvent;
235
- playerQuit: PlayerQuitEvent;
236
- playerChat: PlayerChatEvent;
237
- playerMove: PlayerMoveEvent;
238
- playerInteract: PlayerInteractEvent;
239
- playerCommand: PlayerCommandEvent;
240
- playerDeath: PlayerDeathEvent;
241
- playerRespawn: PlayerRespawnEvent;
242
- playerDropItem: PlayerDropItemEvent;
243
- playerPickupItem: PlayerPickupItemEvent;
244
- playerBucketFill: PlayerBucketFillEvent;
245
- playerBucketEmpty: PlayerBucketEmptyEvent;
246
- playerExpChange: PlayerExpChangeEvent;
247
- playerLevelChange: PlayerLevelChangeEvent;
248
- playerGameModeChange: PlayerGameModeChangeEvent;
249
- foodLevelChange: FoodLevelChangeEvent;
250
- entityDamage: EntityDamageEvent;
251
- entityDeath: EntityDeathEvent;
252
- entitySpawn: EntitySpawnEvent;
253
- projectileLaunch: ProjectileLaunchEvent;
254
- blockBreak: BlockBreakEvent;
255
- blockPlace: BlockPlaceEvent;
256
- blockFade: BlockFadeEvent;
257
- blockGrow: BlockGrowEvent;
258
- blockSpread: BlockSpreadEvent;
259
- blockExplode: BlockExplodeEvent;
260
- inventoryOpen: InventoryOpenEvent;
261
- inventoryClose: InventoryCloseEvent;
262
- serverPing: ServerPingEvent;
263
- serverCommand: ServerCommandEvent;
264
- playerTeleport: PlayerTeleportEvent;
265
- playerItemConsume: PlayerItemConsumeEvent;
266
- entityExplode: EntityExplodeEvent;
267
- entityRegainHealth: EntityRegainHealthEvent;
268
- entityTarget: EntityTargetEvent;
269
- projectileHit: ProjectileHitEvent;
197
+ playerJoin: PlayerJoinEvent;
198
+ playerQuit: PlayerQuitEvent;
199
+ playerChat: PlayerChatEvent;
200
+ playerMove: PlayerMoveEvent;
201
+ playerInteract: PlayerInteractEvent;
202
+ playerCommand: PlayerCommandEvent;
203
+ playerDeath: PlayerDeathEvent;
204
+ playerRespawn: PlayerRespawnEvent;
205
+ playerDropItem: PlayerDropItemEvent;
206
+ playerPickupItem: PlayerPickupItemEvent;
207
+ playerBucketFill: PlayerBucketFillEvent;
208
+ playerBucketEmpty: PlayerBucketEmptyEvent;
209
+ playerExpChange: PlayerExpChangeEvent;
210
+ playerLevelChange: PlayerLevelChangeEvent;
211
+ playerGameModeChange: PlayerGameModeChangeEvent;
212
+ foodLevelChange: FoodLevelChangeEvent;
213
+ entityDamage: EntityDamageEvent;
214
+ entityDeath: EntityDeathEvent;
215
+ entitySpawn: EntitySpawnEvent;
216
+ entityExplode: EntityExplodeEvent;
217
+ entityRegainHealth: EntityRegainHealthEvent;
218
+ entityTarget: EntityTargetEvent;
219
+ projectileLaunch: ProjectileLaunchEvent;
220
+ projectileHit: ProjectileHitEvent;
221
+ blockBreak: BlockBreakEvent;
222
+ blockPlace: BlockPlaceEvent;
223
+ blockFade: BlockFadeEvent;
224
+ blockGrow: BlockGrowEvent;
225
+ blockSpread: BlockSpreadEvent;
226
+ blockExplode: BlockExplodeEvent;
227
+ inventoryOpen: InventoryOpenEvent;
228
+ inventoryClose: InventoryCloseEvent;
229
+ serverPing: ServerPingEvent;
230
+ serverCommand: ServerCommandEvent;
231
+ playerTeleport: PlayerTeleportEvent;
232
+ playerItemConsume: PlayerItemConsumeEvent;
270
233
  };
271
234
 
272
- // ── Event Data Adapter ──────────────────────────────────────────────
273
-
274
- function _loc(raw: any): Location | null {
275
- if (!raw) return null;
276
- return new Location(raw.x, raw.y, raw.z, raw.yaw ?? 0, raw.pitch ?? 0, raw.world);
277
- }
278
-
279
- function adaptEvent(type: string, data: any): any {
280
- if (!data) return data;
281
- const wrap: any = {};
282
- for (const key of Object.keys(data)) {
283
- if (key.startsWith('_')) continue;
284
- wrap[key] = data[key];
285
- }
286
- switch (type) {
287
- case 'playerJoin': case 'playerQuit': case 'playerChat': case 'playerMove':
288
- case 'playerInteract': case 'playerCommand': case 'playerDeath': case 'playerRespawn':
289
- case 'playerTeleport': case 'playerItemConsume':
290
- case 'playerDropItem': case 'playerPickupItem': case 'playerBucketFill': case 'playerBucketEmpty':
291
- case 'playerExpChange': case 'playerLevelChange': case 'playerGameModeChange': case 'foodLevelChange':
292
- case 'blockBreak': case 'blockPlace': case 'inventoryOpen': case 'inventoryClose':
293
- if (data.player) wrap.player = Player.get(data.player);
294
- break;
295
- }
296
- if (data.from) wrap.from = _loc(data.from);
297
- if (data.to) wrap.to = _loc(data.to);
298
- if (data.respawnLocation) wrap.respawnLocation = _loc(data.respawnLocation);
299
- return wrap;
300
- }
301
-
302
- // ── EventOn/EventOff ───────────────────────────────────────────────
235
+ type RawEvent = Record<string, unknown> & { _cancellable?: boolean };
236
+
237
+ function loc(raw: Record<string, unknown> | null): Location | null {
238
+ if (!raw) return null;
239
+ return new Location(
240
+ raw.x as number, raw.y as number, raw.z as number,
241
+ (raw.yaw as number) ?? 0, (raw.pitch as number) ?? 0, raw.world as string,
242
+ );
243
+ }
244
+
245
+ function adaptEvent<K extends keyof EventMap>(type: K, data: RawEvent): EventMap[K] {
246
+ const wrap: Record<string, unknown> = {};
247
+ for (const key of Object.keys(data)) {
248
+ if (key.startsWith('_')) continue;
249
+ wrap[key] = data[key];
250
+ }
251
+ const hasPlayer = [
252
+ 'playerJoin', 'playerQuit', 'playerChat', 'playerMove',
253
+ 'playerInteract', 'playerCommand', 'playerDeath', 'playerRespawn',
254
+ 'playerTeleport', 'playerItemConsume',
255
+ 'playerDropItem', 'playerPickupItem', 'playerBucketFill',
256
+ 'playerBucketEmpty', 'playerExpChange', 'playerLevelChange',
257
+ 'playerGameModeChange', 'foodLevelChange', 'blockBreak',
258
+ 'blockPlace', 'inventoryOpen', 'inventoryClose',
259
+ ] as K[];
260
+ if (hasPlayer.includes(type) && data.player) {
261
+ wrap.player = Player.get(data.player as string);
262
+ }
263
+ if (data.from) wrap.from = loc(data.from as Record<string, unknown>);
264
+ if (data.to) wrap.to = loc(data.to as Record<string, unknown>);
265
+ if (data.respawnLocation) wrap.respawnLocation = loc(data.respawnLocation as Record<string, unknown>);
266
+ return wrap as unknown as EventMap[K];
267
+ }
268
+
269
+ // ── Event Subscription ─────────────────────────────────────────────
270
+
271
+ type EventHandler<K extends keyof EventMap> = (e: EventMap[K]) => void;
272
+ type ManualHandler<K extends keyof EventMap> = (
273
+ e: EventMap[K],
274
+ complete: (result?: Record<string, unknown>) => void,
275
+ ) => void;
303
276
 
304
277
  export function eventOn<K extends keyof EventMap>(
305
- eventType: K,
306
- handler: ((e: EventMap[K]) => void) | ((e: EventMap[K], release: (result?: any) => void) => void),
307
- options?: { manualRelease?: boolean }
278
+ eventType: K,
279
+ handler: EventHandler<K>,
280
+ ): () => void;
281
+ export function eventOn<K extends keyof EventMap>(
282
+ eventType: K,
283
+ options: { manualRelease: true },
284
+ handler: ManualHandler<K>,
285
+ ): () => void;
286
+ export function eventOn<K extends keyof EventMap>(
287
+ eventType: K,
288
+ handlerOrOptions: EventHandler<K> | { manualRelease?: boolean },
289
+ handlerOrUndef?: ManualHandler<K>,
308
290
  ): () => void {
309
- const wrapper = (data: any, release?: Function) => {
310
- if (options?.manualRelease) {
311
- (handler as any)(data as EventMap[K], release);
312
- } else {
313
- (handler as any)(data as EventMap[K]);
314
- }
315
- };
316
- (wrapper as any)._raw = handler;
317
- (wrapper as any)._manualRelease = !!options?.manualRelease;
318
-
319
- if (!(globalThis as any).__yeowEventHandlers) (globalThis as any).__yeowEventHandlers = {};
320
- if (!(globalThis as any).__yeowEventHandlers[eventType]) {
321
- (globalThis as any).__yeowEventHandlers[eventType] = [];
322
- call('event.subscribe', {
323
- pluginName: (globalThis as any).__plugin?.name || 'unknown',
324
- eventType,
325
- });
326
- }
327
- (globalThis as any).__yeowEventHandlers[eventType].push(wrapper);
328
- return () => eventOff(eventType, handler);
329
- }
291
+ const manualRelease = typeof handlerOrOptions === 'object' && handlerOrOptions?.manualRelease === true;
292
+ const handler = manualRelease ? handlerOrUndef! : (handlerOrOptions as EventHandler<K>);
293
+ const pluginName = __plugin?.name || 'unknown';
330
294
 
331
- export function eventOff(eventType: string, handler: Function): void {
332
- const handlers = (globalThis as any).__yeowEventHandlers?.[eventType];
333
- if (!handlers) return;
334
- const idx = handlers.findIndex((h: any) => h._raw === handler || h === handler);
335
- if (idx !== -1) handlers.splice(idx, 1);
336
- if (handlers.length === 0) {
337
- delete (globalThis as any).__yeowEventHandlers[eventType];
338
- call('event.unsubscribe', {
339
- pluginName: (globalThis as any).__plugin?.name || 'unknown',
340
- eventType,
341
- });
342
- }
343
- }
344
-
345
- // ── _onEvent dispatcher (called from init.js) ──────────────────────
346
-
347
- (globalThis as any)._onEvent = (eventType: string, data: any, release: Function): void => {
348
- const handlers = (globalThis as any).__yeowEventHandlers?.[eventType];
349
- if (!handlers?.length) { release({}); return; }
350
-
351
- const cancellable = data?._cancellable;
295
+ const cbId = _registerCallback((data: RawEvent) => {
352
296
  const wrapped = adaptEvent(eventType, data);
353
- const localMods: Record<string, any> = {};
297
+ const cancellable = data?._cancellable;
298
+ const localMods: { cancelled?: boolean } = {};
354
299
 
355
300
  if (cancellable) {
356
- Object.defineProperty(wrapped, 'cancelled', {
357
- get: () => localMods.cancelled || false,
358
- set: (v: boolean) => { localMods.cancelled = v; },
359
- enumerable: true,
301
+ Object.defineProperty(wrapped, 'cancelled', {
302
+ get: () => localMods.cancelled || false,
303
+ set: (v: boolean) => { localMods.cancelled = v; },
304
+ enumerable: true,
305
+ });
306
+ }
307
+
308
+ if (manualRelease) {
309
+ const complete = (result?: Record<string, unknown>) => {
310
+ $send('task', {
311
+ type: 'event.complete',
312
+ params: { callbackId: cbId, mods: result },
313
+ cb: '',
360
314
  });
315
+ };
316
+ (handler as ManualHandler<typeof eventType>)(wrapped, complete);
317
+ return;
361
318
  }
362
319
 
363
- const finish = () => {
364
- if (cancellable && localMods.cancelled) release({ cancelled: true });
365
- else release({});
366
- };
320
+ const result = (handler as EventHandler<typeof eventType>)(wrapped);
321
+ const mods = { cancelled: localMods.cancelled || false };
322
+ $send('task', { type: 'event.complete', params: { callbackId: cbId, mods }, cb: '' });
323
+ }, { persistent: true });
367
324
 
368
- for (const wrapper of handlers) {
369
- try {
370
- if ((wrapper as any)._manualRelease) {
371
- wrapper(wrapped, release);
372
- return;
373
- }
374
- const result = wrapper(wrapped);
375
- if (result && typeof result.then === 'function') {
376
- // Async handler — release immediately with sync mods only.
377
- // Awaiting async operations in event handlers requires manualRelease: true.
378
- finish();
379
- return;
380
- }
381
- } catch (e: any) {
382
- const errInfo: any = {
383
- message: e?.message || String(e),
384
- stack: e?.stack || '',
385
- fileName: e?.fileName || 'main.js',
386
- lineNumber: e?.lineNumber || 0,
387
- columnNumber: e?.columnNumber || 0,
388
- };
389
- if (!errInfo.fileName || errInfo.fileName === 'main.js' || !errInfo.lineNumber) {
390
- const m = e?.stack?.match(/at\s+(?:\S+\s+)?\(?([^\s(]+):(\d+):(\d+)\)?/);
391
- if (m) { errInfo.fileName = m[1]; errInfo.lineNumber = parseInt(m[2]); errInfo.columnNumber = parseInt(m[3]); }
392
- }
393
- (globalThis as any).$send('reportError', errInfo);
394
- }
395
- }
325
+ if (!__yeowEventHandlers) (globalThis as any).__yeowEventHandlers = {};
326
+ const handlers = __yeowEventHandlers!;
327
+ if (!handlers[eventType]) handlers[eventType] = [];
328
+ handlers[eventType].push({ cbId, handler, manualRelease });
396
329
 
397
- finish();
398
- };
330
+ call('event.subscribe', { pluginName, eventType, callbackId: String(cbId) });
331
+ return () => eventOff(eventType, handler);
332
+ }
333
+
334
+ export function eventOff(eventType: string, handler: Function): void {
335
+ if (!__yeowEventHandlers) return;
336
+ const entries = __yeowEventHandlers[eventType];
337
+ if (!entries) return;
338
+ const idx = entries.findIndex((e) => e.handler === handler);
339
+ if (idx !== -1) {
340
+ const removed = entries.splice(idx, 1)[0];
341
+ if (removed.cbId) _unregisterCallback(removed.cbId);
342
+ }
343
+ if (entries.length === 0) {
344
+ delete __yeowEventHandlers![eventType];
345
+ call('event.unsubscribe', {
346
+ pluginName: __plugin?.name || 'unknown',
347
+ eventType,
348
+ });
349
+ }
350
+ }