mca-json 1.0.4 → 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 (43) 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/dist/nbt/snbt-parse.d.ts.map +1 -1
  21. package/dist/nbt/snbt-parse.js +7 -4
  22. package/dist/nbt/snbt-parse.js.map +1 -1
  23. package/dist/nbt/snbt-to-nbt.js +14 -4
  24. package/dist/nbt/snbt-to-nbt.js.map +1 -1
  25. package/package.json +2 -2
  26. package/src/bin/mca-find-signs.ts +214 -0
  27. package/src/bin/mca-remove-chunks.ts +183 -0
  28. package/src/bin/nbt-get-player-location.ts +19 -11
  29. package/src/lib/anvil.ts +5 -3
  30. package/src/lib/bit-data.ts +2 -2
  31. package/src/lib/chunk.ts +15 -7
  32. package/src/nbt/snbt-parse.ts +7 -4
  33. package/src/nbt/snbt-to-nbt.ts +63 -20
  34. package/dist/bin/mca-find-chunks-with-signs.d.ts +0 -3
  35. package/dist/bin/mca-find-chunks-with-signs.d.ts.map +0 -1
  36. package/dist/bin/mca-find-chunks-with-signs.js +0 -79
  37. package/dist/bin/mca-find-chunks-with-signs.js.map +0 -1
  38. package/dist/bin/mca-trim-chunks-without-signs.d.ts +0 -3
  39. package/dist/bin/mca-trim-chunks-without-signs.d.ts.map +0 -1
  40. package/dist/bin/mca-trim-chunks-without-signs.js +0 -117
  41. package/dist/bin/mca-trim-chunks-without-signs.js.map +0 -1
  42. package/src/bin/mca-find-chunks-with-signs.ts +0 -109
  43. package/src/bin/mca-trim-chunks-without-signs.ts +0 -146
package/README.md CHANGED
@@ -46,17 +46,23 @@ mca-chunks ~/.minecraft/saves/Test_World/region/r.2.1.mca
46
46
  This can also read the MCA file from stdin or write the output to a file. See `mca-chunks --help` for usage instructions.
47
47
 
48
48
 
49
- ### `mca-trim-chunks-without-signs` - Erase chunks
49
+ ### `mca-find-signs` - Lists all signs in MCA files
50
50
 
51
- Hides chunks in an MCA file if they do not have signs. If you want to change the seed of your server regularly, you could have players reserve 16x16 chunks by placing a sign anywhere in the chunk with any letters or numbers on it. These signs are different from naturally generating signs. The indexes will be overwritten with zeros to indicate that the data is no longer in the file, so Minecraft will regenerate the chunk the next time it is loaded.
51
+ This utility will list all signs in the given MCA files, along with their coordinates and text. The output is a JSON array of objects, each with the following properties:
52
52
 
53
53
  ```
54
- # Trim chunks in your region files
55
- cd ~/.minecraft/saves/Test_World/
56
- mca-trim-chunks-without-signs region/*.mca DIM1/region/*.mca DIM-1/region/*.mca
54
+ {
55
+ "chunkX": number,
56
+ "chunkZ": number,
57
+ "x": number,
58
+ "y": number,
59
+ "z": number,
60
+ "front": string[],
61
+ "back": string[] | undefined
62
+ }
57
63
  ```
58
64
 
59
- Without using additional flags, this will trim chunks that currently have players. Check the usage with `mca-trim-chunks-without-signs --help` for additional options.
65
+ Signs can be filtered to look for blank signs, signs with any letter or number, or signs that could be naturally generated.
60
66
 
61
67
  For reference, the naturally generating signs will have message like the following.
62
68
 
@@ -64,6 +70,32 @@ For reference, the naturally generating signs will have message like the followi
64
70
  * Igloo basements: ["", "<----", "---->", ""] and ["", "", "", ""]
65
71
 
66
72
 
73
+ ### `mca-json` - Convert MCA to JSON
74
+
75
+ Exports the whole MCA file or just a single chunk to JSON. Very useful to see what's in the file, but the output can be extremely large.
76
+
77
+
78
+ ### `mca-remove-chunks` - Erase chunks
79
+
80
+ Removes the offset and segment size of chunks in MCA files, which will cause Minecraft to forget about those chunks when they are loaded. This is useful for regenerating chunks. You can preserve or remove specific chunks with command-line arguments.
81
+
82
+ ```
83
+ mca-remove-chunks --preserve=-90,118 ~/.minecraft/saves/Test_World/region/r.-3.3.mca
84
+ ```
85
+
86
+
87
+ ### `nbt-get-player-location` - Get player location from a `*.dat` file
88
+
89
+ This utility will read the player's `*.dat` file and get the player's current location. The output is a JSON object with the following properties:
90
+
91
+ ```
92
+ {
93
+ "x": number,
94
+ "y": number,
95
+ "z": number
96
+ }
97
+ ```
98
+
67
99
  ### `nbt-json` - Convert NBT data to JSON
68
100
 
69
101
  This is useful for seeing what's in a player's `*.dat` file. Simply loads and decompresses the NBT, then writes it as a JSON object.
@@ -79,6 +111,8 @@ nbt-json ~/.minecraft/saves/Test_World/playerdata/b248e729-09c2-40dd-9168-12d191
79
111
 
80
112
  ## API
81
113
 
114
+ Are you here to do some coding? This library is also available for use in your own JavaScript programs. Below is the documentation for the API. All methods and classes are fully typed with TypeScript, so you can get autocomplete and type checking in your editor.
115
+
82
116
 
83
117
  ### `class Anvil`
84
118
 
@@ -370,7 +404,6 @@ Special notes about the above example:
370
404
 
371
405
  This has been loosely tested with the following editions. Other versions should work as well. If not, please supply a test MCA file and a description of the problem, or submit a pull request.
372
406
 
407
+ * Java, 1.21.11, data version 4671.
373
408
  * Java, 1.21.4, data version 4189.
374
409
  * Java, 1.17.1, data version 2730.
375
-
376
- Other versions can be supported through pull requests that contain tests to verify loading and processing works.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=mca-find-signs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mca-find-signs.d.ts","sourceRoot":"","sources":["../../src/bin/mca-find-signs.ts"],"names":[],"mappings":""}
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env node
2
+ import debug from 'debug';
3
+ import neodoc from 'neodoc';
4
+ import { Anvil } from '../lib/anvil';
5
+ import { Sign } from '../block/sign';
6
+ import { readFile } from 'node:fs/promises';
7
+ const debugLog = debug('mca-find-signs');
8
+ const args = neodoc.run(`Usage: mca-find-signs [OPTIONS] MCA_FILE...
9
+
10
+ Extracts sign coordinates from MCA files. This is designed to find different
11
+ categories of signs.
12
+
13
+ * Blank signs are blank on both sides.
14
+ * Generated signs can be blank or have any non-numeric and non-alphabetic
15
+ characters. This is so igloos with "<----" and "---->" are included.
16
+ * User-created signs must have at least a number or a letter on the front or
17
+ the back. There's no way to tell from the block data that a user placed a
18
+ sign, but this is a good heuristic.
19
+
20
+ The default output is a JSON array that shows the following information:
21
+
22
+ [
23
+ {
24
+ "chunkX": 0,
25
+ "chunkZ": 3,
26
+ "x": 12,
27
+ "y": 34,
28
+ "z": 56,
29
+ "front": ["Line 1", "Line 2", "Line 3", "Line 4"],
30
+ "back": ["Line 1", "Line 2", "Line 3", "Line 4"]
31
+ },
32
+ ...
33
+ ]
34
+
35
+ Options:
36
+ --blank Only show blank signs. Conflicts with --generated and --user.
37
+ --generated Only show generated signs. Conflicts with --blank and --user.
38
+ --user Only show user-created signs. Conflicts with --blank and --generated.
39
+ --first Stop scanning a chunk after the first sign is found.
40
+ -h, --help Show this message.
41
+ -v, --verbose Show more information during processing.
42
+ `, {
43
+ argv: [...process.argv].slice(2),
44
+ laxPlacement: true,
45
+ });
46
+ if (!args.MCA_FILE?.length) {
47
+ console.error('No files specified.');
48
+ process.exit(1);
49
+ }
50
+ if ((args['--blank'] ? 1 : 0) +
51
+ (args['--generated'] ? 1 : 0) +
52
+ (args['--user'] ? 1 : 0) >
53
+ 1) {
54
+ console.error('Conflicting options specified. Please choose only one of --blank, --generated, or --user.');
55
+ process.exit(1);
56
+ }
57
+ const signs = [];
58
+ for (const file of args.MCA_FILE) {
59
+ try {
60
+ const moreSigns = await processFile(file);
61
+ signs.push(...moreSigns);
62
+ }
63
+ catch (e) {
64
+ console.error(`Error processing file ${file}: ${e}`);
65
+ process.exit(1);
66
+ }
67
+ }
68
+ console.log(JSON.stringify(signs, null, 4));
69
+ async function processFile(filename) {
70
+ debugLog(`Reading file: ${filename}`);
71
+ if (args['--verbose']) {
72
+ console.log(`Reading file: ${filename}`);
73
+ }
74
+ const contents = await readFile(filename);
75
+ const anvil = Anvil.fromBuffer(contents.buffer);
76
+ const chunks = anvil.getAllChunks();
77
+ const allSignsFound = [];
78
+ for (const chunk of chunks) {
79
+ const chunkKey = chunk.chunkKey() || '';
80
+ const signs = processChunk(chunk);
81
+ if (signs.length > 0) {
82
+ debugLog(`Found ${signs.length} signs in chunk ${chunkKey}.`);
83
+ if (args['--verbose']) {
84
+ console.log(`Found ${signs.length} signs in chunk ${chunkKey}.`);
85
+ }
86
+ allSignsFound.push(...signs);
87
+ }
88
+ }
89
+ return allSignsFound;
90
+ }
91
+ function processChunk(chunk) {
92
+ const blockNames = chunk.uniqueBlockNames();
93
+ const foundSigns = [];
94
+ for (const blockName of blockNames) {
95
+ if (blockName.match(/^minecraft:.+_sign$/)) {
96
+ debugLog(`Found sign: ${blockName}`);
97
+ const listOfBlockCoordinates = chunk.findBlocksByName(blockName);
98
+ if (listOfBlockCoordinates.length === 0) {
99
+ debugLog(`Possible corruption - no blocks found for sign: ${blockName}`);
100
+ }
101
+ for (const coords of listOfBlockCoordinates) {
102
+ debugLog(`Checking block at coordinates: ${coords}`);
103
+ const block = chunk.getBlock(coords);
104
+ if (!(block instanceof Sign)) {
105
+ throw new Error(`Block is supposed to be a sign: ${block.name}`);
106
+ }
107
+ const front = block.frontText();
108
+ const back = block.backText();
109
+ const text = [...front, ...(back ?? [])].join('');
110
+ debugLog(`Front: ${JSON.stringify(front)}`);
111
+ debugLog(`Back: ${JSON.stringify(back)}`);
112
+ if (args['--blank'] && text.length !== 0) {
113
+ debugLog(`Skipping non-blank sign.`);
114
+ continue;
115
+ }
116
+ if (args['--generated'] && !text.match(/^[^a-z0-9]*$/i)) {
117
+ debugLog(`Skipping user-created sign.`);
118
+ }
119
+ if (args['--user'] && !text.match(/[a-z0-9]/i)) {
120
+ debugLog(`Skipping a possible generated sign.`);
121
+ continue;
122
+ }
123
+ debugLog(`Found a matching sign.`);
124
+ if (args['--verbose']) {
125
+ console.log(`Found a matching sign: ${JSON.stringify(front)} and ${JSON.stringify(back)}`);
126
+ }
127
+ const chunkCoordinates = chunk.chunkCoordinates();
128
+ if (!chunkCoordinates) {
129
+ throw new Error('Chunk coordinates not found.');
130
+ }
131
+ foundSigns.push({
132
+ chunkX: chunkCoordinates[0],
133
+ chunkZ: chunkCoordinates[1],
134
+ x: coords[0],
135
+ y: coords[1],
136
+ z: coords[2],
137
+ front,
138
+ back: back ?? null,
139
+ });
140
+ if (args['--first']) {
141
+ return foundSigns;
142
+ }
143
+ }
144
+ }
145
+ }
146
+ return foundSigns;
147
+ }
148
+ //# sourceMappingURL=mca-find-signs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mca-find-signs.js","sourceRoot":"","sources":["../../src/bin/mca-find-signs.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport debug from 'debug';\nimport neodoc from 'neodoc';\nimport { Anvil } from '../lib/anvil';\nimport { Chunk } from '../lib/chunk';\nimport { Sign } from '../block/sign';\nimport { readFile } from 'node:fs/promises';\n\ninterface SignInfo {\n chunkX: number;\n chunkZ: number;\n x: number;\n y: number;\n z: number;\n front: string[];\n back: string[] | null;\n}\n\ninterface Args {\n '--blank'?: boolean;\n '--generated'?: boolean;\n '--first'?: boolean;\n '--user'?: boolean;\n '--verbose'?: boolean;\n MCA_FILE?: string[];\n}\n\nconst debugLog = debug('mca-find-signs');\nconst args: Args = neodoc.run(\n `Usage: mca-find-signs [OPTIONS] MCA_FILE...\n\nExtracts sign coordinates from MCA files. This is designed to find different\ncategories of signs.\n\n* Blank signs are blank on both sides.\n* Generated signs can be blank or have any non-numeric and non-alphabetic\n characters. This is so igloos with \"<----\" and \"---->\" are included.\n* User-created signs must have at least a number or a letter on the front or\n the back. There's no way to tell from the block data that a user placed a\n sign, but this is a good heuristic.\n\nThe default output is a JSON array that shows the following information:\n\n[\n {\n \"chunkX\": 0,\n \"chunkZ\": 3,\n \"x\": 12,\n \"y\": 34,\n \"z\": 56,\n \"front\": [\"Line 1\", \"Line 2\", \"Line 3\", \"Line 4\"],\n \"back\": [\"Line 1\", \"Line 2\", \"Line 3\", \"Line 4\"]\n },\n ...\n]\n\nOptions:\n --blank Only show blank signs. Conflicts with --generated and --user.\n --generated Only show generated signs. Conflicts with --blank and --user.\n --user Only show user-created signs. Conflicts with --blank and --generated.\n --first Stop scanning a chunk after the first sign is found.\n -h, --help Show this message.\n -v, --verbose Show more information during processing.\n`,\n {\n argv: [...process.argv].slice(2),\n laxPlacement: true,\n }\n);\n\nif (!args.MCA_FILE?.length) {\n console.error('No files specified.');\n process.exit(1);\n}\n\nif (\n (args['--blank'] ? 1 : 0) +\n (args['--generated'] ? 1 : 0) +\n (args['--user'] ? 1 : 0) >\n 1\n) {\n console.error(\n 'Conflicting options specified. Please choose only one of --blank, --generated, or --user.'\n );\n process.exit(1);\n}\n\nconst signs: SignInfo[] = [];\n\nfor (const file of args.MCA_FILE) {\n try {\n const moreSigns = await processFile(file);\n signs.push(...moreSigns);\n } catch (e) {\n console.error(`Error processing file ${file}: ${e}`);\n\n process.exit(1);\n }\n}\n\nconsole.log(JSON.stringify(signs, null, 4));\n\nasync function processFile(filename: string): Promise<SignInfo[]> {\n debugLog(`Reading file: ${filename}`);\n\n if (args['--verbose']) {\n console.log(`Reading file: ${filename}`);\n }\n\n const contents = await readFile(filename);\n const anvil = Anvil.fromBuffer(contents.buffer);\n const chunks = anvil.getAllChunks();\n const allSignsFound: SignInfo[] = [];\n\n for (const chunk of chunks) {\n const chunkKey = chunk.chunkKey() || '';\n const signs = processChunk(chunk);\n\n if (signs.length > 0) {\n debugLog(`Found ${signs.length} signs in chunk ${chunkKey}.`);\n\n if (args['--verbose']) {\n console.log(\n `Found ${signs.length} signs in chunk ${chunkKey}.`\n );\n }\n\n allSignsFound.push(...signs);\n }\n }\n\n return allSignsFound;\n}\n\nfunction processChunk(chunk: Chunk): SignInfo[] {\n const blockNames = chunk.uniqueBlockNames();\n const foundSigns: SignInfo[] = [];\n\n for (const blockName of blockNames) {\n if (blockName.match(/^minecraft:.+_sign$/)) {\n debugLog(`Found sign: ${blockName}`);\n\n const listOfBlockCoordinates = chunk.findBlocksByName(blockName);\n\n if (listOfBlockCoordinates.length === 0) {\n debugLog(\n `Possible corruption - no blocks found for sign: ${blockName}`\n );\n }\n\n for (const coords of listOfBlockCoordinates) {\n debugLog(`Checking block at coordinates: ${coords}`);\n const block = chunk.getBlock(coords);\n\n if (!(block instanceof Sign)) {\n throw new Error(\n `Block is supposed to be a sign: ${block.name}`\n );\n }\n\n const front = block.frontText();\n const back = block.backText();\n const text = [...front, ...(back ?? [])].join('');\n debugLog(`Front: ${JSON.stringify(front)}`);\n debugLog(`Back: ${JSON.stringify(back)}`);\n\n if (args['--blank'] && text.length !== 0) {\n debugLog(`Skipping non-blank sign.`);\n continue;\n }\n\n if (args['--generated'] && !text.match(/^[^a-z0-9]*$/i)) {\n debugLog(`Skipping user-created sign.`);\n }\n\n if (args['--user'] && !text.match(/[a-z0-9]/i)) {\n debugLog(`Skipping a possible generated sign.`);\n continue;\n }\n\n debugLog(`Found a matching sign.`);\n\n if (args['--verbose']) {\n console.log(\n `Found a matching sign: ${JSON.stringify(front)} and ${JSON.stringify(back)}`\n );\n }\n\n const chunkCoordinates = chunk.chunkCoordinates();\n\n if (!chunkCoordinates) {\n throw new Error('Chunk coordinates not found.');\n }\n\n foundSigns.push({\n chunkX: chunkCoordinates[0],\n chunkZ: chunkCoordinates[1],\n x: coords[0],\n y: coords[1],\n z: coords[2],\n front,\n back: back ?? null,\n });\n\n if (args['--first']) {\n return foundSigns;\n }\n }\n }\n }\n\n return foundSigns;\n}\n"]}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=mca-remove-chunks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mca-remove-chunks.d.ts","sourceRoot":"","sources":["../../src/bin/mca-remove-chunks.ts"],"names":[],"mappings":""}
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ import debug from 'debug';
3
+ import neodoc from 'neodoc';
4
+ import { Anvil } from '../lib/anvil';
5
+ import { readFile, writeFile } from 'node:fs/promises';
6
+ const debugLog = debug('mca-remove-chunks');
7
+ const args = neodoc.run(`Usage: mca-remove-chunks [OPTIONS] MCA_FILE...
8
+
9
+ Removes chunks from MCA files (region/r.*.*.mca or entities/r.*.*.mca).
10
+
11
+ This does not change the file size. Similar to deleting a file on a hard drive,
12
+ the space is marked as free but not actually removed. Minecraft will reuse or reclaim the space when the region file is next modified.
13
+
14
+ You can either specify chunk coordinates to preserve or chunk coordinates to remove, but not both.
15
+
16
+ Options:
17
+ -h, --help Show this message.
18
+ -n, --dry-run Do not write changes to the file.
19
+ --remove CHUNK_COORDINATES...
20
+ Remove chunks with these coordinates. Coordinates are in
21
+ the format "x,z". May be specified multiple times.
22
+ --remove-list FILE
23
+ Remove chunks with coordinates specified in a text file.
24
+ Each line of the file should have coordinates in the format
25
+ "x,z". Safe to combine with --remove option.
26
+ --preserve CHUNK_COORDINATES...
27
+ Do not remove chunks with these coordinates. Coordinates
28
+ are in the format "x,z". May be specified multiple times.
29
+ --preserve-list FILE
30
+ Preserve chunks with coordinates specified in a text file.
31
+ Each line of the file should have coordinates in the format
32
+ "x,z". Safe to combine with --preserve option.
33
+ -v, --verbose Show more information during processing.
34
+ `, {
35
+ argv: [...process.argv].slice(2),
36
+ laxPlacement: true,
37
+ });
38
+ main();
39
+ async function main() {
40
+ if (!args.MCA_FILE?.length) {
41
+ console.error('No files specified.');
42
+ process.exit(1);
43
+ }
44
+ if (args['--remove-list']) {
45
+ const coordinates = await readCoordinates(args['--remove-list']);
46
+ if (!args['--remove']) {
47
+ args['--remove'] = [];
48
+ }
49
+ args['--remove'].push(...coordinates);
50
+ }
51
+ if (args['--preserve-list']) {
52
+ const coordinates = await readCoordinates(args['--preserve-list']);
53
+ if (!args['--preserve']) {
54
+ args['--preserve'] = [];
55
+ }
56
+ args['--preserve'].push(...coordinates);
57
+ }
58
+ if (args['--remove'] && args['--preserve']) {
59
+ console.error('Cannot specify both --remove and --preserve options.');
60
+ process.exit(1);
61
+ }
62
+ if (!args['--remove'] && !args['--preserve']) {
63
+ console.error('Must specify either --remove or --preserve option.');
64
+ process.exit(1);
65
+ }
66
+ if (args['--preserve'] && args['--verbose']) {
67
+ console.log(`Preserving coordinates: ${args['--preserve'].join(' ')}`);
68
+ }
69
+ if (args['--remove'] && args['--verbose']) {
70
+ console.log(`Removing coordinates: ${args['--remove'].join(' ')}`);
71
+ }
72
+ const determinator = makeDeterminator();
73
+ for (const file of args.MCA_FILE) {
74
+ try {
75
+ await processFile(file, determinator);
76
+ }
77
+ catch (e) {
78
+ console.error(`Error processing file ${file}: ${e}`);
79
+ process.exit(1);
80
+ }
81
+ }
82
+ }
83
+ async function processFile(filename, determinator) {
84
+ debugLog(`Reading file: ${filename}`);
85
+ if (args['--verbose']) {
86
+ console.log(`Reading file: ${filename}`);
87
+ }
88
+ const contents = await readFile(filename);
89
+ const anvil = Anvil.fromBuffer(contents.buffer);
90
+ const chunks = anvil.getAllChunks();
91
+ let changes = 0;
92
+ for (const chunk of chunks) {
93
+ const chunkKey = chunk.chunkKey() || '';
94
+ if (determinator(chunkKey)) {
95
+ debugLog(`Preserving chunk: ${chunkKey}`);
96
+ if (args['--verbose']) {
97
+ console.log(`Preserving chunk: ${chunkKey}`);
98
+ }
99
+ }
100
+ else {
101
+ debugLog(`Removing chunk: ${chunkKey}`);
102
+ if (args['--verbose']) {
103
+ console.log(`Removing chunk: ${chunkKey}`);
104
+ }
105
+ anvil.deleteChunk(chunk);
106
+ changes += 1;
107
+ }
108
+ }
109
+ if (args['--verbose']) {
110
+ console.log(`Chunks removed: ${changes}/${chunks.length}`);
111
+ }
112
+ if (changes) {
113
+ if (args['--dry-run']) {
114
+ console.log(`DRY RUN: Would write changes to file: ${filename}`);
115
+ }
116
+ else {
117
+ console.log(`Writing changes to file: ${filename}`);
118
+ const modifiedBuffer = Buffer.from(anvil.buffer());
119
+ await writeFile(filename, modifiedBuffer);
120
+ }
121
+ }
122
+ }
123
+ function makeDeterminator() {
124
+ if (args['--preserve']) {
125
+ const preservedCoordinates = new Set(args['--preserve']);
126
+ return (chunkKey) => preservedCoordinates.has(chunkKey);
127
+ }
128
+ else if (args['--remove']) {
129
+ const removedCoordinates = new Set(args['--remove']);
130
+ return (chunkKey) => !removedCoordinates.has(chunkKey);
131
+ }
132
+ else {
133
+ throw new Error('Invalid state: no determinator could be created.');
134
+ }
135
+ }
136
+ async function readCoordinates(filename) {
137
+ const contents = await readFile(filename, 'utf-8');
138
+ const lines = contents
139
+ .split('\n')
140
+ .map((line) => line.trim())
141
+ .filter((line) => line.length > 0);
142
+ return lines;
143
+ }
144
+ //# sourceMappingURL=mca-remove-chunks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mca-remove-chunks.js","sourceRoot":"","sources":["../../src/bin/mca-remove-chunks.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport debug from 'debug';\nimport neodoc from 'neodoc';\nimport { Anvil } from '../lib/anvil';\nimport { readFile, writeFile } from 'node:fs/promises';\n\nconst debugLog = debug('mca-remove-chunks');\nconst args: {\n '--dry-run'?: boolean;\n '--remove'?: string[];\n '--remove-list'?: string;\n '--preserve'?: string[];\n '--preserve-list'?: string;\n '--verbose'?: boolean;\n MCA_FILE?: string[];\n} = neodoc.run(\n `Usage: mca-remove-chunks [OPTIONS] MCA_FILE...\n\nRemoves chunks from MCA files (region/r.*.*.mca or entities/r.*.*.mca).\n\nThis does not change the file size. Similar to deleting a file on a hard drive,\nthe space is marked as free but not actually removed. Minecraft will reuse or reclaim the space when the region file is next modified.\n\nYou can either specify chunk coordinates to preserve or chunk coordinates to remove, but not both.\n\nOptions:\n -h, --help Show this message.\n -n, --dry-run Do not write changes to the file.\n --remove CHUNK_COORDINATES...\n Remove chunks with these coordinates. Coordinates are in\n the format \"x,z\". May be specified multiple times.\n --remove-list FILE\n Remove chunks with coordinates specified in a text file.\n Each line of the file should have coordinates in the format\n \"x,z\". Safe to combine with --remove option.\n --preserve CHUNK_COORDINATES...\n Do not remove chunks with these coordinates. Coordinates\n are in the format \"x,z\". May be specified multiple times.\n --preserve-list FILE\n Preserve chunks with coordinates specified in a text file.\n Each line of the file should have coordinates in the format\n \"x,z\". Safe to combine with --preserve option.\n -v, --verbose Show more information during processing.\n`,\n {\n argv: [...process.argv].slice(2),\n laxPlacement: true,\n }\n);\n\nmain();\n\nasync function main() {\n if (!args.MCA_FILE?.length) {\n console.error('No files specified.');\n process.exit(1);\n }\n\n if (args['--remove-list']) {\n const coordinates = await readCoordinates(args['--remove-list']);\n\n if (!args['--remove']) {\n args['--remove'] = [];\n }\n\n args['--remove'].push(...coordinates);\n }\n\n if (args['--preserve-list']) {\n const coordinates = await readCoordinates(args['--preserve-list']);\n\n if (!args['--preserve']) {\n args['--preserve'] = [];\n }\n\n args['--preserve'].push(...coordinates);\n }\n\n if (args['--remove'] && args['--preserve']) {\n console.error('Cannot specify both --remove and --preserve options.');\n process.exit(1);\n }\n\n if (!args['--remove'] && !args['--preserve']) {\n console.error('Must specify either --remove or --preserve option.');\n process.exit(1);\n }\n\n if (args['--preserve'] && args['--verbose']) {\n console.log(`Preserving coordinates: ${args['--preserve'].join(' ')}`);\n }\n\n if (args['--remove'] && args['--verbose']) {\n console.log(`Removing coordinates: ${args['--remove'].join(' ')}`);\n }\n\n const determinator = makeDeterminator();\n\n for (const file of args.MCA_FILE) {\n try {\n await processFile(file, determinator);\n } catch (e) {\n console.error(`Error processing file ${file}: ${e}`);\n\n process.exit(1);\n }\n }\n}\n\nasync function processFile(\n filename: string,\n determinator: (chunkKey: string) => boolean\n) {\n debugLog(`Reading file: ${filename}`);\n\n if (args['--verbose']) {\n console.log(`Reading file: ${filename}`);\n }\n\n const contents = await readFile(filename);\n const anvil = Anvil.fromBuffer(contents.buffer);\n const chunks = anvil.getAllChunks();\n let changes = 0;\n\n for (const chunk of chunks) {\n const chunkKey = chunk.chunkKey() || '';\n\n if (determinator(chunkKey)) {\n debugLog(`Preserving chunk: ${chunkKey}`);\n\n if (args['--verbose']) {\n console.log(`Preserving chunk: ${chunkKey}`);\n }\n } else {\n debugLog(`Removing chunk: ${chunkKey}`);\n\n if (args['--verbose']) {\n console.log(`Removing chunk: ${chunkKey}`);\n }\n\n anvil.deleteChunk(chunk);\n changes += 1;\n }\n }\n\n if (args['--verbose']) {\n console.log(`Chunks removed: ${changes}/${chunks.length}`);\n }\n\n if (changes) {\n if (args['--dry-run']) {\n console.log(`DRY RUN: Would write changes to file: ${filename}`);\n } else {\n console.log(`Writing changes to file: ${filename}`);\n const modifiedBuffer = Buffer.from(anvil.buffer());\n await writeFile(filename, modifiedBuffer);\n }\n }\n}\n\nfunction makeDeterminator() {\n if (args['--preserve']) {\n const preservedCoordinates = new Set(args['--preserve']);\n\n return (chunkKey: string) => preservedCoordinates.has(chunkKey);\n } else if (args['--remove']) {\n const removedCoordinates = new Set(args['--remove']);\n\n return (chunkKey: string) => !removedCoordinates.has(chunkKey);\n } else {\n throw new Error('Invalid state: no determinator could be created.');\n }\n}\n\nasync function readCoordinates(filename: string): Promise<string[]> {\n const contents = await readFile(filename, 'utf-8');\n const lines = contents\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n return lines;\n}\n"]}
@@ -13,14 +13,16 @@ async function main() {
13
13
  Scans a player NBT data file for the player location and prints it to stdout.
14
14
  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.
15
15
 
16
- The output shows the following tab delimited fields:
16
+ The output is JSON with the following fields:
17
17
 
18
- * Dimension: "minecraft:overworld", "minecraft:the_nether", or "minecraft:the_end"
19
- * X: The player's X coordinate, rounded
20
- * Y: The player's Y coordinate, rounded
21
- * Z: The player's Z coordinate, rounded
22
- * chunkX: The player's chunk X coordinate
23
- * chunkZ: The player's chunk Z coordinate
18
+ {
19
+ "chunkX": 7, # player's chunk X coordinate
20
+ "chunkZ": -29 # player's chunk Z coordinate
21
+ "x": 123, # player's X coordinate, rounded
22
+ "y": 64, # player's Y coordinate, rounded
23
+ "z": -456, # player's Z coordinate, rounded
24
+ "dimension": "minecraft:overworld", # or "minecraft:the_nether" or "minecraft:the_end"
25
+ }
24
26
 
25
27
  Options:
26
28
  -h, --help Show this message.
@@ -77,7 +79,14 @@ async function processFile(filename) {
77
79
  }
78
80
  const chunkX = Math.floor(x / 16);
79
81
  const chunkZ = Math.floor(z / 16);
80
- const output = [dimension, Math.round(x), Math.round(y), Math.round(z), chunkX, chunkZ];
81
- console.log(output.join('\t'));
82
+ const output = {
83
+ chunkX,
84
+ chunkZ,
85
+ x: Math.round(x),
86
+ y: Math.round(y),
87
+ z: Math.round(z),
88
+ dimension,
89
+ };
90
+ console.log(JSON.stringify(output, null, 4));
82
91
  }
83
92
  //# sourceMappingURL=nbt-get-player-location.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nbt-get-player-location.js","sourceRoot":"","sources":["../../src/bin/nbt-get-player-location.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport debug from 'debug';\nimport getStdin from 'get-stdin';\nimport neodoc from 'neodoc';\nimport { inflate } from 'pako';\nimport { Nbt } from '../nbt/nbt';\nimport { readFile } from 'node:fs/promises';\n\nconst debugLog = debug('mca-trim-chunks-without-signs');\nmain();\n\nasync function main() {\nconst args: {\n 'NBT_FILE'?: string[];\n} = neodoc.run(\n`Usage: nbt-get-player-locations NBT_FILE...\n\nScans a player NBT data file for the player location and prints it to stdout.\nNBT files can be compressed or uncompressed and this tool can handle both. If the filename is \"-\" or not provided, this will read from stdin.\n\nThe output shows the following tab delimited fields:\n\n* Dimension: \"minecraft:overworld\", \"minecraft:the_nether\", or \"minecraft:the_end\"\n* X: The player's X coordinate, rounded\n* Y: The player's Y coordinate, rounded\n* Z: The player's Z coordinate, rounded\n* chunkX: The player's chunk X coordinate\n* chunkZ: The player's chunk Z coordinate\n\nOptions:\n -h, --help Show this message.\n`,\n {\n argv: [...process.argv].slice(2),\n laxPlacement: true,\n }\n);\n\n if (!args.NBT_FILE?.length) {\n args.NBT_FILE = ['-'];\n }\n\n for (const file of args.NBT_FILE) {\n try {\n await processFile(file);\n } catch (e) {\n console.error(`Error processing file ${file}: ${e}`);\n\n process.exit(1);\n }\n }\n}\n\nasync function readInput(filename: string | undefined) {\n if (!filename || filename === '-') {\n debugLog(`Reading from stdin`);\n return await getStdin.buffer();\n }\n\n debugLog(`Reading file: ${filename}`);\n\n return readFile(filename);\n}\n\nasync function processFile(filename: string) {\n const inputBuffer = await readInput(filename);\n\n if (inputBuffer.length === 0) {\n if (filename === '-') {\n console.error('Input (stdin) is empty.');\n } else {\n console.error(`Input file is empty: ${filename}`);\n }\n console.error('Aborting. Use --help for usage.');\n process.exit(1);\n }\n\n let buffer = inputBuffer.buffer as ArrayBufferLike;\n\n try {\n buffer = inflate(inputBuffer).buffer;\n } catch (_ignore) {}\n\n const nbt = Nbt.fromBuffer(buffer);\n\n const dimension = nbt.findChild('Dimension')?.data;\n const x = nbt.findChild('Pos/0')?.data;\n const y = nbt.findChild('Pos/1')?.data;\n const z = nbt.findChild('Pos/2')?.data;\n\n if (typeof dimension !== 'string' || typeof x !== 'number' || typeof y !== 'number' || typeof z !== 'number') {\n console.error(`NBT file ${filename} does not contain valid player location data.`);\n process.exit(1);\n }\n\n const chunkX = Math.floor(x / 16);\n const chunkZ = Math.floor(z / 16);\n\n const output = [dimension, Math.round(x), Math.round(y), Math.round(z), chunkX, chunkZ];\n console.log(output.join('\\t'));\n}\n"]}
1
+ {"version":3,"file":"nbt-get-player-location.js","sourceRoot":"","sources":["../../src/bin/nbt-get-player-location.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport debug from 'debug';\nimport getStdin from 'get-stdin';\nimport neodoc from 'neodoc';\nimport { inflate } from 'pako';\nimport { Nbt } from '../nbt/nbt';\nimport { readFile } from 'node:fs/promises';\n\nconst debugLog = debug('mca-trim-chunks-without-signs');\nmain();\n\nasync function main() {\nconst args: {\n 'NBT_FILE'?: string[];\n} = neodoc.run(\n`Usage: nbt-get-player-locations NBT_FILE...\n\nScans a player NBT data file for the player location and prints it to stdout.\nNBT files can be compressed or uncompressed and this tool can handle both. If the filename is \"-\" or not provided, this will read from stdin.\n\nThe output is JSON with the following fields:\n\n{\n \"chunkX\": 7, # player's chunk X coordinate\n \"chunkZ\": -29 # player's chunk Z coordinate\n \"x\": 123, # player's X coordinate, rounded\n \"y\": 64, # player's Y coordinate, rounded\n \"z\": -456, # player's Z coordinate, rounded\n \"dimension\": \"minecraft:overworld\", # or \"minecraft:the_nether\" or \"minecraft:the_end\"\n}\n\nOptions:\n -h, --help Show this message.\n`,\n {\n argv: [...process.argv].slice(2),\n laxPlacement: true,\n }\n);\n\n if (!args.NBT_FILE?.length) {\n args.NBT_FILE = ['-'];\n }\n\n for (const file of args.NBT_FILE) {\n try {\n await processFile(file);\n } catch (e) {\n console.error(`Error processing file ${file}: ${e}`);\n\n process.exit(1);\n }\n }\n}\n\nasync function readInput(filename: string | undefined) {\n if (!filename || filename === '-') {\n debugLog(`Reading from stdin`);\n return await getStdin.buffer();\n }\n\n debugLog(`Reading file: ${filename}`);\n\n return readFile(filename);\n}\n\nasync function processFile(filename: string) {\n const inputBuffer = await readInput(filename);\n\n if (inputBuffer.length === 0) {\n if (filename === '-') {\n console.error('Input (stdin) is empty.');\n } else {\n console.error(`Input file is empty: ${filename}`);\n }\n console.error('Aborting. Use --help for usage.');\n process.exit(1);\n }\n\n let buffer = inputBuffer.buffer as ArrayBufferLike;\n\n try {\n buffer = inflate(inputBuffer).buffer;\n } catch (_ignore) {}\n\n const nbt = Nbt.fromBuffer(buffer);\n\n const dimension = nbt.findChild('Dimension')?.data;\n const x = nbt.findChild('Pos/0')?.data;\n const y = nbt.findChild('Pos/1')?.data;\n const z = nbt.findChild('Pos/2')?.data;\n\n if (typeof dimension !== 'string' || typeof x !== 'number' || typeof y !== 'number' || typeof z !== 'number') {\n console.error(`NBT file ${filename} does not contain valid player location data.`);\n process.exit(1);\n }\n\n const chunkX = Math.floor(x / 16);\n const chunkZ = Math.floor(z / 16);\n const output = {\n chunkX,\n chunkZ,\n x: Math.round(x),\n y: Math.round(y),\n z: Math.round(z),\n dimension,\n };\n console.log(JSON.stringify(output, null, 4));\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"anvil.d.ts","sourceRoot":"","sources":["../../src/lib/anvil.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAI3C,KAAK,aAAa,GAAG;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACvB,CAAC;AAWF;;GAEG;AACH,qBAAa,KAAK;IACd,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,KAAK;IAIjD,qBAAqB,EAAE,aAAa,EAAE,GAAG,IAAI,CAAQ;IACrD,IAAI,EAAE,UAAU,CAAC;gBAEL,IAAI,EAAE,eAAe;IAIjC;;OAEG;IACH,MAAM,IAAI,eAAe;IAIzB;;;OAGG;IACH,WAAW,CAAC,aAAa,EAAE,QAAQ,GAAG,KAAK,GAAG,IAAI;IAsBlD;;;;;OAKG;IACH,YAAY,IAAI,KAAK,EAAE;IAkBvB,QAAQ,CAAC,WAAW,EAAE,QAAQ,GAAG,KAAK,GAAG,SAAS;IAalD;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAgB/B;;;;;;OAMG;IACH,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,eAAe;IAqBxD;;;;OAIG;IACH,SAAS,CAAC,kBAAkB,IAAI,aAAa,EAAE;CAmBlD"}
1
+ {"version":3,"file":"anvil.d.ts","sourceRoot":"","sources":["../../src/lib/anvil.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAI3C,KAAK,aAAa,GAAG;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACvB,CAAC;AAWF;;GAEG;AACH,qBAAa,KAAK;IACd,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,KAAK;IAIjD,qBAAqB,EAAE,aAAa,EAAE,GAAG,IAAI,CAAQ;IACrD,IAAI,EAAE,UAAU,CAAC;gBAEL,IAAI,EAAE,eAAe;IAIjC;;OAEG;IACH,MAAM,IAAI,eAAe;IAIzB;;;OAGG;IACH,WAAW,CAAC,aAAa,EAAE,QAAQ,GAAG,KAAK,GAAG,IAAI;IAwBlD;;;;;OAKG;IACH,YAAY,IAAI,KAAK,EAAE;IAkBvB,QAAQ,CAAC,WAAW,EAAE,QAAQ,GAAG,KAAK,GAAG,SAAS;IAalD;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAgB/B;;;;;;OAMG;IACH,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,eAAe;IAqBxD;;;;OAIG;IACH,SAAS,CAAC,kBAAkB,IAAI,aAAa,EAAE;CAmBlD"}
package/dist/lib/anvil.js CHANGED
@@ -46,9 +46,10 @@ export class Anvil {
46
46
  }
47
47
  // Clear cache before modifying location entries
48
48
  this.cachedLocationEntries = null;
49
- this.data.seek(index * 4);
50
- this.data.setNByteInteger(0, 3);
51
- this.data.setByte(0);
49
+ this.data.seek(index * 4); // Go to the pointer location for this chunk
50
+ this.data.setNByteInteger(0, 3); // Erase the offset
51
+ this.data.setByte(0); // Erase the sector count
52
+ // The actual chunk data is still present in the file.
52
53
  }
53
54
  /**
54
55
  * Parses all chunks in the Anvil blob. Chunk objects are read in the order
@@ -1 +1 @@
1
- {"version":3,"file":"anvil.js","sourceRoot":"","sources":["../../src/lib/anvil.ts"],"names":[],"mappings":"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","sourcesContent":["import { BinaryData } from './binary-data';\nimport { Chunk } from './chunk';\nimport { Coords2d } from '../types/coords';\nimport { inflate } from 'pako';\nimport { Nbt } from '../nbt/nbt';\n\ntype LocationEntry = {\n offset: number; // 3 bytes\n sectorCount: number; // 1 byte\n};\n\nenum CompressionType {\n GZIP = 1,\n ZLIB = 2,\n NONE = 3,\n}\n\nconst LOCATION_ENTRIES_PER_FILE = 1024;\nconst SECTOR_SIZE = 4096;\n\n/**\n * Read and write Minecraft Anvil region files.\n */\nexport class Anvil {\n static fromBuffer(buffer: ArrayBufferLike): Anvil {\n return new Anvil(buffer);\n }\n\n cachedLocationEntries: LocationEntry[] | null = null;\n data: BinaryData;\n\n constructor(data: ArrayBufferLike) {\n this.data = new BinaryData(data);\n }\n\n /**\n * Returns the buffer that holds the Anvil data.\n */\n buffer(): ArrayBufferLike {\n return this.data.buffer();\n }\n\n /**\n * Delete a chunk. The location is either given in X,Z chunk coordinates or\n * passed in from a Chunk object.\n */\n deleteChunk(chunkLocation: Coords2d | Chunk): void {\n let index;\n\n if (Array.isArray(chunkLocation)) {\n index = this.chunkCoordinatesToIndex(chunkLocation);\n } else {\n const coords = chunkLocation.chunkCoordinates();\n\n if (!coords) {\n throw new Error('Chunk does not have coordinates');\n }\n\n index = this.chunkCoordinatesToIndex(coords);\n }\n\n // Clear cache before modifying location entries\n this.cachedLocationEntries = null;\n this.data.seek(index * 4);\n this.data.setNByteInteger(0, 3);\n this.data.setByte(0);\n }\n\n /**\n * Parses all chunks in the Anvil blob. Chunk objects are read in the order\n * they are stored in the location entry header sector.\n *\n * @returns Chunk[] a list of Chunk objects\n */\n getAllChunks(): Chunk[] {\n // Empty files can be written\n if (this.data.byteLength() === 0) {\n return [];\n }\n\n const chunks = this.getLocationEntries()\n .filter((x) => x.sectorCount > 0)\n .map((offset) => {\n const nbt = Nbt.fromBuffer(this.getChunkData(offset.offset));\n\n return new Chunk(nbt);\n });\n\n return chunks;\n }\n\n // Gets a chunk using chunk coordinates.\n getChunk(chunkCoords: Coords2d): Chunk | undefined {\n const locationEntries = this.getLocationEntries();\n const entry = locationEntries[this.chunkCoordinatesToIndex(chunkCoords)];\n\n if (entry?.sectorCount > 0) {\n const nbt = Nbt.fromBuffer(this.getChunkData(entry.offset));\n\n return new Chunk(nbt);\n }\n\n return;\n }\n\n /**\n * Convert from chunk coordinates to region-relative chunk coordinates, and\n * then to an index.\n */\n private chunkCoordinatesToIndex(chunkCoords: Coords2d): number {\n let regionChunkX = chunkCoords[0];\n let regionChunkZ = chunkCoords[1];\n const step = 4096 * 4096;\n\n while (regionChunkX < 0) {\n regionChunkX += step;\n }\n\n while (regionChunkZ < 0) {\n regionChunkZ += step;\n }\n\n return (regionChunkX % 32) + (regionChunkZ % 32) * 32;\n }\n\n /**\n * Extracts data for a chunk present at the given offset in the Anvil blob.\n * The offset is provided in sectors of SECTOR_SIZE bytes (as in the chunk\n * location entries from the blob's first header sector).\n *\n * @param offset the chunk's offset in number of sectors from the Anvil blob's start.\n */\n protected getChunkData(offset?: number): ArrayBufferLike {\n if (offset !== undefined) {\n this.data.seek(offset * SECTOR_SIZE);\n }\n\n const length = this.data.getUInt();\n const compressionType = this.data.getByte();\n const data = this.data.getArrayBuffer(length - 1); // First byte is the compression type\n\n if (\n compressionType === CompressionType.ZLIB ||\n compressionType === CompressionType.GZIP\n ) {\n // Could be SharedArrayBuffer, but it won't get used again after\n // inflation, so it being \"shared\" does not matter.\n return inflate(data as ArrayBuffer).buffer;\n }\n\n return data;\n }\n\n /**\n * Retrieves the full list of chunk location entries from this Anvil blob.\n * The pointer will be moved to the beginning of the first header sector at\n * the start of this method and advanced to the end during reading.\n */\n protected getLocationEntries(): LocationEntry[] {\n if (this.cachedLocationEntries) {\n return this.cachedLocationEntries;\n }\n\n this.data.seek(0);\n const result: LocationEntry[] = [];\n\n for (let i = 0; i < LOCATION_ENTRIES_PER_FILE; i += 1) {\n result.push({\n offset: this.data.getNByteInteger(3),\n sectorCount: this.data.getByte(),\n });\n }\n\n this.cachedLocationEntries = result;\n\n return result;\n }\n}\n"]}
1
+ {"version":3,"file":"anvil.js","sourceRoot":"","sources":["../../src/lib/anvil.ts"],"names":[],"mappings":"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","sourcesContent":["import { BinaryData } from './binary-data';\nimport { Chunk } from './chunk';\nimport { Coords2d } from '../types/coords';\nimport { inflate } from 'pako';\nimport { Nbt } from '../nbt/nbt';\n\ntype LocationEntry = {\n offset: number; // 3 bytes\n sectorCount: number; // 1 byte\n};\n\nenum CompressionType {\n GZIP = 1,\n ZLIB = 2,\n NONE = 3,\n}\n\nconst LOCATION_ENTRIES_PER_FILE = 1024;\nconst SECTOR_SIZE = 4096;\n\n/**\n * Read and write Minecraft Anvil region files.\n */\nexport class Anvil {\n static fromBuffer(buffer: ArrayBufferLike): Anvil {\n return new Anvil(buffer);\n }\n\n cachedLocationEntries: LocationEntry[] | null = null;\n data: BinaryData;\n\n constructor(data: ArrayBufferLike) {\n this.data = new BinaryData(data);\n }\n\n /**\n * Returns the buffer that holds the Anvil data.\n */\n buffer(): ArrayBufferLike {\n return this.data.buffer();\n }\n\n /**\n * Delete a chunk. The location is either given in X,Z chunk coordinates or\n * passed in from a Chunk object.\n */\n deleteChunk(chunkLocation: Coords2d | Chunk): void {\n let index;\n\n if (Array.isArray(chunkLocation)) {\n index = this.chunkCoordinatesToIndex(chunkLocation);\n } else {\n const coords = chunkLocation.chunkCoordinates();\n\n if (!coords) {\n throw new Error('Chunk does not have coordinates');\n }\n\n index = this.chunkCoordinatesToIndex(coords);\n }\n\n // Clear cache before modifying location entries\n this.cachedLocationEntries = null;\n this.data.seek(index * 4); // Go to the pointer location for this chunk\n this.data.setNByteInteger(0, 3); // Erase the offset\n this.data.setByte(0); // Erase the sector count\n\n // The actual chunk data is still present in the file.\n }\n\n /**\n * Parses all chunks in the Anvil blob. Chunk objects are read in the order\n * they are stored in the location entry header sector.\n *\n * @returns Chunk[] a list of Chunk objects\n */\n getAllChunks(): Chunk[] {\n // Empty files can be written\n if (this.data.byteLength() === 0) {\n return [];\n }\n\n const chunks = this.getLocationEntries()\n .filter((x) => x.sectorCount > 0)\n .map((offset) => {\n const nbt = Nbt.fromBuffer(this.getChunkData(offset.offset));\n\n return new Chunk(nbt);\n });\n\n return chunks;\n }\n\n // Gets a chunk using chunk coordinates.\n getChunk(chunkCoords: Coords2d): Chunk | undefined {\n const locationEntries = this.getLocationEntries();\n const entry = locationEntries[this.chunkCoordinatesToIndex(chunkCoords)];\n\n if (entry?.sectorCount > 0) {\n const nbt = Nbt.fromBuffer(this.getChunkData(entry.offset));\n\n return new Chunk(nbt);\n }\n\n return;\n }\n\n /**\n * Convert from chunk coordinates to region-relative chunk coordinates, and\n * then to an index.\n */\n private chunkCoordinatesToIndex(chunkCoords: Coords2d): number {\n let regionChunkX = chunkCoords[0];\n let regionChunkZ = chunkCoords[1];\n const step = 4096 * 4096;\n\n while (regionChunkX < 0) {\n regionChunkX += step;\n }\n\n while (regionChunkZ < 0) {\n regionChunkZ += step;\n }\n\n return (regionChunkX % 32) + (regionChunkZ % 32) * 32;\n }\n\n /**\n * Extracts data for a chunk present at the given offset in the Anvil blob.\n * The offset is provided in sectors of SECTOR_SIZE bytes (as in the chunk\n * location entries from the blob's first header sector).\n *\n * @param offset the chunk's offset in number of sectors from the Anvil blob's start.\n */\n protected getChunkData(offset?: number): ArrayBufferLike {\n if (offset !== undefined) {\n this.data.seek(offset * SECTOR_SIZE);\n }\n\n const length = this.data.getUInt();\n const compressionType = this.data.getByte();\n const data = this.data.getArrayBuffer(length - 1); // First byte is the compression type\n\n if (\n compressionType === CompressionType.ZLIB ||\n compressionType === CompressionType.GZIP\n ) {\n // Could be SharedArrayBuffer, but it won't get used again after\n // inflation, so it being \"shared\" does not matter.\n return inflate(data as ArrayBuffer).buffer;\n }\n\n return data;\n }\n\n /**\n * Retrieves the full list of chunk location entries from this Anvil blob.\n * The pointer will be moved to the beginning of the first header sector at\n * the start of this method and advanced to the end during reading.\n */\n protected getLocationEntries(): LocationEntry[] {\n if (this.cachedLocationEntries) {\n return this.cachedLocationEntries;\n }\n\n this.data.seek(0);\n const result: LocationEntry[] = [];\n\n for (let i = 0; i < LOCATION_ENTRIES_PER_FILE; i += 1) {\n result.push({\n offset: this.data.getNByteInteger(3),\n sectorCount: this.data.getByte(),\n });\n }\n\n this.cachedLocationEntries = result;\n\n return result;\n }\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import debug from 'debug';
2
2
  const debugLog = debug('bit-data');
3
- // At most, we will have 7 bits to mask
4
- const MASK = [0, 1, 3, 7, 15, 31, 63, 127];
3
+ // At most, we will have 8 bits to mask
4
+ const MASK = [0, 1, 3, 7, 15, 31, 63, 127, 255];
5
5
  export class BitData {
6
6
  static fromLongArrayTag(tag) {
7
7
  const arrayBuffer = new ArrayBuffer(tag.data.length * 8);
@@ -1 +1 @@
1
- {"version":3,"file":"bit-data.js","sourceRoot":"","sources":["../../src/lib/bit-data.ts"],"names":[],"mappings":"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","sourcesContent":["import debug from 'debug';\nimport { NbtLongArray } from '../nbt/nbt-long-array';\n\nconst debugLog = debug('bit-data');\n\n// At most, we will have 7 bits to mask\nconst MASK = [0, 1, 3, 7, 15, 31, 63, 127];\n\nexport class BitData {\n static fromLongArrayTag(tag: NbtLongArray): BitData {\n const arrayBuffer = new ArrayBuffer(tag.data.length * 8);\n const view = new DataView(arrayBuffer);\n let position = 0;\n\n for (const value of tag.data) {\n // Convert back to a byte stream. The source comes from\n // NbtLongArray.fromBinaryData(). Make sure the endianness is\n // correct and matches the reader.\n view.setBigInt64(position, value, true);\n position += 8;\n }\n\n return new BitData(arrayBuffer);\n }\n\n private length: number;\n private partial = 0;\n private partialCount = 0;\n private position = 0;\n private view: DataView;\n\n constructor(data: ArrayBuffer) {\n this.view = new DataView(data);\n this.length = data.byteLength;\n }\n\n currentPosition() {\n return this.position;\n }\n\n getBits(n: number): number {\n debugLog(\n `getBits(${n}), start, position ${this.position}, bits ${this.partial.toString(2).padStart(this.partialCount, '0').substr(-this.partialCount)}`\n );\n\n // Add extra bits to the left\n while (n > this.partialCount) {\n if (this.position >= this.length) {\n throw new Error('Out of data');\n }\n\n this.partial |=\n this.view.getUint8(this.position) << this.partialCount;\n debugLog(\n `getBits(${n}), add byte, position ${this.position}, bits ${this.partial.toString(2).padStart(this.partialCount, '0').substr(-this.partialCount)}`\n );\n this.position += 1;\n this.partialCount += 8;\n }\n\n // Return bits from the right\n const result = this.partial & MASK[n];\n this.partial >>= n;\n this.partialCount -= n;\n debugLog(\n `getBits(${n}), end, position ${this.position}, bits ${this.partial.toString(2).padStart(this.partialCount, '0').substr(-this.partialCount)}, result ${result.toString(2).padStart(n, '0')} (${result})`\n );\n\n return result;\n }\n\n // The amount of bytes left in the buffer\n remainingLength() {\n return this.length - this.position;\n }\n\n seek(position: number, partialCount = 0) {\n this.position = position;\n this.partial = 0;\n this.partialCount = partialCount;\n }\n\n // FIXME - need to figure out how this is written. Does it update just one byte?\n // FIXME - I don't see a flush method.\n setBits(n: number, value: number) {\n // Add the new bits to the right\n this.partial <<= n;\n this.partial |= value;\n this.partialCount += n;\n\n while (this.partialCount >= 8) {\n this.view.setUint8(\n this.position,\n this.partial >> (this.partialCount - 8)\n );\n this.position += 1;\n this.partialCount -= 8;\n this.partial &= MASK[this.partialCount];\n }\n }\n}\n"]}
1
+ {"version":3,"file":"bit-data.js","sourceRoot":"","sources":["../../src/lib/bit-data.ts"],"names":[],"mappings":"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","sourcesContent":["import debug from 'debug';\nimport { NbtLongArray } from '../nbt/nbt-long-array';\n\nconst debugLog = debug('bit-data');\n\n// At most, we will have 8 bits to mask\nconst MASK = [0, 1, 3, 7, 15, 31, 63, 127, 255];\n\nexport class BitData {\n static fromLongArrayTag(tag: NbtLongArray): BitData {\n const arrayBuffer = new ArrayBuffer(tag.data.length * 8);\n const view = new DataView(arrayBuffer);\n let position = 0;\n\n for (const value of tag.data) {\n // Convert back to a byte stream. The source comes from\n // NbtLongArray.fromBinaryData(). Make sure the endianness is\n // correct and matches the reader.\n view.setBigInt64(position, value, true);\n position += 8;\n }\n\n return new BitData(arrayBuffer);\n }\n\n private length: number;\n private partial = 0;\n private partialCount = 0;\n private position = 0;\n private view: DataView;\n\n constructor(data: ArrayBuffer) {\n this.view = new DataView(data);\n this.length = data.byteLength;\n }\n\n currentPosition() {\n return this.position;\n }\n\n getBits(n: number): number {\n debugLog(\n `getBits(${n}), start, position ${this.position}, bits ${this.partial.toString(2).padStart(this.partialCount, '0').substr(-this.partialCount)}`\n );\n\n // Add extra bits to the left\n while (n > this.partialCount) {\n if (this.position >= this.length) {\n throw new Error('Out of data');\n }\n\n this.partial |=\n this.view.getUint8(this.position) << this.partialCount;\n debugLog(\n `getBits(${n}), add byte, position ${this.position}, bits ${this.partial.toString(2).padStart(this.partialCount, '0').substr(-this.partialCount)}`\n );\n this.position += 1;\n this.partialCount += 8;\n }\n\n // Return bits from the right\n const result = this.partial & MASK[n];\n this.partial >>= n;\n this.partialCount -= n;\n debugLog(\n `getBits(${n}), end, position ${this.position}, bits ${this.partial.toString(2).padStart(this.partialCount, '0').substr(-this.partialCount)}, result ${result.toString(2).padStart(n, '0')} (${result})`\n );\n\n return result;\n }\n\n // The amount of bytes left in the buffer\n remainingLength() {\n return this.length - this.position;\n }\n\n seek(position: number, partialCount = 0) {\n this.position = position;\n this.partial = 0;\n this.partialCount = partialCount;\n }\n\n // FIXME - need to figure out how this is written. Does it update just one byte?\n // FIXME - I don't see a flush method.\n setBits(n: number, value: number) {\n // Add the new bits to the right\n this.partial <<= n;\n this.partial |= value;\n this.partialCount += n;\n\n while (this.partialCount >= 8) {\n this.view.setUint8(\n this.position,\n this.partial >> (this.partialCount - 8)\n );\n this.position += 1;\n this.partialCount -= 8;\n this.partial &= MASK[this.partialCount];\n }\n }\n}\n"]}