Tarmoqning kechikishini qoplash algoritmi bilan mobil otishma uchun ballistik hisob-kitoblar mexanikasini qanday yaxshiladik

Tarmoqning kechikishini qoplash algoritmi bilan mobil otishma uchun ballistik hisob-kitoblar mexanikasini qanday yaxshiladik

Salom, men Nikita Brizhak, Pixonic server ishlab chiqaruvchisi. Bugun men mobil multiplayerda kechikishni qoplash haqida gapirmoqchiman.

Server lag kompensatsiyasi haqida ko'plab maqolalar, shu jumladan rus tilida yozilgan. Buning ajablanarli joyi yo'q, chunki bu texnologiya 90-yillarning oxiridan beri ko'p o'yinchi FPS yaratishda faol foydalanilgan. Misol uchun, siz QuakeWorld modini eslashingiz mumkin, u birinchilardan bo'lib foydalangan.

Bundan tashqari, biz uni mobil ko'p o'yinchi otishma Dino Squad-da ishlatamiz.

Ushbu maqolada mening maqsadim ming marta yozilgan narsalarni takrorlash emas, balki texnologiya stekimiz va asosiy o'yin xususiyatlarini hisobga olgan holda o'yinimizda kechikish kompensatsiyasini qanday amalga oshirganimizni aytib berishdir.

Korteksimiz va texnologiyamiz haqida bir necha so'z.

Dino Squad - bu tarmoqdagi mobil PvP shooter. O'yinchilar turli xil qurollar bilan jihozlangan dinozavrlarni boshqaradilar va 6v6 jamoalarida bir-birlari bilan kurashadilar.

Mijoz ham, server ham Unity-ga asoslangan. Arxitektura otishmalar uchun juda klassik: server avtoritar va mijozlarni bashorat qilish mijozlarda ishlaydi. O'yin simulyatsiyasi ichki ECS yordamida yozilgan va serverda ham, mijozda ham qo'llaniladi.

Agar siz kechikish kompensatsiyasi haqida birinchi marta eshitgan bo'lsangiz, bu masala bo'yicha qisqacha ekskursiya.

Ko'p o'yinchi FPS o'yinlarida o'yin odatda uzoq serverda simulyatsiya qilinadi. Aktyorlar o'z ma'lumotlarini (bosilgan tugmalar haqida ma'lumotni) serverga yuboradilar va bunga javoban server olingan ma'lumotlarni hisobga olgan holda ularga yangilangan o'yin holatini yuboradi. Ushbu o'zaro ta'sir sxemasi bilan oldinga tugmachani bosish va o'yinchi belgisi ekranda harakatlanish vaqti o'rtasidagi kechikish har doim pingdan kattaroq bo'ladi.

Mahalliy tarmoqlarda bu kechikish (xalq orasida kirish kechikishi deb ataladi) sezilmas bo'lishi mumkin, Internet orqali o'ynaganda, belgini boshqarishda "muz ustida sirpanish" hissi paydo bo'ladi. Ushbu muammo mobil tarmoqlar uchun ikki baravar dolzarbdir, bunda o'yinchining pingi 200 ms bo'lgan holat hali ham ajoyib ulanish hisoblanadi. Ko'pincha ping 350, 500 yoki 1000 ms bo'lishi mumkin. Keyin kiritish lag bilan tez shooter o'ynash deyarli imkonsiz bo'ladi.

Ushbu muammoni hal qilish mijoz tomonidan simulyatsiyani bashorat qilishdir. Bu erda mijozning o'zi serverdan javob kutmasdan, o'yinchi xarakteriga kiritilgan ma'lumotlarni qo'llaydi. Va javob olinganda, u shunchaki natijalarni taqqoslaydi va raqiblarning pozitsiyalarini yangilaydi. Bu holda tugmani bosish va natijani ekranda ko'rsatish o'rtasidagi kechikish minimaldir.

Bu erda nuanceni tushunish muhimdir: mijoz har doim oxirgi kiritishiga ko'ra o'zini tortadi va dushmanlar - serverdan olingan ma'lumotlardan oldingi holatga ko'ra tarmoq kechikishi bilan. Ya'ni, dushmanga qarata o'q uzganda, o'yinchi uni o'ziga nisbatan o'tmishda ko'radi. Mijozlarni bashorat qilish haqida ko'proq biz ilgari yozganmiz.

Shunday qilib, mijozni bashorat qilish bir muammoni hal qiladi, lekin boshqasini yaratadi: agar o'yinchi dushman o'tmishda bo'lgan nuqtada o'q uzsa, xuddi shu nuqtada o'q otayotganda, dushman endi o'sha joyda bo'lmasligi mumkin. Server lag kompensatsiyasi bu muammoni hal qilishga urinadi. Qurol otilganda, server o'yinchi o'q otish vaqtida mahalliy ko'rgan o'yin holatini tiklaydi va u haqiqatan ham dushmanga zarba berishi mumkinligini tekshiradi. Agar javob "ha" bo'lsa, dushman o'sha paytda serverda bo'lmasa ham, zarba hisoblanadi.

Ushbu bilimlar bilan qurollangan holda biz Dino Squad-da server kechikish kompensatsiyasini amalga oshirishni boshladik. Avvalo, mijoz ko'rgan narsani serverda qanday tiklashni tushunishimiz kerak edi? Va aynan nimani tiklash kerak? Bizning o'yinimizda qurol va qobiliyatlarning zarbalari raykastlar va qoplamalar orqali, ya'ni dushmanning jismoniy to'qnashuvlari bilan o'zaro ta'sir qilish orqali hisoblanadi. Shunga ko'ra, biz o'yinchi mahalliy "ko'rgan" ushbu to'qnashuvlarning o'rnini serverda takrorlashimiz kerak edi. O'sha paytda biz Unity 2018.x versiyasidan foydalanardik. U erda fizika API statik, jismoniy dunyo bitta nusxada mavjud. Uning holatini saqlab qolish va keyin uni qutidan tiklashning hech qanday usuli yo'q. Xo'sh, nima qilish kerak?

Yechim yuzaki edi; uning barcha elementlari biz tomonidan boshqa muammolarni hal qilish uchun allaqachon ishlatilgan:

  1. Har bir mijoz uchun biz tugmachalarni bosganida raqiblarni qaysi vaqtda ko'rganini bilishimiz kerak. Biz allaqachon ushbu ma'lumotni kirish to'plamiga yozdik va undan mijozning bashoratini sozlash uchun foydalandik.
  2. Biz o'yin holatlari tarixini saqlashga qodir bo'lishimiz kerak. Aynan shu erda biz raqiblarimiz (va shuning uchun ularning kollayderlari) pozitsiyalarini egallab olamiz. Serverda allaqachon davlat tarixi bor edi, biz uni qurish uchun foydalanganmiz deltalar. To'g'ri vaqtni bilib, biz tarixda to'g'ri holatni osongina topishimiz mumkin edi.
  3. Endi bizda tarixdan o'yin holati mavjud, biz o'yinchi ma'lumotlarini jismoniy dunyo holati bilan sinxronlashtirishimiz kerak. Mavjud kollayderlar - siljiting, etishmayotganlar - yaratish, keraksizlar - yo'q qilish. Bu mantiq ham allaqachon yozilgan va bir nechta ECS tizimlaridan iborat edi. Biz undan bitta Unity jarayonida bir nechta o'yin xonalarini saqlash uchun foydalandik. Va jismoniy dunyo har bir jarayon uchun bitta bo'lganligi sababli, uni xonalar orasida qayta ishlatish kerak edi. Simulyatsiyaning har bir belgisidan oldin biz jismoniy dunyoning holatini "qayta o'rnatamiz" va uni joriy xona uchun ma'lumotlar bilan qayta ishga tushiramiz, aqlli birlashma tizimi orqali Unity o'yin ob'ektlarini iloji boricha qayta ishlatishga harakat qilamiz. Qolgan narsa o'tmishdagi o'yin holati uchun bir xil mantiqqa murojaat qilish edi.

Ushbu elementlarning barchasini birlashtirib, biz jismoniy dunyoning holatini kerakli daqiqaga qaytara oladigan "vaqt mashinasi" ga ega bo'ldik. Kod oddiy bo'lib chiqdi:

public class TimeMachine : ITimeMachine
{
     //Π˜ΡΡ‚ΠΎΡ€ΠΈΡ ΠΈΠ³Ρ€ΠΎΠ²Ρ‹Ρ… состояний
     private readonly IGameStateHistory _history;

     //Π’Π΅ΠΊΡƒΡ‰Π΅Π΅ ΠΈΠ³Ρ€ΠΎΠ²ΠΎΠ΅ состояниС Π½Π° сСрвСрС
     private readonly ExecutableSystem[] _systems;

     //Набор систСм, Ρ€Π°ΡΡΡ‚Π°Π²Π»ΡΡŽΡ‰ΠΈΡ… ΠΊΠΎΠ»Π»Π°ΠΉΠ΄Π΅Ρ€Ρ‹ Π² физичСском ΠΌΠΈΡ€Π΅ 
     //ΠΏΠΎ Π΄Π°Π½Π½Ρ‹ΠΌ ΠΈΠ· ΠΈΠ³Ρ€ΠΎΠ²ΠΎΠ³ΠΎ состояния
     private readonly GameState _presentState;

     public TimeMachine(IGameStateHistory history, GameState presentState, ExecutableSystem[] timeInitSystems)
     {
         _history = history; 
         _presentState = presentState;
         _systems = timeInitSystems;  
     }

     public GameState TravelToTime(int tick)
     {
         var pastState = tick == _presentState.Time ? _presentState : _history.Get(tick);
         foreach (var system in _systems)
         {
             system.Execute(pastState);
         }
         return pastState;
     }
}

Qolgan narsa bu mashinadan zarbalar va qobiliyatlarni osongina qoplash uchun qanday foydalanishni aniqlash edi.

Eng oddiy holatda, mexanika bitta hitscanga asoslangan bo'lsa, hamma narsa aniq ko'rinadi: o'yinchi otishdan oldin u jismoniy dunyoni kerakli holatga qaytarishi, raykastni bajarishi, zarba yoki o'tkazib yuborilganini hisoblashi kerak va dunyoni dastlabki holatiga qaytarish.

Ammo Dino Squadda bunday mexaniklar juda kam! O'yindagi qurollarning aksariyati snaryadlarni yaratadi - bir nechta simulyatsiya shomillari (ba'zi hollarda o'nlab shomillar) uchun uchadigan uzoq muddatli o'qlar. Ular bilan nima qilish kerak, ular soat nechada uchishlari kerak?

Π’ qadimiy maqola Half-Life tarmog'i to'g'risida, Valve yigitlari bir xil savolni berishdi va ularning javobi shunday edi: snaryadlarning kechikishini qoplash muammoli va undan qochish yaxshiroqdir.

Bizda bunday imkoniyat yo'q edi: snaryadlarga asoslangan qurollar o'yin dizaynining asosiy xususiyati edi. Shuning uchun biz nimadir o'ylab topishimiz kerak edi. Biroz aqliy hujumdan so'ng, biz ishlayotgandek tuyulgan ikkita variantni ishlab chiqdik:

1. Biz snaryadni yaratgan o'yinchining vaqtiga bog'laymiz. Server simulyatsiyasining har bir belgisi, har bir o'yinchining har bir o'qi uchun biz jismoniy dunyoni mijoz holatiga qaytaramiz va kerakli hisob-kitoblarni bajaramiz. Ushbu yondashuv serverga taqsimlangan yuk va raketalarning taxminiy parvoz vaqtiga ega bo'lish imkonini berdi. Biz uchun oldindan bashorat qilish ayniqsa muhim edi, chunki bizda barcha o'qlar, shu jumladan dushman o'qlari ham mijozda bashorat qilingan.

Tarmoqning kechikishini qoplash algoritmi bilan mobil otishma uchun ballistik hisob-kitoblar mexanikasini qanday yaxshiladik
Rasmda 30-raqamdagi o'yinchi intiqlik bilan raketa otadi: u dushman qaysi yo'nalishda yugurayotganini ko'radi va raketaning taxminiy tezligini biladi. Mahalliy ravishda u nishonni 33-qatorda urganini ko'radi. Kechiktirilgan kompensatsiya tufayli u serverda ham paydo bo'ladi

2. Biz hamma narsani birinchi variantda bo'lgani kabi qilamiz, lekin o'q simulyatsiyasining bitta belgisini hisoblab, biz to'xtamaymiz, balki uning bir xil server belgisi ichida parvozini simulyatsiya qilishni davom ettiramiz, har safar uning vaqtini serverga yaqinlashtiramiz. birma-bir belgi qo'yish va kollayder pozitsiyalarini yangilash. Ikki narsadan biri sodir bo'lguncha buni qilamiz:

  • Oβ€˜q muddati tugagan. Bu shuni anglatadiki, hisob-kitoblar tugadi, biz o'tkazib yuborilgan yoki zarbani hisoblashimiz mumkin. Va bu o'q otilgan belgida! Biz uchun bu ham ortiqcha, ham minus edi. Plyus - chunki otishma o'yinchisi uchun bu zarba va dushmanning sog'lig'ining pasayishi o'rtasidagi kechikishni sezilarli darajada kamaytirdi. Salbiy tomoni shundaki, raqiblar o'yinchiga qarata o'q uzganda xuddi shunday ta'sir kuzatilgan: dushman, shekilli, faqat sekin raketa otgan va zarar allaqachon hisoblangan.
  • OΚ»q server vaqtiga yetdi. Bunday holda, uning simulyatsiyasi hech qanday kechikish kompensatsiyasisiz keyingi server belgisida davom etadi. Sekin snaryadlar uchun bu nazariy jihatdan birinchi variantga nisbatan fizikaviy orqaga qaytishlar sonini kamaytirishi mumkin. Shu bilan birga, simulyatsiyadagi notekis yuk ortdi: server yoki bo'sh edi yoki bitta server belgisida bir nechta o'qlar uchun o'nlab simulyatsiya belgilarini hisoblab chiqdi.

Tarmoqning kechikishini qoplash algoritmi bilan mobil otishma uchun ballistik hisob-kitoblar mexanikasini qanday yaxshiladik
Oldingi rasmdagi kabi bir xil stsenariy, lekin ikkinchi sxema bo'yicha hisoblangan. Raketa otish sodir bo'lgan bir belgida server vaqtiga "tutib oldi" va zarbani keyingi belgidayoq sanash mumkin. 31-belgida, bu holda, kechiktirilgan kompensatsiya endi qo'llanilmaydi

Bizning amalga oshirishimizda bu ikki yondashuv bir-biridan atigi bir necha qator kodlarda farq qildi, shuning uchun biz ikkalasini ham yaratdik va uzoq vaqt davomida ular parallel ravishda mavjud edi. Qurolning mexanikasiga va o'q tezligiga qarab, biz har bir dinozavr uchun u yoki bu variantni tanladik. Bu erda burilish nuqtasi mexanika o'yinidagi "agar siz dushmanni falon vaqt ichida ko'p marta urgan bo'lsangiz, falon bonusga ega bo'ling" kabi paydo bo'ldi. O'yinchining dushmanga zarba berish vaqti muhim rol o'ynagan har qanday mexanik ikkinchi yondashuv bilan ishlashdan bosh tortdi. Shunday qilib, biz birinchi variantga o'tdik va endi u barcha qurollar va o'yindagi barcha faol qobiliyatlarga tegishli.

Alohida-alohida, ishlash masalasini ko'tarishga arziydi. Agar siz bularning barchasi ishni sekinlashtiradi deb o'ylasangiz, men javob beraman: shunday. Birlik kollayderlarni harakatga keltirish va ularni yoqish va o'chirishda juda sekin. Dino Squad-da, "eng yomon holatda" stsenariyda, jangda bir vaqtning o'zida bir necha yuzta raketalar bo'lishi mumkin. Har bir snaryadni alohida hisoblash uchun kollayderlarni ko'chirish - bu arzon hashamatdir. Shuning uchun biz fizikani "orqaga qaytarish" sonini minimallashtirishimiz kerak edi. Buning uchun biz ECS-da alohida komponent yaratdik, unda biz o'yinchi vaqtini yozamiz. Biz uni kechikish uchun kompensatsiya talab qiladigan barcha ob'ektlarga (snaryadlar, qobiliyatlar va boshqalar) qo'shdik. Bunday ob'ektlarni qayta ishlashni boshlashdan oldin, biz ularni shu vaqtgacha klasterlashtiramiz va ularni birgalikda qayta ishlaymiz, har bir klaster uchun jismoniy dunyoni bir marta qaytaramiz.

Ushbu bosqichda bizda umumiy ishlaydigan tizim mavjud. Uning kodi biroz soddalashtirilgan shaklda:

public sealed class LagCompensationSystemGroup : ExecutableSystem
{
     //Машина Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ
     private readonly ITimeMachine _timeMachine;

     //Набор систСм лагкомпСнсации
     private readonly LagCompensationSystem[] _systems;
     
     //Наша рСализация кластСризатора
     private readonly TimeTravelMap _travelMap = new TimeTravelMap();

    public LagCompensationSystemGroup(ITimeMachine timeMachine, 
        LagCompensationSystem[] lagCompensationSystems)
     {
         _timeMachine = timeMachine;
         _systems = lagCompensationSystems;
     }

     public override void Execute(GameState gs)
     {
         //На Π²Ρ…ΠΎΠ΄ кластСризатор ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π΅ ΠΈΠ³Ρ€ΠΎΠ²ΠΎΠ΅ состояниС,
         //Π° Π½Π° Π²Ρ‹Ρ…ΠΎΠ΄ Π²Ρ‹Π΄Π°Π΅Ρ‚ Π½Π°Π±ΠΎΡ€ Β«ΠΊΠΎΡ€Π·ΠΈΠ½Β». Π’ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠΎΡ€Π·ΠΈΠ½Π΅ Π»Π΅ΠΆΠ°Ρ‚ энтити,
         //ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ для лагкомпСнсации Π½ΡƒΠΆΠ½ΠΎ ΠΎΠ΄Π½ΠΎ ΠΈ Ρ‚ΠΎ ΠΆΠ΅ врСмя ΠΈΠ· истории.
         var buckets = _travelMap.RefillBuckets(gs);

         for (int bucketIndex = 0; bucketIndex < buckets.Count; bucketIndex++)
         {
             ProcessBucket(gs, buckets[bucketIndex]);
         }

         //Π’ ΠΊΠΎΠ½Ρ†Π΅ лагкомпСнсации ΠΌΡ‹ восстанавливаСм физичСский ΠΌΠΈΡ€ 
         //Π² исходноС состояниС
         _timeMachine.TravelToTime(gs.Time);
     }

     private void ProcessBucket(GameState presentState, TimeTravelMap.Bucket bucket)
     {
         //ΠžΡ‚ΠΊΠ°Ρ‚Ρ‹Π²Π°Π΅ΠΌ врСмя ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π· для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠΎΡ€Π·ΠΈΠ½Ρ‹
         var pastState = _timeMachine.TravelToTime(bucket.Time);

         foreach (var system in _systems)
         {
               system.PastState = pastState;
               system.PresentState = presentState;

               foreach (var entity in bucket)
               {
                   system.Execute(entity);
               }
          }
     }
}

Qolgan narsa tafsilotlarni sozlash edi:

1. Harakatning maksimal masofasini vaqt ichida qanchalik cheklash kerakligini tushuning.

Uyali aloqa tarmoqlari yomon sharoitda o'yinni iloji boricha qulayroq qilish biz uchun muhim edi, shuning uchun biz hikoyani 30 belgi (20 Gts chastotasi bilan) chegarasi bilan chekladik. Bu o'yinchilarga hatto juda yuqori pinglarda ham raqiblarini urish imkonini beradi.

2. Qaysi predmetlarni oβ€˜z vaqtida koβ€˜chirish mumkin, qaysi biri mumkin emasligini aniqlang.

Biz, albatta, raqiblarimizni harakatga keltiramiz. Ammo, masalan, o'rnatiladigan energiya qalqonlari bunday emas. Biz ko'pincha onlayn otishmalarda bo'lgani kabi, himoya qobiliyatiga ustunlik berish yaxshiroq deb qaror qildik. Agar o'yinchi hozirda qalqon qo'ygan bo'lsa, o'tmishdagi lag kompensatsiyalangan o'qlar u orqali uchib ketmasligi kerak.

3. Dinozavrlarning qobiliyatlarini qoplash zarurmi yoki yo'qligini hal qiling: tishlash, quyruq urish va hokazo. Biz nima kerakligini hal qildik va ularni o'q bilan bir xil qoidalarga muvofiq qayta ishlaymiz.

4. Kechikish kompensatsiyasi bajarilayotgan o'yinchining kollayderlari bilan nima qilish kerakligini aniqlang. Yaxshi ma'noda, ularning pozitsiyasi o'tmishga siljimasligi kerak: o'yinchi o'zini hozir serverda bo'lgan vaqtda ko'rishi kerak. Biroq, biz otishma o'yinchisining kollayderlarini ham orqaga qaytaramiz va buning bir nechta sabablari bor.

Birinchidan, bu klasterlashni yaxshilaydi: biz yaqin pingga ega bo'lgan barcha o'yinchilar uchun bir xil jismoniy holatdan foydalanishimiz mumkin.

Ikkinchidan, barcha raykaslar va o'zaro o'xshashliklarda biz har doim qobiliyat yoki raketalarga ega bo'lgan o'yinchining to'qnashuvlarini istisno qilamiz. Dino Squad-da o'yinchilar otish standartlari bo'yicha nostandart geometriyaga ega bo'lgan dinozavrlarni boshqaradilar. Agar o'yinchi g'ayrioddiy burchak ostida otsa va o'qning traektoriyasi o'yinchining dinozavr kollayderidan o'tib ketsa ham, o'q buni e'tiborsiz qoldiradi.

Uchinchidan, biz dinozavr qurolining pozitsiyalarini yoki qobiliyatni qo'llash nuqtasini ECS ma'lumotlaridan foydalangan holda hisoblaymiz, hatto kechikish kompensatsiyasi boshlanishidan oldin.

Natijada, lag-kompensatsiyalangan o'yinchining kollayderlarining haqiqiy pozitsiyasi biz uchun ahamiyatsiz, shuning uchun biz samaraliroq va shu bilan birga oddiyroq yo'lni tanladik.

Tarmoqning kechikishini oddiygina olib tashlash mumkin emas, uni faqat maskalash mumkin. Boshqa har qanday niqoblash usuli singari, server kechikishining kompensatsiyasi ham o'ziga xos xususiyatlarga ega. Bu o'yinchining o'qqa tutilishi hisobiga o'q uzayotgan o'yinchining o'yin tajribasini yaxshilaydi. Biroq, Dino Squad uchun bu erda tanlov aniq edi.

Albatta, bularning barchasi server kodining murakkabligi oshishi bilan to'lanishi kerak edi - dasturchilar va o'yin dizaynerlari uchun. Agar ilgari simulyatsiya tizimlarning oddiy ketma-ket chaqiruvi bo'lsa, unda kechikish kompensatsiyasi bilan unda ichki halqalar va novdalar paydo bo'ldi. Biz ham u bilan ishlashni qulay qilish uchun ko'p kuch sarfladik.

2019 versiyasida (va ehtimol biroz oldinroq) Unity mustaqil jismoniy sahnalarni to'liq qo'llab-quvvatladi. Biz ularni yangilanishdan so'ng deyarli darhol serverda joriy qildik, chunki biz barcha xonalar uchun umumiy bo'lgan jismoniy dunyodan tezda xalos bo'lishni xohladik.

Biz har bir o'yin xonasiga o'z jismoniy sahnasini berdik va shu bilan simulyatsiyani hisoblashdan oldin sahnani qo'shni xonaning ma'lumotlaridan "tozalash" zaruratini yo'q qildik. Birinchidan, bu hosildorlikning sezilarli o'sishini ta'minladi. Ikkinchidan, agar dasturchi yangi o'yin elementlarini qo'shishda sahnani tozalash kodida xatoga yo'l qo'ygan bo'lsa, paydo bo'lgan xatolarning butun sinfidan xalos bo'lishga imkon berdi. Bunday xatolarni tuzatish qiyin edi va ular ko'pincha bir xonaning sahnasidagi jismoniy ob'ektlarning boshqa xonaga "oqishi" ga olib keldi.

Bundan tashqari, biz jismoniy dunyo tarixini saqlash uchun fizik sahnalardan foydalanish mumkinmi yoki yo'qmi, ba'zi tadqiqotlar o'tkazdik. Ya'ni, shartli ravishda, har bir xonaga bitta sahnani emas, balki 30 ta sahnani ajrating va ulardan hikoyani saqlash uchun tsiklik bufer hosil qiling. Umuman olganda, variant ishladi, lekin biz uni amalga oshirmadik: unumdorlikning aqldan oshib ketishini ko'rsatmadi, balki juda xavfli o'zgarishlarni talab qildi. Ko'p sahnalar bilan uzoq vaqt ishlaganda server o'zini qanday tutishini oldindan aytish qiyin edi. Shuning uchun biz qoidaga amal qildik: "Agar bu noto'g'ri bo'lsa, uni tuzatmang".

Manba: www.habr.com

a Izoh qo'shish