介绍 HumbleNet:一个跨平台网络库,可在浏览器中运行

HumbleNet 起源于 2015 年 Humble Bundle 的一个项目,最初是为了支持一个将点对点多人游戏移植到 asm.js,现在则移植到 WebAssembly 的计划。2016 年,Mozilla 的网页游戏项目发现了为网页游戏启用 UDP (用户数据报协议) 网络支持的需求,并询问是否可以与 Humble Bundle 合作将该项目作为开源发布。Humble Bundle 欣然同意,Mozilla 与 OutOfOrder.cc 合作对 HumbleNet 进行打磨和文档化。今天,我们发布了该库的 1.0 版本!

为什么又要一个新的网络库?

HumbleNet 的想法刚出现的时候,我们就知道可以使用 WebSockets 来实现网页上的多人游戏。这种方法需要我们用 WebSockets 替换整个协议(Quake 3 的 asm.js 移植版本采用的方法),或者通过 WebSocket 连接将 UDP 流量隧道化到中央位置的 UDP 服务器。

这两种方法都需要一个中间人来处理所有客户端之间的网络流量,才能正常工作。WebSockets 适用于需要可靠有序通信通道的游戏,但实时游戏需要更低的延迟解决方案。大多数实时游戏更关心接收最新的数据,而不是按顺序获取所有数据。WebRTC 基于 UDP 的数据通道完美地满足了这一需求。HumbleNet 提供了一个围绕 WebRTC 的易于使用的 API 包装器,可以使用 WebRTC 数据通道在客户端之间建立实时 UDP 连接。

HumbleNet 到底是什么?

HumbleNet 是一个简单的 C API,它包装了 WebRTC 和 WebSockets,并隐藏了浏览器和非浏览器平台之间的所有平台差异。该库的当前版本公开了一个简单的点对点 API,允许进行基本的对等发现,以及能够轻松地将数据(通过 WebRTC)发送给其他对等方。这样,您可以构建一个可在 Linux、macOS 和 Windows 上运行的游戏,同时可以使用任何 Web 浏览器 - 它们都可以通过 WebRTC 进行实时通信。 这意味着游戏不需要中央服务器(除了对等发现)来处理网络流量。对等方可以直接相互通信。

HumbleNet 本身使用单个 WebSocket 连接来管理对等发现。此连接仅处理诸如“让我向您进行身份验证”和“名为“bobs-game-server”的服务器的对等方 ID 是什么”以及“将我连接到对等方 #2345”之类的请求。 建立对等连接后,游戏将通过 WebRTC 直接通信。

HumbleNet 演示

我们已将 HumbleNet 集成到 Quake 2Quake 3 的 asm.js 移植版本中,我们还提供了一个 简单的 Unity3D 演示

这是一个我与自己对战 Quake 3 的简单视频。一个游戏在 Firefox 54(通用版)中运行,另一个在 Firefox 开发者版中运行。

入门

您可以在 https://humblenet.github.io/ 找到预构建的可再发行文件。这些文件包括适用于 Linux、macOS、Windows、C# 包装器、Unity3D 插件和 emscripten(用于针对 asm.js 或 WebAssembly)的二进制文件。

启动对等服务器

阅读网站上关于对等服务器的文档。一般来说,对于本地开发,只需启动对等服务器即可。默认情况下,它将在端口 8080 上以非 SSL 模式运行。

使用 HumbleNet API

初始化库

要初始化 HumbleNet,只需调用 humblenet_init(),然后调用 humblnet_p2p_init()。第二个调用将使用指定的凭据启动与对等服务器的连接。

humblenet_init();

// this initializes the P2P portion of the library connecting to the given peer server with the game token/secret (used by the peer server to validate the client).
// the 4th parameter is for future use to authenticate the user with the peer server

humblenet_p2p_init("ws://localhost:8080/ws", "game token", "game secret", NULL);

获取您的本地对等方 ID

在您向其他对等方发送任何数据之前,您需要知道自己的对等方 ID 是什么。可以通过定期轮询 humblenet_p2p_get_my_peer_id() 函数来实现。

// initialization loop (getting a peer)
static PeerId myPeer = 0;

while (myPeer == 0) {
  // allow the polling to run
  humblenet_p2p_wait(50);

  // fetch a peer
  myPeer = humblenet_p2p_get_my_peer_id();
}

发送数据

要发送数据,我们调用 humblenet_p2p_sendto。第三个参数是发送模式类型。目前 HumbleNet 实现两种模式:SEND_RELIABLE 和 SEND_RELIABLE_BUFFERED。 缓冲版本将尝试对多个小消息进行本地缓冲,并将一个较大的消息发送到另一个对等方。它们将在另一端透明地被拆分。

void send_message(PeerId peer, MessageType type, const char* text, int size)
{
  if (size > 255) {
    return;
  }

  uint8_t buff[MAX_MESSAGE_SIZE];

  buff[0] = (uint8_t)type;
  buff[1] = (uint8_t)size;

  if (size > 0) {
    memcpy(buff + 2, text, size);
  }

  humblenet_p2p_sendto(buff, size + 2, peer, SEND_RELIABLE, CHANNEL);
}

初始连接到对等方

第一次连接到对等方时,您将必须在建立连接时多次发送初始消息。这里的基本方法是每秒发送一条问候消息,并在收到确认响应之前等待,然后再假定该对等方已连接。因此,任何应用程序至少需要 3 种消息类型:HELLO、ACK 和某种 DATA 消息类型。

if (newPeer.status == PeerStatus::CONNECTING) {
  time_t now = time(NULL);

  if (now > newPeer.lastHello) {
    // try once a second
    send_message(newPeer.id, MessageType::HELLO, "", 0);
    startPeerLastHello = now;
  }
}

检索数据

要实际检索已发送到您的对等方的数据,您需要使用 humblenet_p2p_peekhumblenet_p2p_recvfrom。如果您假设所有数据包都小于最大大小,那么可以执行类似这样的简单循环来处理任何待处理的消息。 注意:大于缓冲区大小的消息将被截断。使用 humblenet_p2p_peek,您可以查看指定通道的下一条消息的大小。

uint8_t buff[MAX_MESSAGE_SIZE];
bool done = false;

while (!done) {
  PeerId remotePeer = 0;

  int ret = humblenet_p2p_recvfrom(buff, sizeof(buff), &remotePeer, CHANNEL);

  if (ret < 0) {
    if (remotePeer != 0) {
      // disconnected client
    } else {
      // error
      done = true;
    }
  } else if (ret > 0) {
    // we received data process it
    process_message(remotePeer, buff, sizeof(buff), ret);
  } else {
    // 0 return value means no more data to read
    done = true;
  }
}

关闭库

要断开与对等服务器、其他客户端的连接并关闭库,只需调用 humblenet_shutdown

humblenet_shutdown();

查找其他对等方

HumbleNet 目前提供了一种简单的“DNS”式方法来定位其他对等方。 要使用它,您只需在客户端注册一个名称,然后在其他客户端上创建一个虚拟对等方。以 Quake3 的客户端-服务器风格方法为例 - 让您的服务器将它的名称注册为“awesome42”。

humblenet_p2p_register_alias("awesome42");

然后,在您的其他对等方上,为 awesome42 创建一个虚拟对等方。

PeerID serverPeer = humblenet_p2p_virtual_peer_for_alias("awesome42");

现在,客户端可以将数据发送到 serverPeer,HumbleNet 会在解析名称后将虚拟对等方转换为实际对等方。

我们路线图上有两个系统将改进对等发现系统。 一个是事件系统,它允许您请求解析一个对等方,然后在它被解析时通知您。另一个是真正的大厅系统,它允许您创建、搜索和加入大厅,作为一种更通用的查找开放游戏的方式,而无需事先知道任何名称。

开发路线图

我们有一个路线图,说明我们计划在该项目发布后添加哪些内容。请关注 HumbleNet 网站 以获取最新开发情况。

未来的工作项目包括

  1. 事件 API
    1. 允许使用简单的 SDL2 风格的轮询事件系统,以便游戏代码可以更轻松地以更干净的方式检查来自对等服务器的各种事件,例如连接、断开连接等。
  2. 大厅 API
    1. 使用事件 API 来构建一种在对等服务器上创建大厅的方式,以便定位游戏会话(而不是必须注册别名)。
  3. WebSocket API
    1. 添加对使用干净简单的 API 轻松连接到任何 websocket 服务器的支持。

我该如何贡献?

如果您想帮助并为该项目做出贡献,HumbleNet 在 GitHub 上进行开发:https://github.com/HumbleNet/humblenet/。使用问题跟踪器和拉取请求来贡献代码。请务必阅读 CONTRIBUTING.md 指南,了解如何创建拉取请求。

关于 Edward Rudd

更多 Edward Rudd 的文章……

关于 Andre Vrignaud

Andre Vrignaud 是 Mozilla 混合现实项目的平台策略主管,他指导着 Mozilla 为实现开放、可持续的 3D 网络和生态系统而付出的努力。另外,他还会关注网络中立和隐私倡导。

更多 Andre Vrignaud 的文章……