Refactor
This commit is contained in:
parent
5245cdf16d
commit
ebd8f5ce22
@ -2,23 +2,23 @@ use linked_hash_map::LinkedHashMap;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Protocol {
|
pub struct ProtocolHandler {
|
||||||
pub handshaking: ProtocolState,
|
pub handshaking: Protocol,
|
||||||
pub status: ProtocolState,
|
pub status: Protocol,
|
||||||
pub login: ProtocolState,
|
pub login: Protocol,
|
||||||
#[serde(rename = "play")]
|
#[serde(rename = "play")]
|
||||||
pub game: ProtocolState,
|
pub game: Protocol,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ProtocolState {
|
pub struct Protocol {
|
||||||
pub to_client: ProtocolData,
|
pub to_client: Packets,
|
||||||
pub to_server: ProtocolData,
|
pub to_server: Packets,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct ProtocolData {
|
pub struct Packets {
|
||||||
pub types: LinkedHashMap<String, Vec<Data>>,
|
pub types: LinkedHashMap<String, Vec<Data>>,
|
||||||
}
|
}
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
pub mod input;
|
|
||||||
pub mod output;
|
|
@ -1,18 +1,19 @@
|
|||||||
mod data;
|
|
||||||
|
|
||||||
use crate::data::input;
|
|
||||||
use handlebars::*;
|
|
||||||
use heck::{CamelCase, SnakeCase};
|
|
||||||
|
|
||||||
use crate::data::input::{Container, Data, ProtocolData, ProtocolState};
|
|
||||||
use crate::data::output;
|
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::json;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
use crate::mappings::CodeMappings;
|
||||||
|
use crate::transformer::transform_protocol;
|
||||||
|
use handlebars::*;
|
||||||
|
use heck::SnakeCase;
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde_json::json;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
pub mod backend;
|
||||||
|
pub mod frontend;
|
||||||
|
pub mod mappings;
|
||||||
|
pub mod transformer;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
#[structopt(name = "protocol-generator")]
|
#[structopt(name = "protocol-generator")]
|
||||||
struct Opt {
|
struct Opt {
|
||||||
@ -32,25 +33,31 @@ pub fn main() {
|
|||||||
let protocol_data_file =
|
let protocol_data_file =
|
||||||
File::open(protocol_data_file_name).expect("Failed to open protocol data file");
|
File::open(protocol_data_file_name).expect("Failed to open protocol data file");
|
||||||
|
|
||||||
let protocol_input: input::Protocol =
|
let protocol_input: backend::ProtocolHandler =
|
||||||
serde_json::from_reader(protocol_data_file).expect("Failed to parse protocol data");
|
serde_json::from_reader(protocol_data_file).expect("Failed to parse protocol data");
|
||||||
|
|
||||||
|
let mappings = CodeMappings {};
|
||||||
|
|
||||||
let protocols = vec![
|
let protocols = vec![
|
||||||
(
|
(
|
||||||
transform_protocol_state(output::State::Handshake, &protocol_input.handshaking),
|
transform_protocol(
|
||||||
output::State::Handshake,
|
&mappings,
|
||||||
|
frontend::State::Handshake,
|
||||||
|
&protocol_input.handshaking,
|
||||||
|
),
|
||||||
|
frontend::State::Handshake,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
transform_protocol_state(output::State::Status, &protocol_input.status),
|
transform_protocol(&mappings, frontend::State::Status, &protocol_input.status),
|
||||||
output::State::Status,
|
frontend::State::Status,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
transform_protocol_state(output::State::Login, &protocol_input.login),
|
transform_protocol(&mappings, frontend::State::Login, &protocol_input.login),
|
||||||
output::State::Login,
|
frontend::State::Login,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
transform_protocol_state(output::State::Game, &protocol_input.game),
|
transform_protocol(&mappings, frontend::State::Game, &protocol_input.game),
|
||||||
output::State::Game,
|
frontend::State::Game,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -97,264 +104,24 @@ fn create_template_engine() -> Handlebars<'static> {
|
|||||||
template_engine
|
template_engine
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform_protocol_state(
|
|
||||||
state: output::State,
|
|
||||||
protocol_state: &ProtocolState,
|
|
||||||
) -> output::Protocol {
|
|
||||||
let server_bound_packets = transform_protocol_data(
|
|
||||||
protocol_state,
|
|
||||||
&protocol_state.to_server,
|
|
||||||
output::Bound::Server,
|
|
||||||
);
|
|
||||||
let client_bound_packets = transform_protocol_data(
|
|
||||||
protocol_state,
|
|
||||||
&protocol_state.to_client,
|
|
||||||
output::Bound::Client,
|
|
||||||
);
|
|
||||||
|
|
||||||
output::Protocol {
|
|
||||||
state,
|
|
||||||
server_bound_packets,
|
|
||||||
client_bound_packets,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transform_protocol_data(
|
|
||||||
protocol_state: &ProtocolState,
|
|
||||||
protocol_data: &ProtocolData,
|
|
||||||
bound: output::Bound,
|
|
||||||
) -> Vec<output::Packet> {
|
|
||||||
let packet_ids = get_packet_ids(protocol_data);
|
|
||||||
let mut packets = vec![];
|
|
||||||
|
|
||||||
for (unformatted_name, data_vec) in protocol_data.types.iter() {
|
|
||||||
if !unformatted_name.starts_with("packet_")
|
|
||||||
|| unformatted_name == "packet_legacy_server_list_ping"
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let no_prefix_unformatted = unformatted_name.trim_start_matches("packet_");
|
|
||||||
|
|
||||||
let id = *packet_ids
|
|
||||||
.get(no_prefix_unformatted)
|
|
||||||
.expect("Failed to get packet id");
|
|
||||||
|
|
||||||
let packet_name = rename_packet(
|
|
||||||
unformatted_name,
|
|
||||||
&no_prefix_unformatted.to_camel_case(),
|
|
||||||
&bound,
|
|
||||||
protocol_state,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut fields = vec![];
|
|
||||||
|
|
||||||
for data in data_vec {
|
|
||||||
if let Data::Container(container_vec) = data {
|
|
||||||
for container in container_vec {
|
|
||||||
match container {
|
|
||||||
Container::Value { name, data } => match transform_field(name, data) {
|
|
||||||
Some(field) => fields.push(modify_field(&packet_name, field)),
|
|
||||||
None => println!(
|
|
||||||
"[{}] Field \"{}\" are skipped ({:?}",
|
|
||||||
packet_name, name, data
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Container::List { name, data_vec } => {
|
|
||||||
if let Some(name) = name {
|
|
||||||
for data in data_vec {
|
|
||||||
match transform_field(name, data) {
|
|
||||||
Some(field) => {
|
|
||||||
fields.push(modify_field(&packet_name, field))
|
|
||||||
}
|
|
||||||
None => println!(
|
|
||||||
"[{}] Field \"{}\" are skipped ({:?})",
|
|
||||||
packet_name, name, data_vec
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let packet = output::Packet {
|
|
||||||
id,
|
|
||||||
name: packet_name,
|
|
||||||
fields,
|
|
||||||
};
|
|
||||||
|
|
||||||
packets.push(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
packets
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_packet_ids(protocol_data: &ProtocolData) -> HashMap<String, u8> {
|
|
||||||
let reversed_packet_ids = protocol_data
|
|
||||||
.types
|
|
||||||
.get("packet")
|
|
||||||
.and_then(|d| d.get(1))
|
|
||||||
.and_then(|d| match d {
|
|
||||||
Data::Container(data) => data.get(0),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.and_then(|c| match c {
|
|
||||||
Container::List { data_vec, .. } => data_vec.get(1),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.and_then(|d| match d {
|
|
||||||
Data::Mapper { mappings, .. } => Some(mappings),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.expect("Failed to get packet ids");
|
|
||||||
|
|
||||||
reversed_packet_ids
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| {
|
|
||||||
(
|
|
||||||
v.clone(),
|
|
||||||
u8::from_str_radix(k.trim_start_matches("0x"), 16).expect("Invalid packet id"),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transform_field(unformatted_field_name: &str, data: &Data) -> Option<output::Field> {
|
|
||||||
match data {
|
|
||||||
Data::Type(name) => match transform_data_type(name) {
|
|
||||||
Some(data_type) => Some(output::Field {
|
|
||||||
name: format_field_name(unformatted_field_name),
|
|
||||||
data_type,
|
|
||||||
}),
|
|
||||||
None => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transform_data_type(name: &str) -> Option<output::DataType> {
|
|
||||||
match name {
|
|
||||||
"bool" => Some(output::DataType::Boolean),
|
|
||||||
"i8" => Some(output::DataType::Byte),
|
|
||||||
"i16" => Some(output::DataType::Short),
|
|
||||||
"i32" => Some(output::DataType::Int { var_int: false }),
|
|
||||||
"i64" => Some(output::DataType::Long { var_long: false }),
|
|
||||||
"u8" => Some(output::DataType::UnsignedByte),
|
|
||||||
"u16" => Some(output::DataType::UnsignedShort),
|
|
||||||
"f32" => Some(output::DataType::Float),
|
|
||||||
"f64" => Some(output::DataType::Double),
|
|
||||||
"varint" => Some(output::DataType::Int { var_int: true }),
|
|
||||||
"varlong" => Some(output::DataType::Long { var_long: true }),
|
|
||||||
"string" => Some(output::DataType::String { max_length: 0 }),
|
|
||||||
"nbt" | "optionalNbt" => Some(output::DataType::CompoundTag),
|
|
||||||
"UUID" => Some(output::DataType::Uuid { hyphenated: false }),
|
|
||||||
"buffer" => Some(output::DataType::ByteArray { rest: false }),
|
|
||||||
"restBuffer" => Some(output::DataType::ByteArray { rest: true }),
|
|
||||||
"position" => Some(output::DataType::RefType {
|
|
||||||
ref_name: "Position".to_string(),
|
|
||||||
}),
|
|
||||||
"slot" => Some(output::DataType::RefType {
|
|
||||||
ref_name: "Option<Slot>".to_string(),
|
|
||||||
}),
|
|
||||||
"entityMetadata" => Some(output::DataType::RefType {
|
|
||||||
ref_name: "Metadata".to_string(),
|
|
||||||
}),
|
|
||||||
"tags" => Some(output::DataType::RefType {
|
|
||||||
ref_name: "TagsMap".to_string(),
|
|
||||||
}),
|
|
||||||
"option" => None,
|
|
||||||
_ => {
|
|
||||||
println!("Unknown data type \"{}\"", name);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_field_name(unformatted_field_name: &str) -> String {
|
|
||||||
if unformatted_field_name == "type" {
|
|
||||||
String::from("type_")
|
|
||||||
} else {
|
|
||||||
unformatted_field_name.to_snake_case()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rename_packet(
|
|
||||||
unformatted_name: &str,
|
|
||||||
name: &str,
|
|
||||||
bound: &output::Bound,
|
|
||||||
protocol_state: &ProtocolState,
|
|
||||||
) -> String {
|
|
||||||
let new_name = match (name, bound) {
|
|
||||||
("EncryptionBegin", output::Bound::Server) => "EncryptionResponse",
|
|
||||||
("EncryptionBegin", output::Bound::Client) => "EncryptionRequest",
|
|
||||||
("PingStart", output::Bound::Server) => "StatusRequest",
|
|
||||||
("Ping", output::Bound::Server) => "PingRequest",
|
|
||||||
("ServerInfo", output::Bound::Client) => "StatusResponse",
|
|
||||||
("Ping", output::Bound::Client) => "PingResponse",
|
|
||||||
("Login", output::Bound::Client) => "JoinGame",
|
|
||||||
_ => name,
|
|
||||||
}
|
|
||||||
.to_owned();
|
|
||||||
|
|
||||||
if new_name == name
|
|
||||||
&& protocol_state
|
|
||||||
.to_client
|
|
||||||
.types
|
|
||||||
.contains_key(unformatted_name)
|
|
||||||
&& protocol_state
|
|
||||||
.to_server
|
|
||||||
.types
|
|
||||||
.contains_key(unformatted_name)
|
|
||||||
{
|
|
||||||
bidirectional(&new_name, bound)
|
|
||||||
} else {
|
|
||||||
new_name.to_owned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bidirectional(name: &str, bound: &output::Bound) -> String {
|
|
||||||
match bound {
|
|
||||||
output::Bound::Server => format!("ServerBound{}", name),
|
|
||||||
output::Bound::Client => format!("ClientBound{}", name),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn modify_field(packet_name: &str, field: output::Field) -> output::Field {
|
|
||||||
match (packet_name, field.name.as_str()) {
|
|
||||||
("StatusResponse", "response") => field.change_type(output::DataType::RefType {
|
|
||||||
ref_name: "ServerStatus".to_owned(),
|
|
||||||
}),
|
|
||||||
("Success", "uuid") => field.change_type(output::DataType::Uuid { hyphenated: true }),
|
|
||||||
("Disconnect", "reason") => field.change_type(output::DataType::Chat),
|
|
||||||
("ClientBoundChat", "message") => field.change_type(output::DataType::Chat),
|
|
||||||
("ClientBoundChat", "position") => field.change_type(output::DataType::RefType {
|
|
||||||
ref_name: "MessagePosition".to_owned(),
|
|
||||||
}),
|
|
||||||
_ => field,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct GenerateContext<'a> {
|
struct GenerateContext<'a> {
|
||||||
packet_enum_name: String,
|
packet_enum_name: String,
|
||||||
packets: &'a Vec<output::Packet>,
|
packets: &'a Vec<frontend::Packet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_rust_file<W: Write>(
|
fn generate_rust_file<W: Write>(
|
||||||
protocol: &output::Protocol,
|
protocol: &frontend::Protocol,
|
||||||
template_engine: &Handlebars,
|
template_engine: &Handlebars,
|
||||||
mut writer: W,
|
mut writer: W,
|
||||||
) -> Result<(), TemplateRenderError> {
|
) -> Result<(), TemplateRenderError> {
|
||||||
let server_bound_ctx = GenerateContext {
|
let server_bound_ctx = GenerateContext {
|
||||||
packet_enum_name: format!("{}{}BoundPacket", &protocol.state, output::Bound::Server),
|
packet_enum_name: format!("{}{}BoundPacket", &protocol.state, frontend::Bound::Server),
|
||||||
packets: &protocol.server_bound_packets,
|
packets: &protocol.server_bound_packets,
|
||||||
};
|
};
|
||||||
|
|
||||||
let client_bound_ctx = GenerateContext {
|
let client_bound_ctx = GenerateContext {
|
||||||
packet_enum_name: format!("{}{}BoundPacket", &protocol.state, output::Bound::Client),
|
packet_enum_name: format!("{}{}BoundPacket", &protocol.state, frontend::Bound::Client),
|
||||||
packets: &protocol.client_bound_packets,
|
packets: &protocol.client_bound_packets,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
65
protocol-generator/src/mappings.rs
Normal file
65
protocol-generator/src/mappings.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
use crate::backend;
|
||||||
|
use crate::frontend;
|
||||||
|
|
||||||
|
pub trait Mappings {
|
||||||
|
fn rename_packet(
|
||||||
|
&self,
|
||||||
|
unformatted_name: &str,
|
||||||
|
name: &str,
|
||||||
|
bound: &frontend::Bound,
|
||||||
|
protocol: &backend::Protocol,
|
||||||
|
) -> String;
|
||||||
|
|
||||||
|
fn change_field_type(&self, packet_name: &str, field: frontend::Field) -> frontend::Field;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CodeMappings {}
|
||||||
|
|
||||||
|
impl Mappings for CodeMappings {
|
||||||
|
fn rename_packet(
|
||||||
|
&self,
|
||||||
|
unformatted_name: &str,
|
||||||
|
name: &str,
|
||||||
|
bound: &frontend::Bound,
|
||||||
|
protocol: &backend::Protocol,
|
||||||
|
) -> String {
|
||||||
|
let new_name = match (name, bound) {
|
||||||
|
("EncryptionBegin", frontend::Bound::Server) => "EncryptionResponse",
|
||||||
|
("EncryptionBegin", frontend::Bound::Client) => "EncryptionRequest",
|
||||||
|
("PingStart", frontend::Bound::Server) => "StatusRequest",
|
||||||
|
("Ping", frontend::Bound::Server) => "PingRequest",
|
||||||
|
("ServerInfo", frontend::Bound::Client) => "StatusResponse",
|
||||||
|
("Ping", frontend::Bound::Client) => "PingResponse",
|
||||||
|
("Login", frontend::Bound::Client) => "JoinGame",
|
||||||
|
_ => name,
|
||||||
|
}
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
if new_name == name
|
||||||
|
&& protocol.to_client.types.contains_key(unformatted_name)
|
||||||
|
&& protocol.to_server.types.contains_key(unformatted_name)
|
||||||
|
{
|
||||||
|
match bound {
|
||||||
|
frontend::Bound::Server => format!("ServerBound{}", name),
|
||||||
|
frontend::Bound::Client => format!("ClientBound{}", name),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
new_name.to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_field_type(&self, packet_name: &str, field: frontend::Field) -> frontend::Field {
|
||||||
|
match (packet_name, field.name.as_str()) {
|
||||||
|
("StatusResponse", "response") => field.change_type(frontend::DataType::RefType {
|
||||||
|
ref_name: "ServerStatus".to_owned(),
|
||||||
|
}),
|
||||||
|
("Success", "uuid") => field.change_type(frontend::DataType::Uuid { hyphenated: true }),
|
||||||
|
("Disconnect", "reason") => field.change_type(frontend::DataType::Chat),
|
||||||
|
("ClientBoundChat", "message") => field.change_type(frontend::DataType::Chat),
|
||||||
|
("ClientBoundChat", "position") => field.change_type(frontend::DataType::RefType {
|
||||||
|
ref_name: "MessagePosition".to_owned(),
|
||||||
|
}),
|
||||||
|
_ => field,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
196
protocol-generator/src/transformer.rs
Normal file
196
protocol-generator/src/transformer.rs
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
use crate::mappings::Mappings;
|
||||||
|
use crate::{backend, frontend};
|
||||||
|
use heck::{CamelCase, SnakeCase};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub fn transform_protocol<M: Mappings>(
|
||||||
|
mappings: &M,
|
||||||
|
state: frontend::State,
|
||||||
|
protocol: &backend::Protocol,
|
||||||
|
) -> frontend::Protocol {
|
||||||
|
let server_bound_packets = transform_packets(
|
||||||
|
mappings,
|
||||||
|
protocol,
|
||||||
|
&protocol.to_server,
|
||||||
|
frontend::Bound::Server,
|
||||||
|
);
|
||||||
|
|
||||||
|
let client_bound_packets = transform_packets(
|
||||||
|
mappings,
|
||||||
|
protocol,
|
||||||
|
&protocol.to_client,
|
||||||
|
frontend::Bound::Client,
|
||||||
|
);
|
||||||
|
|
||||||
|
frontend::Protocol {
|
||||||
|
state,
|
||||||
|
server_bound_packets,
|
||||||
|
client_bound_packets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transform_packets<M: Mappings>(
|
||||||
|
mappings: &M,
|
||||||
|
protocol: &backend::Protocol,
|
||||||
|
packets: &backend::Packets,
|
||||||
|
bound: frontend::Bound,
|
||||||
|
) -> Vec<frontend::Packet> {
|
||||||
|
let packet_ids = get_packet_ids(packets);
|
||||||
|
let mut output_packets = vec![];
|
||||||
|
|
||||||
|
for (unformatted_name, data_vec) in packets.types.iter() {
|
||||||
|
if !unformatted_name.starts_with("packet_")
|
||||||
|
|| unformatted_name == "packet_legacy_server_list_ping"
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let no_prefix_unformatted = unformatted_name.trim_start_matches("packet_");
|
||||||
|
|
||||||
|
let id = *packet_ids
|
||||||
|
.get(no_prefix_unformatted)
|
||||||
|
.expect("Failed to get packet id");
|
||||||
|
|
||||||
|
let packet_name = mappings.rename_packet(
|
||||||
|
unformatted_name,
|
||||||
|
&no_prefix_unformatted.to_camel_case(),
|
||||||
|
&bound,
|
||||||
|
protocol,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut fields = vec![];
|
||||||
|
|
||||||
|
for data in data_vec {
|
||||||
|
if let backend::Data::Container(container_vec) = data {
|
||||||
|
for container in container_vec {
|
||||||
|
match container {
|
||||||
|
backend::Container::Value { name, data } => {
|
||||||
|
match transform_field(&name, &data) {
|
||||||
|
Some(field) => {
|
||||||
|
fields.push(mappings.change_field_type(&packet_name, field))
|
||||||
|
}
|
||||||
|
None => println!(
|
||||||
|
"[{}] Field \"{}\" are skipped ({:?}",
|
||||||
|
packet_name, name, data
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
backend::Container::List { name, data_vec } => {
|
||||||
|
if let Some(name) = name {
|
||||||
|
for data in data_vec {
|
||||||
|
match transform_field(&name, &data) {
|
||||||
|
Some(field) => fields
|
||||||
|
.push(mappings.change_field_type(&packet_name, field)),
|
||||||
|
None => println!(
|
||||||
|
"[{}] Field \"{}\" are skipped ({:?})",
|
||||||
|
packet_name, name, data_vec
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let packet = frontend::Packet {
|
||||||
|
id,
|
||||||
|
name: packet_name,
|
||||||
|
fields,
|
||||||
|
};
|
||||||
|
|
||||||
|
output_packets.push(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
output_packets
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_packet_ids(packets: &backend::Packets) -> HashMap<String, u8> {
|
||||||
|
let reversed_packet_ids = packets
|
||||||
|
.types
|
||||||
|
.get("packet")
|
||||||
|
.and_then(|d| d.get(1))
|
||||||
|
.and_then(|d| match d {
|
||||||
|
backend::Data::Container(data) => data.get(0),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.and_then(|c| match c {
|
||||||
|
backend::Container::List { data_vec, .. } => data_vec.get(1),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.and_then(|d| match d {
|
||||||
|
backend::Data::Mapper { mappings, .. } => Some(mappings),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.expect("Failed to get packet ids");
|
||||||
|
|
||||||
|
reversed_packet_ids
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
(
|
||||||
|
v.clone(),
|
||||||
|
u8::from_str_radix(k.trim_start_matches("0x"), 16).expect("Invalid packet id"),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transform_field(unformatted_field_name: &str, data: &backend::Data) -> Option<frontend::Field> {
|
||||||
|
match data {
|
||||||
|
backend::Data::Type(name) => match transform_data_type(name) {
|
||||||
|
Some(data_type) => Some(frontend::Field {
|
||||||
|
name: format_field_name(unformatted_field_name),
|
||||||
|
data_type,
|
||||||
|
}),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transform_data_type(name: &str) -> Option<frontend::DataType> {
|
||||||
|
match name {
|
||||||
|
"bool" => Some(frontend::DataType::Boolean),
|
||||||
|
"i8" => Some(frontend::DataType::Byte),
|
||||||
|
"i16" => Some(frontend::DataType::Short),
|
||||||
|
"i32" => Some(frontend::DataType::Int { var_int: false }),
|
||||||
|
"i64" => Some(frontend::DataType::Long { var_long: false }),
|
||||||
|
"u8" => Some(frontend::DataType::UnsignedByte),
|
||||||
|
"u16" => Some(frontend::DataType::UnsignedShort),
|
||||||
|
"f32" => Some(frontend::DataType::Float),
|
||||||
|
"f64" => Some(frontend::DataType::Double),
|
||||||
|
"varint" => Some(frontend::DataType::Int { var_int: true }),
|
||||||
|
"varlong" => Some(frontend::DataType::Long { var_long: true }),
|
||||||
|
"string" => Some(frontend::DataType::String { max_length: 0 }),
|
||||||
|
"nbt" | "optionalNbt" => Some(frontend::DataType::CompoundTag),
|
||||||
|
"UUID" => Some(frontend::DataType::Uuid { hyphenated: false }),
|
||||||
|
"buffer" => Some(frontend::DataType::ByteArray { rest: false }),
|
||||||
|
"restBuffer" => Some(frontend::DataType::ByteArray { rest: true }),
|
||||||
|
"position" => Some(frontend::DataType::RefType {
|
||||||
|
ref_name: "Position".to_string(),
|
||||||
|
}),
|
||||||
|
"slot" => Some(frontend::DataType::RefType {
|
||||||
|
ref_name: "Option<Slot>".to_string(),
|
||||||
|
}),
|
||||||
|
"entityMetadata" => Some(frontend::DataType::RefType {
|
||||||
|
ref_name: "Metadata".to_string(),
|
||||||
|
}),
|
||||||
|
"tags" => Some(frontend::DataType::RefType {
|
||||||
|
ref_name: "TagsMap".to_string(),
|
||||||
|
}),
|
||||||
|
"option" => None,
|
||||||
|
_ => {
|
||||||
|
println!("Unknown data type \"{}\"", name);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_field_name(unformatted_field_name: &str) -> String {
|
||||||
|
if unformatted_field_name == "type" {
|
||||||
|
String::from("type_")
|
||||||
|
} else {
|
||||||
|
unformatted_field_name.to_snake_case()
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,4 @@
|
|||||||
use crate::error::{DecodeError, EncodeError};
|
use crate::impl_enum_encoder_decoder;
|
||||||
use crate::{impl_enum_encoder_decoder, Decoder, DecoderReadExt, Encoder, EncoderWriteExt};
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
|
||||||
use nbt::CompoundTag;
|
use nbt::CompoundTag;
|
||||||
use num_derive::{FromPrimitive, ToPrimitive};
|
use num_derive::{FromPrimitive, ToPrimitive};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
@ -32,31 +30,6 @@ pub struct Position {
|
|||||||
pub z: i32,
|
pub z: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoder for Position {
|
|
||||||
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
|
||||||
let encoded_x = (self.x & 0x3FFFFFF) as i64;
|
|
||||||
let encoded_y = (self.y & 0xFFF) as i64;
|
|
||||||
let encoded_z = (self.z & 0x3FFFFFF) as i64;
|
|
||||||
|
|
||||||
writer.write_i64::<BigEndian>((encoded_x << 38) | (encoded_z << 12) | encoded_y)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decoder for Position {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
|
||||||
let encoded = reader.read_i64::<BigEndian>()?;
|
|
||||||
|
|
||||||
let x = (encoded >> 38) as i32;
|
|
||||||
let y = (encoded & 0xFFF) as i16;
|
|
||||||
let z = (encoded << 26 >> 38) as i32;
|
|
||||||
|
|
||||||
Ok(Position { x, y, z })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Slot {
|
pub struct Slot {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
@ -64,86 +37,8 @@ pub struct Slot {
|
|||||||
pub compound_tag: CompoundTag,
|
pub compound_tag: CompoundTag,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoder for Option<Slot> {
|
|
||||||
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
|
||||||
match self {
|
|
||||||
Some(slot) => {
|
|
||||||
writer.write_bool(true)?;
|
|
||||||
slot.encode(writer)
|
|
||||||
}
|
|
||||||
None => writer.write_bool(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decoder for Option<Slot> {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
|
||||||
if reader.read_bool()? {
|
|
||||||
Ok(Some(Slot::decode(reader)?))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encoder for Slot {
|
|
||||||
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
|
||||||
writer.write_var_i32(self.id)?;
|
|
||||||
writer.write_u8(self.amount)?;
|
|
||||||
writer.write_compound_tag(&self.compound_tag)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decoder for Slot {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
|
||||||
let id = reader.read_var_i32()?;
|
|
||||||
let amount = reader.read_u8()?;
|
|
||||||
let compound_tag = reader.read_compound_tag()?;
|
|
||||||
|
|
||||||
Ok(Slot {
|
|
||||||
id,
|
|
||||||
amount,
|
|
||||||
compound_tag,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Metadata {}
|
pub struct Metadata {}
|
||||||
|
|
||||||
impl Encoder for Metadata {
|
|
||||||
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decoder for Metadata {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TagsMap {}
|
pub struct TagsMap {}
|
||||||
|
|
||||||
impl Encoder for TagsMap {
|
|
||||||
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decoder for TagsMap {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -10,6 +10,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use data::chat::Message;
|
use data::chat::Message;
|
||||||
|
|
||||||
|
use crate::data::game::{Metadata, Position, Slot, TagsMap};
|
||||||
use crate::error::{DecodeError, EncodeError};
|
use crate::error::{DecodeError, EncodeError};
|
||||||
|
|
||||||
pub mod data;
|
pub mod data;
|
||||||
@ -509,6 +510,109 @@ macro_rules! impl_json_encoder_decoder (
|
|||||||
);
|
);
|
||||||
);
|
);
|
||||||
|
|
||||||
|
impl Encoder for Position {
|
||||||
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
|
let encoded_x = (self.x & 0x3FFFFFF) as i64;
|
||||||
|
let encoded_y = (self.y & 0xFFF) as i64;
|
||||||
|
let encoded_z = (self.z & 0x3FFFFFF) as i64;
|
||||||
|
|
||||||
|
writer.write_i64::<BigEndian>((encoded_x << 38) | (encoded_z << 12) | encoded_y)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decoder for Position {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
||||||
|
let encoded = reader.read_i64::<BigEndian>()?;
|
||||||
|
|
||||||
|
let x = (encoded >> 38) as i32;
|
||||||
|
let y = (encoded & 0xFFF) as i16;
|
||||||
|
let z = (encoded << 26 >> 38) as i32;
|
||||||
|
|
||||||
|
Ok(Position { x, y, z })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encoder for Option<Slot> {
|
||||||
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
|
match self {
|
||||||
|
Some(slot) => {
|
||||||
|
writer.write_bool(true)?;
|
||||||
|
slot.encode(writer)
|
||||||
|
}
|
||||||
|
None => writer.write_bool(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decoder for Option<Slot> {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
||||||
|
if reader.read_bool()? {
|
||||||
|
Ok(Some(Slot::decode(reader)?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encoder for Slot {
|
||||||
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
|
writer.write_var_i32(self.id)?;
|
||||||
|
writer.write_u8(self.amount)?;
|
||||||
|
writer.write_compound_tag(&self.compound_tag)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decoder for Slot {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
||||||
|
let id = reader.read_var_i32()?;
|
||||||
|
let amount = reader.read_u8()?;
|
||||||
|
let compound_tag = reader.read_compound_tag()?;
|
||||||
|
|
||||||
|
Ok(Slot {
|
||||||
|
id,
|
||||||
|
amount,
|
||||||
|
compound_tag,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encoder for Metadata {
|
||||||
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decoder for Metadata {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encoder for TagsMap {
|
||||||
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decoder for TagsMap {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod var_int {
|
mod var_int {
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user