Crates.io | djin-protocol |
lib.rs | djin-protocol |
version | 3.3.0 |
source | src |
created_at | 2021-07-24 14:08:07.730701 |
updated_at | 2021-07-24 16:39:51.498943 |
description | A for of protocol, for aoe-djin |
homepage | |
repository | https://github.com/oknozor/protocol |
max_upload_size | |
id | 426783 |
size | 97,516 |
This is a fork of protocol whith some additional features needed for some other projects
Easy protocol definitions in Rust.
This crate adds a custom derive that can be added to types, allowing structured data to be sent and received from any IO stream.
Networking is built-in, with special support for TCP and UDP.
The protocol you define can be used outside of networking too - see the Parcel::from_raw_bytes
and Parcel::raw_bytes
methods.
This crate also provides:
Parcel
sCheckout the examples folder for usage.
Add this to your Cargo.toml
:
[dependencies]
protocol = "3.1"
protocol-derive = "3.1"
The most interesting part here is the djin_protocol::Parcel
trait. Any type that implements this trait can then be serialized to and from a byte stream. All primitive types, standard collections, tuples, and arrays implement this trait.
This crate becomes particularly useful when you define your own Parcel
types. You can use #[derive(Protocol)]
to do this. Note that in order for a type to implement Parcel
, it must also implement Clone
, Debug
, and PartialEq
.
#[derive(Parcel, Clone, Debug, PartialEq]
pub struct Health(f32);
#[derive(Parcel, Clone, Debug, PartialEq]
pub struct SetPlayerPosition {
pub position: (f32, f32),
pub health: Health,
pub values: Vec<String>,
}
Any user-defined type can have the Parcel
trait automatically derived.
#[macro_use] extern crate djin_protocol_derive;#[macro_use] extern crate djin_protocol;
#[derive(Protocol, Clone, Debug, PartialEq)]
pub struct Handshake;
#[derive(Protocol, Clone, Debug, PartialEq)]
pub struct Hello {
id: i64,
data: Vec<u8>,
}
#[derive(Protocol, Clone, Debug, PartialEq)]
pub struct Goodbye {
id: i64,
reason: String,
}
#[derive(Protocol, Clone, Debug, PartialEq)]
pub struct Node {
name: String,
enabled: bool
}
#[protocol(discriminant = "integer")]
#[derive(Protocol, Clone, Debug, PartialEq)]
pub enum PacketKind {
#[protocol(discriminator(0x00))]
Handshake(Handshake),
#[protocol(discriminator(0xaa))]
Hello(Hello),
#[protocol(discriminator(0xaf))]
Goodbye(Goodbye),
}
fn main() {
use std::net::TcpStream;
let stream = TcpStream::connect("127.0.0.1:34254").unwrap();
let mut connection = djin_protocol::wire::stream::Connection::new(stream, djin_protocol::wire::middleware::pipeline::default());
connection.send_packet(&Packet::Handshake(Handshake)).unwrap();
connection.send_packet(&Packet::Hello(Hello { id: 0, data: vec![ 55 ]})).unwrap();
connection.send_packet(&Packet::Goodbye(Goodbye { id: 0, reason: "leaving".to_string() })).unwrap();
loop {
if let Some(response) = connection.receive_packet().unwrap() {
println!("{:?}", response);
break;
}
}
}
Enum values can be transmitted either by their 1-based variant index, or by transmitting the string name of each variant.
NOTE: The default behaviour is to use the variant name as a string (string
).
This behaviour can be changed by the #[protocol(discriminant = "<type>")]
attribute.
Supported discriminant types:
string
(default)
integer
This transmits the 1-based variant number as the over-the-wire discriminant
If enum variants have explicit discriminators, the
Enum variants cannot be reordered in the source without breaking the protocol
#[derive(Protocol, Clone, Debug, PartialEq)]
#[protocol(discriminant = "string")]
pub enum PlayerState {
Stationary,
Flying { velocity: (f32,f32,f32) },
// Discriminators can be explicitly specified.
#[protocol(discriminator("ArbitraryOverTheWireName"))]
Jumping { height: f32 },
}
You can rename the variant for their serialisation.
#[derive(Protocol, Clone, Debug, PartialEq)]
#[protocol(discriminant = "string")]
pub enum Foo {
Bar,
#[protocol(name = "Biz")] // the Bing variant will be send/received as 'Biz'.
Bing,
Baz,
}