太长不看版:将..Whitelist.Enable..设置为true之后可以自动避免一切新增class生效(这也会拦截你禁用的class以及失效的class(还没测试这个功能,但至少接口是这么设计的),暂时没写已有class的新功能的拦截逻辑)
---
仔细检查了一下,这是一个很低级的错误(本来的写法也是,以为那么写没BUG的写法也是)
最开始,我写的是
.SetAndAdvance(OpCodes.Pop, null)
.InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_0, null))
考虑到游戏在那个位置的写法是
callvirt GameDifficulty Game::get_Difficulty()
ldfld bool GameDifficulty::deleteGameOnDeath <- set and advance
(下一个语句的labei): (下一条语句) <-在此insert and advance
我害怕下一个语句可能出现的label(目前并没有)可能会影响insert and advance被重复执行,于是我交换了set和insert的顺序
.InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_0, null))
.SetAndAdvance(OpCodes.Pop, null)
这样修改之后,我们将得到
(label0:) callvirt GameDifficulty Game::get_Difficulty()
(label1:) <- insert and advance
ldfld bool GameDifficulty::deleteGameOnDeath <-在此insert and advance
(下一个语句的labei): (下一条语句)
这里我并不确定(label1:)跟insert哪个在前,但都无所谓,因为这是ldfld,不会有编译器丧心病狂地把label打到ldfld上,因此游戏理应可以完美地执行我们的目标
这就是BUG的成因。
---
昨天玩的时候我就检测到这个BUG了(因为永恒誓约模式下仍然会被删档)
我很轻松地调整了两个语句的顺序,并计划今天上传
因为这两个语句看上去很具有欺骗性:
OpCodes.Ldc_I4_0, null // 读取false
OpCodes.Pop, null // pop 前面读取的false
这两个指令合起来非常像一个nop,因此我下意识以为这么写没问题
---
仔细观察,我们可以发现,代码其实是
before:
callvirt GameDifficulty Game::get_Difficulty()
ldfld bool GameDifficulty::deleteGameOnDeath <- codematcher指向这里
brfalse Label84
insert and advance:
callvirt GameDifficulty Game::get_Difficulty()
ldc.i4.0
ldfld bool GameDifficulty::deleteGameOnDeath <- codematcher指向这里
brfalse Label84
set and advance:
callvirt GameDifficulty Game::get_Difficulty()
ldc.i4.0
pop <- codematcher指向这里
brfalse Label84
after(等价于)
callvirt GameDifficulty Game::get_Difficulty()
nop
brfalse Label84
于是这个BUG吃掉了一条指令,直接把get_Difficulty()拿来算brfalse了
很不巧get_Difficulty() 的结果会被视为true
这就导致了BUG的发生
---
仔细检查了一下,这是一个很低级的错误(本来的写法也是,以为那么写没BUG的写法也是)
最开始,我写的是
.SetAndAdvance(OpCodes.Pop, null)
.InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_0, null))
考虑到游戏在那个位置的写法是
callvirt GameDifficulty Game::get_Difficulty()
ldfld bool GameDifficulty::deleteGameOnDeath <- set and advance
(下一个语句的labei): (下一条语句) <-在此insert and advance
我害怕下一个语句可能出现的label(目前并没有)可能会影响insert and advance被重复执行,于是我交换了set和insert的顺序
.InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_0, null))
.SetAndAdvance(OpCodes.Pop, null)
这样修改之后,我们将得到
(label0:) callvirt GameDifficulty Game::get_Difficulty()
(label1:) <- insert and advance
ldfld bool GameDifficulty::deleteGameOnDeath <-在此insert and advance
(下一个语句的labei): (下一条语句)
这里我并不确定(label1:)跟insert哪个在前,但都无所谓,因为这是ldfld,不会有编译器丧心病狂地把label打到ldfld上,因此游戏理应可以完美地执行我们的目标
这就是BUG的成因。
---
昨天玩的时候我就检测到这个BUG了(因为永恒誓约模式下仍然会被删档)
我很轻松地调整了两个语句的顺序,并计划今天上传
因为这两个语句看上去很具有欺骗性:
OpCodes.Ldc_I4_0, null // 读取false
OpCodes.Pop, null // pop 前面读取的false
这两个指令合起来非常像一个nop,因此我下意识以为这么写没问题
---
仔细观察,我们可以发现,代码其实是
before:
callvirt GameDifficulty Game::get_Difficulty()
ldfld bool GameDifficulty::deleteGameOnDeath <- codematcher指向这里
brfalse Label84
insert and advance:
callvirt GameDifficulty Game::get_Difficulty()
ldc.i4.0
ldfld bool GameDifficulty::deleteGameOnDeath <- codematcher指向这里
brfalse Label84
set and advance:
callvirt GameDifficulty Game::get_Difficulty()
ldc.i4.0
pop <- codematcher指向这里
brfalse Label84
after(等价于)
callvirt GameDifficulty Game::get_Difficulty()
nop
brfalse Label84
于是这个BUG吃掉了一条指令,直接把get_Difficulty()拿来算brfalse了
很不巧get_Difficulty() 的结果会被视为true
这就导致了BUG的发生