ead48cd399
- Document sudo history context (last 10 actions) - Document whitelist scope and syntax auto-repair - Clarify sudo must be typed in chat without slash - Add additional sudo usage example
354 lines
12 KiB
Markdown
354 lines
12 KiB
Markdown
# Minecraft AI God
|
|
|
|
A Python-based "plugin" for vanilla Minecraft servers that integrates a locally-hosted LLM as an in-game God character. Players interact by typing `pray <message>` in chat. God responds with dramatic prose and optionally executes server commands via RCON.
|
|
|
|
No mods, no plugins, no server restarts required. Works with any vanilla Minecraft 1.21+ server.
|
|
|
|
---
|
|
|
|
## How It Works
|
|
|
|
```
|
|
Player types: pray <message>
|
|
│
|
|
▼
|
|
latest.log ←─ tailed by mc_aigod.py
|
|
│
|
|
▼
|
|
RCON: immediate acknowledgment to player ("The heavens stir...")
|
|
│
|
|
▼
|
|
RCON: fetch live server context
|
|
- Online players, time of day, weather, world border
|
|
│
|
|
▼
|
|
RCON: fetch praying player's state
|
|
- Full inventory (with rarity annotations), position, health, food, XP, deaths
|
|
│
|
|
▼
|
|
Call 1 — command_model (qwen3-coder:30b or similar)
|
|
- Decides what server commands to execute (JSON only, no prose)
|
|
- Low temperature (0.3) for precise structured output
|
|
│
|
|
▼
|
|
Call 2 — model (gemma3:12b or similar)
|
|
- Writes God's spoken message knowing what was decided
|
|
- No token competition with commands — full creative freedom
|
|
│
|
|
▼
|
|
RCON: execute commands + broadcast message
|
|
```
|
|
|
|
**Divine Intervention Timer** — a background thread fires at random intervals (Poisson process, user-defined average per day). If players are online, God acts unprompted. LLM can choose silence (`commands: []`) and nothing happens.
|
|
|
|
**Memory** — last 10 prayer exchanges stored as conversation history and passed to every LLM call. Persists across service restarts via JSON file. God remembers.
|
|
|
|
**Server log context** — last 20 minutes of meaningful server events (chat, deaths, joins, leaves) included with every prayer. God knows what's been happening.
|
|
|
|
---
|
|
|
|
## Requirements
|
|
|
|
- Python 3.11+
|
|
- `requests` library (`apt install python3-requests`)
|
|
- Ollama instance with at least one model pulled
|
|
- Minecraft vanilla server with RCON enabled
|
|
- Server running on Linux (systemd for service management)
|
|
|
|
### Minecraft server.properties requirements
|
|
|
|
```properties
|
|
enable-rcon=true
|
|
rcon.port=25575
|
|
rcon.password=yourpassword
|
|
broadcast-rcon-to-ops=false
|
|
```
|
|
|
|
---
|
|
|
|
## File Structure
|
|
|
|
```
|
|
mc_aigod.py # Main script — deploy to /usr/local/bin/
|
|
mc_aigod_shrink.json # Example config — deploy to /etc/mc_aigod.json
|
|
mc-aigod.service # Systemd unit — deploy to /etc/systemd/system/
|
|
Minecraft_Ai_God.md # Full design document with architecture details
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
`/etc/mc_aigod.json`:
|
|
|
|
| Key | Type | Default | Description |
|
|
|---|---|---|---|
|
|
| `server_name` | string | required | Server name passed to God's persona |
|
|
| `log_path` | string | required | Absolute path to `logs/latest.log` |
|
|
| `rcon_host` | string | `"127.0.0.1"` | RCON host |
|
|
| `rcon_port` | int | `25575` | RCON port |
|
|
| `rcon_password` | string | required | RCON password |
|
|
| `ollama_url` | string | required | Ollama base URL e.g. `http://192.168.0.1:11434` |
|
|
| `model` | string | required | Message model — creative writing (e.g. `gemma3:12b`) |
|
|
| `command_model` | string | falls back to `model` | Commands model — structured JSON (e.g. `qwen3-coder:30b`) |
|
|
| `temperature` | float | `0.85` | Message model temperature |
|
|
| `max_tokens` | int | `600` | Max tokens for message call |
|
|
| `cooldown_seconds` | int | `20` | Per-player prayer cooldown |
|
|
| `max_commands_per_response` | int | `6` | Max commands God can issue per prayer |
|
|
| `interventions_per_day` | float | `4` | Avg unprompted interventions per 24h. `0` to disable |
|
|
| `debug_commands` | bool | `false` | Show executed commands in-game via dark gray tellraw |
|
|
| `sudo_enabled` | bool | `true` | Enable sudo translator mode |
|
|
| `sudo_user` | string | `"slingshooter08"` | Only this username can execute sudo commands |
|
|
| `sudo_max_commands` | int | `3` | Max translated commands per sudo request |
|
|
| `memory_path` | string | see below | Path to persist prayer memory JSON |
|
|
| `god_chat_prefix` | string | `"[GOD]"` | Chat prefix (supports Minecraft color codes) |
|
|
|
|
Default memory path: `<instance_data_dir>/aigod_memory.json`
|
|
|
|
### Example config
|
|
|
|
```json
|
|
{
|
|
"server_name": "my-server",
|
|
"log_path": "/path/to/minecraft/logs/latest.log",
|
|
"rcon_host": "127.0.0.1",
|
|
"rcon_port": 25575,
|
|
"rcon_password": "yourpassword",
|
|
"ollama_url": "http://localhost:11434",
|
|
"model": "gemma3:12b",
|
|
"command_model": "qwen3-coder:30b",
|
|
"temperature": 0.85,
|
|
"max_tokens": 600,
|
|
"cooldown_seconds": 20,
|
|
"max_commands_per_response": 6,
|
|
"interventions_per_day": 4,
|
|
"debug_commands": false,
|
|
"sudo_enabled": true,
|
|
"sudo_user": "slingshooter08",
|
|
"sudo_max_commands": 3,
|
|
"memory_path": "/path/to/minecraft/aigod_memory.json",
|
|
"god_chat_prefix": "[§6§lGOD§r]"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Player Usage
|
|
|
|
Type in chat (no slash — vanilla 1.21 rejects unknown slash commands client-side):
|
|
|
|
```
|
|
pray <message> — send a prayer to God
|
|
bible — show help/guidance
|
|
sudo <request> — command translator mode (authorized user only)
|
|
```
|
|
|
|
### Sudo Translator Mode
|
|
|
|
`sudo` is a separate agent path bundled in the same script. It does not use God's persona or speech pipeline.
|
|
|
|
- Trigger: `sudo <natural language request>`
|
|
- Authorization: only `sudo_user` (default `slingshooter08`)
|
|
- Model: uses `command_model`
|
|
- Output: JSON commands only, then executes via same whitelist validator
|
|
- No divine speech generated
|
|
- Uses rolling sudo memory: last 10 sudo actions (request + translated + executed commands)
|
|
- Uses strict command-family whitelist (`give`, `effect`, `xp`, `tp`, `time`, `weather`, `execute`, `kill`, `summon`, `tellraw`, `worldborder`)
|
|
- Includes syntax repair for common model mistakes (e.g., `give player 64 item`, missing `minecraft:`)
|
|
- Must be typed in chat as `sudo ...` (no slash)
|
|
|
|
Example:
|
|
```
|
|
sudo give me 500 wood
|
|
```
|
|
Best-effort translation:
|
|
```
|
|
give slingshooter08 minecraft:oak_log 500
|
|
```
|
|
|
|
Another example:
|
|
```
|
|
sudo give me 10 doors
|
|
```
|
|
Best-effort translation (can be multiple commands):
|
|
```
|
|
give slingshooter08 minecraft:oak_door 10
|
|
```
|
|
|
|
On login players see:
|
|
```
|
|
[GOD] GOD ENABLED — Type "bible" in chat for guidance. Type "pray <message>" to pray.
|
|
```
|
|
|
|
---
|
|
|
|
## God's Capabilities
|
|
|
|
### Commands God can issue
|
|
|
|
**Give any item:**
|
|
```
|
|
give <player> minecraft:<item_id> <count>
|
|
give <player> minecraft:<item_id>[enchantments={sharpness:4,unbreaking:3}] 1
|
|
xp add <player> <amount> levels
|
|
```
|
|
|
|
**Effects (positive/negative):** regeneration, strength, speed, night_vision, fire_resistance, water_breathing, instant_health, blindness, slowness, weakness, hunger, nausea, levitation, effect clear
|
|
|
|
**Movement:** `tp <player> <x> <y> <z>`
|
|
|
|
**World:** `time set day/night`, `weather clear/thunder/rain <duration>`
|
|
|
|
**Punishment:** `execute at <player> run summon minecraft:lightning_bolt ~ ~ ~`, `kill <player>`
|
|
|
|
**Mobs:** `execute at <player> run summon minecraft:creeper ~ ~ ~3`
|
|
|
|
### Item naming rules (Minecraft 1.21)
|
|
|
|
- Always use `minecraft:` namespace prefix
|
|
- Beds: `white_bed`, `red_bed` etc — there is no `minecraft:bed`
|
|
- Logs: `oak_log`, `spruce_log` etc — there is no `minecraft:log`
|
|
- Wool: `white_wool`, `red_wool` etc — there is no `minecraft:wool`
|
|
- Enchantments use component syntax: `item[enchantments={sharpness:5,unbreaking:3}]`
|
|
- Give syntax: `give <player> minecraft:<item> <count>` — count is LAST
|
|
|
|
### God's persona
|
|
|
|
- Benevolent but just, theatrical and dramatic (Old Testament style)
|
|
- Aware of player inventory, health, food, position, deaths
|
|
- Aware of server state: time, weather, world border, online players
|
|
- Aware of recent server events: deaths, chat, joins, leaves (last 20 min)
|
|
- Remembers last 10 prayer exchanges across all players
|
|
- Acts on own accord via intervention timer — may choose silence
|
|
- Not obligated to grant requests — may reward someone else, punish the requester, or do something unexpected
|
|
|
|
---
|
|
|
|
## Model Recommendations
|
|
|
|
**Command model** — needs reliable structured JSON output and Minecraft syntax knowledge:
|
|
- `qwen3-coder:30b` (recommended, ~19GB Q4)
|
|
- `qwen2.5:1.5b` (fast/small, acceptable for commands)
|
|
|
|
**Message model** — needs creative writing, roleplay, dramatic biblical prose:
|
|
- `gemma3:12b` (recommended, ~8GB)
|
|
- `llama3.1:8b` (good alternative)
|
|
|
|
Avoid coding models for the message role. Avoid general models for the command role.
|
|
|
|
Both calls go to the same Ollama instance. If both models fit in VRAM simultaneously there is no swap overhead. If not, Ollama swaps them — adds a few seconds per prayer.
|
|
|
|
---
|
|
|
|
## Deployment
|
|
|
|
```bash
|
|
# Install dependencies
|
|
apt install python3-requests
|
|
|
|
# Deploy files
|
|
cp mc_aigod.py /usr/local/bin/mc_aigod.py
|
|
chmod +x /usr/local/bin/mc_aigod.py
|
|
cp mc_aigod_shrink.json /etc/mc_aigod.json # edit as needed
|
|
cp mc-aigod.service /etc/systemd/system/mc-aigod.service
|
|
|
|
# Enable and start
|
|
systemctl daemon-reload
|
|
systemctl enable --now mc-aigod.service
|
|
|
|
# Monitor
|
|
journalctl -fu mc-aigod.service
|
|
tail -f /var/log/mc_aigod.log
|
|
tail -f /var/log/mc_aigod_responses.log # full untruncated LLM responses
|
|
```
|
|
|
|
---
|
|
|
|
## Debugging
|
|
|
|
**`debug_commands: true` in config** — shows executed commands in-game as dark gray italic text:
|
|
```
|
|
[~] give slingshooter08 minecraft:spruce_log 64 | weather thunder 6000
|
|
```
|
|
Never appears in `latest.log`. Toggle off by setting `false` and restarting.
|
|
|
|
**Log files:**
|
|
- `/var/log/mc_aigod.log` — startup, prayers received, RCON results, errors
|
|
- `/var/log/mc_aigod_responses.log` — full untruncated LLM responses with commands and messages
|
|
|
|
**Common issues:**
|
|
|
|
| Symptom | Cause | Fix |
|
|
|---|---|---|
|
|
| `Unknown item 'minecraft:64'` | LLM put count before item | Auto-fixed by `fix_give_command()`, also update command model |
|
|
| `Unknown item 'minecraft:bed'` | Missing colour prefix | Item library includes warning; auto-namespaced |
|
|
| Message truncated | Token limit hit | Increase `max_tokens`; two-call split helps |
|
|
| `commands: []` but message says it will give something | LLM treating message as action | CRITICAL rule in prompt: commands array is the only way things happen |
|
|
| Prayer not detected | Typed as `/pray` (slash command) | Must type `pray` in chat without slash |
|
|
|
|
---
|
|
|
|
## Architecture Notes
|
|
|
|
### Why no Minecraft plugin?
|
|
|
|
No Java plugin required. The script tails `latest.log` for chat lines matching `pray ` and `bible`, then acts via RCON. This means:
|
|
- Works with any vanilla server version that has RCON
|
|
- No server restart required to install or update
|
|
- Script restarts independently of the server
|
|
|
|
### Log detection patterns
|
|
|
|
```python
|
|
# Chat messages in vanilla 1.21:
|
|
# [HH:MM:SS] [Server thread/INFO]: <playername> pray message here
|
|
# [HH:MM:SS] [Server thread/INFO]: <playername> bible
|
|
|
|
PRAY_PATTERN = re.compile(r'\[.*?\]: <(\w+)> [Pp]ray (.+)')
|
|
BIBLE_PATTERN = re.compile(r'\[.*?\]: <(\w+)> [Bb]ible\s*$')
|
|
JOIN_PATTERN = re.compile(r'\[.*?\]: (\w+) joined the game')
|
|
```
|
|
|
|
Note: `/pray` as a slash command does NOT work — vanilla 1.21 rejects unknown commands client-side before they reach the server log.
|
|
|
|
### Two-call LLM architecture
|
|
|
|
```
|
|
Prayer received
|
|
│
|
|
├─► Command call (command_model, temp=0.3, max_tokens=200, format=json)
|
|
│ System: terse spec, command palette, item rules
|
|
│ Returns: {"commands": [...]}
|
|
│
|
|
└─► Message call (model, temp=0.85, max_tokens=600, no format constraint)
|
|
System: God persona only
|
|
User: prayer + context + "You decided to execute: [commands]"
|
|
Returns: plain prose, any length
|
|
```
|
|
|
|
Separating the calls means:
|
|
- Commands are never truncated by a long message
|
|
- Message has full token budget for dramatic prose
|
|
- Each model does what it's best at
|
|
|
|
### Prayer memory format
|
|
|
|
Stored as a list of `[player, prayer, god_message]` tuples in JSON. Loaded at startup, appended after every successful prayer, capped at 10 entries. Injected into the message call as alternating `user`/`assistant` messages so the LLM sees genuine conversation history.
|
|
|
|
### Divine intervention timing
|
|
|
|
Uses exponential distribution (`random.expovariate`) — the correct model for Poisson arrivals. `interventions_per_day=4` means an average gap of 6 hours but intervals are random and memoryless. Could be 3 in one hour, then nothing for 18 hours.
|
|
|
|
---
|
|
|
|
## Sethpc Infrastructure Context
|
|
|
|
This was developed and deployed on:
|
|
- MCSManager on CT 644 (Proxmox, Debian 12, node-112)
|
|
- Minecraft shrink-world server: port 25566, RCON 25576
|
|
- Ollama on steel141 (192.168.0.141:11434)
|
|
- Models: `gemma3:12b` (messages), `qwen3-coder:30b` (commands)
|
|
- Service: `mc-aigod.service` on CT 644
|
|
- Config: `/etc/mc_aigod.json`
|
|
- Script: `/usr/local/bin/mc_aigod.py`
|