7 官方的制裁 multibind - eLecCap1taL/CS2-CFG-Wiki GitHub Wiki
multibind 限制
在 2024 年 8 月 20 日,V社发布了一次更新。
《反恐精英》在不断发展, 从美术到地图,再到别出心裁的玩法,甚至玩家的输入,CS社区都在塑造着这款游戏。
编写脚本和自动执行玩家指令一直是有争议的话题,但多年来,某些形式的脚本(如跳投)已获得认可,因为它们能让玩家发挥出本不可能发挥出的水平。 事实上,跳跃投掷已成为游戏的重要组成部分,因此我们做了大量工作,使其在没有任何特殊脚本或绑定的情况下也能可靠地使用(即通过跳跃并快速投掷手榴弹)。
培养一个人的协调性和反应速度一直是掌握《反恐精英》的关键。
最近,一些硬件功能模糊了手动输入和自动操作之间的界限,因此我们决定在《反恐精英》中划清界限,明确哪些是可接受的,哪些是不可接受的。
我们不再允许规避这些核心技能的自动化操作(通过脚本或硬件),而且从现在开始(最初--仅限于 Valve 官方服务器),涉嫌通过单个游戏输入自动执行多个玩家操作的玩家可能会被踢出比赛。
为防止意外违规,包含多个移动和/或攻击动作的游戏内绑定将不再有效(例如空绑定和跳跃-投掷绑定)。
这基本是针对键盘的后覆盖功能的,我们要关心的是最后一句话:
为防止意外违规,包含多个移动和/或攻击动作的游戏内绑定将不再有效
这意味着什么?😲😲尝试如下指令:
alias +A "+forward;+right"
alias -A "-forward;-right"
bind p +A
按下 p 的时候,你觉得会发生什么?
按理说,我们会在按下时向右前方走,在松开时停止移动——这基本就是 W 和 D 两个键的合体嘛!😎
真的吗?你会发现你一点都没动起来😢😢。让我们查看一下控制台,看看有没有什么问题。
我们发现了这个:
'right' can't follow 'forward' in the same input binding. Ignoring input.
'right' can't follow 'forward' in the same input binding. Ignoring input.
是的,这就是 2024 年 8 月 20 日更新所禁止的:同一个 bind 内只能触发一个动作指令。不允许多绑定(multibind)
这里的动作指令包含了太多:移动,开火,跳跃,静步,等等。这里列出一些常见的不被限制的操作。
- 定义
alias
或调用 alias。(不用试图通过这个绕过限制,不行的) - 使用
exec
调用其他 cfg 文件。(不用试图通过这个绕过限制,不行的) - cvar 相关指令
- bind
- 换手
- 开关无线电
- 切枪(
slot
)
注意,如果存在特殊 alias(+
-
开头的),它们分别受 multibind 限制。这样的代码是可以的:
alias +A +forward
alias -A -forward
bind p +A
+-动作
算两个,这样的 cfg 是不可以的:
alias jp "+jump;-jump" //触发多动作限制
bind p jp
这应该相当合理,否则你正常按键也走不了路呀!😮
现在你应该也能理解,为什么以前的跳投指令无法再使用了。我们来想一想跳投至少需要几个 "动作"。假设玩家提前拉栓,我们只考虑左键跳投:
+jump
-jump
-attack
三个动作!但我们最多只能有两个动作(特殊 alias 会分开限制),这就是无法跳投的原因。🙁
在刚更新的时候,绕过此限制的方法曾五花八门,但在 2024 年 10 月底的一次更新中,他们绝大部分都被修复了。😥😥
绝大部分被修复了?🤨🤨🤨
绝大部分?😮
也就是说……
绕过 multibind 限制的方法
mouse_x
/ mouse_y
没想到吧,视角的转动也是由 bind 控制的!可见 cfg 是 CS2 不可分割的一部分😀
默认的绑定如下:
bind mouse_x yaw //鼠标横向移动
bind mouse_y pitch //鼠标纵向移动
yaw
和pitch
非常特殊,他们直接接受bind
的返回值工作。你可以尝试bind mouse_x say
并移动鼠标,就能看到鼠标给bind
的返回值了。它们两个是移动视角的指令。在后面我们会再见到它们
必须直接把
yaw
放在bind mouse_x
的最后,否则将无法接收到返回值导致无法转动视角
pitch
同理
也许是出于于性能考虑,mouse_x
和 mouse_y
并没有 multibind 检测。尝试如下 cfg:
bind mouse_x "+jump;-jump;yaw" //必须把 yaw 直接写在最后
bind mouse_y "+jump;-jump;pitch" //必须把 pitch 直接写在最后
你会发现随着转动鼠标,你会跳起来。这一个 bind 里可足足有 3 个动作了!😎
之前的一个 5e 自动连跳脚本就是通过这种方式实现的。
你可以自己尝试编写一个 cfg,按住空格时移动鼠标会移动视角+跳,不按的时候正常移动视角 🤔 你的知识应该已经足够编写这个 cfg 了
一种写法:
//跳跃的组合打包,这个 alias 是多动作,我们需要用 mouse_xy 来绕过限制
//之所以是 -jump 开头,是因为我们需要先抵消掉空格产生的跳跃指令计数
//同时,如果跳跃指令计数已经为 0,先 -jump 也不会有任何副作用,非常完美
alias jump_multibind "-jump;+jump;-jump"
//给鼠标的 alias
//留空后面可以创建一个空的 alias,这可以避免控制台刷屏报错未知指令
alias mouse_x_task
alias mouse_y_task
//启用移动鼠标跳跃
alias mousejump_on "alias mouse_x_task jump_multibind;alias mouse_y_task jump_multibind"
//关闭移动鼠标跳跃
alias mousejump_off "alias mouse_x_task;alias mouse_y_task"
alias +space_task "mousejump_on;+jump"
alias -space_task "mousejump_off;-jump"
bind space +space_task
bind mouse_x "mouse_x_task;yaw"
bind mouse_y "mouse_y_task;pitch"
利用无线电轮盘
打开无线电轮盘,利用滚轮可以切换,一共三个轮盘,每个轮盘 8 个槽位 ,一共 3*8=24 个槽位。
它们的内容也是由 cvar 控制的。
cl_radial_radio_tab_0_text_1 控制了 0 号轮盘的第 1 槽位的内容
默认它们都绑定到了一种特殊的无线电指令,例如 cl_radial_radio_tab_0_text_3 = #Chatwheel_bplan
我们可以更改它的内容,使得他可以触发指令。尝试如下 cfg:
//跳跃的组合打包,这个 alias 是多动作,我们需要绕过限制
alias jump_multibind "-jump;+jump;-jump"
cl_radial_radio_tab_0_text_1 cmd";jump_multibind"
//这是轮盘修改的通用格式,利用 CS2 解析 cfg 的特性使得它触发指令
开启无线电轮盘,找到 0 号轮盘的 1 号槽位(它应该是空白的),触发它,你会发现你跳起来了 😎😎 绕过了 multibind 限制
只修改一个太不方便了,我们修改轮盘内 24 个槽位,使得无论往哪个方向都可以触发:
alias jump_multibind "-jump;+jump;-jump"
cl_radial_radio_tab_0_text_1 cmd";jump_multibind"
cl_radial_radio_tab_0_text_2 cmd";jump_multibind"
省略中间部分...
cl_radial_radio_tab_0_text_8 cmd";jump_multibind"
cl_radial_radio_tab_1_text_1 cmd";jump_multibind"
cl_radial_radio_tab_1_text_2 cmd";jump_multibind"
省略中间部分...
cl_radial_radio_tab_2_text_7 cmd";jump_multibind"
cl_radial_radio_tab_2_text_8 cmd";jump_multibind"
但这样还必须得把鼠标移动过去才能触发,怎么办?
我们会发现一个 cvar:cl_radialmenu_deadzone_size
,其默认值为 0.4,控制着使得轮盘指令生效的至少半径
默认的 0.4 要求你需要把鼠标移到对应的选项位置,才能触发对应的槽位。我们把他改成 0.0,就可以做到不移动鼠标触发槽位。因为所有槽位都是一样的,所以我们不关心具体触发哪个。
同时还有 cvar:cl_radial_radio_tap_to_ping
,默认为 1,代表快速点按开关无线电轮盘时,会进行标点,我们不希望它标点,把它改成 0 即可。
如下 cfg:
alias jump_multibind "-jump;+jump;-jump"
cl_radial_radio_tab_0_text_1 cmd";jump_multibind"
cl_radial_radio_tab_0_text_2 cmd";jump_multibind"
省略中间部分...
cl_radial_radio_tab_0_text_8 cmd";jump_multibind"
cl_radial_radio_tab_1_text_1 cmd";jump_multibind"
cl_radial_radio_tab_1_text_2 cmd";jump_multibind"
省略中间部分...
cl_radial_radio_tab_2_text_7 cmd";jump_multibind"
cl_radial_radio_tab_2_text_8 cmd";jump_multibind"
//修改触发 "死区" 为 0,即不移动鼠标也可以触发
cl_radialmenu_deadzone_size 0.0
//关闭短时间内开关,即点按时的报点
cl_radial_radio_tap_to_ping 0
这个方法的坏处是开轮盘时无法移动视角,且点按特别快时有可能不触发。不过应付定点的跳投或前跳投是足够的。
你可以据此自己尝试编写一个跳投 cfg 🤔 你的知识应该已经足够编写这个 cfg 了
具体不在此处展开,有一个提示:把 jump 和 attack 放进一个 alias 会出现莫名其妙的问题。即不要出现 alias A "+jump;-jump;-attack;-attack2"
这样的代码。你应该新建一个 cfg 文件,在里面写入 +jump;-jump;-attack;-attack2
,再 alias A exec 你刚才新建的cfg文件
exec_async
真神——上面两个方法都有它的缺点
mouse_xy
必须移动鼠标才能触发指令- 无线电轮盘开启时无法转动视角,点快了还有可能不触发
难道我们没有任何办法了吗?
答案是否定的,我们有 exec_async
exec_async 文件名
:类似exec
,不过是以帧速率加载cfg文件
什么意思?什么是帧速率?
新建一个 cfg 文件,输入如下指令:
echo A
echo A
echo A
echo A
重复一百次……
echo A
echo A
我们回到控制台,输入 fps_max 64
锁定到 64fps。
尝试 exec 刚才的文件
,会发现控制台在一瞬间刷屏了:这些指令是瞬间被执行的。
尝试 exec_async 刚才的文件
,会发现虽然依然输出的很快,但并不是瞬间。
是的,这就是 exec_async
的特性,它不像 exec
一样瞬间执行所有指令,相反,它会先读取整个 cfg 文件,再一帧一行地读取。
划重点,exec_async
加载的文件,每帧执行一行,直至结束
如果你是 64fps,一秒就会执行 64 行,如果是 1000fps,一秒就会执行 1000 行。
你会想,这和 multibind 有什么关系 😡😡 难道这就不受 multibind 影响了?
还真是🤣。仔细想一想,multibind 限制的是从 bind 产生的指令,这 exec_async
是游戏引擎每帧执行一行,关 bind 什么事?自然不受影响
你可能欣喜若狂,写出如下的跳投 cfg:
//第一个文件 假设为 A.cfg
-jump;+jump;-jump;-attack;-attack2
//第二个文件 进行绑定
bind p exec_async A
你会想,这不就能跳投了?多简单 😡😡😡 为什么之前说那么多没用的?
有个问题是,exec_async
需要开启作弊,在官匹和平台的对局中都无法使用。能 exec_async
了,意味着都可以用 noclip
飞天穿墙了,还用得着跳投? 🤪🤪
那你又会想了,那实战不能用的指令有啥用啊 😡😡
别急,仔细想一想刚才的两个特点:
exec_async
加载的文件,每帧执行一行,直至结束
exec_async
需要开启作弊,在官匹和平台的对局中无法使用
诶,我有个点子 🤓👆
我们并不是一打开游戏就在对局服务器内的。我们会先进入大厅,再进入对局服务器。
于是我们可以在游戏刚加载时,exec_async
一个超级长的 cfg,使得进入对局服务器后它也在继续执行。
虽然服务器会关闭作弊,但它并不会终止正在执行的、被 exec_async
加载的 cfg 🤓👆
你又会想了,我又不是时时刻刻都跳投,一直执行跳投指令怎么行 😡😡😡
看如下跳投 cfg:
//用于去除 jump 和 attack 指令共存 bug 的 cfg 文件
//假设为 tiaotou.cfg
-jump;+jump;-jump;-attack;-attack2
//用于 exec_async 的文件,超级长,假设为 A.cfg
//这个 cfg !!必须!! 在刚进游戏时自动加载,不能通过控制台手动加载,来不及
//可以使用启动项或 autoexec.cfg,都读到这里了,这个应该会吧 😡
//t 是我们将要在下个文件里定义的 alias
t
t
t
t
t
省略 114514 行……
//开启作弊
sv_cheats 1
//加载我们的超长文件
exec_async A
//默认 t 是空的,即啥也不做
alias t
//跳投的实际触发指令
alias tiaotou_t "exec tiaotou;alias t"
//绑定
bind p alias t tiaotou_t
你可能看晕了,这是什么意思 😦😦
仔细想一想这个过程:
- 加载了一个超长的文件,从此以后
t
被疯狂地执行,一帧一次🤗 - 默认
t
是空的,所以不会做任何事 - 当我们按下 p键 的瞬间,
t
被重新定义为tiaotou_t
。虽然是 bind 触发的指令,但我们只是定义了一个 alias,一个动作都没有,不受 multibind 限制 🤗 - 下一帧时,游戏引擎会执行
t
,从而执行tiaotou_t
。这是由游戏引擎执行的,和 bind 一点关系没有,不受 multibind 限制 🤗 - 玩家跳投,并再次把
t
变为空 - 现在跳投结束了,
t
也是空的,不会做任何事
What can I say,天才 😲😢😨😱😰🥵😳
这个方法的唯一缺点就是会引入一帧的延迟,不过这完全可以接受了,实际上根本不可能感知的到
但是事情还没有结束。CS2 对于 exec_async
加载的文件的大小有限制,不能超过 1MB,即 1024KB
那么 1MB 的 cfg 能加载多长时间呢?事实证明,哪怕使用 LF 换行符而不是 CRLF(可自行上网搜索了解),最长的 cfg 也只能拥有 520000 行左右,假设玩家帧数在 900 以上,大约只能坚持 8 分钟,这怎么办?
幸运的是,exec_async
加载的 cfg 可以使用一个特殊的指令 sleep
。
只有
exec_async
加载的 cfg 内才可以用sleep
!
它的基本功能如同它的名字,睡眠,暂停这个 cfg 的执行。其后可以跟上一个数字,表示暂停时长,执行单位是毫秒。
于是我们可以准备多个 cfg 文件,先让绝大部分 cfg 在一开始就暂停执行,一个一个地开始执行,达到持续的效果
若每个文件有 520000 行 t
,我们假设它能坚持 515000 毫秒,写如下 cfg:
//0.cfg
t
t
t
省略 520000 行……
//1.cfg
sleep 515000
t
t
t
省略 520000 行……
//2.cfg
sleep 515000
sleep 515000
t
t
t
省略 520000 行……
直到……
//22.cfg
sleep 515000
sleep 515000
省略 20 个 sleep
t
t
t
省略 520000 行……
再单独用一个 cfg 加载他们:
sv_cheats 1
exec_async 0
exec_async 1
exec_async 2
省略……
exec_async 22
这大约能坚持 3.3 小时,足够大多数时候使用了。时间到了只需要重新启动游戏即可,事实上很少一直开游戏达到这个时间。如有需要可以自己加长。
问:如果第一个 cfg 结束的时候,第二个 cfg 还没开始怎么办?😳
答:这意味着玩家达到约 990fps,几乎不可能。可以自行计算
问:如果第一个 cfg 还没结束,第二个 cfg 就开始了怎么办?这不是重叠了吗?😡
答:事实上,仅对于绕过 multibind 的目的来说,这并没有任何影响,你可以自己思考一下。
关于 exec_async
的更多用途和更详细的解析,会在计时章节讲解。