Skip to content

Commit 2366e6e

Browse files
documentation and env enforcement
1 parent 481ddca commit 2366e6e

File tree

7 files changed

+101
-7
lines changed

7 files changed

+101
-7
lines changed

.env.example

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
MINECRAFT_HOST=127.0.0.1
2+
MINECRAFT_PORT=25565
3+
MINECRAFT_USERNAME=idk
4+
MINECRAFT_CHEST_X=-382
5+
MINECRAFT_CHEST_Y=68
6+
MINECRAFT_CHEST_Z=-77

README.md

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Minecraft Node.js package loader
2+
3+
Load JavaScript packages from books in chests on a Minecraft server.
4+
5+
This will create a headless Minecraft client, join the configured server, and open
6+
a chest to look for signed books.
7+
8+
Huge shoutout to the authors of `minecraft-protocol` and Prismarine. Their work
9+
lets this simply stitch together some packages and patch a resolution quirk. 💙
10+
11+
## Usage
12+
13+
1. **in-game:** Login to an offline mode Minecraft server (any version supported by [`minecraft-data`](https://npmjs.com/package/minecraft-data))
14+
15+
2. **in-game:** Write your code in a book and quill
16+
17+
> **Note**
18+
> The first line of the first page should be a semver string
19+
20+
> **Note** > `import`ed packages will be loaded relative to the last on-disk module
21+
>
22+
> e.g. if `example.mjs` imports `minecraft:helloer`, any `import` in `helloer` will be
23+
> loaded as if you were in `example.mjs`.
24+
25+
This could look something like:
26+
27+
![Book page 1](./docs/assets/book-page-1.jpg)
28+
29+
![Book page 2](./docs/assets/book-page-2.jpg)
30+
31+
3. **in-game:** Sign your book and quill with the package name as the title
32+
33+
![Book title](./docs/assets/book-title.jpg)
34+
35+
4. **in-game:** Place your book, with any others, in a chest
36+
37+
- This chest must be close enough to the configured player that it can open the chest
38+
without moving. This is 5 blocks away on an unmodified server. Keep in mind spawn
39+
protection prevents non OP-ed players from opening a chest (I forgot this and spent hours trying
40+
to fix the code 😅).
41+
42+
![Chest](./docs/assets/chest.jpg)
43+
44+
5. Configure your shell's environment with the correct variables following [`.env.example`](./.env.example).
45+
46+
6. Put together a consuming script locally (see `example.mjs`)
47+
48+
```js
49+
// example.mjs
50+
import * as helloer from 'minecraft:helloer';
51+
console.log(helloer);
52+
console.log(helloer.getWelcome('Cooper'));
53+
console.log(helloer.getDatedWelcome('Cooper'));
54+
```
55+
56+
7. Run script with the `--loader` flag
57+
58+
```console
59+
$ node --loader ./dist/loader.mjs example.mjs
60+
[Module: null prototype] {
61+
getDatedWelcome: [Function: getDatedWelcome],
62+
getWelcome: [Function: getWelcome]
63+
}
64+
Hi, Cooper
65+
Welcome to 2023, Cooper!
66+
```
67+
68+
## Wishlist
69+
70+
- npm compatible registry so the consumer doesn't need any non-standard dependencies
71+
- Support teleporting the player (issue server command) so spawn location isn't a big deal

docs/assets/book-page-1.jpg

237 KB
Loading

docs/assets/book-page-2.jpg

197 KB
Loading

docs/assets/book-title.jpg

218 KB
Loading

docs/assets/chest.jpg

139 KB
Loading

src/minecraft.mts

+24-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
import mineflayer from 'mineflayer';
22
import { TagType, type List } from 'prismarine-nbt';
3+
// @ts-expect-error
34
import vec3 from 'vec3';
45
import { setTimeout } from 'timers/promises';
56
import { Tags } from 'prismarine-nbt';
67
import debug from 'debug';
8+
import { z } from 'zod';
9+
10+
const envSchema = z.object({
11+
MINECRAFT_HOST: z.string(),
12+
MINECRAFT_PORT: z.string().transform((x) => parseInt(x, 10)),
13+
MINECRAFT_USERNAME: z.string(),
14+
MINECRAFT_CHEST_X: z.string().transform((x) => parseInt(x, 10)),
15+
MINECRAFT_CHEST_Y: z.string().transform((x) => parseInt(x, 10)),
16+
MINECRAFT_CHEST_Z: z.string().transform((x) => parseInt(x, 10)),
17+
});
18+
const env = envSchema.parse(process.env);
719

820
const log = debug('loader:minecraft');
921

1022
const bot = mineflayer.createBot({
11-
host: '127.0.0.1',
12-
port: 25565,
13-
username: 'idk',
23+
host: env.MINECRAFT_HOST,
24+
port: env.MINECRAFT_PORT,
25+
username: env.MINECRAFT_USERNAME,
1426
auth: 'offline',
1527
});
1628

@@ -44,13 +56,18 @@ export const getPackage = async (name: string) => {
4456
};
4557

4658
bot.on('spawn', async () => {
47-
let block = bot.blockAt(vec3(-382, 68, -77));
59+
const loc = vec3(
60+
env.MINECRAFT_CHEST_X,
61+
env.MINECRAFT_CHEST_Y,
62+
env.MINECRAFT_CHEST_Z
63+
);
64+
let block = bot.blockAt(loc);
4865
while (!block) {
49-
log('Block state not yet available. Trying again soon..');
66+
log('Block state not yet available. Trying again soon..', loc);
5067
await setTimeout(10);
51-
block = bot.blockAt(vec3(-382, 68, -77));
68+
block = bot.blockAt(loc);
5269
}
53-
log('Block state available');
70+
log('Block state available', loc);
5471

5572
const chest = await bot.openContainer(block);
5673

0 commit comments

Comments
 (0)