صادفت هذا الربيع مشروعًا تعلم فيه الرجال كيفية تشغيل خادم Dota 2 من إصدار 2014 ، وبالتالي ، تشغيله. أنا من أشد المعجبين بهذه اللعبة ، ولم أستطع تجاوز الفرصة الفريدة للانغماس في طفولتي.
لقد انغمست بعمق شديد ، وحدث أن كتبت برنامج Discord bot ، وهو المسؤول عن جميع الوظائف غير المدعومة في الإصدار القديم من اللعبة ، أي التوفيق.
قبل كل الابتكارات مع الروبوت ، تم إنشاء الردهة يدويًا. جمعنا 10 ردود على رسالة وقمنا بتجميع خادم يدويًا ، أو استضفنا ردهة محلية.
لم تستطع طبيعتي كمبرمج تحمل الكثير من العمل اليدوي ، وقد رسمت بين عشية وضحاها أبسط نسخة من الروبوت ، والتي أحضرت الخادم تلقائيًا عند تعيين 10 أشخاص.
قررت أن أكتب على الفور في nodejs ، لأنني لا أحب الثعبان حقًا ، وأشعر براحة أكبر في هذه البيئة.
هذه هي تجربتي الأولى في كتابة روبوت لـ Discord ، لكن اتضح أنها كانت بسيطة للغاية. يوفر discord.js وحدة npm الرسمية واجهة ملائمة للعمل مع الرسائل ، وجمع ردود الفعل ، وما إلى ذلك.
إخلاء المسؤولية: جميع أمثلة التعليمات البرمجية "محدثة" ، مما يعني أنها مرت بتكرار إعادة كتابة متعددة بين عشية وضحاها.
جوهر التوفيق هو "قائمة الانتظار" التي يتم فيها وضع اللاعبين الذين يرغبون في اللعب وإبعادهم عندما لا يريدون أو يجدون لعبة.
هذا ما يبدو عليه جوهر "اللاعب". في البداية ، كان مجرد معرف مستخدم في Discord ، لكن الخطط تتضمن قاذفة / بحثًا عن لعبة من الموقع ، ولكن أولاً وقبل كل شيء.
export enum Realm {
DISCORD,
EXTERNAL,
}
export default class QueuePlayer {
constructor(public readonly realm: Realm, public readonly id: string) {}
public is(qp: QueuePlayer): boolean {
return this.realm === qp.realm && this.id === qp.id;
}
static Discord(id: string) {
return new QueuePlayer(Realm.DISCORD, id);
}
static External(id: string) {
return new QueuePlayer(Realm.EXTERNAL, id);
}
}
وهنا واجهة قائمة الانتظار. هنا ، بدلاً من "اللاعبين" ، يتم استخدام تجريد في شكل "مجموعة". بالنسبة للاعب واحد ، تتكون المجموعة من نفسه ومن اللاعبين في مجموعة ، على التوالي ، من جميع اللاعبين في المجموعة.
export default interface IQueue extends EventEmitter {
inQueue: QueuePlayer[]
put(uid: Party): boolean;
remove(uid: Party): boolean;
removeAll(ids: Party[]): void;
mode: MatchmakingMode
roomSize: number;
clear(): void
}
قررت استخدام الأحداث لتبادل السياق. مناسب للحالات - بالنسبة للحدث "تم العثور على لعبة لـ 10 أشخاص" ، يمكنك إرسال الرسالة المطلوبة للاعبين في رسائل خاصة ، وتنفيذ منطق العمل الرئيسي - قم بتشغيل مهمة للتحقق من الجاهزية ، وتجهيز الردهة للانطلاق ، وما إلى ذلك.
بالنسبة إلى IOC ، أنا أستخدم InversifyJS. لدي تجربة ممتعة مع هذه المكتبة. بسرعة وسهولة!
لدينا العديد من قوائم الانتظار على الخادم - لقد أضفنا أوضاعًا 1x1 ، العادية / التصنيف ، واثنين من الأوضاع المخصصة. لذلك ، توجد خدمة غرفة فردية تقع بين المستخدم والبحث عن اللعبة.
constructor(
@inject(GameServers) private gameServers: GameServers,
@inject(MatchStatsService) private stats: MatchStatsService,
@inject(PartyService) private partyService: PartyService
) {
super();
this.initQueue(MatchmakingMode.RANKED);
this.initQueue(MatchmakingMode.UNRANKED);
this.initQueue(MatchmakingMode.SOLOMID);
this.initQueue(MatchmakingMode.DIRETIDE);
this.initQueue(MatchmakingMode.GREEVILING);
this.partyService.addListener(
"party-update",
(event: PartyUpdatedEvent) => {
this.queues.forEach((q) => {
if (has(q.queue, (t) => t.is(event.party))) {
// if queue has this party, we re-add party
this.leaveQueue(event.qp, q.mode)
this.enterQueue(event.qp, q.mode)
}
});
}
);
this.partyService.addListener(
"party-removed",
(event: PartyUpdatedEvent) => {
this.queues.forEach((q) => {
if (has(q.queue, (t) => t.is(event.party))) {
// if queue has this party, we re-add party
q.remove(event.party)
}
});
}
);
}
(رمز المعكرونة لفكرة كيف تبدو العمليات)
هنا أقوم بتهيئة قائمة انتظار لكل وضع من أوضاع اللعبة المنفذة ، وكذلك الاستماع إلى التغييرات في "المجموعات" من أجل تصحيح قوائم الانتظار وتجنب بعض التعارضات.
لذلك ، أنا رائع ، لقد أدخلت أجزاء من التعليمات البرمجية لا علاقة لها بالموضوع ، والآن دعنا ننتقل مباشرة إلى إتقان اللغة.
النظر في حالة:
1) المستخدم يريد اللعب.
2) من أجل بدء البحث ، يستخدم Gateway = Discord ، أي يضع رد فعل على الرسالة:
3) تنتقل هذه البوابة إلى RoomService ، وتقول "المستخدم من الخلاف يريد الدخول إلى قائمة الانتظار ، الوضع: لعبة غير مصنفة".
4) تقبل RoomService طلب البوابة وتدفعه إلى قائمة انتظار المستخدم المطلوبة (بتعبير أدق ، مجموعة المستخدمين).
5) يتحقق الطابور من وجود عدد كافٍ من اللاعبين للعب مع كل تغيير. إذا أمكن ، قم بإصدار حدث:
private onRoomFound(players: Party[]) {
this.emit("room-found", {
players,
});
}
6) من الواضح أن RoomService سعيدة بالاستماع إلى كل قائمة انتظار في انتظار بقلق لهذا الحدث. عند المدخل ، نتلقى قائمة باللاعبين ، ونشكل "غرفة" افتراضية منهم ، وبالطبع نصدر حدثًا:
queue.addListener("room-found", (event: RoomFoundEvent) => {
console.log(
`Room found mode: [${mode}]. Time to get free room for these guys`
);
const room = this.getFreeRoom(mode);
room.fill(event.players);
this.onRoomFormed(room);
});
7) لذلك وصلنا إلى أعلى مثيل - فئة Bot . بشكل عام ، يتعامل مع العلاقة بين البوابات (كيف يبدو الأمر سخيفًا باللغة الروسية ، لا أستطيع) ومنطق العمل في التوفيق. يتنصت الروبوت على الحدث ويطلب من DiscordGateway إرسال فحص الجاهزية لجميع المستخدمين.
8) إذا رفض شخص ما اللعبة أو لم يقبلها خلال 3 دقائق ، فلن نعيده إلى قائمة الانتظار. نعيد أي شخص آخر إلى قائمة الانتظار وننتظر حتى يتم تجنيد 10 أشخاص مرة أخرى. إذا قبل جميع اللاعبين اللعبة ، فسيبدأ الجزء الممتع.
تكوين خادم مخصص
ألعابنا مستضافة على VDS مع Windows Server 2012. يمكن استخلاص عدة استنتاجات من هذا:
- لا يوجد عامل على ذلك الذي أصاب قلبي
- نحن نوفر على الإيجار
المهمة هي بدء العملية على VDS باستخدام VPS على نظام Linux. كتب خادم بسيط في Flask. نعم ، لا أحب بايثون ، لكن ما الذي يمكنني فعله - كتابة هذا الخادم عليه أسرع وأسهل.
لديها 3 وظائف:
- بدء تشغيل الخادم مع التكوين - تحديد الخريطة ، وعدد اللاعبين لبدء اللعبة ، ومجموعة من المكونات الإضافية. لن أكتب عن الإضافات الآن - هذه قصة منفصلة مع لترات من القهوة في الليل ممزوجة بالدموع والشعر الممزق.
- إيقاف / إعادة تشغيل الخادم في حالة الاتصالات غير الناجحة ، والتي لا يمكننا التعامل معها إلا يدويًا.
كل شيء بسيط هنا ، حتى أمثلة التعليمات البرمجية غير مناسبة. برنامج نصي لـ 100 سطر
لذلك ، عندما اجتمع 10 أشخاص وقبولوا اللعبة ، يكون الخادم قيد التشغيل والجميع متشوق للعب ، ويأتي رابط الاتصال باللعبة في رسائل خاصة.
من خلال النقر على الرابط ، يتصل اللاعب بخادم اللعبة ، وبعد ذلك كل شيء. بعد 25 دقيقة تقريبًا ، يتم مسح "الغرفة" الافتراضية مع اللاعبين.
أعتذر مقدمًا عن إحراج المقالة ، فأنا لم أكتب هنا منذ فترة طويلة ، وهناك الكثير من الأكواد لإبراز الأقسام المهمة. المعكرونة ، باختصار.
إذا رأيت اهتمامًا بالموضوع ، فسيكون هناك جزء ثان - سيتضمن تعذيبي مع المكونات الإضافية لـ srcds (خادم مصدر مخصص) ، وربما ، نظام تصنيف و mini-dotabuff ، موقع به إحصائيات اللعبة.
روابط قليلة: