Draft out snap builder

This commit is contained in:
ChillerDragon 2024-02-21 14:18:01 +08:00
parent 05867afa62
commit c726756b5c
6 changed files with 160 additions and 11 deletions

View file

@ -8,6 +8,7 @@ SKIP_START_WHITESPACES = 4
class Packer
# Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign
# @return [Array<int>]
def self.pack_int(num)
# the first byte can fit 6 bits
# because the first two bits are extended and sign

61
lib/snapshot/builder.rb Normal file
View file

@ -0,0 +1,61 @@
# frozen_string_literal: true
require_relative 'snapshot'
# should be merged with SnapItemBase
class SnapItem
# @param type [Integer] type of the item for example 5 is obj_flag
# @param id [Integer] id of said item for characters thats the ClientID
# @param fields [Array] array of uncompressed integers
# for example [0, 0, 1] for obj_flag
# would set
# m_X = 0
# m_Y = 0
# m_Team = 1
def initialize(type, id, size, fields)
@type = type
@id = id
@size = size
@fields = fields
end
# basically to_network
# tee int array that will be sent over
# the wire
def to_a
Packer.pack_int(@type) +
Packer.pack_int(@id) +
fields.map { |field| Packer.pack_int(field) }
end
end
class SnapshotBuilder
def initialize
@data_size = 0
@num_items = 0
@items = []
end
##
# insert new snap item into the snap
#
# https://chillerdragon.github.io/teeworlds-protocol/07/snap_items.html
#
# @param type [Integer] type of the item for example 5 is obj_flag
# @param id [Integer] id of said item for characters thats the ClientID
# @param fields [Array] array of uncompressed integers
# for example [0, 0, 1] for obj_flag
# would set
# m_X = 0
# m_Y = 0
# m_Team = 1
def new_item(type, id, size, fields)
item = SnapItem.new(type, id, size, fields)
@items.push(item)
end
# @return [Snapshot]
def finish
Snapshot.new
end
end

30
lib/snapshot/snapshot.rb Normal file
View file

@ -0,0 +1,30 @@
# frozen_string_literal: true
# shared by client and server
class Snapshot
attr_accessor :game_tick, :items
def initialize(items)
# @type game_tick [Integer]
@game_tick = 0
# @type items [Array<SnapItemBase>]
@items = items
end
# @return [Integer] cyclic redundancy check a checksum of all snap items
def crc
sum = 0
@items.each do |item|
sum += item.to_a.sum
end
sum
end
def to_a
data = []
@items.each do |item|
data += item.to_a
end
data
end
end

View file

@ -19,15 +19,7 @@ require_relative 'events/damage'
require_relative 'events/death'
require_relative 'events/hammer_hit'
require_relative '../packer'
class Snapshot
attr_accessor :game_tick, :items
def initialize(items)
@game_tick = 0
@items = items
end
end
require_relative 'snapshot'
class DDNetSnapItem
attr_accessor :notes, :name
@ -76,6 +68,7 @@ end
class SnapshotUnpacker
def initialize(client)
@client = client
# @type verbose [Boolean]
@verbose = client.verbose_snap
end
@ -181,6 +174,7 @@ class SnapshotUnpacker
invalid = false
item_type = u.get_int
id_parsed = u.parsed.last
# @type snap_items [Array<SnapItemBase>]
snap_items = []
while item_type
obj = nil

View file

@ -65,7 +65,7 @@ end
class TeeworldsServer
attr_accessor :clients
attr_reader :hooks, :shutdown_reason
attr_reader :hooks, :shutdown_reason, :current_game_tick
def initialize(options = {})
@verbose = options[:verbose] || false
@ -448,7 +448,7 @@ class TeeworldsServer
# m_GameStartTime + (time_freq()*Tick)/SERVER_TICK_SPEED;
end
def do_snapshot
def do_snap_empty
delta_tick = -1
# DeltaTick = m_aClients[i].m_LastAckedSnapshot;
data = []
@ -470,6 +470,30 @@ class TeeworldsServer
end
end
def do_snap_single
builder = SnapshotBuilder.new
snap = builder.finish
items = snap.to_a
data = []
# Game tick Int
data += Packer.pack_int(@current_game_tick)
# Delta tick Int
data += Packer.pack_int(@current_game_tick - delta_tick)
# Crc Int
data += Packer.pack_int(snap.crc)
# Part size Int The size of this part. Meaning the size in bytes of the next raw data field.
data += Packer.pack_int(items.size)
# Data
data += items
p data
end
def do_snapshot
do_snap_empty
end
def get_player_by_id(id)
@clients[id]&.player
end

View file

@ -0,0 +1,39 @@
# frozen_string_literal: true
# require_relative '../lib/snapshot/builder.rb'
#
# describe 'SnapshotBuilder', :snapshot do
# context 'finish' do
# it 'Should create correct snap' do
# builder = SnapshotBuilder.new
# snap = builder.finish
# expected_payload = [
# 0x00, 0x01, 0x00, 0x0a, # removed_items=0 num_item_deltas=1 _zero=0 type=10 NetObj::Character
# 0x00, 0x29, 0x00, 0x0d, # id=0 tick=41 x=0 y=13
# 0x00, 0xb3, 0x36, 0x00, # vel_x=0 vel_y=3507 angle=0
# 0x00, 0x40, 0x00, 0x00, # direction=0 jumped=-1 hooked_player=0 hook_state=0
# 0x00, 0x00, 0x00, 0x00, # hook_tick=0 hook_x=0 hook_y=0 hook_dx=0
# 0x00, 0x00, 0x00, 0x00, # hook_dy=0 health=0 armor=0 ammo_count=0
# 0x00, 0x00, 0x00, 0x00, # weapon=0 emote=0 attack_tick=0 triggered_events=0
# ]
# expect(snap.to_a).to eq(expected_payload)
# end
# end
# end
# >>> snap NETMSG_SNAPSINGLE (8)
# id=8 game_tick=1908 delta_tick=38
# num_parts=1 part=0 crc=16846 part_size=28
#
# header:
# 11 b4 1d 26 ...& int 17 >> 1 = 8 int 1908 int 38
# 8e 87 02 1c .... int 16846 int 28
#
# payload:
# 00 01 00 0a .... removed_items=0 num_item_deltas=1 _zero=0 type=10 NetObj::Character
# 00 29 00 0d .).. id=0 tick=41 x=0 y=13
# 00 b3 36 00 ..6. vel_x=0 vel_y=3507 angle=0
# 00 40 00 00 .@.. direction=0 jumped=-1 hooked_player=0 hook_state=0
# 00 00 00 00 .... hook_tick=0 hook_x=0 hook_y=0 hook_dx=0
# 00 00 00 00 .... hook_dy=0 health=0 armor=0 ammo_count=0
# 00 00 00 00 .... weapon=0 emote=0 attack_tick=0 triggered_events=0