Біз Dota 2014 үшін сәйкестік жазып жатырмыз

Всем привет.

Осы көктемде мен бір жобаға тап болдым, онда балалар Dota 2 серверінің 2014 нұсқасын іске қосуды және сәйкесінше оны ойнауды үйренді. Мен бұл ойынның үлкен жанкүйерімін, сондықтан мен өзімді балалық шағыммен таныстырудың бірегей мүмкіндігін жіберіп алмадым.

Мен өте терең көгершін болдым және мен ойынның ескі нұсқасында қолдау көрсетілмейтін барлық дерлік функционалдылыққа, атап айтқанда сәйкестікке жауап беретін Discord ботын жаздым.
Ботпен барлық инновацияларға дейін лобби қолмен жасалған. Біз хабарламаға 10 реакция жинадық және серверді қолмен жинадық немесе жергілікті лобби ұйымдастырдық.

Біз Dota 2014 үшін сәйкестік жазып жатырмыз

Менің программист табиғатым сонша қол жұмысына төтеп бере алмады, мен бір түнде 10 адам болғанда серверді автоматты түрде көтеретін боттың ең қарапайым нұсқасын сызып алдым.

Мен бірден nodejs-де жазуды шештім, өйткені маған Python ұнамайды және мен бұл ортада өзімді жайлы сезінемін.

Бұл менің Discord үшін бот жазудағы алғашқы тәжірибем, бірақ бұл өте қарапайым болып шықты. Ресми npm модулі discord.js хабарламалармен жұмыс істеу, реакцияларды жинау және т.б. үшін ыңғайлы интерфейсті қамтамасыз етеді.

Жауапкершіліктен бас тарту: Барлық код мысалдары «ағымдағы», яғни түнде қайта жазудың бірнеше итерациясынан өткен.

Сәйкестіктің негізі - ойнағысы келетін ойыншыларды орналастыратын және ойынды қаламаған немесе таппаған кезде алып тастайтын «кезек».

«Ойыншының» мәні осылай көрінеді. Бастапқыда бұл жай ғана 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, қалыпты/рейтингтік және бірнеше реттелетін режимдерді қостық. Сондықтан, пайдаланушы мен ойын іздеу арасында орналасқан синглтон 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)
          }
        });
      }
    );
  }

(Процестер шамамен қандай болатыны туралы түсінік беру үшін кеспелерді кодтаңыз)

Мұнда мен енгізілген ойын режимдерінің әрқайсысы үшін кезекті инициализациялаймын, сонымен қатар кезектерді реттеу және кейбір қақтығыстарды болдырмау үшін «топтардағы» өзгерістерді тыңдаймын.

Сонымен, жарайсың, мен тақырыпқа еш қатысы жоқ код бөліктерін енгіздім, енді тікелей сәйкестікке көшейік.

Жағдайды қарастырайық:

1) Пайдаланушы ойнағысы келеді.

2) Іздеуді бастау үшін ол Gateway=Discord пайдаланады, яғни хабарламаға реакция жасайды:

Біз Dota 2014 үшін сәйкестік жазып жатырмыз

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-ге барлық пайдаланушыларға дайындық тексеруін жіберуге бұйрық береді.

Біз Dota 2014 үшін сәйкестік жазып жатырмыз

8) Егер біреу 3 минут ішінде ойыннан бас тартса немесе қабылдамаса, біз оларды кезекке қайтармаймыз. Қалғандарының барлығын кезекке қайтарып, қайтадан 10 адам болғанша күтеміз. Егер барлық ойыншылар ойынды қабылдаса, онда қызықты бөлік басталады.

Арнайы сервер конфигурациясы

Біздің ойындарымыз VDS серверінде Windows 2012 серверінде орналастырылған. Бұдан біз бірнеше қорытынды жасауға болады:

  1. Жүрегіме тиген докер жоқ
  2. Жалдау ақысын үнемдейміз

Тапсырма Linux жүйесіндегі VPS жүйесінен VDS жүйесінде процесті іске қосу болып табылады. Мен Flask-те қарапайым сервер жаздым. Иә, маған Python ұнамайды, бірақ сіз не істей аласыз? Оған серверді жазу жылдамырақ және оңайырақ.

Ол 3 функцияны орындайды:

  1. Серверді конфигурациямен іске қосу – картаны, ойынды бастайтын ойыншылар санын және плагиндер жинағын таңдау. Мен енді плагиндер туралы жазбаймын - бұл түнде көз жасы мен жыртылған шашпен араласқан литрлік кофе туралы басқа әңгіме.
  2. Сәтсіз қосылымдар болған жағдайда серверді тоқтату/қайта іске қосу, біз оны қолмен ғана өңдей аламыз.

Мұнда бәрі қарапайым, код мысалдары тіпті жарамайды. 100 жолдық сценарий

Сонымен, 10 адам жиналып, ойынды қабылдаған кезде сервер іске қосылып, барлығы ойнауға ынталы болды, ойынға қосылу сілтемесі жеке хабарламаларға жіберілді.

Біз Dota 2014 үшін сәйкестік жазып жатырмыз

Сілтемені басу арқылы ойыншы ойын серверіне қосылады, содан кейін болды. ~25 минуттан кейін ойыншылары бар виртуалды «бөлме» тазартылады.

Мақаланың ыңғайсыздығы үшін алдын ала кешірім сұраймын, мен мұнда ұзақ уақыт жазбадым және маңызды бөлімдерді бөлектеу үшін тым көп код бар. Қысқасы кеспе.

Егер мен тақырыпқа қызығушылық танытатын болсам, онда екінші бөлім болады - онда менің srcds плагиндерімен азаптауым болады (Бөлінген сервер), және, мүмкін, рейтингтік жүйе және мини-дотабуфф, ойын статистикасы бар сайт.

Кейбір сілтемелер:

  1. Біздің веб-сайт (статистика, көшбасшылар тақтасы, шағын бастапқы бет және клиентті жүктеп алу)
  2. Discord сервері

Ақпарат көзі: www.habr.com

пікір қалдыру