हॉट-स्वैपेबल C ++ मॉड्यूल को कोई कैसे लागू कर सकता है?


39

तेजी से चलने का समय खेल को विकसित करने के लिए महत्वपूर्ण है, मेरी राय में सुविधाओं के ट्रक लोड के साथ फैंसी ग्राफिक्स और इंजन की तुलना में बहुत अधिक है। कोई आश्चर्य नहीं कि कई छोटे डेवलपर्स स्क्रिप्टिंग भाषाओं का चयन करते हैं।

एक गेम को रोकने और परिसंपत्तियों और कोड को संशोधित करने में सक्षम होने का एकता 3 डी तरीका है, फिर भी जारी रखें और बदलाव करें तुरंत इसके लिए महान है। मेरा सवाल है, क्या किसी ने C ++ गेम इंजन पर समान प्रणाली लागू की है?

मैंने सुना है कि कुछ वास्तव में सुपर हाई एंड इंजन करते हैं, लेकिन मुझे यह पता लगाने में अधिक दिलचस्पी है कि क्या यह घर में चलने वाले इंजन या गेम में करने का कोई तरीका है।

जाहिर है कि ट्रेडऑफ़्स होंगे, और मैं कल्पना नहीं कर सकता कि आप खेल को रोकने के दौरान अपने कोड को फिर से जोड़ सकते हैं, इसे फिर से लोड किया गया है और हर परिस्थिति या हर मंच के नीचे काम कर रहा है।

लेकिन शायद एआई प्रोग्रामिंग और सरल स्तर के तर्क मॉड्यूल के लिए यह संभव है। ऐसा नहीं है कि मैं लघु (या दीर्घकालिक) किसी भी परियोजना में ऐसा करना चाहता हूं, लेकिन मैं उत्सुक था।

जवाबों:


26

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

टिमोथी फरार ने अपने दृष्टिकोण की चर्चा की:

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


मैं ब्लेयर के जवाब को पसंद करता हूं, क्योंकि आप वास्तव में सवाल का जवाब देते हैं।
जोनाथन डिकिंसन

15

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

बालों वाली, विवश सामान। कोड के लिए एक स्क्रिप्टिंग भाषा का उपयोग करना बेहतर है जिसे तेज पुनरावृत्ति की आवश्यकता है।

काम के समय, हमारा वर्तमान कोड आधार C ++ और Lua का मिश्रण है। लुआ हमारी परियोजना का कोई छोटा हिस्सा नहीं है, या तो - यह लगभग 50/50 विभाजित है। हमने लुआ के पुन: उड़ान भरने को लागू किया है, जैसे कि आप कोड की एक पंक्ति को बदल सकते हैं, पुनः लोड कर सकते हैं, और चलते रह सकते हैं। वास्तव में, आप खेल को फिर से शुरू किए बिना, लूआ कोड में होने वाले क्रैश बग को ठीक करने के लिए ऐसा कर सकते हैं!


3
एक और महत्वपूर्ण बिंदु यह है कि भले ही आप स्क्रिप्ट में एक प्रणाली रखने का इरादा नहीं रखते हैं, अगर आप बहुत जल्दी इसे पुनरावृत्त करने की उम्मीद करते हैं, तो लूआ में शुरू करने के लिए यह अभी भी पूरी तरह से उचित हो सकता है, फिर सड़क के नीचे C ++ पर जाएं जब आपको ज़रूरत हो अतिरिक्त प्रदर्शन और पुनरावृत्ति की दर धीमी हो गई है। यहां स्पष्ट कमी यह है कि आप कोड को पोर्ट करना समाप्त कर देते हैं, लेकिन तर्क को सही होने में बहुत बार कठिन भाग होता है - कोड को लगभग एक बार लिखने के बाद आप उस भाग का पता लगा लेते हैं।
लोगान किन्काइद

15

(आप "बंदर पैचिंग" या "डक पंचिंग" शब्द के बारे में जानना चाह सकते हैं यदि विनम्र मानसिक छवि के अलावा और कुछ भी नहीं है।)

एक तरफ: यदि आपका लक्ष्य "व्यवहार" परिवर्तनों के लिए पुनरावृति समय को कम कर रहा है, तो कुछ दृष्टिकोणों की कोशिश करें जो आपको वहां सबसे अधिक मिलते हैं, और भविष्य में इसे और अधिक सक्षम करने के लिए अच्छी तरह से संयोजित करें।

(यह थोड़ा स्पर्श पर निकल जाएगा, लेकिन मैं वादा करता हूं कि यह वापस आ जाएगा!)

  • डेटा के साथ शुरू करें , और छोटे से शुरू करें: सीमाओं ("स्तर" या जैसे) पर फिर से लोड करें , फिर फ़ाइल परिवर्तन सूचनाएं प्राप्त करने के लिए ओएस कार्यक्षमता का उपयोग करने के लिए अपना काम करें या बस नियमित रूप से सर्वेक्षण करें
  • (बोनस अंक और कम लोड समय के लिए (पुन: पुनरावृत्ति समय घटाकर) डेटा बेकिंग में देखें ।)
  • स्क्रिप्ट डेटा हैं , और आपको व्यवहार को पुनरावृत्त करने की अनुमति देते हैं। यदि आप एक स्क्रिप्टिंग भाषा का उपयोग करते हैं, तो आपके पास अब उन लिपियों को पुन: लोड, संकलित या संकलित करने की सूचनाएं / क्षमता है। आप अपने दुभाषिया को इन-गेम कंसोल, एक नेटवर्क सॉकेट, या बढ़े हुए रनटाइम लचीलेपन के लिए पसंद कर सकते हैं।
  • कोड डेटा भी हो सकता है : आपका कंपाइलर ओवरले , शेयर्ड लाइब्रेरी, डीएलएल, या लाइक का समर्थन कर सकता है । इसलिए अब आप ओवरले या DLL को फिर से लोड करने और फिर से लोड करने के लिए "सुरक्षित" समय चुन सकते हैं, चाहे मैनुअल हो या स्वचालित। अन्य उत्तर यहां विस्तार से हैं। ध्यान दें कि इसके कुछ वेरिएंट क्रिप्टोग्राफ़िक हस्ताक्षर नसबंदी, एनएक्स (नो- एक्ज़ीक्यूट ) बिट या किसी अन्य सुरक्षा तंत्र के साथ गड़बड़ कर सकते हैं ।
  • एक गहरी, संस्करणबद्ध सेव / लोड सिस्टम पर विचार करें । यदि आप कोड परिवर्तन की स्थिति में भी अपने राज्य को मजबूती से बचा सकते हैं और पुनर्स्थापित कर सकते हैं, तो आप अपने खेल को बंद कर सकते हैं और ठीक उसी बिंदु पर नए तर्क के साथ इसे पुनः आरंभ कर सकते हैं। इजीयर ने किया की तुलना में, लेकिन यह करने योग्य है, और यह निर्देश बदलने के लिए पोकिंग मेमोरी की तुलना में स्पष्ट रूप से आसान और अधिक पोर्टेबल है।
  • अपने खेल की संरचना और नियतत्व के आधार पर आप रिकॉर्डिंग और प्लेबैक करने में सक्षम हो सकते हैं । यदि वह रिकॉर्डिंग सिर्फ "गेम कमांड्स" पर है (कार्ड गेम के बारे में सोचें, उदाहरण के लिए), तो आप अपने द्वारा दिए गए सभी रेंडरिंग कोड को बदल सकते हैं और अपने बदलावों को देखने के लिए रिकॉर्डिंग को फिर से कर सकते हैं। कुछ खेलों के लिए यह कुछ शुरुआती मापदंडों (जैसे एक यादृच्छिक बीज) और फिर उपयोगकर्ता क्रियाओं को रिकॉर्ड करने के रूप में "आसान" है। कुछ के लिए यह बहुत अधिक जटिल है।
  • संकलन समय कम करने के प्रयास करें । पूर्वोक्त सेव / लोड या रिकॉर्ड / प्लेबैक सिस्टम के साथ, या ओवरले या DLL के साथ संयोजन में, यह किसी भी अन्य चीज़ से अधिक आपके टर्नअराउंड को कम कर सकता है।

यदि आप डेटा या कोड को पुनः लोड करने के सभी तरीके प्राप्त नहीं करते हैं, तो भी इनमें से कई बिंदु लाभदायक हैं।

उपाख्यानों का समर्थन:

एक बड़े पीसी आरटीएस (~ 120 व्यक्ति टीम, ज्यादातर सी ++) पर, एक अविश्वसनीय रूप से गहरी राज्य बचत प्रणाली थी, जिसका उपयोग कम से कम तीन उद्देश्यों के लिए किया गया था:

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

मैं डीएस के लिए एक C ++ और Lua कार्ड खेल पर नियतात्मक रिकॉर्ड / प्लेबैक का उपयोग किया है। हमने एआई (सी ++ ओर) के लिए डिज़ाइन किए गए एपीआई में हुक किया और उपयोगकर्ता और एआई कार्यों के सभी रिकॉर्ड किए। हमने इस कार्यक्षमता का उपयोग खेल में (खिलाड़ी के लिए एक रिप्ले प्रदान करने के लिए) किया है, लेकिन मुद्दों का निदान करने के लिए भी: जब कोई दुर्घटना या विषम व्यवहार होता था, तो हमें बस इतना करना होता था कि हम सेव फाइल प्राप्त करें और इसे डिबग बिल्ड में वापस चलाएं।

मैंने भी कुछ समय के बाद ओवरले का उपयोग किया है, और हमने इसे अपने "स्वचालित रूप से इस निर्देशिका को मकड़ी के साथ जोड़ा और नई सामग्री को हैंडहेल्ड" सिस्टम पर अपलोड किया। हमें बस इतना करना होगा कि cutscene / level / जो कुछ भी है उसे छोड़ दें और न केवल नया डेटा (sprites, level Layout, etc) लोड होगा बल्कि ओवरले में कोई नया कोड भी लोड करेगा। दुर्भाग्य से यह कॉपी प्रोटेक्शन और एंटी-हैकिंग मैकेनिज्म के कारण अधिक हालिया हैंडहेल्ड के साथ बहुत कठिन हो रहा है जो विशेष रूप से कोड का इलाज करते हैं। हम इसे अभी भी लुआ स्क्रिप्ट के लिए करते हैं।

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


6

आप रन-टाइम पर हॉट-स्वैपिंग मॉड्यूल की तरह कुछ कर सकते हैं, मॉड्यूल को डायनेमिक लिंक लाइब्रेरी (या UNIX पर साझा लाइब्रेरी) के रूप में कार्यान्वित कर रहे हैं, और लाइब्रेरी से कार्यों को लोड करने के लिए dlopen () और dlsym () का उपयोग कर रहे हैं।

विंडोज के लिए, समतुल्य हैं लोडलॉउंड और गेटप्रोकएड्रेस।

यह एक सी विधि है, और सी ++ में इसका उपयोग करके कुछ नुकसान हैं, आप यहां उसके बारे में पढ़ सकते हैं ।


यह गाइड हॉट-
स्वैपेबल

4

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

  1. कॉलबैक फ़ंक्शन बदलने से काम ठीक से नहीं होगा। ई और सी कोड का एक नया हिस्सा जोड़कर और कॉल ब्लॉक को बदलकर नए ब्लॉक को इंगित करने के लिए काम करता है। कॉलबैक और फ़ंक्शन पॉइंटर्स का उपयोग करने वाली किसी भी चीज के लिए यह अभी भी पुरानी, ​​अनमॉडिफाइड कॉल को निष्पादित करेगा। इसे ठीक करने के लिए आपके पास एक रैपर कॉलबैक फ़ंक्शन हो सकता है जो एक स्थिर फ़ंक्शन को कॉल करता है
  2. हेडर फ़ाइलों को संशोधित करना लगभग कभी काम नहीं करेगा। यह वास्तविक फ़ंक्शन कॉल को संशोधित करने के लिए डिज़ाइन किया गया है।
  3. विभिन्न भाषा निर्माण रहस्यमय रूप से विफल होने का कारण बनेंगे। मेरे व्यक्तिगत अनुभव से, एनम जैसे पूर्व-घोषित चीजें अक्सर ऐसा करती हैं।

मैंने यूआई पुनरावृति जैसी चीजों की गति में नाटकीय रूप से सुधार करने के लिए एडिट और कंटिन्यू का उपयोग किया है। मान लें कि आपके पास काम करने वाला यूआई पूरी तरह से कोड में बना है सिवाय इसके कि आपने गलती से दो बॉक्स के ड्रॉ ऑर्डर को स्वैप कर दिया ताकि आपको कुछ भी दिखाई न दे। कोड की 2 पंक्तियों को बदलकर आप एक तुच्छ यूआई फिक्स की जांच करने के लिए अपने आप को 20 मिनट के संकलन / निर्माण / परीक्षण चक्र से बचा सकते हैं।

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


कौन सा संकलन चक्र 20 मिनट लेता है? मैं बल्कि खुद को गोली मारता हूँ
JqueryToAddNumbers 8:27

3

जैसा कि दूसरों ने कहा है, यह एक कठिन समस्या है, गतिशील रूप से C ++ को लिंक करना। लेकिन यह एक सुलझी हुई समस्या है - आपने COM या उन मार्केटिंग नामों में से एक के बारे में सुना होगा जो वर्षों से इस पर लागू हैं: ActiveX।

डेवलपर के दृष्टिकोण से COM का एक बुरा नाम है क्योंकि यह C ++ घटकों को लागू करने के लिए बहुत प्रयास हो सकता है जो इसका उपयोग करके अपनी कार्यक्षमता को उजागर करते हैं (हालांकि यह ATL - ActiveX टेम्पलेट लाइब्रेरी के साथ आसान बना है)। एक उपभोक्ता के दृष्टिकोण से इसका एक बुरा नाम है क्योंकि इसका उपयोग करने वाले एप्लिकेशन, उदाहरण के लिए किसी Word दस्तावेज़ में Excel स्प्रेडशीट या Excel स्प्रेडशीट में Visio आरेख को एम्बेड करने के लिए, दिन में थोड़ा सा वापस दुर्घटनाग्रस्त हो जाते हैं। और यह एक ही मुद्दे पर नीचे आता है - यहां तक ​​कि Microsoft द्वारा प्रस्तुत सभी मार्गदर्शन के साथ, COM / ActiveX / OLE सही होना मुश्किल है।

मैं इस बात पर जोर दूंगा कि COM की तकनीक स्वाभाविक रूप से खराब नहीं है। सबसे पहले, DirectX COM इंटरफेस का उपयोग करता है ताकि यह कार्यशीलता को उजागर कर सके और यह काफी अच्छी तरह से काम करता है, साथ ही ऐसे अनुप्रयोगों का एक भीड़ है जो ActiveX नियंत्रण का उपयोग करके इंटरनेट एक्सप्लोरर को एम्बेड करता है। दूसरे, यह C ++ कोड को गतिशील रूप से लिंक करने के सबसे सरल तरीकों में से एक है - एक COM इंटरफ़ेस अनिवार्य रूप से सिर्फ एक शुद्ध आभासी वर्ग है। हालाँकि, इसमें CORBA की तरह एक IDL है, आप इसका उपयोग करने के लिए मजबूर नहीं हैं, खासकर यदि आपके द्वारा परिभाषित किए गए इंटरफेस केवल आपके प्रोजेक्ट के भीतर उपयोग किए जाते हैं।

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


2

यहां गेमप्ले कोड के लिए रनटाइम-संकलित C ++ का कार्यान्वयन है । इसके अलावा, मुझे पता है कि कम से कम एक मालिकाना खेल इंजन है जो ऐसा ही करता है। यह मुश्किल है, लेकिन उल्लेखनीय है।

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