नेटकोड को कैसे संभालें?


10

मैं उन विभिन्न तरीकों का मूल्यांकन करने में रुचि रखता हूं जो नेटकोड एक गेम इंजन "हुक इन" कर सकते हैं। मैं अब एक मल्टीप्लेयर गेम डिजाइन कर रहा हूं, और अब तक मैंने यह निर्धारित किया है कि मुझे नेटवर्क सॉकेट को संभालने के लिए एक अलग धागा है, बाकी इंजन से अलग है जो ग्राफिक्स लूप और स्क्रिप्टिंग को संभालता है।

मेरे पास नेटवर्क गेम को पूरी तरह से सिंगल-थ्रेडेड बनाने के लिए एक संभावित तरीका था, जो यह है कि गैर-ब्लॉकिंग सॉकेट्स का उपयोग करके प्रत्येक फ्रेम को रेंडर करने के बाद नेटवर्क की जांच करें। हालाँकि, यह स्पष्ट रूप से इष्टतम नहीं है क्योंकि फ़्रेम को रेंडर करने में लगने वाला समय नेटवर्क लैग में जोड़ा जाता है: नेटवर्क पर आने वाले संदेशों को तब तक इंतजार करना चाहिए जब तक कि वर्तमान फ्रेम रेंडर (और गेम लॉजिक) पूरा नहीं हो जाता। लेकिन, कम से कम इस तरह से गेमप्ले अभी भी कम या ज्यादा सुचारू रहेगा।

नेटवर्किंग के लिए एक अलग थ्रेड होने से खेल नेटवर्क के लिए पूरी तरह से उत्तरदायी है, उदाहरण के लिए यह सर्वर से एक राज्य अद्यतन प्राप्त होने पर तुरंत एसीके पैकेट वापस भेज सकता है। लेकिन मैं गेम कोड और नेटवर्क कोड के बीच संवाद करने के सर्वोत्तम तरीके के बारे में थोड़ा भ्रमित हूं। नेटवर्किंग थ्रेड प्राप्त पैकेट को एक कतार में धकेल देगा, और गेम थ्रेड कतार से उसके लूप के दौरान उचित समय पर पढ़ेगा, इसलिए हमने इस अप-टू-फ़्रेम विलंब से छुटकारा नहीं पाया है।

इसके अलावा, ऐसा लगता है कि मैं चाहता हूं कि थ्रेड जो पैकेट भेजने के लिए संभालता है वह पाइप से नीचे आने वाले पैकेट के लिए अलग से जांच कर रहा है, क्योंकि यह बीच में होने पर एक को भेजने में सक्षम नहीं होगा आने वाले संदेशों की जाँच कर रहा है। मैं selectया इसी तरह की कार्यक्षमता के बारे में सोच रहा हूं ।

मुझे लगता है कि मेरा सवाल है, सबसे अच्छा नेटवर्क जवाबदेही के लिए गेम डिजाइन करने का सबसे अच्छा तरीका क्या है? स्पष्ट रूप से क्लाइंट को सर्वर से जितनी जल्दी हो सके उपयोगकर्ता इनपुट भेजना चाहिए, इसलिए मैं गेम लूप के अंदर, इवेंट प्रोसेसिंग लूप के तुरंत बाद नेट-सेंड कोड आ सकता है। क्या इसका कोई मतलब है?

जवाबों:


13

जवाबदेही को नजरअंदाज करें। एक LAN पर, पिंग महत्वहीन है। इंटरनेट पर, 60-100ms राउंड-ट्रिप एक आशीर्वाद है। लैग देवताओं से प्रार्थना करें कि आपको स्पाइक्स 3K नहीं मिलता है। इस समस्या के लिए आपके सॉफ़्टवेयर को बहुत ही कम अपडेट / सेकंड में चलाना होगा। यदि आप 25 अपडेट / सेकंड के लिए शूट करते हैं, तो आपको एक पैकेट प्राप्त करने और उस पर कार्य करने के बीच 40ms अधिकतम समय मिला है। और वह सिंगल-थ्रेडेड मामला है ...

लचीलेपन और शुद्धता के लिए अपने सिस्टम को डिज़ाइन करें। यहाँ गेम कोड में नेटवर्क सबसिस्टम को हुक करने के बारे में मेरा विचार है: मैसेजिंग। बहुत सारी समस्याओं का समाधान "संदेश" हो सकता है। मुझे लगता है कि मैसेजिंग एक बार लैब चूहों में कैंसर को ठीक कर देता है। मैसेजिंग मुझे मेरी कार बीमा पर $ 200 या अधिक बचाता है। लेकिन गंभीरता से, मैसेजिंग शायद किसी भी सबसिस्टम को गेम कोड से जोड़ने का सबसे अच्छा तरीका है जबकि दो स्वतंत्र सबसिस्टम को बनाए रखते हुए।

नेटवर्क सबसिस्टम और गेम इंजन के बीच किसी भी संचार के लिए और किसी भी दो सबसिस्टम के बीच उस मामले के लिए संदेश का उपयोग करें। इंटर-सबसिस्टम मैसेजिंग एक सरल डेटा हो सकता है, जो std :: list का उपयोग करके पॉइंटर द्वारा पास किए गए डेटा का एक मिश्रण है।

बस एक निवर्तमान संदेश कतार और नेटवर्क सबसिस्टम में गेम इंजन का संदर्भ है। खेल उन संदेशों को डंप कर सकता है जो इसे निवर्तमान कतार में भेजना चाहता है और उन्हें स्वचालित रूप से भेजा जाता है, या शायद जब "फ़्लशमेसेज ()" जैसे कुछ फ़ंक्शन को कहा जाता है। यदि गेम इंजन में एक बड़ा, साझा संदेश कतार था, तो संदेश भेजने के लिए आवश्यक सभी सबसिस्टम (तर्क, AI, भौतिकी, नेटवर्क, आदि) इसमें सभी संदेशों को डंप कर सकते हैं जहां मुख्य गेम लूप तब सभी संदेशों को पढ़ सकता है और फिर उसके अनुसार कार्य करें।

मैं कहूंगा कि दूसरे थ्रेड पर सॉकेट चलाना ठीक है, हालांकि इसकी आवश्यकता नहीं है। इस डिजाइन के साथ एकमात्र समस्या यह है कि यह आम तौर पर अतुल्यकालिक है (आपको ठीक से पता नहीं है कि पैकेट कब भेजे जा रहे हैं) और यह डिबग करना कठिन बना सकता है और समय से संबंधित मुद्दों को प्रकट कर सकता है / बेतरतीब ढंग से गायब हो सकता है। फिर भी, अगर ठीक से किया जाता है, तो इनमें से कोई भी समस्या नहीं होनी चाहिए।

एक उच्च स्तर से, मैं खेल इंजन से ही अलग नेटवर्किंग कहूँगा। खेल इंजन सॉकेट या बफ़र्स के बारे में परवाह नहीं करता है, यह घटनाओं की परवाह करता है। इवेंट्स ऐसी चीजें हैं जैसे "प्लेयर एक्स ने एक गोली चलाई" "गेम टी विस्फोट हुआ"। इनकी व्याख्या सीधे गेम इंजन द्वारा की जा सकती है। वे कहाँ से उत्पन्न होते हैं (एक स्क्रिप्ट, एक ग्राहक की कार्रवाई, एक AI खिलाड़ी, आदि) कोई फर्क नहीं पड़ता।

यदि आप अपने नेटवर्क सबसिस्टम को ईवेंट भेजने / प्राप्त करने के साधन के रूप में मानते हैं, तो आपको सॉकेट पर सिर्फ कॉल रिवीव () करने पर कई फायदे मिलते हैं।

आप बैंडविड्थ का अनुकूलन कर सकते हैं, उदाहरण के लिए, 50 छोटे संदेश (लंबाई में 1-32 बाइट्स) लेकर और नेटवर्क सबसिस्टम उन्हें एक बड़े पैकेट में पैकेज करके भेज सकते हैं। शायद यह भेजने से पहले उन्हें संपीड़ित कर सकता है कि क्या यह एक बड़ी बात थी। दूसरे छोर पर, कोड को पढ़ने के लिए खेल इंजन के लिए फिर से 50 असतत घटनाओं में बड़े पैकेट को अनपैक / अनपैक कर सकते हैं। यह सब पारदर्शी तरीके से हो सकता है।

अन्य शांत चीजों में एकल खिलाड़ी गेम मोड शामिल है जो आपके क्लाइंट कोड को शुद्ध क्लाइंट + शुद्ध सर्वर के साथ साझा मेमोरी स्पेस में मैसेजिंग के माध्यम से एक ही मशीन पर चल रहा है। फिर, यदि आपका एकल खिलाड़ी गेम ठीक से संचालित होता है, तो एक दूरस्थ क्लाइंट (यानी सही मल्टीप्लेयर) भी संचालित होगा। इसके अतिरिक्त, यह आपको समय से पहले विचार करने के लिए मजबूर करता है कि क्लाइंट को कौन से डेटा की जरूरत है क्योंकि आपका एकल प्लेयर गेम या तो सही लगेगा या पूरी तरह से गलत होगा। मिक्स एंड मैच, एक सर्वर चलाएं और मल्टीप्लेयर गेम में एक क्लाइंट बनें - यह सब आसानी से काम करता है।


आपने एक साधारण एसटीडी :: सूची या संदेशों को पारित करने के लिए कुछ का उपयोग करके उल्लेख किया है। यह StackOverflow के लिए एक विषय हो सकता है, लेकिन क्या यह सच है कि धागे सभी एक ही पते के स्थान को साझा करते हैं, और जब तक मैं एक साथ अपनी कतार से संबंधित स्मृति से पंगा लेने से कई धागे रखता हूं, मुझे ठीक होना चाहिए? मैं सामान्य की तरह ही ढेर पर कतार के लिए डेटा आवंटित कर सकता हूं, और बस इसमें कुछ म्यूटेक्स का उपयोग कर सकता हूं?
स्टीवन लू

हाँ, यह सही है। एक म्यूटेक्स std :: सूची के लिए सभी कॉल की सुरक्षा करता है।
पैट्रिक बीबी

जवाब के लिए धन्यवाद! मैं अब तक अपने थ्रेडिंग रूटीन के साथ बहुत प्रगति कर रहा हूं। यह बहुत अच्छा लग रहा है, मेरे अपने खेल इंजन है!
स्टीवन लू

4
वह भावना बुझेगी। हालांकि, आपको जो बड़े पीतल मिलते हैं, वे आपके साथ रहते हैं।
क्रिस 55

@StevenLu कुछ हद तक [अत्यंत] देर से, लेकिन मैं यह बताना चाहता हूं कि थ्रेड्स को एक साथ मेमोरी से खराब होने से रोकना बेहद कठिन हो सकता है , यह इस बात पर निर्भर करता है कि आप इसे कैसे करना चाहते हैं और आप कितने कुशल होना चाहते हैं। क्या आप आज ऐसा कर रहे हैं, मैं आपको कई उत्कृष्ट ओपन-सोर्स समवर्ती-कतार कार्यान्वयनों में से एक के लिए इंगित करता हूं, इसलिए आपको एक जटिल पहिया को सुदृढ़ करने की आवश्यकता नहीं है।
फंड मोनिका का मुकदमा

4

मुझे नेटवर्क सॉकेट को संभालने के लिए (बहुत कम से कम) एक अलग थ्रेड की आवश्यकता है

नहीं, तुम नहीं।

मेरे पास नेटवर्क गेम को पूरी तरह से सिंगल-थ्रेडेड बनाने के लिए एक संभावित तरीका था, जो यह है कि गैर-ब्लॉकिंग सॉकेट्स का उपयोग करके प्रत्येक फ्रेम को रेंडर करने के बाद नेटवर्क की जांच करें। हालाँकि यह स्पष्ट रूप से इष्टतम नहीं है क्योंकि किसी फ्रेम को रेंडर करने में लगने वाला समय नेटवर्क लैग में जोड़ा जाता है:

जरूरी बात नहीं है। आपका तर्क कब अद्यतन होता है? यदि आप इसके साथ अभी तक कुछ नहीं कर सकते हैं तो नेटवर्क से डेटा प्राप्त करने में बहुत कम बिंदु हैं। यदि आपके पास अभी तक कहने के लिए कुछ नहीं है, तो इसी तरह थोड़ा बिंदु प्रतिक्रिया है।

उदाहरण के लिए, यह सर्वर से राज्य अद्यतन प्राप्त होने पर तुरंत एक एसीके पैकेट वापस भेज सकता है।

यदि आपका गेम इतना तेज़ गति से चलने वाला है कि अगले फ्रेम के लिए इंतज़ार करना एक महत्वपूर्ण देरी है, तो यह पर्याप्त डेटा भेजने वाला है, जिसे आपको अलग-अलग ACK पैकेट भेजने की ज़रूरत नहीं है - बस अपने सामान्य डेटा के अंदर ACK मान शामिल करें पेलोड, यदि आपको उनकी आवश्यकता है।

अधिकांश नेटवर्क वाले खेलों के लिए, इस तरह का गेम लूप होना पूरी तरह से संभव है:

while 1:
    read_network_messages()
    read_local_input()
    update_world()
    send_network_updates()
    render_world()

आप रेंडरिंग से अद्यतनों को कम कर सकते हैं, जिसकी अत्यधिक अनुशंसा की जाती है, लेकिन जब तक आपको विशिष्ट आवश्यकता न हो, तब तक बाकी सब कुछ बहुत सरल रह सकता है। वास्तव में आप किस तरह का खेल बना रहे हैं?


7
रेंडरिंग से अपडेट करने की घोषणा करना केवल अत्यधिक अनुशंसित नहीं है, यदि आप गैर-शिट इंजन चाहते हैं तो इसकी आवश्यकता है।
अटैकिंगहोबो

अधिकांश लोग हालांकि इंजन नहीं बना रहे हैं, और शायद अधिकांश गेम अभी भी दो को कम नहीं करते हैं। अद्यतन कार्य के लिए एक बीता हुआ समय मान पारित करने के लिए निपटाना ज्यादातर मामलों में स्वीकार्य रूप से काम करता है।
काइलोटन

2

हालाँकि, यह स्पष्ट रूप से इष्टतम नहीं है क्योंकि फ़्रेम को रेंडर करने में लगने वाला समय नेटवर्क लैग में जोड़ा जाता है: नेटवर्क पर आने वाले संदेशों को तब तक इंतज़ार करना होगा जब तक कि वर्तमान फ़्रेम रेंडर (और गेम लॉजिक) पूरा नहीं हो जाता।

यह बिल्कुल सच नहीं है। संदेश नेटवर्क पर चला जाता है, जबकि रिसीवर वर्तमान फ्रेम प्रदान करता है। नेटवर्क लैग को क्लाइंट की पूरी संख्या में फ़्रेम पर जकड़ा जाता है; हां- लेकिन अगर क्लाइंट के पास इतने कम एफपीएस हैं कि यह एक बड़ी बात है, तो उन्हें बड़ी समस्या है।


0

नेटवर्क संचार को बैच दिया जाना चाहिए। आपको प्रत्येक गेम टिक भेजे गए एक पैकेट के लिए प्रयास करना चाहिए (जो अक्सर तब होता है जब एक फ्रेम प्रदान किया जाता है लेकिन वास्तव में स्वतंत्र होना चाहिए)।

आपकी गेम इकाइयाँ नेटवर्क सबसिस्टम (NSS) के साथ बात करती हैं। NSS संदेशों, ACKs आदि को बैच देता है और कुछ (उम्मीद से एक) को यूडीपी पैकेटों (~ 1500 बाइट्स आमतौर पर) को आकार देता है। एनएसएस केवल एकल यूडीपी पैकेट भेजते समय पैकेट, चैनल, प्राथमिकताएं, पुनरीक्षण आदि का अनुकरण करता है।

गेम के ट्यूटोरियल के गफ़र के माध्यम से पढ़ें या केवल एनेट का उपयोग करें जो ग्लेन फ़ेडलर के कई विचारों को लागू करता है।

या आप बस टीसीपी का उपयोग कर सकते हैं यदि आपके खेल को चिकोटी प्रतिक्रियाओं की आवश्यकता नहीं है। फिर सभी बैचिंग, रिज़र्व और ACK समस्याएं दूर हो जाती हैं। आप फिर भी बैंडविड्थ और चैनलों का प्रबंधन करने के लिए एक एनएसएस चाहते हैं।


0

पूरी तरह से "जवाबदेही को नजरअंदाज न करें"। पहले से ही विलंबित पैकेट में एक और 40ms विलंबता को जोड़ने से थोड़ा लाभ होता है। यदि आप (60fps पर) फ्रेम के एक जोड़े को जोड़ रहे हैं, तो आप एक स्थिति को संसाधित करने में देरी कर रहे हैं फ्रेम के एक और जोड़े को अपडेट करें। पैकेट को जल्दी से स्वीकार करना और उन्हें जल्दी से संसाधित करना बेहतर है ताकि आप सिमुलेशन की सटीकता में सुधार कर सकें।

स्क्रीन पर जो दिखाई दे रहा है, उसका प्रतिनिधित्व करने के लिए आवश्यक न्यूनतम राज्य सूचना के बारे में सोचकर मुझे बैंडविड्थ के अनुकूलन में बड़ी सफलता मिली है। फिर प्रत्येक बिट डेटा को देखना और उसके लिए एक मॉडल चुनना। स्थिति की जानकारी समय के साथ डेल्टा मान के रूप में व्यक्त की जा सकती है। आप इसके लिए अपने स्वयं के सांख्यिकीय मॉडल का उपयोग कर सकते हैं और उन्हें डीबग करने में उम्र बिता सकते हैं, या आप मदद करने के लिए लाइब्रेरी का उपयोग कर सकते हैं। मैं इस लाइब्रेरी के फ़्लोटिंग पॉइंट मॉडल DataBlock_Predict_Float का उपयोग करना पसंद करता हूँ। यह गेम दृश्य ग्राफ के लिए उपयोग की जाने वाली बैंडविड्थ को अनुकूलित करना वास्तव में आसान बनाता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.