Dota 2014 jaoks kosjasobide kirjutamine

Tere kõigile.

Sel kevadel puutusin kokku projektiga, mille käigus poisid õppisid Dota 2 serveri versiooni 2014 käivitama ja vastavalt sellele mängima. Olen selle mängu suur fänn ja ma ei saanud jätta kasutamata seda ainulaadset võimalust sukelduda oma lapsepõlve.

Sukeldusin väga sügavalt ja juhtus nii, et kirjutasin Discordi boti, mis vastutab peaaegu kõigi nende funktsioonide eest, mida mängu vanas versioon ei toeta, nimelt kosjasobitamise eest.
Enne kõiki uuendusi robotiga loodi fuajee käsitsi. Kogusime sõnumile 10 reaktsiooni ja koostasime käsitsi serveri või hostisime kohalikku fuajeet.

Dota 2014 jaoks kosjasobide kirjutamine

Minu programmeerija iseloom ei pidanud nii suurele käsitsitööle vastu ja üleöö visandasin roboti kõige lihtsama versiooni, mis 10 inimese olemasolul serveri automaatselt üles tõstis.

Otsustasin kohe kirjutada nodejs, sest Python mulle väga ei meeldi ja tunnen end selles keskkonnas mugavamalt.

See on minu esimene kogemus Discordi jaoks roboti kirjutamisega, kuid see osutus väga lihtsaks. Ametlik npm-moodul discord.js pakub mugavat liidest sõnumitega töötamiseks, reaktsioonide kogumiseks jne.

Kohustustest loobumine: kõik koodinäited on praegused, mis tähendab, et nad on öösel korduvalt ümber kirjutanud.

Kohtumise aluseks on "järjekord", kuhu mängijad, kes tahavad mängida, paigutatakse ja eemaldatakse, kui nad mängu ei soovi või ei leia.

Selline näeb välja “mängija” olemus. Algselt oli see Discordis lihtsalt kasutaja ID, kuid plaanitakse saidilt mänge käivitada/otsida, kuid kõigepealt.

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

Ja siin on järjekorra liides. Siin kasutatakse "mängijate" asemel abstraktsiooni "rühma" kujul. Üksikmängija puhul koosneb grupp temast endast ja grupi mängijate puhul vastavalt kõikidest grupi mängijatest.

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
}

Otsustasin konteksti vahetamiseks kasutada sündmusi. See sobis juhtumiteks - üritusel "leiti mäng 10 inimesele" saab saata mängijatele privaatsõnumites vajaliku sõnumi ja viia läbi põhiline äriloogika - käivitada ülesanne valmisoleku kontrollimiseks, fuajee ettevalmistamine käivitamiseks ja nii edasi.

IOC jaoks kasutan InversifyJS-i. Mul on selle raamatukoguga töötades meeldiv kogemus. Kiire ja lihtne!

Meie serveris on mitu järjekorda - oleme lisanud 1x1, tavaline/reiting ja paar kohandatud režiimi. Seetõttu on kasutaja ja mänguotsingu vahel üksik RoomService.

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

(Nuudlid koodiga, et anda aimu, kuidas protsessid umbkaudu välja näevad)

Siin initsialiseerin järjekorra iga rakendatud mängurežiimi jaoks ja kuulan ka muudatusi "rühmades", et järjekordi kohandada ja konflikte vältida.

Niisiis, hästi tehtud, sisestasin kooditükke, millel pole teemaga mingit pistmist, ja liigume nüüd otse kosjasobituse juurde.

Vaatleme juhtumit:

1) Kasutaja soovib mängida.

2) Otsingu alustamiseks kasutab ta Gateway=Discord, st paneb sõnumile vastuse:

Dota 2014 jaoks kosjasobide kirjutamine

3) See lüüs läheb teenusesse RoomService ja ütleb: "Discordist pärit kasutaja soovib siseneda järjekorda, režiim: reitinguta mäng."

4) RoomService aktsepteerib lüüsi päringu ja lükkab kasutaja (täpsemalt kasutajarühma) soovitud järjekorda.

5) Järjekord kontrollib iga kord, kui mängimiseks on piisavalt mängijaid. Võimalusel edastage sündmus:

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

6) RoomService kuulab ilmselgelt rõõmsalt iga järjekorda selle sündmuse ootuses. Saame sisendiks mängijate nimekirja, moodustame neist virtuaalse “toa” ja loomulikult väljastame ürituse:

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) Nii jõudsime "kõrgeima" autoriteedi - klassini Bot. Üldiselt tegeleb ta seostega gateway’de (ma ei saa aru, kui naljakas see vene keeles välja näeb) ja kosjasobitamise äriloogika vahel. Bot kuulab sündmust pealt ja käsib DiscordGatewayl saata kõikidele kasutajatele valmisolekukontroll.

Dota 2014 jaoks kosjasobide kirjutamine

8) Kui keegi keeldub või ei võta mängu 3 minuti jooksul vastu, siis me teda järjekorda EI tagasta. Kõik ülejäänud paneme järjekorda tagasi ja ootame, kuni on jälle 10 inimest. Kui kõik mängijad on mängu vastu võtnud, algab huvitav osa.

Spetsiaalse serveri konfiguratsioon

Meie mänge hostitakse VDS-is koos Windows Server 2012-ga. Sellest saame teha mitmeid järeldusi:

  1. Sellel pole dokkerit, mis tabas mind südamesse
  2. Säästame üüri pealt

Ülesanne on protsessi käivitamine VDS-is Linuxi VPS-ist. Kirjutasin lihtsa serveri Flaskis. Jah, Python mulle ei meeldi, aga mida teha? Selle serveri kirjutamine on kiirem ja lihtsam.

See täidab 3 funktsiooni:

  1. Serveri käivitamine konfiguratsiooniga – kaardi valimine, mängijate arv mängu alustamiseks ja pluginate komplekt. Pistikprogrammidest ma nüüd ei kirjuta – see on hoopis teine ​​lugu, kui öine liitrine kohv on segatud pisarate ja rebenenud juustega.
  2. Serveri seiskamine/taaskäivitamine ebaõnnestunud ühenduste korral, millega saame hakkama vaid käsitsi.

Siin on kõik lihtne, koodinäited pole isegi sobivad. 100 rea skript

Niisiis, kui 10 inimest said kokku ja mängu vastu võtsid, pandi server käima ja kõik olid mänguhimulised, saadeti privaatsõnumites link mänguga ühenduse loomiseks.

Dota 2014 jaoks kosjasobide kirjutamine

Lingil klõpsates loob mängija ühenduse mänguserveriga ja ongi kõik. Umbes 25 minuti pärast tühjendatakse mängijatega virtuaalne “tuba”.

Vabandan juba ette artikli kohmakuse pärast, ma pole siia pikka aega kirjutanud ja oluliste jaotiste esiletõstmiseks on liiga palju koodi. Nuudlid, lühidalt.

Kui ma näen teema vastu huvi, tuleb ka teine ​​osa - see sisaldab minu piina srcd-de pistikprogrammidega (spetsiaalne allikas) ja tõenäoliselt reitingusüsteemi ja mini-dotabuffi, mängustatistika saiti.

Mõned lingid:

  1. Meie veebisait (statistika, edetabel, väike sihtleht ja kliendi allalaadimine)
  2. Discordi server

Allikas: www.habr.com

Lisa kommentaar