我在原始开发者的许可下,用Java重新实现旧的BBS MUD游戏。目前我正在使用Java EE 6将EJB Session外观用于游戏逻辑,将JPA用于持久性。我选择会话bean的一个重要原因是JTA。
我对网络应用程序更有经验,如果你得到一个OptimisticLockException,你就会抓住它并告诉用户他们的数据是陈旧的,他们需要重新申请/重新提交。在多用户游戏中一直响应“再试一次”将会带来可怕的体验。鉴于我期望有几个人在战斗中瞄准一个怪物,我认为乐观锁定异常的可能性很高。
我的视图代码,即呈现telnet CLI的部分,是EJB客户端。我应该捕获PersistenceExceptions和TransactionRolledbackLocalExceptions并只是重试吗?你如何决定何时停止?
我应该切换到悲观锁定吗?
在每个用户命令过度杀戮之后是否持续存在?我应该每隔几分钟将整个世界加载到RAM中并倾倒状态吗?
我是否将会话外观设为EJB 3.1单例,这可以作为一个阻塞点,因此无需进行任何类型的JPA锁定? EJB 3.1单例作为多个读者/单个编写器设计(您将方法注释为读者和编写者)。
基本上,对于应用程序中高度并发数据更改的最佳设计和Java持久性API是什么,不向用户提供重新提交/重试提示?
答案 0 :(得分: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 Axes of Awesome,则有人起诉你的风险。
除了球员之外,你还需要坚持世界其他什么?持久存在其他一切通常会对游戏玩法产生负面影响,所以通常你不想这样做。
对于同一个怪物的“并发”访问,根据你的例子,它并不重要。如果你有一个进程,怪物应该在RAM中。你的单个进程会判断谁能够以什么顺序击中怪物。这不是您的持久层的工作。处理游戏中的游戏逻辑并保持结果。
答案 1 :(得分:1)
如果我没记错的话,许多经典的MUD确实将整个世界加载到RAM中并定期转储状态。当然,我的记忆是从16MB工艺尺寸被认为可怕的那一天开始的。