Salta ai contenuti

Primo avvio

Questa è la sequenza unica di primo avvio per Moongate. Si applica sia installando il rilascio con l’installer Linux, sia usando l’immagine container, sia compilando dal sorgente. Moongate è in sviluppo attivo: i personaggi entrano nel mondo, camminano, si vedono tra loro e combattono, e NPC e oggetti eseguono script Lua, ma una parte del gameplay manca ancora. Vedi Stato dell’implementazione.

L’avvio del server richiede una radice, file client leggibili, il database PostgreSQL del ruolo attivo e SQL revisionato, e un’istanza Redis privata per lease dei realm e ticket di handoff monouso. I passaggi sotto preparano queste dipendenze.

  • I tuoi dati client Ultima Online. Moongate non li distribuisce. Il protocollo iniziale dei pacchetti è rivolto a ClassicUO con dati client UO 7.x.
  • Una shell Unix come Bash o Zsh per gli esempi. Installa la CLI Bitwarden, accedi al vault e sbloccalo con BW_SESSION impostata in questa shell. bw status deve indicare unlocked; non stampa le password memorizzate. Mantieni disabilitato il tracing della shell (set +x) prima di caricare segreti. Fermati se una lettura dal vault fallisce.
  • Un server PostgreSQL raggiungibile su cui puoi creare database. Gli esempi di questo repository usano PostgreSQL 16.
  • Un server Redis 7+ raggiungibile con autenticazione e maxmemory-policy noeviction. Mantienilo su una rete privata.
  • Due porte TCP libere in modalità standalone: login usa normalmente 2593 e game 2595. Il server ping UDP usa la porta 12000 quando è libera.
  • Per compilare dal sorgente: Git e l’SDK .NET 10 (global.json imposta solo rollForward su latestPatch e rifiuta gli SDK prerelease; non fissa alcuna versione). Node.js serve solo per lavorare sul sito della documentazione.

Per un primo server Linux, usa il percorso rilascio installato sotto. Scegli il sorgente solo per sviluppare Moongate; usa Docker se vuoi la topologia delle dipendenze fornita. Mantieni la directory di lavoro del percorso scelto per i suoi comandi:

  • Rilascio installato: completa l’installazione Linux. Esegui mgctl e moongate da qualsiasi directory con i comandi installati nel PATH.

  • Checkout del sorgente: esegui quanto segue dalla directory dove conservi i progetti, poi resta nella radice del repository per tutti i comandi dotnet di questa pagina:

    Terminal window
    git clone https://codeberg.org/moongate/moongate.git
    cd moongate
    git switch develop
    dotnet build Moongate.slnx -c Release

    develop include lavoro non rilasciato. Per riprodurre un rilascio, esegui il checkout del suo tag e usa la documentazione pubblicata per quella versione.

  • Docker: segui la ricetta completa login e realm, comprese configurazione, segreti, job dello schema e comandi di avvio. Torna a Collegarsi con ClassicUO per il profilo client. Per un singolo processo con database e Redis esistenti, usa Container standalone.

I passaggi sotto mostrano comandi completi per rilascio e sorgente. Esegui una sola variante a ogni passaggio. Usano /srv/moongate come radice dei dati del server.

  1. Preparare la radice. Assegna al server una directory propria:

    Terminal window
    sudo mkdir -p /srv/moongate && sudo chown "$USER" /srv/moongate
    mgctl init /srv/moongate

    Per un checkout del sorgente, sostituisci solo l’ultimo comando sopra con questo comando completo, eseguito dalla radice del repository:

    Terminal window
    dotnet run --project src/Moongate.Server -c Release -- --initialize-root --root-directory /srv/moongate

    Questo scrive config/moongate.toml con i valori predefiniti, crea logs/ e plugins/, copia l’SQL core del rilascio in migrations/, i file dei dati dello shard in data/, i template in templates/ e gli script di esempio in scripts/. Non richiede database o file client. Preparare una radice con mgctl descrive cosa accade su una radice già esistente.

  2. Modificare la configurazione. Apri /srv/moongate/config/moongate.toml e imposta percorso client ed entrambe le connessioni database. Mantieni le altre sezioni generate:

    [ultima]
    ultima_path = "/absolute/path/to/your/ultima-client"
    [persistence.accounts]
    connection_string = "$MOONGATE_ACCOUNTS_DATABASE"
    [persistence.realm]
    connection_string = "$MOONGATE_REALM_DATABASE"
    [redis]
    connection_string = "$MOONGATE_REDIS_CONNECTION_STRING"
    handoff_secret = "$MOONGATE_HANDOFF_SECRET"

    Usa un percorso client assoluto; i percorsi relativi si risolvono dalla directory di lavoro del processo, non dalla radice. Fornisci i due URI PostgreSQL, la stringa di connessione Redis e un segreto di handoff separato comune al cluster tramite le variabili d’ambiente referenziate. Il segreto deve avere almeno 32 byte UTF-8. Leggi le credenziali da Bitwarden; non scriverne i valori in questo file. Ogni processo login e game di un deployment richiede lo stesso endpoint Redis, password Redis e segreto di handoff. Il riferimento della configurazione elenca ogni impostazione.

    La cifratura del client UO è disabilitata per impostazione predefinita. Per un client che invia traffico cifrato, configura [network.encryption] usando la guida alla cifratura del client prima di avviare il server. Applica lo stesso profilo a processi login e game separati.

  3. Preparare PostgreSQL e Redis. Crea i database Accounts e World e credenziali specifiche per ruolo prima di avviare Moongate. Il server non crea mai database o ruoli. Usa un ruolo runtime solo DML e un ruolo schema separato; vedi Separare ruoli DDL e runtime.

    Prepara Redis con password forte, accesso dalla rete privata e maxmemory-policy noeviction. Tieni la password separata dal segreto di handoff. Per una topologia PostgreSQL e Redis eseguibile, usa l’ esempio Docker di login e realm. Standalone controlla entrambi i database; login controlla solo Accounts, e game solo il proprio Realm. Tutte e tre le modalità controllano anche Redis all’avvio.

  4. Applicare le migrazioni core con credenziali schema. Mantieni il server fermo. L’avvio rifiuta SQL in sospeso. Memorizza gli URI PostgreSQL completi schema e runtime in elementi Bitwarden distinti (gli esempi leggono il campo password di ciascun elemento). Sostituisci i nomi degli elementi sotto con i tuoi. Tutti e quattro gli URI devono puntare ai database creati al passaggio 3: Accounts per auth e Realm per world.

    Carica prima nella shell principale le credenziali che userà l’avvio normale:

    Terminal window
    export MOONGATE_ACCOUNTS_DATABASE="$(bw get password moongate-accounts-runtime-uri)"
    export MOONGATE_REALM_DATABASE="$(bw get password moongate-realm-runtime-uri)"
    export MOONGATE_REDIS_CONNECTION_STRING="$(bw get password moongate-redis-connection)"
    export MOONGATE_HANDOFF_SECRET="$(bw get password moongate-handoff)"

    Per il rilascio installato, applica l’SQL core revisionato in una subshell:

    Terminal window
    (
    set -e
    MOONGATE_ACCOUNTS_DATABASE="$(bw get password moongate-accounts-schema-uri)"
    MOONGATE_REALM_DATABASE="$(bw get password moongate-realm-schema-uri)"
    export MOONGATE_ACCOUNTS_DATABASE MOONGATE_REALM_DATABASE
    mgctl migrate apply --root-directory /srv/moongate --target auth
    mgctl migrate apply --root-directory /srv/moongate --target world
    )

    Per un checkout del sorgente, usa invece questa subshell dalla radice del repository:

    Terminal window
    (
    set -e
    MOONGATE_ACCOUNTS_DATABASE="$(bw get password moongate-accounts-schema-uri)"
    MOONGATE_REALM_DATABASE="$(bw get password moongate-realm-schema-uri)"
    export MOONGATE_ACCOUNTS_DATABASE MOONGATE_REALM_DATABASE
    dotnet run --project src/Moongate.Ctl -c Release -- migrate apply --root-directory /srv/moongate --target auth --migrations-directory ./migrations
    dotnet run --project src/Moongate.Ctl -c Release -- migrate apply --root-directory /srv/moongate --target world --migrations-directory ./migrations
    )

    Continua solo quando entrambe le applicazioni riescono. --target auth legge [persistence.accounts], --target world legge [persistence.realm] dalla configurazione della radice. Le sostituzioni temporanee riguardano solo la subshell: quando termina, la shell principale conserva gli URI runtime. Non avviare il server nella subshell delle migrazioni. Disabilita il tracing della shell (set +x) quando gestisci segreti e non stampare queste variabili.

    Come proprietario dello schema, concedi ai ruoli runtime CONNECT sul database, USAGE sugli schemi delle entità, SELECT, INSERT, UPDATE, DELETE sulle tabelle e USAGE e SELECT sulle sequenze. Concedi solo USAGE e SELECT sulla tabella della cronologia in moongate_migrations, senza scritture della cronologia. Applica anche privilegi predefiniti per le tabelle future; Separare ruoli DDL e runtime descrive il contratto e la ricetta Docker fornisce script di provisioning. Moongate non concede questi diritti autonomamente.

    Usa status al posto di apply per esaminare i file in sospeso. Ogni apply riuscito riporta il numero applicato: Applied {count} migration(s) to {target}. L’opzione Applicare all’avvio richiede privilegi DDL; mantienila disabilitata in questa sequenza con ruolo runtime.

  5. Avviare il server.

    Terminal window
    moongate --root-directory /srv/moongate

    Oppure, dalla radice del repository sorgente:

    Terminal window
    dotnet run --project src/Moongate.Server -c Release -- --root-directory /srv/moongate

    Entrambi i comandi usano le credenziali runtime della shell principale. Passa sempre --root-directory. Senza, o con una radice uguale alla directory del binario, il server rifiuta l’avvio con codice di uscita 2, perché un aggiornamento sostituisce quella directory. Un avvio riuscito registra Postgres connection successful una volta per database, poi servizi caricati ed endpoint associati. Un scripts/init.lua mancante è un avviso e avvia un ambiente di scripting vuoto; uno script bootstrap presente ma fallito impedisce l’avvio. Aggiungi script con Scrivere script Lua. I comandi della console interattiva sono elencati in Comandi del server.

  6. Creare il primo account. Nel terminale del server Login/Standalone in esecuzione, premi * per sbloccare la console locale. Questa console viene trattata come Administrator, quindi può creare un account prima che qualcuno abbia effettuato il login. Salva una nuova password in Bitwarden, poi digita questo nella console del server, sostituendo entrambi i segnaposto (non digitarlo nella shell del sistema operativo):

    account create <username> <password> Regular

    Le password non possono contenere spazi. La console maschera la password ed esclude questa riga dalla cronologia. Attendi una conferma con il nome dell’account e Regular; vedi account per livelli staff e disponibilità. L’accesso API non è necessario per il login di gioco. Per i tutorial staff sui contenuti, scegli GameMaster invece di Regular quando crei l’account.

  7. Collegarsi ed entrare nel mondo. Segui Collegarsi con ClassicUO sotto mentre il server rimane in esecuzione. Conferma di poter creare un personaggio e camminare prima di arrestarlo.

  8. Arrestarlo. Premi Ctrl+C e lascia terminare l’arresto. Dopo un avvio riuscito, l’host esegue un salvataggio finale del mondo prima di chiudere la persistenza PostgreSQL. Un avvio fallito o un game loop in errore non possono garantirlo. Non terminare il processo mentre lo attende. Il primo salvataggio dopo un avvio scrive l’intero mondo e richiede alcuni secondi (da 8 a 15 per quello distribuito); il server lo attende fino a 2 minuti dopo Ctrl+C o SIGTERM. Un salvataggio interrotto a metà non conferma nulla: ciò che è accaduto dall’ultimo salvataggio completato viene perso.

Sul computer da cui giocherai, installa ClassicUO e i tuoi dati client UO completi. Usa Edit Profiles → Add (o Create your first profile) nel launcher ClassicUO e crea un profilo chiamato Moongate. Le istruzioni ufficiali sui profili spiegano dove compaiono questi campi.

Campo del profilo Valore per la prima connessione locale
IP del server 127.0.0.1 quando client e server sono sullo stesso computer
Porta del server 2593, il listener login, non la porta game
Directory UO Percorso assoluto dei dati UO sul computer client
Versione client La versione 7.x di quei file UO, indicata dal loro client.exe; non inventare una versione
Cifratura Disabilitata (0), come nell’impostazione predefinita di Moongate

Lascia disabilitati salvataggio delle credenziali e login automatico; inserisci l’account del passaggio 6 nella schermata di login. Se avvii direttamente il client, questi sono i campi corrispondenti da unire al suo settings.json esistente mentre ClassicUO è chiuso:

{
"ip": "127.0.0.1",
"port": 2593,
"ultimaonlinedirectory": "/absolute/path/to/your/ultima-client",
"clientversion": "7.0.50.1",
"encryption": 0,
"saveaccount": false,
"autologin": false
}

7.0.50.1 è un esempio: sostituiscilo con la versione effettiva dei tuoi dati. Su Windows, usa il percorso client con backslash escapati per JSON. ClassicUO documenta queste opzioni negli argomenti di avvio; -settings può selezionare un file di impostazioni separato per questo shard.

  1. Avvia il profilo, inserisci nome account e password e collegati.
  2. Seleziona lo shard annunciato. Uno standalone predefinito elenca Moongate (o il nome shard configurato); la ricetta Docker elenca i suoi due realm. Login reindirizza il client alla porta del game selezionato.
  3. Seleziona uno slot personaggio vuoto, completa la creazione e scegli una città iniziale. Puoi invece selezionare un personaggio esistente.
  4. Attendi che compaia il mondo e muovi il personaggio. Vedere il personaggio e camminare conferma che login, redirect e ingresso nel mondo sono riusciti.

Per una connessione remota, inserisci nel profilo il nome DNS o IPv4 raggiungibile del server invece di 127.0.0.1. Prima di avviare Moongate, imposta realm_directory.advertised_address su un IPv4 raggiungibile dal client e advertised_port sulla porta game raggiungibile (2595 per questo esempio standalone). Lo standalone predefinito annuncia loopback, che funziona solo per un client sullo stesso computer. Associa il server a un’interfaccia locale adatta con network.listen_address e consenti/inoltra sia login TCP 2593 sia game TCP 2595 attraverso firewall/router. In Docker, pubblica queste porte e usa nell’annuncio la porta game rivolta all’host. Una sola porta login raggiungibile non permette di completare l’ingresso nel mondo. Vedi Configurazione e la Topologia Docker.

Se il login riesce ma lo shard è assente, esamina l’avvio game e il lease Redis. Se la scelta dello shard non permette di collegarsi, controlla indirizzo annunciato e porta game. Per errori di dati client e avvio, usa Problemi comuni all’avvio.

Per abilitare anche l’amministrazione facoltativa con un certificato TLS autofirmato, usa:

Terminal window
mgctl init /srv/moongate --generate-admin-certificate \
--admin-certificate-hosts "login.example.test"

Sostituisci l’host di esempio con il nome DNS o l’indirizzo IP usato dal client amministrativo; ometti --admin-certificate-hosts per il solo localhost. Questo crea certificates/admin.pfx e il pubblico certificates/admin.crt, e aggiorna esplicitamente quattro impostazioni [admin_api]. Il binding predefinito resta 127.0.0.1:2590. Vedi Configurazione del certificato per fiducia del client, accesso dalla rete privata e riuso di un’identità esistente.

mgctl è distribuito nei rilasci dopo 0.11.0; i rilasci da 0.7 a 0.11 hanno invece mgboot <root>. Su 0.6.0, avvia invece il server una volta: scrive la configurazione ed esce. Non serve altro, perché su 0.6.0 sia il server sia il runner delle migrazioni leggono l’SQL core da /opt/moongate/migrations, accanto all’eseguibile:

Terminal window
moongate --root-directory /srv/moongate

Quando la radice è configurata, scripts/run_server.sh esegue compilazione e avvio in un passaggio: pubblica una build Release del server e mgctl in dist/moongate, esegue mgctl init sulla radice per aggiungere i file distribuiti mancanti, e vi avvia il server:

Terminal window
scripts/run_server.sh --root-directory "$HOME/moongate"

--skip-build avvia la build già presente in dist/moongate, --build-only pubblica senza avviare, e ogni altra opzione va al server invariata, per esempio --pid-file-name game.pid.

Senza --root-directory lo script usa MOONGATE_ROOT. Ogni build elimina prima dist/moongate, quindi non conservarvi nulla di tuo. Lo script esporta MOONGATE_ROOT per una configurazione che nomina i percorsi tramite ${MOONGATE_ROOT}; un MOONGATE_ROOT già impostato resta invariato anche quando --root-directory indica un’altra radice, quindi rimuovilo dall’ambiente o passa lo stesso percorso.

Per eseguire un’altra istanza standalone, assegnale una radice propria, porte listener login e game distinte, realm_directory.realm_id e server_index distinti, e un proprio database realm. Riutilizzare ID e indice predefiniti del realm sostituisce il lease Redis della prima istanza. Non puntare mai due server alla stessa radice o allo stesso database realm.

Tutti i percorsi gestiti dal server sotto sono relativi a --root-directory:

Percorso Scopo
config/moongate.toml Creato se mancante; il normale avvio lo conserva, mentre la configurazione esplicita del certificato aggiorna quattro impostazioni [admin_api]
certificates/admin.pfx, certificates/admin.crt Identità TLS di amministrazione facoltativa di mgctl: PFX privato del server e PEM pubblico per la fiducia del client
migrations/auth/, migrations/world/ SQL core copiato da mgctl init; i plugin distribuiscono il proprio sotto plugins/
data/ File dei dati dello shard copiati da mgctl, letti all’avvio game e standalone; vedi File dei dati dello shard
templates/items/, loots/, carves/, mobiles/, npc_lists/, spawns/, decorations/, gumps/, books/, shops/ Template copiati da mgctl, caricati all’avvio game e standalone
logs/moongate-*.clef Eventi di log JSON strutturati, uno per riga
logs/errors/<id>.md Report di ogni eccezione registrata dal server, pronto da incollare in una issue
plugins/ Un bundle assembly per directory plugin; installali e gestiscili con mgctl plugin
scripts/ Sorgente Lua: un init.lua facoltativo, gli script mobile mobiles/<script_id>.lua, gli script oggetto items/<script_id>.lua, i moduli Lua condivisi in common/, gli script gump gumps/<id>.lua, gli script degli eventi stagionali events/<id>.lua, gli script delle abilità skills/<id>.lua, e i generati definitions.lua e .luarc.json
moongate.pid Identificatore del processo corrente
moongate.pid.lock File di lock usato per escludere un’altra istanza

La protezione PID viene acquisita prima di caricare la configurazione. Un PID attivo o un lock già detenuto rifiuta un altro avvio. Un PID obsoleto o malformato viene sostituito. La protezione controlla se il processo è vivo, non l’identità dell’eseguibile, quindi anche un PID riutilizzato può rifiutare l’avvio. Esamina quel processo prima di modificare il file PID. La normale pulizia rimuove il file PID se appartiene ancora a questo processo; il file .lock può restare dopo il rilascio del suo handle. La sola presenza non significa che il server sia in esecuzione.

I log console mostrano ora, livello, sorgente e messaggio. I log su file ruotano ogni giorno e a 10 MiB, conservando fino a 30 file. Per le metriche vedi Diagnostica; per operazioni sullo schema e salvataggi del mondo vedi Persistenza PostgreSQL.

La console mostra un’eccezione su una riga, messaggio e report, mai lo stack:

12:04:31.552 ERR BankService | Opening the bank failed: the bank box is missing - details: /srv/moongate/logs/errors/7f3a9c21aa.md (paste it into an issue)

Il report contiene versione e nome in codice Moongate, sistema, runtime .NET, ora, sorgente, livello, messaggio e l’intera eccezione, con eccezioni interne e stack, in Markdown. Aprilo e incollalo in una issue. Il nome è l’hash dell’eccezione, così la stessa registrata di nuovo, per esempio da un timer, riusa il report, che descrive la prima volta in cui è stata vista. Dopo 500 report non ne vengono scritti di nuovi, così eccezioni con messaggi variabili non possono riempire il disco; elimina i vecchi per fare spazio. In Docker il percorso è quello interno al container, sotto la radice montata. Un wrapper come AggregateException mostra il messaggio di ciò che contiene. I log .clef conservano l’eccezione completa di ogni evento.

Sintomo Controllo
Esce subito dopo aver scritto config/moongate.toml Previsto su una radice nuova: ultima_path è ancora ChangeMe. Continua con il passaggio 2
Errore del percorso client Imposta ultima.ultima_path su dati client reali e leggibili
Map ... needs map{n}.mul or map{n}LegacyMUL.uop, staidx{n}.mul and statics{n}.mul Al client manca quella mappa. Usa un client completo, o rimuovi la mappa da data/maps.toml
The Ultima path has neither MultiCollection.uop nor multi.idx and multi.mul La directory client è incompleta. Punta ultima.ultima_path a un’installazione client completa
tiledata.mul not found in the Ultima path La directory client è incompleta o non è una directory client. Punta ultima.ultima_path a un’installazione client completa
Errore di analisi o validazione TOML Correggi il campo indicato; i file esistenti non vengono sostituiti silenziosamente
Errore Postgres connection Il database non esiste, l’host è errato o il ruolo non può accedere. Dentro un container, localhost è il container stesso
Variabile di connessione mancante Esporta le variabili PostgreSQL e Redis referenziate dalle sezioni TOML attive
Errore di connessione Redis Controlla endpoint privato, credenziale, salute Redis e politica noeviction
Migrazioni in sospeso o modificate Esegui status e apply del runner per la destinazione indicata (passaggio 4), o abilita persistence.auto_apply_migrations. Non modificare mai un file applicato
Necessarie modifiche allo schema PostgreSQL Un’entità richiede DDL non fornito da alcuna migrazione. Genera e revisiona un file SQL versionato con --persistence-schema generate, poi applicalo con il runner a server fermo
Errore di binding della porta Controlla network.listen_address, disponibilità delle porte e indirizzi delle interfacce
Rilevata un’altra istanza Controlla PID e processo in esecuzione; usa una radice separata per un altro server
... file ... not found per un file dati, come maps.toml La radice non ha data/. Riesegui mgctl sulla radice: aggiunge i file mancanti e mantiene gli altri
InvalidDataException che indica un file dati Correggi la voce indicata dal messaggio; vedi le regole di validazione in File dei dati dello shard
Errore di avvio degli script Correggi scripts/init.lua; controlla nome file e riga dello script nel log

Creato con ❤️ da Tom

Offrimi un caffè