Salta al contenuto principale

Come testare le migrazioni dei salvataggi Unity con stati storici dei giocatori

Originale Persistium grafico di migrazione che mostra punti di controllo di salvataggio storici, passaggi di trasformazione e risultati ispezionati.

Un processo pratico, local-first per le prove Unity salvare le migrazioni contro le forme reali dello stato storico prima che un aggiornamento raggiunga i giocatori.

Il test del conto pulito manca l'importante difetto

Una nuova costruzione Unity può sembrare perfetta quando inizia con un profilo vuoto. Un account fresco non ha campi in pensione, missioni semifinite, riferimenti a catalogo in cache, vecchi stack di inventario, acquisti interrotti, o valori validi sotto un precedente regolamento.

Il lavoro di migrazione dovrebbe iniziare da una domanda più forte: può questo esatto stato storico entrare nella costruzione attuale, preservare il significato che ancora conta, e o diventare stato attuale valido o fallire in un modo che è utile per diagnosticare? Questa è una domanda di prodotto tanto quanto una domanda di serializzatore. Un campo può deserializzare con successo pur violando una regola di progressione, puntando al contenuto mancante, o cambiando un diritto di acquisto.

Questo articolo descrive un primo processo di prova locale. Non è volutamente una promessa che uno strumento può capire ogni Unity configurazione di serializzazione, backend, o integrazione della piattaforma. I modelli di dati Unity variano, e un gioco può aggiungere la propria crittografia, sincronizzazione cloud, format binario, o adattatori personalizzati. La parte durevole è la disciplina di test: preservare le forme storiche, copiarle prima di mutazione, rendere esplicito il percorso di migrazione

Costruisci un corpus storico

Ogni dispositivo ha bisogno di un identificatore stabile, la versione che lo ha prodotto, una breve etichetta di scenario, una dichiarazione di formato e una classificazione della privacy. L'etichetta di scenario conta perché una suite di migrazione dovrebbe includere stati significativi, non solo casuale JSON. Esempi includono un nuovo giocatore, un giocatore ad un limite di capitolo, un inventario al suo limite di stack, un utente di ricompensa interrotto, un vecchio profilo di evento

Copia l'apparecchio di una cartella di lavoro temporanea, registra il suo hash di checksum o contenuto, eseguire l'adattatore, e mantenere l'output risultante separatamente. Questo trasforma un risultato sorprendente nella prova che qualcun altro può riprodurre.

Un corpus storico può essere sintetico, acconsentito, accuratamente rifatto, o generato da scenari deterministici. La chiave è che mantiene i casi strutturali e semantici del bordo che importa. La privacy non è un ripensamento: rimuovere identificatori diretti, identificatori di dispositivo, informazioni di contatto, gettoni di pagamento, ricevute grezze e segreti prima che un apparecchio diventi ampiamente accessibile.

Disegnare il grafico di migrazione prima di scrivere un convertitore

I numeri di versione non sono un piano di migrazione. Un gioco può passare da 1 a 2, da 2 a 3 e da 3 a 4, per poi incontrare un giocatore che è passato direttamente da 1 a 4. Può distribuire una correzione rapida che modifica solo un record annidato dell’inventario oppure ritirare un campo ancora richiesto dai vecchi contenuti. Un grafo rende visibili queste scelte: ogni nodo è uno schema di salvataggio o una versione semantica; ogni arco è una trasformazione esplicita o un rifiuto intenzionale; ogni arco ha precondizioni e postcondizioni.

Per ogni versione di avvio supportata, decidere se le catene di gioco adiacenti si trasforma, utilizza una trasformazione diretta, o richiede un aggiornamento lato servizio prima del carico del cliente. Non lasciare le versioni saltate a qualsiasi cosa accada quando i campi sono assenti. Una catena può essere più sicura quando ogni passo è piccolo e testato. Una trasformazione diretta può essere più sicura quando i formati intermedi non sono mai stati destinati ad essere osservabili.

Grafico di migrazione originale con cinque nodi di stato collegati da percorsi di conversione supportati espliciti.
Un grafico di migrazione fa saltare la versione, percorsi supportati e punti di ispezione visibili prima che il codice li nasconda.

Il grafico dovrebbe includere più del tipo serializzato. Le dipendenze del catalogo dei contenuti di registrazione, le versioni di configurazione, le bandiere di funzionalità che influenzano l'interpretazione e la versione di applicazione corrente. Un salvataggio può essere sintatticamente attuale ma non valido perché un elemento di riferimento non esiste più o una regola cambia significato.

Rendere i viaggi rotondi di salvataggio osservabili

Una volta che un apparecchio entra nel percorso di migrazione, eseguire un giro completo: caricare l'ingresso storico, migrarlo, costruire il modello in-game, salvarlo utilizzando l'attuale scrittore, quindi caricare l'output corrente di nuovo. Il primo carico verifica se un record storico può entrare nel sistema. Il secondo carico verifica se l'attuale scrittore ha creato un risultato stabile e auto-coerente.

L'uguaglianza cruda è spesso l'affermazione sbagliata. Si possono aggiungere nuovi default, l'ordine del campo può cambiare, i timestamp possono essere rinfrescati, e le collezioni possono ricevere ordini stabili. Definire un normalizzatore per i valori che sono intenzionalmente non semantici, quindi confrontare il resto. Il normalizzatore stesso merita la revisione; non deve diventare una lista di ignorare la coperta che nasconde la perdita reale.

Se una catena di migrazione ha tre transizioni, non riferire solo che il carico finale non è riuscito. Cattura lo stato dopo ogni punto di controllo, convalidarlo, e fermarsi al primo limite che viola un invariante. Questa localizzazione converte un rapporto vago “vecchio salvare rotto” in un bordo di migrazione specifico, fissazione e differenza di stato.

Invarianti specifici per giochi espressi

Il gioco deve fornire i propri invarianti. Scriverli in lingua che si collega ai risultati del giocatore: un livello è all'interno della gamma corrente; una quantità di inventario è non negativo e limitato; ogni elemento di proprietà ha una definizione conosciuta o un fallback di eredità esplicito; un tutorial completato rimane completato; la valuta è finita; un riferimento di ricerca appartiene ad un catalogo corrente; un diritto di mondo è unico.

Invarianti strutturali rispondono se esistono campi e tipi richiesti. Invarianti di sicurezza rispondono se i dati sono finiti, delimitati e sicuri da visualizzare. Quando un dispositivo non riesce, lo strato dice al team se ha bisogno di un cambio serializzatore, un catalogo, una decisione di regole o una politica di recupero.

Utilizzare mutazioni deterministiche per rendere più difficile il funzionamento del corpus. Iniziare da un dispositivo noto e rimuovere deliberatamente un campo, duplicare un documento di inventario, sostituire un riferimento del catalogo, saltare una versione, riordinare una lista di azione, o alterare una quantità di limite. Ogni mutazione dovrebbe avere un risultato previsto: migrare con un default documentato, rifiutare con una categoria diagnostica, quarantena per il recupero, o richiedere una riparazione dei dati approvata dal proprietario.

Ridurre un risparmio di guasto senza perdere il bug

Gli stati dei giocatori di grandi dimensioni sono difficili da comprendere. Se un insieme di dati di test contiene centinaia di campi e una migrazione fallisce, uno sviluppatore può impiegare ore a leggere dati non pertinenti. La minimizzazione del controesempio riduce i dati di test preservando la violazione dell’invariante. Rimuovere un gruppo di campi, ripetere la stessa migrazione deterministica e mantenere la rimozione solo se l’errore si verifica ancora. Quindi suddividere le raccolte, ridurre i valori verso i limiti e ridurre la cronologia delle azioni se il comportamento di riproduzione è rilevante.

Visiva anti-riduzione originale che mostra un grande stato ristretto attraverso partizioni ripetibili in un dispositivo di regressione minimo.
Il processo di riduzione conserva il fallimento invariante mentre si rimuove lo stato non correlato.

Riduzione richiede riproducibilità. Bloccare l'hash dell'apparecchio, la versione di migrazione, la versione dell'adattatore, il seme deterministico, l'istantanea di configurazione rilevante e l'invariante previsto. Se l'ambiente cambia durante ogni retry, un riduttore può produrre uno stato più piccolo per un problema diverso.

Dare ad ogni apparecchio un nome stabile, una breve spiegazione del fallimento, l'invariante che conserva, e una classificazione attesa. Un dispositivo di regressione è prezioso perché rende una routine di confine precedentemente sorprendente. Non dovrebbe diventare una fonte nascosta di dati del giocatore di produzione o un catch-all per guasti non spiegati.

Metti la prova in CI senza renderla rumorosa

Eseguire un sottoinsieme rappresentativo veloce su ogni cambiamento rilevante: bordi di migrazione attuali, stati storici ad alto rischio e un piccolo insieme di mutazioni deterministiche. Eseguire il corpus completo su un lavoro programmato, candidato di rilascio, o cambiare che tocca serializzazione, progressione, interpretazione del catalogo, sincronizzazione del cloud, o codice salvaformat.

Segnala l'ID di fissaggio, l'avvio e le versioni di destinazione, il bordo di migrazione, lo strato invariante, il primo checkpoint non valido e un percorso per il manufatto o il diff rosso. Evitare di stampare i contenuti del profilo completo nei registri. Quando un guasto è nuovo, conservare le informazioni minime necessarie per riprodurlo localmente; quando è previsto, registrare che la classificazione in modo tale rilascio non normalizza silenziosamente un rischio conosciuto.

La decisione di rilascio dovrebbe indicare che cosa significa l'evidenza. I test di migrazione di passaggio supportano un reclamo limitato: il corpus testato e i percorsi dichiarati si comportano come previsto in quell'ambiente. Non dimostrano che ogni giocatore salva, dispositivo o servizio esterno si comportano in modo identico.

Controllo pratico

  • Tenere un corpus storico versioned con etichette scenario e classificazioni privacy.
  • Copia gli ingressi prima della migrazione e conserva le ciglia per gli artefatti di origine e di uscita.
  • Disegnare bordi di migrazione supportati, tra cui politica di conversione saltata e rifiuti espliciti.
  • Convalida invarianti strutturali, di riferimento, di dominio e di sicurezza dopo ogni checkpoint.
  • Uscita migrata di andata e ritorno attraverso l'attuale scrittore e lettore.
  • Utilizzare mutazioni deterministiche per testare i campi mancanti, i record duplicati, i contenuti in pensione e i valori limite.
  • Ridurre i fallimenti riproducibili in dispositivi di regressione revisionati con manifesti di rigioco.
  • Eseguire sottoinsiemi focalizzati sul rischio continuamente e il corpus completo prima dei cambiamenti sensibili al rilascio.
  • Mantenere i registri e gli artefatti redatti; non trasformare CI in un archivio di dati del giocatore.

Domande frequenti

Abbiamo bisogno di risparmi reali per testare la migrazione?

No. Le forme storiche reali sono utili, ma il corpus può essere sintetico o accuratamente rifatto. Ciò che conta è preservare le combinazioni di stato difficili: contenuto in pensione, progressione lunga, transizioni interrotte, dati facoltativi e valori a confini significativi. Se i dati di produzione vengono utilizzati per scoprire un caso di bordo, derivano una fissazione sicura che mantiene solo la proprietà sotto test.

Ogni vecchia versione dovrebbe rimanere supportata per sempre?

Non necessariamente. Il supporto è una decisione di prodotto e di funzionamento. L'importante è rendere esplicito il limite: quali versioni migrano, che richiedono un aggiornamento precedente, che vengono rifiutati, e come il giocatore è informato. Un percorso accidentale non documentato è l'opzione meno sicura perché non è supportato né chiaramente rifiutato.

Un test serializzato può sostituire i test di gioco?

I test di Serializer stabiliscono che i dati possono essere letti e scritti secondo le forme dichiarate. I test di gioco e di dominio stabiliscono che lo stato risultante ha senso nelle regole attuali.

Qual è il ruolo di Persistium qui?

Persistium è una famiglia di prodotti locali-prima affidabilità in fase di sviluppo. La guida in questa nota è consulenza ingegneristica pratica, non un reclamo che un prodotto pubblico Persistium è disponibile o che ha diagnosticato i dati reali del cliente.

Sistema correlatoEsplorare Persistium