为Dota 2014撰写对接会

你好。



今年春天,我遇到了一个项目,他们在其中学习了如何运行2014版的Dota 2服务器,并据此进行操作。我是这个游戏的忠实拥护者,我不能错过进入童年时代的难得机会。



我投入了很深的精力,碰巧我写了一个Discord机器人,它负责游戏的旧版本中不支持的几乎所有功能,即配对。

在使用机器人进行所有创新之前,大厅是手动创建的。我们收集了对一条消息的10条回复,并手动组装了服务器,或托管了一个本地游说机构。







我作为程序员的天性不能忍受太多的手动工作,一夜之间,我绘制了最简单的bot版本,该机器人在招募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模式,正常/评级和几个自定义模式。因此,在用户和游戏搜索之间存在一个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,即对消息作出响应:







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向所有用户发送准备情况检查。







8)如果有人在3分钟内拒绝或不接受游戏,则我们不会将他们退回队列。我们将其他所有人返回队列,等待10人再次被征募。如果所有玩家都接受了游戏,那么有趣的部分开始。



专用服务器配置



我们的游戏托管在Windows Server 2012的VDS上。由此可以得出一些结论:



  1. 上面没有码头工人,这让我很震惊
  2. 我们节省租金


任务是在Linux上的VPS和VPS上启动该过程。在Flask中编写了一个简单的服务器。是的,我不喜欢python,但是该怎么办-在此服务器上编写此服务器更快,更轻松。



它具有3个功能:



  1. 服务器启动并进行配置-选择地图,启动游戏的玩家人数以及一组插件。我现在不再写插件了-这是一个单独的故事,晚上有几升咖啡,夹杂着眼泪和头发。
  2. 在连接失败的情况下停止/重新启动服务器,我们只能手动处理。


这里的一切都很简单,代码示例甚至不合适。100行的脚本



因此,当10个人聚在一起接受游戏时,服务器正在运行,并且每个人都渴望玩游戏,因此连接到游戏的链接会包含私人消息。







通过单击链接,播放器将连接到游戏服务器,仅此而已。大约25分钟后,将清除与玩家的虚拟“房间”。



对于本文的尴尬之处,我事先表示歉意,我已经在这里写了很长时间了,并且没有太多代码可以突出重点部分。简而言之,面条。



如果我对该主题感兴趣,那么将有第二部分-它将包含我对srcds(源专用服务器)插件的煎熬,以及大概的评分系统和mini-dotabuff(具有游戏统计信息的网站)。



几个链接:



  1. 我们的网站(统计信息,页首横幅,小型Landos和客户端下载)
  2. 不和谐服务器



All Articles