Аб сеткавай мадэлі ў гульнях для пачаткоўцаў

Аб сеткавай мадэлі ў гульнях для пачаткоўцаў
Апошнія два тыдні я працаваў над сеткавым рухавіком для сваёй гульні. Да гэтага я наогул нічога не ведаў аб сеткавых тэхналогіях у гульнях, таму прачытаў мноства артыкулаў і правёў мноства эксперыментаў, каб уразумець усе канцэпцыі і мець магчымасць напісаць уласны сеткавы рухавічок.

У гэтым кіраўніцтве я хацеў бы падзяліцца з вамі рознымі канцэпцыямі, якія вам трэба вывучыць перад напісаннем уласнага гульнявога рухавічка, а таксама самымі лепшымі рэсурсамі і артыкуламі для іх вывучэння.

У цэлым існуе два асноўных тыпу сеткавых архітэктур: peer-to-peer і кліент-серверная. У архітэктуры peer-to-peer (p2p) дадзеныя перадаюцца паміж любымі парамі падлучаных гульцоў, а ў кліент-сервернай архітэктуры дадзеныя перадаюцца толькі паміж гульцамі і серверам.

Хоць архітэктура peer-to-peer па-ранейшаму выкарыстоўваецца ў некаторых гульнях, стандартам з'яўляецца кліент-серверная: яна прасцей у рэалізацыі, патрабуе канал меншай шырыні і палягчае абарону ад читерства. Таму ў гэтым кіраўніцтве мы засяродзімся на кліент-сервернай архітэктуры.

У прыватнасці, нас найбольш цікавяць аўтарытарныя серверы: у такіх сістэмах сервер заўсёды мае рацыю. Напрыклад, калі гулец думае, што знаходзіцца ў каардынатах (10, 5), а сервер кажа яму, што ён у (5, 3), то кліент павінен замяніць сваю пазіцыю той, якую перадае сервер, а не наадварот. Выкарыстанне аўтарытарных сервераў спрашчае распазнанне чытараў.

У гульнявых сеткавых сістэмах ёсць тры асноўных кампанента:

  • Транспартны пратакол: як перадаюцца дадзеныя паміж кліентамі і серверам.
  • Пратакол дадатку: што перадаецца ад кліентаў серверу і ад сервера кліентам і ў якім фармаце.
  • Логіка прыкладання: як перадаюцца дадзеныя выкарыстоўваюцца для абнаўлення стану кліентаў і сервера.

Вельмі важна зразумець ролю кожнай часткі і злучаныя з імі цяжкасці.

Транспартны пратакол

Першы крок заключаецца ў выбары пратакола для транспарціроўкі даных паміж серверам і кліентамі. Для гэтага існуе два Інтэрнэт-пратаколы: TCP и UDP. Але вы можаце стварыць і ўласны транспартны пратакол на аснове аднаго з іх ці прымяніць бібліятэку, у якой яны выкарыстоўваюцца.

Параўнанне TCP і UDP

І TCP, і UDP заснаваны на IP. IP дазваляе перадаваць пакет ад крыніцы атрымальніку, але не дае гарантый, што адпраўлены пакет рана ці позна патрапіць да атрымальніка, што ён дабярэцца да яго хаця б раз і што паслядоўнасць пакетаў прыйдзе ў правільным парадку. Больш таго, пакет можа ўтрымоўваць толькі абмежаваны памер дадзеных, які задаецца велічынёй. MTU.

UDP з'яўляецца ўсяго толькі тонкім пластом па-над IP. Значыць, ён мае тыя ж абмежаванні. У адрозненне ад яго, TCP валодае мноствам асаблівасцяў. Ён забяспечвае надзейнае спарадкаванае злучэнне паміж двума вузламі з праверкай на памылкі. Такім чынам, TCP вельмі зручны і выкарыстоўваецца ў мностве іншых пратаколаў, напрыклад, у HTTP, FTP и SMTP. Але ўсе гэтыя функцыі маюць свой кошт: затрымку.

Каб зразумець, чаму гэтыя функцыі могуць выклікаць затрымку, трэба разабрацца, як працуе TCP. Калі вузел-адпраўнік перадае пакет вузлу-атрымальніку, ён чакае атрымаць пацверджанне (ACK). Калі праз пэўны час ён не атрымлівае яго (бо пакет ці пацверджанне было страчана, ці па нейкіх іншых прычынах), то адпраўляе пакет паўторна. Больш за тое, TCP гарантуе атрыманне пакетаў у правільным парадку, таму пакуль згублены пакет не атрыманы, усе астатнія пакеты не могуць быць апрацаваны, нават калі яны ўжо атрыманы вузлом-атрымальнікам.

Але як вы напэўна разумееце, затрымка ў шматкарыстальніцкіх гульнях вельмі важная, асабліва ў такіх актыўных жанрах, як FPS. Менавіта таму шмат якія гульні выкарыстоўваюць UDP з уласным пратаколам.

Уласны пратакол на аснове UDP можа быць больш эфектыўным за TCP па розных прычынах. Напрыклад, ён можа пазначаць некаторыя пакеты як надзейныя, а іншыя - як ненадзейныя. Таму яго не хвалюе, ці дабраўся ненадзейны пакет да атрымальніка. Ці ён можа апрацоўваць некалькі патокаў дадзеных, каб страчаны ў адным патоку пакет не запавольваў астатнія патокі. Напрыклад, можа быць паток для ўводу гульца і яшчэ адзін паток для паведамленняў чата. Калі паведамленне чата, якое не з'яўляецца тэрміновымі дадзенымі, страчана, то яно не замарудзіць спрацоўванне ўводу, які з'яўляецца неадкладным. Ці ж уласны пратакол можа рэалізаваць надзейнасць інакш, чым у TCP, каб быць больш эфектыўным ва ўмовах відэагульняў.

Дык вось, калі TCP такі адстойны, то мы будзем ствараць свой транспартны пратакол на аснове UDP?

Усё крыху больш складана. Нават хоць TCP амаль субаптымальны для гульнявых сеткавых сістэм, ён можа суцэль добра працаваць пэўна ў вашай гульні і зэканоміць ваш каштоўны час. Напрыклад, затрымка можа і не быць праблемай для пакрокавай гульні або гульні, у якую можна гуляць толькі ў сетках LAN, дзе затрымкі і згуба пакетаў нашмат менш, чым у Інтэрнэце.

У шматлікіх паспяховых гульнях, у тым ліку World of Warcraft, Minecraft і Terraria, выкарыстоўваецца TCP. Аднак у большасці FPS ужываюцца ўласныя пратаколы на аснове UDP, таму ніжэй мы пагаворым пра іх падрабязней.

Калі вы вырашыце выкарыстоўваць TCP, то пераканайцеся, што адключаны алгарытм Нейгла, таму што ён буферызуе пакеты перад адпраўкай, а значыць, павялічвае затрымку.

Каб падрабязней пазнаць пра адрозненні паміж UDP і TCP у кантэксце шматкарыстальніцкіх гульняў, можна прачытаць артыкул Глена Фідлера UDP vs. TCP.

Уласны пратакол

Дык вось, вы хочаце стварыць уласны транспартны пратакол, але не ведаеце, з чаго пачаць? Вам пашанцавала, бо Глен Фідлер напісаў пра гэта два цудоўныя артыкулы. У іх вы знойдзеце мноства разумных думак.

Першы артыкул, Networking for Game Programmers 2008 года, прасцей, чым другая, Building A Game Network Protocol 2016 года. Рэкамендую вам пачаць з больш старой.

Улічыце, што Глен Фідлер - вялікі прыхільнік выкарыстання ўласнага пратакола на аснове UDP. І пасля прачытання яго артыкулаў вы напэўна зразумееце ў яго меркаванне аб тым, што TCP мае ў відэагульнях сур'ёзныя недахопы, і захочаце рэалізаваць уласны пратакол.

Але калі вы пачатковец у працы з сеткамі, то зрабіце сабе ласку і выкарыстоўвайце TCP ці бібліятэку. Для паспяховай рэалізацыі ўласнага транспартнага пратакола трэба папярэдне шмат чаму навучыцца.

Сеткавыя бібліятэкі

Калі вам трэба нешта больш эфектыўнае, чым TCP, але вы не жадаеце затлумляцца рэалізацыяй уласнага пратаколу і ўдавацца ў мноства падрабязнасцяў, то можаце скарыстацца сеткавай бібліятэкай. Іх вельмі шмат:

  • yojimbo Глена Фідлера
  • RakNet, якая больш не падтрымліваецца, але яе форк SLikeNet падобна яшчэ актыўны.
  • ENet — гэта бібліятэка, створаная для шматкарыстальніцкага FPS Куб
  • GameNetworkingSockets кампаніі Valve

Я не спрабаваў іх усё, але перавагу аддаю ENet, таму што яна простая ў выкарыстанні і надзейная. Акрамя таго, у яе ёсць зразумелая дакументацыя і тутарыял для пачаткоўцаў.

Транспартны пратакол: заключэнне

Падвядзем вынік: існуе два асноўных транспартных пратаколу: TCP і UDP. TCP валодае мноствам карысных асаблівасцяў: надзейнасць, захаванне парадку пакетаў, выяўленне памылак. У UDP усяго гэтага няма, затое TCP па сваёй прыродзе валодае падвышанымі затрымкамі, недапушчальнымі для некаторых гульняў. Гэта значыць для забеспячэння нізкіх затрымак можна стварыць уласны пратакол на аснове UDP або выкарыстоўваць бібліятэку, якая рэалізуе транспартны пратакол на UDP і адаптаваную для шматкарыстальніцкіх відэагульняў.

Выбар паміж TCP, UDP і бібліятэкай залежыць ад некалькіх фактараў. Па-першае, ад запатрабаванняў гульні: ці патрэбныя ёй нізкія затрымкі? Па-другое, ад патрабаванняў пратакола дадатку: ці патрэбны яму надзейны пратакол? Як мы ўбачым з наступнай часткі, можна стварыць пратакол прыкладанні, для якога суцэль падыдзе ненадзейны пратакол. Нарэшце, трэба яшчэ ўлічваць дасведчанасць распрацоўніка сеткавага рухавічка.

У мяне ёсць дзве парады:

  • Максімальна абстрагуйце транспартны пратакол ад астатняй часткі прыкладання, каб яго можна было лёгка замяніць, не перапісваючы ўвесь код.
  • Не займайцеся заўчаснай аптымізацыяй. Калі вы не спецыяліст па сетках і не ўпэўненыя, ці патрэбен вам уласны транспартны пратакол на аснове UDP, то можаце пачаць з TCP або бібліятэкі, якія забяспечваюць надзейнасць, а затым пратэставаць і вымераць прадукцыйнасць. Калі ўзнікаюць праблемы і вы ўпэўненыя, што прычына заключаецца ў транспартным пратаколе, то, магчыма, настаў час ствараць уласны транспартны пратакол.

У завяршэнне гэтай часткі рэкамендую вам прачытаць Introduction to Multiplayer Game Programming Браяна Хука, у якім разгледжана мноства тэм, якія тут абмяркоўваюцца.

Пратакол прыкладання

Цяпер, калі мы можам абменьвацца дадзенымі паміж кліентамі і серверам, трэба вырашыць, якія менавіта дадзеныя перадаваць і ў якім фармаце.

Класічная схема складаецца ў тым, што кліенты адпраўляюць серверу ўвод ці дзеянні, а сервер адпраўляе кліентам бягучы гульнявы ​​стан.

Сервер адпраўляе не поўны, а адфільтраваны стан з сутнасцямі, якія знаходзяцца побач з гульцом. Ён робіць гэта з трох прычын. Па-першае, поўнае стан можа быць занадта вялікім для перадачы з высокай частатой. Па-другое, кліентаў у асноўным цікавяць візуальныя і аўдыёдадзеныя, таму што большая частка гульнявой логікі сімулюецца на серверы гульні. Па-трэцяе, у некаторых гульнях гулец не павінен ведаць вызначаных дадзеных, напрыклад, пазіцыю суперніка на іншым канцы карты, бо ў адваротным выпадку ён можа сніфiт пакеты і сапраўды ведаць, куды рухацца, каб яго забіць.

Серыялізацыя

Першым крокам будзе пераўтварэнне дадзеных, якія мы хочам адправіць (увод або гульнявое стан), у прыдатны для перадачы фармат. Гэты працэс называецца серыялізацыяй.

У галаву адразу прыходзіць думка выкарыстоўваць чалавекачытальны фармат, напрыклад JSON ці XML. Але гэта будзе зусім неэфектыўна і марна зойме большую частку канала.

Замест гэтага рэкамендуецца выкарыстоўваць двайковы фармат, які нашмат больш кампактны. Гэта значыць пакеты будуць утрымоўваць толькі некалькі байтаў. Тут трэба ўлічваць праблему парадку байтаў, які на розных кампутарах можа адрознівацца.

Для серыялізацыі дадзеных можна выкарыстоўваць бібліятэку, напрыклад:

Толькі пераканайцеся, што бібліятэка стварае портаваныя архівы і клапоціцца аб парадку байтаў.

Альтэрнатыўным рашэннем можа быць самастойная рэалізацыя, яна не асоба складаная, асабліва калі ў кодзе вы карыстаецеся арыентаваны на дадзеныя падыход. Акрамя таго, яна дазволіць вам выконваць аптымізацыі, якія не заўсёды магчымы пры выкарыстанні бібліятэкі.

Глен Фідлер напісаў аб серыялізацыі два артыкулы: Reading and Writing Packets и Serialization Strategies.

сціск

Колькасць дадзеных, якія перадаюцца паміж кліентамі і серверам, абмежавана прапускной здольнасцю канала. Сціск дадзеных дазволіць перадаваць у кожным снэпшоце больш дадзеных, павялічыць частату абнаўлення ці проста зменшыць патрабаванні да канала.

Бітавая ўпакоўка

Першая тэхніка - гэта бітавая ўпакоўка. Яна складаецца ў выкарыстанні роўна той колькасці бітаў, якое неабходна для апісання патрэбнай велічыні. Напрыклад, калі ў вас ёсць пералік, які можа мець 16 розных значэнняў, то замест цэлага байта (8 біт) можна выкарыстоўваць усяго 4 біты.

Глен Фідлер тлумачыць, як рэалізаваць гэта, у другой частцы артыкула Reading and Writing Packets.

Бітавая ўпакоўка асабліва добра працуе з дыскрэтызацыяй, якая будзе тэмай наступнай часткі.

Дыскрэтызацыя

Дыскрэтызацыя - Гэта тэхніка сціску са стратамі, якая заключаецца ў выкарыстанні для кадавання велічыні толькі падмноства магчымых значэнняў. Прасцей за ўсё рэалізаваць дыскрэтызацыю акругленнем лікаў з якая плавае коскі.

Глен Фідлер (зноў!) паказвае, як прымяняць дыскрэтызацыю на практыцы, у сваім артыкуле Snapshot Compression.

Алгарытмы сціску

Наступнай тэхнікай будуць алгарытмы сціску без страт.

Вось, на мой погляд, тры самыя цікавыя алгарытмы, якія трэба ведаць:

  • Кадаваньне Хафмана з загадзя вылічаным кодам, якое надзвычай хутка і можа даваць добрыя вынікі. Яно выкарыстоўвалася для сціску пакетаў у сеткавым рухавічку Quake3.
  • Zlib - Алгарытм сціску агульнага прызначэння, які ніколі не павялічвае аб'ём дадзеных. Як можна ўбачыць тут, ён ужываўся ў мностве абласцей прымянення. Для абнаўлення станаў ён можа аказацца залішнім. Але ён можа і спатрэбіцца, калі вам трэба адпраўляць кліентам з сервера асеты, доўгія тэксты ці рэльеф.
  • Капіраванне даўжынь серый - гэта, мусіць, найпросты алгарытм сціску, але ён вельмі эфектыўны для вызначаных тыпаў дадзеных, і можа выкарыстоўвацца як этап папярэдняй апрацоўкі перад zlib. Ён асабліва падыходзіць для сціску рэльефу, які складаецца з тайлаў ці вокселяў, у якіх мноства суседніх элементаў паўтараецца.

Дэльта-сціск

Апошняя методыка сціску - гэта дэльта-сціск. Яна складаецца ў тым, што перадаюцца толькі адрозненні паміж бягучым станам гульні і апошнім станам, атрыманым кліентам.

Упершыню яна была ўжытая ў сеткавым рухавічку Quake3. Вось два артыкулы, якія тлумачаць спосаб яе выкарыстання:

Глен Фідлер таксама выкарыстоўваў яе ў другой частцы свайго артыкула Snapshot Compression.

шыфраванне

Акрамя таго, вам можа спатрэбіцца шыфраваць перадачу інфармацыі паміж кліентамі і серверам. На гэта ёсць некалькі прычын:

  • прыватнасць/канфідэнцыяльнасць: паведамленні могуць быць прачытаныя толькі атрымальнікам, і ніводнай іншай асобе, якая выконвае сніфінг сеткі, не атрымаецца іх прачытаць.
  • аўтэнтыфікацыя: чалавек, які жадае выконваць ролю гульца, павінен ведаць яго ключ.
  • прадухіленне чытання: зламысным гульцам будзе нашмат складаней ствараць уласныя пакеты для чытання, ім давядзецца прайграваць схему шыфравання і знаходзіць ключ (які змяняецца пры кожным злучэнні).

Я настойліва раю выкарыстоўваць для гэтага бібліятэку. Прапаную карыстацца натрыю, таму што яна асабліва простая і мае выдатныя тутарыялы. Асоба цікавы тутарыял па абмену ключамі, які дазваляе генераваць новыя ключы пры кожным новым злучэнні.

Пратакол прыкладання: заключэнне

На гэтым мы скончым з пратаколам дадатку. Я лічу, што сціск зусім неабавязкова і рашэнне аб яго выкарыстанні залежыць толькі ад гульні і патрабаванай прапускной здольнасці канала. Шыфраванне, на мой погляд, абавязкова, але ў першым прататыпе можна абысціся без яго.

Логіка прыкладання

Цяпер мы здольныя абнаўляць стан у кліенце, але можам сутыкнуцца з праблемамі затрымак. Гульцу, выканаўшы ўвод, трэба чакаць абнаўленні стану гульні ад сервера, каб убачыць, якое ўздзеянне ён аказаў на свет.

Больш за тое, паміж двума абнаўленнямі стану свет зусім статычны. Калі частата абнаўлення станаў нізкая, то рухі будуць вельмі торганымі.

Існуе некалькі тэхнік, якія дазваляюць зменшыць уплыў гэтай праблемы, і ў наступнай частцы я пра іх распавяду.

Тэхнікі згладжвання затрымак

Усе апісаныя ў гэтым раздзеле тэхнікі падрабязна разгледжаны ў серыі Fast-Paced Multiplayer Габрыэля Гамбетты. Я настойліва рэкамендую прачытаць гэтую цудоўную серыю артыкулаў. У ёй таксама ёсць інтэрактыўнае дэма, якое дазваляе ўбачыць, як гэтыя тэхнікі працуюць на практыцы.

Першая тэхніка заключаецца ў тым, каб прымяняць вынік уводу напрамую, не чакаючы адказу ад сервера. Гэта называецца прагназаваннем на баку кліента. Аднак калі кліент атрымлівае абнаўленне ад сервера, ён павінен пераканацца, што яго прагноз быў дакладным. Калі гэта не так, то яму трэба проста змяніць свой стан паводле атрыманага ад сервера, таму што сервер аўтарытарны. Гэтая тэхніка ўпершыню была скарыстана ў Quake. Больш падрабязна пра яе можна прачытаць у артыкуле Quake Engine code review Фаб'ена Санглара [пераклад на Хабры].

Другі набор тэхнік выкарыстоўваецца для згладжвання руху іншых сутнасцяў паміж двума абнаўленнямі стану. Існуе два спосабу рашэння гэтай задачы: інтэрпаляцыя і экстрапаляцыя. У выпадку інтэрпаляцыі бяруцца два апошнія станы і паказваецца пераход з аднаго ў іншы. Яе недахоп у тым, што яна выклікае невялікую долю затрымкі, таму што кліент заўсёды бачыць тое, што адбывалася ў мінулым. Экстрапаляцыя заключаецца ў прагназаванні таго, дзе зараз павінны знаходзіцца сутнасці на падставе апошняга стану, атрыманага кліентам. Яе недахоп у тым, што калі сутнасць цалкам мяняе напрамак руху, то ўзнікне вялікая хібнасць паміж прагнозам і рэальнай пазіцыяй.

Апошняя, самая прасунутая тэхніка, карысная толькі ў FPS - гэта кампенсацыя лага. Пры выкарыстанні кампенсавання лага сервер улічвае затрымкі кліента, калі ён страляе ў мэту. Напрыклад, калі гулец выканаў хэдшот на сваім экране, але ў рэальнасці яго мэта з-за затрымкі знаходзілася ў іншым месцы, то было б несумленна адмаўляць гульцу ў праве на забойства з-за затрымкі. Таму сервер выконвае перамотку часу назад, на той момант, калі гулец стрэліў, каб сімуляваць, што бачыў гулец на сваім экране, і праверыць калізію паміж яго стрэлам і мэтай.

Глен Фідлер (як заўсёды!) напісаў у 2004 годзе артыкул Network Physics (2004), у якой заклаў падмурак сінхранізацыі сімуляцыі фізікі паміж серверам і кліентам. У 2014 годзе ён напісаў новую серыю артыкулаў Networking Physics, у якой апісаў іншыя тэхнікі для сінхранізацыі сімуляцыі фізікі.

Таксама ў wiki кампаніі Valve ёсць два артыкулы, Source Multiplayer Networking и Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization у якіх разглядаецца кампенсацыя затрымак.

Прадухіленне чытання

Існуе дзве асноўныя тэхнікі прадухілення читерства.

Першая: ускладненне адпраўкі чытарамі шкоднасных пакетаў. Як сказана вышэй, добрым спосабам яе рэалізацыі з'яўляецца шыфраванне.

Другая: аўтарытарны сервер павінен атрымліваць толькі каманды/увод/дзеянні. Кліент не павінен мець магчымасці змяняць стан на сэрвэры, акрамя як адпраўкай уводу. Тады сервер кожны раз пры атрыманні ўводу павінен перад ім ужываннем правяраць яго на дапушчальнасць.

Логіка прыкладання: заключэнне

Рэкамендую вам рэалізаваць спосаб сімуляцыі вялікіх затрымак і нізкіх частот абнаўлення, каб мець магчымасць пратэставаць паводзіны сваёй гульні ў дрэнных умовах, нават калі кліент і сервер запушчаны на адным кампутары. Гэта моцна спросціць рэалізацыю методык згладжвання затрымак.

Іншыя карысныя рэсурсы

Калі вы хочаце вывучыць іншыя рэсурсы, прысвечаныя сеткавым мадэлям, то іх можна знайсці тут:

  • Блог Глена Фідлера - Варта прачытаць увесь яго блог, у ім ёсць мноства выдатных артыкулаў. Тут сабраны ўсе артыкулы па сеткавых тэхналогіях.
  • Awesome Game Networking аўтара M. Fatih MAR - гэта падрабязны спіс артыкулаў і відэа па сеткавым рухавічкам відэагульняў.
  • В wiki сабрэддзіта r/gamedev таксама ёсць мноства карысных спасылак.

Крыніца: habr.com

Дадаць каментар