pso_message macro
This commit is contained in:
		
							parent
							
								
									b692b8b417
								
							
						
					
					
						commit
						d2df4f6490
					
				| @ -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
									
									
									
									
									
								
							
							
						
						
									
										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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/packet/messages.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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,3 +1,4 @@ | ||||
| pub mod login; | ||||
| pub mod patch; | ||||
| pub mod ship; | ||||
| pub mod messages; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user