Dota 2014 üçün uyğunluq yazın

Salam.

Bu yaz mən bir layihə ilə qarşılaşdım ki, orada uşaqlar Dota 2 server versiyasını 2014-cü ildə necə işlətməyi və müvafiq olaraq onun üzərində oynamağı öyrəndilər. Mən bu oyunun böyük pərəstişkarıyam və özümü uşaqlığıma qərq etmək üçün bu unikal fürsəti qaçıra bilməzdim.

Mən çox dərindən göyərdim və elə oldu ki, oyunun köhnə versiyasında dəstəklənməyən demək olar ki, bütün funksiyalara, yəni uyğunlaşmaya cavabdeh olan Discord bot yazdım.
Bot ilə bütün yeniliklərdən əvvəl lobbi əl ilə yaradılmışdı. Biz mesaja 10 reaksiya topladıq və əl ilə server yığdıq və ya yerli lobbiyə ev sahibliyi etdik.

Dota 2014 üçün uyğunluq yazın

Bir proqramçı kimi təbiətim bu qədər əl işinə tab gətirə bilmədi və bir gecədə 10 nəfər olduqda serveri avtomatik qaldıran botun ən sadə versiyasının eskizini çəkdim.

Dərhal nodejs-də yazmağa qərar verdim, çünki Python-u həqiqətən sevmirəm və bu mühitdə özümü daha rahat hiss edirəm.

Bu, Discord üçün bot yazmaqla bağlı ilk təcrübəmdir, amma çox sadə oldu. Rəsmi npm modulu discord.js mesajlarla işləmək, reaksiyaların toplanması və s. üçün rahat interfeys təqdim edir.

İmtina: Bütün kod nümunələri “cari”dir, yəni onlar gecə bir neçə dəfə təkrar yazmadan keçiblər.

Uyğunlaşmanın əsası, oynamaq istəyən oyunçuların oyun tapmaq istəmədikləri zaman yerləşdirildiyi və çıxarıldığı bir "növbə"dir.

“Oyunçu”nun mahiyyəti belə görünür. Əvvəlcə bu Discord-da sadəcə bir istifadəçi identifikatoru idi, lakin saytdan oyunları işə salmaq/axtarmaq planları var, amma ilk növbədə.

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);
  }
}

Və burada növbə interfeysi var. Burada “oyunçular” əvəzinə “qrup” şəklində abstraksiyadan istifadə olunur. Tək oyunçu üçün qrup özündən, qrupdakı oyunçular üçün isə müvafiq olaraq qrupdakı bütün oyunçulardan ibarətdir.

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
}

Kontekst mübadiləsi üçün hadisələrdən istifadə etmək qərarına gəldim. Hallar üçün uyğun idi - "10 nəfərlik oyun tapıldı" hadisəsi zamanı oyunçulara şəxsi mesajlarda lazımi mesaj göndərə və əsas iş məntiqini həyata keçirə bilərsiniz - hazırlığı yoxlamaq, lobbi hazırlamaq tapşırığını işə salın. işə salmaq üçün və s.

IOC üçün InversifyJS istifadə edirəm. Bu kitabxana ilə işləmək mənim xoş təcrübəm var. Tez və asan!

Serverimizdə bir neçə növbəmiz var - biz 1x1, normal/reytinq və bir neçə xüsusi rejim əlavə etdik. Buna görə də, istifadəçi və oyun axtarışı arasında yerləşən singleton RoomService var.

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)
          }
        });
      }
    );
  }

(Proseslərin təxminən necə göründüyü barədə fikir vermək üçün əriştələri kodlayın)

Burada mən həyata keçirilən oyun rejimlərinin hər biri üçün növbəni işə salıram, həmçinin növbələri tənzimləmək və bəzi münaqişələrin qarşısını almaq üçün “qruplar”dakı dəyişiklikləri dinləyirəm.

Yaxşı, mən mövzu ilə heç bir əlaqəsi olmayan kod hissələrini daxil etdim və indi birbaşa uyğunlaşmaya keçək.

Məsələni nəzərdən keçirək:

1) İstifadəçi oynamaq istəyir.

2) Axtarışa başlamaq üçün Gateway=Discord istifadə edir, yəni mesaja reaksiya verir:

Dota 2014 üçün uyğunluq yazın

3) Bu şlüz RoomService-ə gedir və "Nifayətdən gələn istifadəçi növbəyə daxil olmaq istəyir, rejim: qiymətləndirilməmiş oyun" deyir.

4) RoomService şlüzün tələbini qəbul edir və istifadəçini (daha doğrusu, istifadəçi qrupunu) istədiyiniz növbəyə itələyir.

5) Növbə hər dəfə oynamaq üçün kifayət qədər oyunçu olduqda yoxlayır. Mümkünsə, bir hadisə göndərin:

private onRoomFound(players: Party[]) {
    this.emit("room-found", {
      players,
    });
  }

6) RoomService bu hadisəni həyəcanla gözləyərək hər növbəni məmnuniyyətlə dinləyir. Biz oyunçuların siyahısını giriş kimi alırıq, onlardan virtual “otaq” təşkil edirik və təbii ki, tədbir təşkil edirik:

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) Beləliklə, "ən yüksək" səlahiyyətə - sinifə çatdıq Bot. Ümumiyyətlə, o, şlüzlər (rus dilində nə qədər gülməli göründüyünü başa düşə bilmirəm) və uyğunlaşmanın iş məntiqi arasındakı əlaqə ilə məşğul olur. Bot hadisəni eşidir və DiscordGateway-ə bütün istifadəçilərə hazırlıq yoxlaması göndərməyi əmr edir.

Dota 2014 üçün uyğunluq yazın

8) Əgər kimsə 3 dəqiqə ərzində oyunu rədd edərsə və ya qəbul etməzsə, onda biz onları növbəyə qaytarmırıq. Qalan hər kəsi növbəyə qaytarırıq və yenidən 10 nəfər olana qədər gözləyirik. Bütün oyunçular oyunu qəbul edibsə, maraqlı hissə başlayır.

Xüsusi server konfiqurasiyası

Oyunlarımız Windows server 2012 ilə VDS-də yerləşdirilir. Buradan bir neçə nəticə çıxara bilərik:

  1. Ürəyimə dəyən doker yoxdur
  2. İcarəyə qənaət edirik

Vəzifə Linux-da VPS-dən VDS-də bir prosesi idarə etməkdir. Flask-da sadə bir server yazdım. Bəli, Python-u sevmirəm, amma siz nə edə bilərsiniz?Bu serveri ona yazmaq daha sürətli və asandır.

3 funksiyanı yerinə yetirir:

  1. Konfiqurasiya ilə serverin işə salınması - xəritənin seçilməsi, oyunu başlamaq üçün oyunçuların sayı və plaginlər dəsti. İndi plaginlər haqqında yazmayacağam - bu, göz yaşları və cırıq saçlarla qarışdırılmış gecə litrlərlə qəhvə ilə fərqli bir hekayədir.
  2. Yalnız əl ilə idarə edə biləcəyimiz uğursuz bağlantılar halında serverin dayandırılması/yenidən işə salınması.

Burada hər şey sadədir, kod nümunələri belə uyğun deyil. 100 sətir skript

Belə ki, 10 nəfər bir araya gəlib oyunu qəbul edəndə server işə salınıb və hər kəs oynamaq həvəsi ilə qarşılaşanda şəxsi mesajlarda oyuna qoşulmaq üçün link göndərilib.

Dota 2014 üçün uyğunluq yazın

Linkə klikləməklə oyunçu oyun serverinə qoşulur və bu da budur. ~25 dəqiqədən sonra oyunçuların olduğu virtual "otaq" təmizlənir.

Məqalənin yöndəmsizliyinə görə əvvəlcədən üzr istəyirəm, uzun müddətdir burada yazmıram və vacib bölmələri vurğulamaq üçün çox kod var. Bir sözlə əriştə.

Mövzuya maraq görsəm, ikinci hissə olacaq - bu, srcds (Mənbə ayrılmış server) üçün plaginlərim və ehtimal ki, reytinq sistemi və mini-dotabuff, oyun statistikası olan bir sayt ilə əzabımı ehtiva edəcək.

Bəzi bağlantılar:

  1. Veb saytımız (statistika, liderlər lövhəsi, kiçik açılış səhifəsi və müştəri yükləməsi)
  2. Discord serveri

Mənbə: www.habr.com

Добавить комментарий