use proc_macro2::{Punct, Spacing, Span, TokenStream};
use quote::{format_ident, quote};
use syn::{
parse_macro_input, Data, DeriveInput, Fields, GenericArgument, GenericParam, Generics, Ident,
Index, LitStr, Member, PathArguments, Type,
};
use crate::{
message::get_builder_new_method,
shared::{get_builder_generic_fields, get_generics, get_struct_type, StructType},
};
const UNNAMED_FIELD_PREFIX: &str = "unnamed_field_";
pub struct NormalProperty {
pub variable_name: Ident,
pub inner_type: Type,
pub uppercase_variable_name: Ident,
pub index: usize,
}
pub struct EntityProperty {
pub variable_name: Ident,
pub uppercase_variable_name: Ident,
pub index: usize,
}
pub struct NonReplicatedProperty {
pub variable_name: Ident,
pub field_type: Type,
}
#[allow(clippy::large_enum_variant)]
pub enum Property {
Normal(NormalProperty),
Entity(EntityProperty),
NonReplicated(NonReplicatedProperty),
}
fn is_immutable_attr(input: &DeriveInput) -> bool {
input.attrs.iter().any(|attr| {
attr.path().is_ident("replicate")
&& attr
.parse_args::<syn::Ident>()
.map(|id| id == "immutable")
.unwrap_or(false)
})
}
pub fn replicate_impl(
input: proc_macro::TokenStream,
shared_crate_name: TokenStream,
auto_emit_bevy_component: bool,
) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let is_immutable = is_immutable_attr(&input);
let properties = get_properties(&input);
let struct_type = get_struct_type(&input);
let (untyped_generics, typed_generics, turbofish) = get_generics(&input);
let replica_name = input.ident.clone();
let replica_name_str = LitStr::new(
format!(
"{}{}",
&replica_name.to_string(),
&untyped_generics.to_string()
)
.as_str(),
replica_name.span(),
);
let lowercase_replica_name = Ident::new(
replica_name.to_string().to_lowercase().as_str(),
Span::call_site(),
);
let module_name = format_ident!("define_{}", lowercase_replica_name);
let enum_name = format_ident!("{}Property", replica_name);
let builder_name = format_ident!("{}Builder", replica_name);
let builder_generic_fields = get_builder_generic_fields(&input.generics);
if is_immutable {
for prop in &properties {
if matches!(prop, Property::Normal(_) | Property::Entity(_)) {
panic!(
"immutable Replicate cannot hold Property<T> or EntityProperty — use plain T \
fields instead (compile error from #[replicate(immutable)] validation)"
);
}
}
}
let property_enum_definition = get_property_enum_definition(&enum_name, &properties);
let diff_mask_size: u8 = {
let len = properties.len();
if len == 0 {
0_u8
} else {
(((len - 1) / 8) + 1) as u8
}
};
let is_immutable_method: TokenStream = if is_immutable {
quote! {
fn is_immutable(&self) -> bool { true }
}
} else {
quote! {}
};
let builder_is_immutable_method: TokenStream = if is_immutable {
quote! {
fn is_immutable(&self) -> bool { true }
}
} else {
quote! {}
};
let new_complete_method = get_new_complete_method(&enum_name, &properties, &struct_type);
let builder_create_method = get_builder_create_method(&builder_name, &turbofish);
let builder_new_method = get_builder_new_method(
&typed_generics,
&builder_name,
&untyped_generics,
&input.generics,
);
let builder_box_clone_method = get_builder_box_clone_method(&input.generics);
let builder_read_method =
get_builder_read_method(&replica_name, &properties, &struct_type, &turbofish);
let read_create_update_method =
get_read_create_update_method(&replica_name, &properties, &untyped_generics);
let dyn_ref_method = get_dyn_ref_method();
let dyn_mut_method = get_dyn_mut_method();
let clone_method = get_clone_method(&properties, &struct_type);
let mirror_method =
get_mirror_method(&replica_name, &properties, &struct_type, &untyped_generics);
let mirror_single_field_method =
get_mirror_single_field_method(&replica_name, &properties, &struct_type, &untyped_generics);
let set_mutator_method = get_set_mutator_method(&properties, &struct_type);
let publish_method = get_publish_method(&enum_name, &properties, &struct_type);
let unpublish_method = get_unpublish_method(&properties, &struct_type);
let enable_delegation_method =
get_enable_delegation_method(&enum_name, &properties, &struct_type);
let disable_delegation_method = get_disable_delegation_method(&properties, &struct_type);
let localize_method = get_localize_method(&properties, &struct_type);
let read_apply_update_method = get_read_apply_update_method(&properties, &struct_type);
let read_apply_field_update_method =
get_read_apply_field_update_method(&properties, &struct_type);
let write_method = get_write_method(&properties, &struct_type);
let write_update_method: TokenStream = if is_immutable {
quote! {
fn write_update(&self, _diff_mask: &DiffMask, _writer: &mut dyn BitWrite, _converter: &mut dyn LocalEntityAndGlobalEntityConverterMut) {
panic!("write_update called on an immutable component — this is a bug");
}
}
} else {
get_write_update_method(&enum_name, &properties, &struct_type)
};
let relations_waiting_method = get_relations_waiting_method(&properties, &struct_type);
let relations_complete_method = get_relations_complete_method(&properties, &struct_type);
let split_update_method =
get_split_update_method(&replica_name, &properties, &untyped_generics);
let bevy_component_impl = if auto_emit_bevy_component {
bevy_component_impl(
&replica_name,
&typed_generics,
&untyped_generics,
&shared_crate_name,
)
} else {
quote! {}
};
let gen = quote! {
mod #module_name {
use std::{rc::Rc, cell::RefCell, io::Cursor, any::Any, collections::HashSet};
use #shared_crate_name::{
DiffMask, PropertyMutate, PropertyMutator, ComponentUpdate,
ReplicaDynRef, ReplicaDynMut, LocalEntityAndGlobalEntityConverter, LocalEntityAndGlobalEntityConverterMut, ComponentKind, Named,
BitReader, BitWrite, BitWriter, OwnedBitReader, SerdeErr, Serde, EntityAuthAccessor, RemoteEntity,
EntityProperty, GlobalEntity, Replicate, Property, ComponentKinds, ReplicateBuilder, ComponentFieldUpdate,
};
use super::*;
#property_enum_definition
struct #builder_name #typed_generics #builder_generic_fields
#builder_new_method
impl #typed_generics ReplicateBuilder for #builder_name #untyped_generics {
#builder_is_immutable_method
#builder_read_method
#read_create_update_method
#split_update_method
#builder_box_clone_method
}
impl #typed_generics Named for #builder_name #untyped_generics {
fn name(&self) -> String {
#replica_name_str.to_string()
}
fn protocol_name() -> &'static str {
#replica_name_str
}
}
impl #typed_generics #replica_name #untyped_generics {
#new_complete_method
}
impl #typed_generics Named for #replica_name #untyped_generics {
fn name(&self) -> String {
#replica_name_str.to_string()
}
fn protocol_name() -> &'static str {
#replica_name_str
}
}
impl #typed_generics Replicate for #replica_name #untyped_generics {
#is_immutable_method
fn kind(&self) -> ComponentKind {
ComponentKind::of::<#replica_name #untyped_generics>()
}
fn to_any(&self) -> &dyn Any {
self
}
fn to_any_mut(&mut self) -> &mut dyn Any {
self
}
fn to_boxed_any(self: Box<Self>) -> Box<dyn Any> {
self
}
fn copy_to_box(&self) -> Box<dyn Replicate> {
Box::new(self.clone())
}
fn diff_mask_size(&self) -> u8 { #diff_mask_size }
#builder_create_method
#dyn_ref_method
#dyn_mut_method
#mirror_method
#mirror_single_field_method
#publish_method
#unpublish_method
#enable_delegation_method
#disable_delegation_method
#localize_method
#set_mutator_method
#write_method
#write_update_method
#read_apply_update_method
#read_apply_field_update_method
#relations_waiting_method
#relations_complete_method
}
impl #typed_generics Clone for #replica_name #untyped_generics {
#clone_method
}
#bevy_component_impl
}
};
proc_macro::TokenStream::from(gen)
}
#[cfg(feature = "bevy_support")]
fn bevy_component_impl(
replica_name: &Ident,
typed_generics: &proc_macro2::TokenStream,
untyped_generics: &proc_macro2::TokenStream,
shared_crate_name: &proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
quote! {
impl #typed_generics #shared_crate_name::bevy_ecs::component::Component
for #replica_name #untyped_generics
{
const STORAGE_TYPE: #shared_crate_name::bevy_ecs::component::StorageType =
#shared_crate_name::bevy_ecs::component::StorageType::Table;
type Mutability = #shared_crate_name::bevy_ecs::component::Mutable;
}
}
}
#[cfg(not(feature = "bevy_support"))]
fn bevy_component_impl(
_replica_name: &Ident,
_typed_generics: &proc_macro2::TokenStream,
_untyped_generics: &proc_macro2::TokenStream,
_shared_crate_name: &proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
quote! {}
}
fn get_variable_name_for_unnamed_field(index: usize, span: Span) -> Ident {
Ident::new(&format!("{}{}", UNNAMED_FIELD_PREFIX, index), span)
}
fn get_field_name(property: &Property, struct_type: &StructType) -> Member {
match *struct_type {
StructType::Struct => Member::from(property.variable_name().clone()),
StructType::TupleStruct => {
let index = Index {
index: property.index() as u32,
span: property.variable_name().span(),
};
Member::from(index)
}
_ => {
panic!("The struct should not have any fields")
}
}
}
impl Property {
pub fn normal(index: usize, variable_name: Ident, inner_type: Type) -> Self {
Self::Normal(NormalProperty {
index,
variable_name: variable_name.clone(),
inner_type,
uppercase_variable_name: Ident::new(
variable_name.to_string().to_uppercase().as_str(),
Span::call_site(),
),
})
}
pub fn entity(index: usize, variable_name: Ident) -> Self {
Self::Entity(EntityProperty {
index,
variable_name: variable_name.clone(),
uppercase_variable_name: Ident::new(
variable_name.to_string().to_uppercase().as_str(),
Span::call_site(),
),
})
}
pub fn nonreplicated(variable_name: Ident, field_type: Type) -> Self {
Self::NonReplicated(NonReplicatedProperty {
variable_name: variable_name.clone(),
field_type,
})
}
pub fn is_replicated(&self) -> bool {
match self {
Self::Normal(_) | Self::Entity(_) => true,
Self::NonReplicated(_) => false,
}
}
pub fn variable_name(&self) -> &Ident {
match self {
Self::Normal(property) => &property.variable_name,
Self::Entity(property) => &property.variable_name,
Self::NonReplicated(property) => &property.variable_name,
}
}
pub fn uppercase_variable_name(&self) -> &Ident {
match self {
Self::Normal(property) => &property.uppercase_variable_name,
Self::Entity(property) => &property.uppercase_variable_name,
Self::NonReplicated(_) => panic!("Unused for non-replicated properties"),
}
}
pub fn index(&self) -> usize {
match self {
Self::Normal(property) => property.index,
Self::Entity(property) => property.index,
Self::NonReplicated(_) => panic!("Unused for non-replicated properties"),
}
}
}
fn get_properties(input: &DeriveInput) -> Vec<Property> {
let mut fields = Vec::new();
if let Data::Struct(data_struct) = &input.data {
match &data_struct.fields {
Fields::Named(fields_named) => {
for field in fields_named.named.iter() {
if let Some(variable_name) = &field.ident {
if let Type::Path(type_path) = &field.ty {
if let Some(property_seg) = type_path.path.segments.first() {
let property_type = property_seg.ident.clone();
if property_type == "EntityProperty" {
fields.push(Property::entity(
fields.len(),
variable_name.clone(),
));
continue;
} else if property_type == "Property" {
if let PathArguments::AngleBracketed(angle_args) =
&property_seg.arguments
{
if let Some(GenericArgument::Type(inner_type)) =
angle_args.args.first()
{
fields.push(Property::normal(
fields.len(),
variable_name.clone(),
inner_type.clone(),
));
continue;
}
}
} else {
fields.push(Property::nonreplicated(
variable_name.clone(),
field.ty.clone(),
));
}
}
}
}
}
}
Fields::Unnamed(fields_unnamed) => {
for (index, field) in fields_unnamed.unnamed.iter().enumerate() {
if let Type::Path(type_path) = &field.ty {
if let Some(property_seg) = type_path.path.segments.first() {
let property_type = property_seg.ident.clone();
let variable_name =
get_variable_name_for_unnamed_field(index, property_type.span());
if property_type == "EntityProperty" {
fields.push(Property::entity(fields.len(), variable_name));
continue;
} else if let PathArguments::AngleBracketed(angle_args) =
&property_seg.arguments
{
if let Some(GenericArgument::Type(inner_type)) =
angle_args.args.first()
{
fields.push(Property::normal(
fields.len(),
variable_name,
inner_type.clone(),
));
continue;
}
}
}
}
}
}
Fields::Unit => {}
}
} else {
panic!("Can only derive Replicate on a struct");
}
fields
}
fn get_property_enum_definition(enum_name: &Ident, properties: &[Property]) -> TokenStream {
if properties.is_empty() {
return quote! {
enum #enum_name {}
};
}
let hashtag = Punct::new('#', Spacing::Alone);
let mut variant_list = quote! {};
for property in properties.iter().filter(|p| p.is_replicated()) {
let index = syn::Index::from(property.index());
let uppercase_variant_name = property.uppercase_variable_name();
let new_output_right = quote! {
#uppercase_variant_name = #index as u8,
};
let new_output_result = quote! {
#variant_list
#new_output_right
};
variant_list = new_output_result;
}
quote! {
#hashtag[repr(u8)]
enum #enum_name {
#variant_list
}
}
}
pub fn get_dyn_ref_method() -> TokenStream {
quote! {
fn dyn_ref(&self) -> ReplicaDynRef<'_> {
return ReplicaDynRef::new(self);
}
}
}
pub fn get_dyn_mut_method() -> TokenStream {
quote! {
fn dyn_mut(&mut self) -> ReplicaDynMut<'_> {
return ReplicaDynMut::new(self);
}
}
}
fn get_clone_method(properties: &[Property], struct_type: &StructType) -> TokenStream {
let mut output = quote! {};
let mut entity_property_output = quote! {};
for property in properties.iter() {
let field_name = get_field_name(property, struct_type);
match property {
Property::Normal(_) => {
let new_output_right = quote! {
(*self.#field_name).clone(),
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
Property::NonReplicated(_) => {
let new_output_right = quote! {
(self.#field_name).clone(),
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
Property::Entity(_) => {
let new_output_right = quote! {
new_clone.#field_name.mirror(&self.#field_name);
};
let new_output_result = quote! {
#entity_property_output
#new_output_right
};
entity_property_output = new_output_result;
}
};
}
quote! {
fn clone(&self) -> Self {
let mut new_clone = Self::new_complete(#output);
#entity_property_output
return new_clone;
}
}
}
fn get_mirror_method(
replica_name: &Ident,
properties: &[Property],
struct_type: &StructType,
untyped_generics: &TokenStream,
) -> TokenStream {
let mut output = quote! {};
for property in properties.iter().filter(|p| p.is_replicated()) {
let field_name = get_field_name(property, struct_type);
let new_output_right = quote! {
self.#field_name.mirror(&replica.#field_name);
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
fn mirror(&mut self, other: &dyn Replicate) {
if let Some(replica) = other.to_any().downcast_ref::<#replica_name #untyped_generics>() {
#output
} else {
panic!("cannot mirror: other Component is of another type!");
}
}
}
}
fn get_mirror_single_field_method(
replica_name: &Ident,
properties: &[Property],
struct_type: &StructType,
untyped_generics: &TokenStream,
) -> TokenStream {
let mut arms = quote! {};
for property in properties.iter().filter(|p| p.is_replicated()) {
let field_name = get_field_name(property, struct_type);
let index = syn::Index::from(property.index());
let arm = quote! {
#index => {
self.#field_name.mirror(&replica.#field_name);
}
};
arms = quote! {
#arms
#arm
};
}
quote! {
fn mirror_single_field(&mut self, field_index: u8, other: &dyn Replicate) {
if let Some(replica) = other.to_any().downcast_ref::<#replica_name #untyped_generics>() {
match field_index as usize {
#arms
_ => {
}
}
} else {
debug_assert!(
false,
"cannot mirror_single_field: other Component is of another type!"
);
}
}
}
}
fn get_set_mutator_method(properties: &[Property], struct_type: &StructType) -> TokenStream {
let mut output = quote! {};
for property in properties.iter().filter(|p| p.is_replicated()) {
let field_name = get_field_name(property, struct_type);
let new_output_right = quote! {
self.#field_name.set_mutator(mutator);
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
fn set_mutator(&mut self, mutator: &PropertyMutator) {
#output
}
}
}
fn get_publish_method(
enum_name: &Ident,
properties: &[Property],
struct_type: &StructType,
) -> TokenStream {
let mut output = quote! {};
for property in properties.iter().filter(|p| p.is_replicated()) {
let field_name = get_field_name(property, struct_type);
let uppercase_variant_name = property.uppercase_variable_name();
let new_output_right = quote! {
self.#field_name.remote_publish(#enum_name::#uppercase_variant_name as u8, mutator);
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
fn publish(&mut self, mutator: &PropertyMutator) {
#output
}
}
}
fn get_unpublish_method(properties: &[Property], struct_type: &StructType) -> TokenStream {
let mut output = quote! {};
for property in properties.iter().filter(|p| p.is_replicated()) {
let field_name = get_field_name(property, struct_type);
let new_output_right = quote! {
self.#field_name.remote_unpublish();
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
fn unpublish(&mut self) {
#output
}
}
}
fn get_enable_delegation_method(
enum_name: &Ident,
properties: &[Property],
struct_type: &StructType,
) -> TokenStream {
let mut output = quote! {};
for property in properties.iter().filter(|p| p.is_replicated()) {
let field_name = get_field_name(property, struct_type);
let uppercase_variant_name = property.uppercase_variable_name();
let new_output_right = quote! {
self.#field_name.enable_delegation(accessor, mutator_opt.map(|mutator| (#enum_name::#uppercase_variant_name as u8, mutator)));
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
fn enable_delegation(&mut self, accessor: &EntityAuthAccessor, mutator_opt: Option<&PropertyMutator>) {
#output
}
}
}
fn get_disable_delegation_method(properties: &[Property], struct_type: &StructType) -> TokenStream {
let mut output = quote! {};
for property in properties.iter().filter(|p| p.is_replicated()) {
let field_name = get_field_name(property, struct_type);
let new_output_right = quote! {
self.#field_name.disable_delegation();
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
fn disable_delegation(&mut self) {
#output
}
}
}
fn get_localize_method(properties: &[Property], struct_type: &StructType) -> TokenStream {
let mut output = quote! {};
for property in properties.iter().filter(|p| p.is_replicated()) {
let field_name = get_field_name(property, struct_type);
let new_output_right = quote! {
self.#field_name.localize();
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
fn localize(&mut self) {
#output
}
}
}
pub fn get_new_complete_method(
enum_name: &Ident,
properties: &[Property],
struct_type: &StructType,
) -> TokenStream {
let mut args = quote! {};
for property in properties.iter() {
match property {
Property::Normal(property) => {
let field_name = &property.variable_name;
let field_type = &property.inner_type;
let new_output_right = quote! {
#field_name: #field_type,
};
let new_output_result = quote! {
#args #new_output_right
};
args = new_output_result;
}
Property::NonReplicated(property) => {
let field_name = &property.variable_name;
let field_type = &property.field_type;
let new_output_right = quote! {
#field_name: #field_type,
};
let new_output_result = quote! {
#args #new_output_right
};
args = new_output_result;
}
Property::Entity(_) => {
continue;
}
};
}
let mut fields = quote! {};
for property in properties.iter() {
let new_output_right = match property {
Property::Normal(property) => {
let field_name = &property.variable_name;
let field_type = &property.inner_type;
let uppercase_variant_name = &property.uppercase_variable_name;
match *struct_type {
StructType::Struct => {
quote! {
#field_name: Property::<#field_type>::host_owned(#field_name, #enum_name::#uppercase_variant_name as u8)
}
}
StructType::TupleStruct => {
quote! {
Property::<#field_type>::host_owned(#field_name, #enum_name::#uppercase_variant_name as u8)
}
}
_ => {
quote! {}
}
}
}
Property::Entity(property) => {
let field_name = &property.variable_name;
let uppercase_variant_name = &property.uppercase_variable_name;
match *struct_type {
StructType::Struct => {
quote! {
#field_name: EntityProperty::new_for_component(#enum_name::#uppercase_variant_name as u8)
}
}
StructType::TupleStruct => {
quote! {
EntityProperty::new_for_component(#enum_name::#uppercase_variant_name as u8)
}
}
_ => {
quote! {}
}
}
}
Property::NonReplicated(property) => {
let field_name = &property.variable_name;
match *struct_type {
StructType::Struct => {
quote! {
#field_name: #field_name
}
}
StructType::TupleStruct => {
quote! {
#field_name
}
}
_ => {
quote! {}
}
}
}
};
let new_output_result = quote! {
#fields
#new_output_right,
};
fields = new_output_result;
}
let fn_inner = match *struct_type {
StructType::Struct => {
quote! {
Self {
#fields
}
}
}
StructType::TupleStruct => {
quote! {
Self (
#fields
)
}
}
StructType::UnitStruct => {
quote! {
Self
}
}
};
quote! {
pub fn new_complete(#args) -> Self {
#fn_inner
}
}
}
pub fn get_builder_create_method(builder_name: &Ident, turbofish: &TokenStream) -> TokenStream {
let builder_new = quote! {
#builder_name #turbofish::new()
};
quote! {
fn create_builder() -> Box<dyn ReplicateBuilder> where Self:Sized {
Box::new(#builder_new)
}
}
}
pub fn get_builder_read_method(
replica_name: &Ident,
properties: &[Property],
struct_type: &StructType,
turbofish: &TokenStream,
) -> TokenStream {
let mut prop_names = quote! {};
for property in properties.iter() {
let field_name = property.variable_name();
let new_output_right = quote! {
#field_name
};
let new_output_result = quote! {
#prop_names
#new_output_right,
};
prop_names = new_output_result;
}
let mut prop_reads = quote! {};
for property in properties.iter() {
let field_name = property.variable_name();
let new_output_right = match property {
Property::Normal(inner_property) => {
let field_type = &inner_property.inner_type;
quote! {
let #field_name = Property::<#field_type>::new_read(reader)?;
}
}
Property::Entity(_) => {
quote! {
let #field_name = EntityProperty::new_read(reader, converter)?;
}
}
Property::NonReplicated(inner_property) => {
let field_name = &inner_property.variable_name;
let field_type = &inner_property.field_type;
quote! {
let #field_name = <#field_type>::default();
}
}
};
let new_output_result = quote! {
#prop_reads
#new_output_right
};
prop_reads = new_output_result;
}
let replica_build = match *struct_type {
StructType::Struct => {
quote! {
#replica_name #turbofish {
#prop_names
}
}
}
StructType::TupleStruct => {
quote! {
#replica_name #turbofish (
#prop_names
)
}
}
StructType::UnitStruct => {
quote! {
#replica_name
}
}
};
quote! {
fn read(&self, reader: &mut BitReader, converter: &dyn LocalEntityAndGlobalEntityConverter) -> Result<Box<dyn Replicate>, SerdeErr> {
#prop_reads
return Ok(Box::new(#replica_build));
}
}
}
pub fn get_read_create_update_method(
replica_name: &Ident,
properties: &[Property],
untyped_generics: &TokenStream,
) -> TokenStream {
let mut prop_read_writes = quote! {};
for property in properties.iter() {
let new_output_right = match property {
Property::Normal(inner_property) => {
let field_type = &inner_property.inner_type;
quote! {
{
let should_read = bool::de(reader)?;
should_read.ser(&mut update_writer);
if should_read {
Property::<#field_type>::read_write(reader, &mut update_writer)?;
}
}
}
}
Property::Entity(_) => {
quote! {
{
let should_read = bool::de(reader)?;
should_read.ser(&mut update_writer);
if should_read {
EntityProperty::read_write(reader, &mut update_writer)?;
}
}
}
}
Property::NonReplicated(_) => {
continue;
}
};
let new_output_result = quote! {
#prop_read_writes
#new_output_right
};
prop_read_writes = new_output_result;
}
quote! {
fn read_create_update(&self, reader: &mut BitReader) -> Result<ComponentUpdate, SerdeErr> {
let mut update_writer = BitWriter::new();
#prop_read_writes
let owned_reader = update_writer.to_owned_reader();
return Ok(ComponentUpdate::new(ComponentKind::of::<#replica_name #untyped_generics>(), owned_reader));
}
}
}
fn get_split_update_method(
replica_name: &Ident,
properties: &[Property],
untyped_generics: &TokenStream,
) -> TokenStream {
let mut output = quote! {};
for property in properties.iter() {
let new_output_right = match property {
Property::Normal(inner_property) => {
let field_type = &inner_property.inner_type;
quote! {
let should_read = bool::de(reader)?;
should_read.ser(&mut ready_writer);
if should_read {
Property::<#field_type>::read_write(reader, &mut ready_writer)?;
ready_did_write = true;
}
}
}
Property::Entity(inner_property) => {
let index = inner_property.index as u8;
quote! {
let should_read = bool::de(reader)?;
if should_read {
let prop_copy = EntityProperty::new_read(reader, converter)?;
let waiting_entity_opt = prop_copy.waiting_remote_entity();
if let Some(waiting_entity) = waiting_entity_opt {
waiting_did_write = true;
let mut waiting_writer = BitWriter::new();
waiting_entity.copy_to_owned().ser(&mut waiting_writer);
waiting_updates.push((waiting_entity, ComponentFieldUpdate::new(#index, waiting_writer.to_owned_reader())));
} else {
ready_did_write = true;
true.ser(&mut ready_writer);
prop_copy.write_local_entity(converter, &mut ready_writer);
}
} else {
false.ser(&mut ready_writer);
}
}
}
Property::NonReplicated(_) => {
continue;
}
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
fn split_update(
&self,
converter: &dyn LocalEntityAndGlobalEntityConverter,
update: ComponentUpdate
) -> Result<(
Option<Vec<(RemoteEntity, ComponentFieldUpdate)>>,
Option<ComponentUpdate>
), SerdeErr> {
let component_kind = ComponentKind::of::<#replica_name #untyped_generics>();
let reader = &mut update.reader();
let mut waiting_did_write = false;
let mut waiting_updates: Vec<(RemoteEntity, ComponentFieldUpdate)> = Vec::new();
let mut ready_writer = BitWriter::new();
let mut ready_did_write = false;
#output
let waiting_result = {
if waiting_did_write {
Some(waiting_updates)
} else {
None
}
};
let ready_result = {
if ready_did_write {
Some(ComponentUpdate::new(component_kind, ready_writer.to_owned_reader()))
} else {
None
}
};
return Ok((waiting_result, ready_result));
}
}
}
fn get_read_apply_update_method(properties: &[Property], struct_type: &StructType) -> TokenStream {
let mut output = quote! {};
for property in properties.iter() {
let field_name = get_field_name(property, struct_type);
let new_output_right = match property {
Property::Normal(_) => {
quote! {
if bool::de(reader)? {
Property::read(&mut self.#field_name, reader)?;
}
}
}
Property::Entity(_) => {
quote! {
if bool::de(reader)? {
EntityProperty::read(&mut self.#field_name, reader, converter)?;
}
}
}
Property::NonReplicated(_) => {
continue;
}
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
fn read_apply_update(&mut self, converter: &dyn LocalEntityAndGlobalEntityConverter, mut update: ComponentUpdate) -> Result<(), SerdeErr> {
let reader = &mut update.reader();
#output
Ok(())
}
}
}
fn get_read_apply_field_update_method(
properties: &[Property],
struct_type: &StructType,
) -> TokenStream {
let mut output = quote! {};
for property in properties.iter() {
let field_name = get_field_name(property, struct_type);
let new_output_right = match property {
Property::Normal(_) | Property::NonReplicated(_) => {
continue;
}
Property::Entity(inner_property) => {
let index = inner_property.index as u8;
quote! {
#index => {
EntityProperty::read(&mut self.#field_name, reader, converter)?;
}
}
}
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
fn read_apply_field_update(&mut self, converter: &dyn LocalEntityAndGlobalEntityConverter, mut update: ComponentFieldUpdate) -> Result<(), SerdeErr> {
let reader = &mut update.reader();
match update.field_id() {
#output
_ => {}
}
Ok(())
}
}
}
fn get_write_method(properties: &[Property], struct_type: &StructType) -> TokenStream {
let mut property_writes = quote! {};
for property in properties.iter() {
let field_name = get_field_name(property, struct_type);
let new_output_right = match property {
Property::Normal(_) => {
quote! {
Property::write(&self.#field_name, writer);
}
}
Property::Entity(_) => {
quote! {
EntityProperty::write(&self.#field_name, writer, converter);
}
}
Property::NonReplicated(_) => {
continue;
}
};
let new_output_result = quote! {
#property_writes
#new_output_right
};
property_writes = new_output_result;
}
quote! {
fn write(&self, component_kinds: &ComponentKinds, writer: &mut dyn BitWrite, converter: &mut dyn LocalEntityAndGlobalEntityConverterMut) {
self.kind().ser(component_kinds, writer);
#property_writes
}
}
}
fn get_write_update_method(
enum_name: &Ident,
properties: &[Property],
struct_type: &StructType,
) -> TokenStream {
let mut output = quote! {};
for property in properties.iter() {
let field_name = get_field_name(property, struct_type);
let new_output_right = match property {
Property::Normal(property) => {
let uppercase_variant_name = &property.uppercase_variable_name;
quote! {
if let Some(true) = diff_mask.bit(#enum_name::#uppercase_variant_name as u8) {
true.ser(writer);
Property::write(&self.#field_name, writer);
} else {
false.ser(writer);
}
}
}
Property::Entity(property) => {
let uppercase_variant_name = &property.uppercase_variable_name;
quote! {
if let Some(true) = diff_mask.bit(#enum_name::#uppercase_variant_name as u8) {
true.ser(writer);
EntityProperty::write(&self.#field_name, writer, converter);
} else {
false.ser(writer);
}
}
}
Property::NonReplicated(_) => {
continue;
}
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
fn write_update(&self, diff_mask: &DiffMask, writer: &mut dyn BitWrite, converter: &mut dyn LocalEntityAndGlobalEntityConverterMut) {
#output
}
}
}
fn get_relations_waiting_method(fields: &[Property], struct_type: &StructType) -> TokenStream {
let mut body = quote! {};
for field in fields.iter() {
if let Property::Entity(_) = field {
let field_name = get_field_name(field, struct_type);
let body_add_right = quote! {
if let Some(local_entity) = self.#field_name.waiting_remote_entity() {
output.insert(local_entity);
}
};
let new_body = quote! {
#body
#body_add_right
};
body = new_body;
}
}
quote! {
fn relations_waiting(&self) -> Option<HashSet<RemoteEntity>> {
let mut output = HashSet::new();
#body
if output.is_empty() {
return None;
}
return Some(output);
}
}
}
fn get_relations_complete_method(fields: &[Property], struct_type: &StructType) -> TokenStream {
let mut body = quote! {};
for field in fields.iter() {
if let Property::Entity(_) = field {
let field_name = get_field_name(field, struct_type);
let body_add_right = quote! {
self.#field_name.waiting_complete(converter);
};
let new_body = quote! {
#body
#body_add_right
};
body = new_body;
}
}
quote! {
fn relations_complete(&mut self, converter: &dyn LocalEntityAndGlobalEntityConverter) {
#body
}
}
}
pub fn get_builder_box_clone_method(input_generics: &Generics) -> TokenStream {
let fn_impl = if input_generics.gt_token.is_none() {
quote! { Self }
} else {
let mut output = quote! {};
for param in input_generics.params.iter() {
let GenericParam::Type(type_param) = param else {
panic!("Only type parameters are supported for now");
};
let field_name =
format_ident!("phantom_{}", type_param.ident.to_string().to_lowercase());
let new_output_right = quote! {
#field_name: std::marker::PhantomData,
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
Self {
#output
}
}
};
quote! {
fn box_clone(&self) -> Box<dyn ReplicateBuilder> {
let me = #fn_impl ;
Box::new(me)
}
}
}