diff --git a/lib/chunk.rb b/lib/chunk.rb index 1b983bb..3e45a55 100644 --- a/lib/chunk.rb +++ b/lib/chunk.rb @@ -3,7 +3,7 @@ require_relative 'network' require_relative 'bytes' class NetChunk - attr_reader :next, :data, :msg, :sys + attr_reader :next, :data, :msg, :sys, :flags def initialize(data) @next = nil @@ -37,6 +37,14 @@ class NetChunk # sequence number # in da third byte but who needs seq?! end + + def flags_vital + @flags[:vital] + end + + def flags_resend + @flags[:resend] + end end MAX_NUM_CHUNKS = 1024 diff --git a/lib/network.rb b/lib/network.rb index 0268b18..0bac43f 100644 --- a/lib/network.rb +++ b/lib/network.rb @@ -79,6 +79,8 @@ NET_CTRLMSG_ACCEPT = 2 NET_CTRLMSG_CLOSE = 4 NET_CTRLMSG_TOKEN = 5 +NET_MAX_SEQUENCE = 1<<10 + NET_CONNSTATE_OFFLINE = 0 NET_CONNSTATE_TOKEN = 1 NET_CONNSTATE_CONNECT = 2 diff --git a/lib/packet.rb b/lib/packet.rb index f8c2b6c..2ab73c3 100644 --- a/lib/packet.rb +++ b/lib/packet.rb @@ -1,5 +1,46 @@ require 'huffman_tw' +class PacketFlags + attr_reader :bits, :hash + + def initialize(data) + @hash = {} + @bits = '' + if data.class == Hash + @bits = parse_hash(data) + @hash = data + elsif data.class == String + @hash = parse_bits(data) + @bits = data + else + raise "Flags have to be hash or string" + end + end + + def parse_hash(hash) + bits = '' + bits += hash[:connection] == '1' ? '1' : '0' + bits += hash[:compressed] == '1' ? '1' : '0' + bits += hash[:resend] == '1' ? '1' : '0' + bits += hash[:control] == '1' ? '1' : '0' + bits + end + + def parse_bits(four_bit_str) + # takes a 4 character string + # representing the middle of the first byte sent + # in binary representation + # + # and creates a hash out of it + hash = {} + hash[:connection] = four_bit_str[0] == '1' + hash[:compressed] = four_bit_str[1] == '1' + hash[:resend] = four_bit_str[2] == '1' + hash[:control] = four_bit_str[3] == '1' + hash + end +end + # Class holding the parsed packet data class Packet attr_reader :flags, :payload @@ -14,10 +55,9 @@ class Packet # network direction (client/server) @prefix = prefix @huffman = Huffman.new - @flags = {} @data = data flags_byte = @data[0].unpack("B*") - parse_flags(flags_byte.first[2..5]) + @flags = PacketFlags.new(flags_byte.first[2..5]).hash @payload = @data[PACKET_HEADER_SIZE..] if flags_compressed @payload = @huffman.decompress(@payload.unpack("C*")) @@ -51,20 +91,6 @@ class Packet puts "" end - def parse_flags(four_bit_str) - # takes a 4 character string - # representing the middle of the first byte sent - # in binary representation - # - # and creates a hash out of it - @flags = {} - @flags[:connection] = four_bit_str[0] == '1' - @flags[:compressed] = four_bit_str[1] == '1' - @flags[:resend] = four_bit_str[2] == '1' - @flags[:control] = four_bit_str[3] == '1' - @flags - end - def flags_compressed() @flags[:compressed] end diff --git a/teeworlds.rb b/teeworlds.rb index f83efa1..dd9c4b0 100755 --- a/teeworlds.rb +++ b/teeworlds.rb @@ -11,25 +11,51 @@ require_relative 'lib/chunk' require_relative 'lib/server_info' class NetBase - attr_accessor :client_token, :server_token + attr_accessor :client_token, :server_token, :ack def initialize @ip = nil @port = nil @s = nil + @ack = 0 end def connect(socket, ip, port) @s = socket @ip = ip @port = port + @ack = 0 end ## # Sends a packing setting the proper header for you # # @param payload [Array] The Integer list representing the data after the header - def send_packet(payload) + # @param flags [Hash] Packet header flags for more details check the class +PacketFlags+ + def send_packet(payload, flags = {}) + # unsigned char flags_ack; // 6bit flags, 2bit ack + # unsigned char ack; // 8bit ack + # unsigned char numchunks; // 8bit chunks + # unsigned char token[4]; // 32bit token + # // ffffffaa + # // aaaaaaaa + # // NNNNNNNN + # // TTTTTTTT + # // TTTTTTTT + # // TTTTTTTT + # // TTTTTTTT + flags_bits = PacketFlags.new(flags).bits + header_bits = + '00' + # unused flags? # ff + flags_bits + # ffff + @ack.to_s(2).rjust(10, '0') # aa aaaa aaaa + + puts "header bits: #{header_bits}" + header = header_bits.chars.groups_of(4).map do |four_bits| + four_bits.join('').to_i(2) + end + puts "header bytes: #{str_hex(header.pack("C*"))}" + header = [0x00, 0x00, 0x01] + str_bytes(@server_token) data = (header + payload).pack('C*') @s.send(data, 0, @ip, @port) @@ -212,6 +238,10 @@ class TwClient def process_server_packet(data) chunks = BigChungusTheChunkGetter.get_chunks(data) chunks.each do |chunk| + if chunk.flags_vital + @netbase.ack = (@netbase.ack + 1) % NET_MAX_SEQUENCE + puts "got ack: #{@netbase.ack}" + end process_chunk(chunk) end end