diff --git a/lib/chunk.rb b/lib/chunk.rb index e79a5df..5219cae 100644 --- a/lib/chunk.rb +++ b/lib/chunk.rb @@ -4,7 +4,7 @@ require_relative 'bytes' class NetChunk attr_reader :next, :data, :msg, :sys, :flags - @@sent_vital_chunks = 1 + @@sent_vital_chunks = 0 def initialize(data) @next = nil diff --git a/lib/network.rb b/lib/network.rb index 3f1a5b5..7744459 100644 --- a/lib/network.rb +++ b/lib/network.rb @@ -1,18 +1,7 @@ -# randomize this -MY_TOKEN = [0x73, 0x34, 0xB4, 0xA0] - -MSG_TOKEN = [0x04, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x05] + MY_TOKEN + Array.new(512, 0x00) -MSG_INFO = [0x40, 0x19, 0x01, 0x03, 0x30, 0x2E, 0x37, 0x20, 0x38, 0x30, 0x32, 0x66, # @...0.7 802f - 0x31, 0x62, 0x65, 0x36, 0x30, 0x61, 0x30, 0x35, 0x36, 0x36, 0x35, 0x66, # 1be60a05665f - 0x00, 0x00, 0x85, 0x1C] -MSG_STARTINFO = [0x41, 0x19, 0x03, 0x36, 0x6E, 0x61, 0x6D, 0x65 , 0x6C, 0x65, 0x73, 0x73, # A..6nameless - 0x20, 0x6D, 0x65, 0x00, 0x00, 0x40, 0x67, 0x72 , 0x65, 0x65, 0x6E, 0x73, # me..@greens - 0x77, 0x61, 0x72, 0x64, 0x00, 0x64, 0x75, 0x6F , 0x64, 0x6F, 0x6E, 0x6E, # ward.duodonn - 0x79, 0x00, 0x00, 0x73, 0x74, 0x61, 0x6E, 0x64 , 0x61, 0x72, 0x64, 0x00, # y..standard - 0x73, 0x74, 0x61, 0x6E, 0x64, 0x61, 0x72, 0x64 , 0x00, 0x73, 0x74, 0x61, # standard.sta - 0x6E, 0x64, 0x61, 0x72, 0x64, 0x00, 0x01, 0x01 , 0x00, 0x00, 0x00, 0x00, # ndard....... - 0x80, 0xFC, 0xAF, 0x05, 0xEB, 0x83, 0xD0, 0x0A , 0x80, 0xFE, 0x07, 0x80, # ............ - 0xFE, 0x07, 0x80, 0xFE, 0x07, 0x80, 0xFE, 0x07] +GAME_VERSION = "0.7.5" +GAME_NETVERSION_HASH_FORCED = "802f1be60a05665f" +GAME_NETVERSION = "0.7 " + GAME_NETVERSION_HASH_FORCED +CLIENT_VERSION = 0x0705 NETMSG_NULL = 0 NETMSG_INFO = 1 diff --git a/lib/packer.rb b/lib/packer.rb index 4acb71d..5191f79 100644 --- a/lib/packer.rb +++ b/lib/packer.rb @@ -1,62 +1,87 @@ +require_relative 'array' + class Packer - # poor mans int packer - # homebrew not covering - # the full tw fancyness - # # Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign def self.pack_int(num) - if num > 63 || num < -63 - # the first byte can fit 6 bits - # because the first two bits are extended and sign - # so if you have a number bigger than 63 - # it needs two bytes - # which are also least significant byte first etc - # so we do not support that YET - # - # the first too big number 64 is represented as those bits - # 10000000 00000001 - # ^^^ ^ ^ ^ - # ||\ / \ / - # || \ / \ / - # || \ \/ - # || \ / - # || \ / - # || \ / - # || \/ - # || /\ - # || / \ - # || / \ - # || 00000001 000000 - # || | - # || v - # || 64 - # || - # |positive - # extended - raise "Numbers greater than 63 are not supported yet" - end + # the first byte can fit 6 bits + # because the first two bits are extended and sign + # so if you have a number bigger than 63 + # it needs two bytes + # which are also least significant byte first etc + # so we do not support that YET + # + # the first too big number 64 is represented as those bits + # 10000000 00000001 + # ^^^ ^ ^ ^ + # ||\ / \ / + # || \ / \ / + # || \ \/ + # || \ / + # || \ / + # || \ / + # || \/ + # || /\ + # || / \ + # || / \ + # || 00000001 000000 + # || | + # || v + # || 64 + # || + # |positive + # extended sign = '0' if num.negative? sign = '1' num += 1 end num = num.abs + if num > 63 || num < -63 + return self.pack_big_int(sign, num) + end ext = '0' bits = ext + sign + num.to_s(2).rjust(6, '0') [bits.to_i(2)] end + + def self.pack_big_int(sign, num) + num_bits = num.to_s(2) + first = '1' + sign + num_bits[-6..] + + num_bits = num_bits[0..-7] + bytes = [] + num_bits.chars.groups_of(7).each do |seven_bits| + # mark all as extended + bytes << '1' + seven_bits.join('').rjust(7, '0') + end + # least significant first + bytes = bytes.reverse + # mark last byte as unextended + bytes[-1][0] = '0' + ([first] + bytes).map { |b| b.to_i(2) } + end + + def self.pack_str(str) + str.chars.map(&:ord) + [0x00] + end end def todo_make_this_rspec_test + # single byte int p Packer.pack_int(1) == [1] + p Packer.pack_int(3) == [3] p Packer.pack_int(16) == [16] p Packer.pack_int(63) == [63] - p Packer.pack_int(3) == [3] + # negative single byte p Packer.pack_int(-1) == [64] p Packer.pack_int(-2) == [65] - # todo - # p Packer.pack_int(64) == [64] + # multi byte int + p Packer.pack_int(64) == [128, 1] + p Packer.pack_int(99999999999999999) == [191, 131, 255, 147, 246, 194, 215, 232, 88] + + # string + p Packer.pack_str("A") == [65, 0] end diff --git a/lib/teeworlds-client.rb b/lib/teeworlds-client.rb index 641ce04..63d7b66 100644 --- a/lib/teeworlds-client.rb +++ b/lib/teeworlds-client.rb @@ -39,7 +39,9 @@ class TwClient end end @ticks = 0 - @client_token = MY_TOKEN.map { |b| b.to_s(16) }.join('') + # me trying to write cool code + @client_token = (1..4).to_a.map { |_| rand(0..255) } + @client_token = @client_token.map { |b| b.to_s(16) }.join('') puts "client token #{@client_token}" @netbase = NetBase.new @netbase.client_token = @client_token @@ -99,7 +101,15 @@ class TwClient end def send_info() - send_msg(MSG_INFO) + data = [] + data += Packer.pack_str(GAME_NETVERSION) + data += Packer.pack_str("password") + data += Packer.pack_int(CLIENT_VERSION) + msg = NetChunk.create_vital_header({vital: true}, data.size + 1) + + [pack_msg_id(NETMSG_INFO, system: true)] + + data + + @netbase.send_packet(msg, 1) end def send_msg_startinfo() @@ -131,7 +141,7 @@ class TwClient start_info.each do |key, value| if value.class == String - data += value.chars.map(&:ord) + [0x00] + data += Packer.pack_str(value) elsif value.class == Integer data += Packer.pack_int(value) else @@ -177,7 +187,8 @@ class TwClient CHAT_ALL, 64 # should use TARGET_SERVER (-1) instead of hacking 64 in here ] + - str.chars.map(&:ord) + [0x00]) + Packer.pack_str(str) + ) end def send_input