8 multibind 的时序问题 - eLecCap1taL/CS2-CFG-Wiki GitHub Wiki
事实上,8 月 20 日 multibind 更新带来的不只是更新公告中的问题,还存在更新公告之外的暗改 😒😒
cfg 的执行是自然存在一个 “时序”、即 “时间顺序” 的。考虑如下 cfg:
//第一个文件,1.cfg
alias A echo 1
A
//第二个文件,2.cfg
alias A echo 2
A
假如我们在控制台内输入 exec 1;exec 2;exec 1
会发生什么?🤔
[InputService] execing 1
[Console] 1
[InputService] execing 2
[Console] 2
[InputService] execing 1
[Console] 1
先输出 1,后输出 2,再输出 1,一切正常🤓 cfg 是顺序执行的
那如果我们把它绑给按键呢?在控制台内输入 bind p "exec 1;exec 2;exec 1"
并按下 p,看看输出了什么?🤔按理说应该是一样的?🤔
[InputService] execing 1
[InputService] execing 2
[InputService] execing 1
[Console] 1
[Console] 1
[Console] 1
不是,哥们?😦 这啥玩意啊
这就是 bind 的时序错乱问题。我们再用一个例子来感受这一点:
- 在控制台输入
alias A say 1
- 在控制台输入
bind p "alias A say 2;A"
- 按下 p
按理说,在按 p 的瞬间,应该先执行 alias A say 2
使得 A
被定义为 say 2
,再执行 A
的时候聊天栏会输出 2
。
事实上,说了 1
🤡
你可以理解为:在 bind 的绑定内容被触发时,对于它内部(“内部” 指的是穿过所有嵌套的 alias 和 exec,刚才的例子有体现)所有对 alias 的修改,在这次触发中不会生效。
以上面的例子为例,本来 A
被定义为 say 1
,虽然在 bind 内部,写了要先 alias A say 2
,但实际上这被推迟到了整个触发结束后生效,于是触发的是旧内容,say 1
。
再尝试如下例子:
- 在控制台输入
alias A say 1
- 在控制台输入
bind p "alias A say 2;A"
- 按下 p,输出 1 (结束后,
A
才被设置成say 2
) - 在控制台输入
A
,输出 2
这显然是违反常识且不正常的。一开始的 1 2 1
例子才是正常的。可以证实,这种顺序的错乱是由于 multibind 的限制导致的,目前认为是一个 BUG。😥😥
我们已经在上个章节中学会了绕过 multibind 限制,如果我们绕过了 multibind 限制就不会有这个问题
这可能是 V社 为了禁止 multibind 做的一些改动之一,我们不得而知 😥
但绕过 multibind 总会带来一些缺点,还会让写法变的十分复杂。有没有更好的方法呢?
答案是肯定的 🤗
这个方法用到了 管道符 |
,你可能暂时不知道它是什么,没关系,你可以在后面再了解它。😉 你可以先记住这种写法,把它当做一种固有格式。
上文提到,bind 的时序错乱是包括内部所有指令的,即使穿过了多层 exec
也是如此。
然而,通过把 exec 文件名
替换为 echo 文件名 | exec
这种写法(二者都是读取一个 cfg 文件),使得我们能够不再受这种时序错乱影响。
管道符也需要用双引号引起来
也就是说,只需要 bind "echo 文件名 | exec"
,之后就可以在 文件名.cfg
内正常书写 cfg,不再会有时序错乱的问题。
如果 bind 内定义的 alias 之前根本不存在,那么游戏确实会执行新的定义。
考虑如下例子:
- 控制台输入
bind p "alias a_new_alias say 1;a_new_alias"
- 按下 p, 正确地输出了
1
此方法甚至不能算作 “修复”,它也完全没有可复用性,只能算作一种特性。🤔
我们之前提到,自定义 cvar 可以覆盖 alias。我们可以利用这一点来修复时序错误。
考虑如下 cfg:
//定义空的 fix_cmd
alias fix_cmd
//定义 fix
alias fix fix_cmd
//覆盖掉刚才的 alias
setinfo fix 0
//继续使用我们使用刚才的例子
alias A say 1
bind p "alias A say 2;alias fix_cmd A;incrementvar fix 0 0 0 | grep ~"
按下 p,会发现是正常的。🤔
它的正常样子应该是:bind p "alias A say 2;A"
你可以把 alias fix_cmd A;incrementvar fix 0 0 0 | grep ~
直接当成触发 A 的另一种写法,它能触发 A 并不受时序问题的影响。😉
问:不是说自定义的 cvar 不能使用 incrementvar 吗
答:确实可以使用,但只是会有问题,incrementvar 永远会把自定义的 cvar 设置成 0。不过我们此处不在乎它的值是多少
问:这段神秘的写法到底是什么意思?😡
答:我们曾发现,如果先定义一个 alias,再定义一个同名的 cvar 并对其使用
incrementvar
,那个 cvar 会在变成 0 的同时,触发它先前被定义的 alias 的内容,并且不受时序错乱影响。 非常奇怪对吧,但事实就是这样,这是一个经验性的事实 😂 至于为什么还要有一个fix_cmd
,是因为fix
被覆盖成 cvar 后,它作为 alias 的内容就不能再被更改了。因此我们把fix
提前定义为fix_cmd
,之后对fix_cmd
做修改,就可以达到修改fix
的 alias 内容的效果。incrementvar
后面的| grep ~
也是一个管道符用法,它的大概左右是接受掉incrementvar
的内容输出,防止控制台刷屏
还有很多其他的方法。有趣的是,这些方法曾经都不只是能修复时序错误,而且还能绕过 multibind 限制,只不过被修复了。但它们大多依然可以用于修复 multibind 更新带来的 bind 时序错误