Mijn naam is Sergey Kachan, en ik ben een client-ontwikkelaar op het project War Robots. War Robots is er al vele jaren, en gedurende deze tijd heeft het spel een enorme verscheidenheid aan inhoud opgebouwd: robots, wapens, drones, titans, piloten, enzovoort. Vandaag zal ik praten over hoe balans is gestructureerd in ons project, wat er met hen is gebeurd in de afgelopen 11 jaar, en hoe we ermee hebben omgegaan. Balances in het project Net als elk ander project, kan War Robots worden onderverdeeld in twee delen: meta en core gameplay. is elke activiteit die verder gaat dan de kernloop van het spel, maar nog steeds de gameplay beïnvloedt. Meta gameplay (metagaming) is de belangrijkste herhalende cyclus van acties die de speler uitvoert in het spel om hun doelen te bereiken. Core gameplay (core gameplay loop) Elk deel van het project heeft zijn eigen balans nodig, dus we verdelen ook de balans in twee categorieën - meta en core. De oorlogsrobots hebben ook de zogenaamde die hun eigen afzonderlijke balans vereisen. Skirmish modes a is een wijziging van bestaande modi of kaarten met verschillende kenmerken of regels. Skirmish modi zijn vaak gebeurtenis gebaseerd, beschikbaar voor spelers tijdens verschillende feestdagen, voornamelijk voor plezier. Skirmish mode Dus in totaal hebben we 4 saldi: 2 voor de standaardmodus en 2 voor de Skirmish-modus. In 11 jaar tijd heeft War Robots een ton geweldige content verzameld: 95 robots 21 Titans 175 verschillende wapens Veertig drones 16 Moederschap een groot aantal skins, remodels, modules, pilots, torens, ultieme versies van inhoud en kaarten En zoals je je kunt voorstellen, om al dit werk te doen, moeten we informatie over gedrag, statistieken, beschikbaarheid, prijzen en nog veel meer opslaan. Als gevolg hiervan zijn onze balansen gegroeid tot een onevenwichtige grootte: 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 van 13.1 MB van Na een aantal snelle berekeningen, ontdekten we dat een speler zou moeten downloaden Dat is heel veel! 44.6 MB We wilden spelers niet dwingen om zo'n grote hoeveelheid gegevens te downloaden telkens als de balans veranderde. Alleen om u eraan te herinneren: War Robots is aangekomen In 2024 was ons maandelijkse actieve publiek en Elke dag ingelogd. 300 million registered users 4.7 million people 690 thousand players Nu stel je de hoeveelheid gegevens voor. Veel, toch? We dachten dat ook. Dus, we besloten om alles te doen wat we konden om de grootte van onze balans te verminderen! Jacht op het probleem De eerste stap was om de balans te analyseren en te proberen erachter te komen: "Wat neemt zoveel ruimte in beslag?" Het handmatig doorlopen van alles was het laatste wat we wilden doen - het zou eeuwen duren. dus schreven we een reeks tools die alle informatie verzamelden en aggregeren die we nodig hadden over de balans. De tool zou een balansbestand nemen als input en, met behulp van reflectie, itereren door alle structuren, gegevens verzamelen over welke soorten informatie we opgeslagen hebben en hoeveel ruimte elk bezet. De resultaten waren ontmoedigend: Meta balans % 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 procent 164 553 Int32 27 917 procent 161 van 312 Boolean 6.329 procent 36 568 Double 5 845 procent 33 772 Int64 4 682 procent 27 054 jaar Custom structures 26749 procent — Kernbalans % 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 34259 procent 232 229 Double 23.370 % van de 158 418 Int32 20 955 procent 142 050 Boolean 5 306 procent 34 323 Custom structures 16,11 % van de — Na het analyseren van de situatie beseften we dat En er moest er iets aan gedaan worden. strings were taking up far too much space Deze scande het balansbestand en genereerde een kaart van alle strings samen met het aantal keren dat elk werd gedupliceerd. De resultaten waren ook niet bemoedigend.Sommige strengen werden tienduizenden keren herhaald! We hadden het probleem gevonden.Nu was de vraag: hoe kunnen we het oplossen? Optimaliseren van de balans Om voor de hand liggende redenen konden we strings niet helemaal verwijderen. strings worden gebruikt voor dingen als lokalisatietoets en verschillende ID's. Het idee was zo eenvoudig als het komt: Maak een lijst met unieke strings voor elke balans (in wezen een speciale opslag). Stuur deze lijst samen met de gegevens. public class BalanceMessage { public BalanceMessageData Data; public StringStorage Storage; public string Version; } StringStorage is in wezen een wrapper rond een lijst met strings. Wanneer we de stringopslag bouwen, onthoudt elke balansstructuur de index van de string die ze nodig heeft. Later, wanneer we gegevens ophalen, passeren we gewoon de index en krijgen we snel de waarde. public class StringStorage { public List<string> Values; public string GetValue(StringIdx id) => Values[id]; } In plaats van de strengen zelf binnen de balansstructuren door te geven, begonnen we de index van waar de string in de stringopslag wordt opgeslagen door te geven. Vóór : public class SomeBalanceMessage { public string Id; public string Name; public int Amount; } Na het: public class SomeBalanceMessageV2 { public StringIdx Id; public StringIdx Name; public int Amount; } StringIdx is in principe gewoon een wrapper rond een int. Op deze manier hebben we directe stringtransfers binnen de balansstructuren volledig geëlimineerd. 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; } Deze aanpak verminderde het aantal strengen met tientallen keren. 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 10 082 jaar Core balance 232 229 14 228 Niet slecht toch? Maar dat was nog maar het begin, daar stopten we niet. Het opnieuw opstellen van het data protocol Voor het overbrengen en verwerken van balansstructuren gebruikten we . MessagePack MessagePack is een binaire data serialisatie formaat ontworpen als een compactere en snellere alternatief voor JSON. Het is bedoeld voor efficiënte gegevensuitwisseling tussen toepassingen of diensten, waardoor een significante vermindering van de gegevensgrootte - vooral nuttig waar prestaties en bandbreedte van belang zijn. In eerste instantie kwam MessagePack in een JSON-achtig formaat, waar de gebruikte gegevens Dat is zeker handig, maar ook heel ruimteverbruikend, dus we besloten om wat flexibiliteit op te offeren en over te schakelen naar een . string keys binary byte array Vóór : public class SomeBalanceMessage { [Key("id")] public string Id; [Key("name")] public string Name; [Key("amount")] public int Amount; } Na het: public class SomeBalanceMessageV2 { [Key(0)] public StringIdx Id; [Key(1)] public StringIdx Name; [Key(2)] public int Amount; } We hebben ook alle lege collecties verwijderd – in plaats van ze te verzenden, verzenden we nu nullwaarden. Test de veranderingen Een gouden regel van goede ontwikkeling (en een die je veel zenuwen bespaart) is om altijd nieuwe functies te implementeren op een manier die je in staat stelt ze snel terug te rollen als er iets misgaat. Tijdens de ontwikkeling moesten we ervoor zorgen dat alle gegevens correct werden overgedragen.Oude en nieuwe balans - ongeacht formaat of structuur - moesten precies dezelfde waarden produceren.En onthoud, de geoptimaliseerde balans had hun structuur drastisch veranderd, maar dat was niet bedoeld om iets anders dan hun grootte te beïnvloeden. Om dit te bereiken, hebben we een groot aantal eenheidstests voor elke balans geschreven. In eerste instantie vergeleken we alle velden “head-on” – het controleren van elk van hen expliciet. Dit werkte, maar het was tijdrovend, en zelfs de kleinste verandering in de balans zou de tests breken, waardoor we ze constant opnieuw schrijven. Uiteindelijk hadden we genoeg van dat en kwamen we met een handigere testbenadering voor het vergelijken van balans. We namen twee versies van de balansstructuren, bijvoorbeeld SomeBalanceMessage en SomeBalanceMessageV2, en herhaalde over hen – vergelijking veld getallen, namen en waarden. Optimalisatie resultaten Dankzij deze optimalisaties zijn we erin geslaagd zowel de grootte van de via het netwerk verzonden bestanden te verminderen als de tijd die nodig is om ze op de client te deserialiseren. Bestandsgrootte 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 van 86 procent Core balance 13.1 MB van 2.22 MB van - 83 procent Deserialisatie Tijd Old balances Optimized balances Profit Meta balance 967 ms 199 ms - 79 % Core balance 1165 ms 265 ms - 77 % Meta balance 967 miljoen 199 miljoen ms - 79 procent Core balance 1165 miljoen 265 miljoen - 77 procent Gegevens in het geheugen Old balances Optimized balances Profit Meta + Core ~ 45.3 MB ~ 33.5 MB - 26 % Meta + Core ~ 54,3 MB ~ 33,5 MB - 26 procent Conclusies De balansbestanden werden verminderd met meer dan 80%. het verkeer daalde en de spelers waren blij. Samenvattend: wees voorzichtig met de gegevens die u verzendt en stuur niets onnodig. Strings worden het beste opgeslagen in unieke opslagruimten om duplicaten te voorkomen.En als uw aangepaste gegevens (prijzen, statistieken, enz.) ook veel herhaling bevatten, probeer ze ook in unieke opslagruimten te verpakken.