- Rust 98.6%
- JavaScript 1.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| data | ||
| helper | ||
| src | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
| rust-toolchain.toml | ||
Plobot (Rust)
A Rust rewrite of Plobot, the Minecraft bot for Kaboom clones, built on azalea and tokio. It targets Minecraft 26.2.
It keeps the JavaScript version's behaviour: the same commands, help texts,
data files and command-block-based output. Every plugin is ported except
sh.
Requirements
- Nightly Rust. azalea needs it;
rust-toolchain.tomlselects it automatically when you userustup. - Linux on x86_64 or aarch64 for the Scheme plugin's sandbox (see below). Everything else runs anywhere azalea does.
- Optional external programs, only needed by the plugins that use them:
git(about),inxi(diag),rubyandbwrap(bubblewrap) (gs), andfizmo-console(zork).
Building and running
cd rustver
cargo build --release
./target/release/plobot [host] [port] [username] [prefix]
Defaults: kaboom.hcesaropz.dev, port 25565, username _dat, prefix ;.
On the default port the bot looks up the server's _minecraft._tcp SRV record,
like mineflayer. It then tries each of the server's addresses until one
accepts a connection, so it still works on machines without IPv6.
Accounts are offline-mode.
Files
Plugin data lives in data/<plugin>/, relative to the working directory. Set
PLOBOT_DATA to use another directory. For example, PLOBOT_DATA=../plugins
reuses the JavaScript bot's existing files. Files the bot creates itself when
they're missing:
| File | Plugin |
|---|---|
alias/aliases.json |
alias |
paste/pastes.json |
paste |
onjoin/list.json |
onjoin |
hangman/numberOfGuesses |
hangman |
scheme/init.scm |
scheme |
lastBotMentionReg.json |
"I'm a bot" reminders |
Files you provide:
| File | Plugin |
|---|---|
daily/*.txt (included), daily/words |
motd, wotd |
hangman/words |
hangman |
chess/game.pgn (optional; a new game starts if missing) |
chess |
aotd/<lang>wiki-*.gz (Wikipedia title lists) |
aotd |
message/message |
message |
golfscript/golfscript.rb (download; not redistributable) |
gs |
zork/zork1-r119-s880429.z3 |
zork |
These stay in the working directory, like the JavaScript version:
spam_quota: how many times someone must saystopto stop the bot. If the file is missing, the limit is unlimited..die_requestedis created when the bot is stopped from chat.<host>_<port>(e.g.kaboom.hcesaropz.dev_25565) is checked every 10 seconds. Each line is queued as a command-block command, then the file is deleted. The JavaScript versionevaled this file as JavaScript instead.
Scheme sandbox
User Scheme code runs in steel, in a
separate worker process (the bot binary started with --scheme-worker).
steel's own sandboxed engine still lets code require-builtin its
filesystem and process modules. The real boundary is the operating system,
not the interpreter. The worker:
- gets an empty environment and
/as its working directory, - talks to the bot over private pipes; its stdin, stdout and stderr are
/dev/null, - is limited to 1 GiB of memory, no child processes and no core dumps,
- installs a seccomp allowlist before running any user code. Only syscalls
for computing and for using the pipes it already has are allowed. Opening
files, reading file metadata,
exec, sockets, threads and signalling other processes all fail withEPERM. If the filter can't be installed, the worker exits rather than run unsandboxed, - is killed after 3 seconds of evaluation, and when the bot exits.
The worker restarts automatically after a timeout or crash. It re-runs the
default proc and init.scm, but loses anything user code defined at run
time.
GolfScript sandbox
gs runs the reference interpreter, golfscript.rb, which builds string
literals with Ruby's eval. That makes "#{...}" a real Ruby escape hatch.
It's allowed, but Ruby runs inside bubblewrap:
- read-only
/usrand an empty private/tmp; nothing else from the host, including your home directory, - no network, and its own process, IPC and hostname namespaces,
- an empty environment and a 1 GiB memory limit,
- killed after 5 seconds, or when the bot exits.
Output is capped at 16 KiB. Without bwrap the command fails instead of
running unsandboxed.
Architecture
src/main.rs: startup, azalea events, chat processing (one message at a time, in order), the emergency stop, and the periodic tasks.src/chat.rs: works out who sent a chat line and what they said, using the same Kaboom-specific rules as the JavaScript version.src/runner.rs: runs queued commands through a repeating command block. It uses the response-time curve from the main README (75 + (3000 - 75)(1 - e^(-αx))ms), and places a new command block when none is within 16 blocks.src/help.rs:helpand its translations.src/plugins/: one module per plugin, implementing thePlugintrait:initregisters help topics, creates files and starts background tasks.preprocessrewrites a message's words before handlers see them.handleresponds to a message.on_spawn,on_whisperandon_packetreact to those events.
Adding a plugin: create src/plugins/<name>.rs, implement Plugin, and add
it to plugins::all().
Testing
cargo test
helper/helper.js is a small mineflayer bot for testing on a live server. It
joins as claude-helper and only adds a number if that name is taken. It
sends each line from stdin as chat (or a command, if the line starts with
/) and prints everything it sees in chat:
npm install # in the repository root
node rustver/helper/helper.js [host] [port] [version]
It defaults to kaboom.hcesaropz.dev and protocol version 26.1, the newest
mineflayer supports; the server translates. Type .quit to leave.
Differences from the JavaScript version
shis not ported.- The
<host>_<port>file holds commands, not JavaScript. - Errors in one plugin are logged and don't stop the other plugins from handling the same message.
- Private messages from names mineflayer's pattern misses (like
claude-helper) are recognized. gsruns Ruby inside a sandbox with a time limit (see above), and each program gets its own temporary file instead of a shared/tmp/my_script.gs.- Scheme code is run by steel instead of BiwaScheme, so the built-in functions and error messages differ.
serverinfoleaves out:25565even when the port is given on the command line.- Commands that crashed the JavaScript version on bad input now just do
nothing:
hdwith no text,onjoinbefore anyone joined, andchesswith no saved game (which now starts a new one).