Scrittura di matchmaking per Dota 2014

Bonghjornu.

Questa primavera aghju scontru un prughjettu in u quali i picciotti anu amparatu à eseguisce a versione di u servitore Dota 2 2014 è, per quessa, ghjucà nantu à questu. Sò un grande fan di stu ghjocu, è ùn pudia micca passà sta opportunità unica per immerse in a mo zitiddina.

I culombu assai prufonda, è hè accadutu chì aghju scrittu un bot Discord chì hè rispunsevule per quasi tutte e funziunalità chì ùn hè micca supportatu in a versione antica di u ghjocu, vale à dì matchmaking.
Prima di tutte l'innuvazioni cù u bot, u lobby hè statu creatu manualmente. Avemu cullatu 10 reazzioni à un missaghju è assemblatu manualmente un servitore, o ospitu un lobby locale.

Scrittura di matchmaking per Dota 2014

A mo natura cum'è un programatore ùn pudia resiste à tantu travagliu manuale, è per a notte aghju abbozzatu a versione più simplice di u bot, chì hà risuscitatu automaticamente u servitore quandu ci era 10 persone.

Immediatamente decisu di scrive in nodejs, perchè ùn mi piace micca veramente Python, è mi sentu più còmode in questu ambiente.

Questa hè a mo prima sperienza chì scrive un bot per Discord, ma hè diventatu assai simplice. U modulu npm ufficiale discord.js furnisce una interfaccia còmuda per travaglià cù i missaghji, cullezzione di reazzioni, etc.

Disclaimer: Tutti l'esempii di codice sò "attuali", chì significa chì anu passatu per parechje iterazioni di riscrittura di notte.

A basa di matchmaking hè una "queue" in quale i ghjucatori chì volenu ghjucà sò posti è eliminati quandu ùn volenu micca o truvà un ghjocu.

Hè ciò chì l'essenza di un "player" pari. Inizialmente era solu un id d'utilizatore in Discord, ma ci sò piani di lanciari / ricerca di ghjochi da u situ, ma prima cose prima.

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

È quì hè l'interfaccia di fila. Quì, invece di "players", hè aduprata una astrazione in forma di "gruppu". Per un solu ghjucatore, u gruppu hè custituitu da ellu stessu, è per i ghjucatori in un gruppu, rispettivamente, di tutti i ghjucatori in u gruppu.

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
}

Aghju decisu di utilizà avvenimenti per scambià u cuntestu. Era adattatu per i casi - dopu à l'avvenimentu "s'hè trovu un ghjocu per 10 persone", pudete mandà u missaghju necessariu à i ghjucatori in messagi privati, è eseguisce a logica cummerciale basica - lanciate un compitu per verificà a prontezza, preparanu u lobby. per u lanciu, è cusì.

Per IOC aghju utilizatu InversifyJS. Aghju una sperienza piacevule à travaglià cù questa biblioteca. Rapidu è faciule!

Avemu parechje file in u nostru servitore - avemu aghjustatu 1x1, normale / valutatu, è un paru di modi persunalizati. Dunque, ci hè un Singleton RoomService chì si trova trà l'utilizatore è a ricerca di ghjocu.

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

(Codice tagliatelle per dà una idea di ciò chì i prucessi sò apprussimatamente)

Quì aghju inizializatu a fila per ognuna di i modi di ghjocu implementati, è ancu ascolta i cambiamenti in "gruppi" per aghjustà a fila è evità qualchì cunflittu.

Allora, ben fattu, aghju inseritu pezzi di codice chì ùn anu nunda di fà cù u tema, è avà andemu direttamente à u matchmaking.

Fighjemu u casu:

1) L'utilizatore vole ghjucà.

2) Per inizià a ricerca, usa Gateway=Discord, vale à dì, mette una reazione à u missaghju:

Scrittura di matchmaking per Dota 2014

3) Questu gateway va à RoomService è dice "Un utilizatore da discordia vole entre in a fila, modalità: ghjocu senza classificazione".

4) RoomService accetta a dumanda di u gateway è spinge l'utilizatore (più precisamente, u gruppu d'utilizatori) in a fila desiderata.

5) A fila verifica ogni volta chì ci sò abbastanza ghjucatori per ghjucà. Sè pussibule, emette un avvenimentu:

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

6) RoomService hè ovviamente felice à sente ogni fila in anticipazione ansiosa di questu avvenimentu. Ricevemu una lista di ghjucatori cum'è input, formate una "stanza" virtuale da elli, è, sicuru, emette un avvenimentu:

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) Allora avemu ghjuntu à l'autorità "più alta" - a classa Bot. In generale, si tratta di a cunnessione trà e gateways (ùn possu micca capisce quantu hè divertente in russo) è a logica cummerciale di matchmaking. U bot sente l'avvenimentu è urdinà à DiscordGateway per mandà un cuntrollu di prontezza à tutti l'utilizatori.

Scrittura di matchmaking per Dota 2014

8) Se qualcunu rifiuta o ùn accetta micca u ghjocu in 3 minuti, allora ùn li turnemu micca in a fila. Riturnemu tutti l'altri à a fila è aspittemu finu à chì ci sò 10 persone di novu. Se tutti i ghjucatori anu accettatu u ghjocu, allora a parte interessante principia.

Cunfigurazione di u servitore dedicatu

I nostri ghjochi sò allughjati in VDS cù u servitore Windows 2012. Da questu pudemu piglià parechje cunclusioni:

  1. Ùn ci hè micca docker nantu à questu, chì mi culpisce in u core
  2. Salvemu in affittu

U compitu hè di eseguisce un prucessu in VDS da un VPS in Linux. Aghju scrittu un servitore simplice in Flask. Iè, ùn mi piace micca Python, ma chì pudete fà?Hè più veloce è più faciule per scrive stu servitore nantu à questu.

Esegue 3 funzioni:

  1. Cumincià un servitore cù una cunfigurazione - selezziunate una mappa, u numeru di ghjucatori per inizià u ghjocu, è un set di plugins. Ùn scriveraghju micca nantu à i plugins avà - hè una storia diversa cù litri di caffè di notte mischju cù lacrime è capelli strappati.
  2. Stopping / restarting u servitore in casu di cunnessione senza successu, chì pudemu solu trattà manualmente.

Tuttu hè simplice quì, l'esempii di codice ùn sò mancu appruvati. Scrittura di 100 linee

Allora, quandu 10 persone si sò riuniti è accettatu u ghjocu, u servitore hè stata lanciata è tutti eranu ansiosi di ghjucà, un ligame per cunnette à u ghjocu hè statu mandatu in missaghji privati.

Scrittura di matchmaking per Dota 2014

Cliccà nant'à u ligame, u ghjucatore si cunnetta à u servitore di u ghjocu, è questu hè. Dopu ~ 25 minuti, a "stanza" virtuale cù i ghjucatori hè sguassata.

Scusate in anticipu per l'imbarazza di l'articulu, ùn aghju micca scrittu quì per un bellu pezzu, è ci hè troppu codice per mette in risaltu rùbbriche impurtanti. Noodles, in breve.

Se vecu l'interessu in u tema, ci sarà una seconda parte - cuntene u mo turmentu cù plugins per srcds (Source dedicated server), è, probabilmente, un sistema di valutazione è mini-dotabuff, un situ cù statistiche di ghjocu.

Certi ligami:

  1. U nostru situ web (statistiche, classificazione, piccula pagina di destinazione è scaricamentu di u cliente)
  2. Servitore di Discord

Source: www.habr.com

Add a comment