gpt4 book ai didi

python - 需要关于MMORPG数据模型设计、数据库访问和stackless python的建议

转载 作者:太空狗 更新时间:2023-10-29 20:59:32 24 4
gpt4 key购买 nike

就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the help center为指导。




8年前关闭。




我从事开发回合制休闲 MMORPG 游戏服务器。

处理网络的低级引擎(不是我们编写的),
多线程、定时器、服务器间通信、主游戏循环等
由 C++ 编写。高级游戏逻辑由 Python 编写。

我的问题是关于我们游戏中的数据模型设计。

一开始我们只是尝试将一个播放器的所有数据加载到 RAM 和一个共享数据中
客户端登录时缓存服务器并安排计时器定期将数据刷新到
数据缓存服务器和数据缓存服务器将数据持久化到数据库中。

但是我们发现这种方法有一些问题

1) 一些数据需要即时保存或查看,例如任务进度,
升级,元素和金钱 yield 等。

2)根据游戏逻辑,有时我们需要查询一些离线玩家的
数据。

3) 部分全局游戏世界数据需要在不同游戏之间共享
可能在不同主机或不同进程上运行的实例
同一个主机。这是我们需要在游戏之间放置数据缓存服务器的主要原因
逻辑服务器和数据库。

4) 玩家需要在游戏实例之间自由切换。

以下是我们过去遇到的困难:

1)所有的数据访问操作应该是异步的,避免网络I/O
阻塞主游戏逻辑线程。我们必须将消息发送到数据库或
缓存服务器然后在回调函数中处理数据回复消息和
继续进行游戏逻辑。写一些温和的东西很快就会变得痛苦
复杂的游戏逻辑,需要多次与 db 和游戏逻辑对话
分散在许多回调函数中,难以理解和
维持。

2)临时数据缓存服务器使事情变得更加复杂,我们难以维护
数据一致性并有效地更新/加载/刷新数据。

3)游戏内数据查询效率低下繁琐,需要查询游戏逻辑
许多信息,如库存、元素信息、头像状态等。一些
还需要事务机制,例如,如果一个步骤失败了整个
操作应该是回滚。我们尝试在 RAM 中设计一个好的数据模型系统,
构建了大量复杂的索引以缓解大量信息查询,添加
交易支持等。我很快意识到我们正在构建的是一个内存
数据库系统,我们正在重新发明轮子...

最后我转向无堆栈python,我们删除了缓存服务器。所有数据
保存在数据库中。游戏逻辑服务器直接查询数据库。无堆叠
python的微tasklet和channel,我们可以在synchronized中编写游戏逻辑
道路。编写和理解要容易得多,生产力也大大提高
改进。

其实底层的DB访问也是异步的:一个客户端tasklet
向另一个专用 DB I/O 工作线程发出请求,并且 tasklet 是
一个 channel 被屏蔽了,但是整个游戏主逻辑没有被屏蔽,其他
客户端的 tasklet 将被调度并自由运行。当 DB 数据回复
被阻塞的 tasklet 将被唤醒并在“break”时继续运行
点'(继续?)。

有了上面的设计,我有一些问题:

1) 数据库访问将比以前的缓存解决方案更频繁,是否
DB可以支持高频率的查询/更新操作吗?是否有一些成熟的缓存
近期需要redis、memcached等解决方案吗?

2) 我的设计有什么严重的缺陷吗?你们能给我一些更好的吗
建议,尤其是关于游戏内数据管理模式的建议。

任何建议将不胜感激,谢谢。

最佳答案

我曾使用过一个以类似方式运行的 MMO 引擎。然而,它是用 Java 编写的,而不是 Python。

关于你的第一组要点:

1) 异步数据库访问 我们实际上走了另一条路,避免了“主游戏逻辑线程”。所有游戏逻辑任务都作为新线程产生。与 I/O 相比,线程创建和销毁的开销完全消失在本底噪声中。这也保留了将每个“任务”作为一种相当简单的方法的语义,而不是一个令人抓狂的回调链,否则会以其他方式结束(尽管仍然存在这种情况。)这也意味着所有游戏代码都必须是并发,我们越来越依赖带有时间戳的不可变数据对象。

2) 临时缓存 我们使用了很多 WeakReference 对象(我相信 Python 有类似的概念吗?),并且还利用了数据对象之间的拆分,例如“播放器”和“加载器”(实际上是数据库访问方法)例如“播放器SQLLoader;”实例保存了一个指向它们的加载器的指针,加载器被一个全局“工厂”类调用,该类将处理缓存查找与网络或 SQL 加载。数据类中的每个“Setter”方法都会调用方法 changed ,这是 myLoader.changed (this); 的继承样板

为了处理来自其他事件服务器的加载对象,我们使用了使用相同数据类的“代理”对象(再次说,“播放器”),但我们关联的加载器类是一个网络代理,它会(同步,但超过千兆本地网络)在另一台服务器上更新该对象的“主”副本;反过来,“主”副本将调用 changed本身。

我们的 SQL UPDATE逻辑有一个计时器。如果后端数据库收到了 UPDATE在最后 ($n) 秒内(我们通常将其保持在 5 秒左右),它会将对象添加到“脏列表”中。后台计时器任务会定期唤醒并尝试将仍在“脏列表”上的任何对象异步刷新到数据库后端。

由于全局工厂维护对所有核心对象的弱引用,并且会在任何实时服务器上查找给定游戏对象的单个实例化副本,因此我们永远不会尝试实例化由单个 DB 记录支持的一个游戏对象的第二个副本,因此游戏的 RAM 状态可能一次与其 SQL 图像不同长达 5 或 10 秒这一事实无关紧要。

我们的整个 SQL 系统在 RAM(是的,很多 RAM)中运行,作为另一台勇敢地尝试写入磁盘的服务器的镜像。 (由于“老化”,那台糟糕的机器平均每 3-4 个月烧毁一次 RAID 驱动器。RAID 很好。)

值得注意的是,当从缓存中删除对象时,必须将对象刷新到数据库中,例如由于超出缓存 RAM 限额。

3) 内存数据库 ......我没有遇到过这种确切的情况。我们确实有“类事务”逻辑,但这一切都发生在 Java 的 getter/setter 级别上。

而且,关于你的后一点:

1) 是的,PostgreSQL 和 MySQL 尤其能很好地处理这个问题,尤其是当您使用数据库的 RAMdisk 镜像来尝试最大程度地减少实际硬盘磨损时。然而,根据我的经验,MMO 确实倾向于对数据库进行比严格必要的更多的锤击。我们的“5 秒规则”* 是专门为避免必须“正确”解决问题而制定的。我们的每个二传手都会调用 changed .在我们的使用模式中,我们发现一个对象通常要么更改了 1 个字段,然后一段时间没有事件,要么发生“ Storm ”更新,其中许多字段连续更改。构建适当的事务左右(例如,通知对象它即将接受许多写入,并且应该等待片刻再将其保存到数据库中)将涉及更多的计划、逻辑和系统的主要重写;所以,相反,我们绕过了这种情况。

2)好吧,上面是我的设计:-)

事实上,我目前正在开发的 MMO 引擎更多地依赖于内存中的 SQL 数据库,并且(我希望)会做得更好一些。但是,该系统是使用实体-组件-系统模型构建的,而不是我上面描述的 OOP 模型。

如果您已经基于 OOP 模型,那么转向 ECS 是一个非常好的范式转变,如果您可以让 OOP 为您的目的工作,那么最好坚持使用您的团队已经知道的内容。

*- “5 秒规则”是美国的一种口语“民间信仰”,即食物掉到地上后,如果在 5 秒内捡起来仍然可以吃。

关于python - 需要关于MMORPG数据模型设计、数据库访问和stackless python的建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8660622/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com