本帖最后由 imyz 于 2025-2-26 14:41 编辑
上一节内容提到 Valve 将 X 取值由 0 变 1,其本质是将原有的帐号由 0 号宇宙迁移至 1 号宇宙的行为,不过前面章节的重点是围绕玩家个人帐号类型(Individual)的分析,也就是前文所说 “保真” 的这部分,然而这一说法实际上不严谨,究其原因在于,虽然绝大多数玩家日常能接触到的是个人帐号这一类型,可也有例外,比如:Anonymous Game Server(匿名游戏服务器)类型,即官方文档中帐号类型序号为 4、字母为 ‘A’ 那个。匿名游戏服务器类型帐号是 Valve 分配给玩家自搭建 Steam 组服务器(Dedicated Server)之用的,即俗称的“玩家私服”。从逻辑上讲,只要能获得并正确解析出该帐号,则这一类的服务器帐号也必然保真!那么要如何获取这一帐号呢?对有自建服务器的玩家而言很简单,直接在 Server Console 控制台中输入 “status” 指令并回车后即可见其真身,如下图:
既然这类帐号是分配给服务器的,那么显然对于无自建私服的普通玩家而言,若想直接获取该帐号显然是不可能的,但间接方式却是有的,以 CS:S(Counter-Strike: Source)这款游戏为例,玩家可以启动并进入自己的游戏客户端,然后点击 “寻找服务器”,再从出现的服务器列表中任选一个并加入,一旦成功进入服务器后,紧接着再按 ~ 即数字 1 左侧那个键以呼出游戏控制台,最后在控制台下方中输入 status 并回车提交即可显示出该服务器的基本信息,其中便有 SteamID,如下图中红框中冒号后面所示。注意:该方法并不适用于所有游戏,例如 L4D2 便不显示,不过好在理论上仅需知道一个服务器帐号便可举一反三。
上图中的 SteamID 分别显示了 SteamID3 和 SteamID64 两种格式,圆括号中的为后者。乍一看这两串东西感觉有点摸不着头脑,我一开始也这样,而有这种感觉其实是一般正常人的反应,虽然官方文档确实交待了 SteamID64 的来由,但 SteamID3 却说得相对隐晦,因为只讲了一半而另一半却只字不提!这事后面会再说到,还是先来讲相对简单的 SteamID64,请先看截图:
对于具备微机原理基础知识的盆友们相信应当很容易看懂这个示例。SteamID64 本质就是一个 64-bit (8 Bytes) 十六进制无符号整数,上图中间那串 “0101” 是其二进制形式,显然括号中的 “90257587859051544” 即为那台服务器 SteamID64 格式帐号的十进制形式。而 RegularID 那个 STEAM_X:Y:Z 格式与 SteamID64 之间的转换从上图中也能猜出个大概。可但是,“[A:1:1184846872:43212]” 这一 SteamID3 格式一眼看上去好像完全没逻辑对吧!?咱们还是先看官方文档唯一一处对该格式的简介,就是下图中箭头所指的那个,的确是简介没跑了:
读下来好像明白了,但好像又没完全明白,W 后面不应当接字符 ‘]’ 就结束了吗?还有,它与括号中的 SteamID64 二者之间又是怎么相互转换的呢??的确这道题的解题过程有一点点绕,咱们首先得将那个 SteamID64 转换成十六进制,可得:0x0140A8CC469F5418,这样看可能还不太明显,接着再给它分个组:0x01 4 0A8CC 469F5418,再根据官方文档对其中各项的定义可得:X = 1, Y = 0, Z = 469F5418>>1 (右移一位) = 592423436, Type = 4, Instance = A8CC。现在应当能看出些东西了,比如:Type = 4 即对应类型字母 ‘A’,但余下有几个好像并不能直接代入对吧?这时官方文档中的计算公式 W=Zx2+Y 就派上用场了,将 Z 代入该公式即可得 W = 1184846872, 所以这个数字正好与图中 SteamID3 当中的对上了!可问题来了,图中那个 SteamID3 尾部的 “:43212” 又是个啥?这个问题的答案我在官方文档中是找遍了也找不着 —— 这便是我上面所说官方只字不提的那一半……噢,对不起,我这样讲确实也不严谨,因为得加上 “:1:” 之后才是严格意义上的一半。到这里必须得打断一下,现在新问题又来了,尤其是这个 “:1:” 中的 1 又代表啥呐?是 X 吗??假如它是,那么为啥官方不记作 [Letter:X:W] 或是 [Letter:X:W:Instance]???—— 针对这一点官方文档同样压根没交待,既然官方未给出正确答案,我们就先不纠结该问题,毕竟前面还有 Instance = A8CC 这个十六进制数还没用到,聪明的看官们必定想到了它大概率与 W 后面的 “:43212” 是等价的,因为我不会心算,只得老老实实用计算器来将其转换成十进制,果然得到 “43212”。
在大致清楚了 SteamID64 与 SteamID3 之间的转换后,再来看 RegularID,那么很容易可得:STEAM_1:0:592423436。可此处这个 RegularID 是完全没有意义的,原因就在于我们若将 Type 换作 5、6、7 等帐号类型是可以得到完全相同的 RegularID。由此不难联想到:Valve 要么已准备放弃 RegularID 格式,要么就是又默不作声地对其进行了某些不为外人所知的改造…… 针对这点此处暂埋一个伏笔。至此,除这个伏笔和那个 “:1:” 来历尚未明确外,SteamID3 与另两种格式的转换已基本清楚了,并且还可以得出以下结论:
- SteamID64 在 SteamID 宇宙中与自然正整数类似并且其本身是“连号的”;
- RegularID(STEAM_X:Y:Z)因缺少 Type 与 Instance 俩基本元素,其本身若不作改造则仅适用于原 32-bit 体系;
- SteamID3 还细分两种表示形式,[Letter:1:W] 适用于 Individual 类型,而 [Letter:1:W:Instance] 则适用除 Individual 外的其它类型。
基于以上的结论可得,若暂抛开 RegularID 的伏笔不谈,三种格式相互转换当前仅余下 “:1:” 是否等于 X 这一问题,虽然官方并未正面给出答案,但并不妨碍我们利用已知条件来进一步审视与分析 [Letter:1:W:Instance] 这一格式:
- 已知 Letter 代表 Account Type;
- 已知 W = Z x 2 + Y;
- 已知 Instance 代表服务器的实例号;
归纳以上各项条件,组成一个 64-bit 帐号必要的 5 个基本元素当中,除 X 外其余均已确定。假如 “:1:” 之中的 1 不代表 X,显然 SteamID3 这一格式将因缺少必要元素而不适用于 64-bit 体系;同理,[Letter:1:W] 这一格式之所以能与 STEAM_X:Y:Z 相互转换,其中的 “:1:” 必然与 X 对应,这无疑相当于从反面/侧面证明了 “:1:” 逻辑上在 1 号宇宙中就必然是 X。很显然,我这个推理无论逻辑上如何完美都始终缺少一样证据,那么就直接上物证(这还得多谢 CS2):
如此便又带来一个新的思考:之所以 Valve 将 SteamID3 格式定义为 [Letter:1:W] 而非 [Letter:X:W] 的真实意图,极有可能是想将其专用于 1 号宇宙,而在其它宇宙中则另有其它格式 —— 也只有如此才能合理地解释这个让人有些摸不着头脑的设定。另一方面,既然提到了帐号体系,再结合上面官方文档截图中 For 32-bit/64-bit system 的分类,将它与 [Letter:1:W], [Letter:1:W:Instance] 两种格式联系起来看,很显然前者因缺少 Instance 而仅适用于 32-bit 体系,因而作为其 32-bit 的表示形式,而后者则为其 64-bit 表示形式…… 现在是不是就彻底明白了官方文档这一表述背后的真实意思!再换句话讲:玩家帐号 [U:1:W] 其实也是 32-bit 的表示形式,那么它理论上还有 64-bit 表示形式,前面讲了官方定义它的 Instance = 1,严格上讲它应当长 [U:1:W:1] 这模样,而硬生生地将尾巴抹掉就显得很没道理对吧,现在若再结合我第(二)章节末尾那段再理解一下,是不是就能够从另一角度解释为什么我要用“弃用”、“公然铺张浪费”来形容?
----- 插播一条 -----
由此不得不再回到之前所说的“保真”一说,在清楚了 SteamID 三种格式之后应该不难理解,保真一说实际上是针对 RegularID 和SteamID3 二者,而 SteamID64 才应当是本尊。正因如此,SteamID64 理论上都是真的,保真与否在于 Valve 是否官宣启用。
----- 插播结束 -----
既然 SteamID3 的格式尤其是针对服务器类型的已经能够确定,并且它包含了组成一个 64-bit 帐号的所有元素,从逻辑上讲它应当适用于其余帐号类型。可问题在于:诸如帐号类型为 ‘8’ 所给出的 “T / L / c” 这三个字母具体分属哪个号段呐?官方文档只提到 T = Mulit-user chats,L = Lobbies,c = Group(clan) chats,在 X, Y, Z, Type 均不变的情况下,欲在同一帐号类型下再细分号段,从数学上讲仅剩下 Instance 可用了,那么按照常理,要细分理应从高位下手,由于 Instance 是 20 bits,若仅取最高 4 bits 作标识位,理论上可以分为 0x00000, 0x10000, 0x20000, 0x40000, 0x80000 这 5 个大段,肯定够三个字母分,但具体要如何分呢?这又是官方只字未提的地方!不得已,只能自己找线索。
但凡玩过 L4D2 这款游戏的玩家都应该知道它支持创建游戏大厅 (Lobby),这无疑是一个重要线索,也恰因我有这方面的需求,所以才注意到这个极少数人会去关注的一个极不起眼的细节:当游戏客户端创建好大厅并点击加入服务器时,在服务器端的控制台中仔细查找便可发现下图中 Reservation Cookie 处紧跟一个十六进制整数,即:箭头所指红框中的数字:
其本身已极不起眼了,这样定睛一看也还是不明显,那就再给它分个组:0x01 8 60000 03956b67,所以它就是一个采用十六进制数表示的 SteamID64 格式帐号,既然这个 Reservation cookie 是游戏客户端创建大厅后点加入服务器时出现的,我们就有理由相信它与 Lobby 密切相关,索性就暂时认为它就是该游戏客户端创建的那个大厅的 Lobby 号(我也没有证据),于是先锁定字母 ‘L’,再将其转换成 SteamID3 格式则为:[L:1:60124007:393216]。于是乎,基于目前所掌握的信息便可推导出以下推论:
- 实例号 0x60000 最高位 ‘6’ 的二进制为 0110,则只可能归属于 0x40000 这个号段,其余均被排除;
- 实例号 0x60000 与字母 ‘L’ 存在对应关系,并且极有可能专用于 L4D 系列(但不确定);
- 基于以上,实例号高最 4 bits(即 6 所在位)应当为标识位;
- 逻辑上 0x40000 才应当为分段的起始号,所以字母 ‘L’ 的起始号应当为 0x40000;
- 基于避免铺张浪费的原则,0x40000 ~ 0x70000 这几段按理都应当是预留给 Lobby 的。
貌似有点头绪了,于是就顺着这条线索继续深挖。进一步分析,若其成立,则预留给游戏大厅专用的仅有 4, 5, 6, 7 共四种类型,似乎太少!那么假如再多加 4 bits 则可得:4 x 2^4 = 64 种,这好像就有点合理了,若还不够还可再多加 4 bits 则有 64 x 2^4 = 1024!由此推测:Instance 的高 8 或 12 bits 应当是被用作实例的标识符,余下 12 或 8 bits 才应当是真正的实例序号。虽然字母 ‘L’ 已有着落,可余下的字母 ‘T’ 和 ‘c’ 的线索,我目前所能找到最早出现有关于它们的描述的就只有日期分别标注为 2009/11/07 和 2013/7/15 这两篇官方文档的历史记录,门牌号在此:
SteamID - 14:12, 7 November 2009
SteamID - 06:22, 15 July 2013
显然这又侧面佐证了我在前面第(二)章节中对 Steam 帐号设计方面的猜测…… 言归正传:关于帐号类型 8 的定义最早于 2009 年出现且出现的唯一字母为 ‘T’,而在差不多 4 年后的 2013 年才同时增加了 ‘L’ 和 ‘c’。这样一来就为解题提供了充分条件,因为在不清楚未来的规划的情况下,号段最稳妥的划分方法就是先从最小开始而后递增,并且也符合常人的习惯。与此同时,考虑到早期从 32-bit -> 64-bit 的过渡,显然 Instance = 0 是兼容 [T:1:W] 这种 32-bit 形式的最佳号段,所以 ‘T’ 当初应该就是 0x01800000 这一段。基于该推理,帐号类型 8 下各字母的适用范围就暂被确定如下:
- ‘T’ -> 0x0180000000000000 ~ 0x0183FFFFFFFFFFFF
- ‘L’ -> 0x0184000000000000 ~ 0x0187FFFFFFFFFFFF
- ‘c’ -> 0x0188000000000000 ~ 0x018FFFFFFFFFFFFF
有一说一,因为这一理论没有官方文档的支撑或背书,所以该帐号类型的输出结果除 SteamID64 如假包换之外,其余 RegularID 和 SteamID3 两种我不敢说保真!!!与此同时,若有高人能提供线索助我补齐这一短板将不胜感激!
接着再看类型 ‘7’ 即字母 ‘g’ 对应的帐号类型。该类型是指 Clan(战队) 即 Steam Group ID,就是由玩家创建的 “群组” 帐号。若自己就是群组的创建者或管理员,那该帐号的 ID 是可以直接取得的,方法就是启动并登录 Steam Client 客户端,点界面左上方菜单“社区”右侧自己的游戏帐户名会有下拉菜单,再点其中的 “群组 -> 编辑组资料” 会出现该组的详细信息,其中第一项 ID 便是。该 ID 的值事实上就是 W,将其代入格式 [g:1:W] 即可得到其 SteamID3 格式。但我这样讲与无凭无据信口开合没两样,因为官方从未这样讲过,那么举证只能靠自己了!所以,我从 Steam 社区官网找来了验证方法,门牌号在此:
How can you find your Steam Group 64 ID? [TUTORIAL]
按其中的方法将自己的群组名字代入,并粘贴到浏览器地址栏后即可得到群组的 groupID64,详见下图中箭头所指红框中内容:
该 groupID64 就是 SteamID64 格式的十进制形式。前面已介绍过,先将其转换为十六进制后,再取其最低 32 bits(8 个十六进制数字) 即为 W 的十六进制形式,再将 W 转换回十进制后与上面所查到的 ID 对比,假如以上操作过程无误的话,对比结果必然是一致的。所以,我程序输出的 Group ID 这个类型的 SteamID3 格式的 32-bit 形式([g:1:W])理论上也保真!之所以这样说的原因在于:若玩家将自己查到的 groupID64 转换为十六进制后,应当无一例外地发现其必然是以 0x01700000 开头,而这也是为什么官方文档在该类型帐号的 SteamID64 identifier 位置上给出 0x0170000000000000 的原因(否则完全可以将其留空避免造成误导),而这也是官方文档在随后的示例中给出“Example: http://steamcommunity.com/gid/[g:1:4]”这个 32-bit 形式其背后的真实原因,显然官方的用意就是令 Instance = 0。如此一来,Instance 取值介于 0x00001 ~ 0xFFFFF 这一区间的帐号相当于被弃用了(或者被保留下来作今后扩展用)。也正是这个原因,我程序在“解锁”模式下输出该帐号类型的 SteamID3 格式的 64-bit 形式 [g:1:W:Instance] 也不敢说保真 —— 而我这样讲纯粹是依据以上的分析与推理,也举不出任何直接证据。在有人能够举证推翻我的推论之前,还是先不必过多纠结这一点,继续来解说余下的帐号类型。
截至目前,我们已详细分析了 1 号宇宙中编号 1, 4, 7, 8 共四种帐号类型,虽然官方已注明编号第 0, 5, 9 三类不可用,但除第 9 类未给出对应的字母可直接不予理睬之外,尚余 0, 2, 3, 5, 6, 10。经过了上面对 SteamID3 格式的细致分析后,相信明眼人已经知道这余下几种类型的 SteamID3 格式应当如何构成了,我不和各位兜圈子,直接拉清单:
其中要解释一下类型 0,这一类型因为存在两个字母,而根据前面 CS2 服务器给出的信息得知大写 ‘I’ 对应 Instance = 0 这段,而小写 ‘i’ 则确实找不到直接的证据索性简单粗暴地如上图划分号段了。当然还有类型 10 也得交待下,官方定义它为匿名个人帐号类型,按理说应当与类型 1 一样将 Instance = 1 这一段定义给 32-bit 用,但我同样也找不到证据,姑且这样吧。此外,若大家细心则不难发现其中的共同特点:除类型 1 与 10(个人帐号)之外的其余类型,起始号段所采用的 SteamID3 格式均为其 32-bit 形式,而之所以如此也全是为了与类型 7 保持一致!
还有一点,官方文档中提到了一个唤作“Cyber Café Accounts”的帐号,据官方表述,其专用于“Steam 网吧”(一个貌似与 SteamVR 有关的计划),并且与用户帐号均在同一宇宙中获取 Steam 帐号,并且官方正致力于将其转移至其专属宇宙…… 除此外我实在找不到任何与之有关且有用的信息,所以也暂不予理会。
在解决了 SteamID3 格式之中力所能及能解决的困惑的同时保留仍未可知的困惑之后,若要实现三种格式在 0~5 已知各个宇宙中的相互无损转换,现在必须把前面埋下的伏笔给挖出来进一步讨论,即 RegularID 改造。咱们中医食疗法有“以形补形”的理论,就是俗称的“吃啥补啥”,既然 RegularID 缺少 Type 与 Instance 两个元素(以下分别简记作 “TN” 与 “IN”),我们不妨采用老祖宗的办法来帮它补齐,为确保兼容,具体我想到以下两种方案:
- STEAM_X:Y:Z:TN:IN ;
- STEAM_X:Y:Z:TN+IN ;
第 1 种在实际使用中在我个人看来有一个小小的缺憾,就在于 TN 的表示上,若采用字母则必然将出现 STEAM_1:0:999:A:9999 的形式,开头已有字母而随后再在数字中间夹杂字母不是我喜欢的风格,而若改用数字则我的心态会好很多,如:STEAM_1:0:999:10:9999,可是一个帐号中出现 4 个冒号在感观上或多或少都有些“累赘”。综合考虑后,最终我选择了方案 2。以玩家个人帐号 STEAM_1:0:234 为例,已知 TN+IN = 0x100001,将其转换为十进制可得 1048577,所以其补齐后的模样应当是:STEAM_1:0:234:1048577,若按方案 1 则应为:STEAM_1:0:234:1:1,虽然眼下这个情景后者更清爽简洁,但前者则会让人看得不明就里而莫名产生敬畏感,感觉这就蛮对 Valve 的味!与前面 SteamID3 格式类似,我就暂且称 STEAM_X:Y:Z 为 RegularID 的 32-bit 形式,而 STEAM_X:Y:Z:TN+IN 则为其 64-bit 形式。
至此,针对 SteamID 三种格式之间相互转换一事,若不纠结有无官方认证这事儿,则我们现在已经具备了充要条件,并且也细分了 RegularID 与 SteamID3 两种格式的 32-bit 和 64-bit 的两种表示形式。也正是基于此,我的程序区分“常态”与“解锁”两个模式,其默认为常态模式,即:默认 1 号宇宙(X = 1)并在最大限度内采用帐号格式的 32-bit 形式;而在“解锁”这一模式中,各类型的 RegularID 与 SteamID3 都将以 64-bit 形式出现,而类型 9 要问就是都不支持。所以,当见到下图中的输出结果时,虽然我也不敢说 100% 保真,但也请千万不要认为是程序出了 Bug。虽然这并非官方公布或经官方认证的,但的确是我目前能想到最合逻辑的,况且,即使今后官方给出标准答案与我的有出入我也完全不担心,既然能提到这点就表示显然已在我的考虑之中了,这也是我决定重写代码并将其标准化的用意之一,就等着官方说明来补全。
针对 SteamID 转换一事的分析和推理至此也告一段落,接下来就该聊下转换程序本身。 |