1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use std::fmt::Debug;
use std::hash::Hash;

use bevy::prelude::{App, Component, EntityWorldMut, World};
use serde::de::DeserializeOwned;
use serde::Serialize;

use crate::connection::events::{
    IterComponentInsertEvent, IterComponentRemoveEvent, IterComponentUpdateEvent,
};
use crate::prelude::MapEntities;
use crate::protocol::{BitSerializable, EventContext, Protocol};
use crate::shared::replication::components::ShouldBeInterpolated;
use crate::shared::replication::components::ShouldBePredicted;
use crate::shared::replication::ReplicationSend;

// client writes an Enum containing all their message type
// each message must derive message

// that big enum will implement MessageProtocol via a proc macro
// TODO: remove the extra  Serialize + DeserializeOwned + Clone  bounds
pub trait ComponentProtocol:
    BitSerializable
    + Serialize
    + DeserializeOwned
    + MapEntities
    + ComponentBehaviour
    + Debug
    + Send
    + Sync
    + From<ShouldBePredicted>
    + From<ShouldBeInterpolated>
{
    type Protocol: Protocol;

    /// Add systems to send component inserts/removes/updates
    fn add_per_component_replication_send_systems<R: ReplicationSend<Self::Protocol>>(
        app: &mut App,
    );

    /// Adds Component-related events to the app
    fn add_events<Ctx: EventContext>(app: &mut App);

    // TODO: make this a system that runs after io-receive/recv/read
    //  maybe a standalone EventsPlugin
    /// Takes messages that were written and writes MessageEvents
    fn push_component_events<
        E: IterComponentInsertEvent<Self::Protocol, Ctx>
            + IterComponentRemoveEvent<Self::Protocol, Ctx>
            + IterComponentUpdateEvent<Self::Protocol, Ctx>,
        Ctx: EventContext,
    >(
        world: &mut World,
        events: &mut E,
    );

    fn add_prediction_systems(app: &mut App);
    fn add_interpolation_systems(app: &mut App);
}

// TODO: enum_delegate doesn't work with generics + cannot be used multiple times since it derives a bunch of Into/From traits
/// Trait to delegate a method from the ComponentProtocol enum to the inner Component type
#[enum_delegate::register]
pub trait ComponentBehaviour {
    /// Apply a ComponentInsert to an entity
    fn insert(self, entity: &mut EntityWorldMut);

    /// Apply a ComponentUpdate to an entity
    fn update(self, entity: &mut EntityWorldMut);
}

impl<T: Component> ComponentBehaviour for T {
    // Apply a ComponentInsert to an entity
    fn insert(self, entity: &mut EntityWorldMut) {
        // only insert if the entity didn't have the component
        // because otherwise the insert could override an component-update that was received later?

        // but this could cause some issues if we wanted the component to be updated from the insert
        // if entity.get::<T>().is_none() {
        entity.insert(self);
        // }
    }

    // Apply a ComponentUpdate to an entity
    fn update(self, entity: &mut EntityWorldMut) {
        if let Some(mut c) = entity.get_mut::<T>() {
            *c = self;
        }
        // match entity.get_mut::<T>() {
        //     Some(mut c) => *c = self,
        //     None => {
        //         entity.insert(self);
        //     }
        // }
    }
}

pub trait ComponentProtocolKind:
    BitSerializable
    + Serialize
    + DeserializeOwned
    + MapEntities
    + PartialEq
    + Eq
    + Hash
    + Debug
    + Send
    + Sync
    + for<'a> From<&'a <Self::Protocol as Protocol>::Components>
    + ComponentKindBehaviour
{
    type Protocol: Protocol;
}

/// Trait to delegate a method from the ComponentProtocolKind enum to the inner Component type
pub trait ComponentKindBehaviour {
    /// Remove the component for an entity
    fn remove(self, entity: &mut EntityWorldMut);
}

/// Trait to convert a component type into the corresponding ComponentProtocolKind
pub trait IntoKind<K: ComponentProtocolKind> {
    fn into_kind() -> K;
}