use crate::error::ErrorKind;
use crate::parser::{cstring, twint, uuid};
use nom::bytes::streaming::take;
use nom::multi::count;
use nom::Err::Failure;
use nom::IResult;
use serde::Serialize;
use uuid::Uuid;
fn player_input(input: &[u8]) -> IResult<&[u8], [i32; 10], ErrorKind> {
let (input, i0) = twint(input)?;
let (input, i1) = twint(input)?;
let (input, i2) = twint(input)?;
let (input, i3) = twint(input)?;
let (input, i4) = twint(input)?;
let (input, i5) = twint(input)?;
let (input, i6) = twint(input)?;
let (input, i7) = twint(input)?;
let (input, i8) = twint(input)?;
let (input, i9) = twint(input)?;
Ok((input, [i0, i1, i2, i3, i4, i5, i6, i7, i8, i9]))
}
#[derive(Debug, PartialEq, Serialize)]
pub struct PlayerDiff {
pub cid: i32,
pub dx: i32,
pub dy: i32,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct TickSkip {
pub dt: i32,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct PlayerNew {
pub cid: i32,
pub x: i32,
pub y: i32,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct PlayerOld {
pub cid: i32,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct InputDiff {
pub cid: i32,
pub dinput: [i32; 10],
}
#[derive(Debug, PartialEq, Serialize)]
pub struct InputNew {
pub cid: i32,
pub input: [i32; 10],
}
#[derive(Debug, PartialEq, Serialize)]
pub struct NetMessage<'a> {
pub cid: i32,
pub msg_size: i32,
pub msg: &'a [u8],
}
#[derive(Debug, PartialEq, Serialize)]
pub struct Join {
pub cid: i32,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct Drop<'a> {
pub cid: i32,
#[serde(serialize_with = "crate::ser::null_terminator")]
pub reason: &'a [u8],
}
#[derive(Debug, PartialEq, Serialize)]
pub struct ConsoleCommand<'a> {
pub cid: i32,
pub flags: i32,
#[serde(serialize_with = "crate::ser::null_terminator")]
pub cmd: &'a [u8],
pub num_args: i32,
#[serde(serialize_with = "crate::ser::inner_null_terminator")]
pub args: Vec<&'a [u8]>,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct UnknownEx<'a> {
#[serde(with = "uuid::serde::compact")]
pub uuid: Uuid,
pub size: i32,
#[serde(serialize_with = "crate::ser::null_terminator")]
pub data: &'a [u8],
}
#[derive(Debug, PartialEq, Serialize)]
pub struct DdnetVersionOld {
pub cid: i32,
pub version: i32,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct DdnetVersion<'a> {
pub cid: i32,
#[serde(with = "uuid::serde::compact")]
pub connection_id: Uuid,
pub version: i32,
#[serde(serialize_with = "crate::ser::null_terminator")]
pub version_str: &'a [u8],
}
#[derive(Debug, PartialEq, Serialize)]
pub struct TeamSave<'a> {
pub team: i32,
#[serde(with = "uuid::serde::compact")]
pub save_id: Uuid,
#[serde(serialize_with = "crate::ser::null_terminator")]
pub save: &'a [u8],
}
#[derive(Debug, PartialEq, Serialize)]
pub struct Team {
pub team: i32,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct Auth<'a> {
pub cid: i32,
pub level: i32,
#[serde(serialize_with = "crate::ser::null_terminator")]
pub auth_name: &'a [u8],
}
#[derive(Debug, PartialEq, Serialize)]
pub struct AuthLogout {
pub cid: i32,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct PlayerId {
pub cid: i32,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct PlayerTeam {
pub cid: i32,
pub team: i32,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct TeamPractice {
pub team: i32,
pub practice: i32,
}
#[derive(Debug, PartialEq, Serialize)]
pub struct PlayerSwap {
pub cid1: i32,
pub cid2: i32,
}
#[derive(Debug, PartialEq)]
pub enum Chunk<'a> {
PlayerDiff(PlayerDiff),
Eos,
TickSkip(TickSkip),
PlayerNew(PlayerNew),
PlayerOld(PlayerOld),
InputDiff(InputDiff),
InputNew(InputNew),
NetMessage(NetMessage<'a>),
Join(Join),
Drop(Drop<'a>),
ConsoleCommand(ConsoleCommand<'a>),
UnknownEx(UnknownEx<'a>),
Test,
DdnetVersionOld(DdnetVersionOld),
DdnetVersion(DdnetVersion<'a>),
AuthInit(Auth<'a>),
AuthLogin(Auth<'a>),
AuthLogout(AuthLogout),
JoinVer6(PlayerId),
JoinVer7(PlayerId),
RejoinVer6(PlayerId),
TeamSaveSuccess(TeamSave<'a>),
TeamSaveFailure(Team),
TeamLoadSuccess(TeamSave<'a>),
TeamLoadFailure(Team),
PlayerTeam(PlayerTeam),
TeamPractice(TeamPractice),
PlayerReady(PlayerId),
PlayerSwap(PlayerSwap),
}
impl<'a> Chunk<'a> {
pub fn cid(&self) -> Option<i32> {
match self {
Chunk::PlayerDiff(p) => Some(p.cid),
Chunk::Eos => None,
Chunk::TickSkip(_) => None,
Chunk::PlayerNew(p) => Some(p.cid),
Chunk::PlayerOld(p) => Some(p.cid),
Chunk::InputDiff(p) => Some(p.cid),
Chunk::InputNew(p) => Some(p.cid),
Chunk::NetMessage(p) => Some(p.cid),
Chunk::Join(p) => Some(p.cid),
Chunk::Drop(p) => Some(p.cid),
Chunk::ConsoleCommand(p) => Some(p.cid),
Chunk::UnknownEx(_) => None,
Chunk::Test => None,
Chunk::DdnetVersionOld(p) => Some(p.cid),
Chunk::DdnetVersion(p) => Some(p.cid),
Chunk::AuthInit(p) => Some(p.cid),
Chunk::AuthLogin(p) => Some(p.cid),
Chunk::AuthLogout(p) => Some(p.cid),
Chunk::JoinVer6(p) => Some(p.cid),
Chunk::JoinVer7(p) => Some(p.cid),
Chunk::RejoinVer6(p) => Some(p.cid),
Chunk::TeamSaveSuccess(_) => None,
Chunk::TeamSaveFailure(_) => None,
Chunk::TeamLoadSuccess(_) => None,
Chunk::TeamLoadFailure(_) => None,
Chunk::PlayerTeam(p) => Some(p.cid),
Chunk::TeamPractice(_) => None,
Chunk::PlayerReady(p) => Some(p.cid),
Chunk::PlayerSwap(_) => None,
}
}
}
fn chunk_player_diff(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, dx) = twint(input)?;
let (input, dy) = twint(input)?;
Ok((input, Chunk::PlayerDiff(PlayerDiff { cid, dx, dy })))
}
fn chunk_tick_skip(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, dt) = twint(input)?;
Ok((input, Chunk::TickSkip(TickSkip { dt })))
}
fn chunk_player_new(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, x) = twint(input)?;
let (input, y) = twint(input)?;
Ok((input, Chunk::PlayerNew(PlayerNew { cid, x, y })))
}
fn chunk_player_old(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
Ok((input, Chunk::PlayerOld(PlayerOld { cid })))
}
fn chunk_input_diff(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, dinput) = player_input(input)?;
Ok((input, Chunk::InputDiff(InputDiff { cid, dinput })))
}
fn chunk_input_new(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, player_input) = player_input(input)?;
Ok((
input,
Chunk::InputNew(InputNew {
cid,
input: player_input,
}),
))
}
fn chunk_net_message(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, msg_size) = twint(input)?;
if msg_size < 0 {
return Err(Failure(ErrorKind::NegativeBufLen(msg_size)));
}
let (input, msg) = take(msg_size as usize)(input)?;
Ok((input, Chunk::NetMessage(NetMessage { cid, msg_size, msg })))
}
fn chunk_join(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
Ok((input, Chunk::Join(Join { cid })))
}
fn chunk_drop(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, reason) = cstring(input)?;
Ok((input, Chunk::Drop(Drop { cid, reason })))
}
fn chunk_console_command(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, flags) = twint(input)?;
let (input, cmd) = cstring(input)?;
let (input, num_args) = twint(input)?;
if num_args < 0 {
return Err(Failure(ErrorKind::NegativeBufLen(num_args)));
}
let (input, args) = count(cstring, num_args as usize)(input)?;
Ok((
input,
Chunk::ConsoleCommand(ConsoleCommand {
cid,
flags,
cmd,
num_args,
args,
}),
))
}
pub const TH_TEST: Uuid = Uuid::from_u128(0x6bb8ba88_0f0b_382e_8dae_dbf4052b8b7d);
pub const TH_DDNETVER_OLD: Uuid = Uuid::from_u128(0x41b49541_f26f_325d_8715_9baf4b544ef9);
pub const TH_DDNETVER: Uuid = Uuid::from_u128(0x1397b63e_ee4e_3919_b86a_b058887fcaf5);
pub const TH_AUTH_INIT: Uuid = Uuid::from_u128(0x60daba5c_52c4_3aeb_b8ba_b2953fb55a17);
pub const TH_AUTH_LOGIN: Uuid = Uuid::from_u128(0x37ecd3b8_9218_3bb9_a71b_a935b86f6a81);
pub const TH_AUTH_LOGOUT: Uuid = Uuid::from_u128(0xd4f5abe8_edd2_3fb9_abd8_1c8bb84f4a63);
pub const TH_JOINVER6: Uuid = Uuid::from_u128(0x1899a382_71e3_36da_937d_c9de6bb95b1d);
pub const TH_JOINVER7: Uuid = Uuid::from_u128(0x59239b05_0540_318d_bea4_9aa1e80e7d2b);
pub const TH_REJOINVER6: Uuid = Uuid::from_u128(0xc1e921d5_96f5_37bb_8a45_7a06f163d27e);
pub const TH_SAVE_SUCCESS: Uuid = Uuid::from_u128(0x4560c756_da29_3036_81d4_90a50f0182cd);
pub const TH_SAVE_FAILURE: Uuid = Uuid::from_u128(0xb29901d5_1244_3bd0_bbde_23d04b1f7ba9);
pub const TH_LOAD_SUCCESS: Uuid = Uuid::from_u128(0xe05408d3_a313_33df_9eb3_ddb990ab954a);
pub const TH_LOAD_FAILURE: Uuid = Uuid::from_u128(0xef8905a2_c695_3591_a1cd_53d2015992dd);
pub const TH_PLAYER_TEAM: Uuid = Uuid::from_u128(0xa111c04e_1ea8_38e0_90b1_d7f993ca0da9);
pub const TH_TEAM_PRACTICE: Uuid = Uuid::from_u128(0x5792834e_81d1_34c9_a29b_b5ff25dac3bc);
pub const TH_PLAYER_READY: Uuid = Uuid::from_u128(0x638587c9_3f75_3887_918e_a3c2614ffaa0);
pub const TH_PLAYER_SWAP: Uuid = Uuid::from_u128(0x5de9b633_49cf_3e99_9a25_d4a78e9717d7);
fn extension_test(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
if !input.is_empty() {
Err(Failure(ErrorKind::TrailingBytes(
"EX_TEST",
input.len() as u32,
)))
} else {
Ok((input, Chunk::Test))
}
}
fn extension_ddnetver_old(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, version) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_DDNETVER_OLD",
input.len() as u32,
)))
} else {
Ok((
input,
Chunk::DdnetVersionOld(DdnetVersionOld { cid, version }),
))
}
}
fn extension_ddnetver(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, connection_id) = uuid(input)?;
let (input, version) = twint(input)?;
let (input, version_str) = cstring(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_DDNETVER",
input.len() as u32,
)))
} else {
Ok((
input,
Chunk::DdnetVersion(DdnetVersion {
cid,
connection_id,
version,
version_str,
}),
))
}
}
fn extension_auth_init(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, level) = twint(input)?;
let (input, auth_name) = cstring(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_AUTH_INIT",
input.len() as u32,
)))
} else {
Ok((
input,
Chunk::AuthInit(Auth {
cid,
level,
auth_name,
}),
))
}
}
fn extension_auth_login(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, level) = twint(input)?;
let (input, auth_name) = cstring(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_AUTH_INIT",
input.len() as u32,
)))
} else {
Ok((
input,
Chunk::AuthInit(Auth {
cid,
level,
auth_name,
}),
))
}
}
fn extension_auth_logout(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_AUTH_LOGOUT",
input.len() as u32,
)))
} else {
Ok((input, Chunk::AuthLogout(AuthLogout { cid })))
}
}
fn extension_join_ver6(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_JOINVER6",
input.len() as u32,
)))
} else {
Ok((input, Chunk::JoinVer6(PlayerId { cid })))
}
}
fn extension_join_ver7(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_JOINVER7",
input.len() as u32,
)))
} else {
Ok((input, Chunk::JoinVer7(PlayerId { cid })))
}
}
fn extension_rejoin_ver6(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_REJOINVER6",
input.len() as u32,
)))
} else {
Ok((input, Chunk::RejoinVer6(PlayerId { cid })))
}
}
fn extension_save_success(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, team) = twint(input)?;
let (input, save_id) = uuid(input)?;
let (input, save) = cstring(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_SAVE_SUCCESS",
input.len() as u32,
)))
} else {
Ok((
input,
Chunk::TeamSaveSuccess(TeamSave {
team,
save_id,
save,
}),
))
}
}
fn extension_save_failure(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, team) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_SAVE_FAILURE",
input.len() as u32,
)))
} else {
Ok((input, Chunk::TeamSaveFailure(Team { team })))
}
}
fn extension_load_success(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, team) = twint(input)?;
let (input, save_id) = uuid(input)?;
let (input, save) = cstring(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_LOAD_SUCCESS",
input.len() as u32,
)))
} else {
Ok((
input,
Chunk::TeamLoadSuccess(TeamSave {
team,
save_id,
save,
}),
))
}
}
fn extension_load_failure(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, team) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_LOAD_FAILURE",
input.len() as u32,
)))
} else {
Ok((input, Chunk::TeamLoadFailure(Team { team })))
}
}
fn extension_player_team(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
let (input, team) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_PLAYER_TEAM",
input.len() as u32,
)))
} else {
Ok((input, Chunk::PlayerTeam(PlayerTeam { cid, team })))
}
}
fn extension_team_practice(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, team) = twint(input)?;
let (input, practice) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_TEAM_PRACTICE",
input.len() as u32,
)))
} else {
Ok((input, Chunk::TeamPractice(TeamPractice { team, practice })))
}
}
fn extension_player_ready(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_PLAYER_READY",
input.len() as u32,
)))
} else {
Ok((input, Chunk::PlayerReady(PlayerId { cid })))
}
}
fn extension_player_swap(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, cid1) = twint(input)?;
let (input, cid2) = twint(input)?;
if input != b"" {
Err(Failure(ErrorKind::TrailingBytes(
"EX_PLAYER_SWAP",
input.len() as u32,
)))
} else {
Ok((input, Chunk::PlayerSwap(PlayerSwap { cid1, cid2 })))
}
}
fn chunk_unknown_ex(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (input, uuid) = uuid(input)?;
let (input, size) = twint(input)?;
if size < 0 {
return Err(Failure(ErrorKind::NegativeBufLen(size)));
}
let (input, data) = take(size as usize)(input)?;
let (_, c) = match uuid {
TH_TEST => extension_test(data)?,
TH_DDNETVER_OLD => extension_ddnetver_old(data)?,
TH_DDNETVER => extension_ddnetver(data)?,
TH_AUTH_INIT => extension_auth_init(data)?,
TH_AUTH_LOGIN => extension_auth_login(data)?,
TH_AUTH_LOGOUT => extension_auth_logout(data)?,
TH_JOINVER6 => extension_join_ver6(data)?,
TH_JOINVER7 => extension_join_ver7(data)?,
TH_REJOINVER6 => extension_rejoin_ver6(data)?,
TH_SAVE_SUCCESS => { extension_save_success(data) }?,
TH_SAVE_FAILURE => { extension_save_failure(data) }?,
TH_LOAD_SUCCESS => { extension_load_success(data) }?,
TH_LOAD_FAILURE => { extension_load_failure(data) }?,
TH_PLAYER_TEAM => { extension_player_team(data) }?,
TH_TEAM_PRACTICE => { extension_team_practice(data) }?,
TH_PLAYER_READY => { extension_player_ready(data) }?,
TH_PLAYER_SWAP => { extension_player_swap(data) }?,
_ => (input, Chunk::UnknownEx(UnknownEx { uuid, size, data })),
};
Ok((input, c))
}
pub fn chunk(input: &[u8]) -> IResult<&[u8], Chunk, ErrorKind> {
let (i, tag) = twint(input)?;
match tag {
0..=63 => chunk_player_diff(input), -1 => Ok((i, Chunk::Eos)),
-2 => chunk_tick_skip(i),
-3 => chunk_player_new(i),
-4 => chunk_player_old(i),
-5 => chunk_input_diff(i),
-6 => chunk_input_new(i),
-7 => chunk_net_message(i),
-8 => chunk_join(i),
-9 => chunk_drop(i),
-10 => chunk_console_command(i),
-11 => chunk_unknown_ex(i),
other => Err(Failure(ErrorKind::UnknownTag(other))),
}
}