Մենք գրում ենք matchmaking Dota 2014-ի համար

Բարեւ բոլորին

Այս գարնանը ես հանդիպեցի մի նախագծի, որի ընթացքում տղաները սովորեցին, թե ինչպես գործարկել Dota 2 սերվերի 2014-ի տարբերակը և, համապատասխանաբար, խաղալ դրա վրա: Ես այս խաղի մեծ երկրպագու եմ, և ես չէի կարող բաց թողնել իմ մանկության մեջ խորասուզվելու այս բացառիկ հնարավորությունը:

Ես շատ խորն եմ ընկել, և այնպես եղավ, որ ես գրեցի Discord բոտ, որը պատասխանատու է գրեթե բոլոր ֆունկցիոնալության համար, որոնք չեն աջակցվում խաղի հին տարբերակում, այն է՝ matchmaking-ը:
Մինչ բոտի հետ կապված բոլոր նորամուծությունները, լոբբին ստեղծվել է ձեռքով։ Մենք հավաքեցինք հաղորդագրության 10 արձագանք և ձեռքով հավաքեցինք սերվեր կամ հյուրընկալեցինք տեղական լոբբի:

Մենք գրում ենք matchmaking Dota 2014-ի համար

Իմ՝ որպես ծրագրավորողի բնույթը չէր կարող դիմակայել այդքան ձեռքի աշխատանքին, և ես մեկ գիշերվա ընթացքում ուրվագծեցի բոտի ամենապարզ տարբերակը, որն ավտոմատ կերպով բարձրացրեց սերվերը, երբ 10 հոգի կար:

Անմիջապես որոշեցի գրել nodejs-ով, քանի որ Python-ն այնքան էլ չեմ սիրում և այս միջավայրում ինձ ավելի հարմարավետ եմ զգում։

Սա Discord-ի համար բոտ գրելու իմ առաջին փորձն է, բայց պարզվեց, որ շատ պարզ է: Պաշտոնական npm մոդուլը discord.js ապահովում է հարմար ինտերֆեյս հաղորդագրությունների հետ աշխատելու, արձագանքներ հավաքելու և այլն:

Հրաժարում. կոդի բոլոր օրինակները «ընթացիկ» են, ինչը նշանակում է, որ նրանք անցել են գիշերը վերաշարադրելու մի քանի կրկնություններ:

Համապատասխանության հիմքը «հերթն» է, որտեղ խաղացողները, ովքեր ցանկանում են խաղալ, տեղավորվում և հեռացվում են, երբ նրանք չեն ցանկանում կամ խաղ չեն գտնում:

Ահա թե ինչպիսին է «խաղացողի» էությունը. Սկզբում դա ընդամենը օգտատիրոջ id-ն էր 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 հոգու համար խաղ է հայտնաբերվել» իրադարձության դեպքում դուք կարող եք խաղացողներին ուղարկել անձնական հաղորդագրություններով անհրաժեշտ հաղորդագրությունը և իրականացնել հիմնական բիզնես տրամաբանությունը՝ առաջադրանք առաջադրել՝ ստուգելու պատրաստությունը, պատրաստել նախասրահը: գործարկման համար և այլն։

ՄՕԿ-ի համար ես օգտագործում եմ 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)
          }
        });
      }
    );
  }

(Կոդավորեք արիշտա՝ պատկերացում կազմելու համար, թե ինչպիսին են գործընթացները մոտավորապես)

Այստեղ ես նախաստորագրում եմ հերթը իրականացված խաղի յուրաքանչյուր ռեժիմի համար, ինչպես նաև լսում եմ «խմբերի» փոփոխությունները՝ հերթերը կարգավորելու և որոշ կոնֆլիկտներից խուսափելու համար:

Այնպես որ, բրավո, ես տեղադրել եմ կոդի կտորներ, որոնք կապ չունեն թեմայի հետ, իսկ հիմա անցնենք ուղիղ դեպի matchmaking:

Դիտարկենք դեպքը.

1) Օգտագործողը ցանկանում է խաղալ:

2) Որոնումը սկսելու համար նա օգտագործում է Gateway=Discord, այսինքն՝ արձագանքում է հաղորդագրությանը.

Մենք գրում ենք matchmaking 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-ին ուղարկել պատրաստության ստուգում բոլոր օգտատերերին:

Մենք գրում ենք matchmaking Dota 2014-ի համար

8) Եթե ինչ-որ մեկը մերժում է կամ չի ընդունում խաղը 3 րոպեի ընթացքում, ապա մենք ՉԵՆՔ վերադարձնում նրան հերթ: Մնացած բոլորին վերադարձնում ենք հերթ ու սպասում, մինչև նորից 10 հոգի լինի։ Եթե ​​բոլոր խաղացողներն ընդունել են խաղը, ապա սկսվում է հետաքրքիր մասը։

Նվիրված սերվերի կազմաձևում

Մեր խաղերը տեղակայված են VDS-ում Windows սերվերով 2012: Այստեղից մենք կարող ենք մի քանի եզրակացություն անել.

  1. Դրա վրա ոչ մի նավահանգիստ չկա, որը հարվածեց սրտիս
  2. Մենք խնայում ենք վարձով

Խնդիրն այն է, որ VDS-ով պրոցես վարել Linux-ի VPS-ից: Ես գրել եմ պարզ սերվեր Flask-ում: Այո, ես չեմ սիրում Python-ը, բայց ի՞նչ կարող ես անել: Ավելի արագ և հեշտ է այս սերվերը գրել դրա վրա:

Այն կատարում է 3 գործառույթ.

  1. Սերվերի գործարկում կոնֆիգուրացիայով. քարտեզի ընտրություն, խաղը սկսելու խաղացողների թիվը և մի շարք պլագիններ: Ես հիմա չեմ գրի պլագինների մասին. սա այլ պատմություն է գիշերային լիտր սուրճի հետ՝ խառնված արցունքներով և պատառոտված մազերով:
  2. Սերվերի դադարեցում/վերագործարկում անհաջող կապերի դեպքում, որը մենք կարող ենք միայն ձեռքով կարգավորել:

Այստեղ ամեն ինչ պարզ է, կոդերի օրինակները նույնիսկ տեղին չեն: 100 տողանոց սցենար

Այսպիսով, երբ 10 հոգի հավաքվեցին և ընդունեցին խաղը, սերվերը գործարկվեց, և բոլորը ցանկանում էին խաղալ, խաղին միանալու հղումը ուղարկվեց անձնական հաղորդագրություններով:

Մենք գրում ենք matchmaking Dota 2014-ի համար

Սեղմելով հղման վրա՝ խաղացողը միանում է խաղի սերվերին, և վերջ: ~25 րոպե անց խաղացողների հետ վիրտուալ «սենյակը» մաքրվում է:

Նախապես ներողություն եմ խնդրում հոդվածի անհարմարության համար, ես երկար ժամանակ չէի գրել այստեղ, և չափազանց շատ ծածկագիր կա կարևոր բաժինները ընդգծելու համար: Լապշա, մի խոսքով:

Եթե ​​տեսնեմ հետաքրքրություն թեմայի նկատմամբ, կլինի երկրորդ մասը՝ այն կպարունակի իմ տանջանքները srcds-ի համար նախատեսված փլագիններով (Source dedicated server), և, հավանաբար, վարկանիշային համակարգ և մինի-dotabuff, խաղային վիճակագրությամբ կայք։

Որոշ հղումներ.

  1. Մեր կայքը (վիճակագրություն, առաջատարներ, փոքր վայրէջք էջ և հաճախորդի ներբեռնում)
  2. Discord սերվեր

Source: www.habr.com

Добавить комментарий