你好,

一个 TypeScript/JavaScript 开发者完全胜任这个工作。

我一开始要求的是拥有自托管 AI 模型的人,因为这个项目涉及分析一个非常大的反编译/压缩过的 JavaScript 代码库,但我真正需要的是以下两者之一:

  • 一个拥有强大编码 AI/模型的人,能够帮助分析大量代码,或者
  • 一个熟悉逆向工程、打包 JavaScript 以及重构游戏系统的 TypeScript/JavaScript 开发者。

以下是我具体要做的事情。

这是基于反编译源码对《九山登天》中实现的“试玩限制系统”的详细分析。

──────

1. 架构概述

试玩限制跨两个主要文件运作:

  1. _rolldown_dynamic_import_helper.js:15814000:包含核心边界定义、防篡改加密哈希检查以及地牢/矿洞生成过滤器。
  2. Game.js:包含 UI 门控、世界地图限制以及带有存档擦除陷阱的突破拦截器。

──────

2. 境界天花板(试玩停止的地方)

修行进展定义在一个有序数组 F 中:

// _rolldown_dynamic_import_helper.js (第 15814000 行)

var F = [
    "mundane",          // 索引 0 -> 可玩
    "bodyForging",      // 索引 1 -> 可玩
    "meridianOpening",  // 索引 2 -> 试玩上限 (Bus = 2)
    "qiCondensation",   // 索引 3 -> 锁定
    "coreFormation",    // 索引 4 -> 锁定
    "pillarCreation",   // 索引 5 -> 锁定
    "lifeFlourishing",  // 索引 6 -> 完整游戏引擎上限
    "worldShaping",     // 索引 7
    "innerGenesis",     // 索引 8
    "soulAscension",    // 索引 9
];

试玩常量设置为索引 2:

var Bus = 2; // 境界: "meridianOpening"

玩家可以自由游玩凡人境(索引 0)、锻体境(索引 1)和开脉境(索引 2)。

从凝气境(索引 3)开始的所有内容都被锁定。

──────

3. 防篡改校验和算法(Rus, zus, Bus, Vus)

为防止玩家简单地将 JavaScript 包中的 Bus = 2 修改为更高数值,开发者实现了一种防篡改校验和机制:

// _rolldown_dynamic_import_helper.js (第 15814120 行)

function Rus(e, t) {
    let n = 2166136261,
        r = t + String(e) + t.slice(0, 17);

    for (let e = 0; e < r.length; e++)
        ((n ^= r.charCodeAt(e)), (n = Math.imul(n, 16777619)));

    return ((n >>> 0) ^ (e * 73244475)).toString(36);
}

var zus = `k8Fj2mNx9pQw4vLa7cHd5sRt3bYe6gUi`,
    Bus = 2,
    Vus = `4l3via`,
    Hus = Rus(Bus, zus) !== Vus,
    Uus = Hus ? void 0 : F[Bus];

function Wus(e) {
    return Hus ? !0 : F.indexOf(e) > Bus;
}

function Gus(e) {
    return Rus(Bus, zus) === Vus ? e <= Bus : !1;
}

哈希算法的工作原理:

  1. 盐值:
    一个硬编码的 32 字符字母数字密钥:
    "k8Fj2mNx9pQw4vLa7cHd5sRt3bYe6gUi"

  2. 缓冲区生成:
    拼接:
    salt + String(Bus) + salt.slice(0, 17)

  3. FNV-1a 哈希:

  4. 从初始偏移基数 2166136261 开始。
  5. 遍历每个字符:
    (hash ^ charCode) * 16777619
    使用 Math.imul 进行 32 位整数乘法。

  6. 后置加扰:

  7. (hash >>> 0) 转换为无符号 32 位整数。
  8. Bus * 73244475 进行异或运算。
  9. 将结果格式化为 36 进制字母数字字符串。

  10. 期望摘要(Vus):
    对于 Bus = 2,期望哈希值为:
    "4l3via"

“毒丸”陷阱(Hus):

如果有人将:

Bus = 2

改为:

Bus = 6

而不更新 Vus
* Rus(6, zus) 计算得到 "-lhejni",与 "4l3via" 不匹配。
* Hus 变为 true

看看 Wus(e)

function Wus(e) {
    return Hus ? !0 : F.indexOf(e) > Bus;
}

因为 HustrueWus(e) 立即为每个境界返回 true,包括凡人境(境界 0)。

同时:
* Uus 变为 undefined
* Gus(e) 返回 false

结果:
整个游戏立即锁定。
游戏中的每一个区域都变成“试玩锁定”,所有矿洞房间都变成假的锁定节点,导致游戏从一开始就无法游玩。

──────

4. 游戏内强制点

限制从 _rolldown_dynamic_import_helper.js 导出,并在 Game.js 中以两个别名导入:

  • Uus 作为 du -> 导入为 km
    保存字符串 "meridianOpening"
  • Wus 作为 fu -> 导入为 Rce
    这是境界检查函数。

架构:

[Bus = 2, Vus = "4l3via"]
         |
    +----+----+
    |         |
    v         v
  Uus ("meridianOpening")   Wus(realm)
  导出为: [du]             导出为: [fu]
  导入为: km               导入为: Rce
    |         |
    |         |
    v         v
 组件 <sY>     世界地图渲染器
 突破/生命      if (Rce(location.realm))
    |         |
    v         v
  检查玩家境界  组件 <w6e>
  - 移除动作按钮
  - 显示 "Demo Locked"
  - 打开 Steam 商店

如果 player.realm 高于 "meridianOpening"
* 打开“试玩完成”弹窗。
* 关闭弹窗会通过 clearSave() 清除存档。

──────

门控 1:突破拦截与存档擦除(sY 组件)

组件 Game.js:3231987 挂载在突破界面、生命界面和世界地图上:

sY = ({ enabled: e = !0 }) => {
    let t = W((e) => e.player.player),
        n = X(),
        r = (0, Q.useContext)(qC),
        i = km !== void 0 && V.indexOf(t.realm) > V.indexOf(km) && e;

    return (
        (0, Q.useEffect)(() => {
            i && n(`ChangeTab`);
        }, [ i ]),
        i
            ? (0, $.jsx)(J, {
                id: `demo-complete`,
                showBackdrop: !0,
                width: `sm`,
                height: `50vh`,
                title: Z(`Demo Complete`),
                onClose: () => {
                    (r.clearSave(), n(`Confirm`));
                },
                children: (0, $.jsxs)(c, {
                    children: [
                        (0, $.jsx)(w, {
                            children: Z(`Thank You for Playing!`)
                        }),

                        (0, $.jsx)(w, {
                            children: Z(
                                `You have reached the end of the demo, but there is so much more to cultivate beyond this point. New realms, techniques, locations, recipes, characters, and stories await in the full game.`
                            ),
                        }),

                        (0, $.jsx)(w, {
                            children: Z(
                                `Your demo save will automatically transfer over, so you can pick up right where you left off.`
                            ),
                        }),

                        (0, $.jsxs)(B, {
                            onClick: () => {
                                window.open(
                                    `https://store.steampowered.com/app/3992260`,
                                    `_blank`
                                );
                            },
                            children: Z(`Wishlist / Buy on Steam`),
                        }),
                    ],
                }),
            })
            : null
    );
};

该组件的行为是:

  1. 检测
    检查玩家境界索引是否严格大于 km"meridianOpening")。

  2. 动作拦截
    如果玩家尝试突破到凝气境,<sY> 会触发 ChangeTab 来取消/拦截突破。

  3. 弹窗显示
    显示“试玩完成”界面,并附带指向 Steam 的链接。
    Steam 应用 ID:3992260

  4. 存档擦除陷阱
    如果玩家关闭弹窗,它会明确执行:
    javascript r.clearSave(); n("Confirm");
    因此存档被删除,游戏返回主菜单。

──────

门控 2:世界地图与地点锁定(Rce /

世界地图上的地点分配有境界要求:

// Game.js (第 10075240 行)

Xst = [
    { location: Vf,  realm: "mundane" },
    { location: ese, realm: "qiCondensation" },
    { location: dce, realm: "qiCondensation" },
    { location: Jhe, realm: "coreFormation" },
    { location: Ct,  realm: "pillarCreation" },
    // ...
];

当为任何地点 n 构建 UI 时:

// Game.js:4606408

if (n.realm && Rce(n.realm))
    return {
        mainButtons: [(0, $.jsx)(w6e, {}, `demo-lock`)],
        center: void 0,
        eventSide: [],
    };

Rce 就是 Wus

因此,如果 location.realm > "meridianOpening",那么:
* 所有交互按钮被移除。
* 进入宗门被移除。
* 探索被移除。
* 采集被移除。
* 战斗动作被移除。
* 中心视口被清空。
* 事件侧动作被清空。

它们完全被组件 Game.js:4547541 <w6e> 替代。

该组件显示:
标题:"Demo Locked"
描述:"Available in the full version of the game. Your demo save will automatically transfer over."
它还提供一个指向游戏商店页面的 Steam 商店按钮。

──────

门控 3:程序化矿洞与地牢清理($us / Kus)

在生成地下矿洞与深入地下城时:

// _rolldown_dynamic_import_helper.js:15815802

let c = Qus(t, n.y * 9),
    l = t.chambers[c.realm]?.[c.progress],
    u = F.indexOf(c.realm) > F.indexOf(Lus),
    d = Wus(c.realm) || !Gus(F.indexOf(c.realm));

如果该深度境界在试玩中被锁定:

(!l || l.length === 0 || u || d) && (l = [Kus]);

当地下深度到达对应于凝气境或更深的层时,d 评估为真。
房间列表 l 随后被替换为 [Kus]
Kus 是:

var Kus = {
    kind: "locked",
    name: "locked",
    icon: "",
    condition: "1",
    description: "",
    rarity: "mundane"
};

该深度的任何有效房间也会被清理并替换为空白假房间:

for (let e = 0; e < t.chambers.length; e++)
    if (F.indexOf(a.realm) > F.indexOf(Lus) || Wus(a.realm))
        t.chambers[e] = {
            x: t.chambers[e].x,
            y: t.chambers[e].y,
            chamber: {
                name: "locked",
                realm: a.realm,
                realmProgress: a.progress,
            },
        };

这意味着更深的程序化内容不仅仅是被界面隐藏。
每当相关境界超出试玩边界时,生成系统本身就会用锁定的假节点替换原始房间信息。

──────

试玩限制的总结清单

  1. 边界
    试玩硬上限为境界索引 2:meridianOpening

  2. 篡改检测
    存在一个基于定制 32 位 FNV-1a 哈希检查,使用盐值:"k8Fj2mNx9pQw4vLa7cHd5sRt3bYe6gUi"
    境界索引 2 的期望摘要为:"4l3via"

  3. 故障安全机制
    如果修改 Bus 而未同时修改完整性值,Hus 变为 true
    这会导致游戏将所有境界视为锁定,包括境界 0。

  4. 突破陷阱
    <sY> 组件监视玩家境界。
    如果高端高于 meridianOpening

  5. 游戏被拦截;
  6. 显示“试玩完成”画面;
  7. 关闭画面会调用 clearSave() 并删除玩家存档。

  8. 世界地图拦截
    任何需要境界高于索引 2 的地点,其正常动作按钮会被移除。
    该地点被替换为 <w6e>“试玩锁定”卡片。

  9. 地牢拦截
    生成在境界 2 之后的矿洞和地下城楼层,其有效房间、战利品、矿石和遭遇战会被替换为假 Kus/"locked" 节点。

──────

我需要什么帮助

这个项目是一个非常大的打包/压缩 JavaScript 应用程序。

我已经成功定位了几个重要的系统,但我需要帮助继续逆向工程和重构。

特别是,我想要理解和重构:

  • 境界/进展系统;
  • 突破机制;
  • 世界地图限制;
  • 地点要求;
  • 程序化矿洞生成;
  • 程序化地下城生成;
  • 存档处理;
  • UI 门控;
  • 角色进展;
  • 修炼系统;
  • 技巧;
  • 配方;
  • 地点;
  • 角色;
  • 故事/事件系统;
  • 压缩/导入函数之间的关系;
  • 最终将打包代码的相关部分转换为可理解、可维护的 JavaScript/TypeScript 模块。

所以我并不特别需要拥有自托管 AI 模型的人。

一个熟悉大型反编译/压缩应用程序的 TypeScript/JavaScript 开发者绝对有用。

AI 的想法主要是因为 AI 编码模型可以帮助处理并追踪如此大量的代码。

如果你感兴趣,请私信我。

以下是文件/项目:

[url]https://drive.google.com/file/d/1p6pnHaTcLyZLmHCU9Yp-sng_l49fieEY/view?usp=drive_link[/url]