5b28002001
Major changes from this session: Training: - 0.6.0 training running: 9B on steel141 3090 Ti, 27B on rented H100 NVL - 7,256 merged training examples (up from 3,183) - New training data: failure modes (85), midloop messaging (27), prompt injection defense (29), personality (32), gold from quarantine bank (232), new tool examples (30), claude's own experience (10) - All training data RCON-validated at 100% pass rate - Bake-off: gemma3:27b 66%, qwen3.5:27b 61%, translategemma:27b 56% Oracle Bot (Mind's Eye): - Invisible spectator bot (mineflayer) streams world state via WebSocket - HTML5 Canvas frontend at mind.mortdec.ai - Real-time tool trace visualization with expandable entries - Streaming model tokens during inference - Gateway integration: fire-and-forget POST /trace on every tool call Reinforcement Learning: - Gymnasium environment wrapping mineflayer bot (minecraft_env.py) - PPO training via Stable Baselines3 (10K param policy network) - Behavioral cloning pretraining (97.5% accuracy on expert policy) - Infinite training loop with auto-restart and checkpoint resume - Bot learns combat, survival, navigation from raw experience Bot Army: - 8-soldier marching formation with autonomous combat - Combat bots using mineflayer-pvp, pathfinder, armor-manager - Multilingual prayer bots via translategemma:27b (18 languages) - Frame-based AI architecture: LLM planner + reactive micro-scripts Infrastructure: - Fixed mattpc.sethpc.xyz billing gateway (API key + player list parser) - Billing gateway now tracks all LAN traffic (LAN auto-auth) - Gateway fallback for empty god-mode responses - Updated mortdec.ai landing page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
232 lines
8.2 KiB
JavaScript
232 lines
8.2 KiB
JavaScript
/**
|
|
* march_army.js — Bots march in rank-and-file formation around a circular path.
|
|
*
|
|
* The gateway (RCON) acts as guardian angel — paving the road,
|
|
* healing, buffing, clearing threats, and putting on a show.
|
|
*/
|
|
|
|
const mineflayer = require('mineflayer');
|
|
const pvp = require('mineflayer-pvp').plugin;
|
|
const http = require('http');
|
|
|
|
const host = process.argv[2] || '192.168.0.244';
|
|
const port = parseInt(process.argv[3] || '25568', 10);
|
|
const COUNT = parseInt(process.argv[4] || '8', 10);
|
|
|
|
const NAMES = [
|
|
'Guard_I', 'Guard_II', 'Guard_III', 'Guard_IV',
|
|
'Guard_V', 'Guard_VI', 'Guard_VII', 'Guard_VIII',
|
|
];
|
|
|
|
const HOSTILE = new Set(['zombie', 'husk', 'skeleton', 'creeper', 'spider', 'drowned', 'parched', 'phantom', 'witch']);
|
|
|
|
const bots = [];
|
|
let marchAngle = 0;
|
|
const RADIUS = 25;
|
|
const MARCH_SPEED = 0.02; // radians per tick — slow stately march
|
|
const FORMATION_SPACING = Math.PI / (COUNT * 1.2); // spacing between soldiers
|
|
|
|
// ── RCON helper (gateway control) ──
|
|
function rcon(cmd) {
|
|
return new Promise((resolve) => {
|
|
const body = JSON.stringify({ command: cmd });
|
|
const req = http.request({
|
|
hostname: host, port: 25578, path: '/api/chat',
|
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
}, () => resolve());
|
|
req.on('error', () => resolve());
|
|
// Actually just use python for reliability
|
|
resolve();
|
|
});
|
|
}
|
|
|
|
function rconExec(cmd) {
|
|
const { execSync } = require('child_process');
|
|
try {
|
|
execSync(`python3 -c "from mcrcon import MCRcon; m=MCRcon('${host}','REDACTED_RCON',port=25578); m.connect(); m.command('${cmd.replace(/'/g, "\\'")}'); m.disconnect()"`, { timeout: 3000, stdio: 'pipe' });
|
|
} catch (e) {}
|
|
}
|
|
|
|
// ── Spawn soldiers ──
|
|
function spawnSoldier(name, index) {
|
|
return new Promise((resolve) => {
|
|
console.log(`[${name}] Deploying...`);
|
|
const bot = mineflayer.createBot({
|
|
host, port, username: name, auth: 'offline', version: '1.21.11',
|
|
});
|
|
bot.loadPlugin(pvp);
|
|
|
|
const soldier = { bot, name, index, alive: false, kills: 0 };
|
|
|
|
bot.once('spawn', () => {
|
|
soldier.alive = true;
|
|
console.log(`[${name}] Ready`);
|
|
resolve(soldier);
|
|
});
|
|
|
|
bot.on('death', () => {
|
|
soldier.alive = false;
|
|
setTimeout(() => { try { bot.respawn(); } catch (e) {} }, 2000);
|
|
});
|
|
|
|
bot.on('spawn', () => { soldier.alive = true; });
|
|
|
|
bot.on('entityHurt', (e) => {
|
|
if (e !== bot.entity) return;
|
|
// Counter-attack
|
|
for (const ent of Object.values(bot.entities)) {
|
|
if (ent === bot.entity || ent.type === 'player') continue;
|
|
if (HOSTILE.has((ent.name || '').toLowerCase()) && ent.position.distanceTo(bot.entity.position) < 6) {
|
|
bot.pvp.attack(ent);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
bot.on('entityDead', (e) => {
|
|
if (HOSTILE.has((e.name || '').toLowerCase())) soldier.kills++;
|
|
});
|
|
|
|
bot.on('kicked', () => {
|
|
console.log(`[${name}] Kicked, retrying in 10s...`);
|
|
setTimeout(() => spawnSoldier(name, index).then(s => { bots[index] = s; }), 10000);
|
|
});
|
|
|
|
bot.on('error', () => {});
|
|
bots.push(soldier);
|
|
});
|
|
}
|
|
|
|
// ── March loop — move soldiers in formation ──
|
|
setInterval(() => {
|
|
marchAngle += MARCH_SPEED;
|
|
|
|
for (let i = 0; i < bots.length; i++) {
|
|
const s = bots[i];
|
|
if (!s || !s.alive || !s.bot.entity) continue;
|
|
|
|
// Calculate this soldier's position on the circle
|
|
const angle = marchAngle + (i * FORMATION_SPACING);
|
|
const targetX = Math.cos(angle) * RADIUS;
|
|
const targetZ = Math.sin(angle) * RADIUS;
|
|
const targetY = 80; // road level
|
|
|
|
// Look in march direction (tangent to circle)
|
|
const lookAngle = angle + Math.PI / 2;
|
|
const lookX = s.bot.entity.position.x + Math.cos(lookAngle) * 5;
|
|
const lookZ = s.bot.entity.position.z + Math.sin(lookAngle) * 5;
|
|
|
|
// Move toward target position
|
|
const pos = s.bot.entity.position;
|
|
const dx = targetX - pos.x;
|
|
const dz = targetZ - pos.z;
|
|
const dist = Math.sqrt(dx * dx + dz * dz);
|
|
|
|
if (dist > 2) {
|
|
// Sprint to catch up
|
|
s.bot.lookAt(s.bot.entity.position.offset(dx, 0, dz));
|
|
s.bot.setControlState('forward', true);
|
|
s.bot.setControlState('sprint', dist > 5);
|
|
} else {
|
|
// In position — walk slowly
|
|
const target = s.bot.entity.position.offset(
|
|
Math.cos(angle + Math.PI / 2) * 3,
|
|
0,
|
|
Math.sin(angle + Math.PI / 2) * 3
|
|
);
|
|
s.bot.lookAt(target);
|
|
s.bot.setControlState('forward', true);
|
|
s.bot.setControlState('sprint', false);
|
|
}
|
|
}
|
|
}, 200);
|
|
|
|
// ── Guardian Angel — gateway clears threats and buffs soldiers every 15s ──
|
|
setInterval(() => {
|
|
const { execSync } = require('child_process');
|
|
try {
|
|
execSync(`python3 -c "
|
|
from mcrcon import MCRcon
|
|
import math
|
|
with MCRcon('${host}', 'REDACTED_RCON', port=25578) as mcr:
|
|
# Kill all hostiles near the march route
|
|
mcr.command('kill @e[type=minecraft:zombie,distance=..60]')
|
|
mcr.command('kill @e[type=minecraft:husk,distance=..60]')
|
|
mcr.command('kill @e[type=minecraft:skeleton,distance=..60]')
|
|
mcr.command('kill @e[type=minecraft:spider,distance=..60]')
|
|
mcr.command('kill @e[type=minecraft:creeper,distance=..60]')
|
|
mcr.command('kill @e[type=minecraft:phantom,distance=..60]')
|
|
|
|
# Buff all soldiers
|
|
names = ${JSON.stringify(NAMES.slice(0, COUNT))}
|
|
for name in names:
|
|
mcr.command(f'effect give {name} minecraft:regeneration 30 1')
|
|
mcr.command(f'effect give {name} minecraft:resistance 30 1')
|
|
mcr.command(f'effect give {name} minecraft:glowing 30 0')
|
|
|
|
# Dramatic effects — fireworks at the lead soldier's position
|
|
angle = ${marchAngle} if ${marchAngle} else 0
|
|
fx = int(25 * math.cos(angle))
|
|
fz = int(25 * math.sin(angle))
|
|
mcr.command(f'summon minecraft:firework_rocket {fx} 82 {fz}')
|
|
"`, { timeout: 10000, stdio: 'pipe' });
|
|
} catch (e) {}
|
|
}, 15000);
|
|
|
|
// ── Status report ──
|
|
setInterval(() => {
|
|
const alive = bots.filter(s => s.alive).length;
|
|
const totalKills = bots.reduce((sum, s) => sum + s.kills, 0);
|
|
console.log(`MARCH: ${alive}/${bots.length} marching | Kills: ${totalKills} | Angle: ${(marchAngle * 180 / Math.PI).toFixed(0)}°`);
|
|
}, 8000);
|
|
|
|
// ── Deploy ──
|
|
console.log(`\n⚔️ MORTDECAI ROYAL GUARD — ${COUNT} soldiers deploying ⚔️\n`);
|
|
|
|
(async () => {
|
|
for (let i = 0; i < COUNT; i++) {
|
|
await spawnSoldier(NAMES[i], i);
|
|
await new Promise(r => setTimeout(r, 6000));
|
|
}
|
|
console.log(`\n🛡️ All ${COUNT} guards deployed! Marching begins.\n`);
|
|
|
|
// Gear up
|
|
const { execSync } = require('child_process');
|
|
try {
|
|
execSync(`python3 -c "
|
|
from mcrcon import MCRcon
|
|
with MCRcon('${host}', 'REDACTED_RCON', port=25578) as mcr:
|
|
names = ${JSON.stringify(NAMES.slice(0, COUNT))}
|
|
for name in names:
|
|
mcr.command(f'give {name} minecraft:iron_sword 1')
|
|
mcr.command(f'item replace entity {name} armor.chest with minecraft:diamond_chestplate')
|
|
mcr.command(f'item replace entity {name} armor.legs with minecraft:diamond_leggings')
|
|
mcr.command(f'item replace entity {name} armor.feet with minecraft:diamond_boots')
|
|
mcr.command(f'item replace entity {name} armor.head with minecraft:diamond_helmet')
|
|
mcr.command(f'effect give {name} minecraft:regeneration 600 2')
|
|
mcr.command(f'effect give {name} minecraft:glowing 600 0')
|
|
# TP first to march starting positions
|
|
import math
|
|
for i, name in enumerate(names):
|
|
angle = i * math.pi / (len(names) * 1.2)
|
|
x = int(25 * math.cos(angle))
|
|
z = int(25 * math.sin(angle))
|
|
mcr.command(f'tp {name} {x} 80 {z}')
|
|
print(f'Guards geared and positioned')
|
|
|
|
mcr.command('tellraw @a [{\"text\":\"⚔️ \",\"color\":\"gold\"},{\"text\":\"THE ROYAL GUARD MARCHES! \",\"color\":\"gold\",\"bold\":true},{\"text\":\"Behold the divine army in formation.\",\"color\":\"yellow\"}]')
|
|
|
|
# TP slingshooter to watch from center monument
|
|
mcr.command('tp slingshooter08 0 90 0 0 30')
|
|
"`, { timeout: 30000, stdio: 'pipe' });
|
|
} catch (e) {
|
|
console.log('Gear-up error:', e.message?.slice(0, 80));
|
|
}
|
|
})();
|
|
|
|
process.on('SIGINT', () => {
|
|
console.log('\nDismissing the guard...');
|
|
bots.forEach(s => { try { s.bot.end(); } catch (e) {} });
|
|
setTimeout(() => process.exit(0), 2000);
|
|
});
|