127 lines
4.2 KiB
Rust
127 lines
4.2 KiB
Rust
use crate::wit::utils::map_type;
|
|
use heck::ToKebabCase;
|
|
use semver::Version;
|
|
use std::{fs, path::Path};
|
|
use syn::{Fields, Item};
|
|
use wit_encoder::{
|
|
Field, Interface, Package, PackageName, Record, Type as WitType, TypeDef, TypeDefKind, Variant,
|
|
VariantCase,
|
|
};
|
|
|
|
pub fn build() -> String {
|
|
let mut package = Package::new(PackageName::new(
|
|
"pumpkin",
|
|
"plugin",
|
|
Some(Version::new(0, 1, 0)),
|
|
));
|
|
|
|
let mut interface = Interface::new("java-packets");
|
|
|
|
interface.use_type("uuid", "uuid", None);
|
|
|
|
let mut serverbound_variant = Variant::empty();
|
|
let mut clientbound_variant = Variant::empty();
|
|
|
|
// Process serverbound packets
|
|
process_packets(
|
|
"../pumpkin-protocol/src/java/server/play",
|
|
&mut interface,
|
|
&mut serverbound_variant,
|
|
);
|
|
// Process clientbound packets
|
|
process_packets(
|
|
"../pumpkin-protocol/src/java/client/play",
|
|
&mut interface,
|
|
&mut clientbound_variant,
|
|
);
|
|
|
|
interface.type_def(TypeDef::new(
|
|
"serverbound-packet",
|
|
TypeDefKind::Variant(serverbound_variant),
|
|
));
|
|
interface.type_def(TypeDef::new(
|
|
"clientbound-packet",
|
|
TypeDefKind::Variant(clientbound_variant),
|
|
));
|
|
|
|
package.interface(interface);
|
|
package.to_string()
|
|
}
|
|
|
|
fn process_packets(dir: &str, interface: &mut Interface, variant: &mut Variant) {
|
|
let paths = fs::read_dir(dir).expect("Failed to read packet directory");
|
|
let mut sorted_paths: Vec<_> = paths
|
|
.map(|e| e.expect("Failed to read entry").path())
|
|
.collect();
|
|
sorted_paths.sort();
|
|
|
|
for path in sorted_paths {
|
|
if path.extension().is_some_and(|ext| ext == "rs")
|
|
&& path.file_name().is_some_and(|name| name != "mod.rs")
|
|
{
|
|
parse_packet_file(&path, interface, variant);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parse_packet_file(path: &Path, interface: &mut Interface, variant: &mut Variant) {
|
|
let content = fs::read_to_string(path).expect("Failed to read file");
|
|
let file = syn::parse_file(&content).expect("Failed to parse file");
|
|
|
|
for item in file.items {
|
|
if let Item::Struct(s) = item {
|
|
// Only process structs with #[java_packet] attribute
|
|
if !s
|
|
.attrs
|
|
.iter()
|
|
.any(|attr| attr.path().is_ident("java_packet"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
let struct_name = s.ident.to_string();
|
|
let mut fields_list = Vec::new();
|
|
|
|
if let Fields::Named(fields) = s.fields {
|
|
for field in fields.named {
|
|
let type_name = match &field.ty {
|
|
syn::Type::Path(p) => p.path.segments.last().unwrap().ident.to_string(),
|
|
syn::Type::Reference(r) => match &*r.elem {
|
|
syn::Type::Slice(s) => match &*s.elem {
|
|
syn::Type::Path(p) => {
|
|
p.path.segments.last().unwrap().ident.to_string()
|
|
}
|
|
_ => String::new(),
|
|
},
|
|
syn::Type::Path(p) => p.path.segments.last().unwrap().ident.to_string(),
|
|
_ => String::new(),
|
|
},
|
|
_ => String::new(),
|
|
};
|
|
|
|
if type_name == "DynamicRecipe" {
|
|
continue;
|
|
}
|
|
|
|
let field_name = field.ident.as_ref().unwrap().to_string().to_kebab_case();
|
|
let field_type = map_type(&field.ty);
|
|
fields_list.push(Field::new(field_name, field_type));
|
|
}
|
|
}
|
|
let wit_name = struct_name.to_kebab_case();
|
|
if !fields_list.is_empty() {
|
|
interface.type_def(TypeDef::new(
|
|
wit_name.clone(),
|
|
TypeDefKind::Record(Record::new(fields_list)),
|
|
));
|
|
variant.case(VariantCase::value(
|
|
wit_name.clone(),
|
|
WitType::named(wit_name),
|
|
));
|
|
} else {
|
|
variant.case(VariantCase::empty(wit_name));
|
|
}
|
|
}
|
|
}
|
|
}
|