Skip to main content

raknet_rust/
event.rs

1use std::net::SocketAddr;
2
3use crate::client::RaknetClientEvent;
4use crate::proxy::RaknetRelayProxyEvent;
5use crate::server::{PeerId, RaknetServerEvent};
6use crate::transport::TransportMetricsSnapshot;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum RaknetEventSource {
10    Server,
11    Client,
12    Proxy,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum RaknetEventKind {
17    Connected,
18    Disconnected,
19    Packet,
20    OfflinePacket,
21    Forwarded,
22    Dropped,
23    ReceiptAcked,
24    DecodeError,
25    RateLimited,
26    SessionLimitReached,
27    ProxyDropped,
28    WorkerError,
29    WorkerStopped,
30    Metrics,
31}
32
33#[derive(Debug)]
34pub enum RaknetEvent {
35    Server(RaknetServerEvent),
36    Client(RaknetClientEvent),
37    Proxy(RaknetRelayProxyEvent),
38}
39
40impl RaknetEvent {
41    pub fn source(&self) -> RaknetEventSource {
42        match self {
43            Self::Server(_) => RaknetEventSource::Server,
44            Self::Client(_) => RaknetEventSource::Client,
45            Self::Proxy(_) => RaknetEventSource::Proxy,
46        }
47    }
48
49    pub fn kind(&self) -> RaknetEventKind {
50        match self {
51            Self::Server(event) => match event {
52                RaknetServerEvent::PeerConnected { .. } => RaknetEventKind::Connected,
53                RaknetServerEvent::PeerDisconnected { .. } => RaknetEventKind::Disconnected,
54                RaknetServerEvent::Packet { .. } => RaknetEventKind::Packet,
55                RaknetServerEvent::OfflinePacket { .. } => RaknetEventKind::OfflinePacket,
56                RaknetServerEvent::ReceiptAcked { .. } => RaknetEventKind::ReceiptAcked,
57                RaknetServerEvent::PeerRateLimited { .. } => RaknetEventKind::RateLimited,
58                RaknetServerEvent::SessionLimitReached { .. } => {
59                    RaknetEventKind::SessionLimitReached
60                }
61                RaknetServerEvent::ProxyDropped { .. } => RaknetEventKind::ProxyDropped,
62                RaknetServerEvent::DecodeError { .. } => RaknetEventKind::DecodeError,
63                RaknetServerEvent::WorkerError { .. } => RaknetEventKind::WorkerError,
64                RaknetServerEvent::WorkerStopped { .. } => RaknetEventKind::WorkerStopped,
65                RaknetServerEvent::Metrics { .. } => RaknetEventKind::Metrics,
66            },
67            Self::Client(event) => match event {
68                RaknetClientEvent::Connected { .. } => RaknetEventKind::Connected,
69                RaknetClientEvent::Packet { .. } => RaknetEventKind::Packet,
70                RaknetClientEvent::ReceiptAcked { .. } => RaknetEventKind::ReceiptAcked,
71                RaknetClientEvent::DecodeError { .. } => RaknetEventKind::DecodeError,
72                RaknetClientEvent::Disconnected { .. } => RaknetEventKind::Disconnected,
73            },
74            Self::Proxy(event) => match event {
75                RaknetRelayProxyEvent::SessionStarted { .. } => RaknetEventKind::Connected,
76                RaknetRelayProxyEvent::Forwarded { .. } => RaknetEventKind::Forwarded,
77                RaknetRelayProxyEvent::Dropped { .. } => RaknetEventKind::Dropped,
78                RaknetRelayProxyEvent::DecodeError { .. } => RaknetEventKind::DecodeError,
79                RaknetRelayProxyEvent::SessionClosed { .. } => RaknetEventKind::Disconnected,
80                RaknetRelayProxyEvent::DownstreamRateLimited { .. } => RaknetEventKind::RateLimited,
81                RaknetRelayProxyEvent::DownstreamSessionLimitReached { .. } => {
82                    RaknetEventKind::SessionLimitReached
83                }
84                RaknetRelayProxyEvent::DownstreamProxyDropped { .. } => {
85                    RaknetEventKind::ProxyDropped
86                }
87                RaknetRelayProxyEvent::DownstreamDecodeError { .. } => RaknetEventKind::DecodeError,
88                RaknetRelayProxyEvent::DownstreamWorkerError { .. } => RaknetEventKind::WorkerError,
89                RaknetRelayProxyEvent::DownstreamWorkerStopped { .. } => {
90                    RaknetEventKind::WorkerStopped
91                }
92            },
93        }
94    }
95
96    pub fn peer_id(&self) -> Option<PeerId> {
97        match self {
98            Self::Server(event) => match event {
99                RaknetServerEvent::PeerConnected { peer_id, .. }
100                | RaknetServerEvent::PeerDisconnected { peer_id, .. }
101                | RaknetServerEvent::Packet { peer_id, .. }
102                | RaknetServerEvent::ReceiptAcked { peer_id, .. } => Some(*peer_id),
103                RaknetServerEvent::OfflinePacket { .. }
104                | RaknetServerEvent::PeerRateLimited { .. }
105                | RaknetServerEvent::SessionLimitReached { .. }
106                | RaknetServerEvent::ProxyDropped { .. }
107                | RaknetServerEvent::DecodeError { .. }
108                | RaknetServerEvent::WorkerError { .. }
109                | RaknetServerEvent::WorkerStopped { .. }
110                | RaknetServerEvent::Metrics { .. } => None,
111            },
112            Self::Client(_) => None,
113            Self::Proxy(event) => match event {
114                RaknetRelayProxyEvent::SessionStarted { peer_id, .. }
115                | RaknetRelayProxyEvent::Forwarded { peer_id, .. }
116                | RaknetRelayProxyEvent::Dropped { peer_id, .. }
117                | RaknetRelayProxyEvent::DecodeError { peer_id, .. }
118                | RaknetRelayProxyEvent::SessionClosed { peer_id, .. } => Some(*peer_id),
119                RaknetRelayProxyEvent::DownstreamRateLimited { .. }
120                | RaknetRelayProxyEvent::DownstreamSessionLimitReached { .. }
121                | RaknetRelayProxyEvent::DownstreamProxyDropped { .. }
122                | RaknetRelayProxyEvent::DownstreamDecodeError { .. }
123                | RaknetRelayProxyEvent::DownstreamWorkerError { .. }
124                | RaknetRelayProxyEvent::DownstreamWorkerStopped { .. } => None,
125            },
126        }
127    }
128
129    pub fn primary_addr(&self) -> Option<SocketAddr> {
130        match self {
131            Self::Server(event) => match event {
132                RaknetServerEvent::PeerConnected { addr, .. }
133                | RaknetServerEvent::PeerDisconnected { addr, .. }
134                | RaknetServerEvent::Packet { addr, .. }
135                | RaknetServerEvent::OfflinePacket { addr, .. }
136                | RaknetServerEvent::ReceiptAcked { addr, .. }
137                | RaknetServerEvent::PeerRateLimited { addr }
138                | RaknetServerEvent::SessionLimitReached { addr }
139                | RaknetServerEvent::ProxyDropped { addr }
140                | RaknetServerEvent::DecodeError { addr, .. } => Some(*addr),
141                RaknetServerEvent::WorkerError { .. }
142                | RaknetServerEvent::WorkerStopped { .. }
143                | RaknetServerEvent::Metrics { .. } => None,
144            },
145            Self::Client(event) => match event {
146                RaknetClientEvent::Connected { server_addr, .. } => Some(*server_addr),
147                RaknetClientEvent::Packet { .. }
148                | RaknetClientEvent::ReceiptAcked { .. }
149                | RaknetClientEvent::DecodeError { .. }
150                | RaknetClientEvent::Disconnected { .. } => None,
151            },
152            Self::Proxy(event) => match event {
153                RaknetRelayProxyEvent::SessionStarted {
154                    downstream_addr, ..
155                }
156                | RaknetRelayProxyEvent::DownstreamRateLimited {
157                    addr: downstream_addr,
158                }
159                | RaknetRelayProxyEvent::DownstreamSessionLimitReached {
160                    addr: downstream_addr,
161                }
162                | RaknetRelayProxyEvent::DownstreamProxyDropped {
163                    addr: downstream_addr,
164                }
165                | RaknetRelayProxyEvent::DownstreamDecodeError {
166                    addr: downstream_addr,
167                    ..
168                } => Some(*downstream_addr),
169                RaknetRelayProxyEvent::Forwarded { .. }
170                | RaknetRelayProxyEvent::Dropped { .. }
171                | RaknetRelayProxyEvent::DecodeError { .. }
172                | RaknetRelayProxyEvent::SessionClosed { .. }
173                | RaknetRelayProxyEvent::DownstreamWorkerError { .. }
174                | RaknetRelayProxyEvent::DownstreamWorkerStopped { .. } => None,
175            },
176        }
177    }
178
179    pub fn payload_len(&self) -> Option<usize> {
180        match self {
181            Self::Server(event) => match event {
182                RaknetServerEvent::Packet { payload, .. } => Some(payload.len()),
183                _ => None,
184            },
185            Self::Client(event) => match event {
186                RaknetClientEvent::Packet { payload, .. } => Some(payload.len()),
187                _ => None,
188            },
189            Self::Proxy(event) => match event {
190                RaknetRelayProxyEvent::Forwarded { payload_len, .. } => Some(*payload_len),
191                _ => None,
192            },
193        }
194    }
195
196    pub fn decode_error(&self) -> Option<&str> {
197        match self {
198            Self::Server(event) => match event {
199                RaknetServerEvent::DecodeError { error, .. } => Some(error.as_str()),
200                _ => None,
201            },
202            Self::Client(event) => match event {
203                RaknetClientEvent::DecodeError { error } => Some(error.as_str()),
204                _ => None,
205            },
206            Self::Proxy(event) => match event {
207                RaknetRelayProxyEvent::DecodeError { error, .. }
208                | RaknetRelayProxyEvent::DownstreamDecodeError { error, .. } => {
209                    Some(error.as_str())
210                }
211                _ => None,
212            },
213        }
214    }
215
216    pub fn metrics_snapshot(&self) -> Option<(usize, &TransportMetricsSnapshot, u64)> {
217        match self {
218            Self::Server(RaknetServerEvent::Metrics {
219                shard_id,
220                snapshot,
221                dropped_non_critical_events,
222            }) => Some((*shard_id, snapshot.as_ref(), *dropped_non_critical_events)),
223            _ => None,
224        }
225    }
226
227    pub fn as_server(&self) -> Option<&RaknetServerEvent> {
228        match self {
229            Self::Server(event) => Some(event),
230            _ => None,
231        }
232    }
233
234    pub fn as_client(&self) -> Option<&RaknetClientEvent> {
235        match self {
236            Self::Client(event) => Some(event),
237            _ => None,
238        }
239    }
240
241    pub fn as_proxy(&self) -> Option<&RaknetRelayProxyEvent> {
242        match self {
243            Self::Proxy(event) => Some(event),
244            _ => None,
245        }
246    }
247}
248
249impl From<RaknetServerEvent> for RaknetEvent {
250    fn from(value: RaknetServerEvent) -> Self {
251        Self::Server(value)
252    }
253}
254
255impl From<RaknetClientEvent> for RaknetEvent {
256    fn from(value: RaknetClientEvent) -> Self {
257        Self::Client(value)
258    }
259}
260
261impl From<RaknetRelayProxyEvent> for RaknetEvent {
262    fn from(value: RaknetRelayProxyEvent) -> Self {
263        Self::Proxy(value)
264    }
265}
266
267#[cfg(test)]
268mod tests {
269    use bytes::Bytes;
270
271    use super::{RaknetEvent, RaknetEventKind, RaknetEventSource};
272    use crate::client::RaknetClientEvent;
273    use crate::handshake::{OfflinePacket, UnconnectedPing};
274    use crate::protocol::constants::DEFAULT_UNCONNECTED_MAGIC;
275    use crate::protocol::reliability::Reliability;
276    use crate::proxy::{
277        RaknetRelayProxyEvent, RelayDirection, RelayDropReason, RelaySessionCloseReason,
278    };
279    use crate::server::{PeerDisconnectReason, PeerId, RaknetServerEvent};
280    use crate::transport::TransportMetricsSnapshot;
281
282    #[test]
283    fn server_packet_maps_to_common_fields() {
284        let peer_id = PeerId::from_u64(7);
285        let addr = "127.0.0.1:19132".parse().expect("valid socket addr");
286        let event = RaknetEvent::from(RaknetServerEvent::Packet {
287            peer_id,
288            addr,
289            payload: Bytes::from_static(b"abc"),
290            reliability: Reliability::ReliableOrdered,
291            reliable_index: None,
292            sequence_index: None,
293            ordering_index: None,
294            ordering_channel: Some(0),
295        });
296
297        assert_eq!(event.source(), RaknetEventSource::Server);
298        assert_eq!(event.kind(), RaknetEventKind::Packet);
299        assert_eq!(event.peer_id(), Some(peer_id));
300        assert_eq!(event.primary_addr(), Some(addr));
301        assert_eq!(event.payload_len(), Some(3));
302    }
303
304    #[test]
305    fn client_decode_error_is_exposed() {
306        let event = RaknetEvent::from(RaknetClientEvent::DecodeError {
307            error: "bad packet".to_string(),
308        });
309
310        assert_eq!(event.source(), RaknetEventSource::Client);
311        assert_eq!(event.kind(), RaknetEventKind::DecodeError);
312        assert_eq!(event.decode_error(), Some("bad packet"));
313        assert!(event.peer_id().is_none());
314        assert!(event.primary_addr().is_none());
315    }
316
317    #[test]
318    fn proxy_drop_maps_to_common_kind_and_peer() {
319        let peer_id = PeerId::from_u64(99);
320        let event = RaknetEvent::from(RaknetRelayProxyEvent::Dropped {
321            peer_id,
322            direction: RelayDirection::DownstreamToUpstream,
323            reason: RelayDropReason::NoSession,
324        });
325
326        assert_eq!(event.source(), RaknetEventSource::Proxy);
327        assert_eq!(event.kind(), RaknetEventKind::Dropped);
328        assert_eq!(event.peer_id(), Some(peer_id));
329        assert!(event.primary_addr().is_none());
330    }
331
332    #[test]
333    fn server_metrics_can_be_extracted() {
334        let snapshot = TransportMetricsSnapshot {
335            session_count: 3,
336            ..TransportMetricsSnapshot::default()
337        };
338
339        let event = RaknetEvent::from(RaknetServerEvent::Metrics {
340            shard_id: 2,
341            snapshot: Box::new(snapshot),
342            dropped_non_critical_events: 17,
343        });
344
345        let (shard_id, extracted_snapshot, dropped) =
346            event.metrics_snapshot().expect("metrics must be present");
347        assert_eq!(shard_id, 2);
348        assert_eq!(dropped, 17);
349        assert_eq!(extracted_snapshot.session_count, 3);
350    }
351
352    #[test]
353    fn disconnected_kind_is_normalized_across_sources() {
354        let server_peer_id = PeerId::from_u64(1);
355        let server_addr = "127.0.0.1:19132".parse().expect("valid socket addr");
356        let server_event = RaknetEvent::from(RaknetServerEvent::PeerDisconnected {
357            peer_id: server_peer_id,
358            addr: server_addr,
359            reason: PeerDisconnectReason::Requested,
360        });
361        assert_eq!(server_event.kind(), RaknetEventKind::Disconnected);
362
363        let client_event = RaknetEvent::from(RaknetClientEvent::Disconnected {
364            reason: crate::client::ClientDisconnectReason::Requested,
365        });
366        assert_eq!(client_event.kind(), RaknetEventKind::Disconnected);
367
368        let proxy_event = RaknetEvent::from(RaknetRelayProxyEvent::SessionClosed {
369            peer_id: server_peer_id,
370            reason: RelaySessionCloseReason::ProxyShutdown,
371        });
372        assert_eq!(proxy_event.kind(), RaknetEventKind::Disconnected);
373    }
374
375    #[test]
376    fn server_offline_packet_maps_to_common_fields() {
377        let addr = "127.0.0.1:19132".parse().expect("valid socket addr");
378        let event = RaknetEvent::from(RaknetServerEvent::OfflinePacket {
379            addr,
380            packet: OfflinePacket::UnconnectedPing(UnconnectedPing {
381                ping_time: 123,
382                client_guid: 456,
383                magic: DEFAULT_UNCONNECTED_MAGIC,
384            }),
385        });
386
387        assert_eq!(event.source(), RaknetEventSource::Server);
388        assert_eq!(event.kind(), RaknetEventKind::OfflinePacket);
389        assert_eq!(event.primary_addr(), Some(addr));
390        assert!(event.peer_id().is_none());
391        assert!(event.payload_len().is_none());
392    }
393}