use crate::base::generated::steam_language::EMsg;
use crate::base::generated::steam_language_internal::{
ExtendedClientMsgHdr, ISteamSerializableHeader, ISteamSerializableMessage, MsgHdr,
MsgHdrProtoBuf, SteamSerializable, SteamSerializableDeserialize,
};
use crate::base::msg_base::{MsgBase, Reader};
use crate::base::packet_base::IPacketMsg;
use crate::error::SteamError;
use crate::types::job_id::JobID;
use crate::types::steam_id::SteamID;
use steamkit_protos::steam::CMsgProtoBufHeader;
pub trait IClientMsg: Send + Sync {
fn is_proto(&self) -> bool;
fn msg_type(&self) -> EMsg;
fn session_id(&self) -> i32;
fn set_session_id(&mut self, id: i32);
fn steam_id(&self) -> Option<SteamID>;
fn set_steam_id(&mut self, id: SteamID);
fn target_job_id(&self) -> JobID;
fn set_target_job_id(&mut self, id: JobID);
fn source_job_id(&self) -> JobID;
fn set_source_job_id(&mut self, id: JobID);
fn serialize(&self) -> Vec<u8>;
}
pub trait SteamIDRaw {
fn raw_u64(&self) -> u64;
}
impl SteamIDRaw for SteamID {
fn raw_u64(&self) -> u64 {
self.value()
}
}
#[derive(Clone, Copy, PartialEq, Eq, ::prost::Message)]
pub struct SteamProtobufHeaderOnly {}
pub struct ClientMsgProtobuf<TBody: prost::Message + Default = SteamProtobufHeaderOnly> {
pub header: MsgHdrProtoBuf,
pub body: TBody,
pub payload: MsgBase,
}
impl<TBody> ClientMsgProtobuf<TBody>
where
TBody: prost::Message + Default,
{
pub fn new(e_msg: EMsg) -> Self {
Self::with_payload_reserve(e_msg, 0)
}
pub fn with_payload_reserve(e_msg: EMsg, payload_reserve: usize) -> Self {
let mut header = MsgHdrProtoBuf::default();
header.msg = e_msg;
ClientMsgProtobuf {
header,
body: TBody::default(),
payload: MsgBase::new(payload_reserve),
}
}
pub fn reply(e_msg: EMsg, reply_to: &dyn IClientMsg) -> Self {
let mut msg = Self::new(e_msg);
msg.set_target_job_id(reply_to.source_job_id());
msg
}
pub fn from_packet(msg: &dyn IPacketMsg) -> Result<Self, SteamError> {
if !msg.is_proto() {
return Err(SteamError::InvalidArgument(
"ClientMsgProtobuf used for non-proto message!".to_string(),
));
}
let data = msg.get_data();
let mut reader = Reader::new(data);
let header = MsgHdrProtoBuf::read(&mut reader)?;
let body_offset = reader.position();
let mut body_bytes = data.get(body_offset..).ok_or_else(|| {
SteamError::Malformed("protobuf body offset is outside of the packet".to_string())
})?;
let body = TBody::decode(&mut body_bytes)?;
let mut payload = MsgBase::new(0);
if !body_bytes.is_empty() {
payload.write_bytes(body_bytes);
payload.seek(0);
}
Ok(ClientMsgProtobuf {
header,
body,
payload,
})
}
pub fn body(&self) -> &TBody {
&self.body
}
pub fn body_mut(&mut self) -> &mut TBody {
&mut self.body
}
pub fn set_body(&mut self, body: TBody) {
self.body = body;
}
pub fn header(&self) -> &MsgHdrProtoBuf {
&self.header
}
pub fn header_mut(&mut self) -> &mut MsgHdrProtoBuf {
&mut self.header
}
pub fn proto_header(&self) -> &CMsgProtoBufHeader {
&self.header.proto
}
pub fn proto_header_mut(&mut self) -> &mut CMsgProtoBufHeader {
&mut self.header.proto
}
pub fn payload(&self) -> &MsgBase {
&self.payload
}
pub fn payload_mut(&mut self) -> &mut MsgBase {
&mut self.payload
}
}
impl<TBody> IClientMsg for ClientMsgProtobuf<TBody>
where
TBody: prost::Message + Default,
{
fn is_proto(&self) -> bool {
true
}
fn msg_type(&self) -> EMsg {
self.header.msg
}
fn session_id(&self) -> i32 {
self.header.proto.client_sessionid.unwrap_or(0)
}
fn set_session_id(&mut self, id: i32) {
self.header.proto.client_sessionid = Some(id);
}
fn steam_id(&self) -> Option<SteamID> {
Some(SteamID::from_id(self.header.proto.steamid.unwrap_or(0)))
}
fn set_steam_id(&mut self, id: SteamID) {
self.header.proto.steamid = Some(id.raw_u64());
}
fn target_job_id(&self) -> JobID {
JobID::from_value(self.header.proto.jobid_target.unwrap_or(u64::MAX))
}
fn set_target_job_id(&mut self, id: JobID) {
self.header.proto.jobid_target = Some(id.value());
}
fn source_job_id(&self) -> JobID {
JobID::from_value(self.header.proto.jobid_source.unwrap_or(u64::MAX))
}
fn set_source_job_id(&mut self, id: JobID) {
self.header.proto.jobid_source = Some(id.value());
}
fn serialize(&self) -> Vec<u8> {
let mut out = Vec::new();
self.header.write(&mut out);
out.extend_from_slice(&self.body.encode_to_vec());
out.extend_from_slice(&self.payload.payload);
out
}
}
pub struct ClientMsg<TBody>
where
TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
{
pub header: ExtendedClientMsgHdr,
pub body: TBody,
pub payload: MsgBase,
}
impl<TBody> ClientMsg<TBody>
where
TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
{
pub fn new(e_msg: EMsg) -> Self {
Self::with_payload_reserve(e_msg, 0)
}
pub fn with_payload_reserve(e_msg: EMsg, payload_reserve: usize) -> Self {
let mut header = ExtendedClientMsgHdr::default();
header.set_emsg(e_msg);
ClientMsg {
header,
body: TBody::default(),
payload: MsgBase::new(payload_reserve),
}
}
pub fn reply(e_msg: EMsg, reply_to: &dyn IClientMsg) -> Self {
let mut msg = Self::new(e_msg);
msg.set_target_job_id(reply_to.source_job_id());
msg
}
pub fn from_packet(msg: &dyn IPacketMsg) -> Result<Self, SteamError> {
let data = msg.get_data();
let mut reader = Reader::new(data);
let header = ExtendedClientMsgHdr::read(&mut reader)?;
let body = TBody::read(&mut reader)?;
let mut payload = MsgBase::new(0);
let remaining = reader.read_up_to(reader.remaining());
if !remaining.is_empty() {
payload.write_bytes(&remaining);
payload.seek(0);
}
Ok(ClientMsg {
header,
body,
payload,
})
}
pub fn body(&self) -> &TBody {
&self.body
}
pub fn body_mut(&mut self) -> &mut TBody {
&mut self.body
}
pub fn header(&self) -> &ExtendedClientMsgHdr {
&self.header
}
pub fn header_mut(&mut self) -> &mut ExtendedClientMsgHdr {
&mut self.header
}
pub fn payload(&self) -> &MsgBase {
&self.payload
}
pub fn payload_mut(&mut self) -> &mut MsgBase {
&mut self.payload
}
}
impl<TBody> IClientMsg for ClientMsg<TBody>
where
TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
{
fn is_proto(&self) -> bool {
false
}
fn msg_type(&self) -> EMsg {
self.header.msg
}
fn session_id(&self) -> i32 {
self.header.session_id
}
fn set_session_id(&mut self, id: i32) {
self.header.session_id = id;
}
fn steam_id(&self) -> Option<SteamID> {
Some(SteamID::from_id(self.header.steam_id))
}
fn set_steam_id(&mut self, id: SteamID) {
self.header.steam_id = id.raw_u64();
}
fn target_job_id(&self) -> JobID {
JobID::from_value(self.header.target_job_id)
}
fn set_target_job_id(&mut self, id: JobID) {
self.header.target_job_id = id.value();
}
fn source_job_id(&self) -> JobID {
JobID::from_value(self.header.source_job_id)
}
fn set_source_job_id(&mut self, id: JobID) {
self.header.source_job_id = id.value();
}
fn serialize(&self) -> Vec<u8> {
let mut out = Vec::new();
self.header.write(&mut out);
self.body.write(&mut out);
out.extend_from_slice(&self.payload.payload);
out
}
}
pub struct Msg<TBody>
where
TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
{
pub header: MsgHdr,
pub body: TBody,
pub payload: MsgBase,
pub session_id: i32,
pub steam_id: Option<SteamID>,
}
impl<TBody> Msg<TBody>
where
TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
{
pub fn new(e_msg: EMsg) -> Self {
Self::with_payload_reserve(e_msg, 0)
}
pub fn with_payload_reserve(e_msg: EMsg, payload_reserve: usize) -> Self {
let mut header = MsgHdr::default();
header.set_emsg(e_msg);
Msg {
header,
body: TBody::default(),
payload: MsgBase::new(payload_reserve),
session_id: 0,
steam_id: None,
}
}
pub fn reply(e_msg: EMsg, reply_to: &dyn IClientMsg) -> Self {
let mut msg = Self::new(e_msg);
msg.set_target_job_id(reply_to.source_job_id());
msg
}
pub fn from_packet(msg: &dyn IPacketMsg) -> Result<Self, SteamError> {
let data = msg.get_data();
let mut reader = Reader::new(data);
let header = MsgHdr::read(&mut reader)?;
let body = TBody::read(&mut reader)?;
let mut payload = MsgBase::new(0);
let remaining = reader.read_up_to(reader.remaining());
if !remaining.is_empty() {
payload.write_bytes(&remaining);
payload.seek(0);
}
Ok(Msg {
header,
body,
payload,
session_id: 0,
steam_id: None,
})
}
pub fn body(&self) -> &TBody {
&self.body
}
pub fn body_mut(&mut self) -> &mut TBody {
&mut self.body
}
pub fn header(&self) -> &MsgHdr {
&self.header
}
pub fn header_mut(&mut self) -> &mut MsgHdr {
&mut self.header
}
pub fn payload(&self) -> &MsgBase {
&self.payload
}
pub fn payload_mut(&mut self) -> &mut MsgBase {
&mut self.payload
}
}
impl<TBody> IClientMsg for Msg<TBody>
where
TBody: SteamSerializableDeserialize + ISteamSerializableMessage,
{
fn is_proto(&self) -> bool {
false
}
fn msg_type(&self) -> EMsg {
self.header.msg
}
fn session_id(&self) -> i32 {
self.session_id
}
fn set_session_id(&mut self, id: i32) {
self.session_id = id;
}
fn steam_id(&self) -> Option<SteamID> {
self.steam_id
}
fn set_steam_id(&mut self, id: SteamID) {
self.steam_id = Some(id);
}
fn target_job_id(&self) -> JobID {
JobID::from_value(self.header.target_job_id)
}
fn set_target_job_id(&mut self, id: JobID) {
self.header.target_job_id = id.value();
}
fn source_job_id(&self) -> JobID {
JobID::from_value(self.header.source_job_id)
}
fn set_source_job_id(&mut self, id: JobID) {
self.header.source_job_id = id.value();
}
fn serialize(&self) -> Vec<u8> {
let mut out = Vec::new();
self.header.write(&mut out);
self.body.write(&mut out);
out.extend_from_slice(&self.payload.payload);
out
}
}