Dlaczego niewielka aktualizacja potrafi ważyć kilkadziesiąt giga? Jak powstają gigantyczne patche do gier
Wchodzimy na Steam, chcemy odpalić grę, w którą graliśmy wczoraj, a tu wjeżdża aktualizacja. Lista zmian jest krótka - poprawiono błąd jednego zadania, usunięto problem z animacją przeciwnika i naprawiono kilka przypadków wyrzucania gry do pulpitu. Brzmi jak drobiazgi, które powinny ważyć co najwyżej kilkaset megabajtów. Tymczasem klient rozpoczyna pobieranie 38 GB, dysk mieli kolejne pół godziny, a do instalacji potrzeba jeszcze 80 GB wolnego miejsca. Dlaczego tak jest?
Rozmiar aktualizacji nie mówi jednak wprost, ile rzeczy zmienili twórcy. Informuje przede wszystkim o tym, gdzie znajdowały się zmienione dane, w jaki sposób gra została spakowana oraz jak skutecznie platforma potrafi porównać starą wersję plików z nową. Ten sam błąd można poprawić patchem ważącym 200 MB albo 20 GB. Wszystko dlatego, że poprawka musiała przejść przez zupełnie inną strukturę danych.
W niniejszym tekście chciałbym więc odpowiedzieć na pytanie, jak to jest z tymi aktualizacjami? Dlaczego niektóre potrafią zajmować dziesiątki gigabajtów?
Patch zaczyna się długo przed naciśnięciem przycisku „pobierz”
Każda aktualizacja zaczyna się od znalezienia problemu. Czasem robi to tester przed premierą, czasem błąd wychodzi z telemetrii zbieranej z milionów uruchomień, a czasem gracze odkrywają kombinację, której nikt w studiu wcześniej nie przewidział. Dobry raport musi zawierać numer wersji, platformę, konfigurację sprzętową, miejsce wystąpienia problemu, zapis gry, logi oraz możliwie dokładną sekwencję czynności prowadzącą do błędu. To właśnie możliwość odtworzenia problemu jest najważniejsza. Jeżeli gra zawiesza się raz na sto prób, tester musi ustalić, co łączy te przypadki - konkretny przedmiot, wersja językowa, liczba klatek, stan zadania, utrata połączenia z serwerem czy może zapis pochodzący jeszcze z wersji 1.0. Przy produkcji pokroju Baldur’s Gate 3, gdzie jedna decyzja wpływa na dziesiątki późniejszych zdarzeń, pozornie prosty błąd może wymagać przeanalizowania całego łańcucha zależności.
Kiedy programista znajduje przyczynę, powstaje poprawka w kodzie, skrypcie zadania, tabeli danych albo samym zasobie. Następnie trzeba sprawdzić, czy naprawiając jedną rzecz, nie zepsuto pięciu kolejnych. To tak zwane testy regresji. Należy poprawione zadanie przejść na różnych zapisach, poziomach trudności, platformach i wersjach językowych, a zmienioną animację sprawdzić z różnymi modelami postaci, uzbrojeniem oraz liczbą klatek. Najgroźniejsze błędy nie zawsze powodują natychmiastowy crash. Czasem uszkadzają zapis i ujawniają się dopiero kilka godzin później. Dopiero wtedy tworzony jest nowy build. Kod zostaje skompilowany, a tekstury, modele, dźwięki, animacje, mapy i skrypty przechodzą przez proces nazywany „cookingiem”, czyli przygotowaniem danych w formacie odpowiednim dla konkretnej platformy. Osobny build powstaje dla PC, PlayStation i Xboxa, czasem również dla kilku generacji konsol. Na końcu system porównuje nową wersję z wydaną wcześniej i tworzy zestaw danych, który ma zostać dostarczony graczom.
Na PC twórcy mogą umieścić aktualizację na zamkniętej gałęzi Steam i przetestować ją dokładnie w takiej formie, w jakiej pobierze ją użytkownik. Dokumentacja Steamworks wprost zaleca wrzucenie nowego buildu na zabezpieczoną hasłem gałąź testową przed przeniesieniem go na kanał publiczny. Na konsolach dochodzi certyfikacja. Microsoft wymaga między innymi sprawdzenia stabilności, trybów sieciowych, działania po wybudzeniu konsoli i zgodności zapisów z nową wersją. Lista wymagań Xboxa pokazuje, że patch nie jest po prostu plikiem wrzuconym wieczorem na serwer przez programistę, który akurat został dłużej w biurze.
Jak kilka zmienionych plików zamienia się w 80 GB danych?
Współczesna gra rzadko składa się na dysku z setek tysięcy luźnych obrazków, modeli i nagrań. Dane są pakowane w większe archiwa, ponieważ łatwiej je kompresować, zabezpieczać i szybko odczytywać. Zamiast otwierać tysiące małych plików, silnik sięga do kilku dużych kontenerów. To szczególnie istotne w produkcjach projektowanych jeszcze z myślą o wolnych dyskach HDD. Problem zaczyna się wtedy, gdy trzeba wymienić mały element znajdujący się w środku takiego kontenera. SteamPipe dzieli każdy plik na fragmenty o wielkości około 1 MB. Podczas przygotowywania aktualizacji szuka fragmentów identycznych z poprzednią wersją i pozwala graczowi pobrać wyłącznie nowe lub zmodyfikowane części. W idealnej sytuacji zmiana tekstury zajmującej 20 MB oznacza więc pobranie mniej więcej tych 20 MB, a nie całego archiwum o rozmiarze 40 GB. Valve opisuje ten mechanizm szczegółowo w dokumentacji przesyłania buildów na Steam.
Tylko że wystarczy źle przygotowane archiwum, aby cały plan się posypał. Jeżeli zmieniony zasób zwiększy swoją objętość, może przesunąć pozycję wszystkich kolejnych danych. Zmieniają się adresy zapisane w spisie zawartości, granice fragmentów przestają się zgadzać, a Steam widzi tysiące nowych bloków, choć większość właściwych materiałów nadal jest identyczna. Valve ostrzega, że nawet modyfikacja wartości zajmującej 4 lub 8 bajtów potrafi w źle zorganizowanym pliku pakietowym doprowadzić do konieczności pobrania ponad połowy całego archiwum. Dlatego platforma zaleca utrzymywanie stałej kolejności zasobów, ograniczanie wielkości paczek i grupowanie danych według map, poziomów lub funkcji.
Unreal Engine pokazuje drugi ważny problem. Podczas tworzenia patcha silnik porównuje przygotowaną zawartość z poprzednim buildem, ale najmniejszą jednostką aktualizacji jest cały pakiet, na przykład plik .uasset albo .ulevel. Jeżeli zmieni się cokolwiek wewnątrz danego pakietu, do aktualizacji trafia cały plik. Poprawienie jednego parametru mapy może więc oznaczać ponowne dostarczenie całego zasobu poziomu, razem z danymi, których nikt nawet nie dotknął.
Stąd ogromna różnica między aktualizacją ważącą 3 GB, a taką zajmującą 80 GB. Jeżeli twórcy zmieniają kod wykonywalny, kilka skryptów, dane balansu i niewielkie elementy interfejsu, system może dostarczyć jedynie zmodyfikowane fragmenty. Część regulacji w grach sieciowych odbywa się wręcz po stronie serwera i nie wymaga pobierania niczego. Jeśli jednak aktualizacja wymienia mapę, oświetlenie, przerywniki, nagrania głosowe, animacje, tekstury albo duże archiwa, rozmiar gry rośnie błyskawicznie. Jedna nowa lokacja to nie tylko geometria, ale także materiały, dźwięki, dane kolizji, nawigacja przeciwników i często gigabajty tekstur.
Do tego wersje na różne urządzenia nie zawierają dokładnie tych samych plików. Zasoby są inaczej kompresowane i przygotowywane dla poszczególnych procesorów, układów graficznych oraz systemów pamięci. Dobrym przykładem był sezon czwarty Call of Duty: Black Ops Cold War. Ta sama aktualizacja zajmowała około 15,5 GB na PS4, 30 GB na PS5, 17,7 GB na Xboksie One i 31 GB na Xbox Series X|S. Na PC pobierano 21,6 GB bez pakietu HD albo 30,7 GB z materiałami wysokiej rozdzielczości. Treść aktualizacji była zbliżona, ale sposób jej zbudowania już nie. Znaczenie mają także języki, opcjonalne kampanie i paczki tekstur. Nagrania dialogów potrafią zajmować znacznie więcej miejsca niż sam kod, dlatego dobrze zaprojektowana gra pozwala instalować tylko wybraną wersję językową. Call of Duty umożliwiło również usuwanie kampanii, trybu wieloosobowego, operacji specjalnych czy tekstur HD. To nie jest kosmetyczna funkcja menu, lecz decyzja dotycząca architektury całej instalacji. Zasoby muszą zostać rozdzielone tak, aby usunięcie jednego modułu nie pozbawiło pozostałych wspólnych plików.
Pobranie patcha, jego instalacja i końcowy rozmiar to trzy różne liczby
Gracz widzi najczęściej wielkość pobierania, ale po ściągnięciu danych rozpoczyna się właściwe patchowanie. System rozpakowuje nowe fragmenty, odczytuje stare archiwa, tworzy ich zaktualizowane wersje, sprawdza sumy kontrolne i dopiero wtedy usuwa niepotrzebne pliki. Żeby nie zniszczyć całej instalacji w przypadku braku prądu, błędu zapisu albo przerwania procesu, przez pewien czas stara i nowa wersja danych mogą istnieć obok siebie. Dlatego patch ważący 10 GB potrafi potrzebować kilkudziesięciu gigabajtów wolnej przestrzeni.
Spektakularnym przykładem był piąty patch do Baldur’s Gate 3. Pobranie zajmowało około 30 GB, ale Larian informowało o konieczności posiadania mniej więcej 130 GB wolnego miejsca. Graczom z pełnymi dyskami studio zalecało usunięcie gry i ponowne pobranie całej, już zaktualizowanej wersji. Nie oznaczało to, że produkcja nagle urosła o 130 GB. Przestrzeń była potrzebna do przeprowadzenia operacji na dużej części istniejących danych. Sam patch rzeczywiście był ogromny, bo dodawał epilogi, dwa nowe tryby i rozległe poprawki wydajności. Jeszcze bardziej absurdalnie wyglądało to przy wspomnianym Black Ops Cold War. Aktualizacja na PC ważyła od 21,6 do 30,7 GB, ale Activision ostrzegało, że podczas kopiowania może wymagać dodatkowych 60,6 do 92,1 GB przestrzeni. Te dane po zakończeniu instalacji miały zostać odzyskane. To również wyjaśnia sytuację, w której internet dawno przestał pracować, a Steam, Battle.net lub konsola nadal przez kilkanaście minut pokazują „instalowanie”. Wąskim gardłem nie jest wtedy łącze, lecz procesor, szybkość dysku oraz ilość danych wymagających odczytania i ponownego zapisania.
Duży patch może paradoksalnie sprawić, że gra zajmie mniej miejsca. Aktualizacja 14.40 do Fortnite ważyła na PC około 27 GB, ale po jej instalacji cała produkcja skurczyła się o ponad 60 GB. Epic przebudowało organizację danych, poprawiło czasy ładowania i przygotowało strukturę pod mniejsze przyszłe aktualizacje. Podobny zabieg przeprowadziło później Call of Duty: jednorazowa aktualizacja Warzone zajmowała nawet 52-57,8 GB, lecz dzięki optymalizacji instalacja Modern Warfare i Warzone malała później o ponad 30 GB. Activision wyjaśniło wtedy, że większy download był ceną za przebudowę systemu zarządzania zawartością. Dlaczego jedna gra pobiera więc 3 GB, a druga 80 GB? Pierwsza mogła zmienić kilka dobrze odseparowanych pakietów, skryptów i fragmentów kodu. Druga mogła przebudować mapy, dodać materiały kolejnego sezonu, wymienić duże archiwa albo zostać spakowana w sposób, który uniemożliwia utworzenie skutecznej aktualizacji różnicowej. Waga patcha zależy od rodzaju zmienionych zasobów, ich rozmieszczenia, kompresji, możliwości platformy i ilości danych, które trzeba zastąpić. Liczba pozycji w patch notes ma tutaj zaskakująco niewielkie znaczenie.
Nie każdy gigantyczny patch jest zatem dowodem lenistwa deweloperów. Czasem wynika z rozsądnej przebudowy plików, która później oszczędzi graczom dziesiątki gigabajtów. Czasem jest ceną za dużą mapę, nowy dubbing albo setki poprawionych animacji. Ale zdarzają się również aktualizacje rozdęte przez źle zaprojektowane archiwa i wieloletni dług technologiczny. Sam komunikat „naprawiono kilka błędów” nie pokazuje całej prawdy, ponieważ patch nie przenosi listy zmian. Musi przebudować te fragmenty gry, w których zmiany zostały zapisane. A różnica między jednym plikiem a połową produkcji potrafi wynosić dokładnie te brakujące 80 GB.
Przeczytaj również
Komentarze (7)
SORTUJ OD: Najnowszych / Najstarszych / Popularnych