Ahoj, jmenuji se Sergey Kachan a jsem klientský vývojář na projektu War Robots. War Robots je kolem po mnoho let, a během této doby hra nahromadila obrovské množství obsahu: roboty, zbraně, drony, titány, piloti, a tak dále. Dnes budu mluvit o tom, jak jsou bilance strukturovány v našem projektu, co se s nimi stalo během posledních 11 let a jak jsme se s tím vypořádali. Vyvážení v projektu Jako každý jiný projekt, War Robots lze rozdělit do dvou částí: meta a jádro hry. je jakákoli činnost, která přesahuje základní herní cyklus, ale stále ovlivňuje hru.To zahrnuje nákup a upgrade herního obsahu, účast na společenských nebo událostech. Meta gameplay (metagaming) je hlavní opakující se cyklus akcí, které hráč provádí ve hře, aby dosáhl svých cílů. Core gameplay (core gameplay loop) Každá část projektu potřebuje svou vlastní rovnováhu, takže rovněž rozdělujeme rovnováhy do dvou kategorií – meta a jádro. Válečné roboty se také nazývají které vyžadují vlastní oddělené rovnováhy. Skirmish modes a je modifikace stávajících režimů nebo map s různými charakteristikami nebo pravidly. Skirmish režimy jsou často založené na událostech, které jsou k dispozici hráčům během různých svátků, hlavně pro zábavu. Skirmish mode Takže celkem máme 4 rovnováhy: 2 pro výchozí režim a 2 pro režim Skirmish. Během 11 let, War Robots nahromadila spoustu úžasného obsahu: 95 robotů 21 titánů 175 různých zbraní 40 dronů 16 mateřství obrovské množství skins, remodelů, modulů, pilotů, věží, konečných verzí obsahu a map A jak si dokážete představit, pro celou tuto práci potřebujeme ukládat informace o chování, statistice, dostupnosti, cenách a mnohem, mnohem více. V důsledku toho se naše bilance zvětšily na nepřiměřenou velikost: Default mode Skirmish mode Meta balance 9.2 MB 9.2 MB Core balance 13.1 MB 13.1 MB Meta balance 9.2 MB 9.2 MB Core balance 13.1 MB 13.1 MB Po několika rychlých výpočtech jsme zjistili, že hráč bude muset stáhnout To je docela hodně! 44.6 MB Opravdu jsme nechtěli donutit hráče ke stažení tak velkého množství dat pokaždé, když se zůstatek změní. Jen aby vám připomněl: Vojenské roboty dosáhly V roce 2024 bylo naše měsíční aktivní publikum , a Každý den se přihlašuje. 300 million registered users 4.7 million people 690 thousand players Nyní si představte množství dat. Hodně, ne? Mysleli jsme si to taky. Takže jsme se rozhodli udělat vše, co bylo v našich silách, abychom snížili velikost našich bilanců! Hledání problému Prvním krokem bylo analyzovat rovnováhy a pokusit se zjistit: "Co zabírá tolik místa?" Ruční procházení vším bylo poslední věcí, kterou jsme chtěli udělat - to by trvalo věky.Tak jsme napsali sadu nástrojů, které shromažďovaly a agregovaly všechny informace, které jsme potřebovali o rovnováze. Nástroj by vzal rovnovážný soubor jako vstup a pomocí odrazu iteroval všechny struktury, shromažďoval údaje o tom, jaké typy informací jsme uložili a kolik místa každý zabíral. Výsledky byly odrazující: Meta rovnováha % in balance Usage count String 28.478 % 164 553 Int32 27.917 % 161 312 Boolean 6.329 % 36 568 Double 5.845 % 33 772 Int64 4.682 % 27 054 Custom structures 26.749 % — String 28 478 % 164 553 Kč Int32 27 917 % 161 312 Kč Boolean 6 329 % 36 568 Kč Double 5 845 % 33 772 Kč Int64 4 682 % 27 054 Kč Custom structures 26 749 % — Základní rovnováha % in balance Usage count String 34.259 % 232 229 Double 23.370 % 158 418 Int32 20.955 % 142 050 Boolean 5.306 % 34 323 Custom structures 16.11 % — String 34 259 % 232 229 Kč Double 23 370 % 158 418 Kč Int32 20 955 % 142 050 Kč Boolean 5 306 % 34 323 Kč Custom structures 16 11 % — Po analýze situace jsme si uvědomili, že A o tom se muselo něco udělat. strings were taking up far too much space Ten skenoval soubor bilance a vytvořil mapu všech řetězců spolu s počtemkrát, kdy byl každý z nich duplikován. Výsledky nebyly ani povzbudivé.Některé řetězce byly opakovány desítky tisíckrát! Našli jsme problém, teď je otázka: jak to napravit? Optimalizace rovnováhy Z zřejmých důvodů jsme se nedokázali zbavit řetězců úplně. Řetězce se používají pro věci jako lokalizace klíčů a různých ID. Myšlenka byla tak jednoduchá, jak se dostala: Vytvořte seznam jedinečných řetězců pro každou rovnováhu (v podstatě dedikované úložiště). Odesílejte tento seznam spolu s údaji. public class BalanceMessage { public BalanceMessageData Data; public StringStorage Storage; public string Version; } StringStorage je v podstatě obal kolem seznamu řetězců. Když budujeme úložiště řetězců, každá rovnovážná struktura si pamatuje index řetězce, který potřebuje. public class StringStorage { public List<string> Values; public string GetValue(StringIdx id) => Values[id]; } Namísto toho, abychom struny sami procházeli uvnitř rovnovážných struktur, začali jsme procházet indexem toho, kde je struna uložena v úložišti strun. Předtím než: public class SomeBalanceMessage { public string Id; public string Name; public int Amount; } Po tom: public class SomeBalanceMessageV2 { public StringIdx Id; public StringIdx Name; public int Amount; } StringIdx je v podstatě jen obal kolem int. Tímto způsobem jsme zcela eliminovali přímé přenosy řetězců uvnitř rovnovážných struktur. public readonly struct StringIdx : IEquatable<StringIdx> { private readonly int _id; internal StringIdx(int value) {_id = value; } public static implicit operator int(StringIdx value) => value._id; public bool Equals(StringIdx other) => _id == other._id; } Tento přístup snížil počet řetězců o desítkykrát. String usage count String usage count Before After Meta balance 164 553 10 082 Core balance 232 229 14 228 Before After Meta balance 164 553 Kč 10 082 Kč Core balance 232 229 Kč 14 228 Kč Není to špatné, že? Ale to byl teprve začátek – tam jsme se nezastavili. Přepracování datového protokolu Pro přenos a zpracování rovnovážných struktur jsme používali . MessagePack MessagePack je binární formát serializace dat navržený jako kompaktnější a rychlejší alternativa k JSON. je určen pro efektivní výměnu dat mezi aplikacemi nebo službami, což umožňuje významné snížení velikosti dat - zvláště užitečné tam, kde je výkon a šířka pásma důležité. Zpočátku přišel MessagePack ve formátu podobném JSON, kde byly použity data To je jistě pohodlné, ale také poměrně prostorově náročné, takže jsme se rozhodli obětovat nějakou flexibilitu a přejít na . string keys binary byte array Předtím než: public class SomeBalanceMessage { [Key("id")] public string Id; [Key("name")] public string Name; [Key("amount")] public int Amount; } Po tom: public class SomeBalanceMessageV2 { [Key(0)] public StringIdx Id; [Key(1)] public StringIdx Name; [Key(2)] public int Amount; } Také jsme odstranili všechny prázdné sbírky – namísto jejich odesílání nyní přenášíme nulové hodnoty, což snížilo celkovou velikost dat a dobu potřebnou pro serializace a deserializace. Testování změn Zlatým pravidlem dobrého vývoje (a ten, který vám ušetří spoustu nervů) je vždy implementovat nové funkce způsobem, který vám umožní rychle je vrátit, pokud se něco pokazí. Během vývoje jsme se museli ujistit, že všechna data byla správně přenášena. Staré i nové rovnice – bez ohledu na formát nebo strukturu – musely produkovat přesně stejné hodnoty. A pamatujte, že optimalizované rovnice změnily svou strukturu drasticky, ale to nemělo mít vliv na nic jiného než na jejich velikost. Abychom toho dosáhli, napsali jsme pro každou rovnováhu velké množství jednotkových testů. Zpočátku jsme srovnávali všechna pole „hlavně“ – kontrolovali jsme každou z nich explicitně. To fungovalo, ale bylo to časově náročné a i nejmenší změna rovnováhy by přerušila testy a nutila nás je neustále přepisovat. Nakonec jsme toho měli dost a přišli jsme s pohodlnějším testovacím přístupem pro porovnání rovnováhy. Reflexe přišla opět na záchranu. Vzali jsme dvě verze struktur rovnováhy, např. SomeBalanceMessage a SomeBalanceMessageV2, a iterovali nad nimi – porovnávali jsme počty polí, názvy a hodnoty. Pokud se něco nezhodovalo, sledovali jsme problém. Výsledky optimalizace Díky těmto optimalizacím se nám podařilo snížit jak velikost souborů přenášených přes síť, tak čas potřebný na jejich deserializace na klientovi. Velikost souboru Old balances Optimized balances Profit Meta balance 9.2 MB 1.28 MB - 86 % Core balance 13.1 MB 2.22 MB - 83 % Meta balance 9.2 MB 1.28 MB - 86 % Core balance 13.1 MB 2 22 MB - 83 % Čas deserializace Old balances Optimized balances Profit Meta balance 967 ms 199 ms - 79 % Core balance 1165 ms 265 ms - 77 % Meta balance 967 Kč 199 Kč - 79 % Core balance 1165 Kč 265 Kč - 77 % Data v paměti Old balances Optimized balances Profit Meta + Core ~ 45.3 MB ~ 33.5 MB - 26 % Meta + Core • 54,3 MB • 33,5 MB - 26 % Závěry Výsledky optimalizace nás plně uspokojily.Bilanční soubory byly sníženy o více než 80%.Doprava klesla a hráči byli spokojeni. Abyste to shrnuli: buďte opatrní s údaji, které přenášíte, a neposílejte nic zbytečného. Řetězce jsou nejlépe uloženy v jedinečných úložištích, aby se zabránilo vytváření duplikátů. A pokud vaše vlastní data (ceny, statistiky atd.) také obsahují spoustu opakování, zkuste je také zabalit do jedinečných úložišť.