Move snapshot to new file (still not cleaned up)

This commit is contained in:
ChillerDragon 2022-11-17 16:11:34 +01:00
parent 948aafb749
commit 34ebe75d54
2 changed files with 173 additions and 235 deletions

View file

@ -10,6 +10,7 @@ require_relative 'messages/maplist_entry_add'
require_relative 'messages/maplist_entry_rem'
require_relative 'packer'
require_relative 'context'
require_relative 'snapshot'
class GameClient
attr_accessor :players, :pred_game_tick, :ack_game_tick
@ -153,247 +154,13 @@ class GameClient
end
def on_snapshot(chunk)
u = Unpacker.new(chunk.data)
msg_id = u.get_int
msg_id >>= 1
num_parts = 1
part = 0
game_tick = u.get_int
delta_tick = u.get_int
part_size = 0
crc = 0
# complete_size = 0
# data = nil
# TODO: state check
if msg_id == NETMSG_SNAP
num_parts = u.get_int
part = u.get_int
end
unless msg_id == NETMSG_SNAPEMPTY
crc = u.get_int
part_size = u.get_int
end
snap_name = 'SNAP_INVALID'
case msg_id
when NETMSG_SNAP then snap_name = 'NETMSG_SNAP'
when NETMSG_SNAPSINGLE then snap_name = 'NETMSG_SNAPSINGLE'
when NETMSG_SNAPEMPTY then snap_name = 'NETMSG_SNAPEMPTY'
end
return unless msg_id == NETMSG_SNAPSINGLE
puts ">>> snap #{snap_name} (#{msg_id})"
puts " id=#{msg_id} game_tick=#{game_tick} delta_tick=#{delta_tick}"
puts " num_parts=#{num_parts} part=#{part} crc=#{crc} part_size=#{part_size}"
puts "\n header:"
header = []
notes = []
u.parsed.each_with_index do |parsed, index|
color = (index % 2).zero? ? :green : :pink
txt = "#{parsed[:type]} #{parsed[:value]}"
txt += " >> 1 = #{parsed[:value] >> 1}" if header.empty?
notes.push([color, parsed[:pos], parsed[:len], txt])
header += parsed[:raw]
end
hexdump_lines(header.pack('C*'), 1, notes, legend: :inline).each do |hex|
puts " #{hex}"
end
puts "\n payload:"
# [:green, 0, 4, 'who dis?']
notes = []
# data.groups_of(4).each_with_index do |item, index|
# # reverse for little endian
# type = item[0...2].reverse.map { |b| b.to_s(2).rjust(8, '0') }.join.to_i(2)
# notes.push([:green, index * 4, 2, "type=#{type}"])
# next unless item.length == 4
# # reverse for little endian
# id = item[2...4].reverse.map { |b| b.to_s(2).rjust(8, '0') }.join.to_i(2)
# notes.push([:yellow, index * 4 + 2, 2, "id=#{id}"])
# end
@sizes = [
0,
10,
6,
5,
3,
3,
3,
2,
4,
15,
22,
3,
4,
58,
5,
32,
2,
2,
2,
2,
3,
3,
5
]
@snap_items = [
{ name: 'placeholder', size: 0 },
{ name: 'obj_player_input', size: 10 },
{ name: 'obj_projectile', size: 6 },
{ name: 'obj_laser', size: 5 },
{ name: 'obj_pickup', size: 3 },
{ name: 'obj_flag', size: 3 },
{ name: 'obj_game_data', size: 3, fields: [
{ type: 'int', name: 'start_tick' },
{ type: 'int', name: 'flags' },
{ type: 'int', name: 'end_tick' }
] },
{ name: 'obj_game_data_team', size: 2 },
{ name: 'obj_game_data_flag', size: 4 },
{ name: 'obj_character_core', size: 15 },
{ name: 'obj_character', size: 22 },
{ name: 'obj_player_info', size: 3 },
{ name: 'obj_spectator_info', size: 4 },
{ name: 'obj_client_info', size: 58 },
{ name: 'obj_game_info', size: 5 },
{ name: 'obj_tune_params', size: 32 },
{ name: 'event_common', size: 2 },
{ name: 'event_explosion', size: 2 },
{ name: 'event_spawn', size: 2 },
{ name: 'event_hammerhit', size: 2 },
{ name: 'event_death', size: 3 },
{ name: 'event_sound_world', size: 3 },
{ name: 'event_damage', size: 5 }
]
data = u.get_raw
# tw decompresses all bytes at once
# and pads it with zeros to get the 4 byte aligned ints
# we just grab one int at a time cuz yolo
u = Unpacker.new(data)
num_removed_items = u.get_int
p = u.parsed.last
notes.push([:red, p[:pos], p[:len], "removed_items=#{num_removed_items}"])
num_item_deltas = u.get_int
p = u.parsed.last
notes.push([:blue, p[:pos], p[:len], "num_item_deltas=#{num_item_deltas}"])
zero = u.get_int
p = u.parsed.last
notes.push([:cyan, p[:pos], p[:len], "_zero=#{zero}"])
(0...num_removed_items).each do |i|
deleted = u.get_int
notes.push([:red, p[:pos], p[:len], "del[#{i}]=#{deleted}"])
end
item_delta = u.get_int
while item_delta
# item_bits = item_delta.to_s(2).rjust(32, '0')
item_type = item_delta
# item_id_bits = item_bits[0...16]
# item_type_bits = item_bits[16..]
# item_id = item_id_bits.to_i(2)
# item_type = item_type_bits.to_i(2)
item_meta = @snap_items[item_type]
item_name = item_meta[:name]
# { name: 'obj_game_data', size: 3, fields: [
# { type: 'int', name: 'start_tick' },
p = u.parsed.last
notes.push([:green, p[:pos], p[:len], "type = #{item_type} #{item_name}"])
item_id = u.get_int
p = u.parsed.last
notes.push([:cyan, p[:pos], p[:len], "id=#{item_id}"])
size = item_meta[:size]
(0...size).each do |i|
val = u.get_int
p = u.parsed.last
color = (i % 2).zero? ? :yellow : :pink
fields = item_meta[:fields]
desc = ''
desc = fields[i][:name] unless fields.nil? || fields[i].nil?
notes.push([color, p[:pos], p[:len], "data[#{i}]=#{val} #{desc}"])
end
item_delta = u.get_int
end
# skip = 0
# ((3 * 4)...data.size).each do |i|
# skip -= 1
# unless skip.negative?
# # puts "skipped i=#{i} hex=#{str_hex([data[i]].pack('C*'))} skips_left=#{skip}"
# next
# end
# # reverse for little endian
# id = data[i...(i + 2)].reverse.map { |b| b.to_s(2).rjust(8, '0') }.join.to_i(2)
# if data[i + 4].nil? && i > 2
# puts "Error: unexpected end of data at i=#{i + 4} data_size=#{data.size}"
# next
# end
# type = data[(i + 2)...(i + 4)].reverse.map { |b| b.to_s(2).rjust(8, '0') }.join.to_i(2)
# size = @sizes[type]
# # p "id=#{id} type=#{type}"
# if size.nil? && i > 2
# puts "Error: could not get size for type=#{type} -> skip byte"
# next
# end
# size *= 4
# meta = @snap_items[type]
# notes.push([:green, i, 2, "id=#{id}"])
# notes.push([:pink, i + 2, 2, "type=#{type} (#{meta[:name]} size: #{size})"])
# item_payload = data[(i + 4)..]
# u = Unpacker.new(item_payload)
# (0...(size / 4)).each do |d|
# # val = u.get_int
# val='EMPTY'
# field_name = ''
# field_name += meta[:fields][d][:name] unless meta[:fields].nil? || meta[:fields][d].nil?
# notes.push([:yellow, i + 4 + (d * 4), 4, "data[#{d}]=#{val} #{field_name}"])
# end
# skip += 3 + size + 1
# # puts "skip=#{skip}"
# # next
# # next unless item.length == 4
# # # reverse for little endian
# # id = item[2...4].reverse.map { |b| b.to_s(2).rjust(8, '0') }.join.to_i(2)
# # notes.push([:yellow, (index * 4) + 2, 2, "id=#{id}"])
# end
hexdump_lines(data.pack('C*'), 1, notes, legend: :inline).each do |hex|
puts " #{hex}"
end
game_tick = snap_single(chunk)
# ack every snapshot no matter how broken
@ack_game_tick = game_tick
return unless (@pred_game_tick - @ack_game_tick).abs > 10
@pred_game_tick = @ack_game_tick + 1
# exit
end
def on_emoticon(chunk); end

171
lib/snapshot.rb Normal file
View file

@ -0,0 +1,171 @@
# frozen_string_literal: true
require_relative 'packer'
def snap_single(chunk)
u = Unpacker.new(chunk.data)
msg_id = u.get_int
msg_id >>= 1
num_parts = 1
part = 0
game_tick = u.get_int
delta_tick = u.get_int
part_size = 0
crc = 0
# complete_size = 0
# data = nil
# TODO: state check
if msg_id == NETMSG_SNAP
num_parts = u.get_int
part = u.get_int
end
unless msg_id == NETMSG_SNAPEMPTY
crc = u.get_int
part_size = u.get_int
end
snap_name = 'SNAP_INVALID'
case msg_id
when NETMSG_SNAP then snap_name = 'NETMSG_SNAP'
when NETMSG_SNAPSINGLE then snap_name = 'NETMSG_SNAPSINGLE'
when NETMSG_SNAPEMPTY then snap_name = 'NETMSG_SNAPEMPTY'
end
return unless msg_id == NETMSG_SNAPSINGLE
puts ">>> snap #{snap_name} (#{msg_id})"
puts " id=#{msg_id} game_tick=#{game_tick} delta_tick=#{delta_tick}"
puts " num_parts=#{num_parts} part=#{part} crc=#{crc} part_size=#{part_size}"
puts "\n header:"
header = []
notes = []
u.parsed.each_with_index do |parsed, index|
color = (index % 2).zero? ? :green : :pink
txt = "#{parsed[:type]} #{parsed[:value]}"
txt += " >> 1 = #{parsed[:value] >> 1}" if header.empty?
notes.push([color, parsed[:pos], parsed[:len], txt])
header += parsed[:raw]
end
hexdump_lines(header.pack('C*'), 1, notes, legend: :inline).each do |hex|
puts " #{hex}"
end
puts "\n payload:"
notes = []
@sizes = [
0,
10,
6,
5,
3,
3,
3,
2,
4,
15,
22,
3,
4,
58,
5,
32,
2,
2,
2,
2,
3,
3,
5
]
@snap_items = [
{ name: 'placeholder', size: 0 },
{ name: 'obj_player_input', size: 10 },
{ name: 'obj_projectile', size: 6 },
{ name: 'obj_laser', size: 5 },
{ name: 'obj_pickup', size: 3 },
{ name: 'obj_flag', size: 3 },
{ name: 'obj_game_data', size: 3, fields: [
{ type: 'int', name: 'start_tick' },
{ type: 'int', name: 'flags' },
{ type: 'int', name: 'end_tick' }
] },
{ name: 'obj_game_data_team', size: 2 },
{ name: 'obj_game_data_flag', size: 4 },
{ name: 'obj_character_core', size: 15 },
{ name: 'obj_character', size: 22 },
{ name: 'obj_player_info', size: 3 },
{ name: 'obj_spectator_info', size: 4 },
{ name: 'obj_client_info', size: 58 },
{ name: 'obj_game_info', size: 5 },
{ name: 'obj_tune_params', size: 32 },
{ name: 'event_common', size: 2 },
{ name: 'event_explosion', size: 2 },
{ name: 'event_spawn', size: 2 },
{ name: 'event_hammerhit', size: 2 },
{ name: 'event_death', size: 3 },
{ name: 'event_sound_world', size: 3 },
{ name: 'event_damage', size: 5 }
]
data = u.get_raw
# tw decompresses all bytes at once
# and pads it with zeros to get the 4 byte aligned ints
# we just grab one int at a time cuz yolo
u = Unpacker.new(data)
num_removed_items = u.get_int
p = u.parsed.last
notes.push([:red, p[:pos], p[:len], "removed_items=#{num_removed_items}"])
num_item_deltas = u.get_int
p = u.parsed.last
notes.push([:blue, p[:pos], p[:len], "num_item_deltas=#{num_item_deltas}"])
zero = u.get_int
p = u.parsed.last
notes.push([:cyan, p[:pos], p[:len], "_zero=#{zero}"])
(0...num_removed_items).each do |i|
deleted = u.get_int
notes.push([:red, p[:pos], p[:len], "del[#{i}]=#{deleted}"])
end
item_delta = u.get_int
while item_delta
item_type = item_delta
item_meta = @snap_items[item_type]
item_name = item_meta[:name]
p = u.parsed.last
notes.push([:green, p[:pos], p[:len], "type = #{item_type} #{item_name}"])
item_id = u.get_int
p = u.parsed.last
notes.push([:cyan, p[:pos], p[:len], "id=#{item_id}"])
size = item_meta[:size]
(0...size).each do |i|
val = u.get_int
p = u.parsed.last
color = (i % 2).zero? ? :yellow : :pink
fields = item_meta[:fields]
desc = ''
desc = fields[i][:name] unless fields.nil? || fields[i].nil?
notes.push([color, p[:pos], p[:len], "data[#{i}]=#{val} #{desc}"])
end
item_delta = u.get_int
end
hexdump_lines(data.pack('C*'), 1, notes, legend: :inline).each do |hex|
puts " #{hex}"
end
game_tick
end