Count ack per client and thus fix reconnect

Sadly reconnecting somehow crashes the client :(

https://github.com/teeworlds/teeworlds/issues/3182
This commit is contained in:
ChillerDragon 2022-11-13 08:28:27 +01:00
parent 409f880f36
commit 09bd7bfebb
3 changed files with 48 additions and 29 deletions

View file

@ -40,9 +40,8 @@ class NetBase
# Sends a packing setting the proper header for you
#
# @param payload [Array] The Integer list representing the data after the header
# @param num_chunks [Integer] Amount of NetChunks in the payload
# @param flags [Hash] Packet header flags for more details check the class +PacketFlags+
def send_packet(payload, num_chunks = 1, opts = {})
# @param opts [Hash] :chunks, :client and packet header flags for more details check the class +PacketFlags+
def send_packet(payload, opts = { chunks: 1, client: nil, addr: nil })
# unsigned char flags_ack; // 6bit flags, 2bit ack
# unsigned char ack; // 8bit ack
# unsigned char numchunks; // 8bit chunks
@ -55,9 +54,21 @@ class NetBase
# // TTTTTTTT
# // TTTTTTTT
flags_bits = PacketFlags.new(opts).bits
ack = @ack
ip = @ip
port = @port
unless opts[:client].nil?
ack = opts[:client].ack
ip = opts[:client].addr.ip
port = opts[:client].addr.port
end
unless opts[:addr].nil?
ip = opts[:addr].ip
port = opts[:addr].port
end
# unused flags ack num chunks
# ff ffff aa aaaa aaaa NNNN NNNN
header_bits = "00#{flags_bits}#{@ack.to_s(2).rjust(10, '0')}#{num_chunks.to_s(2).rjust(8, '0')}"
header_bits = "00#{flags_bits}#{ack.to_s(2).rjust(10, '0')}#{opts[:chunks].to_s(2).rjust(8, '0')}"
header = header_bits.chars.groups_of(8).map do |eight_bits|
eight_bits.join.to_i(2)
@ -65,12 +76,6 @@ class NetBase
header += str_bytes(@peer_token)
data = (header + payload).pack('C*')
ip = @ip
port = @port
unless opts[:addr].nil?
ip = opts[:addr].ip
port = opts[:addr].port
end
puts "send to #{ip}:#{port}"
@s.send(data, 0, ip, port)

View file

@ -134,7 +134,7 @@ class TeeworldsClient
# TODO: this is same in client and server
# move to NetBase???
def send_ctrl_close
@netbase&.send_packet([NET_CTRLMSG_CLOSE], 0, control: true)
@netbase&.send_packet([NET_CTRLMSG_CLOSE], chunks: 0, control: true)
end
def disconnect
@ -160,18 +160,18 @@ class TeeworldsClient
end
def send_ctrl_keepalive
@netbase.send_packet([NET_CTRLMSG_KEEPALIVE], 0, control: true)
@netbase.send_packet([NET_CTRLMSG_KEEPALIVE], chunks: 0, control: true)
end
def send_msg_connect
msg = [NET_CTRLMSG_CONNECT] + str_bytes(@client_token) + Array.new(501, 0x00)
@netbase.send_packet(msg, 0, control: true)
@netbase.send_packet(msg, chunks: 0, control: true)
end
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)
@netbase.send_packet(msg, chunks: 0, control: true)
end
def send_info
@ -183,7 +183,7 @@ class TeeworldsClient
[pack_msg_id(NETMSG_INFO, system: true)] +
data
@netbase.send_packet(msg, 1)
@netbase.send_packet(msg)
end
def rcon_auth(name, password = nil)
@ -210,7 +210,7 @@ class TeeworldsClient
msg = NetChunk.create_header(vital: true, size: data.size + 1) +
[pack_msg_id(NETMSG_RCON_AUTH, system: true)] +
data
@netbase.send_packet(msg, 1)
@netbase.send_packet(msg)
end
def rcon(command)
@ -219,7 +219,7 @@ class TeeworldsClient
msg = NetChunk.create_header(vital: true, size: data.size + 1) +
[pack_msg_id(NETMSG_RCON_CMD, system: true)] +
data
@netbase.send_packet(msg, 1)
@netbase.send_packet(msg)
end
def send_msg_startinfo
@ -288,7 +288,7 @@ class TeeworldsClient
msg = NetChunk.create_header(vital: false, size: data.size + 1) +
[pack_msg_id(NETMSG_INPUT, system: true)] +
data
@netbase.send_packet(msg, 1)
@netbase.send_packet(msg)
end
def on_msg_token(data)

View file

@ -17,11 +17,13 @@ require_relative 'token'
class Client
attr_accessor :id, :addr, :vital_sent, :last_recv_time, :token, :player
attr_reader :ack
def initialize(attr = {})
@id = attr[:id]
@addr = attr[:addr]
@vital_sent = 0
@ack = 0
@last_recv_time = Time.now
@player = Player.new(
id: @id,
@ -35,6 +37,10 @@ class Client
SecurityToken.validate(@token)
end
def bump_ack
@ack = (@ack + 1) % NET_MAX_SEQUENCE
end
# TODO: use or remove
# not sure if its cool
# one can make vital_sent read only
@ -123,11 +129,19 @@ class TeeworldsServer
end
def on_client_packet(packet)
client = packet.client
if client.nil?
# TODO: turn this into a silent return
# otherwise bad actors can easily trigger this
# with handcrafted packets
puts 'Error: got client packet from unknown client'
exit 1
end
chunks = BigChungusTheChunkGetter.get_chunks(packet.payload)
chunks.each do |chunk|
if chunk.flags_vital && !chunk.flags_resend
@netbase.ack = (@netbase.ack + 1) % NET_MAX_SEQUENCE
puts "got ack: #{@netbase.ack}" if @verbose
packet.client.bump_ack
puts "got ack: #{packet.client.ack}" if @verbose
end
process_chunk(chunk, packet)
end
@ -157,14 +171,14 @@ class TeeworldsServer
msg = [NET_CTRLMSG_CLOSE]
msg += Packer.pack_str(reason) unless reason.nil?
@netbase.set_peer_token(client.token)
@netbase.send_packet(msg, 0, control: true, addr: client.addr)
@netbase.send_packet(msg, chunks: 0, control: true, client:)
# @netbase.set_peer_token(@server_token)
end
def send_ctrl_with_token(addr, token)
msg = [NET_CTRLMSG_TOKEN] + str_bytes(@server_token)
@netbase.set_peer_token(token)
@netbase.send_packet(msg, 0, control: true, addr:)
@netbase.send_packet(msg, chunks: 0, control: true, addr:)
# @netbase.set_peer_token(@server_token)
end
@ -179,33 +193,33 @@ class TeeworldsServer
msg = NetChunk.create_header(vital: true, size: data.size + 1, client:) +
[pack_msg_id(NETMSG_MAP_CHANGE, system: true)] +
data
@netbase.send_packet(msg, 1, addr: client.addr)
@netbase.send_packet(msg, chunks: 1, client:)
end
def send_ready(client)
msg = NetChunk.create_header(vital: true, size: 1, client:) +
[pack_msg_id(NETMSG_CON_READY, system: true)]
@netbase.send_packet(msg, 1, addr: client.addr)
@netbase.send_packet(msg, chunks: 1, client:)
end
def send_ready_to_enter(client)
msg = NetChunk.create_header(vital: true, size: 1, client:) +
[pack_msg_id(NETMSGTYPE_SV_READYTOENTER, system: false)]
@netbase.send_packet(msg, 1, addr: client.addr)
@netbase.send_packet(msg, chunks: 1, client:)
end
def send_server_info(client, server_info)
msg = NetChunk.create_header(vital: true, size: 1 + server_info.size, client:) +
[pack_msg_id(NETMSG_SERVERINFO, system: true)] +
server_info
@netbase.send_packet(msg, 1, addr: client.addr)
@netbase.send_packet(msg, chunks: 1, client:)
end
def send_game_info(client, data)
msg = NetChunk.create_header(vital: true, size: 1 + data.size, client:) +
[pack_msg_id(NETMSGTYPE_SV_GAMEINFO, system: false)] +
data
@netbase.send_packet(msg, 1, addr: client.addr)
@netbase.send_packet(msg, chunks: 1, client:)
end
def on_ctrl_token(packet)
@ -247,7 +261,7 @@ class TeeworldsServer
puts "got connection, sending accept (client token: #{token})"
client = Client.new(id:, addr: packet.addr, token:)
@clients[id] = client
@netbase.send_packet([NET_CTRLMSG_ACCEPT], 0, control: true, addr: packet.addr)
@netbase.send_packet([NET_CTRLMSG_ACCEPT], chunks: 0, control: true, client:)
end
def on_packet(packet)
@ -283,7 +297,7 @@ class TeeworldsServer
[pack_msg_id(NETMSG_SNAPEMPTY, system: true)] +
data
@clients.each do |_id, client|
@netbase.send_packet(msg_snap_empty, 1, addr: client.addr)
@netbase.send_packet(msg_snap_empty, chunks: 1, client:)
end
end