mca-json 1.0.5 → 1.1.0

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.
Files changed (36) hide show
  1. package/README.md +41 -8
  2. package/dist/bin/mca-find-signs.d.ts +3 -0
  3. package/dist/bin/mca-find-signs.d.ts.map +1 -0
  4. package/dist/bin/mca-find-signs.js +148 -0
  5. package/dist/bin/mca-find-signs.js.map +1 -0
  6. package/dist/bin/mca-remove-chunks.d.ts +3 -0
  7. package/dist/bin/mca-remove-chunks.d.ts.map +1 -0
  8. package/dist/bin/mca-remove-chunks.js +144 -0
  9. package/dist/bin/mca-remove-chunks.js.map +1 -0
  10. package/dist/bin/nbt-get-player-location.js +18 -9
  11. package/dist/bin/nbt-get-player-location.js.map +1 -1
  12. package/dist/lib/anvil.d.ts.map +1 -1
  13. package/dist/lib/anvil.js +4 -3
  14. package/dist/lib/anvil.js.map +1 -1
  15. package/dist/lib/bit-data.js +2 -2
  16. package/dist/lib/bit-data.js.map +1 -1
  17. package/dist/lib/chunk.d.ts.map +1 -1
  18. package/dist/lib/chunk.js +12 -7
  19. package/dist/lib/chunk.js.map +1 -1
  20. package/package.json +2 -2
  21. package/src/bin/mca-find-signs.ts +214 -0
  22. package/src/bin/mca-remove-chunks.ts +183 -0
  23. package/src/bin/nbt-get-player-location.ts +19 -11
  24. package/src/lib/anvil.ts +5 -3
  25. package/src/lib/bit-data.ts +2 -2
  26. package/src/lib/chunk.ts +15 -7
  27. package/dist/bin/mca-find-chunks-with-signs.d.ts +0 -3
  28. package/dist/bin/mca-find-chunks-with-signs.d.ts.map +0 -1
  29. package/dist/bin/mca-find-chunks-with-signs.js +0 -79
  30. package/dist/bin/mca-find-chunks-with-signs.js.map +0 -1
  31. package/dist/bin/mca-trim-chunks-without-signs.d.ts +0 -3
  32. package/dist/bin/mca-trim-chunks-without-signs.d.ts.map +0 -1
  33. package/dist/bin/mca-trim-chunks-without-signs.js +0 -117
  34. package/dist/bin/mca-trim-chunks-without-signs.js.map +0 -1
  35. package/src/bin/mca-find-chunks-with-signs.ts +0 -109
  36. package/src/bin/mca-trim-chunks-without-signs.ts +0 -146
@@ -1 +1 @@
1
- {"version":3,"file":"chunk.d.ts","sourceRoot":"","sources":["../../src/lib/chunk.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAS,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM1C,qBAAa,KAAK;IACd,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,OAAO,CAAc;IAE7B;;;;OAIG;gBACS,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;IAW9B;;OAEG;IACH,aAAa,IAAI,OAAO,CAAC,WAAW,CAAC,GAAG,SAAS;IASjD;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,SAAS;IAc1D;;;;OAIG;IACH,gBAAgB,IAAI,QAAQ,GAAG,SAAS;IAmBxC;;;OAGG;IACH,QAAQ,IAAI,MAAM,GAAG,SAAS;IAU9B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,aAAa;IAuCzC;;OAEG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE;IAoC1C;;;;;;;OAOG;IACH,aAAa,IAAI,MAAM,GAAG,SAAS;IASnC;;OAEG;IACH,QAAQ,IAAI,GAAG;IAIf;;OAEG;IACH,gBAAgB,IAAI,GAAG,CAAC,MAAM,CAAC;IAoB/B;;;;;;OAMG;IACH,gBAAgB,IAAI,QAAQ,GAAG,SAAS;IAUxC;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;OAEG;IACH,OAAO,CAAC,eAAe;IAYvB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IA2BpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAWtB;;OAEG;IACH,OAAO,CAAC,WAAW;IAcnB;;OAEG;IACH,OAAO,CAAC,QAAQ;CAenB"}
1
+ {"version":3,"file":"chunk.d.ts","sourceRoot":"","sources":["../../src/lib/chunk.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAS,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM1C,qBAAa,KAAK;IACd,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,OAAO,CAAc;IAE7B;;;;OAIG;gBACS,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;IAW9B;;OAEG;IACH,aAAa,IAAI,OAAO,CAAC,WAAW,CAAC,GAAG,SAAS;IASjD;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,SAAS;IAc1D;;;;OAIG;IACH,gBAAgB,IAAI,QAAQ,GAAG,SAAS;IA0BxC;;;OAGG;IACH,QAAQ,IAAI,MAAM,GAAG,SAAS;IAU9B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,aAAa;IAuCzC;;OAEG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE;IAoC1C;;;;;;;OAOG;IACH,aAAa,IAAI,MAAM,GAAG,SAAS;IASnC;;OAEG;IACH,QAAQ,IAAI,GAAG;IAIf;;OAEG;IACH,gBAAgB,IAAI,GAAG,CAAC,MAAM,CAAC;IAoB/B;;;;;;OAMG;IACH,gBAAgB,IAAI,QAAQ,GAAG,SAAS;IAUxC;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;OAEG;IACH,OAAO,CAAC,eAAe;IAYvB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IA2BpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAWtB;;OAEG;IACH,OAAO,CAAC,WAAW;IAcnB;;OAEG;IACH,OAAO,CAAC,QAAQ;CAenB"}
package/dist/lib/chunk.js CHANGED
@@ -55,19 +55,24 @@ export class Chunk {
55
55
  */
56
56
  chunkCoordinates() {
57
57
  const x =
58
- // 1.18+
58
+ // 1.18+ region chunks
59
59
  this.rootNbt.findChild('xPos') ||
60
- // up to 1.17
60
+ // up to 1.17 region chunks
61
61
  this.rootNbt.findChild('Level/xPos');
62
62
  const z =
63
- // 1.18+
63
+ // 1.18+ region chunks
64
64
  this.rootNbt.findChild('zPos') ||
65
- // up to 1.17
65
+ // up to 1.17 region chunks
66
66
  this.rootNbt.findChild('Level/zPos');
67
- if (typeof x?.data !== 'number' || typeof z?.data !== 'number') {
68
- return;
67
+ if (typeof x?.data === 'number' && typeof z?.data === 'number') {
68
+ return [x.data, z.data];
69
+ }
70
+ // Check for entity chunks
71
+ const position = this.rootNbt.findChild('Position');
72
+ if (position && position.data.length === 2) {
73
+ return [position.data[0], position.data[1]];
69
74
  }
70
- return [x.data, z.data];
75
+ return;
71
76
  }
72
77
  /**
73
78
  * Returns a unique id for this chunk. It's just the chunk's X and Z
@@ -1 +1 @@
1
- {"version":3,"file":"chunk.js","sourceRoot":"","sources":["../../src/lib/chunk.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A Chunk is a 16 x 383 x 16 portion of a Minecraft world. It's stored as NBT\n * data in sections of 16 blocks in the Y axis. The Y axis range is from -64 to\n * 320.\n *\n * This class helps manipulate the chunk data.\n */\n\nimport { Block, BlockInstance } from '../block/block';\nimport { BlockData } from './block-data';\nimport { Coords2d, Coords3d } from '../types/coords';\nimport { NbtBase } from '../nbt/nbt-base';\nimport { NbtCompound } from '../nbt/nbt-compound';\nimport { NbtInt } from '../nbt/nbt-int';\nimport { NbtList } from '../nbt/nbt-list';\nimport { NbtLong } from '../nbt/nbt-long';\nimport { NbtLongArray } from '../nbt/nbt-long-array';\nimport { NbtString } from '../nbt/nbt-string';\nimport { NbtTagType } from '../nbt/nbt-tag-type';\n\nexport class Chunk {\n private dataVersion: number;\n private parsedSections = new Map<NbtCompound, BlockData>();\n private rootNbt: NbtCompound;\n\n /**\n * Constructs a new chunk object from a given NBT tag.\n *\n * @param root the NBT tag containing the chunk's data\n */\n constructor(root: NbtBase<any>) {\n if (!this.isValidChunkRoot(root)) {\n throw new Error('Invalid chunk root tag');\n }\n\n this.rootNbt = root;\n\n // 1.2.1 does not have this tag.\n this.dataVersion = root.findChild<NbtInt>('DataVersion')?.data ?? 0;\n }\n\n /**\n * Gets the block entity list tag.\n */\n blockEntities(): NbtList<NbtCompound> | undefined {\n return (\n // 1.18+\n this.rootNbt.findChild<NbtList<NbtCompound>>('block_entities') ||\n // up to 1.17\n this.rootNbt.findChild<NbtList<NbtCompound>>('Level/TileEntities')\n );\n }\n\n /**\n * Gets a block's entity data\n */\n blockEntityData(coords: Coords3d): NbtCompound | undefined {\n return this.blockEntities()?.data.find((tag: NbtCompound) => {\n const x = tag.findChild<NbtInt>('x');\n const y = tag.findChild<NbtInt>('y');\n const z = tag.findChild<NbtInt>('z');\n\n return (\n x?.data === coords[0] &&\n y?.data === coords[1] &&\n z?.data === coords[2]\n );\n });\n }\n\n /**\n * Gets the chunk's location in chunk coordinates.\n *\n * Returns a tuple of the chunk's X and Z coordinates.\n */\n chunkCoordinates(): Coords2d | undefined {\n const x =\n // 1.18+\n this.rootNbt.findChild<NbtInt>('xPos') ||\n // up to 1.17\n this.rootNbt.findChild<NbtInt>('Level/xPos');\n const z =\n // 1.18+\n this.rootNbt.findChild<NbtInt>('zPos') ||\n // up to 1.17\n this.rootNbt.findChild<NbtInt>('Level/zPos');\n\n if (typeof x?.data !== 'number' || typeof z?.data !== 'number') {\n return;\n }\n\n return [x.data, z.data];\n }\n\n /**\n * Returns a unique id for this chunk. It's just the chunk's X and Z\n * coordinates in number of chunks from the world's origin.\n */\n chunkKey(): string | undefined {\n const coords = this.chunkCoordinates();\n\n if (!coords) {\n return;\n }\n\n return `${coords[0]},${coords[1]}`;\n }\n\n /**\n * Returns an instance of a block at a given location using world\n * coordinates.\n */\n getBlock(coords: Coords3d): BlockInstance {\n const sectionY = Math.floor(coords[1] / 16);\n const sectionsTag = this.sectionsTag();\n\n if (!sectionsTag) {\n throw new Error('Invalid chunk - no sections found');\n }\n\n const section = sectionsTag.data.find(\n (section) => this.sectionY(section) === sectionY\n );\n\n if (!section) {\n throw new Error('Block not found in any sections');\n }\n\n const blockData = this.parseSection(section);\n const [xWorld, zWorld] = this.worldCoordinates() || [0, 0];\n const xChunk = coords[0] - xWorld;\n const zChunk = coords[2] - zWorld;\n\n if (xChunk < 0 || xChunk > 15) {\n throw new Error('X coordinate out of bounds');\n }\n\n if (zChunk < 0 || zChunk > 15) {\n throw new Error('Z coordinate out of bounds');\n }\n\n const index = blockData.chunkCoordinatesToIndex([\n xChunk,\n coords[1] % 16,\n zChunk,\n ]);\n const name = blockData.getBlockByIndex(index);\n\n return Block.create(name, coords, this.blockEntityData(coords));\n }\n\n /**\n * Finds all blocks by a given name. Provides [X, Y, Z] real-world coordinates.\n */\n findBlocksByName(name: string): Coords3d[] {\n const sectionsTag = this.sectionsTag();\n\n if (!sectionsTag) {\n return [];\n }\n\n return sectionsTag.data.flatMap((section) => {\n const yWorld = this.sectionY(section, true);\n const [xWorld, zWorld] = this.worldCoordinates() || [0, 0];\n const palette = this.sectionPalette(section);\n\n if (palette === undefined) {\n return [];\n }\n\n // // If it's not in the palette, we can skip processing.\n if (!this.paletteNameList(palette).includes(name)) {\n return [];\n }\n\n const blockData = this.parseSection(section);\n\n return blockData.findBlocksByName(name).map((index): Coords3d => {\n const chunkCoordinates =\n blockData.indexToChunkCoordinates(index);\n\n return [\n xWorld + chunkCoordinates[0],\n yWorld + chunkCoordinates[1],\n zWorld + chunkCoordinates[2],\n ];\n });\n });\n }\n\n /**\n * See how long the chunk has been inhabited. This number increases by 1\n * for each game tick, which is 20 times per second when a player is\n * nearby. It can also advance slowly when players are farther away.\n *\n * On newly created chunks, this value does not match what is expected. Use\n * this with caution.\n */\n inhabitedTime(): bigint | undefined {\n return (\n // 1.18+\n this.rootNbt.findChild<NbtLong>('InhabitedTime')?.data ||\n // up to 1.17\n this.rootNbt.findChild<NbtLong>('Level/InhabitedTime')?.data\n );\n }\n\n /**\n * Converts the chunk data to a JSON object.\n */\n toObject(): any {\n return this.rootNbt.toObject();\n }\n\n /**\n * Returns a list of unique block names in the chunk as a Set.\n */\n uniqueBlockNames(): Set<string> {\n const sectionsTag = this.sectionsTag();\n\n if (!sectionsTag) {\n return new Set();\n }\n\n const names = sectionsTag.data.flatMap((section) => {\n const palette = this.sectionPalette(section);\n\n if (!palette) {\n return [];\n }\n\n return this.paletteNameList(palette);\n });\n\n return new Set(names);\n }\n\n /**\n * Gets the chunk's location in real-world coordinates.\n *\n * Returns a tuple of the starting block's world X and Z coordinates. The\n * chunk contains the range from X to X+15 (going East), Y to Y+15 (going\n * up), and Z to Z+15 (going South).\n */\n worldCoordinates(): Coords2d | undefined {\n const result = this.chunkCoordinates();\n\n if (!result) {\n return;\n }\n\n return [result[0] * 16, result[1] * 16];\n }\n\n /**\n * Checks if the given NBT tag is a valid chunk root tag.\n * @param tag the tag to check.\n */\n private isValidChunkRoot(nbt: NbtBase<any>): nbt is NbtCompound {\n return nbt.isCompound('');\n }\n\n /**\n * Checks if the given NBT tag is a valid chunk section tag, containing a list of chunk sections.\n */\n private isValidChunkSection(\n nbt: NbtBase<any>\n ): nbt is NbtList<NbtCompound> {\n if (!nbt.isList(NbtTagType.COMPOUND)) {\n return false;\n }\n\n // Java uses \"sections\", unsure what uses \"Sections\"\n if (nbt.name.toLowerCase() === 'sections') {\n return true;\n }\n\n return false;\n }\n\n /**\n * Returns a list of names within a palette tag.\n */\n private paletteNameList(palette: NbtBase<any>): string[] {\n if (!palette.isList(NbtTagType.COMPOUND)) {\n return [];\n }\n\n return (palette as NbtList<NbtCompound>).data.map((tag) => {\n const name = tag.findChild<NbtString>('Name');\n\n return name?.data ?? '';\n });\n }\n\n /**\n * Parses a section tag into BlockData.\n *\n * Uses a cached version if available;\n */\n private parseSection(section: NbtCompound): BlockData {\n if (this.parsedSections.has(section)) {\n return this.parsedSections.get(section)!;\n }\n\n const palette = this.sectionPalette(section);\n\n if (!palette) {\n throw new Error('Invalid chunk section - no palette found');\n }\n\n const blockStates = this.sectionBlockStates(section);\n\n if (!blockStates) {\n throw new Error('Invalid chunk section - no block states found');\n }\n\n const blockData = BlockData.fromPaletteBlockStates(\n this.dataVersion,\n palette,\n blockStates\n );\n this.parsedSections.set(section, blockData);\n\n return blockData;\n }\n\n /**\n * Finds the BlockStates tag in a section.\n */\n private sectionBlockStates(section: NbtCompound): NbtLongArray | undefined {\n return (\n // 1.18+\n section.findChild<NbtLongArray>('block_states/data') ||\n // up to 1.17\n section.findChild<NbtLongArray>('BlockStates')\n );\n }\n\n /**\n * Finds the block palette in a section.\n */\n private sectionPalette(\n section: NbtCompound\n ): NbtList<NbtCompound> | undefined {\n return (\n // 1.18+\n section.findChild<NbtList<NbtCompound>>('block_states/palette') ||\n // up to 1.17\n section.findChild<NbtList<NbtCompound>>('Palette')\n );\n }\n\n /**\n * Returns all sections\n */\n private sectionsTag(): NbtList<NbtCompound> | undefined {\n const section =\n // 1.18+\n this.rootNbt.findChild<NbtList<NbtCompound>>('sections') ||\n // up to 1.17\n this.rootNbt.findChild<NbtList<NbtCompound>>('Level/Sections');\n\n if (section && this.isValidChunkSection(section)) {\n return section;\n }\n\n return;\n }\n\n /**\n * Returns the Y coordinate of a section, which goes from 0 to 256. If you want the world coordinate, pass true to get -128 to 4031.\n */\n private sectionY(section: NbtCompound, useWorldCoordinate = false): number {\n const ySection = section.findChild<NbtInt>('Y')?.data || 0;\n\n if (!useWorldCoordinate) {\n return ySection;\n }\n\n let yWorld = ySection * 16;\n\n if (yWorld >= 4032) {\n yWorld -= 4096;\n }\n\n return yWorld;\n }\n}\n"]}
1
+ {"version":3,"file":"chunk.js","sourceRoot":"","sources":["../../src/lib/chunk.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A Chunk is a 16 x 383 x 16 portion of a Minecraft world. It's stored as NBT\n * data in sections of 16 blocks in the Y axis. The Y axis range is from -64 to\n * 320.\n *\n * This class helps manipulate the chunk data.\n */\n\nimport { Block, BlockInstance } from '../block/block';\nimport { BlockData } from './block-data';\nimport { Coords2d, Coords3d } from '../types/coords';\nimport { NbtBase } from '../nbt/nbt-base';\nimport { NbtCompound } from '../nbt/nbt-compound';\nimport { NbtInt } from '../nbt/nbt-int';\nimport { NbtIntArray } from '../nbt/nbt-int-array';\nimport { NbtList } from '../nbt/nbt-list';\nimport { NbtLong } from '../nbt/nbt-long';\nimport { NbtLongArray } from '../nbt/nbt-long-array';\nimport { NbtString } from '../nbt/nbt-string';\nimport { NbtTagType } from '../nbt/nbt-tag-type';\n\nexport class Chunk {\n private dataVersion: number;\n private parsedSections = new Map<NbtCompound, BlockData>();\n private rootNbt: NbtCompound;\n\n /**\n * Constructs a new chunk object from a given NBT tag.\n *\n * @param root the NBT tag containing the chunk's data\n */\n constructor(root: NbtBase<any>) {\n if (!this.isValidChunkRoot(root)) {\n throw new Error('Invalid chunk root tag');\n }\n\n this.rootNbt = root;\n\n // 1.2.1 does not have this tag.\n this.dataVersion = root.findChild<NbtInt>('DataVersion')?.data ?? 0;\n }\n\n /**\n * Gets the block entity list tag.\n */\n blockEntities(): NbtList<NbtCompound> | undefined {\n return (\n // 1.18+\n this.rootNbt.findChild<NbtList<NbtCompound>>('block_entities') ||\n // up to 1.17\n this.rootNbt.findChild<NbtList<NbtCompound>>('Level/TileEntities')\n );\n }\n\n /**\n * Gets a block's entity data\n */\n blockEntityData(coords: Coords3d): NbtCompound | undefined {\n return this.blockEntities()?.data.find((tag: NbtCompound) => {\n const x = tag.findChild<NbtInt>('x');\n const y = tag.findChild<NbtInt>('y');\n const z = tag.findChild<NbtInt>('z');\n\n return (\n x?.data === coords[0] &&\n y?.data === coords[1] &&\n z?.data === coords[2]\n );\n });\n }\n\n /**\n * Gets the chunk's location in chunk coordinates.\n *\n * Returns a tuple of the chunk's X and Z coordinates.\n */\n chunkCoordinates(): Coords2d | undefined {\n const x =\n // 1.18+ region chunks\n this.rootNbt.findChild<NbtInt>('xPos') ||\n // up to 1.17 region chunks\n this.rootNbt.findChild<NbtInt>('Level/xPos');\n const z =\n // 1.18+ region chunks\n this.rootNbt.findChild<NbtInt>('zPos') ||\n // up to 1.17 region chunks\n this.rootNbt.findChild<NbtInt>('Level/zPos');\n\n if (typeof x?.data === 'number' && typeof z?.data === 'number') {\n return [x.data, z.data];\n }\n\n // Check for entity chunks\n const position = this.rootNbt.findChild<NbtIntArray>('Position');\n\n if (position && position.data.length === 2) {\n return [position.data[0], position.data[1]];\n }\n\n return;\n }\n\n /**\n * Returns a unique id for this chunk. It's just the chunk's X and Z\n * coordinates in number of chunks from the world's origin.\n */\n chunkKey(): string | undefined {\n const coords = this.chunkCoordinates();\n\n if (!coords) {\n return;\n }\n\n return `${coords[0]},${coords[1]}`;\n }\n\n /**\n * Returns an instance of a block at a given location using world\n * coordinates.\n */\n getBlock(coords: Coords3d): BlockInstance {\n const sectionY = Math.floor(coords[1] / 16);\n const sectionsTag = this.sectionsTag();\n\n if (!sectionsTag) {\n throw new Error('Invalid chunk - no sections found');\n }\n\n const section = sectionsTag.data.find(\n (section) => this.sectionY(section) === sectionY\n );\n\n if (!section) {\n throw new Error('Block not found in any sections');\n }\n\n const blockData = this.parseSection(section);\n const [xWorld, zWorld] = this.worldCoordinates() || [0, 0];\n const xChunk = coords[0] - xWorld;\n const zChunk = coords[2] - zWorld;\n\n if (xChunk < 0 || xChunk > 15) {\n throw new Error('X coordinate out of bounds');\n }\n\n if (zChunk < 0 || zChunk > 15) {\n throw new Error('Z coordinate out of bounds');\n }\n\n const index = blockData.chunkCoordinatesToIndex([\n xChunk,\n coords[1] % 16,\n zChunk,\n ]);\n const name = blockData.getBlockByIndex(index);\n\n return Block.create(name, coords, this.blockEntityData(coords));\n }\n\n /**\n * Finds all blocks by a given name. Provides [X, Y, Z] real-world coordinates.\n */\n findBlocksByName(name: string): Coords3d[] {\n const sectionsTag = this.sectionsTag();\n\n if (!sectionsTag) {\n return [];\n }\n\n return sectionsTag.data.flatMap((section) => {\n const yWorld = this.sectionY(section, true);\n const [xWorld, zWorld] = this.worldCoordinates() || [0, 0];\n const palette = this.sectionPalette(section);\n\n if (palette === undefined) {\n return [];\n }\n\n // // If it's not in the palette, we can skip processing.\n if (!this.paletteNameList(palette).includes(name)) {\n return [];\n }\n\n const blockData = this.parseSection(section);\n\n return blockData.findBlocksByName(name).map((index): Coords3d => {\n const chunkCoordinates =\n blockData.indexToChunkCoordinates(index);\n\n return [\n xWorld + chunkCoordinates[0],\n yWorld + chunkCoordinates[1],\n zWorld + chunkCoordinates[2],\n ];\n });\n });\n }\n\n /**\n * See how long the chunk has been inhabited. This number increases by 1\n * for each game tick, which is 20 times per second when a player is\n * nearby. It can also advance slowly when players are farther away.\n *\n * On newly created chunks, this value does not match what is expected. Use\n * this with caution.\n */\n inhabitedTime(): bigint | undefined {\n return (\n // 1.18+\n this.rootNbt.findChild<NbtLong>('InhabitedTime')?.data ||\n // up to 1.17\n this.rootNbt.findChild<NbtLong>('Level/InhabitedTime')?.data\n );\n }\n\n /**\n * Converts the chunk data to a JSON object.\n */\n toObject(): any {\n return this.rootNbt.toObject();\n }\n\n /**\n * Returns a list of unique block names in the chunk as a Set.\n */\n uniqueBlockNames(): Set<string> {\n const sectionsTag = this.sectionsTag();\n\n if (!sectionsTag) {\n return new Set();\n }\n\n const names = sectionsTag.data.flatMap((section) => {\n const palette = this.sectionPalette(section);\n\n if (!palette) {\n return [];\n }\n\n return this.paletteNameList(palette);\n });\n\n return new Set(names);\n }\n\n /**\n * Gets the chunk's location in real-world coordinates.\n *\n * Returns a tuple of the starting block's world X and Z coordinates. The\n * chunk contains the range from X to X+15 (going East), Y to Y+15 (going\n * up), and Z to Z+15 (going South).\n */\n worldCoordinates(): Coords2d | undefined {\n const result = this.chunkCoordinates();\n\n if (!result) {\n return;\n }\n\n return [result[0] * 16, result[1] * 16];\n }\n\n /**\n * Checks if the given NBT tag is a valid chunk root tag.\n * @param tag the tag to check.\n */\n private isValidChunkRoot(nbt: NbtBase<any>): nbt is NbtCompound {\n return nbt.isCompound('');\n }\n\n /**\n * Checks if the given NBT tag is a valid chunk section tag, containing a list of chunk sections.\n */\n private isValidChunkSection(\n nbt: NbtBase<any>\n ): nbt is NbtList<NbtCompound> {\n if (!nbt.isList(NbtTagType.COMPOUND)) {\n return false;\n }\n\n // Java uses \"sections\", unsure what uses \"Sections\"\n if (nbt.name.toLowerCase() === 'sections') {\n return true;\n }\n\n return false;\n }\n\n /**\n * Returns a list of names within a palette tag.\n */\n private paletteNameList(palette: NbtBase<any>): string[] {\n if (!palette.isList(NbtTagType.COMPOUND)) {\n return [];\n }\n\n return (palette as NbtList<NbtCompound>).data.map((tag) => {\n const name = tag.findChild<NbtString>('Name');\n\n return name?.data ?? '';\n });\n }\n\n /**\n * Parses a section tag into BlockData.\n *\n * Uses a cached version if available;\n */\n private parseSection(section: NbtCompound): BlockData {\n if (this.parsedSections.has(section)) {\n return this.parsedSections.get(section)!;\n }\n\n const palette = this.sectionPalette(section);\n\n if (!palette) {\n throw new Error('Invalid chunk section - no palette found');\n }\n\n const blockStates = this.sectionBlockStates(section);\n\n if (!blockStates) {\n throw new Error('Invalid chunk section - no block states found');\n }\n\n const blockData = BlockData.fromPaletteBlockStates(\n this.dataVersion,\n palette,\n blockStates\n );\n this.parsedSections.set(section, blockData);\n\n return blockData;\n }\n\n /**\n * Finds the BlockStates tag in a section.\n */\n private sectionBlockStates(section: NbtCompound): NbtLongArray | undefined {\n return (\n // 1.18+\n section.findChild<NbtLongArray>('block_states/data') ||\n // up to 1.17\n section.findChild<NbtLongArray>('BlockStates')\n );\n }\n\n /**\n * Finds the block palette in a section.\n */\n private sectionPalette(\n section: NbtCompound\n ): NbtList<NbtCompound> | undefined {\n return (\n // 1.18+\n section.findChild<NbtList<NbtCompound>>('block_states/palette') ||\n // up to 1.17\n section.findChild<NbtList<NbtCompound>>('Palette')\n );\n }\n\n /**\n * Returns all sections\n */\n private sectionsTag(): NbtList<NbtCompound> | undefined {\n const section =\n // 1.18+\n this.rootNbt.findChild<NbtList<NbtCompound>>('sections') ||\n // up to 1.17\n this.rootNbt.findChild<NbtList<NbtCompound>>('Level/Sections');\n\n if (section && this.isValidChunkSection(section)) {\n return section;\n }\n\n return;\n }\n\n /**\n * Returns the Y coordinate of a section, which goes from 0 to 256. If you want the world coordinate, pass true to get -128 to 4031.\n */\n private sectionY(section: NbtCompound, useWorldCoordinate = false): number {\n const ySection = section.findChild<NbtInt>('Y')?.data || 0;\n\n if (!useWorldCoordinate) {\n return ySection;\n }\n\n let yWorld = ySection * 16;\n\n if (yWorld >= 4032) {\n yWorld -= 4096;\n }\n\n return yWorld;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mca-json",
3
- "version": "1.0.5",
3
+ "version": "1.1.0",
4
4
  "description": "Convert Minecraft MCA (Anvil files) and NBT into JSON and back, or modify directly in memory. Works on region files and player data.",
5
5
  "bin": {
6
6
  "mca-chunks": "dist/bin/mca-chunks.js",
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/debug": "^4.1.12",
24
- "@types/node": "^25.2.3",
24
+ "@types/node": "^25.3.0",
25
25
  "@types/pako": "^2.0.4",
26
26
  "typescript": "^5.9.3"
27
27
  }
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+
3
+ import debug from 'debug';
4
+ import neodoc from 'neodoc';
5
+ import { Anvil } from '../lib/anvil';
6
+ import { Chunk } from '../lib/chunk';
7
+ import { Sign } from '../block/sign';
8
+ import { readFile } from 'node:fs/promises';
9
+
10
+ interface SignInfo {
11
+ chunkX: number;
12
+ chunkZ: number;
13
+ x: number;
14
+ y: number;
15
+ z: number;
16
+ front: string[];
17
+ back: string[] | null;
18
+ }
19
+
20
+ interface Args {
21
+ '--blank'?: boolean;
22
+ '--generated'?: boolean;
23
+ '--first'?: boolean;
24
+ '--user'?: boolean;
25
+ '--verbose'?: boolean;
26
+ MCA_FILE?: string[];
27
+ }
28
+
29
+ const debugLog = debug('mca-find-signs');
30
+ const args: Args = neodoc.run(
31
+ `Usage: mca-find-signs [OPTIONS] MCA_FILE...
32
+
33
+ Extracts sign coordinates from MCA files. This is designed to find different
34
+ categories of signs.
35
+
36
+ * Blank signs are blank on both sides.
37
+ * Generated signs can be blank or have any non-numeric and non-alphabetic
38
+ characters. This is so igloos with "<----" and "---->" are included.
39
+ * User-created signs must have at least a number or a letter on the front or
40
+ the back. There's no way to tell from the block data that a user placed a
41
+ sign, but this is a good heuristic.
42
+
43
+ The default output is a JSON array that shows the following information:
44
+
45
+ [
46
+ {
47
+ "chunkX": 0,
48
+ "chunkZ": 3,
49
+ "x": 12,
50
+ "y": 34,
51
+ "z": 56,
52
+ "front": ["Line 1", "Line 2", "Line 3", "Line 4"],
53
+ "back": ["Line 1", "Line 2", "Line 3", "Line 4"]
54
+ },
55
+ ...
56
+ ]
57
+
58
+ Options:
59
+ --blank Only show blank signs. Conflicts with --generated and --user.
60
+ --generated Only show generated signs. Conflicts with --blank and --user.
61
+ --user Only show user-created signs. Conflicts with --blank and --generated.
62
+ --first Stop scanning a chunk after the first sign is found.
63
+ -h, --help Show this message.
64
+ -v, --verbose Show more information during processing.
65
+ `,
66
+ {
67
+ argv: [...process.argv].slice(2),
68
+ laxPlacement: true,
69
+ }
70
+ );
71
+
72
+ if (!args.MCA_FILE?.length) {
73
+ console.error('No files specified.');
74
+ process.exit(1);
75
+ }
76
+
77
+ if (
78
+ (args['--blank'] ? 1 : 0) +
79
+ (args['--generated'] ? 1 : 0) +
80
+ (args['--user'] ? 1 : 0) >
81
+ 1
82
+ ) {
83
+ console.error(
84
+ 'Conflicting options specified. Please choose only one of --blank, --generated, or --user.'
85
+ );
86
+ process.exit(1);
87
+ }
88
+
89
+ const signs: SignInfo[] = [];
90
+
91
+ for (const file of args.MCA_FILE) {
92
+ try {
93
+ const moreSigns = await processFile(file);
94
+ signs.push(...moreSigns);
95
+ } catch (e) {
96
+ console.error(`Error processing file ${file}: ${e}`);
97
+
98
+ process.exit(1);
99
+ }
100
+ }
101
+
102
+ console.log(JSON.stringify(signs, null, 4));
103
+
104
+ async function processFile(filename: string): Promise<SignInfo[]> {
105
+ debugLog(`Reading file: ${filename}`);
106
+
107
+ if (args['--verbose']) {
108
+ console.log(`Reading file: ${filename}`);
109
+ }
110
+
111
+ const contents = await readFile(filename);
112
+ const anvil = Anvil.fromBuffer(contents.buffer);
113
+ const chunks = anvil.getAllChunks();
114
+ const allSignsFound: SignInfo[] = [];
115
+
116
+ for (const chunk of chunks) {
117
+ const chunkKey = chunk.chunkKey() || '';
118
+ const signs = processChunk(chunk);
119
+
120
+ if (signs.length > 0) {
121
+ debugLog(`Found ${signs.length} signs in chunk ${chunkKey}.`);
122
+
123
+ if (args['--verbose']) {
124
+ console.log(
125
+ `Found ${signs.length} signs in chunk ${chunkKey}.`
126
+ );
127
+ }
128
+
129
+ allSignsFound.push(...signs);
130
+ }
131
+ }
132
+
133
+ return allSignsFound;
134
+ }
135
+
136
+ function processChunk(chunk: Chunk): SignInfo[] {
137
+ const blockNames = chunk.uniqueBlockNames();
138
+ const foundSigns: SignInfo[] = [];
139
+
140
+ for (const blockName of blockNames) {
141
+ if (blockName.match(/^minecraft:.+_sign$/)) {
142
+ debugLog(`Found sign: ${blockName}`);
143
+
144
+ const listOfBlockCoordinates = chunk.findBlocksByName(blockName);
145
+
146
+ if (listOfBlockCoordinates.length === 0) {
147
+ debugLog(
148
+ `Possible corruption - no blocks found for sign: ${blockName}`
149
+ );
150
+ }
151
+
152
+ for (const coords of listOfBlockCoordinates) {
153
+ debugLog(`Checking block at coordinates: ${coords}`);
154
+ const block = chunk.getBlock(coords);
155
+
156
+ if (!(block instanceof Sign)) {
157
+ throw new Error(
158
+ `Block is supposed to be a sign: ${block.name}`
159
+ );
160
+ }
161
+
162
+ const front = block.frontText();
163
+ const back = block.backText();
164
+ const text = [...front, ...(back ?? [])].join('');
165
+ debugLog(`Front: ${JSON.stringify(front)}`);
166
+ debugLog(`Back: ${JSON.stringify(back)}`);
167
+
168
+ if (args['--blank'] && text.length !== 0) {
169
+ debugLog(`Skipping non-blank sign.`);
170
+ continue;
171
+ }
172
+
173
+ if (args['--generated'] && !text.match(/^[^a-z0-9]*$/i)) {
174
+ debugLog(`Skipping user-created sign.`);
175
+ }
176
+
177
+ if (args['--user'] && !text.match(/[a-z0-9]/i)) {
178
+ debugLog(`Skipping a possible generated sign.`);
179
+ continue;
180
+ }
181
+
182
+ debugLog(`Found a matching sign.`);
183
+
184
+ if (args['--verbose']) {
185
+ console.log(
186
+ `Found a matching sign: ${JSON.stringify(front)} and ${JSON.stringify(back)}`
187
+ );
188
+ }
189
+
190
+ const chunkCoordinates = chunk.chunkCoordinates();
191
+
192
+ if (!chunkCoordinates) {
193
+ throw new Error('Chunk coordinates not found.');
194
+ }
195
+
196
+ foundSigns.push({
197
+ chunkX: chunkCoordinates[0],
198
+ chunkZ: chunkCoordinates[1],
199
+ x: coords[0],
200
+ y: coords[1],
201
+ z: coords[2],
202
+ front,
203
+ back: back ?? null,
204
+ });
205
+
206
+ if (args['--first']) {
207
+ return foundSigns;
208
+ }
209
+ }
210
+ }
211
+ }
212
+
213
+ return foundSigns;
214
+ }
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+
3
+ import debug from 'debug';
4
+ import neodoc from 'neodoc';
5
+ import { Anvil } from '../lib/anvil';
6
+ import { readFile, writeFile } from 'node:fs/promises';
7
+
8
+ const debugLog = debug('mca-remove-chunks');
9
+ const args: {
10
+ '--dry-run'?: boolean;
11
+ '--remove'?: string[];
12
+ '--remove-list'?: string;
13
+ '--preserve'?: string[];
14
+ '--preserve-list'?: string;
15
+ '--verbose'?: boolean;
16
+ MCA_FILE?: string[];
17
+ } = neodoc.run(
18
+ `Usage: mca-remove-chunks [OPTIONS] MCA_FILE...
19
+
20
+ Removes chunks from MCA files (region/r.*.*.mca or entities/r.*.*.mca).
21
+
22
+ This does not change the file size. Similar to deleting a file on a hard drive,
23
+ the space is marked as free but not actually removed. Minecraft will reuse or reclaim the space when the region file is next modified.
24
+
25
+ You can either specify chunk coordinates to preserve or chunk coordinates to remove, but not both.
26
+
27
+ Options:
28
+ -h, --help Show this message.
29
+ -n, --dry-run Do not write changes to the file.
30
+ --remove CHUNK_COORDINATES...
31
+ Remove chunks with these coordinates. Coordinates are in
32
+ the format "x,z". May be specified multiple times.
33
+ --remove-list FILE
34
+ Remove chunks with coordinates specified in a text file.
35
+ Each line of the file should have coordinates in the format
36
+ "x,z". Safe to combine with --remove option.
37
+ --preserve CHUNK_COORDINATES...
38
+ Do not remove chunks with these coordinates. Coordinates
39
+ are in the format "x,z". May be specified multiple times.
40
+ --preserve-list FILE
41
+ Preserve chunks with coordinates specified in a text file.
42
+ Each line of the file should have coordinates in the format
43
+ "x,z". Safe to combine with --preserve option.
44
+ -v, --verbose Show more information during processing.
45
+ `,
46
+ {
47
+ argv: [...process.argv].slice(2),
48
+ laxPlacement: true,
49
+ }
50
+ );
51
+
52
+ main();
53
+
54
+ async function main() {
55
+ if (!args.MCA_FILE?.length) {
56
+ console.error('No files specified.');
57
+ process.exit(1);
58
+ }
59
+
60
+ if (args['--remove-list']) {
61
+ const coordinates = await readCoordinates(args['--remove-list']);
62
+
63
+ if (!args['--remove']) {
64
+ args['--remove'] = [];
65
+ }
66
+
67
+ args['--remove'].push(...coordinates);
68
+ }
69
+
70
+ if (args['--preserve-list']) {
71
+ const coordinates = await readCoordinates(args['--preserve-list']);
72
+
73
+ if (!args['--preserve']) {
74
+ args['--preserve'] = [];
75
+ }
76
+
77
+ args['--preserve'].push(...coordinates);
78
+ }
79
+
80
+ if (args['--remove'] && args['--preserve']) {
81
+ console.error('Cannot specify both --remove and --preserve options.');
82
+ process.exit(1);
83
+ }
84
+
85
+ if (!args['--remove'] && !args['--preserve']) {
86
+ console.error('Must specify either --remove or --preserve option.');
87
+ process.exit(1);
88
+ }
89
+
90
+ if (args['--preserve'] && args['--verbose']) {
91
+ console.log(`Preserving coordinates: ${args['--preserve'].join(' ')}`);
92
+ }
93
+
94
+ if (args['--remove'] && args['--verbose']) {
95
+ console.log(`Removing coordinates: ${args['--remove'].join(' ')}`);
96
+ }
97
+
98
+ const determinator = makeDeterminator();
99
+
100
+ for (const file of args.MCA_FILE) {
101
+ try {
102
+ await processFile(file, determinator);
103
+ } catch (e) {
104
+ console.error(`Error processing file ${file}: ${e}`);
105
+
106
+ process.exit(1);
107
+ }
108
+ }
109
+ }
110
+
111
+ async function processFile(
112
+ filename: string,
113
+ determinator: (chunkKey: string) => boolean
114
+ ) {
115
+ debugLog(`Reading file: ${filename}`);
116
+
117
+ if (args['--verbose']) {
118
+ console.log(`Reading file: ${filename}`);
119
+ }
120
+
121
+ const contents = await readFile(filename);
122
+ const anvil = Anvil.fromBuffer(contents.buffer);
123
+ const chunks = anvil.getAllChunks();
124
+ let changes = 0;
125
+
126
+ for (const chunk of chunks) {
127
+ const chunkKey = chunk.chunkKey() || '';
128
+
129
+ if (determinator(chunkKey)) {
130
+ debugLog(`Preserving chunk: ${chunkKey}`);
131
+
132
+ if (args['--verbose']) {
133
+ console.log(`Preserving chunk: ${chunkKey}`);
134
+ }
135
+ } else {
136
+ debugLog(`Removing chunk: ${chunkKey}`);
137
+
138
+ if (args['--verbose']) {
139
+ console.log(`Removing chunk: ${chunkKey}`);
140
+ }
141
+
142
+ anvil.deleteChunk(chunk);
143
+ changes += 1;
144
+ }
145
+ }
146
+
147
+ if (args['--verbose']) {
148
+ console.log(`Chunks removed: ${changes}/${chunks.length}`);
149
+ }
150
+
151
+ if (changes) {
152
+ if (args['--dry-run']) {
153
+ console.log(`DRY RUN: Would write changes to file: ${filename}`);
154
+ } else {
155
+ console.log(`Writing changes to file: ${filename}`);
156
+ const modifiedBuffer = Buffer.from(anvil.buffer());
157
+ await writeFile(filename, modifiedBuffer);
158
+ }
159
+ }
160
+ }
161
+
162
+ function makeDeterminator() {
163
+ if (args['--preserve']) {
164
+ const preservedCoordinates = new Set(args['--preserve']);
165
+
166
+ return (chunkKey: string) => preservedCoordinates.has(chunkKey);
167
+ } else if (args['--remove']) {
168
+ const removedCoordinates = new Set(args['--remove']);
169
+
170
+ return (chunkKey: string) => !removedCoordinates.has(chunkKey);
171
+ } else {
172
+ throw new Error('Invalid state: no determinator could be created.');
173
+ }
174
+ }
175
+
176
+ async function readCoordinates(filename: string): Promise<string[]> {
177
+ const contents = await readFile(filename, 'utf-8');
178
+ const lines = contents
179
+ .split('\n')
180
+ .map((line) => line.trim())
181
+ .filter((line) => line.length > 0);
182
+ return lines;
183
+ }
@@ -19,14 +19,16 @@ const args: {
19
19
  Scans a player NBT data file for the player location and prints it to stdout.
20
20
  NBT files can be compressed or uncompressed and this tool can handle both. If the filename is "-" or not provided, this will read from stdin.
21
21
 
22
- The output shows the following tab delimited fields:
23
-
24
- * Dimension: "minecraft:overworld", "minecraft:the_nether", or "minecraft:the_end"
25
- * X: The player's X coordinate, rounded
26
- * Y: The player's Y coordinate, rounded
27
- * Z: The player's Z coordinate, rounded
28
- * chunkX: The player's chunk X coordinate
29
- * chunkZ: The player's chunk Z coordinate
22
+ The output is JSON with the following fields:
23
+
24
+ {
25
+ "chunkX": 7, # player's chunk X coordinate
26
+ "chunkZ": -29 # player's chunk Z coordinate
27
+ "x": 123, # player's X coordinate, rounded
28
+ "y": 64, # player's Y coordinate, rounded
29
+ "z": -456, # player's Z coordinate, rounded
30
+ "dimension": "minecraft:overworld", # or "minecraft:the_nether" or "minecraft:the_end"
31
+ }
30
32
 
31
33
  Options:
32
34
  -h, --help Show this message.
@@ -96,7 +98,13 @@ async function processFile(filename: string) {
96
98
 
97
99
  const chunkX = Math.floor(x / 16);
98
100
  const chunkZ = Math.floor(z / 16);
99
-
100
- const output = [dimension, Math.round(x), Math.round(y), Math.round(z), chunkX, chunkZ];
101
- console.log(output.join('\t'));
101
+ const output = {
102
+ chunkX,
103
+ chunkZ,
104
+ x: Math.round(x),
105
+ y: Math.round(y),
106
+ z: Math.round(z),
107
+ dimension,
108
+ };
109
+ console.log(JSON.stringify(output, null, 4));
102
110
  }
package/src/lib/anvil.ts CHANGED
@@ -61,9 +61,11 @@ export class Anvil {
61
61
 
62
62
  // Clear cache before modifying location entries
63
63
  this.cachedLocationEntries = null;
64
- this.data.seek(index * 4);
65
- this.data.setNByteInteger(0, 3);
66
- this.data.setByte(0);
64
+ this.data.seek(index * 4); // Go to the pointer location for this chunk
65
+ this.data.setNByteInteger(0, 3); // Erase the offset
66
+ this.data.setByte(0); // Erase the sector count
67
+
68
+ // The actual chunk data is still present in the file.
67
69
  }
68
70
 
69
71
  /**
@@ -3,8 +3,8 @@ import { NbtLongArray } from '../nbt/nbt-long-array';
3
3
 
4
4
  const debugLog = debug('bit-data');
5
5
 
6
- // At most, we will have 7 bits to mask
7
- const MASK = [0, 1, 3, 7, 15, 31, 63, 127];
6
+ // At most, we will have 8 bits to mask
7
+ const MASK = [0, 1, 3, 7, 15, 31, 63, 127, 255];
8
8
 
9
9
  export class BitData {
10
10
  static fromLongArrayTag(tag: NbtLongArray): BitData {
package/src/lib/chunk.ts CHANGED
@@ -12,6 +12,7 @@ import { Coords2d, Coords3d } from '../types/coords';
12
12
  import { NbtBase } from '../nbt/nbt-base';
13
13
  import { NbtCompound } from '../nbt/nbt-compound';
14
14
  import { NbtInt } from '../nbt/nbt-int';
15
+ import { NbtIntArray } from '../nbt/nbt-int-array';
15
16
  import { NbtList } from '../nbt/nbt-list';
16
17
  import { NbtLong } from '../nbt/nbt-long';
17
18
  import { NbtLongArray } from '../nbt/nbt-long-array';
@@ -75,21 +76,28 @@ export class Chunk {
75
76
  */
76
77
  chunkCoordinates(): Coords2d | undefined {
77
78
  const x =
78
- // 1.18+
79
+ // 1.18+ region chunks
79
80
  this.rootNbt.findChild<NbtInt>('xPos') ||
80
- // up to 1.17
81
+ // up to 1.17 region chunks
81
82
  this.rootNbt.findChild<NbtInt>('Level/xPos');
82
83
  const z =
83
- // 1.18+
84
+ // 1.18+ region chunks
84
85
  this.rootNbt.findChild<NbtInt>('zPos') ||
85
- // up to 1.17
86
+ // up to 1.17 region chunks
86
87
  this.rootNbt.findChild<NbtInt>('Level/zPos');
87
88
 
88
- if (typeof x?.data !== 'number' || typeof z?.data !== 'number') {
89
- return;
89
+ if (typeof x?.data === 'number' && typeof z?.data === 'number') {
90
+ return [x.data, z.data];
90
91
  }
91
92
 
92
- return [x.data, z.data];
93
+ // Check for entity chunks
94
+ const position = this.rootNbt.findChild<NbtIntArray>('Position');
95
+
96
+ if (position && position.data.length === 2) {
97
+ return [position.data[0], position.data[1]];
98
+ }
99
+
100
+ return;
93
101
  }
94
102
 
95
103
  /**