1use prost::Message as ProstMessage;
24
25use crate::base::generated::steam_language::internal as generated;
26use crate::base::generated::steam_language::{
27 EMsg, EAppUsageEvent, EChatAction, EChatActionResult, EChatEntryType, EChatInfoType,
28 EChatPermission, EChatRoomEnterResponse, EChatRoomType, EClanRank, EClanRelationship,
29 EDenyReason, EIntroducerRouting, EResult, EServerType, EUdpPacketType, EUniverse,
30};
31use crate::base::msg_base::Reader;
32use crate::error::SteamError;
33use steamkit_protos::gc::CMsgProtoBufHeader as GcCMsgProtoBufHeader;
34use steamkit_protos::steam::CMsgProtoBufHeader;
35
36pub use crate::base::generated::steam_language::internal::{
37 Accept, ChallengeData, ConnectData, Datagram, Disconnect, ExtendedClientMsgHdr,
38 MsgChannelEncryptRequest, MsgChannelEncryptResponse, MsgChannelEncryptResult,
39 MsgClientAppUsageEvent, MsgClientChatAction, MsgClientChatActionResult, MsgClientChatEnter,
40 MsgClientChatMemberInfo, MsgClientChatMsg, MsgClientChatRoomInfo, MsgClientCreateChat,
41 MsgClientCreateChatResponse, MsgClientGenericResponse, MsgClientGetFriendsWhoPlayGame,
42 MsgClientGetFriendsWhoPlayGameResponse, MsgClientGetLegacyGameKey,
43 MsgClientGetLegacyGameKeyResponse, MsgClientJoinChat, MsgClientJustStrings,
44 MsgClientLogOnResponse,
45 MsgClientLoggedOff, MsgClientLogon, MsgClientMarketingMessageUpdate2,
46 MsgClientOGSBeginSession, MsgClientOGSBeginSessionResponse, MsgClientOGSEndSession,
47 MsgClientOGSEndSessionResponse, MsgClientOGSWriteRow, MsgClientP2PIntroducerMessage,
48 MsgClientServerUnavailable, MsgClientSetIgnoreFriend, MsgClientSetIgnoreFriendResponse,
49 MsgClientUpdateGuestPassesList, MsgClientVACBanStatus, MsgGCHdr, MsgGSDeny, MsgGSApprove,
50 MsgGSGetPlayStatsResponse, MsgGSGetReputationResponse, MsgGSGetUserGroupStatus,
51 MsgGSGetUserGroupStatusResponse, MsgGSKick, MsgGSPerformHardwareSurvey, MsgHdr, UdpHeader,
52};
53
54pub trait SteamSerializable: Send + Sync {
56 fn write(&self, out: &mut Vec<u8>);
58}
59
60pub trait SteamSerializableDeserialize: SteamSerializable + Default + Sized {
62 fn read(r: &mut Reader<'_>) -> Result<Self, SteamError>;
64}
65
66pub trait ISteamSerializable: SteamSerializableDeserialize {}
68
69impl<T> ISteamSerializable for T where T: SteamSerializableDeserialize {}
70
71pub trait ISteamSerializableHeader: ISteamSerializable {
73 fn set_emsg(&mut self, msg: EMsg);
75}
76
77pub trait ISteamSerializableMessage: ISteamSerializable {
79 fn get_emsg(&self) -> EMsg;
81}
82
83pub trait IGCSerializableHeader: ISteamSerializable {
85 fn set_gc_emsg(&mut self, msg: u32);
87}
88
89pub trait IGCSerializableMessage: ISteamSerializable {
91 fn get_gc_emsg(&self) -> u32;
93}
94
95pub use ISteamSerializableHeader as SteamSerializableHeader;
97pub use ISteamSerializableMessage as SteamSerializableMessage;
99pub use IGCSerializableHeader as GcSerializableHeader;
101pub use IGCSerializableMessage as GcSerializableMessage;
103
104pub trait WireEnum: Copy {
106 fn wire_value(self) -> i32;
108 fn from_wire_value(value: i32) -> Option<Self>;
110 fn wire_size() -> usize {
112 4
113 }
114}
115
116pub trait WireValue: Sized {
126 fn write_wire(&self, out: &mut Vec<u8>);
128 fn read_wire(r: &mut Reader<'_>) -> Result<Self, SteamError>;
130}
131
132macro_rules! impl_wire_value {
133 ( $( $ty:ty ),* $(,)? ) => {
134 $(
135 impl WireValue for $ty {
136 fn write_wire(&self, out: &mut Vec<u8>) {
137 let raw = self.wire_value();
138 if Self::wire_size() == 1 {
139 out.push(raw as u8);
140 } else {
141 out.extend_from_slice(&raw.to_le_bytes());
142 }
143 }
144
145 fn read_wire(r: &mut Reader<'_>) -> Result<Self, SteamError> {
146 let raw = if Self::wire_size() == 1 {
147 i32::from(r.read_u8()?)
148 } else {
149 r.read_i32()?
150 };
151 Self::from_wire_value(raw)
152 .ok_or_else(|| SteamError::Malformed(format!("unknown enum value {raw}")))
153 }
154 }
155 )*
156 };
157}
158
159macro_rules! impl_wire_enum {
160 ( $( $ty:ty ),* $(,)? ) => {
161 $(
162 impl WireEnum for $ty {
163 fn wire_value(self) -> i32 {
164 self.value()
165 }
166
167 fn from_wire_value(value: i32) -> Option<Self> {
168 Self::from_value(value)
169 }
170 }
171 )*
172 };
173}
174
175impl_wire_enum!(
176 EMsg,
177 EAppUsageEvent,
178 EChatAction,
179 EChatActionResult,
180 EChatEntryType,
181 EChatInfoType,
182 EChatPermission,
183 EChatRoomEnterResponse,
184 EChatRoomType,
185 EClanRank,
186 EClanRelationship,
187 EDenyReason,
188 EIntroducerRouting,
189 EResult,
190 EServerType,
191 EUniverse,
192);
193
194impl WireEnum for EUdpPacketType {
195 fn wire_value(self) -> i32 {
196 self.value() as i32
197 }
198
199 fn from_wire_value(value: i32) -> Option<Self> {
200 u8::try_from(value).ok().and_then(EUdpPacketType::from_value)
201 }
202
203 fn wire_size() -> usize {
205 1
206 }
207}
208
209impl_wire_value!(
210 EMsg,
211 EAppUsageEvent,
212 EChatAction,
213 EChatActionResult,
214 EChatEntryType,
215 EChatInfoType,
216 EChatPermission,
217 EChatRoomEnterResponse,
218 EChatRoomType,
219 EClanRank,
220 EClanRelationship,
221 EDenyReason,
222 EIntroducerRouting,
223 EResult,
224 EServerType,
225 EUniverse,
226 EUdpPacketType,
227);
228
229macro_rules! impl_wire_value_primitive {
230 ( $( $ty:ident : $write:ident , $read:ident );* $(;)? ) => {
231 $(
232 impl WireValue for $ty {
233 fn write_wire(&self, out: &mut Vec<u8>) {
234 $write(self, out);
235 }
236
237 fn read_wire(r: &mut Reader<'_>) -> Result<Self, SteamError> {
238 $read(r)
239 }
240 }
241 )*
242 };
243}
244
245fn write_u8(v: &u8, out: &mut Vec<u8>) {
246 out.push(*v);
247}
248fn write_i8(v: &i8, out: &mut Vec<u8>) {
249 out.push(*v as u8);
250}
251fn write_u16(v: &u16, out: &mut Vec<u8>) {
252 out.extend_from_slice(&v.to_le_bytes());
253}
254fn write_i16(v: &i16, out: &mut Vec<u8>) {
255 out.extend_from_slice(&v.to_le_bytes());
256}
257fn write_u32(v: &u32, out: &mut Vec<u8>) {
258 out.extend_from_slice(&v.to_le_bytes());
259}
260fn write_i32(v: &i32, out: &mut Vec<u8>) {
261 out.extend_from_slice(&v.to_le_bytes());
262}
263fn write_u64(v: &u64, out: &mut Vec<u8>) {
264 out.extend_from_slice(&v.to_le_bytes());
265}
266fn write_i64(v: &i64, out: &mut Vec<u8>) {
267 out.extend_from_slice(&v.to_le_bytes());
268}
269fn write_f32(v: &f32, out: &mut Vec<u8>) {
270 out.extend_from_slice(&v.to_le_bytes());
271}
272fn write_f64(v: &f64, out: &mut Vec<u8>) {
273 out.extend_from_slice(&v.to_le_bytes());
274}
275fn write_bool(v: &bool, out: &mut Vec<u8>) {
276 out.push(if *v { 1u8 } else { 0u8 });
277}
278
279fn read_u8(r: &mut Reader<'_>) -> Result<u8, SteamError> {
280 r.read_u8()
281}
282fn read_i8(r: &mut Reader<'_>) -> Result<i8, SteamError> {
283 r.read_i8()
284}
285fn read_u16(r: &mut Reader<'_>) -> Result<u16, SteamError> {
286 r.read_u16()
287}
288fn read_i16(r: &mut Reader<'_>) -> Result<i16, SteamError> {
289 r.read_i16()
290}
291fn read_u32(r: &mut Reader<'_>) -> Result<u32, SteamError> {
292 r.read_u32()
293}
294fn read_i32(r: &mut Reader<'_>) -> Result<i32, SteamError> {
295 r.read_i32()
296}
297fn read_u64(r: &mut Reader<'_>) -> Result<u64, SteamError> {
298 r.read_u64()
299}
300fn read_i64(r: &mut Reader<'_>) -> Result<i64, SteamError> {
301 r.read_i64()
302}
303fn read_f32(r: &mut Reader<'_>) -> Result<f32, SteamError> {
304 r.read_f32()
305}
306fn read_f64(r: &mut Reader<'_>) -> Result<f64, SteamError> {
307 r.read_f64()
308}
309fn read_bool(r: &mut Reader<'_>) -> Result<bool, SteamError> {
314 Ok(r.read_u8()? == 1)
315}
316
317impl_wire_value_primitive!(
318 u8 : write_u8 , read_u8;
319 i8 : write_i8 , read_i8;
320 u16 : write_u16 , read_u16;
321 i16 : write_i16 , read_i16;
322 u32 : write_u32 , read_u32;
323 i32 : write_i32 , read_i32;
324 u64 : write_u64 , read_u64;
325 i64 : write_i64 , read_i64;
326 f32 : write_f32 , read_f32;
327 f64 : write_f64 , read_f64;
328 bool : write_bool , read_bool;
329);
330
331pub fn read_enum<T: WireEnum>(r: &mut Reader<'_>) -> Result<T, SteamError> {
336 let raw = r.read_i32()?;
337 T::from_wire_value(raw).ok_or_else(|| SteamError::Malformed(format!("unknown enum value {raw}")))
338}
339
340const PROTO_MASK: u32 = 0x8000_0000;
342
343pub fn get_msg(msg: u32) -> Option<EMsg> {
345 EMsg::from_value((msg & !PROTO_MASK) as i32)
346}
347
348pub fn get_gc_msg(msg: u32) -> u32 {
350 msg & !PROTO_MASK
351}
352
353pub fn make_msg(msg: EMsg, protobuf: bool) -> u32 {
355 let raw = msg.value() as u32;
356 if protobuf {
357 raw | PROTO_MASK
358 } else {
359 raw
360 }
361}
362
363pub fn make_gc_msg(msg: u32, protobuf: bool) -> u32 {
366 if protobuf {
367 msg | PROTO_MASK
368 } else {
369 msg
370 }
371}
372
373macro_rules! steam_serializable {
378 (
379 $ty:ty {
380 $( $field:ident : $kind:ty = $default:expr ),* $(,)?
381 }
382 ) => {
383 impl SteamSerializable for $ty {
384 fn write(&self, out: &mut Vec<u8>) {
385 let _ = out;
386 $( WireValue::write_wire(&self.$field, out); )*
387 }
388 }
389
390 impl SteamSerializableDeserialize for $ty {
391 fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
392 let _ = r;
393 Ok(Self {
394 $( $field: WireValue::read_wire(r)?, )*
395 })
396 }
397 }
398 };
399}
400
401macro_rules! steam_message {
403 ( $ty:ty => $emsg:expr ) => {
404 impl ISteamSerializableMessage for $ty {
405 fn get_emsg(&self) -> EMsg {
406 $emsg
407 }
408 }
409 };
410}
411
412macro_rules! steam_header {
414 ( $ty:ty ) => {
415 impl ISteamSerializableHeader for $ty {
416 fn set_emsg(&mut self, msg: EMsg) {
417 self.msg = msg;
418 }
419 }
420 };
421}
422
423#[derive(Clone, Debug, PartialEq)]
428pub struct MsgHdrProtoBuf {
429 pub msg: EMsg,
431 pub header_length: i32,
433 pub proto: CMsgProtoBufHeader,
435}
436
437impl Default for MsgHdrProtoBuf {
438 fn default() -> Self {
439 MsgHdrProtoBuf {
440 msg: EMsg::Invalid,
441 header_length: 0,
442 proto: CMsgProtoBufHeader::default(),
443 }
444 }
445}
446
447impl SteamSerializable for MsgHdrProtoBuf {
448 fn write(&self, out: &mut Vec<u8>) {
449 let proto_bytes = self.proto.encode_to_vec();
450 out.extend_from_slice(&(make_msg(self.msg, true) as i32).to_le_bytes());
451 out.extend_from_slice(&(proto_bytes.len() as i32).to_le_bytes());
452 out.extend_from_slice(&proto_bytes);
453 }
454}
455
456impl SteamSerializableDeserialize for MsgHdrProtoBuf {
457 fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
458 let raw = r.read_i32()?;
459 let msg = get_msg(raw as u32).unwrap_or(EMsg::Invalid);
460 let header_length = r.read_i32()?;
461 if header_length < 0 {
462 return Err(SteamError::Malformed(format!(
463 "negative protobuf header length {header_length}"
464 )));
465 }
466 let bytes = r.read_bytes(header_length as usize)?;
467 let proto = CMsgProtoBufHeader::decode(bytes.as_slice())?;
468 Ok(MsgHdrProtoBuf {
469 msg,
470 header_length,
471 proto,
472 })
473 }
474}
475
476steam_header!(MsgHdrProtoBuf);
477
478#[derive(Clone, Debug, PartialEq)]
483pub struct MsgGCHdrProtoBuf {
484 pub msg: u32,
486 pub header_length: i32,
488 pub proto: GcCMsgProtoBufHeader,
490}
491
492impl Default for MsgGCHdrProtoBuf {
493 fn default() -> Self {
494 MsgGCHdrProtoBuf {
495 msg: 0,
496 header_length: 0,
497 proto: GcCMsgProtoBufHeader::default(),
498 }
499 }
500}
501
502impl SteamSerializable for MsgGCHdrProtoBuf {
503 fn write(&self, out: &mut Vec<u8>) {
504 let proto_bytes = self.proto.encode_to_vec();
505 out.extend_from_slice(&make_gc_msg(self.msg, true).to_le_bytes());
506 out.extend_from_slice(&(proto_bytes.len() as i32).to_le_bytes());
507 out.extend_from_slice(&proto_bytes);
508 }
509}
510
511impl SteamSerializableDeserialize for MsgGCHdrProtoBuf {
512 fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
513 let raw = r.read_u32()?;
514 let header_length = r.read_i32()?;
515 if header_length < 0 {
516 return Err(SteamError::Malformed(format!(
517 "negative protobuf header length {header_length}"
518 )));
519 }
520 let bytes = r.read_bytes(header_length as usize)?;
521 let proto = GcCMsgProtoBufHeader::decode(bytes.as_slice())?;
522 Ok(MsgGCHdrProtoBuf {
523 msg: get_gc_msg(raw),
524 header_length,
525 proto,
526 })
527 }
528}
529
530impl IGCSerializableHeader for MsgGCHdrProtoBuf {
531 fn set_gc_emsg(&mut self, msg: u32) {
532 self.msg = msg;
533 }
534}
535
536steam_serializable!(generated::UdpHeader {
537 magic: u32 = 0x3130_5356,
538 payload_size: u16 = 0,
539 packet_type: EUdpPacketType = EUdpPacketType::Invalid,
540 flags: u8 = 0,
541 source_conn_id: u32 = 512,
542 dest_conn_id: u32 = 0,
543 seq_this: u32 = 0,
544 seq_ack: u32 = 0,
545 packets_in_msg: u32 = 0,
546 msg_start_seq: u32 = 0,
547 msg_size: u32 = 0,
548});
549
550steam_serializable!(generated::ChallengeData {
551 challenge_value: u32 = 0,
552 server_load: u32 = 0,
553});
554
555steam_serializable!(generated::ConnectData {
556 challenge_value: u32 = 0,
557});
558
559steam_serializable!(generated::Accept {});
560
561steam_serializable!(generated::Datagram {});
562
563steam_serializable!(generated::Disconnect {});
564
565steam_serializable!(generated::MsgHdr {
566 msg: EMsg = EMsg::Invalid,
567 target_job_id: u64 = u64::MAX,
568 source_job_id: u64 = u64::MAX,
569});
570
571steam_header!(generated::MsgHdr);
572
573steam_serializable!(generated::ExtendedClientMsgHdr {
574 msg: EMsg = EMsg::Invalid,
575 header_size: u8 = 36,
576 header_version: u16 = 2,
577 target_job_id: u64 = u64::MAX,
578 source_job_id: u64 = u64::MAX,
579 header_canary: u8 = 239,
580 steam_id: u64 = 0,
581 session_id: i32 = 0,
582});
583
584steam_header!(generated::ExtendedClientMsgHdr);
585
586steam_serializable!(generated::MsgGCHdr {
587 header_version: u16 = 1,
588 target_job_id: u64 = u64::MAX,
589 source_job_id: u64 = u64::MAX,
590});
591
592impl IGCSerializableHeader for generated::MsgGCHdr {
593 fn set_gc_emsg(&mut self, _msg: u32) {}
595}
596
597impl SteamSerializable for generated::MsgHdrProtoBuf {
598 fn write(&self, out: &mut Vec<u8>) {
599 out.extend_from_slice(&(make_msg(self.msg, true) as i32).to_le_bytes());
600 out.extend_from_slice(&self.header_length.to_le_bytes());
601 out.extend_from_slice(&self.proto);
602 }
603}
604
605impl SteamSerializableDeserialize for generated::MsgHdrProtoBuf {
606 fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
607 let raw = r.read_i32()?;
608 let header_length = r.read_i32()?;
609 if header_length < 0 {
610 return Err(SteamError::Malformed(format!(
611 "negative protobuf header length {header_length}"
612 )));
613 }
614 let proto = r.read_bytes(header_length as usize)?;
615 Ok(generated::MsgHdrProtoBuf {
616 msg: get_msg(raw as u32).unwrap_or(EMsg::Invalid),
617 header_length,
618 proto,
619 })
620 }
621}
622
623impl SteamSerializable for generated::MsgGCHdrProtoBuf {
624 fn write(&self, out: &mut Vec<u8>) {
625 out.extend_from_slice(&make_gc_msg(self.msg, true).to_le_bytes());
626 out.extend_from_slice(&self.header_length.to_le_bytes());
627 out.extend_from_slice(&self.proto);
628 }
629}
630
631impl SteamSerializableDeserialize for generated::MsgGCHdrProtoBuf {
632 fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
633 let raw = r.read_u32()?;
634 let header_length = r.read_i32()?;
635 if header_length < 0 {
636 return Err(SteamError::Malformed(format!(
637 "negative protobuf header length {header_length}"
638 )));
639 }
640 let proto = r.read_bytes(header_length as usize)?;
641 Ok(generated::MsgGCHdrProtoBuf {
642 msg: get_gc_msg(raw),
643 header_length,
644 proto,
645 })
646 }
647}
648
649impl IGCSerializableHeader for generated::MsgGCHdrProtoBuf {
650 fn set_gc_emsg(&mut self, msg: u32) {
651 self.msg = msg;
652 }
653}
654
655steam_serializable!(generated::MsgClientJustStrings {});
656
657steam_message!(generated::MsgClientJustStrings => EMsg::Invalid);
658
659steam_serializable!(generated::MsgClientGenericResponse {
660 result: EResult = EResult::Invalid,
661});
662
663steam_message!(generated::MsgClientGenericResponse => EMsg::Invalid);
664
665steam_serializable!(generated::MsgChannelEncryptRequest {
666 protocol_version: u32 = 1,
667 universe: EUniverse = EUniverse::Invalid,
668});
669
670steam_message!(generated::MsgChannelEncryptRequest => EMsg::ChannelEncryptRequest);
671
672steam_serializable!(generated::MsgChannelEncryptResponse {
673 protocol_version: u32 = 1,
674 key_size: u32 = 128,
675});
676
677steam_message!(generated::MsgChannelEncryptResponse => EMsg::ChannelEncryptResponse);
678
679steam_serializable!(generated::MsgChannelEncryptResult {
680 result: EResult = EResult::Invalid,
681});
682
683steam_message!(generated::MsgChannelEncryptResult => EMsg::ChannelEncryptResult);
684
685steam_serializable!(generated::MsgClientLogon {});
686
687steam_message!(generated::MsgClientLogon => EMsg::ClientLogon);
688
689steam_serializable!(generated::MsgClientVACBanStatus {
690 num_bans: u32 = 0,
691});
692
693steam_message!(generated::MsgClientVACBanStatus => EMsg::ClientVACBanStatus);
694
695steam_serializable!(generated::MsgClientAppUsageEvent {
696 app_usage_event: EAppUsageEvent = EAppUsageEvent::GameLaunch,
697 game_id: u64 = 0,
698 offline: u16 = 0,
699});
700
701steam_message!(generated::MsgClientAppUsageEvent => EMsg::ClientAppUsageEvent);
702
703steam_serializable!(generated::MsgClientUpdateGuestPassesList {
704 result: EResult = EResult::Invalid,
705 count_guest_passes_to_give: i32 = 0,
706 count_guest_passes_to_redeem: i32 = 0,
707});
708
709steam_message!(generated::MsgClientUpdateGuestPassesList => EMsg::ClientUpdateGuestPassesList);
710
711const P2P_INTRODUCER_DATA_SIZE: usize = 1450;
716
717impl SteamSerializable for generated::MsgClientP2PIntroducerMessage {
718 fn write(&self, out: &mut Vec<u8>) {
719 out.extend_from_slice(&self.steam_id.to_le_bytes());
720 out.extend_from_slice(&self.routing_type.wire_value().to_le_bytes());
721 let take = self.data.len().min(P2P_INTRODUCER_DATA_SIZE);
724 out.extend_from_slice(&self.data[..take]);
725 out.resize(out.len() + (P2P_INTRODUCER_DATA_SIZE - take), 0);
726 out.extend_from_slice(&self.data_len.to_le_bytes());
727 }
728}
729
730impl SteamSerializableDeserialize for generated::MsgClientP2PIntroducerMessage {
731 fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
732 Ok(generated::MsgClientP2PIntroducerMessage {
733 steam_id: r.read_u64()?,
734 routing_type: read_enum(r)?,
735 data: r.read_bytes(P2P_INTRODUCER_DATA_SIZE)?,
736 data_len: r.read_u32()?,
737 })
738 }
739}
740
741steam_message!(
742 generated::MsgClientP2PIntroducerMessage => EMsg::ClientP2PIntroducerMessage
743);
744
745steam_serializable!(generated::MsgClientOGSBeginSession {
746 account_type: u8 = 0,
747 account_id: u64 = 0,
748 app_id: u32 = 0,
749 time_started: u32 = 0,
750});
751
752steam_message!(generated::MsgClientOGSBeginSession => EMsg::ClientOGSBeginSession);
753
754steam_serializable!(generated::MsgClientOGSBeginSessionResponse {
755 result: EResult = EResult::Invalid,
756 collecting_any: bool = false,
757 collecting_details: bool = false,
758 session_id: u64 = 0,
759});
760
761steam_message!(
762 generated::MsgClientOGSBeginSessionResponse => EMsg::ClientOGSBeginSessionResponse
763);
764
765steam_serializable!(generated::MsgClientOGSEndSession {
766 session_id: u64 = 0,
767 time_ended: u32 = 0,
768 reason_code: i32 = 0,
769 count_attributes: i32 = 0,
770});
771
772steam_message!(generated::MsgClientOGSEndSession => EMsg::ClientOGSEndSession);
773
774steam_serializable!(generated::MsgClientOGSEndSessionResponse {
775 result: EResult = EResult::Invalid,
776});
777
778steam_message!(generated::MsgClientOGSEndSessionResponse => EMsg::ClientOGSEndSessionResponse);
779
780steam_serializable!(generated::MsgClientOGSWriteRow {
781 session_id: u64 = 0,
782 count_attributes: i32 = 0,
783});
784
785steam_message!(generated::MsgClientOGSWriteRow => EMsg::ClientOGSWriteRow);
786
787steam_serializable!(generated::MsgClientGetFriendsWhoPlayGame {
788 game_id: u64 = 0,
789});
790
791steam_message!(
792 generated::MsgClientGetFriendsWhoPlayGame => EMsg::ClientGetFriendsWhoPlayGame
793);
794
795steam_serializable!(generated::MsgClientGetFriendsWhoPlayGameResponse {
796 result: EResult = EResult::Invalid,
797 game_id: u64 = 0,
798 count_friends: u32 = 0,
799});
800
801steam_message!(
802 generated::MsgClientGetFriendsWhoPlayGameResponse => EMsg::ClientGetFriendsWhoPlayGameResponse
803);
804
805steam_serializable!(generated::MsgGSPerformHardwareSurvey {
806 flags: u32 = 0,
807});
808
809steam_message!(generated::MsgGSPerformHardwareSurvey => EMsg::GSPerformHardwareSurvey);
810
811steam_serializable!(generated::MsgGSGetPlayStatsResponse {
812 result: EResult = EResult::Invalid,
813 rank: i32 = 0,
814 lifetime_connects: u32 = 0,
815 lifetime_minutes_played: u32 = 0,
816});
817
818steam_message!(generated::MsgGSGetPlayStatsResponse => EMsg::GSGetPlayStatsResponse);
819
820steam_serializable!(generated::MsgGSGetReputationResponse {
821 result: EResult = EResult::Invalid,
822 reputation_score: u32 = 0,
823 banned: bool = false,
824 banned_ip: u32 = 0,
825 banned_port: u16 = 0,
826 banned_game_id: u64 = 0,
827 time_ban_expires: u32 = 0,
828});
829
830steam_message!(generated::MsgGSGetReputationResponse => EMsg::GSGetReputationResponse);
831
832steam_serializable!(generated::MsgGSDeny {
833 steam_id: u64 = 0,
834 deny_reason: EDenyReason = EDenyReason::InvalidVersion,
835});
836
837steam_message!(generated::MsgGSDeny => EMsg::GSDeny);
838
839steam_serializable!(generated::MsgGSApprove {
840 steam_id: u64 = 0,
841});
842
843steam_message!(generated::MsgGSApprove => EMsg::GSApprove);
844
845steam_serializable!(generated::MsgGSKick {
846 steam_id: u64 = 0,
847 deny_reason: EDenyReason = EDenyReason::InvalidVersion,
848 wait_til_map_change: i32 = 0,
849});
850
851steam_message!(generated::MsgGSKick => EMsg::GSKick);
852
853steam_serializable!(generated::MsgGSGetUserGroupStatus {
854 steam_id_user: u64 = 0,
855 steam_id_group: u64 = 0,
856});
857
858steam_message!(generated::MsgGSGetUserGroupStatus => EMsg::GSGetUserGroupStatus);
859
860steam_serializable!(generated::MsgGSGetUserGroupStatusResponse {
861 steam_id_user: u64 = 0,
862 steam_id_group: u64 = 0,
863 clan_relationship: EClanRelationship = EClanRelationship::None,
864 clan_rank: EClanRank = EClanRank::None,
865});
866
867steam_message!(
868 generated::MsgGSGetUserGroupStatusResponse => EMsg::GSGetUserGroupStatusResponse
869);
870
871steam_serializable!(generated::MsgClientJoinChat {
872 steam_id_chat: u64 = 0,
873 is_voice_speaker: bool = false,
874});
875
876steam_message!(generated::MsgClientJoinChat => EMsg::ClientJoinChat);
877
878steam_serializable!(generated::MsgClientChatEnter {
879 steam_id_chat: u64 = 0,
880 steam_id_friend: u64 = 0,
881 chat_room_type: EChatRoomType = EChatRoomType::Friend,
882 steam_id_owner: u64 = 0,
883 steam_id_clan: u64 = 0,
884 chat_flags: u8 = 0,
885 enter_response: EChatRoomEnterResponse = EChatRoomEnterResponse::Success,
886 num_members: i32 = 0,
887});
888
889steam_message!(generated::MsgClientChatEnter => EMsg::ClientChatEnter);
890
891steam_serializable!(generated::MsgClientChatMsg {
892 steam_id_chatter: u64 = 0,
893 steam_id_chat_room: u64 = 0,
894 chat_msg_type: EChatEntryType = EChatEntryType::Invalid,
895});
896
897steam_message!(generated::MsgClientChatMsg => EMsg::ClientChatMsg);
898
899steam_serializable!(generated::MsgClientChatMemberInfo {
900 steam_id_chat: u64 = 0,
901 r#type: EChatInfoType = EChatInfoType::StateChange,
902});
903
904steam_message!(generated::MsgClientChatMemberInfo => EMsg::ClientChatMemberInfo);
905
906steam_serializable!(generated::MsgClientChatAction {
907 steam_id_chat: u64 = 0,
908 steam_id_user_to_act_on: u64 = 0,
909 chat_action: EChatAction = EChatAction::InviteChat,
910});
911
912steam_message!(generated::MsgClientChatAction => EMsg::ClientChatAction);
913
914steam_serializable!(generated::MsgClientChatActionResult {
915 steam_id_chat: u64 = 0,
916 steam_id_user_acted_on: u64 = 0,
917 chat_action: EChatAction = EChatAction::InviteChat,
918 action_result: EChatActionResult = EChatActionResult::Success,
919});
920
921steam_message!(generated::MsgClientChatActionResult => EMsg::ClientChatActionResult);
922
923steam_serializable!(generated::MsgClientChatRoomInfo {
924 steam_id_chat: u64 = 0,
925 r#type: EChatInfoType = EChatInfoType::StateChange,
926});
927
928steam_message!(generated::MsgClientChatRoomInfo => EMsg::ClientChatRoomInfo);
929
930steam_serializable!(generated::MsgClientSetIgnoreFriend {
931 my_steam_id: u64 = 0,
932 steam_id_friend: u64 = 0,
933 ignore: u8 = 0,
934});
935
936steam_message!(generated::MsgClientSetIgnoreFriend => EMsg::ClientSetIgnoreFriend);
937
938steam_serializable!(generated::MsgClientSetIgnoreFriendResponse {
939 friend_id: u64 = 0,
940 result: EResult = EResult::Invalid,
941});
942
943steam_message!(generated::MsgClientSetIgnoreFriendResponse => EMsg::ClientSetIgnoreFriendResponse);
944
945steam_serializable!(generated::MsgClientLoggedOff {
946 result: EResult = EResult::Invalid,
947 sec_min_reconnect_hint: i32 = 0,
948 sec_max_reconnect_hint: i32 = 0,
949});
950
951steam_message!(generated::MsgClientLoggedOff => EMsg::ClientLoggedOff);
952
953steam_serializable!(generated::MsgClientLogOnResponse {
954 result: EResult = EResult::Invalid,
955 out_of_game_heartbeat_rate_sec: i32 = 0,
956 in_game_heartbeat_rate_sec: i32 = 0,
957 client_supplied_steam_id: u64 = 0,
958 ip_public: u32 = 0,
959 server_real_time: u32 = 0,
960});
961
962steam_message!(generated::MsgClientLogOnResponse => EMsg::ClientLogOnResponse);
963
964steam_serializable!(generated::MsgClientServerUnavailable {
965 jobid_sent: u64 = 0,
966 e_msg_sent: u32 = 0,
967 e_server_type_unavailable: EServerType = EServerType::First,
968});
969
970steam_message!(generated::MsgClientServerUnavailable => EMsg::ClientServerUnavailable);
971
972steam_serializable!(generated::MsgClientCreateChat {
973 chat_room_type: EChatRoomType = EChatRoomType::Friend,
974 game_id: u64 = 0,
975 steam_id_clan: u64 = 0,
976 permission_officer: EChatPermission = EChatPermission::Close,
977 permission_member: EChatPermission = EChatPermission::Close,
978 permission_all: EChatPermission = EChatPermission::Close,
979 members_max: u32 = 0,
980 chat_flags: u8 = 0,
981 steam_id_friend_chat: u64 = 0,
982 steam_id_invited: u64 = 0,
983});
984
985steam_message!(generated::MsgClientCreateChat => EMsg::ClientCreateChat);
986
987steam_serializable!(generated::MsgClientCreateChatResponse {
988 result: EResult = EResult::Invalid,
989 steam_id_chat: u64 = 0,
990 chat_room_type: EChatRoomType = EChatRoomType::Friend,
991 steam_id_friend_chat: u64 = 0,
992});
993
994steam_message!(generated::MsgClientCreateChatResponse => EMsg::ClientCreateChatResponse);
995
996steam_serializable!(generated::MsgClientMarketingMessageUpdate2 {
997 marketing_message_update_time: u32 = 0,
998 count: u32 = 0,
999});
1000
1001steam_message!(generated::MsgClientMarketingMessageUpdate2 => EMsg::ClientMarketingMessageUpdate2);
1002
1003steam_serializable!(generated::MsgClientGetLegacyGameKey {
1004 app_id: u32 = 0,
1005});
1006
1007steam_message!(generated::MsgClientGetLegacyGameKey => EMsg::ClientGetLegacyGameKey);
1008
1009steam_serializable!(generated::MsgClientGetLegacyGameKeyResponse {
1010 app_id: u32 = 0,
1011 result: EResult = EResult::Invalid,
1012 length: u32 = 0,
1013});
1014
1015steam_message!(generated::MsgClientGetLegacyGameKeyResponse => EMsg::ClientGetLegacyGameKeyResponse);
1016
1017#[cfg(test)]
1018mod temp_wire_verification {
1019 use super::*;
1020 use crate::base::generated::steam_language::internal as gen;
1021
1022 fn roundtrip<T>(value: &T, expected_len: usize, label: &str)
1023 where
1024 T: SteamSerializable + SteamSerializableDeserialize + PartialEq + std::fmt::Debug,
1025 {
1026 let mut buf = Vec::new();
1027 value.write(&mut buf);
1028 assert_eq!(buf.len(), expected_len, "{label}: serialized length");
1029 let mut r = Reader::new(&buf);
1030 let back = match T::read(&mut r) {
1031 Ok(v) => v,
1032 Err(e) => panic!("{label}: read failed: {e}"),
1033 };
1034 assert_eq!(*value, back, "{label}: round trip mismatch");
1035 assert_eq!(r.position(), buf.len(), "{label}: trailing bytes");
1036 }
1037
1038 #[test]
1039 fn wire_layout_sizes() {
1040 roundtrip(&gen::UdpHeader::default(), 36, "UdpHeader");
1041 roundtrip(&gen::ChallengeData::default(), 8, "ChallengeData");
1042 roundtrip(&gen::ConnectData::default(), 4, "ConnectData");
1043 roundtrip(&gen::Accept::default(), 0, "Accept");
1044 roundtrip(&gen::Datagram::default(), 0, "Datagram");
1045 roundtrip(&gen::Disconnect::default(), 0, "Disconnect");
1046 roundtrip(&gen::MsgHdr::default(), 20, "MsgHdr");
1047 roundtrip(&gen::ExtendedClientMsgHdr::default(), 36, "ExtendedClientMsgHdr");
1048 roundtrip(&gen::MsgGCHdr::default(), 18, "MsgGCHdr");
1049 roundtrip(&gen::MsgClientJustStrings::default(), 0, "MsgClientJustStrings");
1050 roundtrip(&gen::MsgClientGenericResponse::default(), 4, "MsgClientGenericResponse");
1051 roundtrip(&gen::MsgChannelEncryptRequest::default(), 8, "MsgChannelEncryptRequest");
1052 roundtrip(&gen::MsgChannelEncryptResponse::default(), 8, "MsgChannelEncryptResponse");
1053 roundtrip(&gen::MsgChannelEncryptResult::default(), 4, "MsgChannelEncryptResult");
1054 roundtrip(&gen::MsgClientLogon::default(), 0, "MsgClientLogon");
1055 roundtrip(&gen::MsgClientVACBanStatus::default(), 4, "MsgClientVACBanStatus");
1056 roundtrip(&gen::MsgClientAppUsageEvent::default(), 14, "MsgClientAppUsageEvent");
1057 roundtrip(&gen::MsgClientUpdateGuestPassesList::default(), 12, "MsgClientUpdateGuestPassesList");
1058 roundtrip(&gen::MsgClientOGSBeginSession::default(), 17, "MsgClientOGSBeginSession");
1059 roundtrip(&gen::MsgClientOGSBeginSessionResponse::default(), 14, "MsgClientOGSBeginSessionResponse");
1060 roundtrip(&gen::MsgClientOGSEndSession::default(), 20, "MsgClientOGSEndSession");
1061 roundtrip(&gen::MsgClientOGSEndSessionResponse::default(), 4, "MsgClientOGSEndSessionResponse");
1062 roundtrip(&gen::MsgClientOGSWriteRow::default(), 12, "MsgClientOGSWriteRow");
1063 roundtrip(&gen::MsgClientGetFriendsWhoPlayGame::default(), 8, "MsgClientGetFriendsWhoPlayGame");
1064 roundtrip(&gen::MsgClientGetFriendsWhoPlayGameResponse::default(), 16, "MsgClientGetFriendsWhoPlayGameResponse");
1065 roundtrip(&gen::MsgGSPerformHardwareSurvey::default(), 4, "MsgGSPerformHardwareSurvey");
1066 roundtrip(&gen::MsgGSGetPlayStatsResponse::default(), 16, "MsgGSGetPlayStatsResponse");
1067 roundtrip(&gen::MsgGSGetReputationResponse::default(), 27, "MsgGSGetReputationResponse");
1068 roundtrip(&gen::MsgGSDeny::default(), 12, "MsgGSDeny");
1069 roundtrip(&gen::MsgGSApprove::default(), 8, "MsgGSApprove");
1070 roundtrip(&gen::MsgGSKick::default(), 16, "MsgGSKick");
1071 roundtrip(&gen::MsgGSGetUserGroupStatus::default(), 16, "MsgGSGetUserGroupStatus");
1072 roundtrip(&gen::MsgGSGetUserGroupStatusResponse::default(), 24, "MsgGSGetUserGroupStatusResponse");
1073 roundtrip(&gen::MsgClientJoinChat::default(), 9, "MsgClientJoinChat");
1074 roundtrip(&gen::MsgClientChatEnter::default(), 45, "MsgClientChatEnter");
1075 roundtrip(&gen::MsgClientChatMsg::default(), 20, "MsgClientChatMsg");
1076 roundtrip(&gen::MsgClientChatMemberInfo::default(), 12, "MsgClientChatMemberInfo");
1077 roundtrip(&gen::MsgClientChatAction::default(), 20, "MsgClientChatAction");
1078 roundtrip(&gen::MsgClientChatActionResult::default(), 24, "MsgClientChatActionResult");
1079 roundtrip(&gen::MsgClientChatRoomInfo::default(), 12, "MsgClientChatRoomInfo");
1080 roundtrip(&gen::MsgClientSetIgnoreFriend::default(), 17, "MsgClientSetIgnoreFriend");
1081 roundtrip(&gen::MsgClientSetIgnoreFriendResponse::default(), 12, "MsgClientSetIgnoreFriendResponse");
1082 roundtrip(&gen::MsgClientLoggedOff::default(), 12, "MsgClientLoggedOff");
1083 roundtrip(&gen::MsgClientLogOnResponse::default(), 28, "MsgClientLogOnResponse");
1084 roundtrip(&gen::MsgClientServerUnavailable::default(), 16, "MsgClientServerUnavailable");
1085 roundtrip(&gen::MsgClientCreateChat::default(), 53, "MsgClientCreateChat");
1086 roundtrip(&gen::MsgClientCreateChatResponse::default(), 24, "MsgClientCreateChatResponse");
1087 roundtrip(&gen::MsgClientMarketingMessageUpdate2::default(), 8, "MsgClientMarketingMessageUpdate2");
1088 roundtrip(&gen::MsgClientGetLegacyGameKey::default(), 4, "MsgClientGetLegacyGameKey");
1089 roundtrip(&gen::MsgClientGetLegacyGameKeyResponse::default(), 12, "MsgClientGetLegacyGameKeyResponse");
1090
1091 let p2p = gen::MsgClientP2PIntroducerMessage {
1092 steam_id: 0x0100_0000_0000_0002,
1093 routing_type: EIntroducerRouting::P2PNetworking,
1094 data: vec![0xABu8; 1450],
1095 data_len: 1450,
1096 };
1097 roundtrip(&p2p, 1466, "MsgClientP2PIntroducerMessage");
1098 }
1099
1100 #[test]
1101 fn udp_header_bytes_are_byte_exact() {
1102 let mut buf = Vec::new();
1103 let hdr = gen::UdpHeader {
1104 magic: 0x3130_5356,
1105 payload_size: 0x1234,
1106 packet_type: EUdpPacketType::ChallengeReq,
1107 flags: 0x07,
1108 source_conn_id: 0x1111_1111,
1109 dest_conn_id: 0x2222_2222,
1110 seq_this: 0x3333_3333,
1111 seq_ack: 0x4444_4444,
1112 packets_in_msg: 0x5555_5555,
1113 msg_start_seq: 0x6666_6666,
1114 msg_size: 0x7777_7777,
1115 };
1116 hdr.write(&mut buf);
1117
1118 let expected: Vec<u8> = vec![
1119 0x56, 0x53, 0x30, 0x31, 0x34, 0x12, 0x01, 0x07, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, ];
1131 assert_eq!(buf, expected);
1132 assert_eq!(buf.len(), 36);
1133
1134 let mut r = Reader::new(&buf);
1135 let back = gen::UdpHeader::read(&mut r).expect("udp header read");
1136 assert_eq!(back, hdr);
1137 }
1138
1139 #[test]
1140 fn enum_widths_are_four_bytes_except_udp_packet_type() {
1141 let mut buf = Vec::new();
1142 gen::MsgHdr::default().write(&mut buf);
1143 assert_eq!(buf.len(), 20, "MsgHdr = 4 byte EMsg + 2 x u64");
1144
1145 let mut buf = Vec::new();
1146 gen::MsgClientGenericResponse::default().write(&mut buf);
1147 assert_eq!(buf.len(), 4, "EResult is four bytes");
1148
1149 let mut buf = Vec::new();
1150 gen::MsgClientChatEnter::default().write(&mut buf);
1151 assert_eq!(buf.len(), 45, "EChatRoomType + EChatRoomEnterResponse are four bytes");
1152
1153 let mut buf = Vec::new();
1154 gen::MsgClientOGSBeginSessionResponse::default().write(&mut buf);
1155 assert_eq!(buf.len(), 14, "two bools are one byte each");
1156
1157 let mut buf = Vec::new();
1158 gen::MsgClientP2PIntroducerMessage {
1159 steam_id: 1,
1160 routing_type: EIntroducerRouting::P2PVoiceChat,
1161 data: Vec::new(),
1162 data_len: 0,
1163 }
1164 .write(&mut buf);
1165 assert_eq!(buf.len(), 1466, "u64 + 4 byte enum + 1450 bytes + u32");
1166 }
1167}
1168