Add protocol state enum generation

This commit is contained in:
Vladislavs Golubs 2021-01-23 18:40:28 +03:00
parent ccd8647113
commit 2849483562
5 changed files with 242 additions and 1 deletions

View File

@ -3,4 +3,5 @@
members = [ members = [
"protocol", "protocol",
"protocol-derive", "protocol-derive",
"protocol-generator"
] ]

View File

@ -7,7 +7,7 @@ description = "Derive macro for reading and writing Minecraft packets"
license = "MIT" license = "MIT"
homepage = "https://github.com/eihwaz/minecraft-protocol" homepage = "https://github.com/eihwaz/minecraft-protocol"
repository = "https://github.com/eihwaz/minecraft-protocol" repository = "https://github.com/eihwaz/minecraft-protocol"
keywords = ["minecraft", "protocol", "packet", "io"] keywords = ["minecraft", "derive", "protocol", "packet", "io"]
[lib] [lib]
proc-macro = true proc-macro = true

View File

@ -0,0 +1,16 @@
[package]
name = "minecraft-protocol-generator"
version = "0.0.0"
authors = ["vagola <vladislavs.golubs@yandex.ru>"]
edition = "2018"
description = "CLI for generating Rust code with Minecraft packets"
license = "MIT"
homepage = "https://github.com/eihwaz/minecraft-protocol"
repository = "https://github.com/eihwaz/minecraft-protocol"
keywords = ["minecraft", "cli", "protocol", "packet", "io"]
[dependencies]
clap = "2.33.3"
serde = "1.0.120"
serde_json = "1.0"
handlebars = "3.5.2"

View File

@ -0,0 +1,197 @@
use handlebars::{Handlebars, TemplateRenderError};
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::fmt;
use std::fmt::Display;
use std::fs::File;
use std::io::{BufWriter, Read};
enum State {
Handshake,
Status,
Login,
Game,
}
impl Display for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
State::Handshake => "Handshake",
State::Status => "Status",
State::Login => "Login",
State::Game => "Game",
};
write!(f, "{}", name)
}
}
enum Bound {
Server,
Client,
}
impl Display for Bound {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
Bound::Server => "Server",
Bound::Client => "Client",
};
write!(f, "{}", name)
}
}
#[derive(Serialize)]
struct Packet {
name: String,
fields: Vec<Field>,
}
impl Packet {
pub fn new(name: impl ToString, fields: Vec<Field>) -> Packet {
Packet {
name: name.to_string(),
fields,
}
}
}
#[derive(Serialize)]
struct Field {
name: String,
#[serde(rename(serialize = "type"))]
data_type: DataType,
}
impl Field {
pub fn new(name: impl ToString, data_type: DataType) -> Field {
Field {
name: name.to_string(),
data_type,
}
}
}
#[derive(Serialize)]
enum DataType {
Boolean,
Byte,
UnsignedByte,
Short,
UnsignedShort,
Int,
Long,
Float,
Double,
String,
Chat,
VarInt,
VarLong,
ByteArray,
}
struct Protocol {
state: State,
server_bound_packets: Vec<Packet>,
client_bound_packets: Vec<Packet>,
}
impl Protocol {
pub fn new(
state: State,
server_bound_packets: Vec<Packet>,
client_bound_packets: Vec<Packet>,
) -> Protocol {
Protocol {
state,
server_bound_packets,
client_bound_packets,
}
}
pub fn generate_rust_file(
&self,
template_engine: &Handlebars,
writer: &mut BufWriter<&File>,
) -> Result<(), TemplateRenderError> {
write_protocol_enum(
writer,
&template_engine,
&self.server_bound_packets,
&Bound::Server,
&self.state,
)?;
write_protocol_enum(
writer,
&template_engine,
&self.client_bound_packets,
&Bound::Client,
&self.state,
)?;
Ok(())
}
}
fn write_protocol_enum(
writer: &mut BufWriter<&File>,
template_engine: &Handlebars,
packets: &Vec<Packet>,
bound: &Bound,
state: &State,
) -> Result<(), TemplateRenderError> {
if !packets.is_empty() {
let enum_name = format!("{}{}BoundPacket", state, bound);
let data = json!({
"protocol_state_name": enum_name,
"packets": &packets
});
template_engine.render_to_write("protocol_state_enum", &data, writer)?;
}
Ok(())
}
pub fn main() {
let mut template_engine = Handlebars::new();
template_engine
.register_template_file(
"protocol_state_enum",
"protocol-generator/templates/protocol_state_enum.hbs",
)
.expect("Failed to register template");
let protocol = Protocol::new(
State::Login,
vec![
Packet::new("LoginStart", vec![Field::new("name", DataType::String)]),
Packet::new(
"EncryptionResponse",
vec![
Field::new("shared_secret", DataType::ByteArray),
Field::new("verify_token", DataType::ByteArray),
],
),
Packet::new("LoginPluginResponse", vec![]),
],
vec![
Packet::new("LoginDisconnect", vec![]),
Packet::new("EncryptionRequest", vec![]),
Packet::new("LoginSuccess", vec![]),
Packet::new("SetCompression", vec![]),
Packet::new("LoginPluginRequest", vec![]),
],
);
let file = File::create("login.rs").expect("Failed to create file");
let mut writer = BufWriter::new(&file);
protocol
.generate_rust_file(&template_engine, &mut writer)
.expect("Failed to generate rust file");
}

View File

@ -0,0 +1,27 @@
pub enum {{protocol_state_name}} {
{{~#each packets as |p|}}
{{p.name}}{{#unless @last}},{{/unless}}
{{~/each}}
}
impl {{protocol_state_name}} {
pub fn get_type_id(&self) -> u8 {
match self {
{{~#each packets as |p|}}
Self::{{p.name}} => {{@index}}{{#unless @last}},{{/unless}}
{{~/each}}
}
}
pub fn decode<R: Read>(type_id: u8, reader: &mut R) -> Result<Self, DecodeError> {
match type_id {
{{~#each packets as |p|}}
{{@index}} => {
Ok(Self::{{p.name}})
}
{{~/each}}
_ => Err(DecodeError::UnknownPacketType { type_id })
}
}
}