PyMC_Bot
A Python-controlled Minecraft player bot — joins a server as a normal player, steered from a web config panel with an Ollama brain, anti-AFK and fleet support.
A Python-controlled Minecraft player bot — joins a server as a normal player, steered from a web config panel with an Ollama brain, anti-AFK and fleet support.
[](https://github.com/codero-sus) [](https://the-codero.web.app/) [](LICENSE)
> Made with ❤️ by codero-sus · the-codero.web.app
A Python-controlled Minecraft player bot it joins a (cracked / offline-mode) server as a normal player, you configure and steer it from a web control panel served by Uvicorn, and it can hand the wheel to a local LLM – an Ollama model, or any model hosted by Cortex LLMHoster – that decides how it plays (walking, mining, following players, chatting).
Premium accounts and whole crowds are first-class: add a player with a Microsoft account (device-code login, token cached per account), or fill the server with up to 200 bots at once.
Everything is Python: configuration, the movement engine, the AI loop, the REST/WebSocket API and the web UI. The only JavaScript is a ~300 line adapter that speaks the Minecraft protocol through mineflayer (there is no maintained Python client for modern Minecraft versions), and it is driven purely by JSON from Python.
┌──────────────────────────────┐ ┌───────────────────────────────────────────┐
│ Browser (config page) │ │ Python (pymc_bot/) │
│ HTML/CSS/vanilla JS, no │◄──────►│ FastAPI + Uvicorn server.py │
│ build step │ REST │ MinecraftBot bot.py │
│ REST + WebSocket /ws │ WS │ AgentLoop (LLM/trained) agent.py │
└──────────────────────────────┘ │ Backends: simulated backends.py │
└───────────────┬───────────────────────────┘
│ newline-delimited JSON (stdin/stdout)
┌───────────────▼───────────────────────────┐
│ Node mineflayer → Minecraft server │
│ (cracked/offline auth supported) │
└───────────────────────────────────────────┘
backend: simulated, reporting players, a mob, an animal, a droppedstack and a starter inventory, so trained models have entity and item inputs) lets you try the whole panel, movement and AI loop on any machine, and it is what the test suite runs against.
built-in heuristic policy, so it never just stands there.
bearer key read from the environment and never saved).
detailed, hidden player prompt for it, a goal it sticks to, short-term memory and plain-word perception; it chats in short casual lines with typing pauses and no spam (details).
players one email at a time, and can keep the crowd alive (rejoin on kick, optional idle chatter).
head in smooth, randomised bursts, so servers do not kick them for standing still. It never fights the AI or your manual commands, and it scales to the whole fleet from one thread.
(<kbd>Ctrl</kbd>+<kbd>K</kbd>), an at-a-glance overview strip, a section bar that follows your scroll, collapsible cards, toasts for everything you do, an animated minimap with radar sweep, and a little confetti when the bot joins or a training run finishes. Every animation can be switched off (✨ / <kbd>Alt</kbd>+<kbd>M</kbd>, and the OS "reduce motion" setting is honoured).
./mod, every Minecraft from 1.21.1 to 26.3**) records how you play, pymc_bot trainturns that into a checkpointed player model, and think trained runs the bot with it. No Ollama, no GPU, no cloud: the model learns your movements, your turns and your habits and reproduces them in game.
git clone https://github.com/codero-sus/PyMC_Bot.git
cd PyMC_Bot
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
npm install # mineflayer bridge (only for real servers, needs Node >= 22)
python -m pymc_bot serve # or: python -m pymc_bot (serve is the default)
# -> http://localhost:8000
In the panel:
auto sniffs it), authoffline (cracked), backend auto, then Save & connect.
Set backend to simulated in the panel (or run python -m pymc_bot run --backend simulated). The bot walks around a small pretend world, "mines" blocks, chats with two NPCs and answers you in the event stream – a full dress rehearsal with no server needed.
Your server must allow offline-mode logins (online-mode=false in server.properties; this is what "cracked" means). Then set host/port/username with auth offline.
Online-mode servers need a real account. Set Auth → microsoft and put the account's email in the username field (the in-game name is whatever that account owns):
Premium login: open https://www.microsoft.com/link and enter the code ABCD-1234 (account: you@example.com).
.pymc_profiles/<account>/, so every laterjoin is silent — including after a restart, a kick or a --reconnect.
Notes: each premium player needs its own Microsoft account; tokens are never shared between accounts. Accounts must own Minecraft: Java Edition (Bedrock/console-only accounts fail with "does the account own minecraft?", which the panel shows verbatim).
The Players card adds more bots on top of the main one — all against the same server:
| Control | What it does | | --- | --- | | Players to add + Name pattern | spawn N bots named like PyMC_Bot_1 … PyMC_Bot_N ({n} = index, {name} = main bot name) | | Fleet auth | offline (any name) or microsoft (premium, one email per bot) | | Fleet AI | heuristic (cheap, default), off (just stand there) or ollama (uses the model) | | Gap between joins | stagger connections (1–2s) so the server does not rate-limit the stampede | | Add premium player | adds a single premium player by email | | Broadcast | make every connected bot say the same thing | | idle chatter | the bots occasionally chat (off by default) | | restore these players on restart | remember the fleet in the config file and bring it back on startup |
Every player is listed with its auth mode, state, position and per-bot use / start / stop / drop buttons. The dropdown in the top bar (or use) decides which player the rest of the panel controls — manual chat, commands and actions then apply to that player, and bot: "all" applies to everyone.
fleet.max_bots (default 25, max 200) caps the whole instance. A join batch runs in the background, so the panel stays responsive while twenty players connect.
Headless equivalent:
python -m pymc_bot run --populate 20 # 20 offline players join the configured server
python -m pymc_bot run --premium you@example.com # add a premium player (prints the device code)
Enabled by default for every bot — the main one and the whole fleet. When a bot has been idle for a while (8–25 s by default, randomised), it performs a short burst of human-ish habits:
| Habit | What it looks like | | --- | --- | | look | head turns over several small steps (never a snap), occasionally glancing up or down | | look_at_player | turns towards a nearby player for a moment, then looks away | | stroll | walks one to three blocks somewhere else, then stops | | strafe | a short sideways step while looking slightly off-axis | | hop | one or two jumps in place | | crouch | a brief sneak | | swing | swings the arm a couple of times (some plugins only check animation) |
Two or three habits are often chained, the same habit never repeats back-to-back, and the intervals are jittered — so the pattern does not look like a metronome. The keeper only touches bots that are not busy: AI decisions, follows, mining and your manual commands always take priority.
"antiafk": {
"enabled": true,
"interval_min": 8.0, // idle seconds before a burst (randomised)
"interval_max": 25.0,
"look": true, "look_at_players": true, "walk": true, "jump": true, "sneak": true, "swing": true,
"max_walk_distance": 3.0,
"combo_probability": 0.35, // chance of chaining 2-3 habits in one burst
"verbose": false // log every burst to the event stream
}
Controls live in the Players → Anti-AFK row (toggle, idle window, which habits, Poke all now), and the player table shows each bot's idle time and how many anti-AFK bursts it has done. From the API: GET /api/antiafk/status and POST /api/antiafk/poke {"bot": "all"}.
> Only connect the bot to servers you own or are allowed to use it on. Many servers forbid client bots; > check their rules first. Attacking other players is disabled by default.
Run the updater from the project folder - it only needs Python, nothing has to be installed first:
./updater.sh # macOS / Linux
updater # Windows (cmd or PowerShell: .\updater.cmd), or double-click updater.cmd
python updater.py # anywhere
| Command | What it does | | --- | --- | | updater --check | shows the new commits / mod release, changes nothing | | updater | updates PyMC_Bot; reinstalls Python (and npm) packages only if requirements*.txt / package.json changed | | updater --mod | also updates the recorder mod in your .minecraft/mods folder | | updater --mod-only | only the mod | | updater --mod-only --mc 1.21.4 | installs the mod for 1.21.4 when it is not there yet | | updater --help | all options (--mods-dir, --pre, --reinstall, --deps, --no-deps, --branch, --autostash) |
git fetch + git pull --ff-only. If you edited tracked files the updaterstops and tells you which ones; --autostash stashes them, updates and puts them back.
main source and copies it over thefolder. pymc_bot_config.json, .venv, models/, recordings and every other file of yours are left alone; repository files you changed are backed up to .pymc_backup/<time>/ first.
pymcbot-<version>+<mc>.jar (or a pre-0.6 pymcplaytime-*.jar, which it replaces) in the mods folder (%APPDATA%\.minecraft\mods,~/Library/Application Support/minecraft/mods, ~/.minecraft/mods, or --mods-dir), downloads the jar for the same Minecraft version from the newest release, verifies its size, SHA-256 and fabric.mod.json, and keeps the old jar as *.jar.old. Close Minecraft first on Windows. Stable releases are preferred; --pre opts into pre-releases (used automatically while no stable release exists).
.venv when it exists; otherwise the updater prints the pip command to run.Set GITHUB_TOKEN if you hit GitHub's 60-requests-per-hour limit. Exit code 0 = OK, 1 = error.
Restart the panel / bot afterwards.
The bot can learn how you play and then play like that itself. Three steps:
1. Record – download the PyMC Bot mod jar (pymcbot-<version>+<mc>.jar) for your Minecraft version (1.21.1 – 26.3) from the GitHub release (or build it from ./mod: with JDK 25 installed, cd mod && ./gradlew build -Pmc=<your version> – full steps in Compiling the mod), drop it in .minecraft/mods/ with Fabric API, then in game:
/pymcbot record start # or your hotkey; play normally for 10-60 minutes
/pymcbot record stop
/pymcbot export # -> <gameDir>/pymcbot/dataset.jsonl
A REC badge in the corner shows the run clock and sample count while you record. Everything else is on the PyMC Bot settings page – /pymcbot settings, a hotkey, or Mod Menu's Configure button: tabs for the recorder controls, recording options, advanced entity + item limits, the HUD badge (with a live preview) and About; hover any button for help (details). /pymc still works as a short alias.
The mod samples your playtime 20×/s — position, velocity, view, posture, hotbar, health/food, the 3×3×3 block neighbourhood, nearby entities, and the action you were performing (forward, back, left, right, jump, sneak, look, attack, use, hold, move, none). /pymcbot advanced on additionally records entities, items, the held item, armour and dropped stacks for advanced training.
2. Train – checkpointing itself as it goes:
python -m pymc_bot train --dataset "<gameDir>/pymcbot/dataset.jsonl" --steps 2000
python -m pymc_bot train --resume --run-name playtime-dataset # continue where it left off
python -m pymc_bot models # list checkpoints + metrics
models/playtime-dataset/
├── latest.npz weights (+ optimiser state) - or latest.pt for --engine transformer
├── checkpoints/step-0000200.npz
├── model.json the card the panel lists (metrics, dataset digest, action vocabulary)
├── trainer_state.json step/epoch/RNG/best-val -> exactly resumable
└── metrics.jsonl, metrics.csv
No recording yet? python -m pymc_bot train --simulate 10 fabricates a believable session so you can try the whole pipeline (and the panel's Demo: simulate 5 min button) without launching Minecraft.
3. Run it – either from the shell or the panel:
python -m pymc_bot run --think trained --run-name playtime-dataset --backend simulated
{ "agent": { "mode": "trained" }, "training": { "active_run": "playtime-dataset" } }
The policy predicts a burst of play (default 0.4 s) and applies the same low-level controls the mod recorded, including the predicted head turn — so the bot walks, strafes, hops and looks around the way you did, and the anti-AFK keeper treats it as activity. It obeys the same permission gates as the other brains (looking and idling always; moving, mining/using and attacking only when their switches are on), so a model trained on your playtime can never do something you turned off. Keep Ollama for chat and high-level planning; the trained brain is a separate, local, dependency-light model (--engine mlp needs nothing but NumPy; --engine transformer adds a small attention model). Full details, metrics and troubleshooting: docs/TRAINING.md.
The basic encoder describes the world in coarse buckets ("hostile", "passive", "resource block"). Advanced training learns the entity and item words your playtime actually contains and feeds each one to the model with its distance and where it sits relative to your view — so "a creeper four blocks away while holding a sword" is a different situation from "a cow across the field while holding a pickaxe":
/pymcbot advanced on # in game, before recording: entities + items + drops are written too
/pymcbot record start ... /pymcbot record stop ... /pymcbot export
python -m pymc_bot train --dataset "<gameDir>/pymcbot/dataset.jsonl" --advanced --steps 2000
python -m pymc_bot train --dataset ... --advanced --entity-slots 32 --item-slots 48 # bigger vocabularies
python -m pymc_bot train --dataset ... --inspect # vocabulary + coverage before you train
The learned vocabulary is stored in the checkpoint and in model.json, so the bot encodes the live world into exactly the same slots (layout v2; feature_version guards the hand-off). In the panel it is the Advanced training box in the Player model card: tick it, train, and the status line shows what the dataset offers and which words the newest model learned. Details: docs/TRAINING.md.
# real model
ollama pull llama3.2
ollama serve # usually already running on http://127.0.0.1:11434
Then in the panel: enable AI brain, set the model name, Start AI. Every ollama.decision_interval seconds (default 6s, ±25% so it doesn't act like a metronome) the bot describes the situation and asks for one action:
{"thought": "need wood before tools", "goal": "get wood for tools", "action": "mine", "block": "oak_log"}
The LLM brain (Ollama or Cortex) gets a detailed system prompt (pymc_bot/mind.py) that makes it act like a real player at a keyboard, not an assistant:
first, then keeps working on a goal for several turns (wood → tools → food → shelter before night) instead of picking random actions. A short private thought is shown in the log (💭 ...).
"Food 5/20 (hungry - eat soon)", "Waiting for your answer: <Steve> wanna mine together?" – with the exact JSON appended for coordinates.
agent.memory_turns actions with whether they worked (so it stopsretrying a failing path), its last thoughts and what it already said.
speeches, never the same line twice; a typing pause before sending and a chat_cooldown for messages nobody asked for (replies skip it). If someone sincerely asks whether it is a bot, it says so.
stays hidden:
| Behaviour | What it does | | --- | --- | | 😊 Friendly helper (default) | plays survival, says hi, helps anyone who asks | | 😎 Chill survivor | does its own thing at its own pace, few words | | 💬 Social butterfly | loves to chat, hangs out with players, joins in | | 🤝 Loyal companion | sticks with you, follows you around, watches your back | | 🏗️ Builder | gathers logs and stone, talks about builds | | 🧭 Explorer | wanders far, checks out caves and new places | | ⛏️ Miner | mines ores, upgrades tools, shares coal and iron | | 🐣 New player | still learning, asks questions, a bit clumsy | | 🏆 Experienced pro | efficient and confident, gives short useful tips | | 🤫 Quiet player | rarely talks, just plays near others |
Optional About the character notes ("from Delhi, mixes Hindi and English") are added on top.
In the config the choice is agent.persona (friendly, chill, social, companion, builder, explorer, miner, newbie, pro, quiet) plus agent.persona_notes. Advanced: a non-empty ollama.system_prompt / cortex.system_prompt in the config file replaces the built-in prompt entirely (configs that still hold the old default switch to the new prompt automatically). Small models work; 7–8B models (e.g. llama3.1:8b, qwen2.5:7b) follow the behaviour and goals noticeably better.
Accepted actions: say, goto, follow, wander, mine, attack (gated), jump, eat, look_at_player, stop, wait. The full list, including which permissions gate them, is in docs/API.md. Malformed model output is caught, logged and retried with the heuristic policy instead of crashing the loop.
For demos, tests and CI there is an Ollama-compatible stub – clearly not a language model, just a rule-based responder that answers the same /api/chat, /api/generate and /api/tags endpoints (and the OpenAI-style /v1/chat/completions, /v1/completions, /v1/models, /health, so it can also stand in for Cortex):
python -m pymc_bot stub # listens on http://127.0.0.1:11434
The panel shows a demo brain (stub) badge whenever the endpoint identifies itself as the stub, so it is never mistaken for a real model.
Cortex LLMHoster serves local models through an OpenAI-compatible API on http://127.0.0.1:8624. Select cortex as the brain (or enable it so auto uses it when Ollama is off), export the same CORTEX_API_KEY Cortex was started with, and the bot sends its world state to POST /v1/chat/completions every cortex.decision_interval seconds:
export CORTEX_API_KEY=... # read from the environment, never written to the config
python -m pymc_bot doctor # cortex : http://127.0.0.1:8624/v1 -> OK (1 model(s): qwen-local; ...)
python -m pymc_bot run --think cortex --cortex-model qwen-local
In the panel: AI brain → Cortex LLMHoster → Check Cortex → Use Cortex as brain. Fleet bots can use cortex too. Same action contract, same permission gates and the same heuristic fallback as Ollama. Setup, a weight-free demo (the stub standing in for Cortex) and troubleshooting: docs/CORTEX.md. Cortex is a separate program under its own personal, non-commercial license; PyMC_Bot only talks to it over HTTP.
python -m pymc_bot serve → <http://127.0.0.1:8000>. Besides the cards for the server, players, brain, player model, live view, manual control, events and raw config, the panel has:
| | | | --- | --- | | Overview strip | connection, brain, players online, decisions, distance walked and the active player model, with live counters | | Command palette | <kbd>Ctrl</kbd>/<kbd>⌘</kbd>+<kbd>K</kbd> (or <kbd>/</kbd>): jump to any card, connect, start the AI, train, poke the fleet, switch theme, open the docs | | Themes | 🎨 or <kbd>Alt</kbd>+<kbd>T</kbd>: midnight · daylight · nether · end, remembered per browser | | Motion | ✨ or <kbd>Alt</kbd>+<kbd>M</kbd> turns every animation and the floating-block background off; "reduce motion" in the OS is respected by default | | Cards | collapse with ▾ or a double-click on the title (remembered), cursor spotlight, staggered entrance | | Feedback | toasts for every panel action and connection change, spinners on buttons while a request runs, a damage flash and low-health pulse on the meters, confirmation before destructive actions | | Live view | the minimap eases between status frames, scrolls its grid with the bot and draws a radar sweep | | About | ⓘ opens the credits (also GET /api/about) |
PyMC_Bot joins servers on Aternos, exaroton, Minehut, FalixNodes, and servers shared through playit.gg, ngrok or e4mc tunnels, as well as servers on your own PC.
name.aternos.me and leave the port at 25565. The address has an SRV record thatthe bot follows like the real game does. You can also paste host:port (a Dyn IP or a tunnel address) into Host, and the port fills itself in.
python -m pymc_bot probe pings the server without joining. Itshows online, asleep or starting, the version, the players and where the SRV record points.
placeholder ("● Offline"). The bot now reports it plainly and tells you to start the server from the host's dashboard. With Server asleep? → wait for it to start (wait_for_server), it checks every 15 s and joins once someone has started it. PyMC_Bot never starts a server itself.
Options → Cracked. Otherwise the kick message says which switch to flip. Minehut only lets premium (Microsoft) accounts in.
therefore leave once no real player has been online for alone_minutes (5 by default), and they don't re-join on their own; press Connect when people are back. The bots play while you and your friends are on, which is what Aternos allows. Set leave_when_alone: "always" to use the same rule on any server.
(currently anything newer than 26.1), see Troubleshooting. ViaVersion + ViaBackwards (Aternos offers both as plugins) let the bot in.
| Command | What it does | | --- | --- | | python -m pymc_bot serve [--host 0.0.0.0] [--port 8000] [--autostart] [--auto-agent] | run the web config panel (Uvicorn) | | python -m pymc_bot run [--backend node\|simulated] [--server host:port] [--seconds N] [--no-ai] [--populate N] [--premium EMAIL] [--think auto\|heuristic\|ollama\|cortex\|trained] [--cortex-url URL] [--cortex-model ID] | headless bot: connect and play (optionally populate the server), logs to stdout | | python -m pymc_bot train [--dataset FILE] [--steps N] [--engine mlp\|transformer] [--advanced [--entity-slots N] [--item-slots N]] [--resume] [--simulate MINUTES] [--ollama-model NAME] | train a player model on recorded playtime (self-checkpointing, resumable); --advanced learns entity + item vocabularies | | python -m pymc_bot models [--models-dir models] [--json] | list trained checkpoints with their metrics | | python -m pymc_bot action '{"action":"wander"}' [--url http://127.0.0.1:8000] | poke a running panel from the shell | | python -m pymc_bot doctor | environment check (Node, mineflayer, Minecraft range, Ollama, Cortex, config, server ping, hosting tips) | | python -m pymc_bot probe [host[:port]] [--json] | ping a server without joining: online / asleep / starting, version, players, SRV target, tips for Aternos & co (exit 0 online, 3 asleep, 1 unreachable) | | python -m pymc_bot stub [--port 11434] | the Ollama/OpenAI-compatible demo stub | | python -m pymc_bot about [--json] / --version | author, links and credits |
Everything is also configurable through pymc_bot_config.json (created next to where you run the bot, or --config path.json / PYMC_BOT_CONFIG). The entire file can be edited in the panel under Advanced.
{
"minecraft": {
"host": "127.0.0.1", "port": 25565, // "name.aternos.me" (SRV) or "host:port" also work
"username": "PyMC_Bot", // max 16 characters
"version": "auto", // or "1.20.4", "1.12.2", ...
"auth": "offline", // "offline" (cracked) | "microsoft"
"backend": "auto", // "auto" (node, else simulated) | "node" | "simulated"
"view_distance": "normal", "connect_timeout": 20.0,
"profiles_folder": ".pymc_profiles", // Microsoft token cache, one sub-folder per account
"auto_rejoin": true, // keep re-joining after kicks/disconnects
"rejoin_seconds": 10.0, // base delay between rejoin attempts (backs off to 60s)
"wait_for_server": false, // free host asleep? wait until someone starts it, then join
"wait_minutes": 10.0,
"leave_when_alone": "auto", // "auto" (on for Aternos, whose rules require it) | "always"
"alone_minutes": 5.0 // ... leave after this long with no real player online
},
"ollama": {
"enabled": false,
"base_url": "http://127.0.0.1:11434",
"model": "llama3.2",
"temperature": 0.7, // a little variety makes it more human
"decision_interval": 6.0, // seconds between AI decisions (±25% jitter)
"request_timeout": 60.0,
"system_prompt": "" // "" = built-in human-player prompt (recommended)
},
"cortex": { // Cortex LLMHoster (OpenAI-compatible), see docs/CORTEX.md
"enabled": false,
"base_url": "http://127.0.0.1:8624",
"api_path": "/v1",
"model": "", // blank = Cortex's default model
"api_key_env": "CORTEX_API_KEY", // env var NAME; the key itself is never stored
"temperature": 0.7,
"max_tokens": 320,
"json_mode": true,
"decision_interval": 6.0,
"request_timeout": 120.0,
"system_prompt": ""
},
"training": {
"models_dir": "models", // where checkpoints + model.json cards live
"dataset": "pymcbot/dataset.jsonl", // written by the mod's /pymcbot export
"active_run": "", // "" = newest trained run
"engine": "mlp", // "mlp" (NumPy) | "transformer" (needs torch)
"steps": 1000, "batch_size": 64, "lr": 0.003, "checkpoint_every": 200,
"hidden": [128, 64], // MLP layers
"include_blocks": true, // feed the 3x3x3 block neighbourhood
"advanced": false, // advanced training: learn entity + item vocabularies
"entity_slots": 24, "item_slots": 32, // how many entity/item words the model learns
"val_split": 0.1,
"temperature": 0.2, // 0 = deterministic, >0 samples among similar actions
"step_seconds": 0.4, // how long one predicted action is held
"auto_retrain_samples": 0 // reserved for automatic retraining
},
"agent": {
"mode": "auto", // "auto" | "heuristic" | "ollama" | "cortex" | "trained"
"allow_movement": true, "allow_chat": true, "allow_mining": true,
"allow_attacking": false, // keep this off unless you mean it
"greet_players": true, "max_chat_length": 200,
"persona": "friendly", // friendly | chill | social | companion | builder | explorer | miner | newbie | pro | quiet
"persona_notes": "", // extra character notes for the LLM (max 600 chars)
"human_chat": true, // typing pauses, no repeats, cooldown for unprompted chat
"chat_cooldown": 6.0, // seconds between messages nobody asked for
"memory_turns": 8, // recent actions the LLM remembers
"wander_radius": 16, "action_timeout": 30.0
},
"fleet": {
"enabled": true,
"max_bots": 25, // cap for main bot + fleet
"count": 5, // default for "Add players"
"name_pattern": "PyMC_Bot_{n}",
"auth": "offline", // "offline" | "microsoft"
"ai_mode": "heuristic", // "off" | "heuristic" | "ollama" | "cortex" | "trained"
"stagger_seconds": 1.5, // gap between joins
"chatter": false, "chatter_interval": 45.0,
"chatter_lines": ["hey everyone!", "..."],
"restore_on_start": false, // bring the fleet back when the panel restarts
"roster": [] // written automatically: who should come back
},
"server": { "host": "0.0.0.0", "port": 8000 }
}
Permissions gate the AI's own choices; buttons you press in the panel still work (except attack, which stays gated). Invalid values are rejected with HTTP 422 and never overwrite a good config.
The panel is a thin client over a small JSON API – script it from anything:
# status: bot, AI, stats, backends, Ollama health
curl -s localhost:8000/api/status | jq '.bot.state, .agent.running'
# fill the server with 10 offline players, 1.5s apart
curl -sX POST localhost:8000/api/fleet/populate -H 'content-type: application/json' -d '{"count":10}'
# add one premium player (Microsoft account email)
curl -sX POST localhost:8000/api/fleet/spawn -H 'content-type: application/json' \
-d '{"username":"you@example.com","auth":"microsoft","ai":"heuristic"}'
# who is online, and make them all say something
curl -s localhost:8000/api/fleet/status | jq '.connected, [.bots[].username]'
curl -sX POST localhost:8000/api/fleet/broadcast -H 'content-type: application/json' -d '{"message":"hi!"}'
# switch to the simulated world and connect
curl -sX PUT localhost:8000/api/config -H 'content-type: application/json' \
-d '{"minecraft":{"backend":"simulated"}}'
curl -sX POST localhost:8000/api/bot/start
# drive it by hand
curl -sX POST localhost:8000/api/bot/action -H 'content-type: application/json' -d '{"action":"mine","block":"stone"}'
curl -sX POST localhost:8000/api/bot/chat -H 'content-type: application/json' -d '{"message":"hi everyone"}'
curl -sX POST localhost:8000/api/ai/start
WS /ws pushes event frames (the same coloured log you see in the panel) and a status frame at least every 1.5s. Full endpoint list: docs/API.md. The Python↔Node bridge contract is documented in docs/BRIDGE.md, the playtime-recording mod in mod/README.md, and training plus running the learned policy in docs/TRAINING.md.
pip install -r requirements-dev.txt
pytest -q # 435 tests, no Minecraft server, no LLM required (2 skip without torch)
ruff check . # lint
pytest --cov=pymc_bot -q # coverage
# opt-in end-to-end test against a real (offline-mode) server
PYMC_TEST_SERVER=127.0.0.1:25565 pytest tests/test_node_live.py -v
The suite runs the bot against the simulated world, exercises the whole FastAPI surface (including the WebSocket), tests the Node adapter against a fake bridge process (tests/fixtures/fake_bridge.js) and runs the AI loop against the Ollama stub. The training stack is covered end to end as well: the encoder layout is pinned in tests/test_features.py, tests/test_train.py covers checkpoints/resume/metrics, and tests/test_local_model.py lets a real trained model drive a simulated bot. CI (.github/workflows/ci.yml) runs lint + tests on Python 3.10–3.12, the transformer engine with PyTorch, and builds the Fabric mod for all 16 Minecraft versions from 1.21.1 to 26.3 on JDK 25 (one matrix job per version from mod/versions.json; 1.21.x jars target Java 21, 26.x jars Java 25), **launches the game with the mod on every version from 1.21.4 on** (Fabric's client game test in a virtual display: it records a few seconds of scripted play, checks the dataset and uploads screenshots) and uploads each jar plus a pymcbot-all-versions bundle. When everything is green, the release job attaches all 16 jars to the GitHub release pymcbot-v<mod_version>.
pymc_bot/
├── __main__.py CLI (serve / run / train / models / action / doctor / stub)
├── config.py pydantic settings + atomic JSON persistence
├── backends.py simulated world + Node/mineflayer adapter
├── bot.py player controller: steering, pathfinding, mining, chat, stats
├── fleet.py one or many players: spawn/populate, premium accounts, roster, chatter
├── antiafk.py anti-AFK keeper: human-like walking, looking, jumping, crouching, swinging
├── agent.py decision loop, action validation, permissions, heuristics
├── features.py observation -> 215-float feature vector (mod & live bot share it)
├── playtime.py reads the mod's NDJSON, exports datasets, synthesises demo playtime
├── models.py numpy MLP (default engine) + optional tiny transformer
├── train.py self-checkpointing trainer, metrics, resumable state, service
├── local_model.py runs a checkpoint: predictions -> real low-level bot controls
├── ollama.py tiny Ollama HTTP client
├── cortex.py Cortex LLMHoster client (OpenAI-compatible, bearer key from the environment)
├── ollama_stub.py Ollama/OpenAI-compatible demo stub (not an LLM)
├── server.py FastAPI app + WebSocket + static UI
├── web/ index.html, styles.css, app.js (no build step)
└── node/ minecraft_bridge.js (the only JavaScript)
mod/ Fabric mod (MC 1.21.1 – 26.3): records your playtime into a training dataset
tests/ pytest suite + fake bridge fixture
scripts/dev.sh small helper for common tasks
updater.py self-updater for the bot, its dependencies and the mod (+ updater.cmd / updater.sh)
| Symptom | Fix | | --- | --- | | mineflayer is not installed | run npm install in the project root (Node ≥ 22) | | Bot connects then immediately leaves | check the event stream: wrong version? try an explicit version instead of auto | | unsupported protocol version | set the app's version to the server's (e.g. 1.20.4) | | No data available for version 26.3 / "server runs Minecraft 26.3, but … only supports up to 26.1" | the server is newer than the installed mineflayer (it currently stops at 26.1; 26.2/26.3 support is still being added upstream). The bot now joins as the newest supported version automatically, so either install ViaVersion + ViaBackwards on the server, or run the server on 26.1. Once mineflayer releases support, run the updater (or npm update) and set Version back to auto | | Kicked with Outdated client | same cause: the server only accepts its own version – add ViaVersion + ViaBackwards to it | | "… (Aternos) is offline" / "is starting" | the free host's server is asleep: start it from the dashboard, or set Server asleep? to wait for it to start | | "refused the offline-mode login" | the server is in online mode: turn on Cracked (Aternos: Options) / online-mode=false, or use a Microsoft account | | Bots left with "No real player has been online" | the leave-when-alone rule (always on for Aternos, whose rules forbid idling bots); press Connect when people are on | | Connection error ECONNREFUSED | nothing listens at that host/port – start the Minecraft server or fix the address | | Microsoft login hangs | the device code is printed in the event stream – complete it in a browser | | does the account own minecraft? | that Microsoft account has no Java Edition – use one that owns the game | | Premium bot joins again after a restart with no prompt | that is the per-account token cache in .pymc_profiles/ working as intended | | fleet is full | raise fleet.max_bots or stop/remove players first | | Bots get kicked while joining in bulk | increase Gap between joins (2–3s) or ask the server to raise its throttling | | Still kicked for AFK | lower antiafk.interval_min/interval_max (some servers use 60 s windows), keep walk and swing enabled, or turn on verbose to confirm bursts happen | | Bots wander off while you are not looking | set a smaller antiafk.max_walk_distance, or disable walk (look/swing/jump already defeat most AFK plugins) | | Panel shows demo brain (stub) | you are pointing Ollama at python -m pymc_bot stub, not a real model | | Cortex section says not running (optional…) | nothing to fix: Cortex is only needed when the brain is set to Cortex | | Panel pill says cortex offline | hover it for the reason; usually CORTEX_API_KEY not exported where PyMC_Bot runs, or the Cortex model is not started – see docs/CORTEX.md | | Bot stands still | AI off (press Start AI), or movement permission disabled, or the server is unreachable | | no playtime samples found | record with the mod first (/pymcbot record start, /pymcbot export), or try python -m pymc_bot train --simulate 5 | | advanced training looks like basic training | train --inspect shows an empty vocabulary: the recording was made before /pymcbot advanced on, so there is nothing extra to learn. Record again with it on. | | Trained model only walks forward | play/train more, or lower training.temperature; check val_balanced_accuracy in the model card | | the transformer engine needs PyTorch | pip install -r requirements-train.txt, or use the default --engine mlp | | Bot ignores the trained model | set agent.mode to trained and activate a run (training.active_run, or the panel's checkpoint table) | | ./gradlew build fails in mod/ | needs JDK 25 to run Gradle (Loom 1.18 rejects older runtimes with "requires at least JVM runtime version 25"); pick a version with -Pmc=<version> (./gradlew printTargets lists them). See the build troubleshooting table; CI builds every version on each push, so compare with the mod (fabric <version>) job there | | updater: you have local changes | commit/stash them, or run updater --autostash | | updater: is in use - close Minecraft | Windows locks loaded jars - quit the game, run updater --mod-only again | | updater: rate limit | wait an hour, or set GITHUB_TOKEN to any GitHub token | | Web panel unreachable from another machine | server.host must stay 0.0.0.0 and the port must be open |
| | | | --- | --- | | Author | codero-sus | | Website | the-codero.web.app | | Source | github.com/codero-sus/PyMC_Bot | | Also by codero-sus | Cortex LLMHoster – the optional self-hosted LLM brain | | Built with | FastAPI + Uvicorn, mineflayer, Fabric, Ollama, NumPy / PyTorch |
The same credits are in the panel (footer and ⓘ About), at GET /api/about, in python -m pymc_bot about, in the OpenAPI docs (/docs) and in the Fabric mod (/pymcbot about, fabric.mod.json).
© 2026 codero-sus. Licensed under the PolyForm Noncommercial License 1.0.0 – see LICENSE.
Free for personal, non-commercial use. You may use, change and share PyMC_Bot and the PyMC Bot mod for yourself, as a hobby, for study or research, or in a charity, school or public organisation. You may not sell it, put it behind a paywall, or use it to make money (for example selling bots or "player filling" as a service) without written permission from codero-sus. Anyone you share it with must get the licence text (or its URL) and the Required Notice: line at the top of LICENSE.
Versions published before this change (up to commit 271a602 and the mod releases up to 0.7.0 built from it) were released under the MIT license; copies already obtained under MIT keep those terms.
Third-party parts keep their own licenses: the compile-only Mod Menu API stubs in mod/src/client/java/com/terraformersmc/ (MIT, TerraformersMC; never shipped in the jars), the Fabric template files the mod was bootstrapped from (CC0) and all npm/PyPI dependencies.
<p align="center"><sub>Made with ❤️ by <a href="https://github.com/codero-sus">codero-sus</a> · <a href="https://the-codero.web.app/">the-codero.web.app</a></sub></p>
A web game almost similar to Minecraft and bloxd.io.
A fun game made with perfection — with plans for a 3D Asphalt-style arcade racer and a scored engine comparison.
A collection of various games, feel free to play — now with its own home at gamez.web.app.