Refactor
This commit is contained in:
parent
5245cdf16d
commit
ebd8f5ce22
@ -2,23 +2,23 @@ use linked_hash_map::LinkedHashMap;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Protocol {
|
||||
pub handshaking: ProtocolState,
|
||||
pub status: ProtocolState,
|
||||
pub login: ProtocolState,
|
||||
pub struct ProtocolHandler {
|
||||
pub handshaking: Protocol,
|
||||
pub status: Protocol,
|
||||
pub login: Protocol,
|
||||
#[serde(rename = "play")]
|
||||
pub game: ProtocolState,
|
||||
pub game: Protocol,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProtocolState {
|
||||
pub to_client: ProtocolData,
|
||||
pub to_server: ProtocolData,
|
||||
pub struct Protocol {
|
||||
pub to_client: Packets,
|
||||
pub to_server: Packets,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ProtocolData {
|
||||
pub struct Packets {
|
||||
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::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;
|
||||
|
||||
pub mod backend;
|
||||
pub mod frontend;
|
||||
pub mod mappings;
|
||||
pub mod transformer;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(name = "protocol-generator")]
|
||||
struct Opt {
|
||||
@ -32,25 +33,31 @@ pub fn main() {
|
||||
let 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");
|
||||
|
||||
let mappings = CodeMappings {};
|
||||
|
||||
let protocols = vec![
|
||||
(
|
||||
transform_protocol_state(output::State::Handshake, &protocol_input.handshaking),
|
||||
output::State::Handshake,
|
||||
transform_protocol(
|
||||
&mappings,
|
||||
frontend::State::Handshake,
|
||||
&protocol_input.handshaking,
|
||||
),
|
||||
frontend::State::Handshake,
|
||||
),
|
||||
(
|
||||
transform_protocol_state(output::State::Status, &protocol_input.status),
|
||||
output::State::Status,
|
||||
transform_protocol(&mappings, frontend::State::Status, &protocol_input.status),
|
||||
frontend::State::Status,
|
||||
),
|
||||
(
|
||||
transform_protocol_state(output::State::Login, &protocol_input.login),
|
||||
output::State::Login,
|
||||
transform_protocol(&mappings, frontend::State::Login, &protocol_input.login),
|
||||
frontend::State::Login,
|
||||
),
|
||||
(
|
||||
transform_protocol_state(output::State::Game, &protocol_input.game),
|
||||
output::State::Game,
|
||||
transform_protocol(&mappings, frontend::State::Game, &protocol_input.game),
|
||||
frontend::State::Game,
|
||||
),
|
||||
];
|
||||
|
||||
@ -97,264 +104,24 @@ fn create_template_engine() -> Handlebars<'static> {
|
||||
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)]
|
||||
struct GenerateContext<'a> {
|
||||
packet_enum_name: String,
|
||||
packets: &'a Vec<output::Packet>,
|
||||
packets: &'a Vec<frontend::Packet>,
|
||||
}
|
||||
|
||||
fn generate_rust_file<W: Write>(
|
||||
protocol: &output::Protocol,
|
||||
protocol: &frontend::Protocol,
|
||||
template_engine: &Handlebars,
|
||||
mut writer: W,
|
||||
) -> Result<(), TemplateRenderError> {
|
||||
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,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
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, Decoder, DecoderReadExt, Encoder, EncoderWriteExt};
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use crate::impl_enum_encoder_decoder;
|
||||
use nbt::CompoundTag;
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use std::io::{Read, Write};
|
||||
@ -32,31 +30,6 @@ pub struct Position {
|
||||
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)]
|
||||
pub struct Slot {
|
||||
pub id: i32,
|
||||
@ -64,86 +37,8 @@ pub struct Slot {
|
||||
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)]
|
||||
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)]
|
||||
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 crate::data::game::{Metadata, Position, Slot, TagsMap};
|
||||
use crate::error::{DecodeError, EncodeError};
|
||||
|
||||
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 {
|
||||
use std::io::{Read, Write};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user