【发布时间】:2010-09-29 04:01:47
【问题描述】:
您如何将回合制游戏服务器建模为 RESTful API?例如,国际象棋服务器,您可以在其中与相同 API 的另一个客户端下棋。您需要某种方式来请求和与其他客户协商游戏,以及玩游戏的各个动作的某种方式。
这是 REST (RESTful) API 的理想选择吗?还是应该以不同的方式建模?
【问题讨论】:
您如何将回合制游戏服务器建模为 RESTful API?例如,国际象棋服务器,您可以在其中与相同 API 的另一个客户端下棋。您需要某种方式来请求和与其他客户协商游戏,以及玩游戏的各个动作的某种方式。
这是 REST (RESTful) API 的理想选择吗?还是应该以不同的方式建模?
【问题讨论】:
我在想这样的事情:
/game/<gameID>/move/<moveID>
就基本资源而言。我不确定如何处理“其他玩家移动了吗?”想法,虽然。我曾考虑过在播放移动之前简单地阻止 GET 请求 - 即我的客户会将我的移动坐标放置到
/game/13/move/1
然后会得到
/game/13/move/2
服务器不会立即响应,但会保持连接打开,直到其他玩家移动(即 PUT 到该位置)。这就是中岛所说的“彗星式”吗?
Charlie,我不太清楚你所说的“令牌”是什么意思——这是否可以在不需要轮询或阻塞连接的情况下解决相同的问题?
对于玩家 ID,将其建模为 URL 中的资源是否有意义?我打算简单地使用 HTTP 用户身份验证(其中用户/通行证作为每个请求的一部分发送)。您仍然可以在没有身份验证的情况下获取大多数资源,但是如果您尝试这样做,例如,
PUT /game/13/move/2
如果您没有该游戏的正确凭据,它会给您一个权限被拒绝错误。
【讨论】:
好的,REST 的基本思想是您正在转移状态;您希望服务器上很少或没有“会话状态”。所以你不会想要使用会话状态和keepalive,这就是Comet 所做的。但是想想一个邮件游戏的例子:你们都有一个棋盘副本,并且你们交换动作。邮局不知道这个游戏。
现在,我承认,当我想到它时,我的脑海中就会出现这种情况——事实上,我可能会根据这个问题写一篇文章——但这是我的想法,就像一些故事一样:
就服务器状态而言,您不需要太多东西 --- 尽管您可能希望通过跟踪移动等来扩展它,比如排名 --- 并且可以计算谁有权移动的问题完全来自棋盘页面:如果你有权限,你有一个输入动作的表格;当您发回表单返回时,响应会返回一个页面给您,没有用于输入移动的位置。
我所说的“令牌”只是指“我的举动”/“你的举动”这一状态的任意表示。
好像你需要的资源是
【讨论】:
您要建模的资源是什么?我似乎有四个:你,你的对手,特定的游戏(会话,实例)和游戏板状态。所以它会从类似的东西开始
/game
/game/gameID/gamer/gamerID
/game/gameID/board
我们对InfoQ 有一个很好的介绍/概述。
【讨论】:
我认为 REST 不是这样的应用程序的好选择。您需要执行的转换和操作(进行移动、查看移动历史记录、撤消、获取建议、转出通知)并不能巧妙地映射到 REST 的资源概念。 (如果考虑一下用于更复杂的回合制游戏(如 Scrabble 或 Monopoly)的 RESTful API 的外观,困难可能会更加明显。)
我认为任何明智的 REST API 最终都可能成为非 RESTful 的包装器,例如来回发送 portable game notation 的有状态协议。
【讨论】:
谢谢,查理。我仍然不清楚你是如何在你的计划中得到对手行动的通知的。当然,谁有权移动的问题可以简单地计算出来——要么从棋盘资源中计算出来,要么通过使用明确说明轮到谁移动的单独资源来计算。但是客户怎么知道这个资源已经改变了呢?它是否必须简单地不断轮询,记住以前的状态,直到它注意到发生了变化?在 Post Office 模型中,Post Office 将消息“推送”到客户端(您的邮箱),这在 HTTP 中是不可能的。
我想这是一个更普遍的问题的一部分:如果我想要监视 REST 资源的更改或修改,那么最好的方法是什么?服务器可以做些什么来使客户端更容易做到这一点?
我认为我实际上会将其作为一个单独的问题发布,因为我认为它本身很有趣。
编辑添加:What is a RESTful way of monitoring a REST resource for changes?
【讨论】:
planet.jabber 的一位开发人员参与了在线国际象棋社区Chesspark。他们广泛使用 Jabber/XMPP;如果我没记错的话,these are his posts on the subject。
XMPP 是一种即时消息协议,大致基于小型 XML 消息交换。大多数语言都有库,包括 Javascript。不过,我不确定它是否适合您的问题。
【讨论】:
我认为你可以建模 RESTful。 实施这将更加困难,因为您要么需要comet) 式的解决方案,要么必须通过 AJAX 以相对较短的间隔轮询服务器。
就如何公开 RESTful 界面而言,我想说的是,您需要一个带有坐标的棋盘、可以占据这些坐标的棋子以及改变这些坐标的动作。
当玩家移动时,将创建一个新动作。在验证以确保它被允许之后,您将更新游戏的状态,然后呈现更新 UI 所需的任何响应。
所以我基本上就是这样建模的。不过,实施方面是我认为更大的困难。
【讨论】:
我不认为这一切都那么复杂,中岛。你会传递数据,比如 JSON,用于棋盘位置、移动,以及谁有下一步移动的标记。这就像通过邮件玩一样。
你首先去游戏并寻找合作伙伴,所以
/game/
为您提供等待的人员列表。当你进来时,如果没有人在等你,你会得到一个游戏ID;否则,您选择等待的人并获取他们拥有的游戏 ID。
/game/gameID
向您展示棋盘。你需要一些东西来决定谁玩白棋,我会把它留作练习。 GET 操作为您提供棋盘,因此 POST 发送移动;如果你没有移动,你会得到一个错误。您可以通过下一个 GET 找到结果。
见鬼,在这个模型中我什至没有使用玩家 ID,虽然它可能很好,所以没有人可以作为一个 kibitzer 潜入游戏。
【讨论】:
所以你的想法是,不是将动作作为第一类对象,而是将每个动作都视为游戏本身的更新?这当然是一种不同的方法,尽管出于几个原因,我认为我更愿意将动作对象拆分为它自己的第一类实体。最大的原因是我相信它更具可测试性。移动是否有效可以存在于动作对象中,而无需担心棋盘始终处于有效状态。当然,我不知道这两种方法会带来什么,但这对我来说感觉更好。
另一个你可以通过 YAGNI 完全反驳的原因是,一流的动作对象会提供移动历史。也许很有趣,但除非有要求,否则这是一个有争议的问题。
【讨论】:
对于象棋这样的简单游戏,实际上只是定义媒体类型。
下面是一个示例,它可能是一个过度简化的媒体类型来模拟国际象棋游戏。
我将跳过可能在同一服务器上运行的多个游戏的管理,而只是为已经运行的游戏建模。
第一步通常是为应用程序定义一个索引。
index
游戏的入口点。获取此内容以发现有关游戏的信息。
有效负载可能如下所示:
{
"links": {
"self": "http://my-chess-game.host/games/123",
"player": "http://my-chess-game.host/players/1",
"player": "http://my-chess-game.host/players/2",
"me": "http://my-chess-game.host/players/1",
...
}
"board": [
{
"x": 0,
"y": 1,
"piece": null,
"rel": "space",
"href": "http://my-chess-game/.../boards/123/0/1/something-random-to-discourage-uri-construction"
},
{
"x": 1,
"y": 2,
"rel": "space",
"href": "...",
"piece": {
"player": "http://my-chess-game/.../players/1",
"type": "http://my-chess-game/pieces/Bishop",
"rel": "piece",
"href": "http://my-chess-game/games/123/pieces/player1/Bishop/1",
"links": [
{ "rel": "move": "href": "http://my-chess-game/.../boards/123/..." },
...
]
}
},
...
]
}
move
将 JSON 有效负载发布到标有 rel 或 move 的链接以移动片段。必须包含以下字段:
成功的响应的状态码为 200,并且将包含一个与 index 有效负载相同的实体,并具有游戏的更新状态。
如果不允许用户将他的棋子移到那里,或者轮不到他,则为 400。
player
获取玩家的描述。
响应中必须包含以下字段:
piece
片段嵌入在index 有效负载中,但可以单独存在。每个piece 必须具有以下字段:
每件作品都必须有一个move 链接。
我可以在这里做出的另一个设计决定是为每个有效动作提供一个单独的链接。这样做可能有充分的理由,但我想证明这不是必需的。您可能还希望包含一些其他资源来处理诸如帮助客户确定轮到谁以及诸如此类的事情。
对于更复杂的游戏,例如文明、RTS、FPS 或 MMOG 等等,这可能不是那么实用的 IMO。
【讨论】: