为什么我找不到饥荒的bin饥荒mod文件夹在哪,好像根本没有饥荒一样,求给一个位置

《饥荒》MOD人物制作图文教程 怎么制作人物MOD_攻略秘籍_新浪游戏_新浪网
《饥荒》MOD人物制作图文教程 怎么制作人物MOD
15:05:27& &来源:
  《饥荒》怎么制作人物MOD?很多小伙伴喜欢自己创造角色,但不清楚具体的操作步骤,今天小编带来“Crscendo”分享的《饥荒》MOD人物制作图文教程,希望对各位有帮助。
  需要用到的工具don‘t starve mod tools(正版用户上传MOD的工具。包含各种制作MOD的工具。动画制作生成。音频转换,脚本工具)
  TEXTool (可以浏览到游戏里的TEX格式的软件。也可以把PNG格式图片转换为TEX。反之也可以把TEX转换成PNG)
  TEX格式文件:游戏里所需的图片格式。
  PNG格式文件:可保存含有ALPHA通道的图层。一种图片格式文件。
  首先一个人物MOD里包含的文件。如下图
  adim:这个里面是游戏里所使用的所有道具的动画文件(以前大多数国人做MOD都喜欢直接把这个文件里的解压包里的TEX转换成PNG然后再进行制图。这样不好,不是最好的办法。会造成图形丢失)
  bigportraits:选取人物时左边的大图。
  exported:动画生成的文件夹。(很重要很重要很重要,做人物MOD的核心)
  images:人物和道具在选取。存档。死亡列表,小地图图标。
  scripts:人物和道具主要的脚本。
  modicon:开启MOD时的图标(只需要TEX格式图片)
  modinfo:MOD信息(制作人,MOD名字。MOD版本。适用于什么版本等)
  modmian:MOD主要的脚本。
  好了。到了这里MOD基本构架就介绍完毕。
  下面我先讲解软件的使用。
  kools:可把anim里的文件反编辑成scml文件。方便进行制图。
  TEX可直接打开TEX文件预览和把图片保存为PNG格式。
  反之也可以把含有ALPHA通道图层保存为TEX格式。这个软件作用对做MOD不太大。只有一个地方要用到。
  kools这个软件非常的重要。这是做人物MOD的重要部分。
  首先你要找到一个人物的贴图模型。在游戏文件夹里去找官方人物也行。下载官方给予的MOD人物模板也可以。
  下面我用wendy为例,在游戏文件里找到wendy的解压包
  唔,我找不到wendy的bin文件了。我用官方的MOD人物模型吧。如下图。你需要找到build。anim 以及TEX文件。有些人物是2个TEX文件。有些是1个。不用纠结。。。
  其他MOD教程:
  Steam汉化教程及联机MOD下载推荐:点击进入
  人物MOD推荐及图文攻略:点击进入
  更多相关内容请关注:饥荒专区1 2 3 4下一页友情提示:支持键盘左右键“← →”翻页标签:
  然后把这些文件复制到一个盘里的文件夹里。任何盘(注:需英文文件夹)
  这个时候就需要启用cmd命令。进入你的cmd
  切换到你安装ktools里的盘。例如X:我自己是D盘。那么我就切换到D盘。
  然后cd ktools(进入某盘里的某个文件夹。 PS:CD空格按tab可自动补全)
  然后下面输入,krane.exe E:\tie E:\output (启动krane程序。目标文件夹“tietu”,输出文件夹“output”)
  当然也可以不切换盘。
  这样你就可以把你刚才从解压包里弄出来的文件生成sxml文件了。这个时候你去output文件夹里去看。就可以找到已经成功的文件了。如下图
  这个文件夹呢。实际上是把一个人物的基本贴图全部分解了出来。然后好方便你进行自己的创作。比如我进入headbase文件夹。里面是分解出来的人物的头部。
  最下面需要spriter打开的文件呢。是方便你预览人物的。也可以进行人物动作的拼接。但是我不打算这样做,因为我们一切的动作按照官方的就好。如下图。
  更多相关内容请关注:饥荒专区上一页1 2 3 4下一页友情提示:支持键盘左右键“← →”翻页标签:
  ktools讲解到这里。这个软件的呢。就是帮助你把人物的贴图和动画文件转换成scml文件进行创作。
  下面我开始讲解MOD的正文。首先进入你的MOD。把ANIM里的解压包全部删除掉。(为什么要删除掉。因为我们在进入游戏时,don‘t starve mod tools会帮助我们把exported里我们自己创作的人物贴图转换成游戏里需要的解压包形式的文件。
  然后进入bigportraits以及images文件夹以及子文件夹里。把TEX格式的文件全部删,保留同名的PNG格式文件。如果没有请用TEX软件打开TEX文件保存一张出来。
  下面进入最复杂的一点。首先我们进入exported文件夹里。这个时间你应该把你转换出来的scml文件和所有人物的分解图文件夹做成一个文件夹放入,文件夹名字是你mod人物名字。然后进行改名。用写字板或者文本工具打开scml。
  打开之后乱码看的我头疼。但是没关系。我基友告诉我了怎么修改。“嘿嘿嘿”
  首先你找到属于这个文件的名字。例如我用千年狐的扇子作为例子。人物的不改名也可以直接用。所以我就不说明了。
  千年狐的扇子是swap_gumifan,然后你想你的道具叫ABC。那么。选择文本里的替换选项。然后全部替换。
  现在这把扇子叫做ABC了。是属于你MOD里的道具了。是不是超级简单。因为我把千年狐的扇子改成了我现在做的MOD里的道具。取名为ddpitchfork,所以现在我们用文本打开这个文件应该是。
  下面应该是替换图形了。我现在利用我画的恶魔叉换掉扇子的模型。
  不要问我背景为什么是兔子。上次MOD改了之后忘了备份扇子的模型了。
  把你不要的图层删除掉。
  那么我现在打开这个道具的scml,就能看到替换成功了。
  现在这个道具就属于你自己的模型以及自己的命名了。人物也同样。我现在把模板里的三个狐狸头和表情换掉应该是这个样子。
  更多相关内容请关注:饥荒专区上一页1 2 3 4下一页友情提示:支持键盘左右键“← →”翻页标签:
  这里需要注意的是。
  1:如果贴图位置不对。请在制图软件里调整位置。不要去碰scml里的位置坐标。免得出错。
  2:不要部位的贴图不要直接删除文件。保留一张含有ALPHA通道的图层。(空白图层)
  下面我把人物全部图形替换掉。。。就成了下面这个样子。
  现在我们进入images以及bigportraits文件以及子文件里。打开PNG文件把图形全部替换。切记不可变换图层大小。
  替换全部完之后。我们把MOD放入游戏MODS文件夹里。然后打开游戏进行游戏。
  don‘t starve mod tools就会把ANIM里没有的解压包生成出来。以及把没有转换成TEX格式的PNG格式文件自动转换了。
  然后我们可以在游戏看到我们自己制作好了的全部图形。如下:
  人物选取大图我还没有画完。。。
  我看了一下大家的问题。最大的问题是因为通过ktools反编辑后的图形会出现错乱的问题。
  ktools的作者更新到的最新版本是4.1.2。但是还是只有32位的。
  更多相关内容请关注:饥荒专区上一页1 2 3 4友情提示:支持键盘左右键“← →”翻页标签:
新浪声明:新浪网登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
  《饥荒》螃蟹怎么抓,很多小伙伴表示海难中的螃蟹非常不好抓,今天小编带来“andy25339”分享的《饥荒》海难抓螃蟹方法汇总及...
来自:攻略秘籍
  《饥荒》大家知道曼德拉草吗?今天小编就为大家带来了饥荒曼德拉草寻找方法图文解析,非常不错的内容哦,小伙伴们你们知道哪...
来自:攻略秘籍
蜘蛛网是饥荒中非常重要的材料,产出主要来自于杀死蜘蛛,但是蜘蛛的掉落有3种,所以蛛网的掉率不高,在后期我们需要大量的蜘蛛网...
来自:攻略
  《饥荒》服装怎么交易?可能很多小伙伴不清楚该如何操作,今天小编带来“q”分享的《饥荒》联机版服装交易赠送方法详...
来自:攻略秘籍
中国网络游戏排行榜(China Game Weight Rank)是由新浪游戏推出的目前国内最全面、最专业、最公正的最新网络游戏评测排行榜,涵盖2010至今内所有新游戏,力图为中国游戏玩家打造最值得信赖的新网游推荐平台。
评天下游戏、测产品深浅—新浪中国网络游戏排行榜CGWR! 
全民手游攻略
“全民手游攻略”是新浪游戏为全球手游玩家量身打造的一款手游攻略大全及专业游戏问答社区APP。“全民手游攻略”涵盖Apple Store游戏畅销榜前150名手游,网罗最新手游通关秘籍,帮助玩家畅玩手游;打造最全手游攻略资料站,帮助你用最省钱的方式吊打土豪。最火爆专业的游戏问答,让众多游戏大神带你开启不
同的手游人生,寻找和你志同道合的小伙伴一起并肩作战。
新浪游戏大事记
16年01月14日
16年01月14日
15年12月15日
15年11月19日
15年11月16日
15年01月08日
14年12月27日
14年12月17日
14年03月27日
14年01月09日
13年12月27日
13年12月07日
13年10月11日
用微信扫描二维码
分享至微信朋友圈快意游戏、简单生活!
相关资源:
各位好,我叫Tricia。叫我老崔也可以。这次这个教程贴。主要针对于正版用户。因为有一款工具需要steam账户下载。破解版用户请在网上自行寻找。人物MOD难点在于画贴图的部分。下面我会讲解到要使用的软件以及使用方法,和画图部分。人物脚本部分我只会讲解一部分基础的。需要用到的工具如下:don't starve mod tools(正版用户上传MOD的工具。包含各种制作MOD的工具。动画制作生成。音频转换,脚本工具)TEXTool (可以浏览到游戏里的TEX格式的软件。也可以把PNG格式图片转换为TEX。反之也可以把TEX转换成PNG)TEX格式文件:游戏里所需的图片格式。PNG格式文件:可保存含有ALPHA通道的图层。一种图片格式文件。首先一个人物MOD里包含的文件。如下图adim:这个里面是游戏里所使用的所有道具的动画文件(以前大多数国人做MOD都喜欢直接把这个文件里的解压包里的TEX转换成PNG然后再进行制图。这样不好,不是最好的办法。会造成图形丢失)bigportraits:选取人物时左边的大图。exported:动画生成的文件夹。(很重要很重要很重要,做人物MOD的核心)images:人物和道具在选取。存档。死亡列表,小地图图标。scripts:人物和道具主要的脚本。modicon:开启MOD时的图标(只需要TEX格式图片)modinfo:MOD信息(制作人,MOD名字。MOD版本。适用于什么版本等)modmian:MOD主要的脚本。kools:可把anim里的文件反编辑成scml文件。方便进行制图。TEX可直接打开TEX文件预览和把图片保存为PNG格式。反之也可以把含有ALPHA通道图层保存为TEX格式。这个软件作用对做MOD不太大。只有一个地方要用到。kools这个软件非常的重要。这是做人物MOD的重要部分。首先你要找到一个人物的贴图模型。在游戏文件夹里去找官方人物也行。下载官方给予的MOD人物模板也可以。下面我用wendy为例,在游戏文件里找到wendy的解压包
唔,我找不到wendy的bin文件了。我用官方的MOD人物模型吧。如下图。你需要找到build。 anim 以及TEX文件。有些人物是2个TEX文件。有些是1个。不用纠结。然后把这些文件复制到一个盘里的文件夹里。任何盘(注:需英文文件夹)这个时候就需要启用cmd命令。进入你的cmd切换到你安装ktools里的盘。例如X:我自己是D盘。那么我就切换到D盘。然后cd ktools(进入某盘里的某个文件夹. PS:CD空格按tab可自动补全)然后下面输入,krane.exe E:\tie E:\output (启动krane程序。目标文件夹“tietu”,输出文件夹“output”)当然也可以不切换盘。这样你就可以把你刚才从解压包里弄出来的文件生成sxml文件了。这个时候你去output文件夹里去看。就可以找到已经成功的文件了。如下图这个文件夹呢。实际上是把一个人物的基本贴图全部分解了出来。然后好方便你进行自己的创作。比如我进入headbase文件夹。里面是分解出来的人物的头部。最下面需要spriter打开的文件呢。是方便你预览人物的。也可以进行人物动作的拼接。但是我不打算这样做,因为我们一切的动作按照官方的就好。如下图。ktools讲解到这里。这个软件的呢。就是帮助你把人物的贴图和动画文件转换成scml文件进行创作。
下面我开始讲解MOD的正文。首先进入你的MOD。把ANIM里的解压包全部删除掉。.(为什么要删除掉。因为我们在进入游戏时,don't starve mod tools会帮助我们把exported里我们自己创作的人物贴图转换成游戏里需要的解压包形式的文件。然后进入bigportraits以及images文件夹以及子文件夹里。把TEX格式的文件全部删,保留同名的PNG格式文件。如果没有请用TEX软件打开TEX文件保存一张出来。下面进入最复杂的一点。首先我们进入exported文件夹里。这个时间你应该把你转换出来的scml文件和所有人物的分解图文件夹做成一个文件夹放入,文件夹名字是你mod人物名字。然后进行改名。用写字板或者文本工具打开scml。打开之后乱码看的我头疼。但是没关系。我基友告诉我了怎么修改。&嘿嘿嘿&首先你找到属于这个文件的名字。例如我用千年狐的扇子作为例子。人物的不改名也可以直接用。所以我就不说明了。千年狐的扇子是swap_gumifan,然后你想你的道具叫ABC。那么。选择文本里的替换选项。然后全部替换。现在这把扇子叫做ABC了。是属于你MOD里的道具了。是不是超级简单。因为我把千年狐的扇子改成了我现在做的MOD里的道具。取名为ddpitchfork,所以现在我们用文本打开这个文件应该是。下面应该是替换图形了。我现在利用我画的恶魔叉换掉扇子的模型。不要问我背景为什么是兔子。上次MOD改了之后忘了备份扇子的模型了。把你不要的图层删除掉。那么我现在打开这个道具的scml,就能看到替换成功了。现在这个道具就属于你自己的模型以及自己的命名了。人物也同样。我现在把模板里的三个狐狸头和表情换掉应该是这个样子。这里需要注意的是。1:如果贴图位置不对。请在制图软件里调整位置。不要去碰scml里的位置坐标。免得出错。2:不要部位的贴图不要直接删除文件。保留一张含有ALPHA通道的图层。(空白图层)下面我把人物全部图形替换掉。..就成了下面这个样子。现在我们进入images以及bigportraits文件以及子文件里。打开PNG文件把图形全部替换。切记不可变换图层大小。替换全部完之后。我们把MOD放入游戏MODS文件夹里。然后打开游戏进行游戏。don't starve mod tools就会把ANIM里没有的解压包生成出来。以及把没有转换成TEX格式的PNG格式文件自动转换了。然后我们可以在游戏看到我们自己制作好了的全部图形。如下:更多相关攻略:
《饥荒》相关文章
(阅读:407)
(阅读:303)
(阅读:481)
(阅读:253)
(阅读:594)
(阅读:711)
(阅读:959)
(阅读:342)
(阅读:240)
(阅读:683)
LOL地图梗是什么意思呢?LOL地图中会有多少种梗呢?这个地图梗会有什么意思呢?那么接下来就和小编一起来看看LOL地图梗详解,喜欢的小伙伴们快来看看吧,希望对大家有所帮助。
玩心引领文化破壁 第五届CIGC聚焦互娱新势力3月7日,第五届中国国际互动娱乐大会(China Interactive-entertainment Global Conference,以下简称CIGC)在广州香格里拉大酒店正式启幕。工信部发布《2018中国泛娱乐产业白皮书》,解读泛娱乐产业趋势。大会主办方三七互娱携手旗下诸多泛娱乐生态公司重磅亮相,来自游戏、影视、动漫、音乐、文学、VR/AR等领域代表性公司也纷纷登场,探讨泛娱乐大发展的时 ...
扫描二维码&p&首先,楼主的情况,参考 &a class=&member_mention& href=&//www.zhihu.com/people/4f70c5e4c628f94e9aa93dcb& data-hash=&4f70c5e4c628f94e9aa93dcb& data-hovercard=&p$b$4f70c5e4c628f94e9aa93dcb&&@木头龙&/a& 的回答即可。而针对标题的回答如下。&/p&&p&&br&&/p&&p&最近帮朋友做了下数据恢复,Windows系统,机械硬盘。使用过国内的一些数据恢复软件,比如DiskGenius,发现免费版有恢复的大小限制。而EsayRecovery,版本又比较杂乱,很难分辨。另外的其他一些免费软件,要么是扫描速度太慢,或者是功能不完善,很难有满意的。&/p&&p&于是我想还是不要依赖百度了,所以就用google去搜数据恢复的软件,发现还是挺靠谱的。首先给出一个免费数据恢复软件的列表。&/p&&p&&b&&a href=&//link.zhihu.com/?target=https%3A//www.lifewire.com/free-data-recovery-software-tools-2622893& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&19 Free Data Recovery Software Tools&/a&&/b&&/p&&p&&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-772adb9bd72c467d265d59ae_b.png& data-rawwidth=&1004& data-rawheight=&124& class=&origin_image zh-lightbox-thumb& width=&1004& data-original=&https://pic3.zhimg.com/v2-772adb9bd72c467d265d59ae_r.png&&&/figure&&p&&br&&/p&&h2&其中最靠谱的要算 &a href=&//link.zhihu.com/?target=https%3A//www.lifewire.com/recuva-review-2622892& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Recuva&/a& 了,我最后就是用这个软件帮我朋友做了数据恢复,十几个G的数据都成功恢复出来了,暂时没有遇到大小限制,扫描和恢复的速度也很快。重点是完全免费。&/h2&&p&&br&&/p&&p&为了帮助更多人脱离国内那些良莠不齐的数据恢复软件,并且掌握数据恢复的安全流程(有些人因为流程不对,会导致数据永远无法恢复),我后来翻译了一篇数据恢复的文章&a href=&//link.zhihu.com/?target=https%3A//www.lifewire.com/how-to-recover-deleted-files-2622870& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&How To Recover Deleted Files&/a&,并用Recuva为例子,给大家科普一下正确的姿势。希望大家能从中获益。&/p&&p&&br&&/p&&h2&&a href=&//link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s%3F__biz%3DMzA5NDI1OTIxMA%3D%3D%26mid%3D%26idx%3D1%26sn%3De9c525e239fc67b32b144e7dec57f525%26chksm%3D87ea98e1b09d11f2dbdf4b311c2ee81a206aaa518f25c%26mpshare%3D1%26scene%3D23%26srcid%3D0715Jtoig4MLynDZ8oGi6tWI%23rd& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&文件从回收站删除就真的找不回了吗?&/a&&/h2&&p&(更多Recuva的使用方法,各位可自己探索,我以后也会继续更新,包括格式化恢复,U盘和固态硬盘的数据恢复等)&/p&
首先,楼主的情况,参考
的回答即可。而针对标题的回答如下。 最近帮朋友做了下数据恢复,Windows系统,机械硬盘。使用过国内的一些数据恢复软件,比如DiskGenius,发现免费版有恢复的大小限制。而EsayRecovery,版本又比较杂乱,很难分辨。另外的…
额,搜到了这个问题,我就随便答一下吧,下面都是我的猜测,正确与否自行判断。&br&&br&&b&一、用的什么引擎?&/b&&br&不知道,推测可能是cocos-2d或者他们自己写的一个框架。&br&&br&&ul&&li&Don't Starve 用&a href=&//link.zhihu.com/?target=http%3A//www.fmod.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&FMOD&/a&来处理声音,在data\sound\目录下的文件可以用fmod_eventplayer.exe这个程序来编辑。&/li&&li&Don't Starve 的图片好像是用类似PVR格式的纹理加载的。数据目录下面所有后缀是*.tex的就是纹理文件。每一个纹理还有一个同名的xml文件来描述资源。这些纹理可以用Don't Starve 的TEXTools程序查看,用&a href=&//link.zhihu.com/?target=https%3A//github.com/nsimplex/ktools& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&nsimplex/ktools · GitHub&/a&和png格式的图片相互转换。&/li&&li&Don't Starve 的动画文件用&a href=&//link.zhihu.com/?target=http%3A//www.brashmonkey.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Spriter 2D sprite animation character creation software.&/a&制作。可以用Don't Starve Mod Tools 工具将Spriter的动画文件弄成Don't Starve 能载入的动画文件。Don't Starve 的动画文件本质上包括三部分,一个anim.bin的二进制文件用来描述动画,多个atlas-xxx.tex的纹理文件用来放置动画需要的文件,bulid.bin承担描述纹理文件的作用。之后将这些文件压缩成zip文件,主要提高加载速度。用上面提到的ktools可以将饥荒的动画文件转换成Spriter能编辑的格式。不过anim.bin,bulid.bin,atlas-xxx.tex,这些文件不一定每个动画文件都有,比如那些角色共用一套anim.bin动画文件,每个角色只是纹理换了一下。所以用ktools转换不成功时,注意分析这些动画的依赖关系,把对应的打包成zip,就行了。&/li&&/ul&因为上面所说的都在cocos-2d里面都有用到,所以我才推测它是cocos-2d的。 &br&--------------------------------------------------------------&br&&b&二、怎么组织的游戏世界?&/b&&br&我几个月前翻译过国外的一篇介绍游戏世界的生成方式的文章,在这里&a href=&//link.zhihu.com/?target=http%3A//blog.csdn.net/czfblog/article/details/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&【饥荒】关于随机地图生成的方式&/a&。BTW,这个博客里还有几篇关于饥荒的,有兴趣可以看一下。&br&&br&&ul&&li&Don't Starve使用&a href=&//link.zhihu.com/?target=http%3A//www.mapeditor.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Tiled Map Editor&/a& 来编辑地图块,Tiled是开源的哟。Don't Starve的地图是随机生成的,但是也有一些内在联系,比如pigking只会出现在森林边上,而且每一个pigking周围都有四个sanityrock(好像是叫这个?),这些东东的位置就是用Tiled编辑出来的,此外还有一大堆,在Static Layouts目录下。&br&&/li&&li&一个Room有多个Static Layouts&/li&&li&一个Task有多个Room,还有几个key,lock。&/li&&li&生成地图就把对应的key和lock拼接起来,可以保证地图的连贯性。&/li&&/ul&-------------------------------------------------------------------------&br&&br&&b&三、&/b&&b&反正就是&/b&&b&详细一点的吧。&/b&&br&要详细的话我就随便写写,想到那写那。&br&&br&&b&1.面向对象&/b&&br&Don't Starve 主要用Lua脚本实现游戏逻辑,大体上,饥荒的Lua部分采用面向对象的编程范式,在class.lua文件里用lua metatable实现了一套面向对象的系统,可实现类的继承。&br&在饥荒里基本上每一个类都由一个独立的脚本文件定义,这些个脚本文件大体结构如下:&br&&div class=&highlight&&&pre&&code class=&language-lua&&&span class=&nb&&require&/span& &span class=&s2&&&&/span&&span class=&s&&class&&/span& &span class=&c1&&--载入class文件&/span&
&span class=&kd&&local&/span& &span class=&n&&baseclass&/span&&span class=&o&&=&/span&&span class=&nb&&require&/span& &span class=&s2&&&&/span&&span class=&s&&baseclass&&/span&
&span class=&c1&&--基类,有时没有&/span&
&span class=&c1&&--这里可以载入其他的东东&/span&
&span class=&c1&&--创建继承基类的子类&/span&
&span class=&c1&&--为了不污染全局变量域,使用local关键字&/span&
&span class=&c1&&--没有基类时,这样写:&/span&
&span class=&c1&&--~local childclass=Class(function (self,...)&/span&
&span class=&kd&&local&/span& &span class=&n&&childclass&/span&&span class=&o&&=&/span&&span class=&n&&Class&/span&&span class=&p&&(&/span&&span class=&n&&baseclass&/span&&span class=&p&&,&/span&&span class=&k&&function&/span& &span class=&p&&(&/span&&span class=&n&&self&/span&&span class=&p&&,&/span&&span class=&o&&...&/span&&span class=&p&&)&/span&
&span class=&n&&self&/span&&span class=&p&&.&/span&&span class=&n&&value&/span&&span class=&o&&=&/span&&span class=&mi&&0&/span&
&span class=&c1&&--属性&/span&
&span class=&c1&&--初始化函数内容&/span&
&span class=&k&&end&/span&&span class=&p&&)&/span&
&span class=&c1&&--方法&/span&
&span class=&k&&function&/span& &span class=&nf&&childclass&/span&&span class=&p&&:&/span&&span class=&n&&fn&/span&&span class=&p&&(&/span&&span class=&o&&...&/span&&span class=&p&&)&/span&
&span class=&k&&end&/span&
&span class=&c1&&--因为使用了local,所以要返回,其他文件才能接收到&/span&
&span class=&k&&return&/span& &span class=&n&&childclass&/span&
&/code&&/pre&&/div&如果你了解了饥荒时如何实现面向对象的,看饥荒的源码就可以很快速的理解了。&br&&br&&b&2.代码与资源分布&/b&&br&下面列出了饥荒中各个文件或文件夹的作用:&br&(以*结尾的,该文件夹里就是一堆上面所说的类)&br&&div class=&highlight&&&pre&&code class=&language-bash&&├─bin
二进制程序文件
├─data 数据目录
├─bigportraits
人物肖像纹理
好像是用来放置于天气相关纹理的
├─images
主要纹理放置目录
├─levels
放置地皮纹理
├─minimap
├─models
未知,猜测与洞穴有关
├─scriptlibs
一些用lua实现的库,用来网络通信与json字符串处理
├─scripts
主要的lua脚本目录
├─behaviours
├─brains
├─cameras
相机,用来控制玩家所能看到的视野*
├─components
组件,一组通用的,用来描述物品的代码*
├─languages
语言文件,国际化用的
地图相关代码*
├─levels
世界模式,有生存,冒险,洞穴等等
├─static_layouts
Tasks,上面那些具体去看博客
卖萌用的,直接在命令行运行,有只会动的大象^.^
├─prefabs
定义世界里所有的物品
├─scenarios
场景,也就是贴吧所说的彩蛋
├─screens
├─stategraphs
状态机或状态图,用来和AI一起控制动物(和植物?)的运动
└─widgets
界面小部件(按钮,文本框etc.)*
├─shaders
└─mods 插件放置目录
&/code&&/pre&&/div&此外,data文件里还有DLC0001目录,这个目录来放置DLC版本的文件,结构和data目录一样。&br&&br&&br&&br&&br&&br&下面就一大堆网址:&br&&a href=&//link.zhihu.com/?target=https%3A//github.com/kleientertainment/ds_mod_tools& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&kleientertainment/ds_mod_tools · GitHub&/a& 游戏官方发布的mod工具,拿来自动转换纹理和动画,在win和ubuntu下编译成功。&br&&a href=&//link.zhihu.com/?target=https%3A//github.com/nsimplex/ktools& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&nsimplex/ktools · GitHub&/a& Ktools 工具,可以把tex纹理和png图片相互转换。还可以转换动画文件。&br&&a href=&//link.zhihu.com/?target=https%3A//github.com/nsimplex/wicker& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&nsimplex/wicker · GitHub&/a& 一个饥荒Mod框架。&br&&a href=&//link.zhihu.com/?target=https%3A//github.com/debugman18/UpAndAway& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&debugman18/UpAndAway · GitHub&/a& 一个大型饥荒Mod《天堂》,利用上面的wicker写的。&br&&a href=&//link.zhihu.com/?target=https%3A//github.com/jkolokotronis/dsmods& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&jkolokotronis/dsmods · GitHub&/a&
还是一个大型mod《黑夜英雄》,不过真的有点大,400多M,翻下墙再去克隆。
额,搜到了这个问题,我就随便答一下吧,下面都是我的猜测,正确与否自行判断。 一、用的什么引擎? 不知道,推测可能是cocos-2d或者他们自己写的一个框架。 Don't Starve 用来处理声音,在data\sound\目录下的文件可以用fmod_eventplayer.exe这个程序…
&figure&&img src=&https://pic3.zhimg.com/1655feb05cd5d744ab663bd_b.jpg& data-rawwidth=&1920& data-rawheight=&1080& class=&origin_image zh-lightbox-thumb& width=&1920& data-original=&https://pic3.zhimg.com/1655feb05cd5d744ab663bd_r.jpg&&&/figure&新写一篇文章,以纪念在制作和调试饥荒游戏LuaJIT桥接PATCH中探过的雷区。&p&本文大体已经更新完结,如有新的内容会及时补充。&br&&/p&&p&关于饥荒游戏的介绍,直接看这个答案好了:&a href=&https://www.zhihu.com/question/& class=&internal&&&span class=&invisible&&https://www.&/span&&span class=&visible&&zhihu.com/question/2128&/span&&span class=&invisible&&2202&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&关于本插件的详细介绍,下载及源码请点击:&a href=&http://link.zhihu.com/?target=https%3A//paintdream.github.io/DontStarveLuaJIT/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&paintdream.github.io/Do&/span&&span class=&invisible&&ntStarveLuaJIT/&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&本文所用到的软件:OllyDBG 2.01、Microsoft Visual C++ 2008&/p&&br&&p&-----------------------------------------我是分割线------------------------------------------------&/p&&p&&b&0x00 神船搁浅&/b&&/p&&p&我要给饥荒写PATCH,并不是一个刚开始就计划好的事情。饥荒发布于Steam平台并且添加了创意工坊,里面有很多好的、差的、能用的、不能用的、原创的以及抄的Mods,大部分都维护得很好,开游戏时连存档带MOD设置都会被云同步更新,几乎可以是完美了。&/p&&p&然而唯一的美中不足似乎是:这游戏居然很吃配置。N年前买的神船竟然会在玩久了之后就会时不时顿卡,以至于到后来居然越来越卡了。虽然i7-3610QM+GT650M在现在看来也比较渣,但是至少跑跑dota2中配都还很流畅,也不至于在饥荒上惨成这样。&/p&&p&打开任务管理器瞄一眼,发现情况还是很有意思的。在卡顿的时候,CPU只有两个核心满载运行,其余都在打酱油——通过暂停-恢复的办法观察CPU占用率,可以得出结论,CPU0是负责渲染的线程,CPU6是负责脚本逻辑的线程。&/p&&p&&figure&&img src=&https://pic2.zhimg.com/e61b23a34abda57c9dfb88c_b.png& data-rawheight=&519& data-rawwidth=&345& class=&content_image& width=&345&&&/figure&卡顿是来自于两方面的,一方面如果绘制的对象较多,机器就会明显开始变卡。这时CPU0的使用率会明显下降,说明有大量绘制指令阻塞了渲染管线,导致绘制线程长期处于等待渲染完成的阻塞状态。同时用户的输入反馈也会被明显阻滞,推测读取用户输入的相关代码也是在这个线程中执行的(饥荒使用了DirectInput来读取键盘操作)。饥荒本身这种哥特式的画风,使用一些预制的纹理搞搞Billboard贴图就可以做出伪2D的画面效果了,渲染本身压力应该不算大。但是考虑到ES版本低的限制用不了Instanced Draw,很可能是每个对象都对应一个draw call,并且每个对象绘制时都重复提交了大量数据,导致带宽用尽。不过这方面我现在还没有做仔细的研究,仅仅是猜测而已。&/p&&p&另一方面如果脚本的逻辑过于复杂,也会明显变卡。这时CPU6将会被占满,而由于逻辑帧与渲染帧同步的关系,CPU0的占用率会变低。这方面的问题相对来说比较严重,因为为了好玩大家都会加载很多MODs,Shipwrecked DLC新增加的游戏特性也非常吃CPU,导致大家在玩这个DLC时感受到的卡顿远远比原版或者Reign of Giants DLC时要明显得多。因此在很多论坛上大家解决这个卡顿的办法只有——别开那么多MOD,换个好点的电脑。&/p&&p&本文尝试解决的是脚本逻辑引起的卡顿。&/p&&p&&b&0x01 积重难返&/b&&br&&/p&&p&好消息是饥荒使用了我比较熟悉的lua 5.1.4作为脚本引擎(但是实际上我更希望看到的是lua 5.3),并且随游戏附送了所有的lua脚本(大约18万行,不算DLC),所有的逻辑都是在脚本中实现的。&/p&&p&研究了几个小时后发现,作者似乎仅仅是把它当作一个工具来用,没有专门在lua特性上下功夫。为了写起来更方便和熟悉,当需要异步操作时,大部分的需求都是通过C层面支持的时钟回调(往往配合全局任务队列)来完成的。这个设计也给我造成了麻烦,因为它会使原本完整的逻辑从中断开,调试时打印调用堆栈就只能看到当前事件触发后的逻辑。&/p&&p&那么,为什么不用coroutine呢?我搜了下源码,有三个地方对coroutine进行了封装,其中两个是有关网络的第三方库,主要负责发邮件之类的工作 ,和游戏逻辑没什么关系。唯一用到的就是调度器添加任务的时候,为每一个新任务创建一个coroutine,然后调度的时候枚举所有任务一个个执行。&/p&&p&然而,这个coroutine似乎仅仅是由来保存一个独立执行栈的——调度器对它的操作仅仅是从队列中取出然后执行完删掉。全部代码中只有9个很少执行到的地方用到了Yield()。个人觉得这个coroutine的用法值得商榷——毕竟coroutine本身还是有一点点开销的,而且绝大多数异步需求还是用回调来完成的。&/p&&p&从lua代码架构上看,这套脚本自己实现了一个简单的类似C++的面向对象框架,模块的封装比较任性,耦合度也有点高,经常会出现在底层框架代码中去加一些代码来控制上层实现逻辑的代码。而它发布的两个DLC并不是在原有代码框架上开发的扩展包,而是直接把代码复制出来进行修改的,估计最初写的时候也没想要有个DLC……&/p&&p&可以看出,游戏本身应该是直接从一个简单的原型一步步迭代上来的,虽然在开发的过程中有过抽象和封装,但是积重难返,大体的结构已经固定,没有什么简单的优化空间了。脚本负责了太重的任务,并行方面做的工作也不多,试图直接优化现有代码对于我来说已经成了死局。&/p&&p&因此把lua引擎替换为LuaJIT已经成了为数不多的可以让程序运行起来不那么卡的选择了。虽然从道理上讲,这也只能算是治标不治本,但是好歹能有的玩啊。&/p&&p&&b&0x02 横生枝节&/b&&/p&&p&但是事情远远没有想的那么简单。我最初以为直接把饥荒可能用到的lua51.dll换成luajit.dll就万事大吉了,然而翻遍了安装目录也没看到lua51.dll库的影子。那么次一点的结果就是LUA被封装在了别的DLL里,由dontstarve_steam.exe启动时加载。然而最终发现lua引擎是直接作为静态库或者源码编译进主程序的,这是最糟糕的结果。&/p&&p&这个麻烦在于,我们知道exe通常不需要导出函数(仔细看了下dontstarve_steam.exe确实没导出),那么如何定位lua api在exe中的位置就成了首要解决的问题。&/p&&p&EXE中lua api的布局和DLL中是完全不一样的:编译器可以剪掉那些没被引用到的函数与常量,也可以重排函数的顺序。想要通过公开的或者凑巧的办法定位到这些函数是不可能的。那么就剩下了三条路:直接改EXE,硬编码与特征码搜索。&/p&&p&直接改EXE或者硬编码相应函数的RVA看起来简单粗暴,但是万一饥荒EXE更新了就得新发布个版本,而且为了得到地址还得手工用特征码先搜索并较正一遍。想了想还是用特征码吧,而且还要尽量做成非侵入式的,这样就可以有效地兼容饥荒自己的更新。&/p&&p&&b&0x03 探针计划&/b&&/p&特征码搜索需要先找到每个函数对应的特征码。为此我的思路是,先编译一份原汁原味的lua 5.1.4的DLL,把LUA API设置为导出,这样就能得到每个API的地址,进而用函数开头的二进制指令作为特征码在EXE中进行搜索。接下来,只需要把搜索到的源地址和名字一一对应起来,然后加载luajit.dll就可以再通过kernel32.dll!GetProcAddress拿到目标地址,通过inline hook将源地址处的执行流导向目标地址。由于这些hooks不需要执行原函数,计算跳板所保留的原函数头部字节数也就没有必要了。&p&不同的编译器生成目标代码时的算法都是很不一样的,甚至就算是同一系列的编译器,随着版本更新也会有不同。那么应该用什么来编译原版lua DLL呢?当然是与饥荒主程序保持一致。OllyDBG告诉我们答案:MSVC9,即Microsoft Visual C++ 2008。&/p&&figure&&img src=&https://pic1.zhimg.com/04da3c510db72fc7afa61b_b.png& data-rawheight=&409& data-rawwidth=&601& class=&origin_image zh-lightbox-thumb& width=&601& data-original=&https://pic1.zhimg.com/04da3c510db72fc7afa61b_r.jpg&&&/figure&&br&&p&然而在开始编码之前,先要解决的问题是:怎么让我的代码得到执行?&/p&&p&很明显如果想要做非侵入式的Patch,要么额外写一个启动器,要么通过某种技巧让我们的代码在游戏启动时得到执行。官方的MOD机制允许自定义mod在游戏初始化的时候执行lua代码,然而这时候lua引擎已经建立,再替换就晚了,肯定不行。&/p&&p&于是我的目光移动到exe依赖的那些DLL上,看看有什么偷梁换柱的策略。所谓的偷梁换柱就是指,针对目标EXE所依赖的DLL,写一个傀儡DLL,名字和导出函数都和目标DLL一样,然后放在EXE目录中就可以在EXE启动时被加载。加载后再手动用kernel32.dllLoadLibraryA/W加载目标DLL,并把导出的函数都转接到目标DLL中对应的函数里去。早些年一些病毒为了禁用杀毒软件,就在杀毒软件安装目录下放一个假的ws2_32.dll,这样杀毒软件启动时就会崩溃或者被劫持。注意一些系统核心的DLL是不受这个影响的,如kernel32.dll, ntdll.dll等)&/p&&p&饥荒主程序目录下那些DLL大多都是C++写的,导出函数的名字都是经过name decoration的,处理起来会比较麻烦。加之导出函数的数量也很多,有点不划算。&/p&&p&找了半天,最佳的选择是DINPUT8.DLL,这个DLL是DirectInput所需要的。选择它的原因是dontstarve_steam.exe只从其中导入了一个函数DirectInput8Create,用来创建DirectInput的设备对象,由于这个对象是用COM封装的,所以DLL不再需要导出其成员函数,导入表项因此就简洁了很多。还有一个好处,由于它是处于系统目录中的DLL,替换它不需要替换原有的DLL,只需要在EXE目录中放置即可被加载,是完美的非侵入式PATCH的选择。&br&&/p&&figure&&img src=&https://pic2.zhimg.com/3f6fe4799_b.png& data-rawheight=&253& data-rawwidth=&422& class=&origin_image zh-lightbox-thumb& width=&422& data-original=&https://pic2.zhimg.com/3f6fe4799_r.jpg&&&/figure&&p&如前文所述,你接管了DINPUT8.DLL,那么也就得实现DirectInput8Create这个函数,否则游戏启动时会找不到入口。这个很容易:&br&&/p&&div class=&highlight&&&pre&&code class=&language-c&&&span&&/span&&span class=&k&&typedef&/span& &span class=&nf&&HRESULT&/span&&span class=&p&&(&/span&&span class=&n&&WINAPI&/span& &span class=&o&&*&/span&&span class=&n&&PFNDirectInput8Create&/span&&span class=&p&&)(&/span&&span class=&n&&HINSTANCE&/span& &span class=&n&&hinst&/span&&span class=&p&&,&/span& &span class=&n&&DWORD&/span& &span class=&n&&dwVersion&/span&&span class=&p&&,&/span& &span class=&n&&REFIID&/span& &span class=&n&&riidltf&/span&&span class=&p&&,&/span& &span class=&n&&LPVOID&/span& &span class=&o&&*&/span&&span class=&n&&ppvOut&/span&&span class=&p&&,&/span& &span class=&n&&LPUNKNOWN&/span& &span class=&n&&punkOuter&/span&&span class=&p&&);&/span&
&span class=&k&&static&/span& &span class=&n&&PFNDirectInput8Create&/span& &span class=&n&&pfnDirectInput8Create&/span& &span class=&o&&=&/span& &span class=&nb&&NULL&/span&&span class=&p&&;&/span&
&span class=&n&&HRESULT&/span& &span class=&n&&WINAPI&/span& &span class=&nf&&DirectInput8Create&/span&&span class=&p&&(&/span&&span class=&n&&HINSTANCE&/span& &span class=&n&&hinst&/span&&span class=&p&&,&/span& &span class=&n&&DWORD&/span& &span class=&n&&dwVersion&/span&&span class=&p&&,&/span& &span class=&n&&REFIID&/span& &span class=&n&&riidltf&/span&&span class=&p&&,&/span& &span class=&n&&LPVOID&/span& &span class=&o&&*&/span&&span class=&n&&ppvOut&/span&&span class=&p&&,&/span& &span class=&n&&LPUNKNOWN&/span& &span class=&n&&punkOuter&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&return&/span& &span class=&n&&pfnDirectInput8Create&/span&&span class=&p&&(&/span&&span class=&n&&hinst&/span&&span class=&p&&,&/span& &span class=&n&&dwVersion&/span&&span class=&p&&,&/span& &span class=&n&&riidltf&/span&&span class=&p&&,&/span& &span class=&n&&ppvOut&/span&&span class=&p&&,&/span& &span class=&n&&punkOuter&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&nl&&DllMain&/span&&span class=&p&&:&/span&
&span class=&n&&TCHAR&/span& &span class=&n&&systemPath&/span&&span class=&p&&[&/span&&span class=&n&&MAX_PATH&/span&&span class=&p&&];&/span&
&span class=&o&&::&/span&&span class=&n&&GetSystemDirectory&/span&&span class=&p&&(&/span&&span class=&n&&systemPath&/span&&span class=&p&&,&/span& &span class=&n&&MAX_PATH&/span&&span class=&p&&);&/span&
&span class=&n&&HMODULE&/span& &span class=&n&&hInput&/span& &span class=&o&&=&/span& &span class=&o&&::&/span&&span class=&n&&LoadLibrary&/span&&span class=&p&&(&/span&&span class=&n&&CString&/span&&span class=&p&&(&/span&&span class=&n&&systemPath&/span&&span class=&p&&)&/span& &span class=&o&&+&/span& &span class=&n&&_T&/span&&span class=&p&&(&/span&&span class=&s&&&&/span&&span class=&se&&\\&/span&&span class=&s&&DINPUT8.DLL&&/span&&span class=&p&&));&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&hInput&/span& &span class=&o&&!=&/span& &span class=&nb&&NULL&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&n&&pfnDirectInput8Create&/span& &span class=&o&&=&/span& &span class=&p&&(&/span&&span class=&n&&PFNDirectInput8Create&/span&&span class=&p&&)&/span&&span class=&o&&::&/span&&span class=&n&&GetProcAddress&/span&&span class=&p&&(&/span&&span class=&n&&hInput&/span&&span class=&p&&,&/span& &span class=&s&&&DirectInput8Create&&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&在上面的代码里,DllMain里需要加载真正的DINPUT8.DLL,然后获取到DirectInput8Create的地址,然后才能在中继代码中CALL。需要注意的是,我们最好不要包含任何有关DirectX SDK的头文件,这些头文件往往会指定API为导入,而我们需要将同名的中继函数导出。&/p&&p&如果遇到了类型没定义的错误也不用急,按照其长度写一个等价的就行了。后文我会介绍一个更简单的制作中继函数跳板的办法。&/p&&p&那么,接下来是怎么搜的问题。我们先来编译原版lua51.dll,编译设置选择Release版本。&/p&&figure&&img src=&https://pic2.zhimg.com/a5b0dab4785b9_b.png& data-rawheight=&600& data-rawwidth=&800& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic2.zhimg.com/a5b0dab4785b9_r.jpg&&&/figure&&br&&p&如果在用kernel32.dll!LoadLibraryA/W加载了原版的lua51.dll之后,直接拿API函数开始的二进制码去做搜索,只能搜索到一小部分函数。这是由于DLL中的代码需要根据.reloc节的信息进行重定位,需要定位的地方二进制值会变,因此纯粹的memcmp行不通。&/p&&p&在这里我直接采用了一个较简单但是相对耗时的策略:在搜索时利用反汇编引擎XDE32一条条解析指令格式,发现需要重定位的指令,就读取地址处的值代替地址本身充当特征码。&/p&&div class=&highlight&&&pre&&code class=&language-c&&&span&&/span&&span class=&k&&while&/span& &span class=&p&&(&/span&&span class=&n&&target&/span& &span class=&o&&&&/span& &span class=&n&&entry&/span&&span class=&p&&.&/span&&span class=&n&&instr&/span& &span class=&o&&+&/span& &span class=&n&&INSTR_SIZE&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&n&&xde_disasm&/span&&span class=&p&&((&/span&&span class=&n&&BYTE&/span&&span class=&o&&*&/span&&span class=&p&&)&/span&&span class=&n&&c&/span&&span class=&p&&,&/span& &span class=&o&&&&/span&&span class=&n&&instr&/span&&span class=&p&&);&/span&
&span class=&kt&&int&/span& &span class=&n&&len&/span& &span class=&o&&=&/span& &span class=&n&&instr&/span&&span class=&p&&.&/span&&span class=&n&&len&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&len&/span& &span class=&o&&==&/span& &span class=&mi&&0&/span&&span class=&p&&)&/span&
&span class=&k&&break&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&instr&/span&&span class=&p&&.&/span&&span class=&n&&opcode&/span& &span class=&o&&==&/span& &span class=&mh&&0x68&/span& &span class=&o&&||&/span& &span class=&n&&instr&/span&&span class=&p&&.&/span&&span class=&n&&addrsize&/span& &span class=&o&&==&/span& &span class=&mi&&4&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&c1&&// read memory data&/span&
&span class=&n&&PVOID&/span& &span class=&n&&addr&/span& &span class=&o&&=&/span& &span class=&n&&instr&/span&&span class=&p&&.&/span&&span class=&n&&opcode&/span& &span class=&o&&==&/span& &span class=&mh&&0x68&/span& &span class=&o&&?&/span& &span class=&o&&*&/span&&span class=&p&&(&/span&&span class=&n&&PVOID&/span&&span class=&o&&*&/span&&span class=&p&&)(&/span&&span class=&n&&c&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&:&/span& &span class=&p&&(&/span&&span class=&n&&PVOID&/span&&span class=&p&&)&/span&&span class=&n&&instr&/span&&span class=&p&&.&/span&&span class=&n&&addr_l&/span&&span class=&p&&[&/span&&span class=&mi&&0&/span&&span class=&p&&];&/span&
&span class=&kt&&char&/span& &span class=&n&&buf&/span&&span class=&p&&[&/span&&span class=&mi&&16&/span&&span class=&p&&];&/span&
&span class=&n&&memset&/span&&span class=&p&&(&/span&&span class=&n&&buf&/span&&span class=&p&&,&/span& &span class=&mi&&0&/span&&span class=&p&&,&/span& &span class=&k&&sizeof&/span&&span class=&p&&(&/span&&span class=&n&&buf&/span&&span class=&p&&));&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&addr&/span& &span class=&o&&!=&/span& &span class=&nb&&NULL&/span& &span class=&o&&&&&/span& &span class=&o&&::&/span&&span class=&n&&ReadProcessMemory&/span&&span class=&p&&(&/span&&span class=&o&&::&/span&&span class=&n&&GetCurrentProcess&/span&&span class=&p&&(),&/span& &span class=&n&&addr&/span&&span class=&p&&,&/span& &span class=&n&&buf&/span&&span class=&p&&,&/span& &span class=&mi&&4&/span&&span class=&p&&,&/span& &span class=&nb&&NULL&/span&&span class=&p&&))&/span& &span class=&p&&{&/span&
&span class=&n&&entry&/span&&span class=&p&&.&/span&&span class=&n&&stringList&/span&&span class=&p&&.&/span&&span class=&n&&push_back&/span&&span class=&p&&(&/span&&span class=&n&&std&/span&&span class=&o&&::&/span&&span class=&n&&make_pair&/span&&span class=&p&&(&/span&&span class=&n&&addr&/span&&span class=&p&&,&/span& &span class=&n&&std&/span&&span class=&o&&::&/span&&span class=&n&&string&/span&&span class=&p&&(&/span&&span class=&n&&buf&/span&&span class=&p&&)));&/span&
&span class=&p&&}&/span&
&span class=&n&&BYTE&/span& &span class=&n&&temp&/span&&span class=&p&&[&/span&&span class=&mi&&16&/span&&span class=&p&&];&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&instr&/span&&span class=&p&&.&/span&&span class=&n&&opcode&/span& &span class=&o&&==&/span& &span class=&mh&&0x68&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&n&&memcpy&/span&&span class=&p&&(&/span&&span class=&n&&temp&/span&&span class=&p&&,&/span& &span class=&n&&c&/span&&span class=&p&&,&/span& &span class=&n&&instr&/span&&span class=&p&&.&/span&&span class=&n&&len&/span&&span class=&p&&);&/span&
&span class=&o&&*&/span&&span class=&p&&(&/span&&span class=&n&&PVOID&/span&&span class=&o&&*&/span&&span class=&p&&)(&/span&&span class=&n&&temp&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&=&/span& &span class=&o&&*&/span&&span class=&p&&(&/span&&span class=&n&&PVOID&/span&&span class=&o&&*&/span&&span class=&p&&)&/span&&span class=&n&&buf&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span& &span class=&k&&else&/span& &span class=&p&&{&/span&
&span class=&n&&instr&/span&&span class=&p&&.&/span&&span class=&n&&addr_l&/span&&span class=&p&&[&/span&&span class=&mi&&0&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&o&&*&/span&&span class=&p&&(&/span&&span class=&kt&&long&/span&&span class=&o&&*&/span&&span class=&p&&)&/span&&span class=&n&&buf&/span&&span class=&p&&;&/span&
&span class=&n&&xde_asm&/span&&span class=&p&&(&/span&&span class=&n&&temp&/span&&span class=&p&&,&/span& &span class=&o&&&&/span&&span class=&n&&instr&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&n&&memcpy&/span&&span class=&p&&(&/span&&span class=&n&&target&/span&&span class=&p&&,&/span& &span class=&n&&temp&/span&&span class=&p&&,&/span& &span class=&n&&len&/span& &span class=&o&&+&/span& &span class=&n&&target&/span& &span class=&o&&&&/span& &span class=&n&&entry&/span&&span class=&p&&.&/span&&span class=&n&&instr&/span& &span class=&o&&+&/span& &span class=&n&&INSTR_SIZE&/span& &span class=&o&&?&/span& &span class=&n&&entry&/span&&span class=&p&&.&/span&&span class=&n&&instr&/span& &span class=&o&&+&/span& &span class=&n&&INSTR_SIZE&/span& &span class=&o&&-&/span& &span class=&nl&&target&/span& &span class=&p&&:&/span& &span class=&n&&len&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span& &span class=&k&&else&/span& &span class=&p&&{&/span&
&span class=&n&&memcpy&/span&&span class=&p&&(&/span&&span class=&n&&target&/span&&span class=&p&&,&/span& &span class=&n&&c&/span&&span class=&p&&,&/span& &span class=&n&&len&/span& &span class=&o&&+&/span& &span class=&n&&target&/span& &span class=&o&&&&/span& &span class=&n&&entry&/span&&span class=&p&&.&/span&&span class=&n&&instr&/span& &span class=&o&&+&/span& &span class=&n&&INSTR_SIZE&/span& &span class=&o&&?&/span& &span class=&n&&entry&/span&&span class=&p&&.&/span&&span class=&n&&instr&/span& &span class=&o&&+&/span& &span class=&n&&INSTR_SIZE&/span& &span class=&o&&-&/span& &span class=&nl&&target&/span& &span class=&p&&:&/span& &span class=&n&&len&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&n&&c&/span& &span class=&o&&+=&/span& &span class=&n&&len&/span&&span class=&p&&;&/span&
&span class=&n&&target&/span& &span class=&o&&+=&/span& &span class=&n&&len&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&o&&!&/span&&span class=&n&&edge&/span&&span class=&p&&)&/span&
&span class=&n&&validLength&/span& &span class=&o&&+=&/span& &span class=&n&&len&/span& &span class=&o&&+&/span& &span class=&n&&target&/span& &span class=&o&&&&/span& &span class=&n&&entry&/span&&span class=&p&&.&/span&&span class=&n&&instr&/span& &span class=&o&&+&/span& &span class=&n&&INSTR_SIZE&/span& &span class=&o&&?&/span& &span class=&n&&entry&/span&&span class=&p&&.&/span&&span class=&n&&instr&/span& &span class=&o&&+&/span& &span class=&n&&INSTR_SIZE&/span& &span class=&o&&-&/span& &span class=&nl&&target&/span& &span class=&p&&:&/span& &span class=&n&&len&/span&&span class=&p&&;&/span&
&span class=&n&&entry&/span&&span class=&p&&.&/span&&span class=&n&&lengthHist&/span&&span class=&p&&[&/span&&span class=&n&&len&/span&&span class=&p&&]&/span&&span class=&o&&++&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&o&&*&/span&&span class=&n&&c&/span& &span class=&o&&==&/span& &span class=&mh&&0xCC&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&n&&edge&/span& &span class=&o&&=&/span& &span class=&nb&&true&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&具体到匹配算法,为了避免因微小长度差异而导致整体错位,我没有直接memcmp,而是用了最长公共子串的动态规划算法,时间和空间复杂度都为O(N * N)。对于不太长的特征序列,性能上的损失可以接受。&/p&&div class=&highlight&&&pre&&code class=&language-c&&&span&&/span&&span class=&k&&static&/span& &span class=&kt&&int&/span& &span class=&nf&&CommonLength&/span&&span class=&p&&(&/span&&span class=&k&&const&/span& &span class=&n&&BYTE&/span&&span class=&o&&*&/span& &span class=&n&&x&/span&&span class=&p&&,&/span& &span class=&kt&&int&/span& &span class=&n&&xlen&/span&&span class=&p&&,&/span& &span class=&k&&const&/span& &span class=&n&&BYTE&/span&&span class=&o&&*&/span& &span class=&n&&y&/span&&span class=&p&&,&/span& &span class=&kt&&int&/span& &span class=&n&&ylen&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&kt&&int&/span& &span class=&n&&opt&/span&&span class=&p&&[&/span&&span class=&n&&INSTR_SIZE&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&&span class=&p&&][&/span&&span class=&n&&INSTR_SIZE&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&&span class=&p&&];&/span&
&span class=&n&&memset&/span&&span class=&p&&(&/span&&span class=&n&&opt&/span&&span class=&p&&,&/span& &span class=&mi&&0&/span&&span class=&p&&,&/span& &span class=&k&&sizeof&/span&&span class=&p&&(&/span&&span class=&n&&opt&/span&&span class=&p&&));&/span&
&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&kt&&int&/span& &span class=&n&&i&/span& &span class=&o&&=&/span& &span class=&mi&&1&/span&&span class=&p&&;&/span& &span class=&n&&i&/span& &span class=&o&&&=&/span& &span class=&n&&xlen&/span&&span class=&p&&;&/span& &span class=&n&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&kt&&int&/span& &span class=&n&&j&/span& &span class=&o&&=&/span& &span class=&mi&&1&/span&&span class=&p&&;&/span& &span class=&n&&j&/span& &span class=&o&&&=&/span& &span class=&n&&ylen&/span&&span class=&p&&;&/span& &span class=&n&&j&/span&&span class=&o&&++&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&x&/span&&span class=&p&&[&/span&&span class=&n&&i&/span& &span class=&o&&-&/span& &span class=&mi&&1&/span&&span class=&p&&]&/span& &span class=&o&&==&/span& &span class=&n&&y&/span&&span class=&p&&[&/span&&span class=&n&&j&/span& &span class=&o&&-&/span& &span class=&mi&&1&/span&&span class=&p&&])&/span&
&span class=&n&&opt&/span&&span class=&p&&[&/span&&span class=&n&&i&/span&&span class=&p&&][&/span&&span class=&n&&j&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&n&&opt&/span&&span class=&p&&[&/span&&span class=&n&&i&/span& &span class=&o&&-&/span& &span class=&mi&&1&/span&&span class=&p&&][&/span&&span class=&n&&j&/span& &span class=&o&&-&/span& &span class=&mi&&1&/span&&span class=&p&&]&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&&span class=&p&&;&/span&
&span class=&k&&else&/span&
&span class=&n&&opt&/span&&span class=&p&&[&/span&&span class=&n&&i&/span&&span class=&p&&][&/span&&span class=&n&&j&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&n&&opt&/span&&span class=&p&&[&/span&&span class=&n&&i&/span& &span class=&o&&-&/span& &span class=&mi&&1&/span&&span class=&p&&][&/span&&span class=&n&&j&/span&&span class=&p&&]&/span& &span class=&o&&&&/span& &span class=&n&&opt&/span&&span class=&p&&[&/span&&span class=&n&&i&/span&&span class=&p&&][&/span&&span class=&n&&j&/span& &span class=&o&&-&/span& &span class=&mi&&1&/span&&span class=&p&&]&/span& &span class=&o&&?&/span& &span class=&n&&opt&/span&&span class=&p&&[&/span&&span class=&n&&i&/span& &span class=&o&&-&/span& &span class=&mi&&1&/span&&span class=&p&&][&/span&&span class=&n&&j&/span&&span class=&p&&]&/span& &span class=&o&&:&/span& &span class=&n&&opt&/span&&span class=&p&&[&/span&&span class=&n&&i&/span&&span class=&p&&][&/span&&span class=&n&&j&/span& &span class=&o&&-&/span& &span class=&mi&&1&/span&&span class=&p&&];&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&k&&return&/span& &span class=&n&&opt&/span&&span class=&p&&[&/span&&span class=&n&&xlen&/span&&span class=&p&&][&/span&&span class=&n&&ylen&/span&&span class=&p&&];&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&br&搜索的范围只需要设置为dontstarve_steam.exe的.text节所在范围即可,其实我在试了几个版本之后,发现.text + 0x170000之后才会有对应的lua api的函数。因此为了速度快些就直接以.text + 0x170000为地点,.text的末尾为终点了。(当然其实这么做有一定风险,不过一直懒得改。。万一哪天饥荒有大更新很可能会出错)&p&搜索到目标函数之后,标记下来,并与luajit连接即可。下面是inline hook的代码:&/p&&div class=&highlight&&&pre&&code class=&language-c&&&span&&/span&&span class=&k&&static&/span& &span class=&kt&&void&/span& &span class=&nf&&Hook&/span&&span class=&p&&(&/span&&span class=&n&&BYTE&/span&&span class=&o&&*&/span& &span class=&n&&from&/span&&span class=&p&&,&/span& &span class=&n&&BYTE&/span&&span class=&o&&*&/span& &span class=&n&&to&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&c1&&// prepare inline hook&/span&
&span class=&kt&&unsigned&/span& &span class=&kt&&char&/span& &span class=&n&&code&/span&&span class=&p&&[&/span&&span class=&mi&&5&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&p&&{&/span& &span class=&mh&&0xe9&/span&&span class=&p&&,&/span& &span class=&mh&&0x00&/span&&span class=&p&&,&/span& &span class=&mh&&0x00&/span&&span class=&p&&,&/span& &span class=&mh&&0x00&/span&&span class=&p&&,&/span& &span class=&mh&&0x00&/span& &span class=&p&&};&/span&
&span class=&o&&*&/span&&span class=&p&&(&/span&&span class=&n&&DWORD&/span&&span class=&o&&*&/span&&span class=&p&&)(&/span&&span class=&n&&code&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&&span class=&p&&)&/span& &span class=&o&&=&/span& &span class=&p&&(&/span&&span class=&n&&DWORD&/span&&span class=&p&&)&/span&&span class=&n&&to&/span& &span class=&o&&-&/span& &span class=&p&&(&/span&&span class=&n&&DWORD&/span&&span class=&p&&)&/span&&span class=&n&&from&/span& &span class=&o&&-&/span& &span class=&mi&&5&/span&&span class=&p&&;&/span&
&span class=&n&&DWORD&/span& &span class=&n&&oldProtect&/span&&span class=&p&&;&/span&
&span class=&o&&::&/span&&span class=&n&&VirtualProtect&/span&&span class=&p&&(&/span&&span class=&n&&from&/span&&span class=&p&&,&/span& &span class=&mi&&5&/span&&span class=&p&&,&/span& &span class=&n&&PAGE_READWRITE&/span&&span class=&p&&,&/span& &span class=&o&&&&/span&&span class=&n&&oldProtect&/span&&span class=&p&&);&/span&
&span class=&o&&::&/span&&span class=&n&&memcpy&/span&&span class=&p&&(&/span&&span class=&n&&from&/span&&span class=&p&&,&/span& &span class=&n&&code&/span&&span class=&p&&,&/span& &span class=&mi&&5&/span&&span class=&p&&);&/span&
&span class=&o&&::&/span&&span class=&n&&VirtualProtect&/span&&span class=&p&&(&/span&&span class=&n&&from&/span&&span class=&p&&,&/span& &span class=&mi&&5&/span&&span class=&p&&,&/span& &span class=&n&&oldProtect&/span&&span class=&p&&,&/span& &span class=&o&&&&/span&&span class=&n&&oldProtect&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&(个人比较喜欢0xE8/0xE9这种HOOK,不需要改寄存器,长度也很短。当然也可以用PUSH+RET, FF 15/25 即CALL/JMP DWORD PTR [ADDR]的,各有所需)&/p&&br&&p&&b&0x04 引火烧身&/b&&/p&&p&写完代码,编译好DLL,并将其改名为DINPUT8.DLL。同时再编译一份luajit的DLL,重命名为luajit.dll(原来的名字是lua51.dll),再加上直接用VS2008编译lua 5.1.4源码得到的lua51.dll,总共是三个文件。&/p&&p&将这三个文件复制到饥荒的bin目录,启动游戏。(见证奇迹的时候到了!!)&/p&&p&不出意外,BOOM!程序崩溃了!!&/p&&p&客位看官不好意思,前面洋洋洒洒写了一大堆,看起来有极大可能并没有什么用。&/p&&figure&&img src=&https://pic2.zhimg.com/11a027ee4f3dcccf830215_b.jpg& data-rawheight=&237& data-rawwidth=&280& class=&content_image& width=&280&&&/figure&不过仔细想想,这也不总是悲剧,至少证明了我们还是具有了利用DINPUT8傀儡来捅篓子的能力。&br&&p&但是为什么会崩溃呢?总得有个交待啊!!通过仔细的排查(其实就是打了个LOG),发现其实有部分函数就没Hook上。&/p&&p&但是为什么没Hook上呢?前面搜索花了那么大的精力,为什么结果还是不对呢?&/p&&br&&p&&b&0x05 调试器下没有秘密&/b&&/p&&p&没办法,只能用OllyDBG调试下试试了。由于饥荒的exe你直接点击是不会启动游戏的,它只会启动STEAM,然后用STEAM再启动游戏,所以我只能在DINPUT8.DLL的DllMain里,强行加一个getchar(),然后在STEAM中启动游戏,使用OllyDBG附加调试。&/p&&p&按ALT+E打开模块列表,点击dontstarve_steam.exe,启动Search for =& All inter-modular calls,就可以看到大多数函数已经被成功HOOK了。&/p&&figure&&img src=&https://pic1.zhimg.com/18ea8a0ac9d736e158a40a2ec079c6ec_b.png& data-rawheight=&944& data-rawwidth=&1472& class=&origin_image zh-lightbox-thumb& width=&1472& data-original=&https://pic1.zhimg.com/18ea8a0ac9d736e158a40a2ec079c6ec_r.jpg&&&/figure&&br&&p&一个个检查HOOK的函数,发现只要是搜索到了目标,基本上就没有什么大问题。问题在于很多函数就没找到,而且吊诡的是,自己手工去用OD强大的Search Command功能去搜,放宽搜索的匹配条件,也是找不到的。&/p&&figure&&img src=&https://pic1.zhimg.com/759e8bc888bd69eb849a05d_b.jpg& data-rawheight=&52& data-rawwidth=&90& class=&content_image& width=&90&&&/figure&所以……&p&难道是……&/p&&p&那些函数压根就不存在……吗?&br&&/p&&figure&&img src=&https://pic4.zhimg.com/ed27b57e1f7ba4faad26aae469c024ff_b.jpg& data-rawheight=&78& data-rawwidth=&78& class=&content_image& width=&78&&&/figure&&p&好吧。这个结论竟然对了一半。&/p&&p&确实有些api在exe中是没有的,因为exe没用到它们,编译器在连接的时候把它们抹掉了。我于是在lua51.dll中删除了它们的导出项。&/p&&p&另一部分呢,确实是没搜索到。通过查找字符串引用逐步定位相关指令的办法(具体就不详述了),我发现了一件怪事:&/p&&p&这些API和lua 5.1.4 DLL中的api在二进制层面上差别非常大!&/p&&br&&br&&br&&b&0x06 飞轮与链条&/b&&p&如果饥荒的作者修改过这些函数的实现,那么情况就非常僵了——我得在luajit中做等价的修改。不过仔细比对后发现,事情没有那么糟糕。&/p&&p&不一致的地方主要来自于两种原因:&/p&&p&1. 部分API调用内部函数被inline&/p&&p&2. 饥荒作者删除了部分API中关于luaC_checkGC调用&/p&&br&&br&&p&对于1,参考下图:&/p&&figure&&img src=&https://pic3.zhimg.com/9efda57f47acf5a540e20bf_b.png& data-rawheight=&752& data-rawwidth=&1619& class=&origin_image zh-lightbox-thumb& width=&1619& data-original=&https://pic3.zhimg.com/9efda57f47acf5a540e20bf_r.jpg&&&/figure&&p&luaO_pushfstring是lua_pushfstring所调用的函数,在饥荒主程序里这个调用是被inline的,导致其与我编译的lua51.dll中lua_pushfstring的二进制码不一致。&/p&&br&&p&对于2,参考下图:&/p&&p&&figure&&img src=&https://pic3.zhimg.com/efacb334860a_b.png& data-rawheight=&752& data-rawwidth=&1573& class=&origin_image zh-lightbox-thumb& width=&1573& data-original=&https://pic3.zhimg.com/efacb334860a_r.jpg&&&/figure&饥荒作者删除了代码中的一些luaC_checkGC的调用,从而使得一些函数不再会触发垃圾收集。这种想法是为了避免某些BUG吗?(比如C层面持有的对象因没有维持引用而失效)还是试图降低卡顿?我不得而知。但是奇怪的是,联机版的饥荒(针对联机版的内容主要参见后文)改动的地点和单机版并不一致。(上图中我添加的luaC_checkGC_是一个空宏,等价于把GC调用删除掉)&/p&&p&经过如上的修改之后重新编译lua51.dll,我们重新制作的链条终于能和饥荒原程序的飞轮啮合在一起了。&/p&&p&&b&0x07 打包炸弹&/b&&/p&&p&启动时崩溃,按照OllyDBG的LOG跟踪到是luajit有部分常数的值太小(如函数串最多常量个数,参数列表最多参数个数),改成大一些的值即可,不在此详述了。(饥荒真是内存杀手)&/p&&p&折腾了半天,我们的破补丁总算能勉强跑起来了。顺利度过loading界面,主界面成功启动!久违的背景音乐响起~&/p&&figure&&img src=&https://pic3.zhimg.com/ca9634288eaf9d8913770e_b.png& data-rawheight=&727& data-rawwidth=&1292& class=&origin_image zh-lightbox-thumb& width=&1292& data-original=&https://pic3.zhimg.com/ca9634288eaf9d8913770e_r.jpg&&&/figure&&br&&p&先随便试试基本的功能吧,先点下MODS看看会不会挂……&/p&&p&由墨菲定律可知:如果一个地方你感觉会出错,那么它就会出错。。于是有插件崩溃了。&/p&&figure&&img src=&https://pic1.zhimg.com/9efa97cd533aaadf_b.png& data-rawheight=&728& data-rawwidth=&1293& class=&origin_image zh-lightbox-thumb& width=&1293& data-original=&https://pic1.zhimg.com/9efa97cd533aaadf_r.jpg&&&/figure&&p&虽然在游戏中按“`”键也可以打开内置的控制台,但是这里LOG显示的行数有限,且不能滚屏。于是直接打开OllyDBG的LOG看看倒底发生了什么:&/p&&figure&&img src=&https://pic3.zhimg.com/e70debf13bcb68c5a3268cae_b.png& data-rawheight=&640& data-rawwidth=&862& class=&origin_image zh-lightbox-thumb& width=&862& data-original=&https://pic3.zhimg.com/e70debf13bcb68c5a3268cae_r.jpg&&&/figure&&br&&p&(这些LOG是用OutputDebugString输出的,需要OllyDBG 2.0以上版本才支持在调试器内显示它们。)&/p&&p&从上面的文字中可以看出,在加载翼语MOD的时候,mods.lua第42行报错,提示'arg'这个变量没有被定义。&br&&/p&&p&那么就去看看喽:&/p&&div class=&highlight&&&pre&&code class=&language-lua&&&span&&/span&&span class=&kd&&local&/span& &span class=&n&&runmodfn&/span& &span class=&o&&=&/span& &span class=&k&&function&/span&&span class=&p&&(&/span&&span class=&n&&fn&/span&&span class=&p&&,&/span&&span class=&n&&mod&/span&&span class=&p&&,&/span&&span class=&n&&modtype&/span&&span class=&p&&)&/span&
&span class=&k&&return&/span& &span class=&p&&(&/span&&span class=&k&&function&/span&&span class=&p&&(&/span&&span class=&o&&...&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&fn&/span& &span class=&k&&then&/span&
&span class=&kd&&local&/span& &span class=&n&&status&/span&&span class=&p&&,&/span& &span class=&n&&r&/span& &span class=&o&&=&/span& &span class=&nb&&xpcall&/span&&span class=&p&&(&/span& &span class=&k&&function&/span&&span class=&p&&()&/span& &span class=&k&&return&/span& &span class=&n&&fn&/span&&span class=&p&&(&/span&&span class=&nb&&unpack&/span&&span class=&p&&(&/span&&span class=&n&&arg&/span&&span class=&p&&))&/span& &span class=&k&&end&/span&&span class=&p&&,&/span& &span class=&nb&&debug.traceback&/span&&span class=&p&&)&/span& &span class=&c1&&--&& 42&/span&
&span class=&k&&if&/span& &span class=&ow&&not&/span& &span class=&n&&status&/span& &span class=&k&&then&/span&
&span class=&nb&&print&/span&&span class=&p&&(&/span&&span class=&s2&&&&/span&&span class=&s&&error calling &&/span&&span class=&o&&..&/span&&span class=&n&&modtype&/span&&span class=&o&&..&/span&&span class=&s2&&&&/span&&span class=&s&& in mod &&/span&&span class=&o&&..&/span&&span class=&n&&ModInfoname&/span&&span class=&p&&(&/span&&span class=&n&&mod&/span&&span class=&p&&.&/span&&span class=&n&&modname&/span&&span class=&p&&)&/span&&span class=&o&&..&/span&&span class=&s2&&&&/span&&span class=&s&&: &/span&&span class=&se&&\n&/span&&span class=&s&&&&/span&&span class=&o&&..&/span&&span class=&n&&r&/span&&span class=&p&&)&/span&
&span class=&n&&ModManager&/span&&span class=&p&&:&/span&&span class=&n&&RemoveBadMod&/span&&span class=&p&&(&/span&&span class=&n&&mod&/span&&span class=&p&&.&/span&&span class=&n&&modname&/span&&span class=&p&&,&/span&&span class=&n&&r&/span&&span class=&p&&)&/span&
&span class=&n&&ModManager&/span&&span class=&p&&:&/span&&span class=&n&&DisplayBadMods&/span&&span class=&p&&()&/span&
&span class=&k&&else&/span&
&span class=&k&&return&/span& &span class=&n&&r&/span&
&span class=&k&&end&/span&
&span class=&k&&end&/span&
&span class=&k&&end&/span&&span class=&p&&)&/span&
&span class=&k&&end&/span&
&/code&&/pre&&/div&&p&问题很明显,饥荒作者使用了旧的表示可变参数表的语法。在5.1以前,你可以使用arg来表示{...}这个表,arg[i]即可用于提取可变参数中的第i项。但是后来这个语法就被默认弃用掉了,仅保留一个宏可以开启这个兼容。LuaJIT则完全不兼容这个写法,通过仔细查看代码,它甚至删除了实现arg兼容所占用的mask bit而将这个bit用在了其他地方。&/p&&p&当时分析到这的时候,我觉得直接要求用户修改这个文件也不是什么难事,毕竟添加如下一行就可以解决问题:&/p&&div class=&highlight&&&pre&&code class=&language-lua&&&span&&/span&&span class=&kd&&local&/span& &span class=&n&&arg&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&&span class=&o&&...&/span&&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&于是我写好了详细的说明,将程序及源码发布在了Github上,并且在贴吧开了贴收集用户反馈。&br&&/p&&br&&p&&b&0x08 冒烟的补丁&/b&&/p&&p&第一波的反馈喜忧参半,可喜的是有部分用户能成功安装补丁,并且说确实有明显的效果,特别是针对Shipwrecked DLC加上大量Mods,忧的是仍然有大量不能正确启动的bug,报错也是千奇百怪,甚至有大量连游戏本身启动什么也没看到就闪退的问题。&/p&&p&对我冲击比较大的是,修改源代码文件这个要求对于普通用户来说实在是太难了。很多用户找不到文件,不知道用什么工具打开,看不懂英文因而无从下手,打了中文标点也浑然不知。而且很多第三方MOD里也用了这个旧的arg语法,要想举一反三,从我给出的修改mods.lua的方案直接得出修正第三方mod中相应语法问题的玩家非常少——毕竟贴吧以娱乐为主,不像知乎会有很多程序员。这很难办。&/p&&p&怎么办呢,只能自己把这个兼容性补丁做了。&/p&&p&在研究了一下午luajit parser之后,我放弃了按照lua源码中相同的设计添加兼容的思路。一方面如上所述,没有可用的mask bit,想要跑起来需要增加对应FLAG变量的位宽;另一方面lua源码中这个功能是在VM中解释时实现的,而luajit没有解释器,它直接跑的是原生指令。而想看明白JIT COMPILER的实现机制并且从中精准地插入这个功能并非易事。&/p&&p&不过又看了一下午parser之后,发现其实还有一个简单的办法。那就是在检测到当前编译的函数是可变参数的时候,动态插入一句local arg = {...}。这样的话有两种做法:&/p&&p&1、检测源文件文本,作一个简单的处理,定位到函数定义的地方,然后插入一行。&/p&&p&2、直接在AST生成的时候插入语法结点。&/p&&p&方法1的难度是比较低的,但是也需要稍微作一点解析工作,比如把注释,字符串跳过,检测函数定义的语句之类的。比较容易想不周全而出错。&/p&&p&要想想周全,就得搞个简单的parser。直接按方法2借用luajit自己的parser是最好的选择。为了使探索更有目的,我编写了这样的一个函数:&/p&&div class=&highlight&&&pre&&code class=&language-lua&&&span&&/span&&span class=&k&&function&/span& &span class=&nf&&test&/span&&span class=&p&&(&/span&&span class=&o&&...&/span&&span class=&p&&)&/span& &span class=&kd&&local&/span& &span class=&n&&arg&/span& &span class=&o&&=&/span& &span class=&p&&{&/span& &span class=&o&&...&/span& &span class=&p&&}&/span& &span class=&k&&end&/span&
&/code&&/pre&&/div&&p&然后跑起luajit来看看local arg = { ... }这句话究竟都会走哪些路径来生成AST。调试了一个小时之后,终于搞出来了。&/p&&p&打开lj_parse.c,定位到:&/p&&div class=&highlight&&&pre&&code class=&language-c&&&span&&/span&&span class=&k&&static&/span& &span class=&kt&&void&/span& &span class=&nf&&parse_chunk&/span&&span class=&p&&(&/span&&span class=&n&&LexState&/span& &span class=&o&&*&/span&&span class=&n&&ls&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&kt&&int&/span& &span class=&n&&islast&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&&span class=&p&&;&/span&
&span class=&n&&synlevel_begin&/span&&span class=&p&&(&/span&&span class=&n&&ls&/span&&span class=&p&&);&/span&
&span class=&n&&add_argstmt&/span&&span class=&p&&(&/span&&span class=&n&&ls&/span&&span class=&p&&);&/span& &span class=&c1&&// HERE!!!!!&/span&
&span class=&k&&while&/span& &span class=&p&&(&/span&&span class=&o&&!&/span&&span class=&n&&islast&/span& &span class=&o&&&&&/span& &span class=&o&&!&/span&&span class=&n&&parse_isend&/span&&span class=&p&&(&/span&&span class=&n&&ls&/span&&span class=&o&&-&&/span&&span class=&n&&tok&/span&&span class=&p&&))&/span& &span class=&p&&{&/span&
&span class=&n&&islast&/span& &span class=&o&&=&/span& &span class=&n&&parse_stmt&/span&&span class=&p&&(&/span&&span class=&n&&ls&/span&&span class=&p&&);&/span&
&span class=&n&&lex_opt&/span&&span class=&p&&(&/span&&span class=&n&&ls&/span&&span class=&p&&,&/span& &span class=&sc&&';'&/span&&span class=&p&&);&/span&
&span class=&n&&lua_assert&/span&&span class=&p&&(&/span&&span class=&n&&ls&/span&&span class=&o&&-&&/span&&span class=&n&&fs&/span&&span class=&o&&-&&/span&&span class=&n&&framesize&/span& &span class=&o&&&=&/span& &span class=&n&&ls&/span&&span class=&o&&-&&/span&&span class=&n&&fs&/span&&span class=&o&&-&&/span&&span class=&n&&freereg&/span& &span class=&o&&&&&/span&
&span class=&n&&ls&/span&&span class=&o&&-&&/span&&span class=&n&&fs&/span&&span class=&o&&-&&/span&&span class=&n&&freereg&/span& &span class=&o&&&=&/span& &span class=&n&&ls&/span&&span class=&o&&-&&/span&&span class=&n&&fs&/span&&span class=&o&&-&&/span&&span class=&n&&nactvar&/span&&span class=&p&&);&/span&
&span class=&n&&ls&/span&&span class=&o&&-&&/span&&span class=&n&&fs&/span&&span class=&o&&-&&/span&&span class=&n&&freereg&/span& &span class=&o&&=&/span& &span class=&n&&ls&/span&&span class=&o&&-&&/span&&span class=&n&&fs&/span&&span class=&o&&-&&/span&&span class=&n&&nactvar&/span&&span class=&p&&;&/span&
&span class=&cm&&/* Free registers after each stmt. */&/span&
&span class=&p&&}&/span&
&span class=&n&&synlevel_end&/span&&span class=&p&&(&/span&&span class=&n&&ls&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&在标记处加一行调用add_argstmt的语句,然后编写这个函数:&/p&&div class=&highlight&&&pre&&code class=&language-c&&&span&&/span&&span class=&k&&static&/span& &span class=&kt&&void&/span& &span class=&nf&&add_argstmt&/span&&span class=&p&&(&/span&&span class=&n&&LexState&/span&&span class=&o&&*&/span& &span class=&n&&ls&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&n&&ExpDesc&/span& &span class=&n&&e&/span&&span class=&p&&;&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&ls&/span&&span class=&o&&-&&/span&&span class=&n&&fs&/span&&span class=&o&&-&&/span&&span class=&n&&flags&/span& &span class=&o&&&&/span& &span class=&n&&PROTO_VARARG&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&n&&var_new_lit&/span&&span class=&p&&(&/span&&span class=&n&&ls&/span&&span class=&p&&,&/span& &span class=&mi&&0&/span&&span class=&p&&,&/span& &span class=&s&&&arg&&/span&&span class=&p&&);&/span&
&span class=&c1&&// nexps = expr_list(ls, &e);&/span&
&span class=&p&&{&/span&
&span class=&n&&synlevel_begin&/span&&span class=&p&&(&/span&&span class=&n&&ls&/span&&span class=&p&&);&/span&
&span class=&c1&&// expr_unop(ls, &e);&/span&
&span class=&p&&{&/span&
&span class=&c1&&// expr_simple(ls, v);&/span&
&span class=&p&&{&/span&
&span class=&c1&&// expr_table(ls, v);&/span&
&span class=&p&&{&/span&
&span class=&n&&ExpDesc&/span& &span class=&n&&key&/span&&span class=&p&&,&/span& &span class=&n&&val&/span&&span class=&p&&;&/span&
&span class=&n&&FuncState&/span& &span class=&o&&*&/span&&span class=&n&&fs&/span& &span class=&o&&=&/span& &span class=&n&

我要回帖

更多关于 vs里找不到bin文件夹 的文章

 

随机推荐