Dane trwałe umożliwiają śledzenie i zapisywanie danych dla każdego gracza między sesjami gry. Persystencja danych działa na zasadzie przechowywania w Verse danych dla poszczególnych graczy, takich jak jego profil lub statystyki. Dane te mogą być następnie aktualizowane tyle razy, ile razy zmieni się ich wartość. Ponieważ dane te są możliwe do utrwalenia, będą utrzymywać się podczas sesji gry i będą dostępne za każdym razem, gdy gracz będzie online w grze. Aby dowiedzieć się więcej, patrz sekcja: Używanie persystencji danych w Verse.
Na tej stronie omówiono dobre praktyki dotyczące pracy z danymi trwałymi w Verse.
Używanie klas do późniejszego dodawania nowych pól
Obecnie jedynym typem danych możliwych do utrwalenia, które można zmienić po opublikowaniu wyspy, jest typ class, o ile nowe pola mają wartości domyślne. Oznacza to, że wczytanie zapisanych danych z poprzedniej wersji będzie zawierać nowe pola i ich wartości domyślne.
Przyjrzyjmy się przykładowi publikacji projektu z następującymi danymi możliwymi do utrwalenia.
player_profile_data := class<final><persistable>:
Class:player_class = player_class.Villager
XP:int = 0
Rank:int = 0Ponieważ projekt został opublikowany i jest aktualnie dostępny, to utrwalone dane graczy, którzy grali w grę, będą z nimi powiązane. Gdybyśmy dodali więcej pól do danych profilu gracza, takich jak liczba zadań i historia, dane możliwe do utrwalenia mogłyby wyglądać następująco w zaktualizowanym projekcie.
player_profile_data := class<final><persistable>:
Class:player_class = player_class.Villager
XP:int = 0
Rank:int = 0
CompletedQuestCount:int = 0
QuestHistory:[]string = array{}Utrwalone dane wszystkich graczy, którzy grali z pierwszą wersją klasy player_profile_data będą teraz zawierać nowe pola:
CompletedQuestCountz wartością0, co stanowi wartość domyślną, która została określona.QuestHistoryz pustą tablicą ciągów tekstowych, co stanowi wartość domyślną, która została określona.
To działa, ponieważ dla nowych pól podano wartość domyślną, aby móc zaktualizować starszą wersję danych.
Ponieważ tylko klasy mogą być aktualizowane po opublikowaniu projektu, zdecydowanie zalecamy użycie klasy jako typu wartości dowolnej zmiennej weak_map z zakresem modułu.
Aby uzyskać więcej informacji na temat tworzenia klas możliwych do utrwalenia, patrz: Typy możliwe do utrwalenia.
Używanie konstruktorów do częściowych aktualizacji
Jeśli korzystasz z klas, zalecamy użycie konstruktora, aby utworzyć nową instancję klasy, która zawiera zaktualizowany stan, ponieważ konstruktory pozwalają na częściowe aktualizacje klas.
Poniższy przykład pokazuje, jak można zaktualizować PlayerProfileDataMap. Funkcja GrantXP() pobiera aktualne dane danego gracza, a następnie wywołuje konstruktor MakePlayerProfileData(), aby utworzyć nową wersję danych jego profilu. Ponieważ dane źródłowe gracza są przekazywane do konstruktora wraz z nową wartością PD, tylko wartość PD zostanie zaktualizowana, podczas gdy wszystkie inne dane gracza pozostaną takie same.
MakePlayerProfileData<constructor>(Src:player_profile_data)<transacts> := player_profile_data:
Version := Src.Version
Class := Src.Class
XP := Src.XP
Rank := Src.Rank
CompletedQuestCount := Src.CompletedQuestCount
QuestHistory := Src.QuestHistory
GrantXP(Agent:agent, GrantedXP:int):void=
if:
W poprzednim przykładzie pokazano, jak zaktualizować jedno pole, ale w ten sposób można zaktualizować dowolną liczbę pól:
set PlayerProfileDataMap[Player] = player_profile_data:
QuestHistory := UpdatedSaveData.QuestHistory
CompletedQuestCount := OldData.CompletedQuestCount + 1
MakePlayerProfileData<constructor>(OldData)Wersjonowanie danych możliwych do utrwalenia
Zalecamy stosowanie wersjonowania w klasach możliwych do utrwalenia, aby wykryć wersję Instancji dla danych wcześniej zapisanych dla Gracza. Korzystając z wersji, można wykrywać i stosować migracje, jeśli definicja klasy możliwej do utrwalenia lub logika rozgrywki zmienia się w czasie.
Choć do oznaczenia wersji klasy utrwalonej można użyć wartości liczb całkowitych lub ciągu tekstowego, zalecamy jednak używanie wartości opcji do przechowywania odwołań do bieżących i poprzednich wersji danych. Rozważ poniższą konfigurację:
var SavedPlayerData:weak_map(player, player_data) = map{}
# A player data class containing optional fields of versioned player data. Only one of these
# optional values should contain a real value at any given time.
player_data := class<final><persistable>:
V1:?v1_player_data = false
V2:?v2_player_data = false
# Original version of player data.
W tym przypadku klasa player_data zawiera wartości opcji zarówno dla pierwszej, jak i drugiej wersji powiązanej klasy danych, które są reprezentowane przez klasy v1_player_data i v2_player_data. Zawsze powinna być ustawiona tylko jedna V1 lub V2, aby zapobiec sytuacji, w której gracze mają powiązanych z nimi wiele wersji danych.
Oryginalne dane gracza w V1 zawierają trzy pola wartości typu int. Wersja danych V2 zmienia wartość pola Czas gry na wartość zmiennopozycyjną oraz dodaje dwa nowe pola. Ze względu na to, że typ pola Czas gry uległ zmianie w wersji V2, będzie musiał zostać skonwertowany dla każdego gracza, który nadal ma dane starej wersji V1. Gdy gracz ze starymi danymi V1 dołącza do przygody, możesz użyć funkcji konstruktora pomocnika, aby zbudować nową klasę danych V2 w oparciu o jego stare dane, w następujący sposób:
# Create v1_player_data using existing v1_player_data.
MakeV1PlayerData<constructor>(SourceData:v1_player_data)<transacts> := v1_player_data:
XP := SourceData.XP
Rank := SourceData.Rank
Playtime := SourceData.Playtime
# Create v2_player_data using existing v2_player_data.
MakeV2PlayerData<constructor>(SourceData:v2_player_data)<transacts> := v2_player_data:
XP := SourceData.XP
Rank := SourceData.Rank
Może się zdarzyć, że zechcesz wymusić reset danych dla graczy dołączających do twojej wyspy. Można to zrobić poprzez ponowne przypisanie wartości domyślnej dla danych możliwych do utrwalenia w 'weak_map' dla wszystkich graczy i zmianę pola Version klasy. Jeśli używasz opcjonalnych wersjonowanych danych, możesz zresetować dane, ustawiając dla opcjonalnych pól wartość false.
Aby dowiedzieć się, czy dane gracza zostały już zresetowane, możesz sprawdzić wartość Version w danych możliwych do utrwalenia gracza, aby ustalić, czy jest ona najnowsza.
Testowanie, czy utrwalone dane znajdują się w limitach
Jeśli aktualizacja może wpłynąć na całkowity rozmiar utrwalonych danych, należy sprawdzić, czy utrwalone dane nadal mieszczą się w ramach ograniczeń systemu persystencji w Verse. Jeśli spróbujesz zaktualizować dane możliwe do utrwalenia i ich rozmiar przekroczy limity, pojawi się błąd czasu wykonywania programu Verse. Więcej informacji znajduje się w sekcji: Maksymalny rozmiar obiektu możliwego do utrwalenia.
Można sprawdzić, jak aktualizacje wpływają na całkowity rozmiar, korzystając z funkcji FitsInPlayerMap().
W poniższym przykładzie dane możliwe do utrwalenia zawierają tablicę ciągów tekstowych. Jeśli ta tablica kiedykolwiek stanie się zbyt duża, by zapisać ją w weak_map, co dzieje się, gdy FitsInPlayerMap() się nie powiedzie, ten przykład opróżnia tablicę i dodaje tylko ostatnio zapisany element.
# Construct and return a new player_profile_data with updated quest history.
SetQuestHistory(Src:player_profile_data, NewQuestHistory:[]string)<transacts>:player_profile_data =
NewData:player_profile_data = player_profile_data:
MakePlayerProfileData<constructor>(Src)
QuestHistory := NewQuestHistory
# Set a player's quest history in the PlayerProfileDataMap.
RecordQuestHistory(Agent:agent, QuestHistory:string):void=
if:
CheckSaveDataForPlayer[Agent]
Reakcja na dołączenie gracza do twojej wyspy
Kiedy nowy gracz dołączy do twojej wyspy, nie będzie miał automatycznie dodanego wpisu do możliwej do utrwalenia weak_map. Trzeba będzie dodać ten wpis w Verse.
W tym celu można albo sprawdzić, czy gracz jest już na liście weak_map za każdym razem, gdy uzyskujesz do niej dostęp, albo można dodać domyślne dane do zmiennej weak_map za każdym razem, gdy dołącza gracz, o czym można się dowiedzieć, subskrybując zdarzenie PlayerAddedEvent() w grze.
GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
# Later in your file
OnPlayerAdded(Player:player):void=
if:
not PlayerProfileDataMap[Player]
set PlayerProfileDataMap[Player] = player_profile_data{}