Skip to content

Commit

Permalink
compression ????!?!???
Browse files Browse the repository at this point in the history
  • Loading branch information
nothendev committed Sep 30, 2023
1 parent 422a65f commit efd54f5
Show file tree
Hide file tree
Showing 14 changed files with 404 additions and 22 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ resolver = "2"

[workspace.dependencies]
oxcr_protocol.path = "protocol"
oxcr_cli.path = "cli"
tokio = { version = "1", features = [ "macros", "full", "rt", "sync" ] }
derive_more.version = "0.99.17"
tracing.version = "0"
Expand All @@ -17,3 +18,4 @@ bytes = { version = "1", features = [ "serde" ] }
bevy = { version = "0.11.2", features = ["multi-threaded"], default-features = false }
rayon = "1"
itertools = "0"
flate2 = "1"
12 changes: 10 additions & 2 deletions cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub enum CliCommand {
Decode(ByteInput),
Help,
VarInt(ByteInput),
Decompress(ByteInput),
}

#[derive(Debug, Clone)]
Expand All @@ -46,10 +47,11 @@ pub enum Flag {
Help,
Decode(ByteInput),
VarInt(ByteInput),
Decompress(ByteInput),
}

#[derive(Debug)]
enum FlagName<'a> {
pub enum FlagName<'a> {
Short(&'a str),
Long(&'a str),
}
Expand Down Expand Up @@ -90,7 +92,7 @@ fn byte_input(input: &str) -> ByteInput {
}

#[parser(extras = Extra)]
fn flag_list(input: &str) -> Vec<FlagName<'a>> {
pub fn flag_list(input: &str) -> Vec<FlagName<'a>> {
if input.offset.saturating_sub(1) >= input.input.len() {
return Ok(vec![]);
}
Expand Down Expand Up @@ -134,6 +136,11 @@ fn flags(input: &str) -> Vec<Flag> {

Flag::Decode(byte_input(input)?)
}
FlagName::Short("c") | FlagName::Long("decompress") => {
one_of(" =")(input)?;

Flag::Decompress(byte_input(input)?)
}
FlagName::Short("V") | FlagName::Long("varint") => {
one_of(" =")(input)?;

Expand All @@ -160,6 +167,7 @@ fn flags_handle<'a>(
Flag::Help => cli.command = CliCommand::Help,
Flag::Decode(input) => cli.command = CliCommand::Decode(input),
Flag::VarInt(input) => cli.command = CliCommand::VarInt(input),
Flag::Decompress(input) => cli.command = CliCommand::Decompress(input),
}
}
try { flags(input)?.into_iter().for_each(|flag| handle(cli, flag)) }
Expand Down
18 changes: 17 additions & 1 deletion cli/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{ops::Range, str::FromStr};
use std::{borrow::Cow, num::ParseIntError, ops::Range, str::FromStr};

use oxcr_protocol::{
aott,
Expand All @@ -17,6 +17,8 @@ pub enum ParseError {
found: char,
#[label = "here"]
at: SourceSpan,
#[help]
help: Option<Cow<'static, str>>,
},
#[error("unexpected end of input{}", .expected.as_ref().map(|expectation| format!(", expected {expectation}")).unwrap_or_else(String::new))]
#[diagnostic(
Expand Down Expand Up @@ -44,6 +46,18 @@ pub enum ParseError {
#[label = "here"]
at: SourceSpan,
},
#[error("expected a number with radix {radix}, got {actual}")]
#[diagnostic(code(cli::expected_number))]
ExpectedNumber {
radix: u32,
actual: String,
#[label = "here"]
at: SourceSpan,
#[help]
help: Option<Cow<'static, str>>,
#[source]
error: ParseIntError,
},
}

#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -79,6 +93,7 @@ impl<'a> aott::error::Error<&'a str> for ParseError {
expected: Expectation::AnyOf(expected),
found: found.into_clone(),
at: span.into(),
help: None,
}
}

Expand All @@ -87,6 +102,7 @@ impl<'a> aott::error::Error<&'a str> for ParseError {
expected: Expectation::EndOfInput,
found: found.into_clone(),
at: span.into(),
help: None,
}
}
}
6 changes: 6 additions & 0 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#![feature(iterator_try_collect, try_blocks)]

mod cli;
pub use cli::{flag_list, Extra, FlagName};
mod error;
pub use error::*;
17 changes: 8 additions & 9 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mod cli;
mod error;

fn run(_path: String, args: &str) -> Result<(), Report> {
let tsub_guard = tracing_subscriber::fmt()
let tracing_set_default_level = tracing_subscriber::fmt()
.pretty()
.with_env_filter(EnvFilter::from_env("OXCR_LOG"))
.finish()
Expand All @@ -30,9 +30,9 @@ fn run(_path: String, args: &str) -> Result<(), Report> {
.parse(args)
.map_err(|parse_error| Report::new(parse_error).with_source_code(args.to_owned()))?;

drop(tsub_guard);
drop(tracing_set_default_level);

let _tsub_guard = tracing_subscriber::fmt()
let _tracing_set_real_level = tracing_subscriber::fmt()
.with_env_filter(cli.level.to_string())
.pretty()
.set_default();
Expand All @@ -43,17 +43,15 @@ fn run(_path: String, args: &str) -> Result<(), Report> {
CliCommand::Help => help(),
CliCommand::Decode(inp) => {
let bytes = read_byte_input(inp)?;
let spack = SerializedPacket {
length: LoginPlay::ID.length_of() + bytes.len(),
data: bytes,
id: LoginPlay::ID,
};
debug!("{bytes:?}");
let spack = SerializedPacket::deserialize.parse(&bytes)?;
let deserialized: LoginPlay = spack.try_deserialize(LoginPlay::STATE)?;

println!("{:#?}", deserialized);
}
CliCommand::VarInt(inp) => {
let bytes = read_byte_input(inp)?;

println!(
"{:#?}",
VarInt::<i64>::deserialize
Expand All @@ -62,7 +60,8 @@ fn run(_path: String, args: &str) -> Result<(), Report> {
.map_err(reconcile(bytes))?
);
}
}
CliCommand::Decompress(inp) => todo!(),
};

Ok(())
}
Expand Down
2 changes: 2 additions & 0 deletions protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ uuid = { version = "1.4.1", features = ["v3"] }
bitflags = "2.4.0"
miette = { version = "5.10.0", features = ["fancy"] }
indexmap = { version = "2.0.0", features = ["serde"] }

flate2.workspace = true
1 change: 1 addition & 0 deletions protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ impl PlayerNet {

return Err::<!, crate::error::Error>(e);
});

let recv_task = tokio::spawn(async move {
async {
let mut buf = BytesMut::new();
Expand Down
96 changes: 96 additions & 0 deletions protocol/src/model/packets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,102 @@ impl Serialize for SerializedPacket {
}
}

#[derive(Debug, Clone)]
pub struct SerializedPacketCompressed {
pub length: usize,
pub data_length: usize,
pub id: Compress<super::VarInt, Zlib>,
pub data: Compress<Bytes, Zlib>,
}

impl SerializedPacketCompressed {
pub fn new<P: Packet + Serialize>(packet: P) -> Result<Self, Error> {
Self::new_ref(&packet)
}

pub fn new_ref<P: Packet + Serialize + ?Sized>(packet: &P) -> Result<Self, Error> {
try {
let data = packet.serialize()?;
let id = P::ID;
let data_length = id.length_of() + data.len();
let datalength = VarInt::<i32>(data_length.try_into().unwrap());
let length = datalength.length_of() + Compress(datalength, Zlib).serialize()?.len();
Self {
length,
data_length,
id: Compress(id, Zlib),
data: Compress(data, Zlib),
}
}
}

pub fn try_deserialize<P: Packet + Deserialize<Context = PacketContext>>(
&self,
state: State,
) -> Result<P, Error> {
let context = PacketContext {
id: self.id.0,
state,
};

P::deserialize
.parse_with_context(self.data.0.as_ref(), context)
.map_err(|e| match e {
Error::Ser(error) => Error::SerSrc(WithSource {
source: BytesSource::new(
self.data.0.clone(),
Some(format!("packet_0x{:x}.bin", self.id.0 .0)),
),
span: error.span,
kind: error.kind,
}),
e => e,
})
}
}

impl Deserialize for SerializedPacketCompressed {
#[parser(extras = "Extra<Self::Context>")]
fn deserialize(input: &[u8]) -> Self {
try {
let packet_length_varint = VarInt::<i32>::deserialize(input)?;
assert!(packet_length_varint.0 >= 0);
let packet_length = packet_length_varint.0 as usize;
let data_length_varint = VarInt::<i32>::deserialize(input)?;
assert!(data_length_varint.0 >= 0);
let data_length = data_length_varint.0 as usize;
let id: Compress<VarInt<i32>, Zlib> = Compress::decompress(Bytes::copy_from_slice(
input.input.slice_from(input.offset..),
))?;
let data = Compress::decompress(Bytes::from(
take(data_length - id.0.length_of()).parse_with(input)?,
))?;
Self {
length: packet_length,
data_length,
id,
data,
}
}
}
}

impl Serialize for SerializedPacketCompressed {
fn serialize_to(&self, buf: &mut BytesMut) -> Result<(), Error> {
let length = VarInt::<i32>(self.length.try_into().map_err(|_| Error::VarIntTooBig)?);
length.serialize_to(buf)?;
let data_length = VarInt::<i32>(
self.data_length
.try_into()
.map_err(|_| Error::VarIntTooBig)?,
);
data_length.serialize_to(buf)?;
self.id.serialize_to(buf)?;
self.data.serialize_to(buf)?;
Ok(())
}
}

#[derive(Debug, Clone)]
pub struct PluginMessage {
pub channel: Identifier,
Expand Down
31 changes: 31 additions & 0 deletions protocol/src/model/packets/play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,34 @@ impl Packet for SetDefaultSpawnPosition {
const ID: crate::model::VarInt = VarInt(0x50);
const STATE: crate::model::State = State::Play;
}

/// This packet tells the client that it should enable support for the feature flags listed in the `feature_flags` field.
/// Note that adding arbitrary identifiers (instead of the constants under this struct,
/// like `FEATURE_VANILLA` or `FEATURE_BUNDLE`) to the list may cause the client to explode.
///
/// # Info
/// Packet ID: 0x6b
/// State: Play
/// Bound to: Client
///
/// # Layout
/// Total Features: VarInt ;; number of elements in the next array
/// Feature Flags: Array<Identifier>
#[derive(Debug, Clone)]
pub struct FeatureFlags {
pub feature_flags: Array<Identifier>,
}

impl Packet for FeatureFlags {
const ID: crate::model::VarInt = VarInt(0x6b);
const STATE: crate::model::State = State::Play;
}

impl_ser!(|PacketContext| FeatureFlags => [feature_flags]);

impl FeatureFlags {
/// A feature flag that enables all vanilla features in the notchian client.
pub const FEATURE_VANILLA: Identifier = Identifier::new_static(Namespace::Minecraft, "vanilla");
/// A feature flag that enables support for bundles in the notchian client.
pub const FEATURE_BUNDLE: Identifier = Identifier::new_static(Namespace::Minecraft, "bundle");
}
Loading

0 comments on commit efd54f5

Please sign in to comment.