3

在获得原始开发人员许可的情况下,我正在用 Java 重新实现一个旧的 BBS MUD 游戏。目前我正在使用带有 EJB 会话外观的 Java EE 6 用于游戏逻辑和 JPA 用于持久性。我选择会话 bean 的一个重要原因是 JTA。

我对网络应用程序更有经验,如果你得到一个 OptimisticLockException,你只需抓住它并告诉用户他们的数据已经过时,他们需要重新申请/重新提交。在多用户游戏中一直以“再试一次”来回应会带来可怕的体验。考虑到我希望几个人在战斗中瞄准一个怪物,我认为 OptimisticLockException 的可能性会很高。

我的视图代码(呈现 telnet CLI 的部分)是 EJB 客户端。我应该捕获 PersistenceExceptions 和 TransactionRolledbackLocalExceptions 并重试吗?你如何决定何时停止?

我应该切换到悲观锁定吗?

在每个用户命令过度杀伤后都坚持吗?我是否应该将整个世界加载到 RAM 中并每隔几分钟转储一次状态?

我是否让我的会话外观成为一个 EJB 3.1 单例,它可以作为一个阻塞点,从而消除执行任何类型的 JPA 锁定的需要?EJB 3.1 单例用作多读取器/单写入器设计(您将方法注释为读取器和写入器)。

基本上,对于应用程序中的高并发数据更改,在向用户呈现重新提交/重试提示是不可接受的情况下,最好的设计和 java 持久性 API 是什么?

4

2 回答 2

4

我不禁觉得你把应该是一个简单的问题过度复杂化了。

商业和成功的 MMO 经常采用这样的方法:

Every few minutes or after a significant action:
    copy the player data
    pass the player data to a background thread

In the background thread:
    write each piece of player data to the database

没有“高度并发”的数据更改,因为每个玩家都将自己的数据保存到数据库的不同行中。(在某些情况下,这实际上是一行 - 一些商业游戏只是将玩家数据存储为针对用户 ID 的 Blob。)有时数据有点陈旧,但这并不重要。仅当服务器崩溃时才会出现问题,否则您最终会将当前状态输入数据库。这不是我们所说的银行间信用转账。

至于 MUD 和 BBS 游戏,他们的算法会更简单:

Every few minutes or after a significant action:
    For each property in the player object:
        write a property to the player's file

同样,这里没有争用,因为每个播放器都有自己的文件。

在每个用户命令之后坚持下去确实是矫枉过正,除非你的游戏是高度货币化的,并且如果由于服务器崩溃而失去了 +3 Ax of Awesome,人们就有可能起诉你。

而除了球员,你还需要坚持什么?持久化其他所有内容通常会对游戏玩法产生负面影响,因此通常您不想这样做。

至于对同一个怪物的“并发”访问,根据您的示例,没关系。如果你有一个进程,怪物应该在 RAM 中。你的单一进程仲裁谁可以击中怪物以及以什么顺序。这不是您的持久层的工作。处理游戏中的游戏逻辑并持久化结果。

于 2010-04-01T10:55:26.263 回答
1

如果我没记错的话,许多经典的 MUD 确实确实将整个世界加载到 RAM 中并定期转储状态。当然,我的记忆是从 16MB 的进程大小被认为是可怕的那一天开始的。

于 2010-03-22T17:42:12.063 回答