Qwen3.5-9B on prod, Gemini 2.5 Flash for dev, error correction, branding

Prod deployment:
- paper-ai and shrink-world switched from gemma3n:e4b to qwen3.5:9b
- Error correction: detects RCON errors (<--[HERE]), asks model to fix, retries
- Broadened error patterns: Unknown game mode, Unknown enchantment, etc.
- Fixed fire fallback matching "firework" as fire intent
- Fixed command format examples (WRONG vs RIGHT in prompt)
- max_tokens bumped to 600 for command calls
- Removed template workflow commands from sudo prompt

Dev server:
- Gemini 2.5 Flash ($0.15/$0.60 per M tokens) replaces Flash Lite
- 10 bots for ~$1-1.5/hr training data generation
- Dynamic pricing by model name in cost tracker

Branding:
- Rajdhani Bold as official Mortdecai font
- Logo variants: mortdecai + mortdec.ai in 6 fonts
- Whitelist page updated with Mortdecai branding + mortdec.ai domain

Whitelist UUID fix:
- Looks up real Mojang UUID via api.mojang.com
- Patches all whitelist.json files directly
- No more offline-mode UUID mismatches

WorldEdit schematics:
- 77 schematics installed (villages, bridges, lighthouses, parks, etc.)

Mortdecai v4 training in progress: 63% complete on steel141

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 23:09:27 -04:00
parent b75a737c11
commit 7a31e500e4
23 changed files with 2299 additions and 6 deletions
+60 -6
View File
@@ -25,7 +25,7 @@ SERVERS = [
WHITELIST_LOG = "/var/log/mc_whitelist.log"
LOGO_URL = "https://storage.googleapis.com/sethfreiberg.com/sethflix/favicon.png"
LOGO_URL = "https://git.sethpc.xyz/Seth/Mortdecai/raw/branch/master/branding/mortdec_ai_rajdhani.png"
def rcon_command(cmd, host, port, password):
@@ -54,11 +54,65 @@ def rcon_command(cmd, host, port, password):
return f"ERROR: {e}"
def lookup_mojang_uuid(username):
"""Look up a player's real Mojang UUID from the API."""
try:
import urllib.request
url = f"https://api.mojang.com/users/profiles/minecraft/{username}"
req = urllib.request.Request(url, headers={"User-Agent": "MortdecaiWhitelist/1.0"})
resp = urllib.request.urlopen(req, timeout=5)
data = json.loads(resp.read())
raw = data.get("id", "")
if len(raw) == 32:
# Format as UUID with dashes
return f"{raw[:8]}-{raw[8:12]}-{raw[12:16]}-{raw[16:20]}-{raw[20:]}"
return raw
except Exception:
return None
def whitelist_player(username):
"""Whitelist player on all servers, fixing UUIDs to real Mojang UUIDs."""
results = {}
# Get real UUID from Mojang
uuid = lookup_mojang_uuid(username)
for srv in SERVERS:
# RCON whitelist add (creates entry, possibly with wrong offline UUID)
result = rcon_command(f"whitelist add {username}", srv["host"], srv["rcon_port"], srv["rcon_pass"])
results[srv["name"]] = result.strip()
# Fix UUIDs in whitelist.json files directly
if uuid:
WHITELIST_FILES = [
"/opt/paper-ai-25567/whitelist.json",
"/opt/mcsmanager/daemon/data/InstanceData/shrinkborder1234567890abcdef12345/whitelist.json",
"/opt/mcsmanager/daemon/data/InstanceData/d39f55861cb34204a92a18a9e1c78ca6/whitelist.json",
]
for wl_path in WHITELIST_FILES:
try:
with open(wl_path) as f:
wl = json.load(f)
changed = False
for entry in wl:
if entry["name"].lower() == username.lower() and entry["uuid"] != uuid:
entry["uuid"] = uuid
entry["name"] = username
changed = True
if changed:
with open(wl_path, "w") as f:
json.dump(wl, f, indent=2)
except Exception:
pass
# Reload whitelists
for srv in SERVERS:
try:
rcon_command("whitelist reload", srv["host"], srv["rcon_port"], srv["rcon_pass"])
except Exception:
pass
return results
@@ -71,7 +125,7 @@ PAGE = """<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Minecraft AI Server — sethpc.xyz</title>
<title>Mortdecai — Minecraft AI</title>
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
@@ -80,8 +134,8 @@ PAGE = """<!DOCTYPE html>
a:hover {{ color: #e65c00; }}
.header {{ background: #000; padding: 1rem; text-align: center; border-bottom: 2px solid #D35400; }}
.header img {{ width: 40px; vertical-align: middle; border: none; }}
.header span {{ font-size: 1.2rem; font-weight: 700; color: #D35400; margin-left: 0.5rem; vertical-align: middle; }}
.header img {{ height: 40px; vertical-align: middle; border: none; }}
.header span {{ font-size: 1.2rem; font-weight: 700; color: #D35400; margin-left: 0.5rem; vertical-align: middle; display: none; }}
.container {{ max-width: 600px; margin: 0 auto; padding: 1.5rem; }}
@@ -137,12 +191,12 @@ PAGE = """<!DOCTYPE html>
<div class="header">
<img src="{logo}" alt="logo" class="noborder">
<span>sethpc.xyz</span>
<span>mortdec.ai</span>
</div>
<div class="container">
<div class="hero">
<h1>Minecraft AI Server</h1>
<h1>Mortdecai</h1>
<p>An AI runs on this server that listens to in-game chat and does things in the world based on what you say.</p>
</div>