Salta ai contenuti

mgctl, lo strumento Moongate

mgctl è l’unico strumento distribuito accanto a mgserver, il server, negli archivi delle versioni e nelle immagini Docker; l’installatore Linux lo collega come comando mgctl. Le versioni fino a 0.11 chiamavano il server Moongate.Server. Le versioni da 0.7 a 0.11 chiamavano lo strumento mgboot, che preparava solo la directory radice, e distribuivano le migrazioni e il convertitore come altri due eseguibili, migration-runner/Moongate.MigrationRunner e mg-uoxconv; in quel caso, leggi mgboot <root> al posto di mgctl init <root>.

Comando Cosa fa
mgctl init <root> Prepara la directory radice del server; questa pagina. mgctl <root> fa lo stesso
mgctl migrate status|apply --target auth|world Elenca o applica l’SQL versionato; vedi Migrazioni della persistenza
mgctl plugin install|list|remove Installa, elenca o rimuove i pacchetti dei plugin; vedi Installare un pacchetto plugin
mgctl run Esegue il server e lo riavvia dopo un restart o un crash; vedi Eseguire il server
mgctl completion bash|zsh|fish Stampa lo script che completa mgctl con TAB; vedi Completamento con TAB

mgctl --help elenca i comandi e mgctl <command> --help le opzioni di uno di essi.

I convertitori di dati (UOX3 e ModernUO) non fanno più parte di mgctl: sono uno strumento Python, moongate-convert; vedi Migrare da UOX3.

mgctl completion <shell> stampa uno script di completamento per bash, zsh o fish. Con esso, TAB completa i comandi (mgctl mi → migrate), la seconda parola di migrate, le opzioni del comando e ciò che le segue: directory dopo --root-directory e simili, auth o world dopo --target, e una directory per la radice di init.

L’installatore Linux colloca gli script dove le shell li cercano, quindi una nuova shell completa mgctl senza altre operazioni. Altrove, caricalo personalmente:

Terminal window
eval "$(mgctl completion bash)" # bash: add the line to ~/.bashrc
source <(mgctl completion zsh) # zsh: add the line to ~/.zshrc, after compinit
mgctl completion fish | source # fish: or save it as ~/.config/fish/completions/mgctl.fish

La riga bash usa eval perché bash 3.2 di macOS non carica nulla da source <(...); lì, un percorso con uno spazio viene completato senza le relative virgolette.

Lo script fish è generato dallo stesso elenco di comandi degli altri due ma, a differenza di essi, non viene esercitato dai test.

Un plugin pubblicato come pacchetto .nupkg, come quelli di moongate-plugins, si installa con mgctl plugin, senza decomprimerlo a mano. La guida per chi gestisce uno shard, con configurazione, aggiornamento e rimozione, è Installare e gestire i plugin; questa sezione elenca cosa controllano i comandi. Ferma prima il server: legge plugins/ una sola volta, all’avvio.

Terminal window
mgctl plugin install Moongate.Plugins.OpenAi --root-directory /srv/moongate
mgctl plugin install MyPlugin --repo myuser/my-plugins --root-directory /srv/moongate
mgctl plugin install ./Moongate.Plugins.OpenAi.0.2.1.nupkg --root-directory /srv/moongate
mgctl plugin install https://codeberg.org/moongate/moongate-plugins/releases/download/Moongate.Plugins.OpenAi-v0.2.1/Moongate.Plugins.OpenAi.0.2.1.nupkg
mgctl plugin list --root-directory /srv/moongate
mgctl plugin remove Moongate.Plugins.OpenAi --root-directory /srv/moongate

install accetta un file di pacchetto, un indirizzo https (uno http semplice viene rifiutato, tranne per questa macchina, e un reindirizzamento deve rispettare la stessa regola) o un id di plugin, che cerca in un repository: vedi Installare per id da un repository. Esegue questi passi in ordine e si ferma al primo che fallisce, lasciando il plugin installato com’era:

  1. Legge il nuspec: il tipo del pacchetto deve essere MoongatePlugin e l’id un semplice nome di cartella (lettere, cifre e ., -, _ tra di esse; niente nomi di dispositivo come CON).
  2. Controlla la dipendenza esatta da Moongate.Server.Core o Moongate.Hosting (un plugin può nominare l’uno, l’altro o entrambi, alla stessa versione): deve essere la versione di questo mgctl, cioè quella del server accanto a lui. Un plugin compilato per un’altra versione viene rifiutato qui, prima che possa fallire il caricamento all’avvio.
  3. Controlla il bundle: al massimo 1000 file, nessuna voce che esce dalla cartella del bundle, e i file <id>.dll e <id>.deps.json che servono al loader.
  4. Mostra l’id, la versione, la versione di Moongate per cui è stato compilato e lo SHA-256 del pacchetto, e chiede Install it? [y/N]. --yes salta la domanda. Un plugin gira dentro il server con tutti i suoi permessi: installalo solo da una fonte di cui ti fidi.
  5. Decomprime solo la cartella plugin/ del pacchetto, al massimo 100 MB, in una cartella di lavoro dentro plugins/ il cui nome inizia con un punto, poi sostituisce <root>/plugins/<id>/ con essa. Installare una versione più recente sopra una più vecchia è il modo di aggiornare un plugin; il nuspec resta nella cartella.

Una cartella di lavoro lasciata da un’installazione interrotta viene ignorata dal server e cancellata dall’installazione successiva; una -old contiene il bundle che si stava sostituendo, e l’installazione dice dov’è.

list mostra ogni cartella di plugins/ che non inizia con un punto, con la sua versione, la versione di Moongate per cui è stata compilata e da dove viene: package per un’installazione di mgctl, by hand per un bundle copiato a mano, la cui versione è letta dal suo assembly. remove <id> cancella la cartella. Le tabelle del database del plugin restano: le migrazioni vanno solo avanti, quindi eliminale tu se vuoi che spariscano.

La radice viene da --root-directory, poi da MOONGATE_ROOT, poi dalla directory di mgctl, come per migrate; deve contenere config/moongate.toml, così un percorso sbagliato viene rifiutato invece di ricevere una cartella plugins/. Nulla viene installato o cambiato per conto del server: non c’è aggiornamento automatico; il plugin e il repository sono quelli che indichi tu.

Con un id di plugin al posto di un file, install cerca il plugin in un repository e installa la build più recente fatta per la versione di Moongate di questo mgctl. Un argomento che finisce con .nupkg, indica un file esistente o inizia con http:// o https:// è un pacchetto, non un id; qualunque altra cosa che sia un id valido viene cercata, quindi un nome di file scritto male viene cercato nel repository.

Terminal window
mgctl plugin install Moongate.Plugins.OpenAi # the community repository
mgctl plugin install MyPlugin --repo myuser/my-plugins # releases of a Codeberg repository
mgctl plugin install MyPlugin --repo https://codeberg.org/myuser/my-plugins # the same, as an address
mgctl plugin install MyPlugin --repo https://github.com/myuser/my-plugins # releases of a GitHub repository
mgctl plugin install MyPlugin --repo ./my-packages # a folder of .nupkg files
mgctl plugin install MyPlugin --repo myuser/my-plugins --version 1.2.0 # an exact version
--repo Dove cerca
assente moongate/moongate-plugins
una cartella i file <id>.<version>.nupkg che contiene; una cartella scritta come owner/name vuole ./ davanti, altrimenti è presa per un repository Codeberg
owner/name (sempre su Codeberg), https://codeberg.org/owner/name o https://github.com/owner/name le release pubblicate (le 500 più recenti), per gli asset chiamati <id>.<version>.nupkg (il nome che dà dotnet pack); il tag non conta
  • Le versioni si provano dalla più recente, e vince la prima il cui pacchetto dice di essere fatto per questo Moongate; si aprono al massimo le 10 più recenti, e la risposta lo dice se ce ne sono di più. Se nessuna lo è, il comando elenca le versioni trovate e il Moongate per cui è fatta ciascuna. Un pacchetto il cui nuspec nomina un altro id o un’altra versione rispetto al nome del file viene saltato.
  • Le prerelease (2.0.0-beta.1, e le release segnate come prerelease) e le bozze non vengono mai scelte da sole. --version indica una versione, anche una prerelease, e viene rifiutata come un file se è fatta per un altro Moongate.
  • --repo e --version vanno solo con un id; con un file o un indirizzo vengono rifiutati.
  • La richiesta di conferma mostra host e repository accanto a id, versione e SHA-256. Un repository è affidabile quanto il suo proprietario: i controlli qui sopra non rendono sicuro un plugin.
  • CODEBERG_TOKEN (o GITHUB_TOKEN per un repository su GitHub), dall’ambiente e mai dalla riga di comando, raggiunge un repository privato e alza il limite dell’API. Va solo all’host dell’API, mai all’host a cui un download reindirizza, e un indirizzo di asset fuori dall’origine dell’API viene ignorato finché c’è un token. MGCTL_RELEASES_API_URL indirizza mgctl a un’altra API delle release, come un mirror o un’altra istanza Forgejo; sostituisce l’API dell’host su cui sta il repository, e il token di quell’host la segue.

mgctl run avvia mgserver come processo figlio e lo tiene attivo:

mgctl run --root-directory ~/moongate
mgctl run --root-directory ~/moongate --log-level Debug
Il server esce con mgctl run
0 (uno shutdown, Ctrl+C) esce con 0
75 (un restart) lo riavvia subito
qualsiasi altro codice lo riavvia dopo 1, 2, 4… fino a 30 secondi

Cinque fallimenti di fila, ciascuno entro 60 secondi dal proprio avvio, terminano il launcher con exit code 1, così una configurazione sbagliata non può andare in loop all’infinito; un’esecuzione che dura un minuto è sana e azzera il conteggio.

Opzione Significato
--root-directory <path> La radice del server; MOONGATE_ROOT se assente. Viene data anche al server
--no-init Non esegue mgctl init sulla radice prima di ogni avvio
--server-path <file> Il mgserver da eseguire; di default quello accanto a mgctl
--restart-delay <seconds> La prima attesa dopo un crash, da 0 a 30 (default 1)

Tutto il resto della riga, con o senza un -- davanti, va a mgserver invariato.

Prima di ogni avvio il launcher prepara la radice come fa mgctl init, così un riavvio raccoglie i file di dati che una nuova build porta; non stampa nulla quando funziona e ciò che init ha detto quando fallisce. Il server tiene il tuo terminale: prompt, completamento con TAB e cronologia funzionano come senza il launcher.

Arresto. Ctrl+C raggiunge il server attraverso il terminale e il launcher termina con lui. Un SIGTERM o SIGHUP inviato al solo launcher (docker stop, kill, systemd con KillMode=mixed) viene passato al server una sola volta, e il launcher lo aspetta: il salvataggio finale di un mondo grande può durare uno o due minuti, e un arresto non viene mai riavviato, qualunque cosa risponda il server. Il launcher termina con l’ultimo exit code del server. Un terminale che si chiude manda SIGHUP anche al server, che non lo gestisce: chiudi la sessione con uno shutdown, non chiudendo la finestra. Su Windows nulla viene inoltrato; la console consegna Ctrl+C a entrambi i processi.

mgctl init prepara una directory dati Moongate senza avviare il server né connettersi a PostgreSQL. È il passo 1 della sequenza del primo avvio.

Terminal window
mgctl init /absolute/path/to/moongate-data

La directory radice è l’unico argomento posizionale obbligatorio. I percorsi relativi vengono risolti dalla directory di lavoro corrente. Racchiudi tra virgolette i percorsi contenenti spazi:

Terminal window
mgctl init "/srv/my realm"
mgctl init --help

mgctl <root>, senza init, è la forma precedente e funziona ancora per una radice scritta come percorso (/srv/moongate, ./data) o che indica una directory esistente; un nuovo nome semplice come mgctl data viene rifiutato, così un comando scritto male non diventa mai una directory radice. Una riga di comando che non indica alcun comando, come mgctl migrate da solo, termina con codice 2.

Su Windows, usa mgctl.exe init C:\MoongateData dalla distribuzione estratta. Mantieni insieme mgctl e mgserver della stessa versione. Quando prepara una directory radice, mgctl mostra la stessa intestazione Moongate, versione e nome in codice del server, seguiti da Root setup. L’output di aiuto e versione omette l’intestazione, così come mgctl init <root> --no-header: scripts/run_server.sh lo usa perché il server che avvia subito dopo mostra già l’intestazione.

Generare un certificato di amministrazione

Section titled “Generare un certificato di amministrazione”
Terminal window
mgctl init /srv/moongate --generate-admin-certificate \
--admin-certificate-hosts "login.example.test,192.0.2.10"

La CLI usa ConsoleAppFramework. --generate-admin-certificate è un flag opzionale attivato dalla sua presenza. --admin-certificate-hosts è un’unica stringa separata da virgole e richiede quel flag. Ometti l’opzione degli host per lo sviluppo locale: ogni certificato include automaticamente localhost, 127.0.0.1 e ::1. Le voci aggiuntive devono essere nomi DNS o indirizzi IP, senza schemi URL, porte o indirizzi jolly. Usa i nomi a cui si connettono i client; *, 0.0.0.0 e :: sono indirizzi di ascolto, non identità del certificato.

L’operazione viene eseguita offline e crea un certificato server RSA autofirmato valido per un anno. Scrive questi file sotto la directory radice:

File Contenuto
certificates/admin.pfx Certificato server e chiave privata; nessuna password; lettura/scrittura solo per il proprietario su Unix
certificates/admin.crt Certificato PEM pubblico da considerare attendibile nei client di amministrazione

Aggiorna queste quattro impostazioni in config/moongate.toml:

[admin_api]
enabled = true
allow_insecure_loopback = false
certificate_path = "certificates/admin.pfx"
certificate_password = ""

La porta esistente, l’indirizzo di ascolto, le altre impostazioni e i commenti vengono conservati. Una sezione mancante viene aggiunta; le definizioni admin_api esistenti inline o con chiavi puntate devono prima essere convertite in una sezione [admin_api] esplicita. Un percorso di certificato personalizzato non viene mai sostituito implicitamente: cancella esplicitamente quell’impostazione solo se intendi cambiare identità.

Nessuna porta viene aperta durante la configurazione. L’API parte al successivo avvio normale del server. L’indirizzo di ascolto predefinito rimane 127.0.0.1:2590; per l’accesso dalla rete privata, configura un’interfaccia raggiungibile o listen_address = "*" e includi il DNS/IP effettivamente usato dai client negli host del certificato. Vedi API di amministrazione.

Eseguire di nuovo lo stesso comando riusa una coppia di certificati corrispondenti e non scaduti. Fallisce senza sovrascrivere la coppia se uno dei file è mancante, non valido, scaduto, non corrispondente o privo di un nome richiesto. Non esiste rinnovo o rotazione implicita. Per una sostituzione deliberata, arresta il server, sposta entrambi i file di certificato esistenti in una posizione di backup protetta, esegui di nuovo la generazione e aggiorna la fiducia dei client prima di riavviare. Un PFX senza password contiene comunque la chiave privata: conservalo sul server e limita l’accesso. Su Windows, usa un’ACL appropriata per l’account di servizio. Distribuisci solo admin.crt; i client devono verificare attendibilità e nome host.

Percorso sotto la directory radice Scopo
config/moongate.toml Valori predefiniti attuali del server serializzati come TOML snake_case; le sezioni dei plugin come [ultima] vengono aggiunte al primo avvio del server
logs/, plugins/ Directory standard del server
migrations/auth/ I file SQL auth principali inclusi nella distribuzione
migrations/world/ Il file SQL World principale incluso nella distribuzione: le tabelle di mobile, oggetti, stato del mondo, prigione, bacheca, allegati dei libri, pagine di aiuto e gilde
data/ I file di dati dello shard inclusi nella distribuzione: mappe, regioni, razze, abilità, messaggi e il resto; vedi File di dati dello shard
templates/ I template di oggetti, bottino, mobile, libri, negozi e altri inclusi nella distribuzione; vedi Template
scripts/ Gli script di esempio per mobile e oggetti inclusi nella distribuzione: gli script mobiles/ e items/ (per esempio mobiles/wander.lua e items/potion.lua) con i moduli condivisi common/ e gli script events/, gumps/ e skills/; il motore scrive qui definitions.lua e .luarc.json all’avvio
.mgctl.lock File conservato per impedire inizializzazioni simultanee

La nuova configurazione imposta persistence.migrations_directory al percorso assoluto migrations dentro questa directory radice. Anche il normale avvio del server usa <root>/migrations per impostazione predefinita quando l’impostazione è assente; mantenere il percorso esplicito fa usare a mgctl migrate lo stesso catalogo. Gli altri valori rimangono quelli predefiniti del server: generazione automatica delle migrazioni e sincronizzazione automatica dello schema sono disabilitate. Le credenziali Redis di runtime devono essere fornite prima di avviare il server. La directory radice non richiede accesso ai database né file del client Ultima Online per essere preparata.

File di dati, template e script vengono copiati solo se mancanti, quindi un file modificato rimane com’è. Esegui di nuovo mgctl init dopo un aggiornamento per aggiungere i file introdotti da una nuova versione; un file esistente nella directory radice non viene mai sostituito, quindi confrontalo con quello accanto al nuovo binario mgserver (data/, templates/, scripts/) per integrare le modifiche a monte. Non viene rimosso nulla: un template rinominato o spostato da una versione rimane nella directory radice accanto alla sua nuova copia e il server si arresta all’avvio per l’id duplicato, quindi elimina il file obsoleto. Un file distribuito che hai eliminato ritorna alla successiva esecuzione; svuotalo invece per mantenerlo escluso.

La preparazione delle migrazioni di base copia l’SQL versionato distribuito con Moongate. Non genera nuovo SQL dalle entità, non carica plugin, non crea database e non applica migrazioni. Usa gli strumenti di persistenza per le modifiche alle entità dopo l’inizializzazione.

Eseguire di nuovo il comando senza opzioni di certificato conserva la configurazione e i file esistenti. Le migrazioni distribuite mancanti vengono aggiunte solo quando il catalogo esistente è compatibile. Una migrazione esistente con lo stesso numero di sequenza ma un nome di file o checksum diverso interrompe l’inizializzazione prima che vengano scritti la configurazione o i file SQL. L’errore identifica il file in conflitto. Non eliminare la cronologia delle migrazioni per aggirarlo; riconcilia il catalogo o scegli una nuova directory radice.

Senza --generate-admin-certificate, una configurazione esistente non viene mai riscritta, compresa l’impostazione della directory delle migrazioni. Controlla personalmente l’impostazione se punta fuori da questa directory radice. Se sposti la directory radice, aggiorna di conseguenza il percorso assoluto in una configurazione appena generata.

Prosegui con Avviare un server Moongate: modifica la configurazione generata, crea i database PostgreSQL, applica l’SQL distribuito con mgctl migrate apply e avvia il server. Per lo sviluppo, puoi invece abilitare persistence.auto_generate_migrations dopo aver preparato i database; vedi il tutorial sulle entità.

Per un’immagine di una versione:

Terminal window
docker volume create moongate-data
docker run --rm --entrypoint /app/mgctl \
--mount type=volume,source=moongate-data,target=/data \
docker.io/tgiachi/moongate:latest init /data

Le immagini fino a 0.11.0 hanno invece /app/mgboot: usa --entrypoint /app/mgboot e passa solo /data. L’inizializzazione termina dopo aver preparato la directory radice montata; non apre alcun listener TCP.

Pubblica server e strumento nella stessa directory:

Terminal window
dotnet publish src/Moongate.Server -c Release -r linux-x64 -o dist/moongate
dotnet publish src/Moongate.Ctl -c Release -r linux-x64 -o dist/moongate
./dist/moongate/mgctl init /absolute/path/to/moongate-data

Cambia l’identificatore di runtime per la tua piattaforma. Il server espone inoltre la stessa operazione offline per lo sviluppo dai sorgenti:

Terminal window
dotnet run --project src/Moongate.Server -- --initialize-root --root-directory /absolute/path/to/moongate-data

Le stesse opzioni di certificato funzionano con l’operazione offline del server:

Terminal window
dotnet run --project src/Moongate.Server -- --initialize-root \
--root-directory /srv/moongate --generate-admin-certificate \
--admin-certificate-hosts "login.example.test,192.0.2.10"

Il server rifiuta le opzioni di certificato al di fuori di --initialize-root.

MOONGATE_SERVER_EXECUTABLE può sostituire l’eseguibile del server adiacente per i test locali degli strumenti. Deve riferirsi a un eseguibile compatibile della stessa versione dei sorgenti.

Creato con ❤️ da Tom

Offrimi un caffè