為 Dota 2014 撰寫對接會

大家好。

今年春天,我遇到了一個項目,其中的成員學習如何運行 Dota 2 伺服器版本 2014,並相應地在其上進行遊戲。 我是這款遊戲的忠實粉絲,我不能錯過這個沉浸在童年時光的獨特機會。

我研究得很深,碰巧我寫了一個 Discord 機器人,它負責幾乎所有舊版本遊戲不支援的功能,即配對。
在機器人進行所有創新之前,大廳是手動創建的。 我們收集了 10 個對一條訊息的反應,並手動組裝了一個伺服器,或託管了一個本地大廳。

為 Dota 2014 撰寫對接會

我作為程式設計師的本性無法承受如此多的手動工作,連夜我勾勒出了最簡單的機器人版本,當有 10 個人時,它會自動提升伺服器。

我立即決定用nodejs來寫,因為我不太喜歡Python,而且我在這個環境中感覺更舒服。

這是我第一次為 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人遊戲」事件發生時,您可以透過私訊向玩家發送必要的訊息,並執行基本的業務邏輯 - 啟動任務檢查準備情況,準備大廳用於發射等。

對於 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 並顯示“來自 Discord 的用戶想要進入隊列,模式:未評級遊戲。”

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) 這樣我們就到達了「最高」權威-班級 博特。 總的來說,他處理網關(我不明白它在俄語中看起來有多有趣)和牽線搭橋的業務邏輯之間的聯繫。 機器人無意中聽到該事件並命令 DiscordGateway 向所有用戶發送準備檢查。

為 Dota 2014 撰寫對接會

8) 如果有人在 3 分鐘內拒絕或不接受遊戲,那麼我們不會將他們送回隊列。 我們讓其他人都回到隊列中,等待直到再次有 10 個人。 如果所有玩家都接受了遊戲,那麼有趣的部分就開始了。

專用伺服器配置

我們的遊戲託管在Windows Server 2012的VDS上。由此我們可以得出幾個結論:

  1. 上面沒有docker,戳中了我的心
  2. 我們節省房租

任務是從 Linux 上的 VPS 在 VDS 上執行進程。 我在 Flask 中寫了一個簡單的伺服器。 是的,我不喜歡Python,但是你能做什麼?用它來寫這個伺服器更快更容易。

它執行 3 個功能:

  1. 使用組態啟動伺服器 - 選擇地圖、啟動遊戲的玩家數量以及一組外掛程式。 我現在不會寫插件 - 這是一個不同的故事,晚上喝幾公升咖啡,混合著眼淚和撕裂的頭髮。
  2. 連線不成功時停止/重新啟動伺服器,我們只能手動處理。

這裡一切都很簡單,程式碼範例甚至都不合適。 100行腳本

於是,當10人齊聚並接受遊戲時,伺服器啟動了,大家都躍躍欲試,私訊中就發送了連接遊戲的連結。

為 Dota 2014 撰寫對接會

透過點擊鏈接,玩家連接到遊戲伺服器,然後就這樣了。 大約 25 分鐘後,玩家所在的虛擬「房間」就被清理乾淨了。

對於文章的尷尬,我提前表示歉意,我已經很久沒有在這裡寫文章了,而且程式碼太多,無法突出顯示重要部分。 簡而言之,麵條。

如果我發現對該主題感興趣,將會有第二部分- 它將包含我對srcds(來源專用伺服器)插件的折磨,並且可能還有一個評級系統和mini-dotabuff(一個包含遊戲統計數據的網站)。

一些連結:

  1. 我們的網站(統計、排行榜、小型登陸頁面和客戶下載)
  2. 不和諧伺服器

來源: www.habr.com

添加評論