Negli ultimi giorni ho realizzato Orione, un NPC che entra realmente nel mio server Minecraft e si comporta come un personaggio semi-autonomo. L’obiettivo non era creare un semplice bot da chat, ma un’entita’ capace di muoversi nel mondo, raccogliere risorse, interagire con una cassa condivisa, difendere una base e rispondere ai miei comandi in gioco.
Architettura del progetto
L’infrastruttura gira su Proxmox ed e’ stata separata in due container distinti: uno dedicato al server Minecraft e uno dedicato all’NPC IA. Nel primo gira il server Java con avvio automatico; nel secondo gira un servizio Node.js che connette Orione al mondo di gioco tramite Mineflayer, con supporto al pathfinding, gestione inventario, automazioni e logica personalizzata.
Per garantire continuita’ dopo i riavvii ho aggiunto uno stato persistente su file. In questo modo Orione ricorda la cassa assegnata, il giocatore autorizzato ai comandi, eventuali timer di sospensione e altre informazioni utili al riavvio del processo.
Ruolo di ChatGPT e OpenAI
ChatGPT e’ stato usato come supporto di progettazione e sviluppo: struttura del codice, raffinamento dei loop di controllo, diagnostica dei problemi, definizione dei comandi naturali e revisione incrementale del comportamento del bot. Le API OpenAI sono state integrate per dare una base a una futura componente conversazionale contestuale, capace di usare il mondo Minecraft come ambiente operativo reale e non solo come sfondo per una chat.
Il punto chiave del progetto e’ l’unione tra linguaggio e azione: un prompt o un comando non produce solo una risposta testuale, ma puo’ tradursi in movimento, raccolta risorse, crafting, difesa o interazione con blocchi e contenitori.
Il ruolo concreto delle API OpenAI
Le API OpenAI non servono a far muovere direttamente il personaggio nel mondo Minecraft. Il loro ruolo corretto e’ piu’ alto livello: interpretare richieste umane, trasformarle in intenzioni comprensibili dal codice del bot, generare risposte piu’ naturali, spiegare cosa Orione sta facendo e supportare la pianificazione di attivita’ complesse.
In pratica il flusso corretto e’ questo: il giocatore scrive un comando o una richiesta, il livello applicativo decide se la richiesta va gestita con regole fisse oppure se conviene passarla al modello OpenAI, il modello restituisce una risposta o una struttura logica, e solo a quel punto il codice locale decide quali azioni consentite eseguire davvero in gioco.
Questo significa che l’API non dovrebbe mai avere accesso diretto e incontrollato a funzioni pericolose. La parte IA deve restare separata da quella esecutiva. Ad esempio il modello puo’ dire che l’intento del giocatore e’ raccogliere legna e mettere al sicuro le risorse, ma poi tocca al codice Node.js scegliere le routine abilitate: trovare alberi naturali, usare la cassa assegnata, evitare case costruite dai giocatori, interrompersi se il pathfinding fallisce.
In una architettura piu’ evoluta, OpenAI puo’ essere usato anche per:
- interpretare frasi meno rigide dei semplici comandi testuali;
- riassumere lo stato corrente del bot in linguaggio naturale;
- produrre spiegazioni in chat su cosa sta facendo Orione e perche’;
- scegliere tra task ad alto livello, lasciando l’esecuzione a funzioni locali sicure;
- mantenere un comportamento piu’ credibile e meno meccanico nelle interazioni con i giocatori.
Dal punto di vista pratico, le API OpenAI entrano quindi come livello di orchestrazione semantica. Mineflayer, pathfinder, inventario, crafting, combattimento e gestione dei blocchi restano responsabilita’ del bot locale.
Un esempio minimo di chiamata puo’ essere questo:
async function askOpenAI(prompt) {
const response = await openai.responses.create({
model: process.env.OPENAI_MODEL || 'gpt-5.5',
input: prompt
})
return response.output_text
}
La parte importante non e’ la chiamata in se’, ma il contratto attorno alla chiamata. Conviene definire un insieme ristretto di azioni possibili, ad esempio followPlayer, collectWood, guardChest, sleepAtBed, e tradurre l’output del modello solo in questi comportamenti noti. In questo modo il modello aiuta a decidere o spiegare, ma non puo’ produrre effetti arbitrari nel server.
Questa separazione e’ fondamentale anche per costi, debugging e affidabilita’. Se il modello non risponde, il bot deve continuare a funzionare almeno nelle attivita’ base. Se il modello propone qualcosa di incoerente, il codice puo’ scartarlo. E se il server cambia stato, e’ il bot locale che ha sempre l’ultima parola sulle azioni realmente possibili.
Stack tecnico utilizzato
- Proxmox VE per ospitare i container separati.
- Debian 12 come base per il server Minecraft e per il container dell’NPC.
- Java Temurin/OpenJDK per il server Minecraft.
- Node.js per il processo del bot.
- Mineflayer per collegare Orione al server come se fosse un giocatore.
- mineflayer-pathfinder per movimento, navigation e goal.
- mineflayer-collectblock per facilitare alcune raccolte automatiche.
- OpenAI API per l’estensione della parte IA.
- systemd per rendere sia il server sia il bot persistenti al boot.
- ViaProxy per gestire la compatibilita’ protocollo tra bot e server quando necessario.
Documentazione completa delle implementazioni fatte
Nel corso dello sviluppo Orione e’ stato esteso con una serie di implementazioni progressive, pensate per farlo sembrare sempre meno un bot rigido e sempre piu’ un personaggio reale presente nel server. Qui sotto riassumo tutte le funzionalita’ introdotte.
1. Modalita’ servo e controllo autorizzato
- Comando
orione servoper attivare la modalita’ in cui Orione risponde a un giocatore specifico. - Memorizzazione persistente del giocatore autorizzato ai comandi.
- Blocco di molti comandi quando la modalita’ servo non e’ attiva.
- Separazione tra comportamento autonomo e comportamento guidato dal giocatore.
2. Guida e comandi informativi
- Comando
orione guidaper far elencare a Orione i comandi base disponibili. - Comando
orione coordinateper conoscere la posizione attuale del bot. - Comando
orione dove sonoper ottenere la posizione del giocatore che parla. - Comando
orione inventarioper far riportare cosa ha addosso o nello zaino. - Comando
orione controlla cassaper ispezionare il contenuto della cassa di riferimento.
3. Richiamo, movimento e teleport
- Comando
orione seguimiper fare seguire il giocatore. - Comando
orione restaper fermare il follow. - Comando
orione vieni quioorione torna da meper richiamarlo vicino al giocatore. - Comando
orione teleportati da meper usare il teleport via comandi server quando necessario. - Miglioramento del richiamo in modo che il bot sia gestibile anche quando e’ lontano o incastrato.
4. Cassa condivisa e persistenza
- Comando
orione usa questa cassaper associare una chest come deposito o punto logistico. - Salvataggio persistente della posizione della cassa su file JSON.
- Ripristino automatico della cassa assegnata al reboot del servizio.
- Uso della cassa come fonte di strumenti, torce, cibo, carbone e materiali.
5. Gestione risorse e prelievo oggetti
- Comando
orione prendi torce dalla cassa. - Comando
orione prendi legna. - Comando
orione prendi carbone. - Comando
orione prendi cibo. - Comando
orione prendi armatura. - Comando
orione prendi ascia legnocon tentativo di prelievo dalla cassa e crafting solo come fallback. - Controllo esplicito per fare verificare se l’ascia fosse gia’ presente nella cassa prima di craftarla.
6. Raccolta della legna migliorata
- Prima versione della raccolta della legna.
- Miglioramento della logica per evitare comportamenti inefficienti o distruttivi.
- Filtro per considerare come legna solo tronchi appartenenti ad alberi naturali.
- Esclusione delle costruzioni del giocatore dalla raccolta automatica.
7. Raccolta del carbone e sicurezza del mining
- Abilitazione della ricerca di carbone come risorsa utile in modalita’ libera.
- Restrizione della ricerca ai blocchi esposti e piu’ sicuri.
- Riduzione della tendenza a scavare verso il basso in modo incontrollato.
- Controlli per evitare che Orione si comporti come un miner cieco invece che come un NPC sensato.
8. Modalita’ libera e autonomia
- Comando
orione liberoper attivare una modalita’ autonoma. - Priorita’ su raccolta di uova, carne, legna e carbone.
- Loop di lavoro periodici per mantenere il bot attivo anche senza comandi continui.
- Transizione dal comportamento puramente reattivo a una forma di attivita’ persistente.
9. Casa iniziale e costruzione
- Richiesta iniziale per fare costruire una casa al boot.
- Comando
orione costruisci casaper creare una struttura semplice. - Gestione di supporti e blocchi minimi necessari alla costruzione.
- Primi tentativi di comportamento da survival builder, non solo raccoglitore.
10. Notte, letti e riposo
- Ricerca del primo letto disponibile quando non ci sono giocatori online.
- Uso del letto come punto di attesa e sicurezza.
- Se trova pecore in modalita’ libera, tentativo di ottenere lana e farsi un letto.
- Migliorie per fare sembrare il comportamento notturno piu’ naturale.
11. Comando dormi con disconnessione e riconnessione
- Comando
orione dormi. - Disconnessione volontaria del bot per 30 secondi.
- Persistenza del timer di sleep su file.
- Ritardo di bootstrap al riavvio per aspettare la fine del periodo di sonno prima di riconnettersi.
12. Difesa della cassa e combattimento
- Comportamento difensivo verso zombie vicini alla cassa.
- Modalita’ di guardia notturna quando il bot si trova in zona letto o in area base.
- Interruzione del riposo in presenza di ostili.
- Primo passaggio da NPC passivo a NPC che difende una zona precisa.
13. Armi, armatura e crafting base
- Costruzione o recupero di armi base.
- Tentativo di craftare una spada, preferendo opzioni migliori quando disponibili.
- Equip automatico di armatura e strumenti trovati o presi dalla cassa.
- Introduzione di una logica minima di preparazione al combattimento.
14. Coordinate, presenza e realismo
- Aggiunta di comandi per sapere dove si trova Orione.
- Aggiunta della possibilita’ di sapere dove si trova il giocatore.
- Migliorie generali per farlo apparire piu’ simile a un giocatore reale e meno a un processo invisibile.
15. Integrazione della logica di servizio e obbedienza selettiva
- Obiettivo esplicito: Orione deve rispondere ai comandi solo se gli viene detto
orione servo. - Riduzione dei comportamenti da “sempre in ascolto”.
- Maggiore coerenza tra ruolo del personaggio e interazione col proprietario.
16. Gestione problemi osservati durante i test
- Correzioni legate al fatto che Orione non guardava correttamente nella cassa prima di craftare un oggetto.
- Correzioni legate alla perdita del riferimento della cassa dopo i riavvii.
- Migliorie sulla resilienza del bot quando il pathfinding si bloccava o il task falliva.
- Logging e verifiche incrementali basate sul comportamento reale osservato nel server.
17. Compatibilita’ tecnica del bot
- Separazione del server Minecraft e del bot in container diversi.
- Uso di ViaProxy per gestire differenze di protocollo tra bot e server.
- Integrazione del tutto come servizi di sistema con restart automatico.
- Preparazione del bot per un funzionamento stabile nel tempo, non solo per una demo temporanea.
Struttura logica del bot
Il codice del bot e’ stato organizzato attorno ad alcuni blocchi fondamentali:
- bootstrap e caricamento configurazione da
.env; - lettura e scrittura dello stato persistente su file JSON;
- inizializzazione della connessione Mineflayer;
- registrazione dei listener sugli eventi principali (
spawn,chat,end,error); - parser dei comandi testuali;
- loop periodici per raccolta, difesa, riposo, crafting e gestione della cassa;
- funzioni di utilita’ per ricerca blocchi, movimento, withdraw/deposit e verifica delle risorse.
Una semplificazione della struttura iniziale in JavaScript e’ questa:
import mineflayer from 'mineflayer'
import { pathfinder } from 'mineflayer-pathfinder'
import OpenAI from 'openai'
import fs from 'fs'
import dotenv from 'dotenv'
dotenv.config()
const bot = mineflayer.createBot({
host: process.env.MC_HOST,
port: Number(process.env.MC_PORT || 25565),
username: process.env.MC_USERNAME,
version: process.env.MC_VERSION
})
bot.loadPlugin(pathfinder)
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
const stateFile = '/opt/minecraft-ai-bot/state.json'
function loadState() {
try {
return JSON.parse(fs.readFileSync(stateFile, 'utf8'))
} catch {
return {}
}
}
function saveState(state) {
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2))
}
Configurazione minima per ricrearlo
Per replicare il progetto servono almeno due ambienti separati: uno con Minecraft server e uno con Node.js. Nel container del bot bastano pochi file iniziali:
mkdir -p /opt/minecraft-ai-bot
cd /opt/minecraft-ai-bot
npm init -y
npm install mineflayer mineflayer-pathfinder mineflayer-collectblock openai dotenv vec3
Un esempio di file .env e’ il seguente:
MC_HOST=172.16.4.10
MC_PORT=25565
MC_USERNAME=OrioneAI
MC_VERSION=1.21.4
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-5.5
Per l’avvio automatico del bot ho usato un servizio systemd simile a questo:
[Unit]
Description=Minecraft AI Bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=/opt/minecraft-ai-bot
ExecStart=/usr/bin/node /opt/minecraft-ai-bot/index.js
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Gestione dei comandi in gioco
Una parte importante del progetto e’ il parser dei comandi chat. La logica non si limita a confrontare stringhe, ma applica anche regole di autorizzazione: ad esempio Orione deve rispondere davvero solo quando entra in modalita’ servo, oppure quando il comando proviene dal giocatore autorizzato.
bot.on('chat', async (username, message) => {
if (username === bot.username) return
const text = message.trim().toLowerCase()
if (text === 'orione servo') {
state.servantLeader = username
saveState(state)
bot.chat(`Servo attivo per ${username}`)
return
}
if (text === 'orione coordinate') {
const p = bot.entity.position
bot.chat(`Sono a ${Math.floor(p.x)} ${Math.floor(p.y)} ${Math.floor(p.z)}`)
return
}
})
Raccolta risorse e cassa condivisa
Per far sembrare Orione utile e credibile non bastava farlo camminare. Ho aggiunto un sistema di lavoro basato su priorita’: ricerca di legna da alberi naturali, recupero di carbone esposto, uso di una cassa assegnata come deposito e prelievo automatico di strumenti, torce, cibo e materiali. Il bot controlla il contenuto della cassa, valuta il proprio inventario e poi decide se prelevare, craftare o cercare nel mondo.
Questa parte richiede attenzione per evitare effetti collaterali indesiderati: ad esempio considerare come legna solo i tronchi realmente associati ad alberi, cosi’ da non smontare case o strutture dei giocatori.
Difesa, notte e comportamenti autonomi
Una parte interessante del progetto e’ stata l’aggiunta di comportamenti da pseudo-giocatore: difesa della zona della cassa, ricerca di un letto quando non ci sono utenti online, crafting di strumenti base, equipaggiamento minimo e modalita’ libera per raccolta di risorse. Ho introdotto anche un comando di sospensione temporanea, in cui Orione si disconnette e si ricollega dopo un intervallo prestabilito.
Per la notte e per i momenti di inattivita’ la logica puo’ essere schematizzata cosi’: se non ci sono giocatori online, il bot cerca il primo letto disponibile; se trova ostili nei pressi della zona protetta, interrompe il riposo e passa in modalita’ difesa.
Integrazione con OpenAI
L’integrazione con OpenAI e’ stata pensata come uno strato sopra la logica operativa. In pratica il modello puo’ essere usato per interpretare intenzioni, spiegare cosa sta facendo il bot, generare risposte piu’ naturali o decidere azioni ad alto livello. La parte critica e’ non lasciare che il modello esegua direttamente qualsiasi cosa: serve sempre una mappatura tra intenzione e set di azioni consentite dal codice.
async function askOpenAI(prompt) {
const response = await openai.responses.create({
model: process.env.OPENAI_MODEL || 'gpt-5.5',
input: prompt
})
return response.output_text
}
Nella pratica conviene usare il modello per analisi e pianificazione locale, mentre la parte esecutiva resta in funzioni deterministiche del bot.
Passaggi pratici di sviluppo
- Creare il server Minecraft e verificare che un client umano possa entrare correttamente.
- Creare un container separato per il bot con Node.js, cosi’ da isolare dipendenze, log e restart.
- Far connettere inizialmente un bot minimale, senza IA, solo con login ed evento
spawn. - Aggiungere il pathfinding e verificare movimenti semplici verso coordinate o giocatori.
- Implementare un parser di comandi ridotto, per esempio
orione servoeorione coordinate. - Aggiungere gestione dell’inventario e interazione con una cassa vicina.
- Inserire persistenza dello stato per non perdere riferimenti importanti al riavvio.
- Solo dopo questa base, integrare OpenAI per risposte, pianificazione o comportamento contestuale.
- Infine consolidare con systemd, logging e test ripetuti nel mondo reale.
Cosa serve davvero per ricrearlo
Se qualcuno volesse rifare il progetto da zero, questi sono i punti essenziali:
- un server Minecraft Java accessibile in rete;
- un bot Mineflayer che riesca a connettersi stabilmente al server;
- un sistema di persistenza locale per ricordare stato e coordinate importanti;
- un parser dei comandi semplice ma robusto;
- funzioni di movimento affidabili e protette da timeout o fallback;
- una gestione sicura di casse, crafting e inventario;
- una separazione chiara tra IA testuale e azioni consentite in gioco;
- servizi systemd per restart automatico e resilienza al boot.
Le difficolta’ principali
La parte piu’ complessa non e’ stata la chat, ma il comportamento fisico nel mondo: pathfinding, ostacoli, blocchi irregolari, inventario, gestione delle casse e distinzione tra risorse naturali e strutture costruite dal giocatore. Un bot che sembra davvero presente deve saper fallire bene, riprovare, evitare di scavare a caso e non distruggere la base.
Per questo ho introdotto controlli aggiuntivi sulla raccolta della legna, limiti sulla ricerca del carbone in profondita’, persistenza della cassa, fallback sui task bloccati e log di debug per capire quando il percorso o l’interazione con un blocco fallivano.
Prossimi sviluppi
La base adesso e’ solida: il prossimo passo e’ rendere Orione ancora piu’ credibile, con routine migliori, priorita’ dinamiche, uso piu’ intelligente delle risorse nella cassa, e una componente conversazionale sempre piu’ integrata con lo stato reale del mondo di gioco.
L’idea finale e’ semplice: non un plugin che simula un NPC, ma un personaggio che vive davvero nel server e collabora con i giocatori come se fosse uno di loro.