# frozen_string_literal: true module Steam module Networking # An object representing a packet from the connection. # # The packets come in the form # # LENGTH MAGIC BODY # # To create a packet only the body needs to be supplied. The body should # never contain the MAGIC or LENGTH class Packet # Valve's TCP Packet identifier TCP_MAGIC = 'VT01' # The raw bytes of the TCP packet attr_reader :body # The EMsg type derived from the Packet body attr_reader :msg_type alias emsg msg_type # Instantiates a Packet object # # @param body [String] def initialize(body) raise 'packet must have raw tcp body' if body.nil? || body.empty? @body = body @io = ByteReader.new(StringIO.new(body)) @iden = ByteReader.new(StringIO.new(body)).unsigned_int32 @msg_type = @iden & ~Message::PROTO_MASK self end # Encode a Packet to a byte string # # @return [String] byte string representation of the Packet def encode stream = ByteWriter.new stream.write_unsigned_int32(body.length) stream.write(TCP_MAGIC) stream.write(body) stream.string end # Returns true if the Packet contains other packets # # @return [Bool] def multi? @msg_type == EMsg::MULTI end # Returns true if the Packet is Protobuf backed # # @return [Bool] def proto? (@iden & Message::PROTO_MASK) == Message::PROTO_MASK end # Converts the Packet into a Message object. # # @param msg [Message] The message to decode from the packet # @return [Message] the resulting message def as_message(msg) cm = if proto? ProtobufMessage.new(MsgHdrProtoBuf.new, msg, @msg_type) else ClientMessage.new(MsgHdr.new, msg, @msg_type) end cm.decode(@io) cm end end end end