From af7367be0cd581cb345756e55b041fa37d99d3a5 Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Mon, 24 Jun 2024 11:42:08 +0800 Subject: [PATCH] Drop known chunks. Fixes duplicated chat messages. --- lib/chunk.rb | 11 ++++++++--- lib/connection.rb | 20 ++++++++++++++++++++ lib/teeworlds_client.rb | 17 ++++++++++++++--- 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 lib/connection.rb diff --git a/lib/chunk.rb b/lib/chunk.rb index 46ffa07..1a9b1b2 100644 --- a/lib/chunk.rb +++ b/lib/chunk.rb @@ -13,13 +13,14 @@ require_relative 'bytes' # # https://chillerdragon.github.io/teeworlds-protocol/07/packet_layout.html class NetChunk - attr_reader :next, :data, :msg, :sys, :flags, :header_raw, :full_raw + attr_reader :next, :data, :msg, :sys, :flags, :seq, :header_raw, :full_raw @@sent_vital_chunks = 0 def initialize(data) @next = nil @flags = {} + @seq = 0 @size = 0 parse_header(data[0..2]) header_size = if flags_vital @@ -135,8 +136,12 @@ class NetChunk size_bytes.map! { |b| b[2..].join } @size = size_bytes.join.to_i(2) - # sequence number - # in da third byte but who needs seq?! + if @flags[:vital] + data = data[0..2].bytes + @seq = (data[1] & (0xC0 << 2)) | data[2] + else + @seq = 0 + end end # @return [Boolean] diff --git a/lib/connection.rb b/lib/connection.rb new file mode 100644 index 0000000..810095a --- /dev/null +++ b/lib/connection.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +## +# Only used for chunks where the sequence number does not match the expected value +# to decide wether to drop known chunks silently or request resend if something got lost +# +# true - if the sequence number is already known and the chunk should be dropped +# false - if the sequence number is off and we need to request a resend of lost chunks +# +# @return [Boolean] +def seq_in_backroom?(seq, ack) + bottom = ack - (NET_MAX_SEQUENCE / 2) + if bottom.negative? + return true if seq <= ack + return true if seq >= (bottom + NET_MAX_SEQUENCE) + elsif seq <= ack && seq >= bottom + return true + end + false +end diff --git a/lib/teeworlds_client.rb b/lib/teeworlds_client.rb index aba2b29..fdadb47 100644 --- a/lib/teeworlds_client.rb +++ b/lib/teeworlds_client.rb @@ -14,6 +14,7 @@ require_relative 'packer' require_relative 'models/player' require_relative 'game_client' require_relative 'config' +require_relative 'connection' class TeeworldsClient attr_reader :state, :hooks, :game_client, :verbose_snap @@ -445,9 +446,19 @@ class TeeworldsClient end chunks = BigChungusTheChunkGetter.get_chunks(data) chunks.each do |chunk| - if chunk.flags_vital && !chunk.flags_resend && chunk.msg != NETMSG_NULL - @netbase.ack = (@netbase.ack + 1) % NET_MAX_SEQUENCE - puts "got ack: #{@netbase.ack}" if @verbose + if chunk.flags_vital + if chunk.seq == (@netbase.ack + 1) % NET_MAX_SEQUENCE + # in sequence + @netbase.ack = (@netbase.ack + 1) % NET_MAX_SEQUENCE + else + puts "warning: got chunk out of sequence! seq=#{chunk.seq} expected_seq=#{(@netbase.ack + 1) % NET_MAX_SEQUENCE}" + if seq_in_backroom?(chunk.seq, @netbase.ack) + puts ' dropping known chunk ...' + next + end + # TODO: request resend + puts ' REQUESTING RESEND NOT IMPLEMENTED' + end end process_chunk(chunk) end