#arrow #polars #mpq #binary-format #generative-art #starcraft2 #sc2replay

bin+lib s2protocol

星际争霸II - 重放格式的解析器,导出到不同的目标格式

20 个稳定版本 (3 个主要版本)

3.1.1 2024年6月30日
3.0.4 2024年3月1日
3.0.3 2023年11月17日
2.0.0 2023年9月5日
0.1.1 2023年2月17日

#139解析器实现

Download history 174/week @ 2024-06-13 28/week @ 2024-06-20 169/week @ 2024-06-27 36/week @ 2024-07-04 122/week @ 2024-07-25 13/week @ 2024-08-01

每月下载量 135

MIT 和可能 LGPL-3.0-or-later

2MB
32K SLoC

Crates.io Workflow Status

s2protocol-rs

星际争霸2协议重放格式的nom解析器。提供迭代器通过游戏循环转换最小状态机,跟踪单位组、升级、目标等。

动机

目标是学习如何使用 nom 解析二进制文件格式,并学习星际争霸2重放文件为何如此小巧却能包含大量信息。

从可用的数据中,可以创建分析、可视化以及生成艺术,例如使用

消费事件

转换迭代器

这些是消费事件的不同的方式

  • SC2EventIterator 收集 TrackerEvents 和 GameEvents。事件按其出现顺序提供,无论是 Tracker 还是 Game
  • TrackerEventIterator 允许仅消费 Tracker 事件
  • GameEventIterator 允许仅消费 Game 事件

事件更改转换最小状态机,更新

  • 名称
  • 位置
  • 攻击事件
  • 等。

迭代器返回一个元组 (SC2EventType, UnitChangeHint) 第一个项目包含从 SC2Replay 反序列化的事件本身。第二个项目向迭代器的消费者暗示由于此事件而对状态机执行的改变。例如,单位可能已被删除、添加、更改位置等。

let source: PathBuf = PathBuf::new();
let res = s2protocol::state::SC2EventIterator::new(&source)?;
for (event, change_hint) in res.into_iter() {
    println!("{},", serde_json::to_string(&event)?);
}

与 polars 交互

生成 IPC Arrow 数据集

在目录ipcs/中,每个实现的数据类型都会创建一个.ipc文件。--source是包含重放目录(或单个文件)的目录。支持多个子目录。文件使用并行操作处理。对于17K重放(2.3GB),解析/转换/拆分它们需要120秒。实际情况可能不同,在这种情况下,只有10K个文件具有有效的初始化数据(即支持的协议版本)。

$ mkdir ipcs/
$ cargo run -r -- -v error --timing --source /home/seb/SCReplaysOnNVMe --output /home/seb/git/s2protocol-rs/ipcs/ write-arrow-ipc --process-max-files 10000000 all
Located 17021 matching files by extension
10299 files have valid init data, processing...
Total time: 121.943999981s
& du -sh ipcs
13G     ipcs

& ls -ltra ipcs
total 13123004
drwxr-xr-x  2 seb seb       4096 Jun 16 09:33 ./
drwxr-xr-x 11 seb seb       4096 Jun 16 22:09 ../
-rw-r--r--  1 seb seb   81070479 Jun 16 22:10 init_data.ipc
-rw-r--r--  1 seb seb   10789826 Jun 16 22:10 details.ipc
-rw-r--r--  1 seb seb  843902480 Jun 16 22:10 stats.ipc
-rw-r--r--  1 seb seb   67967152 Jun 16 22:10 upgrades.ipc
-rw-r--r--  1 seb seb 5712488337 Jun 16 22:10 unit_born.ipc
-rw-r--r--  1 seb seb 4652841877 Jun 16 22:11 unit_died.ipc
-rw-r--r--  1 seb seb 2068859942 Jun 16 22:12 cmd.ipc

Jupyter笔记本

有关如何与数据交互的示例的Jupyter笔记本可在s2-polars-data-analysis找到

polars-cli

$ cargo install polars-cli
$ # List the max number of minerals that were lost in per map when the army was killed.
❯ echo "SELECT ext_fs_replay_file_name, MAX(minerals_lost_army) FROM read_ipc('/home/seb/git/s2protocol-rs/ipcs/stats.ipc') GROUP BY ext_fs_replay_file_name ORDER BY minerals_lost_army DESC;"|polars
┌───────────────────────────────────┬────────────────────┐
│ ext_fs_replay_file_name           ┆ minerals_lost_army │
│ ---                               ┆ ---                │
│ str                               ┆ i32                │
╞═══════════════════════════════════╪════════════════════╡
│ Heavy Artillery LE (349).SC2Repl… ┆ 71362              │
│ Arctic Dream LE (398).SC2Replay   ┆ 59375              │
│ Nightscape LE (52).SC2Replay      ┆ 54846              │
│ …                                 ┆ …                  │
│ Emerald City LE (223).SC2Replay   ┆ 43450              │
│ Rhoskallian LE (101).SC2Replay    ┆ 41614              │
│ Fields of Death (345).SC2Replay   ┆ 41529              │
│ Rhoskallian LE (346).SC2Replay    ┆ 41425              │
└───────────────────────────────────┴────────────────────┘

状态

  • 协议75689的重放跟踪、游戏事件和聊天消息事件
  • 协议84643的重放跟踪、游戏事件和聊天消息事件
  • 协议87702的重放跟踪、游戏事件和聊天消息事件
  • 协议88500的重放跟踪、游戏事件和聊天消息事件
  • 协议89634的重放跟踪、游戏事件和聊天消息事件
  • 协议89720的重放跟踪、游戏事件和聊天消息事件
  • 协议90136的重放跟踪、游戏事件和聊天消息事件
  • 协议90779的重放跟踪、游戏事件和聊天消息事件
  • 协议90870的重放跟踪、游戏事件和聊天消息事件
  • 已解析单位移动。
  • 解码标签/回收已完成以匹配游戏事件。
  • 已解析游戏事件(尽管一些看似不相关的事件被跳过了)。
  • 读取版本并从版本调用正确的模块,以便我们可以支持多个模块。
  • 支持MPQ嵌入文件:replay.initData
  • 添加剩余的跟踪/游戏事件类型。
  • 支持MPQ嵌入文件:replay.gamemetadata.json
  • 支持MPQ嵌入文件:replay.attributes.events

版本兼容性。

经过一些测试后,似乎大多数类型在不同版本之间都是兼容的,因此只有在它们不同时才会成为协议版本的一部分。由于我是在协议87702上开始这项练习的,所以所有类型都将相对于它。也就是说,大多数模块将尽可能重用协议87702。这也解释了为什么像75689这样的旧版本仍然尽可能多地引用87702。

上面的生成器因此将显示创建的示例代码S2ProtoResult,以代替展开/恐慌。

生成特定协议的代码

使用以下方法生成了可用的协议版本的Rust代码:现在我们将将其与./src/versions/protocol99999.template文件进行比较,并从中我们可以分析有什么变化。值得注意的是,用于聊天消息的位数仍然错误地计算为3,因此需要忽略。

mkdir src/versions/protocol89720/
RUST_LOG_SPAN_EVENTS=full RUST_LOG=debug cargo watch -i src/versions/protocol89720/mod.rs -x 'run -- --source ../s2protocol/json/protocol89720.json generate --output src/versions/protocol89720/mod.rs'
# Add the new module to src/versions/mod.rs
# Run rust format on the new src/versions/protocol87702/mod.rs file
# cargo check, cargo build, etc
# Additionally some code to transform from Protocol-Specific to Protocol-Agnostic was added, TODO: Add to generator.rs

JSON规范来源

Blizzard/s2protocol仓库

依赖项

~19-32MB
~338K SLoC