こんにちは。
この春、私は彼らが Dota 2 サーバー バージョン 2014 の実行方法を学び、それに従ってプレイするプロジェクトに出会いました。 私はこのゲームの大ファンなので、子供時代に浸れるこのユニークな機会を逃すわけにはいきませんでした。
私は非常に深く研究し、たまたま古いバージョンのゲームでサポートされていないほぼすべての機能、つまりマッチメイキングを担当する Discord ボットを作成することができました。
ボットによるあらゆる革新が行われる前は、ロビーは手動で作成されていました。 メッセージに対する 10 件の反応を収集し、手動でサーバーを構築するか、ローカル ロビーをホストしました。
プログラマーとしての私の性質は、あまりにも多くの手作業に耐えることができず、一晩かけて、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 人用のゲームが見つかった」というイベントが発生すると、プライベート メッセージでプレイヤーに必要なメッセージを送信し、基本的なビジネス ロジックを実行することができます。準備状況を確認するタスクを起動し、ロビーを準備します。打ち上げなどに。
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 を使用します。つまり、メッセージに反応します。
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 に準備状況チェックをすべてのユーザーに送信するよう命令します。
8) 誰かが 3 分以内にゲームを拒否または受け入れなかった場合、その人をキューに戻すことはありません。 他の全員を列に戻し、再び 10 人になるまで待ちます。 すべてのプレイヤーがゲームに同意した場合、興味深い部分が始まります。
専用サーバー構成
私たちのゲームは Windows Server 2012 の VDS でホストされています。これから、いくつかの結論を導き出すことができます。
- 港湾労働者がいないことに私は心を打たれました
- 家賃を節約します
タスクは、Linux 上の VPS から VDS 上のプロセスを実行することです。 Flaskで簡単なサーバーを書きました。 はい、私は Python は好きではありませんが、何ができますか? このサーバーを Python 上で作成する方が速くて簡単です。
次の 3 つの機能を実行します。
- 構成を使用してサーバーを起動します。マップ、ゲームを開始するプレーヤーの数、およびプラグインのセットを選択します。 プラグインについては今は書きません。夜に何リットルものコーヒーに涙と破れた髪が混ざった場合は別の話になります。
- 接続が失敗した場合のサーバーの停止/再起動。これは手動でのみ処理できます。
ここではすべてが単純であり、コード例は適切ではありません。 100行のスクリプト
10 人が集まってゲームを承認すると、サーバーが起動し、全員がプレイしたいと熱望し、ゲームに接続するためのリンクがプライベート メッセージで送信されました。
リンクをクリックすると、プレーヤーはゲーム サーバーに接続するだけです。 約 25 分後、プレイヤーがいる仮想「ルーム」はクリアされます。
記事がぎこちないことをあらかじめお詫びしておきます。長い間ここに書いていなかったので、コードが多すぎて重要なセクションを強調できませんでした。 要するに麺類。
このトピックに興味があれば、第 XNUMX 部が作成されるでしょう。そこには、srcds (ソース専用サーバー) 用のプラグインと、おそらく評価システムとミニ dotabuff、ゲーム統計のサイトに関する私の苦痛が含まれます。
いくつかのリンク:
出所: habr.com