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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
//! An older networking solution that is now deprecated.
//!
//! In the future you should use [`networking_sockets`][../networking_sockets], but for now the wrapper for the new API
//! is still unfinished.
use super::*;
/// Access to the steam networking interface
pub struct Networking {
pub(crate) net: *mut sys::ISteamNetworking,
pub(crate) _inner: Arc<Inner>,
}
/// The method used to send a packet
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum SendType {
/// Send the packet directly over udp.
///
/// Can't be larger than 1200 bytes
Unreliable,
/// Like `Unreliable` but doesn't buffer packets
/// sent before the connection has started.
UnreliableNoDelay,
/// Reliable packet sending.
///
/// Can't be larger than 1 megabyte.
Reliable,
/// Like `Reliable` but applies the nagle
/// algorithm to packets being sent
ReliableWithBuffering,
}
/// P2P session error codes
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum P2PSessionError {
/// No error
None = 0,
#[deprecated(
note = "For privacy reasons, there is no error if the remote user is playing another game."
)]
NotRunningApp = 1,
/// Local user doesn't own the app that is running
NoRightsToApp = 2,
#[deprecated(note = "For privacy reasons, there is no error if the remote user is offline")]
NotLoggedIn = 3,
/// Target isn't responding, perhaps not calling AcceptP2PSessionWithUser()
/// This may also occur behind corporate firewalls (P2P sessions require UDP ports 3478, 4379, and 4380 to be open for outgoing traffic)
Timeout = 4,
/// Unknown error code
Unknown(u8),
}
impl From<u8> for P2PSessionError {
fn from(value: u8) -> Self {
#[allow(deprecated)]
match value {
0 => P2PSessionError::None,
1 => P2PSessionError::NotRunningApp,
2 => P2PSessionError::NoRightsToApp,
3 => P2PSessionError::NotLoggedIn,
4 => P2PSessionError::Timeout,
other => P2PSessionError::Unknown(other),
}
}
}
impl From<P2PSessionError> for u8 {
fn from(error: P2PSessionError) -> Self {
#[allow(deprecated)]
match error {
P2PSessionError::None => 0,
P2PSessionError::NotRunningApp => 1,
P2PSessionError::NoRightsToApp => 2,
P2PSessionError::NotLoggedIn => 3,
P2PSessionError::Timeout => 4,
P2PSessionError::Unknown(code) => code,
}
}
}
/// Information about a P2P session with a remote user
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct P2PSessionState {
/// Whether we've got an active open connection
pub connection_active: bool,
/// Whether we're currently trying to establish a connection
pub connecting: bool,
/// Last error recorded
pub error: P2PSessionError,
/// Whether it's going through a Steam relay server
pub using_relay: bool,
/// Number of bytes queued for sending
pub bytes_queued_for_send: i32,
/// Number of packets queued for sending
pub packets_queued_for_send: i32,
/// Potential IP address of remote host (could be Steam relay server)
pub remote_ip: Option<std::net::Ipv4Addr>,
/// Remote port number
pub remote_port: Option<u16>,
}
impl Networking {
/// Accepts incoming packets from the given user
///
/// Should only be called in response to a `P2PSessionRequest`.
pub fn accept_p2p_session(&self, user: SteamId) -> bool {
unsafe { sys::SteamAPI_ISteamNetworking_AcceptP2PSessionWithUser(self.net, user.0) }
}
/// Closes the p2p connection between the given user
pub fn close_p2p_session(&self, user: SteamId) -> bool {
unsafe { sys::SteamAPI_ISteamNetworking_CloseP2PSessionWithUser(self.net, user.0) }
}
/// Gets the connection state to the specified user
///
/// Returns the P2P session state if a connection exists with the user,
/// or None if no connection exists.
pub fn get_p2p_session_state(&self, user: SteamId) -> Option<P2PSessionState> {
unsafe {
let mut state: sys::P2PSessionState_t = std::mem::zeroed();
if sys::SteamAPI_ISteamNetworking_GetP2PSessionState(self.net, user.0, &mut state) {
Some(P2PSessionState {
connection_active: state.m_bConnectionActive != 0,
connecting: state.m_bConnecting != 0,
error: state.m_eP2PSessionError.into(),
using_relay: state.m_bUsingRelay != 0,
bytes_queued_for_send: state.m_nBytesQueuedForSend,
packets_queued_for_send: state.m_nPacketsQueuedForSend,
remote_ip: (state.m_nRemoteIP != 0).then_some(state.m_nRemoteIP.into()),
remote_port: (state.m_nRemotePort != 0).then_some(state.m_nRemotePort),
})
} else {
None
}
}
}
/// Sends a packet to the user, starting the
/// connection if it isn't started already
pub fn send_p2p_packet(&self, remote: SteamId, send_type: SendType, data: &[u8]) -> bool {
self.send_p2p_packet_on_channel(remote, send_type, data, 0)
}
/// Sends a packet to the user on a specific channel
pub fn send_p2p_packet_on_channel(
&self,
remote: SteamId,
send_type: SendType,
data: &[u8],
channel: i32,
) -> bool {
unsafe {
let send_type = match send_type {
SendType::Unreliable => sys::EP2PSend::k_EP2PSendUnreliable,
SendType::UnreliableNoDelay => sys::EP2PSend::k_EP2PSendUnreliableNoDelay,
SendType::Reliable => sys::EP2PSend::k_EP2PSendReliable,
SendType::ReliableWithBuffering => sys::EP2PSend::k_EP2PSendReliableWithBuffering,
};
sys::SteamAPI_ISteamNetworking_SendP2PPacket(
self.net,
remote.0,
data.as_ptr().cast(),
data.len() as u32,
send_type,
channel,
)
}
}
/// Returns whether there is a packet queued that can be read.
///
/// Returns the size of the queued packet if any.
pub fn is_p2p_packet_available(&self) -> Option<usize> {
self.is_p2p_packet_available_on_channel(0)
}
/// Returns whether there is a packet available on a specific channel
pub fn is_p2p_packet_available_on_channel(&self, channel: i32) -> Option<usize> {
unsafe {
let mut size = 0;
if sys::SteamAPI_ISteamNetworking_IsP2PPacketAvailable(self.net, &mut size, channel) {
Some(size as usize)
} else {
None
}
}
}
/// Attempts to read a queued packet into the buffer
/// if there are any.
///
/// Returns the steam id of the sender and the size of the
/// packet.
pub fn read_p2p_packet(&self, buf: &mut [u8]) -> Option<(SteamId, usize)> {
self.read_p2p_packet_from_channel(buf, 0)
}
/// Attempts to read a queued packet into the buffer
/// from a specific channel
pub fn read_p2p_packet_from_channel(
&self,
buf: &mut [u8],
channel: i32,
) -> Option<(SteamId, usize)> {
unsafe {
let mut size = 0;
let mut remote = 0;
if sys::SteamAPI_ISteamNetworking_ReadP2PPacket(
self.net,
buf.as_mut_ptr().cast(),
buf.len() as _,
&mut size,
&mut remote as *mut _ as *mut _,
channel,
) {
Some((SteamId(remote), size as usize))
} else {
None
}
}
}
}
/// Called when a user wants to communicate via p2p
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct P2PSessionRequest {
/// The steam ID of the user requesting a p2p
/// session
pub remote: SteamId,
}
impl_callback!(cb: P2PSessionRequest_t => P2PSessionRequest {
Self {
remote: SteamId(cb.m_steamIDRemote.m_steamid.m_unAll64Bits),
}
});
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct P2PSessionConnectFail {
pub remote: SteamId,
pub error: u8,
}
impl_callback!(cb: P2PSessionConnectFail_t => P2PSessionConnectFail {
Self {
remote: SteamId(cb.m_steamIDRemote.m_steamid.m_unAll64Bits),
error: cb.m_eP2PSessionError,
}
});