Browse Source

pso_message macro

pull/3/head
jake 5 years ago
parent
commit
d2df4f6490
  1. 102
      psopacket/src/lib.rs
  2. 64
      src/lib.rs
  3. 45
      src/packet/messages.rs
  4. 1
      src/packet/mod.rs

102
psopacket/src/lib.rs

@ -404,6 +404,108 @@ pub fn pso_packet(attr: TokenStream, item: TokenStream) -> TokenStream {
q.into()
}
fn generate_psomessage_impl(msg_cmd: u8, name: syn::Ident, attrs: &Vec<AttrType>) -> proc_macro2::TokenStream {
let from_bytes = generate_from_bytes(&attrs);
let as_bytes = generate_as_bytes(&attrs);
quote! {
impl PSOMessage for #name {
const CMD: u8 = #msg_cmd;
fn from_bytes<R: std::io::Read + std::io::Seek >(mut cur: &mut R) -> Result<#name, PacketParseError> {
let mut buf1 = [0u8; 1];
cur.read(&mut buf1).unwrap();
let cmd = buf1[0];
cur.read(&mut buf1).unwrap();
let size = buf1[0];
let mut subbuf = vec![0u8; size as usize * 4 - 2];
let len = cur.read(&mut subbuf).unwrap();
if cmd != #msg_cmd {
return Err(PacketParseError::WrongPacketCommand);
}
if len != size as usize * 4 - 2 {
return Err(PacketParseError::WrongPacketSize(size as u16 * 4, len));
}
let mut cur = std::io::Cursor::new(subbuf);
let result = Ok(#name {
#(#from_bytes)*
});
result
}
fn as_bytes(&self) -> Vec<u8> {
let mut buf = Vec::new();
#(#as_bytes)*
while buf.len() % 4 != 2 {
buf.push(0);
}
let mut fullbuf = Vec::new();
fullbuf.push(#msg_cmd);
fullbuf.push((buf.len() as u8 + 2) / 4);
fullbuf.extend_from_slice(&mut buf);
fullbuf
}
}
}
}
#[proc_macro_attribute]
pub fn pso_message(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as syn::AttributeArgs);
let mut cmd = 0;
for a in args {
if let NestedMeta::Lit(lit) = a {
if let syn::Lit::Int(litint) = lit {
cmd = litint.base10_parse().unwrap();
}
}
}
let pkt_struct = parse_macro_input!(item as ItemStruct);
let mut attrs = match get_struct_fields(pkt_struct.fields.iter()) {
Ok(a) => a,
Err(err) => return err
};
// this is a lot of work to make a `u8` token, surely this can be easier?
let mut punctuated: syn::punctuated::Punctuated<syn::PathSegment, syn::Token![::]> = syn::punctuated::Punctuated::new();
punctuated.push_value(syn::PathSegment {
ident: syn::Ident::new("u8", proc_macro2::Span::call_site()),
arguments: syn::PathArguments::None,
});
let u8tpath = syn::TypePath {
qself: None,
path: syn::Path {
leading_colon: None,
segments: punctuated
}
};
attrs.insert(0, AttrType::Value(u8tpath.clone(), syn::Ident::new("target", proc_macro2::Span::call_site()), AttrMeta::None));
attrs.insert(0, AttrType::Value(u8tpath, syn::Ident::new("client", proc_macro2::Span::call_site()), AttrMeta::None));
let struct_def = generate_struct_def(pkt_struct.ident.clone(), &attrs);
let psopacket_impl = generate_psomessage_impl(cmd, pkt_struct.ident.clone(), &attrs);
let debug_impl = generate_debug_impl(pkt_struct.ident.clone(), &attrs);
let partialeq_impl = generate_partialeq_impl(pkt_struct.ident.clone(), &attrs);
let q = quote!{
#[derive(Clone)]
#struct_def
#psopacket_impl
#debug_impl
#partialeq_impl
};
q.into()
}
#[proc_macro_derive(PSOPacketData)]
pub fn pso_packet_data(input: TokenStream) -> TokenStream {
let derive = parse_macro_input!(input as DeriveInput);

64
src/lib.rs

@ -1,4 +1,6 @@
#![allow(incomplete_features)]
#![feature(const_generics)]
#![feature(seek_convenience)]
pub mod crypto;
pub mod packet;
@ -117,7 +119,8 @@ pub trait PSOPacket: std::fmt::Debug {
#[cfg(test)]
mod test {
use super::*;
use psopacket::{pso_packet, PSOPacketData};
use psopacket::{pso_packet, pso_message, PSOPacketData};
use crate::packet::messages::PSOMessage;
#[test]
fn test_basic_pso_packet() {
@ -462,4 +465,63 @@ mod test {
b: 456,
});
}
#[test]
fn test_pso_message() {
#[pso_message(0x23)]
struct Test {
a: u32,
b: f32,
}
let test = Test {
client: 1,
target: 2,
a: 123,
b: 4.56,
};
let mut bytes = test.as_bytes();
assert!(bytes == vec![35, 3, 1, 2, 123, 0, 0, 0, 133, 235, 145, 64]);
bytes[6] = 2;
let test2 = Test::from_bytes(&mut std::io::Cursor::new(bytes)).unwrap();
assert!(test2 == Test {
client: 1,
target: 2,
a: 131195,
b: 4.56,
});
}
#[test]
fn test_pso_message_non_4_byte_size() {
#[pso_message(0x23)]
struct Test {
a: u32,
b: f32,
c: u8,
}
let test = Test {
client: 1,
target: 2,
a: 123,
b: 4.56,
c: 5,
};
let mut bytes = test.as_bytes();
assert!(bytes == vec![35, 4, 1, 2, 123, 0, 0, 0, 133, 235, 145, 64, 5, 0, 0, 0]);
bytes[6] = 2;
let test2 = Test::from_bytes(&mut std::io::Cursor::new(bytes)).unwrap();
assert!(test2 == Test {
client: 1,
target: 2,
a: 131195,
b: 4.56,
c: 5,
});
}
}

45
src/packet/messages.rs

@ -0,0 +1,45 @@
use std::io::{Seek, SeekFrom};
use psopacket::pso_message;
use crate::{PSOPacketData, PacketParseError};
pub trait PSOMessage {
const CMD: u8;
fn from_bytes<R: std::io::Read + std::io::Seek>(cur: &mut R) -> Result<Self, PacketParseError> where Self: Sized;
fn as_bytes(&self) -> Vec<u8>;
}
#[pso_message(0x40)]
pub struct PlayerWalking {
x: f32,
y: f32,
z: f32,
}
pub enum Message {
PlayerWalking(PlayerWalking),
}
impl PSOPacketData for Message {
fn from_bytes<R: std::io::Read + std::io::Seek>(mut cur: &mut R) -> Result<Self, PacketParseError> {
let mut byte = [0u8; 1];
cur.read(&mut byte);
cur.seek(SeekFrom::Current(-1)); // Cursor doesn't implement Peek?
match byte[0] {
PlayerWalking::CMD => Ok(Message::PlayerWalking(PlayerWalking::from_bytes(&mut cur)?)),
_ => Err(PacketParseError::WrongPacketCommand),
}
}
fn as_bytes(&self) -> Vec<u8> {
Vec::new()
}
}

1
src/packet/mod.rs

@ -1,3 +1,4 @@
pub mod login;
pub mod patch;
pub mod ship;
pub mod messages;
Loading…
Cancel
Save