Add packet struct write
This commit is contained in:
parent
e7eace3ef5
commit
5a2b7e81fa
@ -3,4 +3,5 @@
|
|||||||
members = [
|
members = [
|
||||||
"protocol",
|
"protocol",
|
||||||
"protocol-derive",
|
"protocol-derive",
|
||||||
|
"protocol-generator",
|
||||||
]
|
]
|
17
protocol-generator/Cargo.toml
Normal file
17
protocol-generator/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "minecraft-protocol-generator"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["vagola <vladislavs.golubs@yandex.ru>"]
|
||||||
|
edition = "2018"
|
||||||
|
description = "Minecraft protocol generator"
|
||||||
|
license = "MIT"
|
||||||
|
homepage = "https://github.com/eihwaz/minecraft-protocol"
|
||||||
|
repository = "https://github.com/eihwaz/minecraft-protocol"
|
||||||
|
keywords = ["minecraft", "protocol", "packet", "io"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = "1.0.120"
|
||||||
|
serde_json = "1.0"
|
||||||
|
handlebars = "3.5.2"
|
||||||
|
heck = "0.3.2"
|
||||||
|
protodef-parser = "0.1.0"
|
12
protocol-generator/src/error.rs
Normal file
12
protocol-generator/src/error.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use handlebars::RenderError;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FrontendError {
|
||||||
|
TemplateRenderError { render_error: RenderError },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RenderError> for FrontendError {
|
||||||
|
fn from(render_error: RenderError) -> Self {
|
||||||
|
FrontendError::TemplateRenderError { render_error }
|
||||||
|
}
|
||||||
|
}
|
138
protocol-generator/src/frontend.rs
Normal file
138
protocol-generator/src/frontend.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use crate::error::FrontendError;
|
||||||
|
use handlebars::Handlebars;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Serialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum DataType {
|
||||||
|
#[serde(rename(serialize = "bool"))]
|
||||||
|
Boolean,
|
||||||
|
#[serde(rename(serialize = "i8"))]
|
||||||
|
Byte,
|
||||||
|
#[serde(rename(serialize = "u8"))]
|
||||||
|
UnsignedByte,
|
||||||
|
#[serde(rename(serialize = "i16"))]
|
||||||
|
Short,
|
||||||
|
#[serde(rename(serialize = "u16"))]
|
||||||
|
UnsignedShort,
|
||||||
|
#[serde(rename(serialize = "i32"))]
|
||||||
|
Int {
|
||||||
|
var_int: bool,
|
||||||
|
},
|
||||||
|
#[serde(rename(serialize = "u32"))]
|
||||||
|
UnsignedInt,
|
||||||
|
#[serde(rename(serialize = "i64"))]
|
||||||
|
Long {
|
||||||
|
var_long: bool,
|
||||||
|
},
|
||||||
|
#[serde(rename(serialize = "u64"))]
|
||||||
|
UnsignedLong,
|
||||||
|
#[serde(rename(serialize = "f32"))]
|
||||||
|
Float,
|
||||||
|
#[serde(rename(serialize = "f64"))]
|
||||||
|
Double,
|
||||||
|
String {
|
||||||
|
max_length: u16,
|
||||||
|
},
|
||||||
|
Uuid {
|
||||||
|
hyphenated: bool,
|
||||||
|
},
|
||||||
|
#[serde(rename(serialize = "Vec<u8>"))]
|
||||||
|
ByteArray {
|
||||||
|
rest: bool,
|
||||||
|
},
|
||||||
|
CompoundTag,
|
||||||
|
RefType {
|
||||||
|
ref_name: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Serialize)]
|
||||||
|
pub struct PacketStruct {
|
||||||
|
pub name: String,
|
||||||
|
pub fields: Vec<Field>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketStruct {
|
||||||
|
pub fn new(name: impl ToString, fields: Vec<Field>) -> PacketStruct {
|
||||||
|
PacketStruct {
|
||||||
|
name: name.to_string(),
|
||||||
|
fields,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Serialize)]
|
||||||
|
pub struct Field {
|
||||||
|
pub name: String,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub data_type: DataType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Field {
|
||||||
|
pub fn new(name: impl ToString, data_type: DataType) -> Field {
|
||||||
|
Field {
|
||||||
|
name: name.to_string(),
|
||||||
|
data_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_packet_struct<W: Write>(
|
||||||
|
template_engine: &Handlebars,
|
||||||
|
packet_struct: PacketStruct,
|
||||||
|
write: &mut W,
|
||||||
|
) -> Result<(), FrontendError> {
|
||||||
|
template_engine.render_to_write("packet_struct", &packet_struct, write)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::frontend::{write_packet_struct, DataType, Field, PacketStruct};
|
||||||
|
use crate::templates;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_packet_struct() {
|
||||||
|
let template_engine = templates::create_template_engine("templates");
|
||||||
|
|
||||||
|
let fields = vec![
|
||||||
|
Field::new("boolean", DataType::Boolean),
|
||||||
|
Field::new("byte", DataType::Byte),
|
||||||
|
Field::new("unsigned_byte", DataType::UnsignedByte),
|
||||||
|
Field::new("short", DataType::Short),
|
||||||
|
Field::new("unsigned_short", DataType::UnsignedShort),
|
||||||
|
Field::new("int", DataType::Int { var_int: false }),
|
||||||
|
Field::new("varint", DataType::Int { var_int: true }),
|
||||||
|
Field::new("unsigned_int", DataType::UnsignedInt),
|
||||||
|
Field::new("long", DataType::Long { var_long: false }),
|
||||||
|
Field::new("varlong", DataType::Long { var_long: true }),
|
||||||
|
Field::new("unsigned_long", DataType::UnsignedLong),
|
||||||
|
Field::new("float", DataType::Float),
|
||||||
|
Field::new("double", DataType::Double),
|
||||||
|
Field::new("string", DataType::String { max_length: 20 }),
|
||||||
|
Field::new("uuid", DataType::Uuid { hyphenated: false }),
|
||||||
|
Field::new("hyphenated", DataType::Uuid { hyphenated: true }),
|
||||||
|
Field::new("byte_array", DataType::ByteArray { rest: false }),
|
||||||
|
Field::new("rest", DataType::ByteArray { rest: true }),
|
||||||
|
Field::new("compound_tag", DataType::CompoundTag),
|
||||||
|
Field::new(
|
||||||
|
"ref",
|
||||||
|
DataType::RefType {
|
||||||
|
ref_name: "Chat".to_string(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let packet_struct = PacketStruct::new("TestPacket", fields);
|
||||||
|
let mut vec = vec![];
|
||||||
|
|
||||||
|
write_packet_struct(&template_engine, packet_struct, &mut vec)
|
||||||
|
.expect("Failed to write packet struct");
|
||||||
|
|
||||||
|
let result = String::from_utf8(vec).expect("Failed to convert vec to string");
|
||||||
|
|
||||||
|
assert_eq!(result, include_str!("../test/packet_struct.txt"));
|
||||||
|
}
|
||||||
|
}
|
5
protocol-generator/src/main.rs
Normal file
5
protocol-generator/src/main.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod error;
|
||||||
|
mod frontend;
|
||||||
|
mod templates;
|
||||||
|
|
||||||
|
pub fn main() {}
|
87
protocol-generator/src/templates.rs
Normal file
87
protocol-generator/src/templates.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use handlebars::{Context, Handlebars, Helper, Output, RenderContext, RenderError};
|
||||||
|
use heck::SnakeCase;
|
||||||
|
|
||||||
|
pub fn create_template_engine(templates_folder: &str) -> Handlebars<'static> {
|
||||||
|
let mut template_engine = Handlebars::new();
|
||||||
|
|
||||||
|
template_engine.register_helper("snake_case", Box::new(format_snake_case));
|
||||||
|
template_engine.register_helper("packet_id", Box::new(format_packet_id));
|
||||||
|
template_engine.register_helper(
|
||||||
|
"protocol_version_module",
|
||||||
|
Box::new(format_protocol_version_module),
|
||||||
|
);
|
||||||
|
template_engine.register_escape_fn(|s| s.to_owned());
|
||||||
|
|
||||||
|
register_template_file(&mut template_engine, templates_folder, "packet_struct");
|
||||||
|
|
||||||
|
template_engine
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_template_file(template_engine: &mut Handlebars, templates_folder: &str, name: &str) {
|
||||||
|
let tpl_path = format!("{}/{}.hbs", templates_folder, name);
|
||||||
|
|
||||||
|
template_engine
|
||||||
|
.register_template_file(name, tpl_path)
|
||||||
|
.expect("Failed to register template");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_snake_case(
|
||||||
|
h: &Helper,
|
||||||
|
_: &Handlebars,
|
||||||
|
_: &Context,
|
||||||
|
_: &mut RenderContext,
|
||||||
|
out: &mut dyn Output,
|
||||||
|
) -> Result<(), RenderError> {
|
||||||
|
let str = h
|
||||||
|
.param(0)
|
||||||
|
.and_then(|v| v.value().as_str())
|
||||||
|
.ok_or(RenderError::new(
|
||||||
|
"Param 0 with str type is required for snake case helper.",
|
||||||
|
))? as &str;
|
||||||
|
|
||||||
|
let snake_case_str = str.to_snake_case();
|
||||||
|
|
||||||
|
out.write(snake_case_str.as_ref())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_packet_id(
|
||||||
|
h: &Helper,
|
||||||
|
_: &Handlebars,
|
||||||
|
_: &Context,
|
||||||
|
_: &mut RenderContext,
|
||||||
|
out: &mut dyn Output,
|
||||||
|
) -> Result<(), RenderError> {
|
||||||
|
let id = h
|
||||||
|
.param(0)
|
||||||
|
.and_then(|v| v.value().as_u64())
|
||||||
|
.ok_or(RenderError::new(
|
||||||
|
"Param 0 with u64 type is required for packet id helper.",
|
||||||
|
))? as u64;
|
||||||
|
|
||||||
|
let packet_id_str = format!("{:#04X}", id);
|
||||||
|
|
||||||
|
out.write(packet_id_str.as_ref())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_protocol_version_module(
|
||||||
|
h: &Helper,
|
||||||
|
_: &Handlebars,
|
||||||
|
_: &Context,
|
||||||
|
_: &mut RenderContext,
|
||||||
|
out: &mut dyn Output,
|
||||||
|
) -> Result<(), RenderError> {
|
||||||
|
let version = h
|
||||||
|
.param(0)
|
||||||
|
.and_then(|v| v.value().as_str())
|
||||||
|
.ok_or(RenderError::new(
|
||||||
|
"Param 0 with str type is required for packet id helper.",
|
||||||
|
))? as &str;
|
||||||
|
|
||||||
|
let formatted_protocol_module_version =
|
||||||
|
format!("v_{}", version.replace(".", "_").replace("-", "_"));
|
||||||
|
|
||||||
|
out.write(formatted_protocol_module_version.as_ref())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
19
protocol-generator/templates/packet_struct.hbs
Normal file
19
protocol-generator/templates/packet_struct.hbs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
pub struct {{name}} {
|
||||||
|
{{~#each fields as |f|}}
|
||||||
|
{{~#if f.var_int}}
|
||||||
|
#[packet(with = "var_int")]{{/if}}
|
||||||
|
{{~#if f.var_long}}
|
||||||
|
#[packet(with = "var_long")]{{/if}}
|
||||||
|
{{~#if f.rest}}
|
||||||
|
#[packet(with = "rest")]{{/if}}
|
||||||
|
{{~#if f.hyphenated}}
|
||||||
|
#[packet(with = "uuid_hyp_str")]{{/if}}
|
||||||
|
{{~#if f.max_length}}
|
||||||
|
#[packet(max_length = {{f.max_length}})]{{/if}}
|
||||||
|
{{~#if (ne f.type "RefType")}}
|
||||||
|
pub {{f.name}}: {{f.type}},
|
||||||
|
{{~else}}
|
||||||
|
pub {{f.name}}: {{f.ref_name}},
|
||||||
|
{{~/if}}
|
||||||
|
{{~/each}}
|
||||||
|
}
|
27
protocol-generator/test/packet_struct.txt
Normal file
27
protocol-generator/test/packet_struct.txt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
pub struct TestPacket {
|
||||||
|
pub boolean: bool,
|
||||||
|
pub byte: i8,
|
||||||
|
pub unsigned_byte: u8,
|
||||||
|
pub short: i16,
|
||||||
|
pub unsigned_short: u16,
|
||||||
|
pub int: i32,
|
||||||
|
#[packet(with = "var_int")]
|
||||||
|
pub varint: i32,
|
||||||
|
pub unsigned_int: u32,
|
||||||
|
pub long: i64,
|
||||||
|
#[packet(with = "var_long")]
|
||||||
|
pub varlong: i64,
|
||||||
|
pub unsigned_long: u64,
|
||||||
|
pub float: f32,
|
||||||
|
pub double: f64,
|
||||||
|
#[packet(max_length = 20)]
|
||||||
|
pub string: String,
|
||||||
|
pub uuid: Uuid,
|
||||||
|
#[packet(with = "uuid_hyp_str")]
|
||||||
|
pub hyphenated: Uuid,
|
||||||
|
pub byte_array: Vec<u8>,
|
||||||
|
#[packet(with = "rest")]
|
||||||
|
pub rest: Vec<u8>,
|
||||||
|
pub compound_tag: CompoundTag,
|
||||||
|
pub ref: Chat,
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user