yeow-api 0.2.107 → 0.2.108

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yeow-api",
3
- "version": "0.2.107",
3
+ "version": "0.2.108",
4
4
  "description": "Yeow API",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
package/src/block.ts CHANGED
@@ -2,32 +2,82 @@ import { call, post } from './task.js';
2
2
  import type { TaskOptions } from './task.js';
3
3
  import { Location } from './location.js';
4
4
  import type { ItemStack } from './item.js';
5
+ import { Material } from './material.js';
5
6
 
7
+ /** 方块状态(Minecraft 原版键值对枚举,值统一为字符串)。 */
8
+ export interface BlockState {
9
+ [key: string]: string;
10
+ }
11
+
12
+ /**
13
+ * Block —— 方块数据描述符 + 可选的世界位置(location)。
14
+ * 对应 Minecraft 原版的方块概念:类型 + 方块状态(键值对枚举,如
15
+ * `facing`、`waterlogged`、`level` 等,值统一为字符串)。
16
+ *
17
+ * **静态数据语义**:`type` / `state` / `location` 均为**获取时刻的快照**,
18
+ * 之后世界变化不会自动更新;需要最新状态请重新调用 `world.getBlock`。
19
+ *
20
+ * 两种来源:
21
+ * - `Block.of(type, state?)` —— 纯数据描述符,无 location(用于放置/比较)
22
+ * - `world.getBlock(x, y, z)` —— 世界中的方块,带 location(yaw/pitch 忽略,为 0)
23
+ */
6
24
  export class Block {
7
25
  constructor(
8
- public readonly world: string,
9
- public readonly x: number,
10
- public readonly y: number,
11
- public readonly z: number,
12
26
  public readonly type: string,
27
+ public readonly state?: BlockState,
28
+ /** 世界位置(由 world.getBlock 返回时存在;yaw/pitch 恒为 0)。 */
29
+ public readonly location?: Location,
13
30
  ) {}
14
31
 
15
- get location(): Location {
16
- return new Location(this.x, this.y, this.z, undefined, undefined, this.world);
32
+ static of(type: string, state?: BlockState): Block {
33
+ return new Block(type, state);
34
+ }
35
+
36
+ /** 派生一个带状态的新描述符(原对象不变)。 */
37
+ withState(state: BlockState): Block {
38
+ return new Block(this.type, { ...(this.state ?? {}), ...state }, this.location);
39
+ }
40
+
41
+ /** 是否与给定类型/状态相同(忽略空状态差异)。 */
42
+ matches(type: string, state?: BlockState): boolean {
43
+ if (this.type !== type) return false;
44
+ if (!state || Object.keys(state).length === 0) return true;
45
+ const s = this.state ?? {};
46
+ for (const [k, v] of Object.entries(state)) {
47
+ if (s[k] !== v) return false;
48
+ }
49
+ return true;
17
50
  }
18
- isSolid(options?: TaskOptions): Promise<boolean> { return post<boolean>('block.isSolid', { world: this.world, x: this.x, y: this.y, z: this.z }, options); }
19
- isSolidSync(options?: TaskOptions): boolean { return call<boolean>('block.isSolid', { world: this.world, x: this.x, y: this.y, z: this.z }, options); }
20
- isLiquid(options?: TaskOptions): Promise<boolean> { return post<boolean>('block.isLiquid', { world: this.world, x: this.x, y: this.y, z: this.z }, options); }
21
- isLiquidSync(options?: TaskOptions): boolean { return call<boolean>('block.isLiquid', { world: this.world, x: this.x, y: this.y, z: this.z }, options); }
22
- isEmpty(options?: TaskOptions): Promise<boolean> { return post<boolean>('block.isEmpty', { world: this.world, x: this.x, y: this.y, z: this.z }, options); }
23
- isEmptySync(options?: TaskOptions): boolean { return call<boolean>('block.isEmpty', { world: this.world, x: this.x, y: this.y, z: this.z }, options); }
51
+
52
+ // ── 材料级静态判断(基于类型,委托 Material;不依赖位置/状态)──
53
+
54
+ isSolid(options?: TaskOptions): Promise<boolean> { return Material.isSolid(this.type, options); }
55
+ isSolidSync(options?: TaskOptions): boolean { return Material.isSolidSync(this.type, options); }
56
+ isLiquid(options?: TaskOptions): Promise<boolean> { return Material.isLiquid(this.type, options); }
57
+ isLiquidSync(options?: TaskOptions): boolean { return Material.isLiquidSync(this.type, options); }
58
+ isAir(options?: TaskOptions): Promise<boolean> { return Material.isAir(this.type, options); }
59
+ isAirSync(options?: TaskOptions): boolean { return Material.isAirSync(this.type, options); }
60
+
61
+ // ── 世界操作(需要 location)──
62
+
63
+ private get pos(): { world: string; x: number; y: number; z: number } | null {
64
+ const l = this.location;
65
+ if (!l || l.world === undefined) return null;
66
+ return { world: l.world, x: l.x, y: l.y, z: l.z };
67
+ }
68
+
69
+ /** 按该方块的位置自然破坏并掉落物品(需要 location)。 */
24
70
  breakNaturally(tool?: ItemStack, options?: TaskOptions): Promise<boolean> {
25
- const p: Record<string, unknown> = { world: this.world, x: this.x, y: this.y, z: this.z };
71
+ const pos = this.pos;
72
+ if (!pos) return Promise.reject(new Error('block has no location (create with world.getBlock)'));
73
+ const p: Record<string, unknown> = { ...pos };
26
74
  if (tool) p.item = tool;
27
75
  return post<boolean>('block.breakNaturally', p, options);
28
76
  }
29
77
  breakNaturallySync(tool?: ItemStack, options?: TaskOptions): boolean {
30
- const p: Record<string, unknown> = { world: this.world, x: this.x, y: this.y, z: this.z };
78
+ const pos = this.pos;
79
+ if (!pos) throw new Error('block has no location (create with world.getBlock)');
80
+ const p: Record<string, unknown> = { ...pos };
31
81
  if (tool) p.item = tool;
32
82
  return call<boolean>('block.breakNaturally', p, options);
33
83
  }
package/src/core.ts CHANGED
@@ -9,6 +9,7 @@ export type { ChunkData } from './chunk.js';
9
9
  export { Entity, LivingEntity } from './entity.js';
10
10
  export type { BoundingBox } from './entity.js';
11
11
  export { Block } from './block.js';
12
+ export type { BlockState } from './block.js';
12
13
  export { Inventory } from './inventory.js';
13
14
  export { registerCommand } from './command.js';
14
15
  export type { CommandOptions, CommandPayload, CommandSender, ManualCompleter } from './command.js';
@@ -84,7 +85,7 @@ export { createBoard as createScoreboard, deleteBoard as deleteScoreboard,
84
85
  setTeamFriendlyFire, setTeamSeeInvisible, setTeamOption,
85
86
  teamAddEntry, teamRemoveEntry, teamGetEntries,
86
87
  setPlayerBoard } from './scoreboard.js';
87
- export { getMaterials, getBlocks, getItems } from './material.js';
88
+ export { Material, getMaterials, getBlocks, getItems } from './material.js';
88
89
  export type { MaterialInfo } from './material.js';
89
90
  export { registerService, registerNativeService, request as serviceRequest, subscribe as serviceSubscribe, publish as servicePublish } from './service.js';
90
91
  export type { ServiceResult, NativeServiceResult } from './service.js';
package/src/material.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { post } from './task.js';
1
+ import { call, post } from './task.js';
2
2
  import type { TaskOptions } from './task.js';
3
3
 
4
4
  export interface MaterialInfo {
@@ -34,3 +34,33 @@ export async function getItems(options?: TaskOptions): Promise<string[]> {
34
34
  Object.freeze(_items);
35
35
  return _items;
36
36
  }
37
+
38
+ /**
39
+ * Material —— 材料级静态判断对象(不依赖坐标/状态)。
40
+ * 基于方块类型(material)判断其固有属性:固体/液体/空气。
41
+ */
42
+ export const Material = {
43
+ /** 是否为固体方块(基于类型,状态不影响)。 */
44
+ isSolid(type: string, options?: TaskOptions): Promise<boolean> {
45
+ return post<boolean>('material.isSolid', { type }, options);
46
+ },
47
+ isSolidSync(type: string, options?: TaskOptions): boolean {
48
+ return call<boolean>('material.isSolid', { type }, options);
49
+ },
50
+
51
+ /** 是否为液体(水 / 熔岩)。 */
52
+ isLiquid(type: string, options?: TaskOptions): Promise<boolean> {
53
+ return post<boolean>('material.isLiquid', { type }, options);
54
+ },
55
+ isLiquidSync(type: string, options?: TaskOptions): boolean {
56
+ return call<boolean>('material.isLiquid', { type }, options);
57
+ },
58
+
59
+ /** 是否为空气(空方块)。 */
60
+ isAir(type: string, options?: TaskOptions): Promise<boolean> {
61
+ return post<boolean>('material.isAir', { type }, options);
62
+ },
63
+ isAirSync(type: string, options?: TaskOptions): boolean {
64
+ return call<boolean>('material.isAir', { type }, options);
65
+ },
66
+ };
package/src/world.ts CHANGED
@@ -2,6 +2,7 @@ import { call, post } from './task.js';
2
2
  import type { TaskOptions } from './task.js';
3
3
  import { Location, LocationData } from './location.js';
4
4
  import { Block } from './block.js';
5
+ import type { BlockState } from './block.js';
5
6
  import { Chunk, ChunkData } from './chunk.js';
6
7
 
7
8
  interface WorldData {
@@ -100,18 +101,32 @@ export class World {
100
101
  return call<string>('world.getBiome', { world: this.name, x, y, z }, options);
101
102
  }
102
103
  getBlock(x: number, y: number, z: number, options?: TaskOptions): Promise<Block | null> {
103
- return post<{ x: number; y: number; z: number; type: string }>('world.getBlock', { world: this.name, x, y, z }, options)
104
- .then((r) => (r ? new Block(this.name, r.x, r.y, r.z, r.type) : null));
104
+ return post<{ world: string; x: number; y: number; z: number; type: string; state: BlockState }>('world.getBlock', { world: this.name, x, y, z }, options)
105
+ .then((r) => (r ? new Block(r.type, r.state, new Location(r.x, r.y, r.z, 0, 0, r.world)) : null));
105
106
  }
106
107
  getBlockSync(x: number, y: number, z: number, options?: TaskOptions): Block | null {
107
- const r = call<{ x: number; y: number; z: number; type: string }>('world.getBlock', { world: this.name, x, y, z }, options);
108
- return r ? new Block(this.name, r.x, r.y, r.z, r.type) : null;
109
- }
110
- setBlock(x: number, y: number, z: number, blockType: string, options?: TaskOptions): Promise<void> {
111
- return post('world.setBlock', { world: this.name, x, y, z, blockType }, options);
112
- }
113
- setBlockSync(x: number, y: number, z: number, blockType: string, options?: TaskOptions): void {
114
- call('world.setBlock', { world: this.name, x, y, z, blockType }, options);
108
+ const r = call<{ world: string; x: number; y: number; z: number; type: string; state: BlockState }>('world.getBlock', { world: this.name, x, y, z }, options);
109
+ return r ? new Block(r.type, r.state, new Location(r.x, r.y, r.z, 0, 0, r.world)) : null;
110
+ }
111
+ setBlock(x: number, y: number, z: number, block: Block | string, options?: TaskOptions): Promise<void> {
112
+ const p: Record<string, unknown> = { world: this.name, x, y, z };
113
+ if (typeof block === 'string') {
114
+ p.blockType = block;
115
+ } else {
116
+ p.blockType = block.type;
117
+ if (block.state && Object.keys(block.state).length > 0) p.state = block.state;
118
+ }
119
+ return post('world.setBlock', p, options);
120
+ }
121
+ setBlockSync(x: number, y: number, z: number, block: Block | string, options?: TaskOptions): void {
122
+ const p: Record<string, unknown> = { world: this.name, x, y, z };
123
+ if (typeof block === 'string') {
124
+ p.blockType = block;
125
+ } else {
126
+ p.blockType = block.type;
127
+ if (block.state && Object.keys(block.state).length > 0) p.state = block.state;
128
+ }
129
+ call('world.setBlock', p, options);
115
130
  }
116
131
  getEntities(options?: TaskOptions): Promise<string[]> {
117
132
  return post<string[]>('world.getEntities', { world: this.name }, options);