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 时序错乱的方法

echo | exec

这个方法用到了 管道符 |,你可能暂时不知道它是什么,没关系,你可以在后面再了解它。😉 你可以先记住这种写法,把它当做一种固有格式。

上文提到,bind 的时序错乱是包括内部所有指令的,即使穿过了多层 exec 也是如此。

然而,通过把 exec 文件名 替换为 echo 文件名 | exec 这种写法(二者都是读取一个 cfg 文件),使得我们能够不再受这种时序错乱影响。

管道符也需要用双引号引起来

也就是说,只需要 bind "echo 文件名 | exec",之后就可以在 文件名.cfg 内正常书写 cfg,不再会有时序错乱的问题。

定义新 alias

如果 bind 内定义的 alias 之前根本不存在,那么游戏确实会执行新的定义。

考虑如下例子:

  • 控制台输入 bind p "alias a_new_alias say 1;a_new_alias"
  • 按下 p, 正确地输出了 1

此方法甚至不能算作 “修复”,它也完全没有可复用性,只能算作一种特性。🤔

与 cvar 相结合

我们之前提到,自定义 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 时序错误

⚠️ **GitHub.com Fallback** ⚠️