ELK GAMEVERSE
ابدأ الآن

لماذا نعيد تشغيل كل جولة على خوادمنا

لوحة الصدارة لا تستحق شيئًا إلا إذا تعذّرت الكتابة فيها مباشرة. إليك كلفة ذلك وعائده.

الطريقة البديهية لبناء لعبة هي ترك الهاتف يقرّر. ينهي اللاعب، فتحسب التطبيقة النتيجة وتخبر الخادم بما جرى. سريع وبسيط، وليس للخادم رأي البتة.

لكنه يعني أيضًا أن النتيجة هي ما تقوله التطبيقة. والتطبيقة برنامج على حاسوب شخص آخر، وقد يغيّره.

ما نفعله بدل ذلك

كل لعبة هنا تسجّل حركاتك — لا النتيجة بل الحركات. وعند الانتهاء تذهب تلك القائمة إلى خوادمنا، فيلعب الخادم الجولة نفسها من الوضع الابتدائي نفسه، مطبّقًا القواعد نفسها على الحركات نفسها.

فإن بلغ نتيجتك، قُبلت الجولة وحُسبت المكافأة من نسخة الخادم. وإن لم يبلغها، رُفضت الجولة ولم يُقيَّد شيء.

المحرّك هو الشيفرة ذاتها في الجهتين، وهو حتمي: بالبذرة نفسها والحركات نفسها يجب أن ينتج اللوح نفسه. وهذا الشرط يشكّل قدرًا مدهشًا من التصميم: كل ما هو عشوائي يجب أن يأتي من بذرة يملكها الخادم أيضًا، وكل ما يقرأ الساعة يجب أن يقرأ وقتًا يتفق عليه الطرفان. وقاعدة تسلك سلوكًا على جهاز سريع غير سلوكها على بطيء ليست قاعدة يمكننا تحمّلها.

ما تكلفته

بضعة أجزاء من الألف من الثانية لكل جولة منتهية، وقيدٌ حقيقي على كيفية كتابة الألعاب. وهو يكشف أيضًا أخطاءنا نحن، وذلك أقل راحة مما يبدو.

مثال، لأنه من النوع الصادق: كان محصول يُوزَّع بالمرور على مفاتيح كائن JSON. ونسختان من المحتوى نفسه — واحدة من ملف وأخرى من قاعدة البيانات — أعادتا تلك المفاتيح بترتيب مختلف، فامتلأ المخزن في منتصف الطريق. القواعد نفسها والحركات نفسها ونتيجة مختلفة. ورُفضت جولات للاعبين صادقين برسالة عن مستوطنة فيها ٥٩٠ حِملًا بدل ٥٩١. ولم يبدُ في اللعبة شيء خاطئ؛ كانت الإعادة ببساطة تختلف مع نفسها.

كان هذا الخلل غير مرئي لأي اختبار وحدة، لأن كل نصف ينجح وحده. ولزم أن يُغذّى الطرفان من مسارين مختلفين حتى ينكشف.

ما عائدها

لوحة صدارة لا يستطيع أحد الكتابة فيها مباشرة، ومكافأة لا يمكن اختلاقها. وهما الخاصية ذاتها مقولة مرتين: تكسب ما تقوله الإعادة.

ويتيح لنا ذلك أيضًا أن نكون محدَّدين حين يختلّ شيء. فالجولة المرفوضة ليست اتهامًا غامضًا، بل اختلافًا عند نقطة معلومة، وغالبًا اتصالًا انقطع في منتصف اللعب لا فعلًا من اللاعب.