soe_protocol/lib.rs
1//! A Rust implementation of version 3 of the SOE (Sony Online Entertainment) network protocol.
2//!
3//! The SOE protocol is a UDP transport layer used by various games (Free Realms, H1Z1,
4//! Landmark, PlanetSide 2, etc.). It provides sessions, packet verification (CRC32),
5//! optional compression (zlib), reliable/ordered data transmission, and optional
6//! encryption (RC4).
7//!
8//! # Design
9//!
10//! This crate is structured as an **I/O-agnostic core**: the protocol logic is a pure state
11//! machine that never touches the network or the clock directly. [`SoeSession`] (a
12//! single connection) and [`SoeMultiplexer`] (many connections demultiplexed by remote
13//! address) consume incoming datagrams and the current [`Instant`](std::time::Instant),
14//! and produce outgoing datagrams plus [`SocketEvent`]s. You drive them from whatever
15//! runtime you like.
16//!
17//! Ready-made adapters are provided over that core:
18//!
19//! * [`SyncSoeSocket`] — a blocking, dependency-free driver over [`std::net::UdpSocket`].
20//! * `TokioSoeSocket` / `TokioSoeServer` — async drivers behind the `tokio` feature.
21//!
22//! See the `examples/` directory for runnable client/server pairs in both styles.
23//!
24//! # Cargo features
25//!
26//! * `tokio` *(off by default)* — enables the Tokio adapters (`TokioSoeSocket`,
27//! `TokioSoeServer`, `SoeHandle`). With default features the crate has no async
28//! runtime dependency.
29//!
30//! # Quick start
31//!
32//! A minimal synchronous client that connects, sends one message, and prints replies:
33//!
34//! ```no_run
35//! use std::net::SocketAddr;
36//! use std::time::Duration;
37//!
38//! use soe_protocol::SessionParameters;
39//! use soe_protocol::socket::{SocketConfig, SocketEvent, SoeSocket};
40//! use soe_protocol::sync_rt::SyncSoeSocket;
41//!
42//! # fn main() -> std::io::Result<()> {
43//! let server: SocketAddr = "127.0.0.1:20260".parse().unwrap();
44//!
45//! // Both peers must agree on the application-protocol string.
46//! let config = SocketConfig {
47//! default_session_params: SessionParameters {
48//! application_protocol: "MyGame".to_owned(),
49//! ..SessionParameters::default()
50//! },
51//! ..SocketConfig::default()
52//! };
53//!
54//! let mut socket = SyncSoeSocket::bind(
55//! "127.0.0.1:0".parse().unwrap(),
56//! config,
57//! Duration::from_millis(5),
58//! )?;
59//! socket.connect(server);
60//!
61//! loop {
62//! for event in socket.step()? {
63//! match event {
64//! SocketEvent::SessionOpened { remote } => {
65//! let _ = socket.enqueue_data(&remote, b"hello");
66//! }
67//! SocketEvent::DataReceived { data, .. } => {
68//! println!("received {} bytes", data.len());
69//! }
70//! SocketEvent::SessionClosed { .. } => return Ok(()),
71//! }
72//! }
73//! }
74//! # }
75//! ```
76
77#![warn(missing_docs)]
78
79pub mod channel;
80pub mod constants;
81pub(crate) mod crc32;
82pub mod error;
83pub(crate) mod io;
84pub(crate) mod packet_utils;
85pub mod packets;
86pub mod protocol;
87pub(crate) mod rc4;
88pub mod session;
89pub mod socket;
90pub mod sync_rt;
91#[cfg(feature = "tokio")]
92pub mod tokio_rt;
93pub(crate) mod varint;
94pub(crate) mod zlib;
95
96pub use error::{Error, Result};
97pub use protocol::{DisconnectReason, OpCode};
98pub use rc4::Rc4KeyState;
99pub use session::{
100 ApplicationParameters, SessionEvent, SessionMode, SessionParameters, SessionState, SoeSession,
101};
102pub use socket::{RemoteAddr, SocketConfig, SocketEvent, SoeMultiplexer, SoeSocket, UdpTransport};
103pub use sync_rt::SyncSoeSocket;
104#[cfg(feature = "tokio")]
105pub use tokio_rt::{SoeHandle, TokioSoeServer, TokioSoeSocket};