15个版本 (2个稳定版)
2.0.1 | 2024年4月7日 |
---|---|
2.0.0 | 2024年3月30日 |
2.0.0-alpha.4 | 2024年1月7日 |
2.0.0-alpha.2 | 2023年12月9日 |
1.0.0-alpha.3 | 2021年7月5日 |
#371 in 解析实现
102 每月下载量
215KB
6.5K SLoC
Peppi
Peppi是用于任天堂GameCube上的《超级 Smash Brothers Melee》的.slp游戏回放文件的Rust解析器。这些回放是由Jas Laferriere的Slippi录制代码生成的,该代码运行在Wii或Dolphin仿真器上。
⚠️ slp
工具已移动到peppi-slp包。
安装
在你的Cargo.toml
[dependencies]
peppi = "2.0"
用法
使用.slp
进行一次性的解析(对于.slpp
,请使用peppi::read
代替)
use std::{fs, io};
use peppi::io::slippi::read;
fn main() {
let mut r = io::BufReader::new(fs::File::open("tests/data/game.slp").unwrap());
let game = read(&mut r, None).unwrap();
println!("{:#?}", game);
}
更复杂的示例
use std::{fs, io};
use peppi::io::slippi::read;
use peppi::frame::Rollbacks;
// `ssbm-data` provides enums for characters, stages, action states, etc.
// You can just hard-code constants instead, if you prefer.
use ssbm_data::action_state::Common::{self, *};
/// Print the frames on which each player died.
fn main() {
let mut r = io::BufReader::new(fs::File::open("tests/data/game.slp").unwrap());
let game = read(&mut r, None).unwrap();
let mut is_dead = vec![false; game.frames.ports.len()];
let rollbacks = game.frames.rollbacks(Rollbacks::ExceptLast);
for frame_idx in 0..game.frames.len() {
if rollbacks[frame_idx] {
continue;
}
for (port_idx, port_data) in game.frames.ports.iter().enumerate() {
match port_data
.leader
.post
.state
.get(frame_idx)
.and_then(|s| Common::try_from(s).ok())
{
Some(DeadDown)
| Some(DeadLeft)
| Some(DeadRight)
| Some(DeadUp)
| Some(DeadUpStar)
| Some(DeadUpStarIce)
| Some(DeadUpFall)
| Some(DeadUpFallHitCamera)
| Some(DeadUpFallHitCameraFlat)
| Some(DeadUpFallIce)
| Some(DeadUpFallHitCameraIce) => {
if !is_dead[port_idx] {
is_dead[port_idx] = true;
println!(
"{} died on frame {}",
game.start.players[port_idx].port,
game.frames.id.get(frame_idx).unwrap(),
)
}
}
_ => is_dead[port_idx] = false,
}
}
}
}
实时解析
use std::fs;
use std::io::BufReader;
use byteorder::ReadBytesExt;
use peppi::io::slippi::de;
fn main() {
let mut r = BufReader::new(fs::File::open("tests/data/game.slp").unwrap());
// UBJSON wrapper (skip if using spectator protocol)
let size = de::parse_header(&mut r, None).unwrap() as usize;
// payload sizes & game start
let mut state = de::parse_start(&mut r, None).unwrap();
// loop until we hit GameEnd or run out of bytes
while de::parse_event(&mut r, &mut state, None).unwrap() != de::Event::GameEnd as u8
&& state.bytes_read() < size
{
println!(
"current frame number: {:?}",
state.frames().id.iter().last()
);
}
// `U` (0x55) means metadata next (skip if using spectator protocol)
if r.read_u8().unwrap() == 0x55 {
de::parse_metadata(&mut r, &mut state, None).unwrap();
}
}
开发
src/frame
中的Rust源文件是使用Clojure从frames.json
生成的,该文件描述了每个规范版本中存在的每个帧的字段。如果您修改了frames.json
或gen/src
中的生成器代码,请运行gen/scripts/frames
以重新生成这些Rust文件。
如果您正在添加对规范新版本的支持,您还需要更新 peppi::io::slippi::MAX_SUPPORTED_VERSION
。
目标
- 性能:Peppi旨在成为
.slp
文件最快的解析器。 - 易用性:与解析数据交互应该既简单又自然。
- 宽容度:在可行的情况下,对格式不正确的数据进行接受并警告。
- 跨语言支持:其他语言应该能够轻松高效地与Peppi交互。
- 往返测试:Peppi可以解析回放,并将其完全相同地写回。
- 替代格式:Peppi提供了一个替代格式,它比
.slp
更具可压缩性和易于处理。
Peppi格式
Peppi格式(.slpp
)是一个包含以下文件的GNU tar存档,顺序如下
peppi.json
:Peppi特定信息。metadata.json
:Slippi的元数据块。start.json
:游戏开始事件的JSON表示。start.raw
:原始二进制游戏开始事件。end.json
:游戏结束事件的JSON表示。end.raw
:原始二进制游戏结束事件。frames.arrow
:以箭头格式(见下文)表示的帧数据。
大部分数据在frames.arrow
中,这是一个包含所有游戏帧数据的Arrow IPC文件。这是一个列格式,使得.slpp
大约是.slp
的两倍可压缩。
要转换格式,请使用slp
CLI工具。
依赖项
~15–26MB
~475K SLoC