rubocop -a
This commit is contained in:
parent
0be954538c
commit
ddef46991b
|
@ -9,15 +9,15 @@ client = TeeworldsClient.new(verbose: true)
|
|||
# all keys are optional
|
||||
# if not provided they will fall back to the default value
|
||||
client.set_startinfo(
|
||||
name: "ruby gamer",
|
||||
clan: "",
|
||||
name: 'ruby gamer',
|
||||
clan: '',
|
||||
country: -1,
|
||||
body: "spiky",
|
||||
marking: "duodonny",
|
||||
decoration: "",
|
||||
hands: "standard",
|
||||
feet: "standard",
|
||||
eyes: "standard",
|
||||
body: 'spiky',
|
||||
marking: 'duodonny',
|
||||
decoration: '',
|
||||
hands: 'standard',
|
||||
feet: 'standard',
|
||||
eyes: 'standard',
|
||||
custom_color_body: 0,
|
||||
custom_color_marking: 0,
|
||||
custom_color_decoration: 0,
|
||||
|
@ -29,7 +29,8 @@ client.set_startinfo(
|
|||
color_decoration: 0,
|
||||
color_hands: 0,
|
||||
color_feet: 0,
|
||||
color_eyes: 0)
|
||||
color_eyes: 0
|
||||
)
|
||||
|
||||
# connect to localhost and block the current thread
|
||||
client.connect('localhost', 8303, detach: false)
|
||||
|
|
|
@ -4,7 +4,7 @@ class Array
|
|||
|
||||
groups = []
|
||||
group = []
|
||||
self.each do |item|
|
||||
each do |item|
|
||||
group.push(item)
|
||||
|
||||
if group.size >= max_size
|
||||
|
@ -16,4 +16,3 @@ class Array
|
|||
groups
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
# turn byte array into hex string
|
||||
def str_hex(data)
|
||||
data.unpack("H*").first.scan(/../).join(' ').upcase
|
||||
data.unpack1('H*').scan(/../).join(' ').upcase
|
||||
end
|
||||
|
||||
# turn hex string to byte array
|
||||
|
@ -10,9 +9,9 @@ def str_bytes(str)
|
|||
end
|
||||
|
||||
def bytes_to_str(data)
|
||||
data.unpack("H*").join('')
|
||||
data.unpack('H*').join('')
|
||||
end
|
||||
|
||||
def get_byte(data, start = 0, num = 1)
|
||||
data[start...(start+num)].unpack("H*").join('').upcase
|
||||
data[start...(start + num)].unpack('H*').join('').upcase
|
||||
end
|
||||
|
|
|
@ -27,4 +27,3 @@ class ChatMesage
|
|||
"#{@author.name}: #{@message}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
25
lib/chunk.rb
25
lib/chunk.rb
|
@ -4,6 +4,7 @@ require_relative 'bytes'
|
|||
|
||||
class NetChunk
|
||||
attr_reader :next, :data, :msg, :sys, :flags
|
||||
|
||||
@@sent_vital_chunks = 0
|
||||
|
||||
def initialize(data)
|
||||
|
@ -14,8 +15,8 @@ class NetChunk
|
|||
chunk_end = CHUNK_HEADER_SIZE + @size
|
||||
# puts "data[0]: " + str_hex(data[0])
|
||||
@data = data[CHUNK_HEADER_SIZE...chunk_end]
|
||||
@msg = @data[0].unpack("C*").first
|
||||
@sys = @msg & 1 == 1 ? true : false
|
||||
@msg = @data[0].unpack1('C*')
|
||||
@sys = @msg & 1 == 1
|
||||
@msg >>= 1
|
||||
@next = data[chunk_end..] if data.size > chunk_end
|
||||
end
|
||||
|
@ -42,9 +43,7 @@ class NetChunk
|
|||
# represented as an Array of 3 integers
|
||||
def self.create_vital_header(flags, size, seq = nil)
|
||||
@@sent_vital_chunks += 1
|
||||
if seq.nil?
|
||||
seq = @@sent_vital_chunks
|
||||
end
|
||||
seq = @@sent_vital_chunks if seq.nil?
|
||||
|
||||
flag_bits = '00'
|
||||
flag_bits[0] = flags[:resend] ? '1' : '0'
|
||||
|
@ -54,7 +53,6 @@ class NetChunk
|
|||
# size_bits[0..5]
|
||||
# size_bits[6..]
|
||||
|
||||
|
||||
seq_bits = seq.to_s(2).rjust(10, '0')
|
||||
# seq_bits[0..1]
|
||||
# seq_bits[2..]
|
||||
|
@ -81,13 +79,13 @@ class NetChunk
|
|||
|
||||
def parse_header(data)
|
||||
# flags
|
||||
flags = data[0].unpack("B*").first
|
||||
flags = data[0].unpack1('B*')
|
||||
flags = flags[0..1]
|
||||
@flags[:resend] = flags[0] == "1"
|
||||
@flags[:vital] = flags[1] == "1"
|
||||
@flags[:resend] = flags[0] == '1'
|
||||
@flags[:vital] = flags[1] == '1'
|
||||
|
||||
# size
|
||||
size = data[0..1].unpack("B*").first
|
||||
size = data[0..1].unpack1('B*')
|
||||
size_bytes = size.chars.groups_of(8)
|
||||
# trim first 2 bits of both bytes
|
||||
# Size: 2 bytes (..00 0000 ..00 0010)
|
||||
|
@ -117,13 +115,12 @@ class BigChungusTheChunkGetter
|
|||
while chunk.next
|
||||
chunk = NetChunk.new(chunk.next)
|
||||
chunks.push(chunk)
|
||||
if chunks.size > MAX_NUM_CHUNKS
|
||||
next unless chunks.size > MAX_NUM_CHUNKS
|
||||
|
||||
# inf loop guard case
|
||||
puts "Warning: abort due to max num chunks bein reached"
|
||||
puts 'Warning: abort due to max num chunks bein reached'
|
||||
break
|
||||
end
|
||||
end
|
||||
chunks
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ class Context
|
|||
end
|
||||
|
||||
def verify
|
||||
@data.each do |key, value|
|
||||
@data.each do |key, _value|
|
||||
next if @old_data.key? key
|
||||
|
||||
raise "Error: invalid data key '#{key}'\n valid keys: #{@old_data.keys}"
|
||||
|
@ -42,19 +42,20 @@ class GameClient
|
|||
# puts "Got playerinfo flags: #{chunk.flags}"
|
||||
u = Unpacker.new(chunk.data[1..])
|
||||
player = Player.new(
|
||||
id: u.get_int(),
|
||||
local: u.get_int(),
|
||||
team: u.get_int(),
|
||||
name: u.get_string(),
|
||||
clan: u.get_string(),
|
||||
country: u.get_int())
|
||||
id: u.get_int,
|
||||
local: u.get_int,
|
||||
team: u.get_int,
|
||||
name: u.get_string,
|
||||
clan: u.get_string,
|
||||
country: u.get_int
|
||||
)
|
||||
# skinparts and the silent flag
|
||||
# are currently ignored
|
||||
|
||||
context = Context.new(
|
||||
@client,
|
||||
player: player,
|
||||
chunk: chunk
|
||||
player:,
|
||||
chunk:
|
||||
)
|
||||
if @client.hooks[:client_info]
|
||||
@client.hooks[:client_info].call(context)
|
||||
|
@ -68,17 +69,17 @@ class GameClient
|
|||
|
||||
def on_client_drop(chunk)
|
||||
u = Unpacker.new(chunk.data[1..])
|
||||
client_id = u.get_int()
|
||||
reason = u.get_string()
|
||||
silent = u.get_int()
|
||||
client_id = u.get_int
|
||||
reason = u.get_string
|
||||
silent = u.get_int
|
||||
|
||||
context = Context.new(
|
||||
@cliemt,
|
||||
player: @players[client_id],
|
||||
chunk: chunk,
|
||||
client_id: client_id,
|
||||
chunk:,
|
||||
client_id:,
|
||||
reason: reason == '' ? nil : reason,
|
||||
silent: silent
|
||||
silent:
|
||||
)
|
||||
if @client.hooks[:client_drop]
|
||||
@client.hooks[:client_drop].call(context)
|
||||
|
@ -89,7 +90,7 @@ class GameClient
|
|||
@players.delete(context.data[:client_id])
|
||||
end
|
||||
|
||||
def on_ready_to_enter(chunk)
|
||||
def on_ready_to_enter(_chunk)
|
||||
@client.send_enter_game
|
||||
end
|
||||
|
||||
|
@ -103,11 +104,10 @@ class GameClient
|
|||
@client.send_msg_startinfo
|
||||
end
|
||||
|
||||
def on_emoticon(chunk)
|
||||
end
|
||||
def on_emoticon(chunk); end
|
||||
|
||||
def on_map_change(chunk)
|
||||
context = Context.new(@client, chunk: chunk)
|
||||
context = Context.new(@client, chunk:)
|
||||
if @client.hooks[:map_change]
|
||||
@client.hooks[:map_change].call(context)
|
||||
context.verify
|
||||
|
@ -121,17 +121,14 @@ class GameClient
|
|||
def on_chat(chunk)
|
||||
u = Unpacker.new(chunk.data[1..])
|
||||
data = {
|
||||
mode: u.get_int(),
|
||||
client_id: u.get_int(),
|
||||
target_id: u.get_int(),
|
||||
message: u.get_string()
|
||||
mode: u.get_int,
|
||||
client_id: u.get_int,
|
||||
target_id: u.get_int,
|
||||
message: u.get_string
|
||||
}
|
||||
data[:author] = @players[data[:client_id]]
|
||||
msg = ChatMesage.new(data)
|
||||
|
||||
if @client.hooks[:chat]
|
||||
@client.hooks[:chat].call(msg)
|
||||
@client.hooks[:chat].call(msg) if @client.hooks[:chat]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ class NetBase
|
|||
eight_bits.join('').to_i(2)
|
||||
end
|
||||
|
||||
header = header + str_bytes(@server_token)
|
||||
header += str_bytes(@server_token)
|
||||
data = (header + payload).pack('C*')
|
||||
@s.send(data, 0, @ip, @port)
|
||||
|
||||
|
@ -54,4 +54,3 @@ class NetBase
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
GAME_VERSION = "0.7.5"
|
||||
GAME_NETVERSION_HASH_FORCED = "802f1be60a05665f"
|
||||
GAME_NETVERSION = "0.7 " + GAME_NETVERSION_HASH_FORCED
|
||||
GAME_VERSION = '0.7.5'
|
||||
GAME_NETVERSION_HASH_FORCED = '802f1be60a05665f'
|
||||
GAME_NETVERSION = '0.7 ' + GAME_NETVERSION_HASH_FORCED
|
||||
CLIENT_VERSION = 0x0705
|
||||
|
||||
NETMSG_NULL = 0
|
||||
|
@ -97,4 +97,3 @@ TARGET_SERVER = -1
|
|||
|
||||
PACKET_HEADER_SIZE = 7
|
||||
CHUNK_HEADER_SIZE = 3
|
||||
|
||||
|
|
|
@ -36,9 +36,8 @@ class Packer
|
|||
num += 1
|
||||
end
|
||||
num = num.abs
|
||||
if num > 63 || num < -63
|
||||
return self.pack_big_int(sign, num)
|
||||
end
|
||||
return pack_big_int(sign, num) if num > 63 || num < -63
|
||||
|
||||
ext = '0'
|
||||
bits = ext + sign + num.to_s(2).rjust(6, '0')
|
||||
[bits.to_i(2)]
|
||||
|
@ -69,25 +68,25 @@ end
|
|||
class Unpacker
|
||||
def initialize(data)
|
||||
@data = data
|
||||
if data.class == String
|
||||
@data = data.unpack("C*")
|
||||
elsif data.class == Array
|
||||
if data.instance_of?(String)
|
||||
@data = data.unpack('C*')
|
||||
elsif data.instance_of?(Array)
|
||||
@data = data
|
||||
else
|
||||
raise "Error: Unpacker expects array of integers or byte string"
|
||||
raise 'Error: Unpacker expects array of integers or byte string'
|
||||
end
|
||||
end
|
||||
|
||||
def get_string()
|
||||
def get_string
|
||||
return nil if @data.nil?
|
||||
|
||||
str = ''
|
||||
@data.each_with_index do |byte, index|
|
||||
if byte == 0x00
|
||||
if index == @data.length - 1
|
||||
@data = nil
|
||||
@data = if index == @data.length - 1
|
||||
nil
|
||||
else
|
||||
@data = @data[(index + 1)..]
|
||||
@data[(index + 1)..]
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
@ -98,10 +97,10 @@ class Unpacker
|
|||
''
|
||||
end
|
||||
|
||||
def get_int()
|
||||
def get_int
|
||||
return nil if @data.nil?
|
||||
|
||||
# todo: make this more performant
|
||||
# TODO: make this more performant
|
||||
# it should not read in ALL bytes
|
||||
# of the WHOLE packed data
|
||||
# it should be max 4 bytes
|
||||
|
@ -150,8 +149,8 @@ def todo_make_this_rspec_test
|
|||
# p Packer.pack_int(-3).first.to_s(2) == '1000010'
|
||||
# p Packer.pack_int(-4).first.to_s(2) == '1000011'
|
||||
|
||||
p Packer.pack_int(64).map { |e| e.to_s(2).rjust(8, '0') } == ["10000000", "00000001"]
|
||||
p Packer.pack_int(-64).map { |e| e.to_s(2).rjust(8, '0') } == ["11000000", "00000000"]
|
||||
p Packer.pack_int(64).map { |e| e.to_s(2).rjust(8, '0') } == %w[10000000 00000001]
|
||||
p Packer.pack_int(-64).map { |e| e.to_s(2).rjust(8, '0') } == %w[11000000 00000000]
|
||||
|
||||
# # multi byte int
|
||||
# p Packer.pack_int(64) == [128, 1]
|
||||
|
@ -184,8 +183,8 @@ def todo_also_rspec_unpacker
|
|||
# p u.get_int() == 64
|
||||
|
||||
u = Unpacker.new([128, 1, 128, 1])
|
||||
p u.get_int() == 64
|
||||
p u.get_int() == 64
|
||||
p u.get_int == 64
|
||||
p u.get_int == 64
|
||||
# p u.get_int() == nil
|
||||
|
||||
# (-128..128).each do |i|
|
||||
|
@ -194,15 +193,14 @@ def todo_also_rspec_unpacker
|
|||
# end
|
||||
|
||||
u = Unpacker.new(['00000001'.to_i(2)])
|
||||
p u.get_int() == 1
|
||||
p u.get_int == 1
|
||||
|
||||
u = Unpacker.new(['10000000'.to_i(2), '00000001'.to_i(2)])
|
||||
p u.get_int() == 64
|
||||
p u.get_int == 64
|
||||
|
||||
# todo should be -64
|
||||
# TODO: should be -64
|
||||
# u = Unpacker.new(['11000000'.to_i(2), '00000000'.to_i(2)])
|
||||
# p u.get_int()
|
||||
end
|
||||
|
||||
# todo_also_rspec_unpacker
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@ class PacketFlags
|
|||
def initialize(data)
|
||||
@hash = {}
|
||||
@bits = ''
|
||||
if data.class == Hash
|
||||
if data.instance_of?(Hash)
|
||||
@bits = parse_hash(data)
|
||||
@hash = data
|
||||
elsif data.class == String
|
||||
elsif data.instance_of?(String)
|
||||
@hash = parse_bits(data)
|
||||
@bits = data
|
||||
else
|
||||
raise "Flags have to be hash or string"
|
||||
raise 'Flags have to be hash or string'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -56,12 +56,12 @@ class Packet
|
|||
@prefix = prefix
|
||||
@huffman = Huffman.new
|
||||
@data = data
|
||||
flags_byte = @data[0].unpack("B*")
|
||||
flags_byte = @data[0].unpack('B*')
|
||||
@flags = PacketFlags.new(flags_byte.first[2..5]).hash
|
||||
@payload = @data[PACKET_HEADER_SIZE..]
|
||||
if flags_compressed
|
||||
@payload = @huffman.decompress(@payload.unpack("C*"))
|
||||
@payload = @payload.pack("C*")
|
||||
@payload = @huffman.decompress(@payload.unpack('C*'))
|
||||
@payload = @payload.pack('C*')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -70,37 +70,36 @@ class Packet
|
|||
token = bytes[3..6].join(' ').green
|
||||
payload = bytes[7..].join(' ')
|
||||
puts @prefix + " data: #{[header, token, payload].join(' ')}"
|
||||
print @prefix + " "
|
||||
print "header".ljust(3 * 3, ' ').yellow
|
||||
print "token".ljust(4 * 3, ' ').green
|
||||
puts "data"
|
||||
print @prefix + ' '
|
||||
print 'header'.ljust(3 * 3, ' ').yellow
|
||||
print 'token'.ljust(4 * 3, ' ').green
|
||||
puts 'data'
|
||||
end
|
||||
|
||||
def to_s()
|
||||
puts @prefix + "Packet"
|
||||
def to_s
|
||||
puts @prefix + 'Packet'
|
||||
puts @prefix + " flags: #{@flags}"
|
||||
bytes = str_hex(@data).split(' ')
|
||||
# todo: check terminal size?
|
||||
# TODO: check terminal size?
|
||||
max_width = 14
|
||||
rows = bytes.groups_of(max_width)
|
||||
annotate_first_row(rows.first)
|
||||
rows[1..].each do |row|
|
||||
print @prefix + " "
|
||||
print @prefix + ' '
|
||||
puts row.join(' ')
|
||||
end
|
||||
puts ""
|
||||
puts ''
|
||||
end
|
||||
|
||||
def flags_compressed()
|
||||
def flags_compressed
|
||||
@flags[:compressed]
|
||||
end
|
||||
|
||||
def flags_connless()
|
||||
def flags_connless
|
||||
@flags[:connection] == false
|
||||
end
|
||||
|
||||
def flags_control()
|
||||
def flags_control
|
||||
@flags[:control]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
class Player
|
||||
attr_accessor :id, :local, :team, :name, :clan
|
||||
attr_accessor :country
|
||||
attr_accessor :skin_parts, :skin_custom_colors, :skin_colors
|
||||
attr_accessor :id, :local, :team, :name, :clan, :country, :skin_parts, :skin_custom_colors, :skin_colors
|
||||
|
||||
def initialize(data = {})
|
||||
@id = data[:id] || -1
|
||||
|
@ -15,4 +13,3 @@ class Player
|
|||
@skin_colors = data[:skin_colors] || Array.new(6, 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,4 +12,3 @@ class ServerInfo
|
|||
"version=#{@version} gametype=#{gametype} map=#{map} name=#{name}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,4 +22,3 @@ class String
|
|||
colorize(35)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
100
lib/teeworlds-client.rb
Normal file → Executable file
100
lib/teeworlds-client.rb
Normal file → Executable file
|
@ -28,15 +28,15 @@ class TeeworldsClient
|
|||
@signal_disconnect = false
|
||||
@game_client = GameClient.new(self)
|
||||
@start_info = {
|
||||
name: "ruby gamer",
|
||||
clan: "",
|
||||
name: 'ruby gamer',
|
||||
clan: '',
|
||||
country: -1,
|
||||
body: "spiky",
|
||||
marking: "duodonny",
|
||||
decoration: "",
|
||||
hands: "standard",
|
||||
feet: "standard",
|
||||
eyes: "standard",
|
||||
body: 'spiky',
|
||||
marking: 'duodonny',
|
||||
decoration: '',
|
||||
hands: 'standard',
|
||||
feet: 'standard',
|
||||
eyes: 'standard',
|
||||
custom_color_body: 0,
|
||||
custom_color_marking: 0,
|
||||
custom_color_decoration: 0,
|
||||
|
@ -86,12 +86,10 @@ class TeeworldsClient
|
|||
|
||||
def connect(ip, port, options = {})
|
||||
options[:detach] = options[:detach] || false
|
||||
if options[:detach]
|
||||
if @thread_running
|
||||
puts "Error: connection thread already running call disconnect() first"
|
||||
if options[:detach] && @thread_running
|
||||
puts 'Error: connection thread already running call disconnect() first'
|
||||
return
|
||||
end
|
||||
end
|
||||
disconnect
|
||||
@signal_disconnect = false
|
||||
@ticks = 0
|
||||
|
@ -123,13 +121,9 @@ class TeeworldsClient
|
|||
end
|
||||
|
||||
def disconnect
|
||||
puts "disconnecting."
|
||||
unless @netbase.nil?
|
||||
@netbase.send_packet([NET_CTRLMSG_CLOSE], 0, control: true)
|
||||
end
|
||||
unless @s.nil?
|
||||
@s.close
|
||||
end
|
||||
puts 'disconnecting.'
|
||||
@netbase.send_packet([NET_CTRLMSG_CLOSE], 0, control: true) unless @netbase.nil?
|
||||
@s.close unless @s.nil?
|
||||
@signal_disconnect = true
|
||||
end
|
||||
|
||||
|
@ -148,25 +142,25 @@ class TeeworldsClient
|
|||
@netbase.send_packet(data)
|
||||
end
|
||||
|
||||
def send_ctrl_keepalive()
|
||||
def send_ctrl_keepalive
|
||||
@netbase.send_packet([NET_CTRLMSG_KEEPALIVE], 0, control: true)
|
||||
end
|
||||
|
||||
def send_msg_connect()
|
||||
def send_msg_connect
|
||||
msg = [NET_CTRLMSG_CONNECT] + str_bytes(@client_token) + Array.new(501, 0x00)
|
||||
@netbase.send_packet(msg, 0, control: true)
|
||||
end
|
||||
|
||||
def send_ctrl_with_token()
|
||||
def send_ctrl_with_token
|
||||
@state = NET_CONNSTATE_TOKEN
|
||||
msg = [NET_CTRLMSG_TOKEN] + str_bytes(@client_token) + Array.new(512, 0x00)
|
||||
@netbase.send_packet(msg, 0, control: true)
|
||||
end
|
||||
|
||||
def send_info()
|
||||
def send_info
|
||||
data = []
|
||||
data += Packer.pack_str(GAME_NETVERSION)
|
||||
data += Packer.pack_str("password")
|
||||
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)] +
|
||||
|
@ -175,13 +169,13 @@ class TeeworldsClient
|
|||
@netbase.send_packet(msg, 1)
|
||||
end
|
||||
|
||||
def send_msg_startinfo()
|
||||
def send_msg_startinfo
|
||||
data = []
|
||||
|
||||
@start_info.each do |key, value|
|
||||
if value.class == String
|
||||
if value.instance_of?(String)
|
||||
data += Packer.pack_str(value)
|
||||
elsif value.class == Integer
|
||||
elsif value.instance_of?(Integer)
|
||||
data += Packer.pack_int(value)
|
||||
else
|
||||
puts "Error: invalid startinfo #{key}: #{value}"
|
||||
|
@ -196,16 +190,18 @@ class TeeworldsClient
|
|||
)
|
||||
end
|
||||
|
||||
def send_msg_ready()
|
||||
def send_msg_ready
|
||||
@netbase.send_packet(
|
||||
NetChunk.create_vital_header({ vital: true }, 1) +
|
||||
[pack_msg_id(NETMSG_READY, system: true)])
|
||||
[pack_msg_id(NETMSG_READY, system: true)]
|
||||
)
|
||||
end
|
||||
|
||||
def send_enter_game()
|
||||
def send_enter_game
|
||||
@netbase.send_packet(
|
||||
NetChunk.create_vital_header({ vital: true }, 1) +
|
||||
[pack_msg_id(NETMSG_ENTERGAME, system: true)])
|
||||
[pack_msg_id(NETMSG_ENTERGAME, system: true)]
|
||||
)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -219,7 +215,7 @@ class TeeworldsClient
|
|||
end
|
||||
|
||||
def send_input
|
||||
header = [0x10, 0x0A, 01] + str_bytes(@token)
|
||||
header = [0x10, 0x0A, 0o1] + str_bytes(@token)
|
||||
random_compressed_input = [
|
||||
0x4D, 0xE9, 0x48, 0x13, 0xD0, 0x0B, 0x6B, 0xFC, 0xB7, 0x2B, 0x6E, 0x00, 0xBA
|
||||
]
|
||||
|
@ -237,17 +233,17 @@ class TeeworldsClient
|
|||
@token = bytes_to_str(data)
|
||||
@netbase.server_token = @token
|
||||
puts "Got token #{@token}"
|
||||
send_msg_connect()
|
||||
send_msg_connect
|
||||
end
|
||||
|
||||
def on_msg_accept
|
||||
puts "got accept. connection online"
|
||||
puts 'got accept. connection online'
|
||||
@state = NET_CONNSTATE_ONLINE
|
||||
send_info
|
||||
end
|
||||
|
||||
def on_msg_close
|
||||
puts "got NET_CTRLMSG_CLOSE"
|
||||
puts 'got NET_CTRLMSG_CLOSE'
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -258,7 +254,7 @@ class TeeworldsClient
|
|||
when NET_CTRLMSG_TOKEN then on_msg_token(data)
|
||||
when NET_CTRLMSG_ACCEPT then on_msg_accept
|
||||
when NET_CTRLMSG_CLOSE then on_msg_close
|
||||
when NET_CTRLMSG_KEEPALIVE then # silently ignore keepalive
|
||||
when NET_CTRLMSG_KEEPALIVE # silently ignore keepalive
|
||||
else
|
||||
puts "Uknown control message #{msg}"
|
||||
exit(1)
|
||||
|
@ -273,14 +269,12 @@ class TeeworldsClient
|
|||
when NETMSGTYPE_SV_EMOTICON then @game_client.on_emoticon(chunk)
|
||||
when NETMSGTYPE_SV_CHAT then @game_client.on_chat(chunk)
|
||||
else
|
||||
if @verbose
|
||||
puts "todo non sys chunks. skipped msg: #{chunk.msg}"
|
||||
end
|
||||
puts "todo non sys chunks. skipped msg: #{chunk.msg}" if @verbose
|
||||
end
|
||||
end
|
||||
|
||||
def process_chunk(chunk)
|
||||
if !chunk.sys
|
||||
unless chunk.sys
|
||||
on_message(chunk)
|
||||
return
|
||||
end
|
||||
|
@ -289,7 +283,7 @@ class TeeworldsClient
|
|||
when NETMSG_MAP_CHANGE
|
||||
@game_client.on_map_change(chunk)
|
||||
when NETMSG_SERVERINFO
|
||||
puts "ignore server info for now"
|
||||
puts 'ignore server info for now'
|
||||
when NETMSG_CON_READY
|
||||
@game_client.on_connected
|
||||
when NETMSG_NULL
|
||||
|
@ -303,7 +297,7 @@ class TeeworldsClient
|
|||
def process_server_packet(packet)
|
||||
data = packet.payload
|
||||
if data.size.zero?
|
||||
puts "Error: packet payload is empty"
|
||||
puts 'Error: packet payload is empty'
|
||||
puts packet.to_s
|
||||
return
|
||||
end
|
||||
|
@ -311,9 +305,7 @@ class TeeworldsClient
|
|||
chunks.each do |chunk|
|
||||
if chunk.flags_vital && !chunk.flags_resend
|
||||
@netbase.ack = (@netbase.ack + 1) % NET_MAX_SEQUENCE
|
||||
if @verbose
|
||||
puts "got ack: #{@netbase.ack}"
|
||||
end
|
||||
puts "got ack: #{@netbase.ack}" if @verbose
|
||||
end
|
||||
process_chunk(chunk)
|
||||
end
|
||||
|
@ -327,12 +319,12 @@ class TeeworldsClient
|
|||
pck = nil
|
||||
end
|
||||
if pck.nil? && @token.nil?
|
||||
@wait_for_token = @wait_for_token || 0
|
||||
@wait_for_token ||= 0
|
||||
@wait_for_token += 1
|
||||
if @wait_for_token > 6
|
||||
@token = nil
|
||||
send_ctrl_with_token
|
||||
puts "retrying connection ..."
|
||||
puts 'retrying connection ...'
|
||||
end
|
||||
end
|
||||
return unless pck
|
||||
|
@ -340,22 +332,18 @@ class TeeworldsClient
|
|||
data = pck.first
|
||||
|
||||
packet = Packet.new(data, '<')
|
||||
if @verbose
|
||||
puts packet.to_s
|
||||
end
|
||||
puts packet.to_s if @verbose
|
||||
|
||||
# process connless packets data
|
||||
if packet.flags_control
|
||||
msg = data[PACKET_HEADER_SIZE].unpack("C*").first
|
||||
msg = data[PACKET_HEADER_SIZE].unpack1('C*')
|
||||
on_ctrl_message(msg, data[(PACKET_HEADER_SIZE + 1)..])
|
||||
else # process non-connless packets
|
||||
process_server_packet(packet)
|
||||
end
|
||||
|
||||
@ticks += 1
|
||||
if @ticks % 8 == 0
|
||||
send_ctrl_keepalive
|
||||
end
|
||||
send_ctrl_keepalive if @ticks % 8 == 0
|
||||
# if @ticks % 20 == 0
|
||||
# send_chat("hello world")
|
||||
# end
|
||||
|
@ -364,12 +352,10 @@ class TeeworldsClient
|
|||
def connection_loop
|
||||
until @signal_disconnect
|
||||
tick
|
||||
# todo: proper tick speed sleep
|
||||
# TODO: proper tick speed sleep
|
||||
sleep 0.001
|
||||
end
|
||||
@thread_running = false
|
||||
@signal_disconnect = false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
16
sample.rb
16
sample.rb
|
@ -5,15 +5,15 @@ require_relative 'lib/teeworlds-client'
|
|||
args = { verbose: false, ip: nil, port: nil }
|
||||
|
||||
ARGV.each do |arg|
|
||||
if arg == '--help' || arg == '-h'
|
||||
puts "usage: teeworlds.rb [OPTIONS..] [host] [port]"
|
||||
echo "options:"
|
||||
echo " --help|-h show this help"
|
||||
echo " --verbose|-v verbose output"
|
||||
echo "example:"
|
||||
echo " teeworlds.rb --verbose localhost 8303"
|
||||
if ['--help', '-h'].include?(arg)
|
||||
puts 'usage: teeworlds.rb [OPTIONS..] [host] [port]'
|
||||
echo 'options:'
|
||||
echo ' --help|-h show this help'
|
||||
echo ' --verbose|-v verbose output'
|
||||
echo 'example:'
|
||||
echo ' teeworlds.rb --verbose localhost 8303'
|
||||
exit(0)
|
||||
elsif arg == '--verbose' || arg == '-v'
|
||||
elsif ['--verbose', '-v'].include?(arg)
|
||||
args[:verbose] = true
|
||||
elsif args[:ip].nil?
|
||||
args[:ip] = arg
|
||||
|
|
|
@ -10,4 +10,3 @@ describe 'Array', :array do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
require_relative '../lib/packet'
|
||||
|
||||
|
||||
describe 'Packet', :packet do
|
||||
context 'Set flag bits' do
|
||||
it 'Should set the control flag bit' do
|
||||
expect(PacketFlags.new(control: true).bits).to eq("0001")
|
||||
expect(PacketFlags.new(control: true).bits).to eq('0001')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ describe 'BigChungusTheChunkGetter', :chunk_getter do
|
|||
# one empty motd chunks
|
||||
data = [
|
||||
0x40, 0x02, 0x02, 0x02, 0x00
|
||||
].pack("C*")
|
||||
].pack('C*')
|
||||
chunks = BigChungusTheChunkGetter.get_chunks(data)
|
||||
expect(chunks.size).to eq(1)
|
||||
end
|
||||
|
@ -28,7 +28,7 @@ describe 'BigChungusTheChunkGetter', :chunk_getter do
|
|||
data = [
|
||||
0x40, 0x02, 0x02, 0x02, 0x00,
|
||||
0x40, 0x02, 0x02, 0x02, 0x00
|
||||
].pack("C*")
|
||||
].pack('C*')
|
||||
chunks = BigChungusTheChunkGetter.get_chunks(data)
|
||||
expect(chunks.size).to eq(2)
|
||||
expect(chunks[0].msg).to eq(NETMSGTYPE_SV_MOTD)
|
||||
|
@ -42,7 +42,7 @@ describe 'BigChungusTheChunkGetter', :chunk_getter do
|
|||
0x40, 0x02, 0x02, 0x02, 0x00, # motd
|
||||
0x40, 0x07, 0x03, 0x22, 0x01, 0x00, 0x01, 0x00, 0x01, 0x08, # server settings
|
||||
0x40, 0x01, 0x04, 0x0b # ready
|
||||
].pack("C*")
|
||||
].pack('C*')
|
||||
chunks = BigChungusTheChunkGetter.get_chunks(data)
|
||||
expect(chunks.size).to eq(3)
|
||||
expect(chunks[0].msg).to eq(NETMSGTYPE_SV_MOTD)
|
||||
|
@ -56,11 +56,10 @@ describe 'BigChungusTheChunkGetter', :chunk_getter do
|
|||
0xee, 0xcb, 0xd0, 0xd7, 0x02, 0x9c, 0x0e, 0x08, 0xa8, 0x15, 0x1a, 0xb3, 0xbb, 0xb1, 0xd4, 0x04,
|
||||
0x75, 0x68, 0xec, 0xe3, 0x41, 0x6e, 0x83, 0x20, 0xaf, 0x97, 0x0f, 0x49, 0xbe, 0x4f, 0x3c, 0x61,
|
||||
0x04, 0xf4, 0xbe, 0x60, 0xd2, 0x87, 0x39, 0x91, 0x59, 0xab
|
||||
].pack("C*")
|
||||
].pack('C*')
|
||||
chunks = BigChungusTheChunkGetter.get_chunks(map_change)
|
||||
expect(chunks.size).to eq(1)
|
||||
expect(chunks[0].sys).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rake'
|
||||
|
|
Loading…
Reference in a new issue