Skip to content

Commit 6034379

Browse files
committed
Draft out snap builder
1 parent 05867af commit 6034379

File tree

6 files changed

+152
-11
lines changed

6 files changed

+152
-11
lines changed

lib/packer.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
class Packer
1010
# Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign
11+
# @return [Array<int>]
1112
def self.pack_int(num)
1213
# the first byte can fit 6 bits
1314
# because the first two bits are extended and sign

lib/snapshot/builder.rb

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# frozen_string_literal: true
2+
3+
require_relative 'snapshot'
4+
5+
# should be merged with SnapItemBase
6+
class SnapItem
7+
# @param type [Integer] type of the item for example 5 is obj_flag
8+
# @param id [Integer] id of said item for characters thats the ClientID
9+
# @param fields [Array] array of uncompressed integers
10+
# for example [0, 0, 1] for obj_flag
11+
# would set
12+
# m_X = 0
13+
# m_Y = 0
14+
# m_Team = 1
15+
def initialize(type, id, size, fields)
16+
@type = type
17+
@id = id
18+
@size = size
19+
@fields = fields
20+
end
21+
22+
# basically to_network
23+
# tee int array that will be sent over
24+
# the wire
25+
def to_a
26+
Packer.pack_int(@type) +
27+
Packer.pack_int(@id) +
28+
fields.map { |field| Packer.pack_int(field) }
29+
end
30+
end
31+
32+
class SnapshotBuilder
33+
def initialize
34+
@data_size = 0
35+
@num_items = 0
36+
@items = []
37+
end
38+
39+
##
40+
# insert new snap item into the snap
41+
#
42+
# https://chillerdragon.github.io/teeworlds-protocol/07/snap_items.html
43+
#
44+
# @param type [Integer] type of the item for example 5 is obj_flag
45+
# @param id [Integer] id of said item for characters thats the ClientID
46+
# @param fields [Array] array of uncompressed integers
47+
# for example [0, 0, 1] for obj_flag
48+
# would set
49+
# m_X = 0
50+
# m_Y = 0
51+
# m_Team = 1
52+
def new_item(type, id, size, fields)
53+
item = SnapItem.new(type, id, size, fields)
54+
@items.push(item)
55+
end
56+
57+
# @return [Snapshot]
58+
def finish
59+
Snapshot.new
60+
end
61+
end

lib/snapshot/snapshot.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# frozen_string_literal: true
2+
3+
# shared by client and server
4+
class Snapshot
5+
attr_accessor :game_tick, :items
6+
7+
def initialize(items)
8+
# @type game_tick [Integer]
9+
@game_tick = 0
10+
# @type items [Array<SnapItemBase>]
11+
@items = items
12+
end
13+
14+
# @return crc [Integer] cyclic redundancy check a checksum of all snap items
15+
def crc
16+
sum = 0
17+
@items.each do |item|
18+
sum += item.to_a.sum
19+
end
20+
sum
21+
end
22+
end

lib/snapshot/unpacker.rb

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,7 @@
1919
require_relative 'events/death'
2020
require_relative 'events/hammer_hit'
2121
require_relative '../packer'
22-
23-
class Snapshot
24-
attr_accessor :game_tick, :items
25-
26-
def initialize(items)
27-
@game_tick = 0
28-
@items = items
29-
end
30-
end
22+
require_relative 'snapshot'
3123

3224
class DDNetSnapItem
3325
attr_accessor :notes, :name
@@ -76,6 +68,7 @@ def self.valid_type?(type)
7668
class SnapshotUnpacker
7769
def initialize(client)
7870
@client = client
71+
# @type verbose [Boolean]
7972
@verbose = client.verbose_snap
8073
end
8174

@@ -181,6 +174,7 @@ def snap_single(chunk)
181174
invalid = false
182175
item_type = u.get_int
183176
id_parsed = u.parsed.last
177+
# @type snap_items [Array<SnapItemBase>]
184178
snap_items = []
185179
while item_type
186180
obj = nil

lib/teeworlds_server.rb

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def seq
6565

6666
class TeeworldsServer
6767
attr_accessor :clients
68-
attr_reader :hooks, :shutdown_reason
68+
attr_reader :hooks, :shutdown_reason, :current_game_tick
6969

7070
def initialize(options = {})
7171
@verbose = options[:verbose] || false
@@ -448,7 +448,7 @@ def tick_start_time(tick)
448448
# m_GameStartTime + (time_freq()*Tick)/SERVER_TICK_SPEED;
449449
end
450450

451-
def do_snapshot
451+
def do_snap_empty
452452
delta_tick = -1
453453
# DeltaTick = m_aClients[i].m_LastAckedSnapshot;
454454
data = []
@@ -470,6 +470,30 @@ def do_snapshot
470470
end
471471
end
472472

473+
def do_snap_single
474+
builder = SnapshotBuilder.new
475+
snap = builder.finish
476+
items = snap.to_a
477+
478+
data = []
479+
# Game tick Int
480+
data += Packer.pack_int(@current_game_tick)
481+
# Delta tick Int
482+
data += Packer.pack_int(@current_game_tick - delta_tick)
483+
# Crc Int
484+
data += Packer.pack_int(snap.crc)
485+
# Part size Int The size of this part. Meaning the size in bytes of the next raw data field.
486+
data += Packer.pack_int(items.size)
487+
# Data
488+
data += items
489+
490+
p data
491+
end
492+
493+
def do_snapshot
494+
do_snap_empty
495+
end
496+
473497
def get_player_by_id(id)
474498
@clients[id]&.player
475499
end

spec/09_snap_builder_spec.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# frozen_string_literal: true
2+
3+
# require_relative '../lib/snapshot/builder.rb'
4+
#
5+
# describe 'SnapshotBuilder', :snapshot do
6+
# context 'finish' do
7+
# it 'Should create correct snap' do
8+
# builder = SnapshotBuilder.new
9+
# snap = builder.finish
10+
# expected_payload = [
11+
# 0x00, 0x01, 0x00, 0x0a, # removed_items=0 num_item_deltas=1 _zero=0 type=10 NetObj::Character
12+
# 0x00, 0x29, 0x00, 0x0d, # id=0 tick=41 x=0 y=13
13+
# 0x00, 0xb3, 0x36, 0x00, # vel_x=0 vel_y=3507 angle=0
14+
# 0x00, 0x40, 0x00, 0x00, # direction=0 jumped=-1 hooked_player=0 hook_state=0
15+
# 0x00, 0x00, 0x00, 0x00, # hook_tick=0 hook_x=0 hook_y=0 hook_dx=0
16+
# 0x00, 0x00, 0x00, 0x00, # hook_dy=0 health=0 armor=0 ammo_count=0
17+
# 0x00, 0x00, 0x00, 0x00, # weapon=0 emote=0 attack_tick=0 triggered_events=0
18+
# ]
19+
# expect(snap.to_a).to eq(expected_payload)
20+
# end
21+
# end
22+
# end
23+
24+
# >>> snap NETMSG_SNAPSINGLE (8)
25+
# id=8 game_tick=1908 delta_tick=38
26+
# num_parts=1 part=0 crc=16846 part_size=28
27+
#
28+
# header:
29+
# 11 b4 1d 26 ...& int 17 >> 1 = 8 int 1908 int 38
30+
# 8e 87 02 1c .... int 16846 int 28
31+
#
32+
# payload:
33+
# 00 01 00 0a .... removed_items=0 num_item_deltas=1 _zero=0 type=10 NetObj::Character
34+
# 00 29 00 0d .).. id=0 tick=41 x=0 y=13
35+
# 00 b3 36 00 ..6. vel_x=0 vel_y=3507 angle=0
36+
# 00 40 00 00 .@.. direction=0 jumped=-1 hooked_player=0 hook_state=0
37+
# 00 00 00 00 .... hook_tick=0 hook_x=0 hook_y=0 hook_dx=0
38+
# 00 00 00 00 .... hook_dy=0 health=0 armor=0 ammo_count=0
39+
# 00 00 00 00 .... weapon=0 emote=0 attack_tick=0 triggered_events=0

0 commit comments

Comments
 (0)