基于 WebRTC 的云游戏开源项目(四)

用Golang编写CloudRetro

在开发者内部实施Go

启动Go频道

多亏Go的频道设计精美,事件流和并发问题才得以极大简化。如下图所示,在不同的GoRoutine中有多个并行运行的组件。每个组件管理自己的状态,用通道通信。Golang的select语句强制在每个游戏帧时都处理一个基元事件。这意味着此设计不需要锁定。例如,用户要存档时,是需要一张完整的游戏状态快照的。该状态直到保存完成之前,都需要通过运行输入保持不间断。在每个游戏帧中,后端只能处理保存操作或输入操作,因此操作的同期是很安全的。

func (e *gameEmulator) gameUpdate() {
for {
    select {
        case <-e.saveOperation:
            e.saveGameState()
        case key := <-e.input:
            e.updateGameState(key)
        case <-e.done:
            e.close()
            return
    }
    }
}

扇入/扇出

这个Golang模式完全匹配我的CrowdPlay和Multiple Player用例。按照这种模式,同一房间中的所有用户输入都会扇入一个中央输入通道。之后,我们可以将游戏媒体扇出给同一房间中的所有用户。因此,我们实现了来自不同用户的,多个游戏会话之间的游戏状态共享。

不同会话也可同步

Golang的缺点

Golang并不完美——它的频道很慢。与锁定相比,Go通道只是处理并发和流事件的一种更简单的方法。但是通道并不能提供最佳性能。通道后隐藏着一个复杂的锁定逻辑。因此,通过在替换通道时重新应用锁定和基远值,我对性能进行了一些调整以优化性能。

此外,Golang废物收集器是不可控的。所以有时运行会有一些长时间停顿。这极大地损害了该流应用程序的实时性。

CGO

该项目使用了一些现有的Golang开源VP8 / H264库进行媒体压缩,并将Libretro应用于游戏模拟器。所有这些库都使用了CGO,只是Go中对C库的包装。你可以参考Dave的博客文章,从中可以了解更多该操作的问题。其中,我面临的问题是:

  • 即使使用Golang Recovery,我也无法捕获CGO中的崩溃;
  • 当我们无法在CGO下检测细粒度的问题时,就无法定义性能瓶颈到底在哪儿。

结论

我成功揭开了云游戏服务的面纱,并创建了一个平台。在平台上我和朋友可以在线玩复古游戏。如果没有Pion库和Pion社区的支持,我不可能完成这个项目是。所以我非常感谢Pion及其集中开发。WebRTC和Pion提供的简单API可以实现平稳的集成。尽管我之前对端对端(P2P)并不了解。但我的第一个概念证明还是在同一周发布了。

尽管集成起来很简单,但是P2P流媒体的确是计算机科学中一个非常难的领域。它必须处理好IP和NAT等常年网络架构的复杂性,才能创建端对端会话。在钻研此项目时,我积累了许多有关网络和性能优化的宝贵知识。因此,我建议所有人尝试用WebRTC创建一些P2P产品。

CloudRetro能满足我作为复古游戏玩家需要的所有用例。但是,我认为该项目可以在很多领域进行改进。例如使网络更可靠,性能更高,提供更高图像质量的游戏,或在用户之间共享游戏。我正在努力改善这几点。

文章地址:https://webrtchacks.com/open-source-cloud-gaming-with-webrtc/

原文作者:Thanh Nguyen

填写常用邮箱,接收社区更新

WebRTC 中文社区由

运营