Docker: login e realm
Collega ClassicUO al login su 127.0.0.1:2593 dopo aver completato i passaggi sotto;
scegli uno dei realm e crea un personaggio per confermare l’ingresso nel mondo.
L’esempio Compose compila il sorgente corrente ed esegue un processo login, due processi game, PostgreSQL 16 e Redis 7.4 privato. PostgreSQL contiene database separati Accounts, Realm 1 e Realm 2. Redis contiene lease dei realm di breve durata e ticket di handoff del login monouso. Nulla nell’esempio pubblica Redis sull’host.
Preparare configurazione e segreti
Section titled “Preparare configurazione e segreti”Usa Docker Engine o Docker Desktop con Compose v2, Git, la tua directory client
Ultima Online leggibile e una shell Unix come Bash o Zsh. Gli esempi export e
$(...) sotto non usano la sintassi PowerShell. Installa la
CLI Bitwarden, configurala per il tuo vault,
accedi e sbloccala con BW_SESSION impostata nella stessa shell che eseguirà Compose.
Controlla la CLI e lo stato del vault senza stampare i segreti memorizzati:
command -v bw >/dev/nullbw statusContinua quando lo stato è unlocked. Segui le istruzioni della CLI collegate se
è locked o unauthenticated. Non abilitare il tracing della shell quando esporti
segreti. Parti da un checkout di questo repository, nella sua radice:
cd examples/docker/login-realmscp .env.example .envImposta UO_DATA_PATH in .env su una directory host assoluta. Se necessario,
cambia lì le porte rivolte all’host o i nomi PostgreSQL non segreti. Se cambi
GAME_1_PORT o GAME_2_PORT, imposta il corrispondente
realm_directory.advertised_port in config/game-1.toml o config/game-2.toml
su quella porta host; aggiorna anche il corrispondente *-admin.toml se usi
l’override di amministrazione. Altrimenti il redirect game invia i client alla
vecchia porta. Tieni password e chiavi di handoff fuori da .env, TOML e repository.
Crea nove record segreti distinti in Bitwarden: sette password PostgreSQL, una password Redis esadecimale di 64 caratteri e un segreto di handoff esadecimale indipendente di almeno 64 caratteri. Esportali nella shell che esegue Compose, sostituendo i nomi effettivi degli elementi Bitwarden:
export MOONGATE_POSTGRES_ADMIN_PASSWORD="$(bw get password moongate-postgres-admin)"export MOONGATE_ACCOUNTS_SCHEMA_PASSWORD="$(bw get password moongate-accounts-schema)"export MOONGATE_ACCOUNTS_RUNTIME_PASSWORD="$(bw get password moongate-accounts-runtime)"export MOONGATE_REALM_1_SCHEMA_PASSWORD="$(bw get password moongate-realm-1-schema)"export MOONGATE_REALM_1_RUNTIME_PASSWORD="$(bw get password moongate-realm-1-runtime)"export MOONGATE_REALM_2_SCHEMA_PASSWORD="$(bw get password moongate-realm-2-schema)"export MOONGATE_REALM_2_RUNTIME_PASSWORD="$(bw get password moongate-realm-2-runtime)"export MOONGATE_REDIS_PASSWORD="$(bw get password moongate-redis)"export MOONGATE_HANDOFF_SECRET="$(bw get password moongate-handoff)"Compose monta questi valori come segreti. L’entrypoint del server, che termina con mgctl run, costruisce
MOONGATE_REDIS_CONNECTION_STRING=redis:6379,password=... ed esporta
MOONGATE_HANDOFF_SECRET solo nell’ambiente del processo runtime. I TOML montati
contengono riferimenti ai nomi delle variabili, non ai valori. Redis legge la propria
password dal proprio mount segreto in un file di configurazione privato in memoria.
Anche gli URI di connessione PostgreSQL vengono assemblati in memoria da segreti
specifici per ruolo. Prima di avviare il server, l’entrypoint esegue mgctl sul
volume, aggiungendo ogni file dei dati dello shard distribuito
dall’immagine e mancante nel volume, conservando quelli già presenti.
Valida senza stampare il modello Compose espanso, poi compila:
docker compose config --quietdocker compose build login game-1 game-2 auth-schema-apply schema-preview schema-apply schema-apply-realm-2 migration-statusInput segreti mancanti fanno fallire la validazione indicando il nome. Usa
--quiet: renderizzare il modello espanso può esporre valori dall’estensione di
validazione anticipata di Compose.
Creare i database e revisionare l’SQL
Section titled “Creare i database e revisionare l’SQL”Lo script di inizializzazione PostgreSQL crea sei ruoli e tre database sul primo
volume vuoto. I ruoli schema possiedono DDL; i ruoli runtime hanno solo DML e
l’accesso necessario alle sequenze. L’inizializzazione non viene ripetuta quando
si ricrea un container. Non eseguire mai docker compose down --volumes su dati da conservare.
Applica l’SQL Accounts incorporato prima dell’avvio di login:
docker compose up -d --wait postgres redisdocker compose --profile schema run --rm auth-schema-applyRevisiona e applica l’SQL World per Realm 1, le tabelle core del mondo più quelle del plugin di esempio, poi le tabelle core del mondo per Realm 2:
docker compose --profile schema run --rm migration-statusdocker compose --profile schema run --rm schema-previewdocker compose --profile schema run --rm schema-applydocker compose --profile schema run --rm migration-statusdocker compose --profile schema run --rm schema-apply-realm-2Ogni server game richiede l’SQL World applicato prima dell’avvio; una migrazione in
sospeso interrompe l’avvio. Lo script di inizializzazione crea lo schema world
in entrambi i database Realm e concede a ogni ruolo runtime DML sulle sue tabelle
e uso delle sequenze ID. Un volume inizializzato prima dell’esistenza delle tabelle
core del mondo non ha quei permessi: esegui manualmente le istruzioni
provision_world_schema di postgres/init.sh per ogni database Realm, o ricrea
il volume usa e getta.
I job dello schema non avviano un client Redis. Usano il segreto PostgreSQL del
ruolo schema selezionato ed eseguono solo la destinazione richiesta. I TOML runtime
impostano auto_sync_schema = false e usano l’SQL core incluso in /app/migrations.
Per dettagli su scrittura e applicazione dell’SQL dei plugin, vedi
Generare, revisionare e applicare.
Avviare, osservare e arrestare
Section titled “Avviare, osservare e arrestare”docker compose up -d login game-1 game-2docker compose psdocker compose logs --tail 100 login game-1 game-2Crea un account nella console login (non nella shell host o in una console game):
docker compose attach loginPremi * per sbloccare i comandi. Salva una password in Bitwarden e digita questo,
sostituendo i segnaposto; le password non possono contenere spazi:
account create <username> <password> RegularAttendi la conferma con il nome del nuovo account e Regular. La console locale
ha permessi Administrator; i container solo game non registrano account.
Scollegati con la sequenza Docker Ctrl-P, Ctrl-Q così login resta in esecuzione.
Sull’host Docker, crea il profilo ClassicUO
con IP 127.0.0.1, porta login 2593, dati/versione del client e cifratura
disabilitata. Accedi con l’account, seleziona Moongate Realm 1 o Moongate Realm 2, crea un
personaggio e cammina nel mondo. Per giocare da remoto, imposta l’indirizzo server
del profilo e gli IPv4 annunciati da entrambi i game come descritto in
Topologia, e rendi raggiungibili le porte host 2593, 2595 e 2596.
Usa le porte host modificate se hai personalizzato .env. Se l’elenco dei realm
è vuoto, controlla log e lease sotto.
I tre servizi runtime attendono la salute di PostgreSQL e Redis. L’avvio controlla
il database del ruolo e la connessione Redis. I processi game pubblicano
moongate:realm:1 e moongate:realm:2, ciascuno con lease di 15 secondi rinnovato
ogni cinque secondi. Per esaminarli senza mostrare la password Redis:
docker compose exec -T redis sh -ec 'export REDISCLI_AUTH="$(cat /run/secrets/redis-password)"; redis-cli --raw HGETALL moongate:realm:1; redis-cli TTL moongate:realm:1'Le chiavi dovrebbero avere TTL positivi. Non usare FLUSHDB: Redis può contenere
altro stato Moongate di breve durata. Lo script smoke
dell’esempio controlla entrambi i lease oltre un intero intervallo di lease.
Ogni volume /data contiene PID, log, script e plugin di quel processo; non
condividere una radice tra server. Arresta o ricrea un game indipendentemente:
docker compose stop game-1docker compose up -d --no-build --force-recreate game-1docker compose down rimuove container e bridge privato ma conserva i volumi
nominati. I salvataggi del mondo non sono backup PostgreSQL; imposta separatamente
la politica di backup del database.
Topologia
Section titled “Topologia”| Servizio | Database | Endpoint client sull’host | Accesso Redis |
|---|---|---|---|
login |
Solo runtime Accounts | 127.0.0.1:2593 |
Lease e ticket di handoff |
game-1 |
Solo runtime Realm 1 | 127.0.0.1:2595 |
Il proprio lease e ticket di handoff |
game-2 |
Solo runtime Realm 2 | 127.0.0.1:2596 |
Il proprio lease e ticket di handoff |
redis |
Nessuno | Nessuna porta pubblicata | Solo bridge privato moongate |
| Job del profilo schema | Solo la destinazione schema selezionata | Nessuno | Nessuno |
Ogni server ha il proprio volume /data. PostgreSQL ha un volume persistente
separato. Redis non ha un volume persistente: ticket e lease sono intenzionalmente
effimeri. Un riavvio Redis invalida entrambi; i processi game in esecuzione
ripubblicano i lease. Un’interruzione Redis impedisce nuovi elenchi di realm e
handoff, mentre le sessioni game già ammesse continuano. Redis usa
maxmemory-policy noeviction, così la pressione di memoria rifiuta le scritture
anziché rimuovere silenziosamente ticket attivi.
game-1 usa lo stage sample-plugin del Dockerfile. Il suo plugin registra
sample_greeter.notes in Realm 1; i job dello schema esercitano lo stesso plugin
e SQL revisionato. game-2 usa l’immagine ordinaria. Login non ha credenziali del
database Realm, e i game non hanno credenziali Accounts. Tutti e tre condividono
una credenziale Redis e un segreto di handoff separato; questi server sono quindi
un unico dominio di fiducia privato. Non servono certificati dei peer o ACL per realm.
Il server ping UDP risponde sulla porta host 12000 (PING_PORT) solo da game-1:
i tre server condividono un indirizzo host, e un solo processo per indirizzo può
possedere la porta, quindi login e game-2 impostano enable_ping_server = false.
Dentro i container, login ascolta sulla porta 2593 e ogni game sulla 2595. Compose
mappa game 2 sulla porta host 2596, dichiarata anche da
realm_directory.advertised_port. L’indirizzo IPv4 annunciato predefinito è
127.0.0.1 per un client sull’host Docker. Per client remoti, imposta
MOONGATE_BIND_ADDRESS in .env su un IPv4 di un’interfaccia host (o 0.0.0.0
per pubblicare su tutte le interfacce); il valore predefinito 127.0.0.1 accetta
solo client locali. Imposta advertised_address in entrambi i TOML game su un IPv4
raggiungibile dal client. Consenti o inoltra le porte login e game pubblicate
attraverso firewall/router dell’host. Imposta lo stesso indirizzo in
game-1-admin.toml e game-2-admin.toml se usi l’override di amministrazione.
L’elenco 0xA8 ha indirizzi IPv4 ma non porte; 0x8C fornisce la porta del realm selezionato.
Protocollo di login
Section titled “Protocollo di login”Un login 0x80 riuscito riceve un elenco 0xA8 filtrato. 0xA0 seleziona un realm;
login invia 0x8C con indirizzo IPv4, porta e chiave monouso, poi chiude quella
connessione di login. Il client si riconnette alla porta game scelta, invia la
chiave grezza di quattro byte come seed, poi 0x91 con la stessa chiave e credenziali.
Game consuma il ticket da Redis, associa l’account alla sessione locale e risponde
con le funzionalità supportate (0xB9) e i personaggi salvati dell’account (0xA9)
con le città iniziali. La creazione salva un nuovo personaggio e i suoi oggetti
iniziali; selezionare un personaggio lo porta nel mondo di quel realm.
Smoke test usa e getta
Section titled “Smoke test usa e getta”Dalla radice del repository:
MOONGATE_SMOKE_UO_PATH=/path/to/ultima-client sh examples/docker/login-realms/smoke.shI server game caricano tiledata.mul, mappe e multi all’avvio, quindi lo script
richiede una vera directory client; si arresta con codice di uscita 2 quando
MOONGATE_SMOKE_UO_PATH non contiene tiledata.mul. Lo script crea un progetto
Compose univoco con volumi temporanei e credenziali sintetiche solo di processo.
Compila immagini locali, controlla ruoli PostgreSQL e comportamento dello schema,
applica l’SQL World a entrambi i realm e verifica che i ruoli runtime possano
scrivere oggetti del mondo, avvia tutti e tre gli host, verifica TTL positivi di
entrambi i lease Redis dopo un intero intervallo, e controlla l’arresto corretto.
Handoff UO 0x80→0x91 e riutilizzo dei ticket sono coperti dai test di integrazione
basati su Redis nella suite .NET.
Risoluzione dei problemi
Section titled “Risoluzione dei problemi”- Compose indica una variabile mancante: esporta il valore nominato da Bitwarden
nella stessa shell, o imposta
UO_DATA_PATHin.env. - Il controllo di salute Redis fallisce: esamina
docker compose logs redis, il mount segreto e l’indirizzo di rete privata del container. Redis non ha una porta host da verificare. - Login non ha realm: esamina
docker compose logs game-1 game-2e chiavimoongate:realm:*e TTL. Verifica che tutti e tre i container runtime condividano lo stesso servizio Redis e segreto di handoff. - L’avvio segnala SQL in sospeso: esegui il job del profilo schema corrispondente mentre il runtime interessato è fermo.
- Il client non entra nel game selezionato: conferma che
advertised_addresssia raggiungibile dal client, la porta host corrisponda adadvertised_port, e la chiave di redirect non sia scaduta o già usata. - Il container esce: esamina
docker compose ps -ae i log. Correggi l’errore database, Redis o configurazione prima di ricrearlo.
Endpoint di amministrazione privato
Section titled “Endpoint di amministrazione privato”La porta 2590 è riservata all’amministrazione gRPC facoltativa e resta disabilitata nella configurazione predefinita immagine/Compose. Usa TLS del server sulla rete privata; non servono mTLS o mapping di porte pubbliche. Vedi la guida all’amministrazione e la configurazione Compose facoltativa.
Creato con ❤️ da Tom
