steamkit/base/
client_msg.rs1use crate::base::generated::steam_language::EMsg;
4use crate::base::generated::steam_language_internal::{
5 ExtendedClientMsgHdr, ISteamSerializableHeader, ISteamSerializableMessage, MsgHdr,
6 MsgHdrProtoBuf, SteamSerializable, SteamSerializableDeserialize,
7};
8use crate::base::msg_base::{MsgBase, Reader};
9use crate::base::packet_base::IPacketMsg;
10use crate::error::SteamError;
11use crate::types::job_id::JobID;
12use crate::types::steam_id::SteamID;
13use steamkit_protos::steam::CMsgProtoBufHeader;
14
15pub trait IClientMsg: Send + Sync {
17 fn is_proto(&self) -> bool;
19
20 fn msg_type(&self) -> EMsg;
22
23 fn session_id(&self) -> i32;
25
26 fn set_session_id(&mut self, id: i32);
28
29 fn steam_id(&self) -> Option<SteamID>;
31
32 fn set_steam_id(&mut self, id: SteamID);
34
35 fn target_job_id(&self) -> JobID;
37
38 fn set_target_job_id(&mut self, id: JobID);
40
41 fn source_job_id(&self) -> JobID;
43
44 fn set_source_job_id(&mut self, id: JobID);
46
47 fn serialize(&self) -> Vec<u8>;
49}
50
51pub trait SteamIDRaw {
54 fn raw_u64(&self) -> u64;
56}
57
58impl SteamIDRaw for SteamID {
59 fn raw_u64(&self) -> u64 {
60 self.value()
61 }
62}
63
64#[derive(Clone, Copy, PartialEq, Eq, ::prost::Message)]
69pub struct SteamProtobufHeaderOnly {}
70
71pub struct ClientMsgProtobuf<TBody: prost::Message + Default = SteamProtobufHeaderOnly> {
73 pub header: MsgHdrProtoBuf,
75 pub body: TBody,
77 pub payload: MsgBase,
79}
80
81impl<TBody> ClientMsgProtobuf<TBody>
82where
83 TBody: prost::Message + Default,
84{
85 pub fn new(e_msg: EMsg) -> Self {
87 Self::with_payload_reserve(e_msg, 0)
88 }
89
90 pub fn with_payload_reserve(e_msg: EMsg, payload_reserve: usize) -> Self {
92 let mut header = MsgHdrProtoBuf::default();
93 header.msg = e_msg;
94 ClientMsgProtobuf {
95 header,
96 body: TBody::default(),
97 payload: MsgBase::new(payload_reserve),
98 }
99 }
100
101 pub fn reply(e_msg: EMsg, reply_to: &dyn IClientMsg) -> Self {
103 let mut msg = Self::new(e_msg);
104 msg.set_target_job_id(reply_to.source_job_id());
105 msg
106 }
107
108 pub fn from_packet(msg: &dyn IPacketMsg) -> Result<Self, SteamError> {
110 if !msg.is_proto() {
111 return Err(SteamError::InvalidArgument(
112 "ClientMsgProtobuf used for non-proto message!".to_string(),
113 ));
114 }
115
116 let data = msg.get_data();
117 let mut reader = Reader::new(data);
118 let header = MsgHdrProtoBuf::read(&mut reader)?;
119 let body_offset = reader.position();
120
121 let mut body_bytes = data.get(body_offset..).ok_or_else(|| {
122 SteamError::Malformed("protobuf body offset is outside of the packet".to_string())
123 })?;
124 let body = TBody::decode(&mut body_bytes)?;
125
126 let mut payload = MsgBase::new(0);
127 if !body_bytes.is_empty() {
128 payload.write_bytes(body_bytes);
129 payload.seek(0);
130 }
131
132 Ok(ClientMsgProtobuf {
133 header,
134 body,
135 payload,
136 })
137 }
138
139 pub fn body(&self) -> &TBody {
141 &self.body
142 }
143
144 pub fn body_mut(&mut self) -> &mut TBody {
146 &mut self.body
147 }
148
149 pub fn set_body(&mut self, body: TBody) {
151 self.body = body;
152 }
153
154 pub fn header(&self) -> &MsgHdrProtoBuf {
156 &self.header
157 }
158
159 pub fn header_mut(&mut self) -> &mut MsgHdrProtoBuf {
161 &mut self.header
162 }
163
164 pub fn proto_header(&self) -> &CMsgProtoBufHeader {
166 &self.header.proto
167 }
168
169 pub fn proto_header_mut(&mut self) -> &mut CMsgProtoBufHeader {
171 &mut self.header.proto
172 }
173
174 pub fn payload(&self) -> &MsgBase {
176 &self.payload
177 }
178
179 pub fn payload_mut(&mut self) -> &mut MsgBase {
181 &mut self.payload
182 }
183}
184
185impl<TBody> IClientMsg for ClientMsgProtobuf<TBody>
186where
187 TBody: prost::Message + Default,
188{
189 fn is_proto(&self) -> bool {
190 true
191 }
192
193 fn msg_type(&self) -> EMsg {
194 self.header.msg
195 }
196
197 fn session_id(&self) -> i32 {
198 self.header.proto.client_sessionid.unwrap_or(0)
199 }
200
201 fn set_session_id(&mut self, id: i32) {
202 self.header.proto.client_sessionid = Some(id);
203 }
204
205 fn steam_id(&self) -> Option<SteamID> {
206 Some(SteamID::from_id(self.header.proto.steamid.unwrap_or(0)))
207 }
208
209 fn set_steam_id(&mut self, id: SteamID) {
210 self.header.proto.steamid = Some(id.raw_u64());
211 }
212
213 fn target_job_id(&self) -> JobID {
214 JobID::from_value(self.header.proto.jobid_target.unwrap_or(u64::MAX))
215 }
216
217 fn set_target_job_id(&mut self, id: JobID) {
218 self.header.proto.jobid_target = Some(id.value());
219 }
220
221 fn source_job_id(&self) -> JobID {
222 JobID::from_value(self.header.proto.jobid_source.unwrap_or(u64::MAX))
223 }
224
225 fn set_source_job_id(&mut self, id: JobID) {
226 self.header.proto.jobid_source = Some(id.value());
227 }
228
229 fn serialize(&self) -> Vec<u8> {
230 let mut out = Vec::new();
231 self.header.write(&mut out);
232 out.extend_from_slice(&self.body.encode_to_vec());
233 out.extend_from_slice(&self.payload.payload);
234 out
235 }
236}
237
238pub struct ClientMsg<TBody>
240where
241 TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
242{
243 pub header: ExtendedClientMsgHdr,
245 pub body: TBody,
247 pub payload: MsgBase,
249}
250
251impl<TBody> ClientMsg<TBody>
252where
253 TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
254{
255 pub fn new(e_msg: EMsg) -> Self {
257 Self::with_payload_reserve(e_msg, 0)
258 }
259
260 pub fn with_payload_reserve(e_msg: EMsg, payload_reserve: usize) -> Self {
262 let mut header = ExtendedClientMsgHdr::default();
263 header.set_emsg(e_msg);
264 ClientMsg {
265 header,
266 body: TBody::default(),
267 payload: MsgBase::new(payload_reserve),
268 }
269 }
270
271 pub fn reply(e_msg: EMsg, reply_to: &dyn IClientMsg) -> Self {
273 let mut msg = Self::new(e_msg);
274 msg.set_target_job_id(reply_to.source_job_id());
275 msg
276 }
277
278 pub fn from_packet(msg: &dyn IPacketMsg) -> Result<Self, SteamError> {
280 let data = msg.get_data();
281 let mut reader = Reader::new(data);
282 let header = ExtendedClientMsgHdr::read(&mut reader)?;
283 let body = TBody::read(&mut reader)?;
284
285 let mut payload = MsgBase::new(0);
286 let remaining = reader.read_up_to(reader.remaining());
287 if !remaining.is_empty() {
288 payload.write_bytes(&remaining);
289 payload.seek(0);
290 }
291
292 Ok(ClientMsg {
293 header,
294 body,
295 payload,
296 })
297 }
298
299 pub fn body(&self) -> &TBody {
301 &self.body
302 }
303
304 pub fn body_mut(&mut self) -> &mut TBody {
306 &mut self.body
307 }
308
309 pub fn header(&self) -> &ExtendedClientMsgHdr {
311 &self.header
312 }
313
314 pub fn header_mut(&mut self) -> &mut ExtendedClientMsgHdr {
316 &mut self.header
317 }
318
319 pub fn payload(&self) -> &MsgBase {
321 &self.payload
322 }
323
324 pub fn payload_mut(&mut self) -> &mut MsgBase {
326 &mut self.payload
327 }
328}
329
330impl<TBody> IClientMsg for ClientMsg<TBody>
331where
332 TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
333{
334 fn is_proto(&self) -> bool {
335 false
336 }
337
338 fn msg_type(&self) -> EMsg {
339 self.header.msg
340 }
341
342 fn session_id(&self) -> i32 {
343 self.header.session_id
344 }
345
346 fn set_session_id(&mut self, id: i32) {
347 self.header.session_id = id;
348 }
349
350 fn steam_id(&self) -> Option<SteamID> {
351 Some(SteamID::from_id(self.header.steam_id))
352 }
353
354 fn set_steam_id(&mut self, id: SteamID) {
355 self.header.steam_id = id.raw_u64();
356 }
357
358 fn target_job_id(&self) -> JobID {
359 JobID::from_value(self.header.target_job_id)
360 }
361
362 fn set_target_job_id(&mut self, id: JobID) {
363 self.header.target_job_id = id.value();
364 }
365
366 fn source_job_id(&self) -> JobID {
367 JobID::from_value(self.header.source_job_id)
368 }
369
370 fn set_source_job_id(&mut self, id: JobID) {
371 self.header.source_job_id = id.value();
372 }
373
374 fn serialize(&self) -> Vec<u8> {
375 let mut out = Vec::new();
376 self.header.write(&mut out);
377 self.body.write(&mut out);
378 out.extend_from_slice(&self.payload.payload);
379 out
380 }
381}
382
383pub struct Msg<TBody>
385where
386 TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
387{
388 pub header: MsgHdr,
390 pub body: TBody,
392 pub payload: MsgBase,
394 pub session_id: i32,
396 pub steam_id: Option<SteamID>,
398}
399
400impl<TBody> Msg<TBody>
401where
402 TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
403{
404 pub fn new(e_msg: EMsg) -> Self {
406 Self::with_payload_reserve(e_msg, 0)
407 }
408
409 pub fn with_payload_reserve(e_msg: EMsg, payload_reserve: usize) -> Self {
411 let mut header = MsgHdr::default();
412 header.set_emsg(e_msg);
413 Msg {
414 header,
415 body: TBody::default(),
416 payload: MsgBase::new(payload_reserve),
417 session_id: 0,
418 steam_id: None,
419 }
420 }
421
422 pub fn reply(e_msg: EMsg, reply_to: &dyn IClientMsg) -> Self {
424 let mut msg = Self::new(e_msg);
425 msg.set_target_job_id(reply_to.source_job_id());
426 msg
427 }
428
429 pub fn from_packet(msg: &dyn IPacketMsg) -> Result<Self, SteamError> {
431 let data = msg.get_data();
432 let mut reader = Reader::new(data);
433 let header = MsgHdr::read(&mut reader)?;
434 let body = TBody::read(&mut reader)?;
435
436 let mut payload = MsgBase::new(0);
437 let remaining = reader.read_up_to(reader.remaining());
438 if !remaining.is_empty() {
439 payload.write_bytes(&remaining);
440 payload.seek(0);
441 }
442
443 Ok(Msg {
444 header,
445 body,
446 payload,
447 session_id: 0,
448 steam_id: None,
449 })
450 }
451
452 pub fn body(&self) -> &TBody {
454 &self.body
455 }
456
457 pub fn body_mut(&mut self) -> &mut TBody {
459 &mut self.body
460 }
461
462 pub fn header(&self) -> &MsgHdr {
464 &self.header
465 }
466
467 pub fn header_mut(&mut self) -> &mut MsgHdr {
469 &mut self.header
470 }
471
472 pub fn payload(&self) -> &MsgBase {
474 &self.payload
475 }
476
477 pub fn payload_mut(&mut self) -> &mut MsgBase {
479 &mut self.payload
480 }
481}
482
483impl<TBody> IClientMsg for Msg<TBody>
484where
485 TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
486{
487 fn is_proto(&self) -> bool {
488 false
489 }
490
491 fn msg_type(&self) -> EMsg {
492 self.header.msg
493 }
494
495 fn session_id(&self) -> i32 {
496 self.session_id
497 }
498
499 fn set_session_id(&mut self, id: i32) {
500 self.session_id = id;
501 }
502
503 fn steam_id(&self) -> Option<SteamID> {
504 self.steam_id
505 }
506
507 fn set_steam_id(&mut self, id: SteamID) {
508 self.steam_id = Some(id);
509 }
510
511 fn target_job_id(&self) -> JobID {
512 JobID::from_value(self.header.target_job_id)
513 }
514
515 fn set_target_job_id(&mut self, id: JobID) {
516 self.header.target_job_id = id.value();
517 }
518
519 fn source_job_id(&self) -> JobID {
520 JobID::from_value(self.header.source_job_id)
521 }
522
523 fn set_source_job_id(&mut self, id: JobID) {
524 self.header.source_job_id = id.value();
525 }
526
527 fn serialize(&self) -> Vec<u8> {
528 let mut out = Vec::new();
529 self.header.write(&mut out);
530 self.body.write(&mut out);
531 out.extend_from_slice(&self.payload.payload);
532 out
533 }
534}