feat: add GrabCraft blueprints, building helpers, and world state awareness
All checks were successful
Deploy to Docker / deploy (push) Successful in 12s

- Phase 1: Player position (querytarget @s) and testforblock tools
- Phase 2: GrabCraft scraper with LRU cache, 372-block Java-to-Bedrock
  mapping, search and auto-build blueprint tools with dryRun support
- Phase 3: Raise build limit to 5000 (MAX_BUILD_COMMANDS env), add
  progress notifications and build cancellation
- Phase 4: Geometric shape builders (sphere, cylinder, dome, pyramid,
  wall, box) using fill commands for efficiency
- Phase 5: Event buffer 100->1000 (EVENT_BUFFER_SIZE env), add
  getByTypes and getSince query methods
- Phase 6: MCP resources for block ID reference and GrabCraft categories

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-16 22:19:52 +00:00
parent 6ad340ff88
commit 6a22a5155b
16 changed files with 4843 additions and 2636 deletions

692
src/block-map.js Normal file
View File

@@ -0,0 +1,692 @@
import { log } from './utils.js';
const TAG = 'BlockMap';
/**
* Mapping from GrabCraft Java Edition block IDs to Bedrock Edition block IDs.
* GrabCraft uses format like "5:5" (blockId:dataValue) and English names.
* Bedrock uses string IDs like "planks" with data values.
*
* Format: { javaId: "numericId:data", bedrockId: "block_name", bedrockData: number }
*/
const BLOCK_MAP = new Map([
// ── Air ──
['0', { bedrock: 'air', data: 0, name: 'Air' }],
// ── Stone variants ──
['1', { bedrock: 'stone', data: 0, name: 'Stone' }],
['1:1', { bedrock: 'stone', data: 1, name: 'Granite' }],
['1:2', { bedrock: 'stone', data: 2, name: 'Polished Granite' }],
['1:3', { bedrock: 'stone', data: 3, name: 'Diorite' }],
['1:4', { bedrock: 'stone', data: 4, name: 'Polished Diorite' }],
['1:5', { bedrock: 'stone', data: 5, name: 'Andesite' }],
['1:6', { bedrock: 'stone', data: 6, name: 'Polished Andesite' }],
// ── Grass & Dirt ──
['2', { bedrock: 'grass_block', data: 0, name: 'Grass Block' }],
['3', { bedrock: 'dirt', data: 0, name: 'Dirt' }],
['3:1', { bedrock: 'dirt', data: 1, name: 'Coarse Dirt' }],
['3:2', { bedrock: 'podzol', data: 0, name: 'Podzol' }],
// ── Cobblestone ──
['4', { bedrock: 'cobblestone', data: 0, name: 'Cobblestone' }],
// ── Planks ──
['5', { bedrock: 'planks', data: 0, name: 'Oak Planks' }],
['5:1', { bedrock: 'planks', data: 1, name: 'Spruce Planks' }],
['5:2', { bedrock: 'planks', data: 2, name: 'Birch Planks' }],
['5:3', { bedrock: 'planks', data: 3, name: 'Jungle Planks' }],
['5:4', { bedrock: 'planks', data: 4, name: 'Acacia Planks' }],
['5:5', { bedrock: 'planks', data: 5, name: 'Dark Oak Planks' }],
// ── Saplings ──
['6', { bedrock: 'sapling', data: 0, name: 'Oak Sapling' }],
['6:1', { bedrock: 'sapling', data: 1, name: 'Spruce Sapling' }],
['6:2', { bedrock: 'sapling', data: 2, name: 'Birch Sapling' }],
['6:3', { bedrock: 'sapling', data: 3, name: 'Jungle Sapling' }],
['6:4', { bedrock: 'sapling', data: 4, name: 'Acacia Sapling' }],
['6:5', { bedrock: 'sapling', data: 5, name: 'Dark Oak Sapling' }],
// ── Bedrock ──
['7', { bedrock: 'bedrock', data: 0, name: 'Bedrock' }],
// ── Water & Lava ──
['8', { bedrock: 'water', data: 0, name: 'Water' }],
['9', { bedrock: 'water', data: 0, name: 'Water (stationary)' }],
['10', { bedrock: 'lava', data: 0, name: 'Lava' }],
['11', { bedrock: 'lava', data: 0, name: 'Lava (stationary)' }],
// ── Sand & Gravel ──
['12', { bedrock: 'sand', data: 0, name: 'Sand' }],
['12:1', { bedrock: 'sand', data: 1, name: 'Red Sand' }],
['13', { bedrock: 'gravel', data: 0, name: 'Gravel' }],
// ── Ores ──
['14', { bedrock: 'gold_ore', data: 0, name: 'Gold Ore' }],
['15', { bedrock: 'iron_ore', data: 0, name: 'Iron Ore' }],
['16', { bedrock: 'coal_ore', data: 0, name: 'Coal Ore' }],
// ── Logs ──
['17', { bedrock: 'log', data: 0, name: 'Oak Log' }],
['17:1', { bedrock: 'log', data: 1, name: 'Spruce Log' }],
['17:2', { bedrock: 'log', data: 2, name: 'Birch Log' }],
['17:3', { bedrock: 'log', data: 3, name: 'Jungle Log' }],
['162', { bedrock: 'log2', data: 0, name: 'Acacia Log' }],
['162:1', { bedrock: 'log2', data: 1, name: 'Dark Oak Log' }],
// ── Leaves ──
['18', { bedrock: 'leaves', data: 0, name: 'Oak Leaves' }],
['18:1', { bedrock: 'leaves', data: 1, name: 'Spruce Leaves' }],
['18:2', { bedrock: 'leaves', data: 2, name: 'Birch Leaves' }],
['18:3', { bedrock: 'leaves', data: 3, name: 'Jungle Leaves' }],
['161', { bedrock: 'leaves2', data: 0, name: 'Acacia Leaves' }],
['161:1', { bedrock: 'leaves2', data: 1, name: 'Dark Oak Leaves' }],
// ── Sponge ──
['19', { bedrock: 'sponge', data: 0, name: 'Sponge' }],
['19:1', { bedrock: 'sponge', data: 1, name: 'Wet Sponge' }],
// ── Glass ──
['20', { bedrock: 'glass', data: 0, name: 'Glass' }],
['102', { bedrock: 'glass_pane', data: 0, name: 'Glass Pane' }],
// ── Lapis ──
['21', { bedrock: 'lapis_ore', data: 0, name: 'Lapis Lazuli Ore' }],
['22', { bedrock: 'lapis_block', data: 0, name: 'Lapis Lazuli Block' }],
// ── Dispenser & Noteblock ──
['23', { bedrock: 'dispenser', data: 0, name: 'Dispenser' }],
['25', { bedrock: 'noteblock', data: 0, name: 'Note Block' }],
// ── Sandstone ──
['24', { bedrock: 'sandstone', data: 0, name: 'Sandstone' }],
['24:1', { bedrock: 'sandstone', data: 1, name: 'Chiseled Sandstone' }],
['24:2', { bedrock: 'sandstone', data: 2, name: 'Smooth Sandstone' }],
// ── Wool ──
['35', { bedrock: 'wool', data: 0, name: 'White Wool' }],
['35:1', { bedrock: 'wool', data: 1, name: 'Orange Wool' }],
['35:2', { bedrock: 'wool', data: 2, name: 'Magenta Wool' }],
['35:3', { bedrock: 'wool', data: 3, name: 'Light Blue Wool' }],
['35:4', { bedrock: 'wool', data: 4, name: 'Yellow Wool' }],
['35:5', { bedrock: 'wool', data: 5, name: 'Lime Wool' }],
['35:6', { bedrock: 'wool', data: 6, name: 'Pink Wool' }],
['35:7', { bedrock: 'wool', data: 7, name: 'Gray Wool' }],
['35:8', { bedrock: 'wool', data: 8, name: 'Light Gray Wool' }],
['35:9', { bedrock: 'wool', data: 9, name: 'Cyan Wool' }],
['35:10', { bedrock: 'wool', data: 10, name: 'Purple Wool' }],
['35:11', { bedrock: 'wool', data: 11, name: 'Blue Wool' }],
['35:12', { bedrock: 'wool', data: 12, name: 'Brown Wool' }],
['35:13', { bedrock: 'wool', data: 13, name: 'Green Wool' }],
['35:14', { bedrock: 'wool', data: 14, name: 'Red Wool' }],
['35:15', { bedrock: 'wool', data: 15, name: 'Black Wool' }],
// ── Gold & Iron blocks ──
['41', { bedrock: 'gold_block', data: 0, name: 'Block of Gold' }],
['42', { bedrock: 'iron_block', data: 0, name: 'Block of Iron' }],
// ── Slabs ──
['44', { bedrock: 'stone_block_slab', data: 0, name: 'Stone Slab' }],
['44:1', { bedrock: 'stone_block_slab', data: 1, name: 'Sandstone Slab' }],
['44:3', { bedrock: 'stone_block_slab', data: 3, name: 'Cobblestone Slab' }],
['44:4', { bedrock: 'stone_block_slab', data: 4, name: 'Brick Slab' }],
['44:5', { bedrock: 'stone_block_slab', data: 5, name: 'Stone Brick Slab' }],
['126', { bedrock: 'wooden_slab', data: 0, name: 'Oak Slab' }],
['126:1', { bedrock: 'wooden_slab', data: 1, name: 'Spruce Slab' }],
['126:2', { bedrock: 'wooden_slab', data: 2, name: 'Birch Slab' }],
['126:3', { bedrock: 'wooden_slab', data: 3, name: 'Jungle Slab' }],
['126:4', { bedrock: 'wooden_slab', data: 4, name: 'Acacia Slab' }],
['126:5', { bedrock: 'wooden_slab', data: 5, name: 'Dark Oak Slab' }],
// ── Bricks ──
['45', { bedrock: 'brick_block', data: 0, name: 'Bricks' }],
['98', { bedrock: 'stonebrick', data: 0, name: 'Stone Bricks' }],
['98:1', { bedrock: 'stonebrick', data: 1, name: 'Mossy Stone Bricks' }],
['98:2', { bedrock: 'stonebrick', data: 2, name: 'Cracked Stone Bricks' }],
['98:3', { bedrock: 'stonebrick', data: 3, name: 'Chiseled Stone Bricks' }],
['112', { bedrock: 'nether_brick', data: 0, name: 'Nether Bricks' }],
// ── TNT ──
['46', { bedrock: 'tnt', data: 0, name: 'TNT' }],
// ── Bookshelf ──
['47', { bedrock: 'bookshelf', data: 0, name: 'Bookshelf' }],
// ── Mossy Cobblestone ──
['48', { bedrock: 'mossy_cobblestone', data: 0, name: 'Mossy Cobblestone' }],
// ── Obsidian ──
['49', { bedrock: 'obsidian', data: 0, name: 'Obsidian' }],
// ── Torches ──
['50', { bedrock: 'torch', data: 0, name: 'Torch' }],
// ── Stairs ──
['53', { bedrock: 'oak_stairs', data: 0, name: 'Oak Stairs' }],
['67', { bedrock: 'stone_stairs', data: 0, name: 'Cobblestone Stairs' }],
['108', { bedrock: 'brick_stairs', data: 0, name: 'Brick Stairs' }],
['109', { bedrock: 'stone_brick_stairs', data: 0, name: 'Stone Brick Stairs' }],
['114', { bedrock: 'nether_brick_stairs', data: 0, name: 'Nether Brick Stairs' }],
['128', { bedrock: 'sandstone_stairs', data: 0, name: 'Sandstone Stairs' }],
['134', { bedrock: 'spruce_stairs', data: 0, name: 'Spruce Stairs' }],
['135', { bedrock: 'birch_stairs', data: 0, name: 'Birch Stairs' }],
['136', { bedrock: 'jungle_stairs', data: 0, name: 'Jungle Stairs' }],
['163', { bedrock: 'acacia_stairs', data: 0, name: 'Acacia Stairs' }],
['164', { bedrock: 'dark_oak_stairs', data: 0, name: 'Dark Oak Stairs' }],
['156', { bedrock: 'quartz_stairs', data: 0, name: 'Quartz Stairs' }],
// ── Chest ──
['54', { bedrock: 'chest', data: 0, name: 'Chest' }],
// ── Diamond ──
['56', { bedrock: 'diamond_ore', data: 0, name: 'Diamond Ore' }],
['57', { bedrock: 'diamond_block', data: 0, name: 'Block of Diamond' }],
// ── Crafting Table ──
['58', { bedrock: 'crafting_table', data: 0, name: 'Crafting Table' }],
// ── Furnace ──
['61', { bedrock: 'furnace', data: 0, name: 'Furnace' }],
['62', { bedrock: 'lit_furnace', data: 0, name: 'Burning Furnace' }],
// ── Doors ──
['64', { bedrock: 'wooden_door', data: 0, name: 'Oak Door' }],
['71', { bedrock: 'iron_door', data: 0, name: 'Iron Door' }],
['193', { bedrock: 'spruce_door', data: 0, name: 'Spruce Door' }],
['194', { bedrock: 'birch_door', data: 0, name: 'Birch Door' }],
['195', { bedrock: 'jungle_door', data: 0, name: 'Jungle Door' }],
['196', { bedrock: 'acacia_door', data: 0, name: 'Acacia Door' }],
['197', { bedrock: 'dark_oak_door', data: 0, name: 'Dark Oak Door' }],
// ── Ladders ──
['65', { bedrock: 'ladder', data: 0, name: 'Ladder' }],
// ── Rails ──
['66', { bedrock: 'rail', data: 0, name: 'Rail' }],
['27', { bedrock: 'golden_rail', data: 0, name: 'Powered Rail' }],
['28', { bedrock: 'detector_rail', data: 0, name: 'Detector Rail' }],
['157', { bedrock: 'activator_rail', data: 0, name: 'Activator Rail' }],
// ── Snow & Ice ──
['78', { bedrock: 'snow_layer', data: 0, name: 'Snow Layer' }],
['79', { bedrock: 'ice', data: 0, name: 'Ice' }],
['80', { bedrock: 'snow', data: 0, name: 'Snow Block' }],
['174', { bedrock: 'packed_ice', data: 0, name: 'Packed Ice' }],
// ── Cactus ──
['81', { bedrock: 'cactus', data: 0, name: 'Cactus' }],
// ── Clay ──
['82', { bedrock: 'clay', data: 0, name: 'Clay' }],
// ── Jukebox ──
['84', { bedrock: 'jukebox', data: 0, name: 'Jukebox' }],
// ── Fences ──
['85', { bedrock: 'fence', data: 0, name: 'Oak Fence' }],
['113', { bedrock: 'nether_brick_fence', data: 0, name: 'Nether Brick Fence' }],
['188', { bedrock: 'fence', data: 1, name: 'Spruce Fence' }],
['189', { bedrock: 'fence', data: 2, name: 'Birch Fence' }],
['190', { bedrock: 'fence', data: 3, name: 'Jungle Fence' }],
['191', { bedrock: 'fence', data: 4, name: 'Acacia Fence' }],
['192', { bedrock: 'fence', data: 5, name: 'Dark Oak Fence' }],
// ── Fence Gates ──
['107', { bedrock: 'fence_gate', data: 0, name: 'Oak Fence Gate' }],
['183', { bedrock: 'spruce_fence_gate', data: 0, name: 'Spruce Fence Gate' }],
['184', { bedrock: 'birch_fence_gate', data: 0, name: 'Birch Fence Gate' }],
['185', { bedrock: 'jungle_fence_gate', data: 0, name: 'Jungle Fence Gate' }],
['186', { bedrock: 'acacia_fence_gate', data: 0, name: 'Acacia Fence Gate' }],
['187', { bedrock: 'dark_oak_fence_gate', data: 0, name: 'Dark Oak Fence Gate' }],
// ── Pumpkin & Melon ──
['86', { bedrock: 'pumpkin', data: 0, name: 'Pumpkin' }],
['91', { bedrock: 'lit_pumpkin', data: 0, name: 'Jack o\'Lantern' }],
['103', { bedrock: 'melon_block', data: 0, name: 'Melon Block' }],
// ── Netherrack & Soul Sand ──
['87', { bedrock: 'netherrack', data: 0, name: 'Netherrack' }],
['88', { bedrock: 'soul_sand', data: 0, name: 'Soul Sand' }],
// ── Glowstone ──
['89', { bedrock: 'glowstone', data: 0, name: 'Glowstone' }],
// ── Stained Glass ──
['95', { bedrock: 'stained_glass', data: 0, name: 'White Stained Glass' }],
['95:1', { bedrock: 'stained_glass', data: 1, name: 'Orange Stained Glass' }],
['95:2', { bedrock: 'stained_glass', data: 2, name: 'Magenta Stained Glass' }],
['95:3', { bedrock: 'stained_glass', data: 3, name: 'Light Blue Stained Glass' }],
['95:4', { bedrock: 'stained_glass', data: 4, name: 'Yellow Stained Glass' }],
['95:5', { bedrock: 'stained_glass', data: 5, name: 'Lime Stained Glass' }],
['95:6', { bedrock: 'stained_glass', data: 6, name: 'Pink Stained Glass' }],
['95:7', { bedrock: 'stained_glass', data: 7, name: 'Gray Stained Glass' }],
['95:8', { bedrock: 'stained_glass', data: 8, name: 'Light Gray Stained Glass' }],
['95:9', { bedrock: 'stained_glass', data: 9, name: 'Cyan Stained Glass' }],
['95:10', { bedrock: 'stained_glass', data: 10, name: 'Purple Stained Glass' }],
['95:11', { bedrock: 'stained_glass', data: 11, name: 'Blue Stained Glass' }],
['95:12', { bedrock: 'stained_glass', data: 12, name: 'Brown Stained Glass' }],
['95:13', { bedrock: 'stained_glass', data: 13, name: 'Green Stained Glass' }],
['95:14', { bedrock: 'stained_glass', data: 14, name: 'Red Stained Glass' }],
['95:15', { bedrock: 'stained_glass', data: 15, name: 'Black Stained Glass' }],
// ── Stained Glass Panes ──
['160', { bedrock: 'stained_glass_pane', data: 0, name: 'White Stained Glass Pane' }],
['160:1', { bedrock: 'stained_glass_pane', data: 1, name: 'Orange Stained Glass Pane' }],
['160:2', { bedrock: 'stained_glass_pane', data: 2, name: 'Magenta Stained Glass Pane' }],
['160:3', { bedrock: 'stained_glass_pane', data: 3, name: 'Light Blue Stained Glass Pane' }],
['160:4', { bedrock: 'stained_glass_pane', data: 4, name: 'Yellow Stained Glass Pane' }],
['160:5', { bedrock: 'stained_glass_pane', data: 5, name: 'Lime Stained Glass Pane' }],
['160:6', { bedrock: 'stained_glass_pane', data: 6, name: 'Pink Stained Glass Pane' }],
['160:7', { bedrock: 'stained_glass_pane', data: 7, name: 'Gray Stained Glass Pane' }],
['160:8', { bedrock: 'stained_glass_pane', data: 8, name: 'Light Gray Stained Glass Pane' }],
['160:9', { bedrock: 'stained_glass_pane', data: 9, name: 'Cyan Stained Glass Pane' }],
['160:10', { bedrock: 'stained_glass_pane', data: 10, name: 'Purple Stained Glass Pane' }],
['160:11', { bedrock: 'stained_glass_pane', data: 11, name: 'Blue Stained Glass Pane' }],
['160:12', { bedrock: 'stained_glass_pane', data: 12, name: 'Brown Stained Glass Pane' }],
['160:13', { bedrock: 'stained_glass_pane', data: 13, name: 'Green Stained Glass Pane' }],
['160:14', { bedrock: 'stained_glass_pane', data: 14, name: 'Red Stained Glass Pane' }],
['160:15', { bedrock: 'stained_glass_pane', data: 15, name: 'Black Stained Glass Pane' }],
// ── Iron Bars ──
['101', { bedrock: 'iron_bars', data: 0, name: 'Iron Bars' }],
// ── Quartz ──
['155', { bedrock: 'quartz_block', data: 0, name: 'Quartz Block' }],
['155:1', { bedrock: 'quartz_block', data: 1, name: 'Chiseled Quartz' }],
['155:2', { bedrock: 'quartz_block', data: 2, name: 'Pillar Quartz' }],
// ── Terracotta (Hardened Clay) ──
['159', { bedrock: 'stained_hardened_clay', data: 0, name: 'White Terracotta' }],
['159:1', { bedrock: 'stained_hardened_clay', data: 1, name: 'Orange Terracotta' }],
['159:2', { bedrock: 'stained_hardened_clay', data: 2, name: 'Magenta Terracotta' }],
['159:3', { bedrock: 'stained_hardened_clay', data: 3, name: 'Light Blue Terracotta' }],
['159:4', { bedrock: 'stained_hardened_clay', data: 4, name: 'Yellow Terracotta' }],
['159:5', { bedrock: 'stained_hardened_clay', data: 5, name: 'Lime Terracotta' }],
['159:6', { bedrock: 'stained_hardened_clay', data: 6, name: 'Pink Terracotta' }],
['159:7', { bedrock: 'stained_hardened_clay', data: 7, name: 'Gray Terracotta' }],
['159:8', { bedrock: 'stained_hardened_clay', data: 8, name: 'Light Gray Terracotta' }],
['159:9', { bedrock: 'stained_hardened_clay', data: 9, name: 'Cyan Terracotta' }],
['159:10', { bedrock: 'stained_hardened_clay', data: 10, name: 'Purple Terracotta' }],
['159:11', { bedrock: 'stained_hardened_clay', data: 11, name: 'Blue Terracotta' }],
['159:12', { bedrock: 'stained_hardened_clay', data: 12, name: 'Brown Terracotta' }],
['159:13', { bedrock: 'stained_hardened_clay', data: 13, name: 'Green Terracotta' }],
['159:14', { bedrock: 'stained_hardened_clay', data: 14, name: 'Red Terracotta' }],
['159:15', { bedrock: 'stained_hardened_clay', data: 15, name: 'Black Terracotta' }],
['172', { bedrock: 'hardened_clay', data: 0, name: 'Terracotta' }],
// ── Concrete ──
['251', { bedrock: 'concrete', data: 0, name: 'White Concrete' }],
['251:1', { bedrock: 'concrete', data: 1, name: 'Orange Concrete' }],
['251:2', { bedrock: 'concrete', data: 2, name: 'Magenta Concrete' }],
['251:3', { bedrock: 'concrete', data: 3, name: 'Light Blue Concrete' }],
['251:4', { bedrock: 'concrete', data: 4, name: 'Yellow Concrete' }],
['251:5', { bedrock: 'concrete', data: 5, name: 'Lime Concrete' }],
['251:6', { bedrock: 'concrete', data: 6, name: 'Pink Concrete' }],
['251:7', { bedrock: 'concrete', data: 7, name: 'Gray Concrete' }],
['251:8', { bedrock: 'concrete', data: 8, name: 'Light Gray Concrete' }],
['251:9', { bedrock: 'concrete', data: 9, name: 'Cyan Concrete' }],
['251:10', { bedrock: 'concrete', data: 10, name: 'Purple Concrete' }],
['251:11', { bedrock: 'concrete', data: 11, name: 'Blue Concrete' }],
['251:12', { bedrock: 'concrete', data: 12, name: 'Brown Concrete' }],
['251:13', { bedrock: 'concrete', data: 13, name: 'Green Concrete' }],
['251:14', { bedrock: 'concrete', data: 14, name: 'Red Concrete' }],
['251:15', { bedrock: 'concrete', data: 15, name: 'Black Concrete' }],
// ── Concrete Powder ──
['252', { bedrock: 'concrete_powder', data: 0, name: 'White Concrete Powder' }],
['252:1', { bedrock: 'concrete_powder', data: 1, name: 'Orange Concrete Powder' }],
['252:2', { bedrock: 'concrete_powder', data: 2, name: 'Magenta Concrete Powder' }],
['252:3', { bedrock: 'concrete_powder', data: 3, name: 'Light Blue Concrete Powder' }],
['252:4', { bedrock: 'concrete_powder', data: 4, name: 'Yellow Concrete Powder' }],
['252:5', { bedrock: 'concrete_powder', data: 5, name: 'Lime Concrete Powder' }],
['252:6', { bedrock: 'concrete_powder', data: 6, name: 'Pink Concrete Powder' }],
['252:7', { bedrock: 'concrete_powder', data: 7, name: 'Gray Concrete Powder' }],
['252:8', { bedrock: 'concrete_powder', data: 8, name: 'Light Gray Concrete Powder' }],
['252:9', { bedrock: 'concrete_powder', data: 9, name: 'Cyan Concrete Powder' }],
['252:10', { bedrock: 'concrete_powder', data: 10, name: 'Purple Concrete Powder' }],
['252:11', { bedrock: 'concrete_powder', data: 11, name: 'Blue Concrete Powder' }],
['252:12', { bedrock: 'concrete_powder', data: 12, name: 'Brown Concrete Powder' }],
['252:13', { bedrock: 'concrete_powder', data: 13, name: 'Green Concrete Powder' }],
['252:14', { bedrock: 'concrete_powder', data: 14, name: 'Red Concrete Powder' }],
['252:15', { bedrock: 'concrete_powder', data: 15, name: 'Black Concrete Powder' }],
// ── Glazed Terracotta ──
['235', { bedrock: 'white_glazed_terracotta', data: 0, name: 'White Glazed Terracotta' }],
['236', { bedrock: 'orange_glazed_terracotta', data: 0, name: 'Orange Glazed Terracotta' }],
['237', { bedrock: 'magenta_glazed_terracotta', data: 0, name: 'Magenta Glazed Terracotta' }],
['238', { bedrock: 'light_blue_glazed_terracotta', data: 0, name: 'Light Blue Glazed Terracotta' }],
['239', { bedrock: 'yellow_glazed_terracotta', data: 0, name: 'Yellow Glazed Terracotta' }],
['240', { bedrock: 'lime_glazed_terracotta', data: 0, name: 'Lime Glazed Terracotta' }],
['241', { bedrock: 'pink_glazed_terracotta', data: 0, name: 'Pink Glazed Terracotta' }],
['242', { bedrock: 'gray_glazed_terracotta', data: 0, name: 'Gray Glazed Terracotta' }],
['243', { bedrock: 'silver_glazed_terracotta', data: 0, name: 'Light Gray Glazed Terracotta' }],
['244', { bedrock: 'cyan_glazed_terracotta', data: 0, name: 'Cyan Glazed Terracotta' }],
['245', { bedrock: 'purple_glazed_terracotta', data: 0, name: 'Purple Glazed Terracotta' }],
['246', { bedrock: 'blue_glazed_terracotta', data: 0, name: 'Blue Glazed Terracotta' }],
['247', { bedrock: 'brown_glazed_terracotta', data: 0, name: 'Brown Glazed Terracotta' }],
['248', { bedrock: 'green_glazed_terracotta', data: 0, name: 'Green Glazed Terracotta' }],
['249', { bedrock: 'red_glazed_terracotta', data: 0, name: 'Red Glazed Terracotta' }],
['250', { bedrock: 'black_glazed_terracotta', data: 0, name: 'Black Glazed Terracotta' }],
// ── Carpet ──
['171', { bedrock: 'carpet', data: 0, name: 'White Carpet' }],
['171:1', { bedrock: 'carpet', data: 1, name: 'Orange Carpet' }],
['171:2', { bedrock: 'carpet', data: 2, name: 'Magenta Carpet' }],
['171:3', { bedrock: 'carpet', data: 3, name: 'Light Blue Carpet' }],
['171:4', { bedrock: 'carpet', data: 4, name: 'Yellow Carpet' }],
['171:5', { bedrock: 'carpet', data: 5, name: 'Lime Carpet' }],
['171:6', { bedrock: 'carpet', data: 6, name: 'Pink Carpet' }],
['171:7', { bedrock: 'carpet', data: 7, name: 'Gray Carpet' }],
['171:8', { bedrock: 'carpet', data: 8, name: 'Light Gray Carpet' }],
['171:9', { bedrock: 'carpet', data: 9, name: 'Cyan Carpet' }],
['171:10', { bedrock: 'carpet', data: 10, name: 'Purple Carpet' }],
['171:11', { bedrock: 'carpet', data: 11, name: 'Blue Carpet' }],
['171:12', { bedrock: 'carpet', data: 12, name: 'Brown Carpet' }],
['171:13', { bedrock: 'carpet', data: 13, name: 'Green Carpet' }],
['171:14', { bedrock: 'carpet', data: 14, name: 'Red Carpet' }],
['171:15', { bedrock: 'carpet', data: 15, name: 'Black Carpet' }],
// ── Redstone ──
['55', { bedrock: 'redstone_wire', data: 0, name: 'Redstone Wire' }],
['73', { bedrock: 'redstone_ore', data: 0, name: 'Redstone Ore' }],
['76', { bedrock: 'redstone_torch', data: 0, name: 'Redstone Torch' }],
['69', { bedrock: 'lever', data: 0, name: 'Lever' }],
['70', { bedrock: 'stone_pressure_plate', data: 0, name: 'Stone Pressure Plate' }],
['72', { bedrock: 'wooden_pressure_plate', data: 0, name: 'Oak Pressure Plate' }],
['77', { bedrock: 'stone_button', data: 0, name: 'Stone Button' }],
['143', { bedrock: 'wooden_button', data: 0, name: 'Oak Button' }],
['123', { bedrock: 'redstone_lamp', data: 0, name: 'Redstone Lamp' }],
['33', { bedrock: 'piston', data: 0, name: 'Piston' }],
['29', { bedrock: 'sticky_piston', data: 0, name: 'Sticky Piston' }],
['93', { bedrock: 'unpowered_repeater', data: 0, name: 'Repeater' }],
['149', { bedrock: 'unpowered_comparator', data: 0, name: 'Comparator' }],
['152', { bedrock: 'redstone_block', data: 0, name: 'Block of Redstone' }],
['151', { bedrock: 'daylight_detector', data: 0, name: 'Daylight Detector' }],
['154', { bedrock: 'hopper', data: 0, name: 'Hopper' }],
['158', { bedrock: 'dropper', data: 0, name: 'Dropper' }],
['146', { bedrock: 'trapped_chest', data: 0, name: 'Trapped Chest' }],
['147', { bedrock: 'light_weighted_pressure_plate', data: 0, name: 'Light Weighted Pressure Plate' }],
['148', { bedrock: 'heavy_weighted_pressure_plate', data: 0, name: 'Heavy Weighted Pressure Plate' }],
// ── Trapdoors ──
['96', { bedrock: 'trapdoor', data: 0, name: 'Oak Trapdoor' }],
['167', { bedrock: 'iron_trapdoor', data: 0, name: 'Iron Trapdoor' }],
// ── Emerald ──
['129', { bedrock: 'emerald_ore', data: 0, name: 'Emerald Ore' }],
['133', { bedrock: 'emerald_block', data: 0, name: 'Block of Emerald' }],
// ── End Stone ──
['121', { bedrock: 'end_stone', data: 0, name: 'End Stone' }],
['206', { bedrock: 'end_bricks', data: 0, name: 'End Stone Bricks' }],
// ── Purpur ──
['201', { bedrock: 'purpur_block', data: 0, name: 'Purpur Block' }],
['202', { bedrock: 'purpur_pillar', data: 0, name: 'Purpur Pillar' }],
['203', { bedrock: 'purpur_stairs', data: 0, name: 'Purpur Stairs' }],
// ── Prismarine ──
['168', { bedrock: 'prismarine', data: 0, name: 'Prismarine' }],
['168:1', { bedrock: 'prismarine', data: 1, name: 'Prismarine Bricks' }],
['168:2', { bedrock: 'prismarine', data: 2, name: 'Dark Prismarine' }],
['169', { bedrock: 'sea_lantern', data: 0, name: 'Sea Lantern' }],
// ── Hay Bale ──
['170', { bedrock: 'hay_block', data: 0, name: 'Hay Bale' }],
// ── Anvil ──
['145', { bedrock: 'anvil', data: 0, name: 'Anvil' }],
// ── Slime Block ──
['165', { bedrock: 'slime', data: 0, name: 'Slime Block' }],
// ── Coal Block ──
['173', { bedrock: 'coal_block', data: 0, name: 'Block of Coal' }],
// ── Red Sandstone ──
['179', { bedrock: 'red_sandstone', data: 0, name: 'Red Sandstone' }],
['179:1', { bedrock: 'red_sandstone', data: 1, name: 'Chiseled Red Sandstone' }],
['179:2', { bedrock: 'red_sandstone', data: 2, name: 'Smooth Red Sandstone' }],
['180', { bedrock: 'red_sandstone_stairs', data: 0, name: 'Red Sandstone Stairs' }],
// ── Misc utility blocks ──
['26', { bedrock: 'bed', data: 0, name: 'Bed' }],
['30', { bedrock: 'web', data: 0, name: 'Cobweb' }],
['31', { bedrock: 'tallgrass', data: 1, name: 'Grass' }],
['31:2', { bedrock: 'tallgrass', data: 2, name: 'Fern' }],
['32', { bedrock: 'deadbush', data: 0, name: 'Dead Bush' }],
['37', { bedrock: 'yellow_flower', data: 0, name: 'Dandelion' }],
['38', { bedrock: 'red_flower', data: 0, name: 'Poppy' }],
['38:1', { bedrock: 'red_flower', data: 1, name: 'Blue Orchid' }],
['38:2', { bedrock: 'red_flower', data: 2, name: 'Allium' }],
['38:3', { bedrock: 'red_flower', data: 3, name: 'Azure Bluet' }],
['38:4', { bedrock: 'red_flower', data: 4, name: 'Red Tulip' }],
['38:5', { bedrock: 'red_flower', data: 5, name: 'Orange Tulip' }],
['38:6', { bedrock: 'red_flower', data: 6, name: 'White Tulip' }],
['38:7', { bedrock: 'red_flower', data: 7, name: 'Pink Tulip' }],
['38:8', { bedrock: 'red_flower', data: 8, name: 'Oxeye Daisy' }],
['39', { bedrock: 'brown_mushroom', data: 0, name: 'Brown Mushroom' }],
['40', { bedrock: 'red_mushroom', data: 0, name: 'Red Mushroom' }],
['83', { bedrock: 'reeds', data: 0, name: 'Sugar Cane' }],
['100', { bedrock: 'red_mushroom_block', data: 0, name: 'Red Mushroom Block' }],
['99', { bedrock: 'brown_mushroom_block', data: 0, name: 'Brown Mushroom Block' }],
['104', { bedrock: 'pumpkin_stem', data: 0, name: 'Pumpkin Stem' }],
['106', { bedrock: 'vine', data: 0, name: 'Vines' }],
['110', { bedrock: 'mycelium', data: 0, name: 'Mycelium' }],
['111', { bedrock: 'waterlily', data: 0, name: 'Lily Pad' }],
['115', { bedrock: 'nether_wart', data: 0, name: 'Nether Wart' }],
['116', { bedrock: 'enchanting_table', data: 0, name: 'Enchanting Table' }],
['117', { bedrock: 'brewing_stand', data: 0, name: 'Brewing Stand' }],
['118', { bedrock: 'cauldron', data: 0, name: 'Cauldron' }],
['120', { bedrock: 'end_portal_frame', data: 0, name: 'End Portal Frame' }],
['122', { bedrock: 'dragon_egg', data: 0, name: 'Dragon Egg' }],
['130', { bedrock: 'ender_chest', data: 0, name: 'Ender Chest' }],
['138', { bedrock: 'beacon', data: 0, name: 'Beacon' }],
['166', { bedrock: 'barrier', data: 0, name: 'Barrier' }],
['175', { bedrock: 'double_plant', data: 0, name: 'Sunflower' }],
['175:1', { bedrock: 'double_plant', data: 1, name: 'Lilac' }],
['175:2', { bedrock: 'double_plant', data: 2, name: 'Double Tallgrass' }],
['175:3', { bedrock: 'double_plant', data: 3, name: 'Large Fern' }],
['175:4', { bedrock: 'double_plant', data: 4, name: 'Rose Bush' }],
['175:5', { bedrock: 'double_plant', data: 5, name: 'Peony' }],
['198', { bedrock: 'end_rod', data: 0, name: 'End Rod' }],
['199', { bedrock: 'chorus_plant', data: 0, name: 'Chorus Plant' }],
['200', { bedrock: 'chorus_flower', data: 0, name: 'Chorus Flower' }],
['207', { bedrock: 'beetroot', data: 0, name: 'Beetroot' }],
['208', { bedrock: 'grass_path', data: 0, name: 'Grass Path' }],
['209', { bedrock: 'end_gateway', data: 0, name: 'End Gateway' }],
['213', { bedrock: 'magma', data: 0, name: 'Magma Block' }],
['214', { bedrock: 'nether_wart_block', data: 0, name: 'Nether Wart Block' }],
['215', { bedrock: 'red_nether_brick', data: 0, name: 'Red Nether Bricks' }],
['216', { bedrock: 'bone_block', data: 0, name: 'Bone Block' }],
['218', { bedrock: 'observer', data: 0, name: 'Observer' }],
['219', { bedrock: 'shulker_box', data: 0, name: 'White Shulker Box' }],
// ── Walls ──
['139', { bedrock: 'cobblestone_wall', data: 0, name: 'Cobblestone Wall' }],
['139:1', { bedrock: 'cobblestone_wall', data: 1, name: 'Mossy Cobblestone Wall' }],
// ── Banners ──
['176', { bedrock: 'standing_banner', data: 0, name: 'Banner' }],
// ── Signs ──
['63', { bedrock: 'standing_sign', data: 0, name: 'Sign' }],
['68', { bedrock: 'wall_sign', data: 0, name: 'Wall Sign' }],
// ── Flower Pot ──
['140', { bedrock: 'flower_pot', data: 0, name: 'Flower Pot' }],
// ── Skull / Head ──
['144', { bedrock: 'skull', data: 0, name: 'Mob Head' }],
// ── Armor Stand (entity, but GrabCraft uses it) ──
['416', { bedrock: 'air', data: 0, name: 'Armor Stand (entity)' }],
]);
/**
* Name-based fuzzy lookup table (lowercase name -> bedrock ID).
* Built from BLOCK_MAP for fallback matching when numeric ID fails.
*/
const NAME_MAP = new Map();
for (const [, entry] of BLOCK_MAP) {
NAME_MAP.set(entry.name.toLowerCase(), entry);
}
// Additional common name aliases
const ALIASES = new Map([
['dark oak wood plank', BLOCK_MAP.get('5:5')],
['oak wood plank', BLOCK_MAP.get('5')],
['spruce wood plank', BLOCK_MAP.get('5:1')],
['birch wood plank', BLOCK_MAP.get('5:2')],
['jungle wood plank', BLOCK_MAP.get('5:3')],
['acacia wood plank', BLOCK_MAP.get('5:4')],
['dark oak wood', BLOCK_MAP.get('162:1')],
['oak wood', BLOCK_MAP.get('17')],
['spruce wood', BLOCK_MAP.get('17:1')],
['birch wood', BLOCK_MAP.get('17:2')],
['jungle wood', BLOCK_MAP.get('17:3')],
['acacia wood', BLOCK_MAP.get('162')],
['stone brick', BLOCK_MAP.get('98')],
['mossy stone brick', BLOCK_MAP.get('98:1')],
['cracked stone brick', BLOCK_MAP.get('98:2')],
['chiseled stone brick', BLOCK_MAP.get('98:3')],
['brick', BLOCK_MAP.get('45')],
['nether brick', BLOCK_MAP.get('112')],
['glass pane', BLOCK_MAP.get('102')],
['cobble', BLOCK_MAP.get('4')],
['plank', BLOCK_MAP.get('5')],
['planks', BLOCK_MAP.get('5')],
['wooden plank', BLOCK_MAP.get('5')],
['wooden planks', BLOCK_MAP.get('5')],
['log', BLOCK_MAP.get('17')],
['wood', BLOCK_MAP.get('17')],
['leaves', BLOCK_MAP.get('18')],
['torch', BLOCK_MAP.get('50')],
['crafting table', BLOCK_MAP.get('58')],
['workbench', BLOCK_MAP.get('58')],
['furnace', BLOCK_MAP.get('61')],
['chest', BLOCK_MAP.get('54')],
['door', BLOCK_MAP.get('64')],
['fence', BLOCK_MAP.get('85')],
['wool', BLOCK_MAP.get('35')],
['carpet', BLOCK_MAP.get('171')],
['glass', BLOCK_MAP.get('20')],
['sand', BLOCK_MAP.get('12')],
['gravel', BLOCK_MAP.get('13')],
['dirt', BLOCK_MAP.get('3')],
['grass', BLOCK_MAP.get('2')],
['grass block', BLOCK_MAP.get('2')],
['water', BLOCK_MAP.get('8')],
['lava', BLOCK_MAP.get('10')],
['cobblestone wall', BLOCK_MAP.get('139')],
['mossy cobblestone wall', BLOCK_MAP.get('139:1')],
['redstone', BLOCK_MAP.get('55')],
['redstone lamp', BLOCK_MAP.get('123')],
['glowstone', BLOCK_MAP.get('89')],
['sea lantern', BLOCK_MAP.get('169')],
]);
for (const [name, entry] of ALIASES) {
if (entry) NAME_MAP.set(name, entry);
}
/** Track unknown blocks for reporting */
const unknownBlocks = new Map();
/**
* Resolve a GrabCraft block ID and/or name to a Bedrock setblock string.
* @param {string} gcId - GrabCraft numeric ID like "5:5" or "5"
* @param {string} [gcName] - English name from GrabCraft for fuzzy matching
* @returns {{ block: string, data: number, matched: boolean, name: string }}
*/
export function resolveBlock(gcId, gcName) {
// Try exact numeric ID first
if (gcId && BLOCK_MAP.has(gcId)) {
const entry = BLOCK_MAP.get(gcId);
return { block: entry.bedrock, data: entry.data, matched: true, name: entry.name };
}
// Try base ID without data value
if (gcId && gcId.includes(':')) {
const baseId = gcId.split(':')[0];
if (BLOCK_MAP.has(baseId)) {
const entry = BLOCK_MAP.get(baseId);
return { block: entry.bedrock, data: entry.data, matched: true, name: entry.name };
}
}
// Try name-based lookup
if (gcName) {
const lower = gcName.toLowerCase().trim();
if (NAME_MAP.has(lower)) {
const entry = NAME_MAP.get(lower);
return { block: entry.bedrock, data: entry.data, matched: true, name: entry.name };
}
// Fuzzy: try removing common suffixes/prefixes
const simplified = lower
.replace(/\b(block of|block)\b/g, '')
.replace(/\s+/g, ' ')
.trim();
if (NAME_MAP.has(simplified)) {
const entry = NAME_MAP.get(simplified);
return { block: entry.bedrock, data: entry.data, matched: true, name: entry.name };
}
// Partial match: check if any name contains the search term
for (const [name, entry] of NAME_MAP) {
if (name.includes(lower) || lower.includes(name)) {
return { block: entry.bedrock, data: entry.data, matched: true, name: entry.name };
}
}
}
// Track unknown block
const key = `${gcId || 'unknown'}:${gcName || 'unnamed'}`;
unknownBlocks.set(key, (unknownBlocks.get(key) || 0) + 1);
// Fallback to stone
log(TAG, `Unknown block: id=${gcId}, name=${gcName} — using stone fallback`);
return { block: 'stone', data: 0, matched: false, name: gcName || `Unknown(${gcId})` };
}
/**
* Format a resolved block for a setblock command.
* @param {{ block: string, data: number }} resolved
* @returns {string} e.g. "planks 5" or "stone 0"
*/
export function formatBlock(resolved) {
return resolved.data > 0 ? `${resolved.block} ${resolved.data}` : resolved.block;
}
/**
* Get all unknown blocks encountered so far (for reporting).
* @returns {Map<string, number>}
*/
export function getUnknownBlocks() {
return new Map(unknownBlocks);
}
/**
* Clear the unknown blocks tracker.
*/
export function clearUnknownBlocks() {
unknownBlocks.clear();
}
/**
* Get all known block mappings (for MCP resource).
* @returns {Array<{ javaId: string, bedrockId: string, bedrockData: number, name: string }>}
*/
export function getAllBlocks() {
const blocks = [];
for (const [javaId, entry] of BLOCK_MAP) {
blocks.push({
javaId,
bedrockId: entry.bedrock,
bedrockData: entry.data,
name: entry.name,
});
}
return blocks;
}