使用 webSockets 进行在线编程游戏的网络结构
Network structure for online programming game with webSockets
问题
我正在制作一个游戏,您可以在其中提供一段代码来表示智能代理的代理程序(想想 Robocode 等),但基于浏览器。作为一个 AI/ML 大部分人,我对 Web 开发的知识 was/is 非常缺乏,所以我在实现整个架构时遇到了一些麻烦。基本上,在文本(代码)上传后,自然是客户端的一部分,后端将负责 运行 核心逻辑并返回 JSON 客户端将解析和使用的数据主要是绘图部分。现在真的不需要多人游戏支持。
如果我在 Robocode 的执行循环之后建模,我需要为每场战斗创建一个单独的进程,然后将不同的代理(用户创建的或非用户创建的)分配给不同的线程,并为每个循环给它们一些执行时间,从而生成新信息提供给代理以及绘制整个场景的数据。我试图想出一种构建多个客户端的好方法,servers/web servers/processes [...],并得出了多种可能的解决方案。
最喜欢的解决方案(截至目前)
客户端与 Node.js 服务器通信,该服务器的工作方式有点像一个接口(想想 websocketd),用于同一台(服务器)机器上的独特进程 运行,通过 ID 和进程跟踪客户端和进程相应地转发数据(通过 webSockets)。因此,一个示例场景是:
- 客户端C1向服务器S请求新的战斗并发送代码(不一定是一步,我知道);
- S 处理代码(例如编译),执行新的战斗并开始与其进程 P1 的连接(名为 pipes/FIFO?);
- P1 生成 JSON,发送给 S;
- S看到P1是"connected"给C1,发送数据给C1(只要战斗活跃就会重复3、4步);
- 客户端C2请求新的战斗;
- 重复前面的步骤; C2 分配给新进程 P2;
- 客户端 C3 请求 "watching" 在 P1 下进行战斗(使用独特的 URL 或令牌);
- S 找到 P1 的 ID,与接收到的 ID 进行比较并将 P1 绑定到 C3;
这样,服务器将从分支进程接收到的数据转发给连接到每个特定战斗的所有客户端。
问题
关于此方法:
- 够简单吗?有没有更简单甚至更优雅的方法呢?可扩展性会成为问题吗?
- 它是否足够安全(整个编译和 运行 代码——可能是 C++——在服务器上)?
- 速度够快吗(目前最让我担心的是这个)?让一个服务器处理整个流量似乎有点违反直觉,但据我所知,如果我将所有这些进程分配给一个单独的 Web 服务器,我将需要为每个进程使用不同的端口,这似乎更糟。
由于这是一个基于理论和观点的问题...我可以随意将球投向不同的方向。我可能会在考虑或阅读评论时编辑答案。
每场战斗一个过程?
听起来很贵。此外,还存在消息在进程之间来回传递的问题......不妨在机器之间发送消息并完全分离关注点。
我们可以让它们 运行 单独存在,而不是分叉战斗,允许它们崩溃并重新启动并做任何他们想做的事情,而不会对任何其他战斗或我们的服务器造成任何伤害。
Javascript?为什么只有一种语言?
我会考虑利用面向对象的方法或语言 - 至少在战斗中,如果不是在服务器上也是如此。
如果我们分离代码,我们可以使用不同的语言。否则我可能会选择 Ruby,因为这对我来说很容易,但也许我错了,深入研究 Javascript 的原型就可以了。
哦...外国代码 - 消毒是有序的。
外国代码安全吗?它是否应该使用一种本地化的 sped 语言来保证使用现有语言解释器的安全性,这可能会让代码混淆它不应该做的事情......
我可能会编写自己的 "pseudo language" 专为战斗而设计...或者(如果这对我和我来说是一个非常本地的项目)使用 Ruby 其中一个是消毒宝石.
战斗和网络服务的扩展速度可能不同。
在我看来,处理消息 - client->server->battle
和 battle->server->client
- 是相当容易的工作。然而,处理战斗似乎更耗费资源。
我说服自己,关注点分离几乎是不可避免的。
拥有服务器后端和不同的战斗后端将使您能够更快地扩展战斗处理程序,而不会在有任何需要之前浪费资源来扩展网络服务器。
网络断开。
假设我们允许玩家下线,而他们的代理 "fight" 在现场......当我们需要发送我们的用户 "Mitchel" 时会发生什么,他刚刚重新连接到服务器 X,a他在服务器 Y 上肆虐的一场战斗的消息?
分离关注点意味着从一开始我们就有一个可以扩展的通信系统,允许我们的用户连接到不同的端点并仍然收到他们的消息。
综上所述,我认为这是我的工作流程:
Http 工作流程:
客户端 -> Web 服务器:请求具有标识符和可选战斗数据的代理(战斗数据用于创建代理,省略战斗数据将用于将请求限制为现有代理(如果存在) ).
此步骤可能会根据客户端身份验证/凭据(即会话数据/cookie 标识符或登录过程)自动执行。
如果请求中存在战斗数据(请求):
Web 服务器 -> 战斗实例:如果不存在则创建代理。
如果请求中缺少战斗数据:
Web 服务器-> 战斗数据库,检查代理是否存在。
Web 服务器 -> 客户端:关于代理的响应(存在/创建 vs none)
如果存在或创建了Agent,则在为连接设置凭据后发起Websocket连接(会话数据,唯一cookie标识符或一次性唯一令牌附加到Websocket请求查询).
如果代理不存在,将客户端转发到网络表单以填写代理代码、战斗类型等数据'。
Websocket "workflows"(非线性):
代理有数据:代理消息->(对战通信管理器)->Web服务器->客户端
可以将 Redis 或类似的数据库放在那里,允许消息在用户离线时堆叠,并允许多个战斗实例和多个 Web 服务器实例。
客户端更新到Agent:客户端消息->(对战通信管理器)->Web服务器->Agent
问题
我正在制作一个游戏,您可以在其中提供一段代码来表示智能代理的代理程序(想想 Robocode 等),但基于浏览器。作为一个 AI/ML 大部分人,我对 Web 开发的知识 was/is 非常缺乏,所以我在实现整个架构时遇到了一些麻烦。基本上,在文本(代码)上传后,自然是客户端的一部分,后端将负责 运行 核心逻辑并返回 JSON 客户端将解析和使用的数据主要是绘图部分。现在真的不需要多人游戏支持。
如果我在 Robocode 的执行循环之后建模,我需要为每场战斗创建一个单独的进程,然后将不同的代理(用户创建的或非用户创建的)分配给不同的线程,并为每个循环给它们一些执行时间,从而生成新信息提供给代理以及绘制整个场景的数据。我试图想出一种构建多个客户端的好方法,servers/web servers/processes [...],并得出了多种可能的解决方案。
最喜欢的解决方案(截至目前)
客户端与 Node.js 服务器通信,该服务器的工作方式有点像一个接口(想想 websocketd),用于同一台(服务器)机器上的独特进程 运行,通过 ID 和进程跟踪客户端和进程相应地转发数据(通过 webSockets)。因此,一个示例场景是:
- 客户端C1向服务器S请求新的战斗并发送代码(不一定是一步,我知道);
- S 处理代码(例如编译),执行新的战斗并开始与其进程 P1 的连接(名为 pipes/FIFO?);
- P1 生成 JSON,发送给 S;
- S看到P1是"connected"给C1,发送数据给C1(只要战斗活跃就会重复3、4步);
- 客户端C2请求新的战斗;
- 重复前面的步骤; C2 分配给新进程 P2;
- 客户端 C3 请求 "watching" 在 P1 下进行战斗(使用独特的 URL 或令牌);
- S 找到 P1 的 ID,与接收到的 ID 进行比较并将 P1 绑定到 C3;
这样,服务器将从分支进程接收到的数据转发给连接到每个特定战斗的所有客户端。
问题
关于此方法:
- 够简单吗?有没有更简单甚至更优雅的方法呢?可扩展性会成为问题吗?
- 它是否足够安全(整个编译和 运行 代码——可能是 C++——在服务器上)?
- 速度够快吗(目前最让我担心的是这个)?让一个服务器处理整个流量似乎有点违反直觉,但据我所知,如果我将所有这些进程分配给一个单独的 Web 服务器,我将需要为每个进程使用不同的端口,这似乎更糟。
由于这是一个基于理论和观点的问题...我可以随意将球投向不同的方向。我可能会在考虑或阅读评论时编辑答案。
每场战斗一个过程?
听起来很贵。此外,还存在消息在进程之间来回传递的问题......不妨在机器之间发送消息并完全分离关注点。
我们可以让它们 运行 单独存在,而不是分叉战斗,允许它们崩溃并重新启动并做任何他们想做的事情,而不会对任何其他战斗或我们的服务器造成任何伤害。
Javascript?为什么只有一种语言?
我会考虑利用面向对象的方法或语言 - 至少在战斗中,如果不是在服务器上也是如此。
如果我们分离代码,我们可以使用不同的语言。否则我可能会选择 Ruby,因为这对我来说很容易,但也许我错了,深入研究 Javascript 的原型就可以了。
哦...外国代码 - 消毒是有序的。
外国代码安全吗?它是否应该使用一种本地化的 sped 语言来保证使用现有语言解释器的安全性,这可能会让代码混淆它不应该做的事情......
我可能会编写自己的 "pseudo language" 专为战斗而设计...或者(如果这对我和我来说是一个非常本地的项目)使用 Ruby 其中一个是消毒宝石.
战斗和网络服务的扩展速度可能不同。
在我看来,处理消息 -
client->server->battle
和battle->server->client
- 是相当容易的工作。然而,处理战斗似乎更耗费资源。我说服自己,关注点分离几乎是不可避免的。
拥有服务器后端和不同的战斗后端将使您能够更快地扩展战斗处理程序,而不会在有任何需要之前浪费资源来扩展网络服务器。
网络断开。
假设我们允许玩家下线,而他们的代理 "fight" 在现场......当我们需要发送我们的用户 "Mitchel" 时会发生什么,他刚刚重新连接到服务器 X,a他在服务器 Y 上肆虐的一场战斗的消息?
分离关注点意味着从一开始我们就有一个可以扩展的通信系统,允许我们的用户连接到不同的端点并仍然收到他们的消息。
综上所述,我认为这是我的工作流程:
Http 工作流程:
客户端 -> Web 服务器:请求具有标识符和可选战斗数据的代理(战斗数据用于创建代理,省略战斗数据将用于将请求限制为现有代理(如果存在) ).
此步骤可能会根据客户端身份验证/凭据(即会话数据/cookie 标识符或登录过程)自动执行。
如果请求中存在战斗数据(请求):
Web 服务器 -> 战斗实例:如果不存在则创建代理。
如果请求中缺少战斗数据:
Web 服务器-> 战斗数据库,检查代理是否存在。
Web 服务器 -> 客户端:关于代理的响应(存在/创建 vs none)
如果存在或创建了Agent,则在为连接设置凭据后发起Websocket连接(会话数据,唯一cookie标识符或一次性唯一令牌附加到Websocket请求查询).
如果代理不存在,将客户端转发到网络表单以填写代理代码、战斗类型等数据'。
Websocket "workflows"(非线性):
代理有数据:代理消息->(对战通信管理器)->Web服务器->客户端
可以将 Redis 或类似的数据库放在那里,允许消息在用户离线时堆叠,并允许多个战斗实例和多个 Web 服务器实例。
客户端更新到Agent:客户端消息->(对战通信管理器)->Web服务器->Agent