在lua中支持replay
在游戏中replay是个必不可少的功能,无论是玩家的需求还是对开发和调试的帮助。但是lua的官方实现并不能很好地支持replay的实现。
replay的关键在于所有行为都是确定且可重现的。对于随机函数math.random很简单,lua提供了设置随机种子的函数math.randomseed,相同的随机种子总是产生相同的随机序列,我们只需要保证replay使用了相同的随机种子即可。
但是lua还有一个随机行为,考虑以下代码
|
|
你会发现每次的输出都是不一样的。lua遍历表的次序取决于key的hash值,而lua的很多数据类型的hash都是随机的,这包括了string、table、userdata等。而且它并不取决于math.randomseed指定的随机种子,所以不能很简单地解决它。
重写pairs函数?
既然pair有随机性,那么我们来写一个没有随机性pairs吧。
|
|
这是一个根据key排序次序遍历的pairs,但是这个pairs也有几个问题
现在的输出是a、b、c、d…,但我更希望是看起来是随机的次序,这样行为看起来会和原版的pairs更像。这就需要在排序比较函数里加上一个hash函数。然而由于lua无法高效地访问字符串,所以如果hash函数不用c实现的话,效率低得可怕。
|
|
hash函数需要保证没有冲突。由于第一次遍历是随机的,所以两个hash值相同的项遍历的次序也是随机的。
重写lua的hash函数
|
|
这里面有随机性的是LUA_TSHRSTR、LUA_TLNGSTR、LUA_TLIGHTUSERDATA、LUA_TLCF和default。
遍历string key
先说字符串,字符串在计算hash时引入了一个随机种子,这个随机种子和math.randomseed不一样,在lstate.c里的lua_newstate初始化。
|
|
|
|
为了保证这个随机种子的随机性,lua引入了多种随机变量来保证随机性。所以最好的办法就是重写makeseed,或者把随机种子从lua_newstate的参数传入。因为lua_newstate只需要在lua_State初始化时调用一次,所以随机种子从lua_newstate的参数传入是个不错的方案。
这样我们就可以通过传入的随机种子来改变pairs遍历字符串key时的次序了。
遍历userdata key
首先要修改userdata的数据结构,加一个hash值,这个在lobject.h中
|
|
然后在初始化时,给一个全局唯一的ID。
|
|