• Important Notice: Please review our guidelines regarding cheating, game balancing, and mods for Helldivers 2. Read the full notice here before downloading or uploading mods.

HD2 Scanner(HD2 扫描器) v0.7a

A Mod for Helldivers 2
定位:

前置 / 核心服务 mod,本身不添加武器、皮肤或游戏内容。
它做三件事:

  1. 背景定位游戏内存里的 LDLD 数据表
  2. 通过 _G.HD2Scanner 把表基址广播给其他 mod
  3. 提供通用全量扫描 memscan,避免每个 mod 各自重复扫几 GB 内存
主要特性:

  • 广播的表:
    • MountComponentData
    • HellpodRackComponentData
    • HellpodPayloadComponentData
    • WeaponMagazineComponentData
    • TurretComponentData
    • ProjectileSettings
    • ExplosionSettings
  • memscan:
    • scan_request / scan_cancel / scan_status
    • 256 KB 分块 + 重叠
    • 8 MB/帧预算
    • 自身 pattern 跳过
    • 多请求排队
    • 只回传地址,消费者自己校验/写入
  • 只读,不写游戏数据表
  • 自检 11/11
  • 实机连续多轮 命中 7 稳定
依赖:

API接入用法
# HD2 Scanner API 接口文档
> 适用范围:HD2 Scanner v0.7.0+
> 入口:`_G.HD2Scanner`
---
# 中文
## 0. 概览
`HD2Scanner` 对外提供两类 API:
1. **数据表广播 API**:Scanner 后台定位 LDLD 数据表,把表基址广播给消费者。
2. **通用全量扫描 API (`memscan`)**:消费者提交字节 pattern,Scanner 分片扫描内存并回调命中地址。
消费者只读;结构校验、写入、回读都由消费者自己负责。
## 1. 获取接口
```lua
local S = rawget(_G, 'HD2Scanner')
if type(S) ~= 'table' or S.version ~= 1 then return end
```
`HD2Scanner.version` 当前为 `1`。
## 2. 数据表广播 API
### 2.1 `request(type_hash, name)`
订阅一张已知数据表。
```lua
local h = S.request(0x3845B1E0, 'MountComponentData')
```
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| `type_hash` | number | 数据表类型哈希 |
| `name` | string / nil | 可选,日志/诊断用名称 |
返回 handle:
| 字段/方法 | 类型 | 说明 |
|---|---|---|
| `hash` | number | 订阅的 type hash |
| `declare_need()` | function | 请求 Scanner 立刻插队扫描一轮 |
| `cancel()` | function | 取消订阅 |
| `declare_ok()` | function | 空实现,兼容保留 |
建议消费者加载后:
```lua
local h = S.request(RACK_TYPE, 'MountComponentData')
h.declare_need()
```
### 2.2 `poll(type_hash)`
读取广播快照:
```lua
local snap = S.poll(0x3845B1E0)
for _, e in ipairs(snap.entries) do
print(string.format('addr=0x%X size=%d watched=%s', e.addr, e.size, tostring(e.watched)))
end
print('generation=', snap.generation)
```
返回:
| 字段 | 说明 |
|---|---|
| `entries` | 表副本数组,每项 `{ addr, size, watched? }` |
| `generation` | 快照代号;表地址/副本数变化时递增 |
| `size` | Scanner 已知的期望表大小;不是运行时重新解析的值 |
未订阅/未命中时返回:
```lua
{ entries = {}, generation = 0 }
```
### 2.3 `watch(type_hash, addr)` / `unwatch(type_hash, addr)`
标记/取消标记某个广播条目:
```lua
S.watch(0x3845B1E0, addr)
S.unwatch(0x3845B1E0, addr)
```
当前版本中 `watched` 主要供诊断/UI 状态使用,不改变定位算法。
### 2.4 `status()`
返回扫描内核状态表。关键字段:
| 字段 | 说明 |
|---|---|
| `state` | `IDLE` / `PROBING` / `SETTLE` |
| `rounds` | 已完成扫描轮数 |
| `found` | 上一轮命中的表数量 |
| `regions` | 上一轮扫描的区段数 |
| `reads` | 上一轮 ReadProcessMemory 次数 |
| `ms` | 上一轮 CPU 耗时(毫秒) |
| `wall` | 上一轮墙上时间(毫秒) |
| `shard_frames` | 上一轮分片帧数 |
| `last_at` | 上次完成时间 |
| `interval` | 探针周期(秒) |
| `urgent` | 是否有 urgent 挂起 |
| `next_at` | 下次探针时间 |
| `lost` | 掉过的表副本数 |
### 2.5 `published()`
返回当前广播表:
```lua
local pub = S.published()
for hash, slot in pairs(pub) do
print(hash, slot.name, #slot.entries, slot.generation)
end
```
### 2.6 手动读内存 / 区域枚举
```lua
local bytes = S.read(addr, n)
local regions = S.regions_list()
```
或:
```lua
local bytes = S.api.read(addr, n)
local regions = S.api.regions()
```
## 3. 通用全量扫描 API (`memscan`)
### 3.1 `scan_request(req)`
```lua
local ok, why = S.scan_request{
id = 'exo_strat_pkg',
patterns = {
{ key = 'patriot', bytes = pkg_le_1 },
{ key = 'emancipator', bytes = pkg_le_2 },
},
budget = 8 * 1024 * 1024,
on_hit = function(key, addr) end,
on_done = function(hits) end,
}
```
字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `id` | string | 是 | 唯一请求 ID |
| `patterns` | table | 是 | 数组;每项 `{ key, bytes }` |
| `patterns.key` | any | 是 | 回调里回传的 key |
| `patterns.bytes` | string | 是 | 原始字节串,长度 1..64 |
| `budget` | number / nil | 否 | 每帧字节预算,默认 8 MB |
| `on_hit` | function / nil | 否 | `on_hit(key, addr)` |
| `on_done` | function / nil | 否 | `on_done(hits)` |
返回:
- `true`:请求已启动或已入队
- `false, reason`:参数非法 / 队列中已有同 ID 请求
### 3.2 回调语义
`on_hit(key, addr)`:
- 命中时立即回调;
- 同一 key 每个地址最多回调一次;
- 每个 key 最多保留 64 个地址;
- 回调运行在 Scanner 的 frame 内,不要在里面做重活或写内存。
`on_done(hits)`:
- 扫描完成时回调;
- `hits[key]` 是该 key 的地址数组;
- 如果请求被取消,则回调 `{ cancelled = true }`。
`hits` 结构:
```lua
{
patriot = { 0x12345678, 0x23456789, ... },
emancipator = { ... },
}
```
### 3.3 `scan_cancel(id)`
```lua
S.scan_cancel('exo_strat_pkg')
```
取消正在运行或排队中的请求。
### 3.4 `scan_status(id)`
```lua
local st = S.scan_status('exo_strat_pkg')
```
返回:
| 值 | 说明 |
|---|---|
| `idle` | 没有这个请求 |
| `running` | 正在扫描 |
| `queued` | 已排队 |
| `done` | 上一次请求已完成 |
### 3.5 扫描服务内部行为
- `VirtualQuery` 枚举可读 committed 区段
- 区段按大小降序
- 256 KB 分块,块间重叠,防止 pattern 跨块
- 默认 8 MB/帧
- 每个 pattern 自身地址 +/-4096 跳过,避免扫到自己
- 多请求排队,同一时间只跑一个全量扫描
- 命中只回传地址;结构校验/写入由消费者自己做
## 4. 已知数据表类型哈希
| 表 | type_hash |
|---|---:|
| `MountComponentData` | `0x3845B1E0` |
| `HellpodRackComponentData` | `0xA98BB156` |
| `HellpodPayloadComponentData` | `0xDDB5C03F` |
| `WeaponMagazineComponentData` | `0xFB8D88A3` |
| `TurretComponentData` | `0x1EBA7593` |
| `ProjectileSettings` | `0xBD4042C2` |
| `ExplosionSettings` | `0x2AEA2592` |
## 5. 最小消费者示例
```lua
local S = rawget(_G, 'HD2Scanner')
if type(S) ~= 'table' or S.version ~= 1 then return end
local RACK_TYPE = 0x3845B1E0
local h = S.request(RACK_TYPE, 'MountComponentData')
local function frame()
local snap = S.poll(RACK_TYPE)
if #snap.entries > 0 then
local magic = snap.entries[1].addr
local size = snap.entries[1].size
-- 消费者在这里自己做校验和写入
else
h.declare_need()
end
end
```
---
# English
## 0. Overview
`HD2Scanner` exposes two API families:
1. **Data table broadcast API**: the background kernel locates LDLD tables and broadcasts their addresses.
2. **Generic full-memory scan API (`memscan`)**: consumers submit byte patterns and receive hit addresses.
The consumer is responsible for validation, writes, and read-back verification.
## 1. Get the interface
```lua
local S = rawget(_G, 'HD2Scanner')
if type(S) ~= 'table' or S.version ~= 1 then return end
```
`HD2Scanner.version` is currently `1`.
## 2. Data table broadcast API
### 2.1 `request(type_hash, name)`
```lua
local h = S.request(0x3845B1E0, 'MountComponentData')
```
Returns a handle:
| Field/Method | Description |
|---|---|
| `hash` | subscribed type hash |
| `declare_need()` | ask Scanner to start an urgent scan now |
| `cancel()` | cancel the subscription |
| `declare_ok()` | no-op, compatibility |
Recommended:
```lua
local h = S.request(RACK_TYPE, 'MountComponentData')
h.declare_need()
```
### 2.2 `poll(type_hash)`
```lua
local snap = S.poll(0x3845B1E0)
for _, e in ipairs(snap.entries) do
-- e.addr, e.size, e.watched
end
```
Returns:
| Field | Description |
|---|---|
| `entries` | array of `{ addr, size, watched? }` |
| `generation` | snapshot generation; increments when entries change |
If not subscribed or not found:
```lua
{ entries = {}, generation = 0 }
```
### 2.3 `watch` / `unwatch`
```lua
S.watch(type_hash, addr)
S.unwatch(type_hash, addr)
```
Marks an entry as watched. Currently used for diagnostics/UI state.
### 2.4 `status()`
Returns the kernel state table. Key fields: `state`, `rounds`, `found`, `regions`, `reads`, `ms`, `wall`, `shard_frames`, `last_at`, `interval`, `urgent`, `next_at`, `lost`.
### 2.5 `published()`
Returns the published table map:
```lua
local pub = S.published()
for hash, slot in pairs(pub) do
-- slot.name, slot.entries, slot.generation
end
```
### 2.6 Raw memory and regions
```lua
local bytes = S.read(addr, n)
local regions = S.regions_list()
-- aliases
local bytes2 = S.api.read(addr, n)
local regions2 = S.api.regions()
```
## 3. Generic full-memory scan API (`memscan`)
### 3.1 `scan_request(req)`
```lua
local ok, why = S.scan_request{
id = 'exo_strat_pkg',
patterns = {
{ key = 'patriot', bytes = pkg_le_1 },
{ key = 'emancipator', bytes = pkg_le_2 },
},
budget = 8 * 1024 * 1024,
on_hit = function(key, addr) end,
on_done = function(hits) end,
}
```
Fields:
| Field | Type | Required | Description |
|---|---|---:|---|
| `id` | string | yes | unique request id |
| `patterns` | table | yes | array of `{ key, bytes }` |
| `patterns.key` | any | yes | passed back to callbacks |
| `patterns.bytes` | string | yes | raw byte string, length 1..64 |
| `budget` | number / nil | no | bytes per frame, default 8 MB |
| `on_hit` | function / nil | no | `on_hit(key, addr)` |
| `on_done` | function / nil | no | `on_done(hits)` |
Returns:
- `true`: started or queued
- `false, reason`: invalid request or duplicate queued id
### 3.2 Callback semantics
`on_hit(key, addr)`:
- called immediately on a hit
- each address is reported once per key
- up to 64 addresses per key
- runs inside Scanner frame; keep it short and do not write memory here
`on_done(hits)`:
- called when the scan finishes
- `hits[key]` is an array of addresses
- on cancel: `{ cancelled = true }`
`hits` shape:
```lua
{
patriot = { 0x12345678, ... },
emancipator = { ... },
}
```
### 3.3 `scan_cancel(id)`
Cancels a running or queued request.
### 3.4 `scan_status(id)`
Returns one of: `idle`, `running`, `queued`, `done`.
### 3.5 Internal behavior
- enumerates readable committed regions with `VirtualQuery`
- sorts regions by size descending
- 256 KB chunks with overlap
- 8 MB per frame by default
- skips each pattern's own copies within +/-4096
- requests are queued; one full scan at a time
- returns addresses only; the consumer validates and writes
## 4. Known type hashes
| Table | type_hash |
|---|---:|
| MountComponentData | `0x3845B1E0` |
| HellpodRackComponentData | `0xA98BB156` |
| HellpodPayloadComponentData | `0xDDB5C03F` |
| WeaponMagazineComponentData | `0xFB8D88A3` |
| TurretComponentData | `0x1EBA7593` |
| ProjectileSettings | `0xBD4042C2` |
| ExplosionSettings | `0x2AEA2592` |
## 5. Minimal consumer example
```lua
local S = rawget(_G, 'HD2Scanner')
if type(S) ~= 'table' or S.version ~= 1 then return end
local RACK_TYPE = 0x3845B1E0
local h = S.request(RACK_TYPE, 'MountComponentData')
local function frame()
local snap = S.poll(RACK_TYPE)
if #snap.entries > 0 then
local magic = snap.entries[1].addr
local size = snap.entries[1].size
-- validate and write here
else
h.declare_need()
end
end
```
26.3 KB
Log in or sign up to download
Uploaded by
君则0910
Downloads
75
Comments
9
Views
801
First release
Last update
Platform
PC 
Version
v0.7a
Total Size
26.3 KB
Rating
0.00 star(s) 0 ratings

Uploaded by

  • 75
  • 801

Ratings

0.00 star(s) 0 ratings

Tags

There are no tags available.

More mods from 君则0910

  • PC AC-8 75 发机炮背包(AC8 Rack Backpack)

  • PC TD-110 挂载切换(TD-110 Loadout)

  • PC Guard Dog Loadout(护卫犬挂载自选)

  • PC EXO 战备自选(ExoLoadout)

  • PC No Large Piercing

HvN

Members
Members
Oct 3, 2026
3
0
so what does this even do n what it's for??
 

HvN

Members
Members
Oct 3, 2026
3
0
still have zero idea what it's for but those other stuff looks cool
 

君则0910

Members
Members
Sep 21, 2026
9
4
Simply put: previously, every Lua mod ran its own scan, and sometimes you'd get several mods scanning over and over just to find a single table. This mod was made specifically for that problem — by hosting the scanning service on this table, it cuts down on duplicate scans and therefore reduces wasted performance.
still have zero idea what it's for but those other stuff looks cool
 

Phyr1te

Members
Members
May 15, 2026
1
0
Simply put: previously, every Lua mod ran its own scan, and sometimes you'd get several mods scanning over and over just to find a single table. This mod was made specifically for that problem — by hosting the scanning service on this table, it cuts down on duplicate scans and therefore reduces wasted performance.
oh nice, i was worried with how many lua script mods i have installed the problem would increase, thanks dud
 

HvN

Members
Members
Oct 3, 2026
3
0
Simply put: previously, every Lua mod ran its own scan, and sometimes you'd get several mods scanning over and over just to find a single table. This mod was made specifically for that problem — by hosting the scanning service on this table, it cuts down on duplicate scans and therefore reduces wasted performance.
oh sweet. so is this used in tandem or a replacement? if used together, what load order should i put this in
 

君则0910

Members
Members
Sep 21, 2026
9
4
Not a performance mod — it's a framework. Alone it does nothing; adapted mods get the benefit by dropping their duplicate scans, unadapted ones are unaffected. Still unstable and changing, I just built a new version.
Simply put: previously, every Lua mod ran its own scan, and sometimes you'd get several mods scanning over and over just to find a single table. This mod was made specifically for that problem — by hosting the scanning service on this table, it cuts down on duplicate scans and therefore reduces wasted performance.

oh sweet. so is this used in tandem or a replacement? if used together, what load order should i put this in
 

君则0910

Members
Members
Sep 21, 2026
9
4
To be precise, it's closer to a support-library mod than anything else, and it needs MDL or BSL as a prerequisite.
Not a performance mod — it's a framework. Alone it does nothing; adapted mods get the benefit by dropping their duplicate scans, unadapted ones are unaffected. Still unstable and changing, I just built a new version.
 

tiestofalljays

Members
Members
Sep 19, 2026
6
4
I'd love to know what is going at Arrowhead rn. The dev leads' inboxes/Teams/Slack/Jira/whatever notifications are probably blowing up.