Бид Dota 2014-д зориулж тохирох тоглоом бичиж байна

Бүгдээрээ сайн байцгаана уу.

Энэ хавар би залуус Dota 2 серверийн 2014 хувилбарыг ажиллуулж, үүний дагуу үүн дээр тоглож сурсан төсөлтэй танилцсан. Би энэ тоглоомын үнэнч шүтэн бишрэгч бөгөөд бага насаа шимтэн үзэх энэ онцгой боломжийг орхиж чадаагүй юм.

Би маш гүн гүнзгий тагтаанд орсон бөгөөд тоглоомын хуучин хувилбарт дэмжигдээгүй бараг бүх функцийг хариуцдаг Discord ботыг бичсэн, тухайлбал тохирол хийх.
Боттой холбоотой бүх шинэчлэлээс өмнө лоббиг гараар бүтээсэн. Бид мессежийн 10 хариуг цуглуулж, гараар сервер угсарч эсвэл орон нутгийн лобби зохион байгуулсан.

Бид Dota 2014-д зориулж тохирох тоглоом бичиж байна

Програмист миний мөн чанар ийм их гарын ажлыг тэсвэрлэж чадахгүй байсан бөгөөд нэг шөнийн дотор 10 хүн байхад серверийг автоматаар босгодог роботын хамгийн энгийн хувилбарыг зурсан.

Би Python-д үнэхээр дургүй, энэ орчинд илүү тухтай санагддаг тул би тэр даруй nodejs дээр бичихээр шийдсэн.

Энэ бол миний анхны Discord-д зориулж бот бичих туршлага, гэхдээ энэ нь маш энгийн зүйл болсон. Албан ёсны npm модуль discord.js нь мессежтэй ажиллах, хариу үйлдэл цуглуулах гэх мэт тохиромжтой интерфейсээр хангадаг.

Анхааруулга: Бүх кодын жишээнүүд нь "одоогийн" бөгөөд шөнийн цагаар дахин бичих хэд хэдэн давталтыг давсан гэсэн үг юм.

Тоглолтын үндэс нь тоглохыг хүссэн тоглогчдыг тоглоомыг хүсэхгүй эсвэл олохгүй үед нь байрлуулж, хасдаг "дараалал" юм.

“Тоглогч” хүний ​​мөн чанар ийм л харагддаг. Эхэндээ энэ нь зүгээр л Discord дахь хэрэглэгчийн id байсан боловч сайтаас тоглоом эхлүүлэх/хайх төлөвлөгөөтэй байгаа ч хамгийн түрүүнд хийх зүйл.

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 хүний ​​​​тоглоом олдсон" үйл явдлын дараа та тоглогчдод шаардлагатай мессежийг хувийн мессежээр илгээж, бизнесийн үндсэн логикийг хэрэгжүүлэх боломжтой - бэлэн байдлыг шалгах, лобби бэлтгэх ажлыг эхлүүлэх. хөөргөх гэх мэт.

ОУОХ-ны хувьд би InversifyJS ашигладаг. Энэ номын сантай ажиллах таатай туршлага надад бий. Хурдан бөгөөд хялбар!

Манай сервер дээр хэд хэдэн дараалал бий - бид 1x1, хэвийн/үнэлгээтэй, хэд хэдэн захиалгат горимыг нэмсэн. Тиймээс хэрэглэгч болон тоглоомын хайлтын хооронд байрладаг singleton 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 хүн ирэх хүртэл хүлээнэ. Хэрэв бүх тоглогчид тоглоомыг зөвшөөрсөн бол сонирхолтой хэсэг эхэлнэ.

Зориулалтын серверийн тохиргоо

Манай тоглоомууд Windows 2012 сервертэй VDS дээр байрладаг. Үүнээс бид хэд хэдэн дүгнэлт хийж болно:

  1. Түүн дээр докер байхгүй байгаа нь миний зүрхийг цохилоо
  2. Бид түрээсээ хэмнэдэг

Даалгавар бол Linux дээрх VPS-ээс VDS дээр процесс ажиллуулах явдал юм. Би Flask дээр энгийн сервер бичсэн. Тийм ээ, би Python-д дургүй, гэхдээ та юу хийж чадах вэ? Энэ сервер дээр энэ серверийг бичих нь илүү хурдан бөгөөд хялбар юм.

Энэ нь 3 функцийг гүйцэтгэдэг:

  1. Серверийг тохиргоотой эхлүүлэх - газрын зураг, тоглоомыг эхлүүлэх тоглогчдын тоо, залгаасуудын багцыг сонгох. Би одоо залгаасуудын талаар бичихгүй - энэ бол шөнө нулимс, урагдсан үстэй холилдсон литр кофены өөр түүх юм.
  2. Амжилтгүй холболт гарсан тохиолдолд серверийг зогсоох/дахин эхлүүлэх, бид үүнийг зөвхөн гараар зохицуулах боломжтой.

Энд бүх зүйл энгийн, кодын жишээ нь тийм ч тохиромжтой биш юм. 100 мөр скрипт

Ингээд 10 хүн нийлж тоглоомоо хүлээж авангуут ​​сервер ажиллуулж, хүн бүр тоглохыг хүсэх үед тоглоомтой холбогдох линкийг хувийн мессежээр илгээсэн байна.

Бид Dota 2014-д зориулж тохирох тоглоом бичиж байна

Холбоос дээр дарснаар тоглогч тоглоомын серверт холбогдож, ингээд л болоо. ~25 минутын дараа тоглогчидтой виртуал "өрөө" цэвэрлэгддэг.

Өгүүллийн эвгүй байдалд урьдчилж хүлцэл өчье, би энд удаан хугацаагаар бичээгүй бөгөөд чухал хэсгүүдийг тодруулахад хэтэрхий олон код байна. Товчхондоо гоймон.

Хэрэв би энэ сэдвийг сонирхож байгаа бол хоёр дахь хэсэг байх болно - энэ нь srcds-д зориулсан залгаасууд (Эх сурвалж зориулагдсан сервер), магадгүй үнэлгээний систем, мини-дотабуфф, тоглоомын статистик бүхий сайтыг агуулсан байх болно.

Зарим холбоосууд:

  1. Манай вэбсайт (статистик, тэргүүлэгчдийн самбар, жижиг хуудас болон үйлчлүүлэгчийн татаж авах)
  2. Discord сервер

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх