use crate::base::gc::msg_base_gc::IClientGCMsg;
use crate::base::gc::packet_base_gc::IPacketGCMsg;
use crate::base::generated::steam_language_internal::{
IGCSerializableHeader, IGCSerializableMessage, MsgGCHdr, MsgGCHdrProtoBuf, SteamSerializable,
SteamSerializableDeserialize,
};
use crate::base::msg_base::{MsgBase, Reader};
use crate::error::SteamError;
use crate::types::job_id::JobID;
use steamkit_protos::gc::CMsgProtoBufHeader;
pub struct ClientGCMsgProtobuf<TBody>
where
TBody: prost::Message + Default,
{
pub header: MsgGCHdrProtoBuf,
pub body: TBody,
pub payload: MsgBase,
}
impl<TBody> ClientGCMsgProtobuf<TBody>
where
TBody: prost::Message + Default,
{
pub fn new(e_msg: u32) -> Self {
Self::with_payload_reserve(e_msg, 0)
}
pub fn with_payload_reserve(e_msg: u32, payload_reserve: usize) -> Self {
let mut header = MsgGCHdrProtoBuf::default();
header.set_gc_emsg(e_msg);
ClientGCMsgProtobuf {
header,
body: TBody::default(),
payload: MsgBase::new(payload_reserve),
}
}
pub fn reply(e_msg: u32, reply_to: &dyn IClientGCMsg) -> 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 IPacketGCMsg) -> Result<Self, SteamError> {
if !msg.is_proto() {
return Err(SteamError::InvalidArgument(
"ClientGCMsgProtobuf used for non-proto message!".to_string(),
));
}
let mut result = Self::new(msg.msg_type());
result.deserialize(msg.get_data())?;
Ok(result)
}
pub fn body(&self) -> &TBody {
&self.body
}
pub fn body_mut(&mut self) -> &mut TBody {
&mut self.body
}
pub fn header(&self) -> &MsgGCHdrProtoBuf {
&self.header
}
pub fn header_mut(&mut self) -> &mut MsgGCHdrProtoBuf {
&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> IClientGCMsg for ClientGCMsgProtobuf<TBody>
where
TBody: prost::Message + Default,
{
fn is_proto(&self) -> bool {
true
}
fn msg_type(&self) -> u32 {
self.header.msg
}
fn target_job_id(&self) -> JobID {
JobID::from_value(self.header.proto.job_id_target.unwrap_or(u64::MAX))
}
fn set_target_job_id(&mut self, id: JobID) {
self.header.proto.job_id_target = Some(id.value());
}
fn source_job_id(&self) -> JobID {
JobID::from_value(self.header.proto.job_id_source.unwrap_or(u64::MAX))
}
fn set_source_job_id(&mut self, id: JobID) {
self.header.proto.job_id_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
}
fn deserialize(&mut self, data: &[u8]) -> Result<(), SteamError> {
let mut reader = Reader::new(data);
let header = MsgGCHdrProtoBuf::read(&mut reader)?;
let remaining = reader.remaining();
let body_bytes = reader.read_up_to(remaining);
let body = TBody::decode(body_bytes.as_slice())?;
let mut payload = MsgBase::new(0);
if !body_bytes.is_empty() {
payload.write_bytes(&body_bytes);
payload.seek(0);
}
self.header = header;
self.body = body;
self.payload = payload;
Ok(())
}
}
pub struct ClientGCMsg<TBody>
where
TBody: SteamSerializableDeserialize + IGCSerializableMessage,
{
pub header: MsgGCHdr,
pub body: TBody,
pub payload: MsgBase,
pub msg_type: u32,
}
impl<TBody> ClientGCMsg<TBody>
where
TBody: SteamSerializableDeserialize + IGCSerializableMessage,
{
pub fn new(payload_reserve: usize) -> Self {
let body = TBody::default();
let msg_type = body.get_gc_emsg();
ClientGCMsg {
header: MsgGCHdr::default(),
body,
payload: MsgBase::new(payload_reserve),
msg_type,
}
}
pub fn reply(reply_to: &dyn IClientGCMsg) -> Self {
let mut msg = Self::new(0);
msg.set_target_job_id(reply_to.source_job_id());
msg
}
pub fn from_packet(msg: &dyn IPacketGCMsg) -> Result<Self, SteamError> {
if msg.is_proto() {
return Err(SteamError::InvalidArgument(
"ClientGCMsg used for proto message!".to_string(),
));
}
let mut result = Self::new(0);
result.deserialize(msg.get_data())?;
Ok(result)
}
pub fn body(&self) -> &TBody {
&self.body
}
pub fn body_mut(&mut self) -> &mut TBody {
&mut self.body
}
pub fn header(&self) -> &MsgGCHdr {
&self.header
}
pub fn header_mut(&mut self) -> &mut MsgGCHdr {
&mut self.header
}
pub fn payload(&self) -> &MsgBase {
&self.payload
}
pub fn payload_mut(&mut self) -> &mut MsgBase {
&mut self.payload
}
}
impl<TBody> IClientGCMsg for ClientGCMsg<TBody>
where
TBody: SteamSerializableDeserialize + IGCSerializableMessage,
{
fn is_proto(&self) -> bool {
false
}
fn msg_type(&self) -> u32 {
self.msg_type
}
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
}
fn deserialize(&mut self, data: &[u8]) -> Result<(), SteamError> {
let mut reader = Reader::new(data);
let header = MsgGCHdr::read(&mut reader)?;
let body = TBody::read(&mut reader)?;
let remaining = reader.remaining();
let rest = reader.read_up_to(remaining);
let mut payload = MsgBase::new(0);
if !rest.is_empty() {
payload.write_bytes(&rest);
payload.seek(0);
}
self.header = header;
self.body = body;
self.payload = payload;
Ok(())
}
}