Kita nulis matchmaking kanggo Dota 2014

Halo kabeh.

Ing musim semi iki, aku nemokake proyek ing ngendi wong lanang sinau babagan cara mbukak server Dota 2 versi 2014 lan, mula, main. Aku penggemar gedhe saka game iki, lan aku ora bisa nglewati kesempatan unik iki kanggo nyemplungaken dhewe ing kanak-kanak.

Aku dara banget rumiyin, lan kedaden aku wrote bot Discord sing tanggung jawab kanggo meh kabeh fungsi sing ora didhukung ing versi lawas saka game, yaiku Matchmaking.
Sadurunge kabeh inovasi karo bot, lobi digawe kanthi manual. We diklumpukake 10 reaksi kanggo pesen lan kanthi manual nglumpuk server, utawa tuan rumah lobi lokal.

Kita nulis matchmaking kanggo Dota 2014

Sifatku minangka programer ora bisa nahan karya manual, lan ing wayah wengi aku nggawe sketsa versi paling gampang saka bot, sing kanthi otomatis ngunggahake server nalika ana 10 wong.

Aku langsung mutusakΓ© kanggo nulis ing nodejs, amarga aku ora seneng Python, lan aku aran luwih nyaman ing lingkungan iki.

Iki minangka pengalaman pertamaku nulis bot kanggo Discord, nanging ternyata gampang banget. Modul npm resmi discord.js nyedhiyakake antarmuka sing trep kanggo nggarap pesen, ngumpulake reaksi, lsp.

Penafian: Kabeh conto kode "saiki", tegese wis ngalami sawetara pengulangan nulis ulang ing wayah wengi.

Ing basis saka Matchmaking punika "antrian" kang pemain sing arep kanggo muter diselehake lan dibusak nalika padha ora pengin utawa golek game.

Iki minangka inti saka "pamuter" katon. Wiwitane mung id pangguna ing Discord, nanging ana rencana kanggo miwiti / nelusuri game saka situs kasebut, nanging luwih dhisik.

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

Lan kene antarmuka antrian. Ing kene, tinimbang "pemain", abstraksi ing wangun "grup" digunakake. Kanggo pemain siji, klompok kasebut kalebu awake dhewe, lan kanggo pemain ing grup, kabeh pemain ing grup kasebut.

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
}

Aku mutusake nggunakake acara kanggo ngganti konteks. Cocog kanggo kasus - nalika acara "game kanggo 10 wong ditemokake", sampeyan bisa ngirim pesen sing dibutuhake kanggo para pemain ing pesen pribadi, lan nindakake logika bisnis dhasar - miwiti tugas kanggo mriksa kesiapan, nyiyapake lobi. kanggo peluncuran, lan liya-liyane.

Kanggo IOC aku nggunakake InversifyJS. Aku duwe pengalaman sing nyenengake nggarap perpustakaan iki. Cepet lan gampang!

Kita duwe sawetara antrian ing server kita - kita wis nambahake 1x1, normal/rating, lan sawetara mode khusus. Mulane, ana Singleton RoomService sing dumunung ing antarane pangguna lan telusuran game.

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

(Kode mie kanggo menehi gambaran babagan proses sing kira-kira katon)

Kene aku initialize antrian kanggo saben mode game dipun ginakaken, lan uga ngrungokake owah-owahan ing "klompok" kanggo nyetel antrian lan supaya sawetara konflik.

Dadi, uga rampung, aku nglebokake potongan kode sing ora ana hubungane karo topik kasebut, lan saiki ayo langsung menyang Matchmaking.

Ayo dipikirake kasus kasebut:

1) Pangguna pengin muter.

2) Kanggo miwiti panelusuran, dheweke nggunakake Gateway=Discord, yaiku, menehi reaksi marang pesen:

Kita nulis matchmaking kanggo Dota 2014

3) Gerbang iki menyang RoomService lan ujar "Panganggo saka perselisihan pengin mlebu antrian, mode: game tanpa rating."

4) RoomService nampa panjalukan gateway lan nyurung pangguna (luwih tepate, grup pangguna) menyang antrian sing dikarepake.

5) Antrian mriksa saben-saben ana cukup pemain kanggo muter. Yen bisa, emit acara:

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

6) RoomService temenan seneng ngrungokake saben antrian ing nunggu cemas acara iki. Kita nampa dhaptar pemain minangka input, mbentuk "kamar" virtual saka wong-wong mau, lan, mesthi, ngetokake acara:

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) Dadi, kita entuk panguwasa "paling dhuwur" - kelas Bot. UmumΓ©, dheweke ana hubungane karo gateway (aku ora ngerti carane lucu katon ing basa Rusia) lan logika bisnis matchmaking. Bot kasebut ngrungokake acara kasebut lan mrentah DiscordGateway supaya ngirim priksa kesiapan menyang kabeh pangguna.

Kita nulis matchmaking kanggo Dota 2014

8) Yen wong nolak utawa ora nampa game ing 3 menit, banjur kita ora bali menyang antrian. Kita bali wong liya menyang antrian lan ngenteni nganti ana 10 wong maneh. Yen kabeh pemain wis nampa game, banjur bagean menarik wiwit.

Konfigurasi server khusus

Game kita di-host ing VDS karo server Windows 2012. Saka iki kita bisa nggawe sawetara kesimpulan:

  1. Ora ana docker ing, sing kenek atiku
  2. Kita ngirit ing sewa

Tugas kanggo mbukak proses ing VDS saka VPS ing Linux. Aku nulis server prasaja ing Flask. Ya, aku ora seneng Python, nanging apa sampeyan bisa nindakake? Iku luwih cepet lan luwih gampang kanggo nulis server iki.

Iki nindakake 3 fungsi:

  1. Miwiti server karo konfigurasi - milih peta, nomer pemain kanggo miwiti game, lan pesawat saka Plugins. Aku ora bakal nulis babagan plugin saiki - iki crita sing beda karo liter kopi ing wayah wengi dicampur karo luh lan rambut robek.
  2. Mungkasi / miwiti maneh server yen ana sambungan sing ora kasil, sing mung bisa ditangani kanthi manual.

Kabeh iku prasaja ing kene, conto kode malah ora cocok. skrip 100 baris

Dadi, nalika 10 wong ngumpul lan nampa game kasebut, server diluncurake lan kabeh wong kepengin main, link kanggo nyambung menyang game dikirim ing pesen pribadi.

Kita nulis matchmaking kanggo Dota 2014

Kanthi ngeklik ing link, pamuter nyambung menyang server game, lan banjur iku. Sawise ~ 25 menit, "kamar" virtual karo pemain dibusak.

Aku njaluk ngapura ing advance kanggo awkwardness saka artikel, Aku wis ora nulis kene kanggo dangu, lan ana kakehan kode kanggo nyorot bagean penting. Mie, singkatane.

Yen aku ndeleng kapentingan ing topik, bakal ana bagean liya - bakal ngemot siksa sandi karo plugins kanggo srcds (Sumber darmabakti server), lan, mbokmenawa, sistem HFS lan mini-dotabuff, situs karo statistik game.

Sawetara pranala:

  1. Situs web kita (statistik, leaderboard, landing page cilik lan download klien)
  2. Server Discord

Source: www.habr.com

Add a comment