teeworlds_network 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/array.rb +28 -0
- data/lib/bytes.rb +139 -0
- data/lib/chunk.rb +177 -0
- data/lib/config.rb +44 -0
- data/lib/context.rb +29 -0
- data/lib/game_client.rb +196 -0
- data/lib/game_server.rb +122 -0
- data/lib/messages/cl_emoticon.rb +63 -0
- data/lib/messages/cl_say.rb +52 -0
- data/lib/messages/client_info.rb +141 -0
- data/lib/messages/game_info.rb +25 -0
- data/lib/messages/input_timing.rb +48 -0
- data/lib/messages/maplist_entry_add.rb +44 -0
- data/lib/messages/maplist_entry_rem.rb +44 -0
- data/lib/messages/rcon_cmd_add.rb +52 -0
- data/lib/messages/rcon_cmd_rem.rb +44 -0
- data/lib/messages/rcon_line.rb +44 -0
- data/lib/messages/server_info.rb +64 -0
- data/lib/messages/server_settings.rb +23 -0
- data/lib/messages/start_info.rb +129 -0
- data/lib/messages/sv_client_drop.rb +57 -0
- data/lib/models/chat_message.rb +39 -0
- data/lib/models/map.rb +57 -0
- data/lib/models/net_addr.rb +18 -0
- data/lib/models/packet_flags.rb +42 -0
- data/lib/models/player.rb +56 -0
- data/lib/models/token.rb +20 -0
- data/lib/net_base.rb +106 -0
- data/lib/network.rb +148 -0
- data/lib/packer.rb +194 -0
- data/lib/packet.rb +73 -0
- data/lib/snapshot/events/damage.rb +24 -0
- data/lib/snapshot/events/death.rb +20 -0
- data/lib/snapshot/events/explosion.rb +16 -0
- data/lib/snapshot/events/hammer_hit.rb +16 -0
- data/lib/snapshot/events/sound_world.rb +20 -0
- data/lib/snapshot/events/spawn.rb +16 -0
- data/lib/snapshot/items/character.rb +43 -0
- data/lib/snapshot/items/client_info.rb +22 -0
- data/lib/snapshot/items/flag.rb +22 -0
- data/lib/snapshot/items/game_data.rb +22 -0
- data/lib/snapshot/items/game_data_flag.rb +24 -0
- data/lib/snapshot/items/game_data_team.rb +21 -0
- data/lib/snapshot/items/laser.rb +24 -0
- data/lib/snapshot/items/pickup.rb +22 -0
- data/lib/snapshot/items/player_info.rb +22 -0
- data/lib/snapshot/items/player_input.rb +32 -0
- data/lib/snapshot/items/projectile.rb +25 -0
- data/lib/snapshot/items/spectator_info.rb +23 -0
- data/lib/snapshot/items/tune_params.rb +90 -0
- data/lib/snapshot/snap_event_base.rb +14 -0
- data/lib/snapshot/snap_item_base.rb +86 -0
- data/lib/snapshot/unpacker.rb +301 -0
- data/lib/string.rb +81 -0
- data/lib/teeworlds_client.rb +506 -0
- data/lib/teeworlds_network.rb +4 -0
- data/lib/teeworlds_server.rb +363 -0
- data/lib/version.rb +3 -0
- metadata +132 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetObj
|
6
|
+
class Laser < SnapItemBase
|
7
|
+
attr_accessor :x, :y, :from_x, :from_y, :start_tick
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
x
|
12
|
+
y
|
13
|
+
from_x
|
14
|
+
from_y
|
15
|
+
start_tick
|
16
|
+
]
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.match_type?(type)
|
21
|
+
type == NETOBJTYPE_LASER
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetObj
|
6
|
+
class Pickup < SnapItemBase
|
7
|
+
attr_accessor :x, :y, :type
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
x
|
12
|
+
y
|
13
|
+
type
|
14
|
+
]
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.match_type?(type)
|
19
|
+
type == NETOBJTYPE_PICKUP
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetObj
|
6
|
+
class PlayerInfo < SnapItemBase
|
7
|
+
attr_accessor :player_flags, :score, :latency
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
player_flags
|
12
|
+
score
|
13
|
+
latency
|
14
|
+
]
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.match_type?(type)
|
19
|
+
type == NETOBJTYPE_PLAYERINFO
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetObj
|
6
|
+
class PlayerInput < SnapItemBase
|
7
|
+
attr_accessor :direction, :target_x, :target_y,
|
8
|
+
:jump, :fire, :hook,
|
9
|
+
:player_flags, :wanted_weapon,
|
10
|
+
:next_weapon, :prev_weapon
|
11
|
+
|
12
|
+
def initialize(hash_or_raw)
|
13
|
+
@field_names = %i[
|
14
|
+
direction
|
15
|
+
target_x
|
16
|
+
target_y
|
17
|
+
jump
|
18
|
+
fire
|
19
|
+
hook
|
20
|
+
player_flags
|
21
|
+
wanted_weapon
|
22
|
+
next_weapon
|
23
|
+
prev_weapon
|
24
|
+
]
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.match_type?(type)
|
29
|
+
type == NETOBJTYPE_PLAYERINPUT
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetObj
|
6
|
+
class Projectile < SnapItemBase
|
7
|
+
attr_accessor :x, :y, :vel_x, :vel_y, :type, :start_tick
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
x
|
12
|
+
y
|
13
|
+
vel_x
|
14
|
+
vel_y
|
15
|
+
type
|
16
|
+
start_tick
|
17
|
+
]
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.match_type?(type)
|
22
|
+
type == NETOBJTYPE_PROJECTILE
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetObj
|
6
|
+
class SpectatorInfo < SnapItemBase
|
7
|
+
attr_accessor :spec_mode, :spectator_id, :x, :y
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
spec_mode
|
12
|
+
spectator_id
|
13
|
+
x
|
14
|
+
y
|
15
|
+
]
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.match_type?(type)
|
20
|
+
type == NETOBJTYPE_SPECTATORINFO
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../packer'
|
4
|
+
|
5
|
+
class NetObj
|
6
|
+
class TuneParams
|
7
|
+
attr_accessor :tune_params
|
8
|
+
attr_reader :notes, :name, :id
|
9
|
+
|
10
|
+
def initialize(hash_or_raw)
|
11
|
+
@field_names = [:tune_params]
|
12
|
+
@fields = @field_names.map do |_|
|
13
|
+
0
|
14
|
+
end
|
15
|
+
@size = @fields.count
|
16
|
+
@name = self.class.name
|
17
|
+
@notes = [] # hexdump annotation notes
|
18
|
+
if hash_or_raw.instance_of?(Hash)
|
19
|
+
init_hash(hash_or_raw)
|
20
|
+
elsif hash_or_raw.instance_of?(Unpacker)
|
21
|
+
init_unpacker(hash_or_raw)
|
22
|
+
else
|
23
|
+
init_raw(hash_or_raw)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate
|
28
|
+
@fields.select(&:nil?).empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
def init_unpacker(u)
|
32
|
+
@id = u.get_int
|
33
|
+
p = u.parsed.last
|
34
|
+
@notes.push([:cyan, p[:pos], p[:len], "id=#{@id}"])
|
35
|
+
i = 0
|
36
|
+
@fields.map! do |_|
|
37
|
+
# TODO: as of right now it can get nil values here
|
38
|
+
# the fix would be "u.get_int || 0"
|
39
|
+
# but fixing it would probably make it harder
|
40
|
+
# to debug invalid data
|
41
|
+
#
|
42
|
+
# but do rethink this in a later point please :)
|
43
|
+
# for now call .validate() everywhere
|
44
|
+
val = u.get_int
|
45
|
+
|
46
|
+
p = u.parsed.last
|
47
|
+
color = (i % 2).zero? ? :yellow : :pink
|
48
|
+
desc = @field_names[i]
|
49
|
+
@notes.push([color, p[:pos], p[:len], "#{desc}=#{val}"])
|
50
|
+
i += 1
|
51
|
+
|
52
|
+
val
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def init_raw(data)
|
57
|
+
u = Unpacker.new(data)
|
58
|
+
init_unpacker(u)
|
59
|
+
end
|
60
|
+
|
61
|
+
def init_hash(attr)
|
62
|
+
@fields_names.each do |name|
|
63
|
+
instance_variable_set("@#{name}", attr[name] || 0)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_h
|
68
|
+
hash = {}
|
69
|
+
hash[:id] = @id
|
70
|
+
@field_names.each_with_index do |name, index|
|
71
|
+
hash[name] = @fields[index]
|
72
|
+
end
|
73
|
+
hash
|
74
|
+
end
|
75
|
+
|
76
|
+
# basically to_network
|
77
|
+
# int array the server sends to the client
|
78
|
+
def to_a
|
79
|
+
arr = []
|
80
|
+
@fields.each do |value|
|
81
|
+
arr += Packer.pack_int(value)
|
82
|
+
end
|
83
|
+
arr
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_s
|
87
|
+
to_h
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../packer'
|
4
|
+
require_relative 'snap_item_base'
|
5
|
+
|
6
|
+
class SnapEventBase < SnapItemBase
|
7
|
+
attr_reader :x, :y
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names.prepend(:x)
|
11
|
+
@field_names.prepend(:y)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../packer'
|
4
|
+
|
5
|
+
class SnapItemBase
|
6
|
+
attr_reader :notes, :name, :id
|
7
|
+
|
8
|
+
def initialize(hash_or_raw)
|
9
|
+
@fields = @field_names.map do |_|
|
10
|
+
0
|
11
|
+
end
|
12
|
+
@size = @fields.count
|
13
|
+
@name = self.class.name
|
14
|
+
@notes = [] # hexdump annotation notes
|
15
|
+
if hash_or_raw.instance_of?(Hash)
|
16
|
+
init_hash(hash_or_raw)
|
17
|
+
elsif hash_or_raw.instance_of?(Unpacker)
|
18
|
+
init_unpacker(hash_or_raw)
|
19
|
+
else
|
20
|
+
init_raw(hash_or_raw)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate
|
25
|
+
@fields.select(&:nil?).empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def init_unpacker(u)
|
29
|
+
@id = u.get_int
|
30
|
+
p = u.parsed.last
|
31
|
+
@notes.push([:cyan, p[:pos], p[:len], "id=#{@id}"])
|
32
|
+
i = 0
|
33
|
+
@fields.map! do |_|
|
34
|
+
# TODO: as of right now it can get nil values here
|
35
|
+
# the fix would be "u.get_int || 0"
|
36
|
+
# but fixing it would probably make it harder
|
37
|
+
# to debug invalid data
|
38
|
+
#
|
39
|
+
# but do rethink this in a later point please :)
|
40
|
+
# for now call .validate() everywhere
|
41
|
+
val = u.get_int
|
42
|
+
|
43
|
+
p = u.parsed.last
|
44
|
+
color = (i % 2).zero? ? :yellow : :pink
|
45
|
+
desc = @field_names[i]
|
46
|
+
@notes.push([color, p[:pos], p[:len], "#{desc}=#{val}"])
|
47
|
+
i += 1
|
48
|
+
|
49
|
+
val
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def init_raw(data)
|
54
|
+
u = Unpacker.new(data)
|
55
|
+
init_unpacker(u)
|
56
|
+
end
|
57
|
+
|
58
|
+
def init_hash(attr)
|
59
|
+
@fields_names.each do |name|
|
60
|
+
instance_variable_set("@#{name}", attr[name] || 0)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_h
|
65
|
+
hash = {}
|
66
|
+
hash[:id] = @id
|
67
|
+
@field_names.each_with_index do |name, index|
|
68
|
+
hash[name] = @fields[index]
|
69
|
+
end
|
70
|
+
hash
|
71
|
+
end
|
72
|
+
|
73
|
+
# basically to_network
|
74
|
+
# int array the server sends to the client
|
75
|
+
def to_a
|
76
|
+
arr = []
|
77
|
+
@fields.each do |value|
|
78
|
+
arr += Packer.pack_int(value)
|
79
|
+
end
|
80
|
+
arr
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_s
|
84
|
+
to_h
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'items/game_data'
|
4
|
+
require_relative 'items/character'
|
5
|
+
require_relative 'items/projectile'
|
6
|
+
require_relative 'items/pickup'
|
7
|
+
require_relative 'items/flag'
|
8
|
+
require_relative 'items/game_data_team'
|
9
|
+
require_relative 'items/game_data_flag'
|
10
|
+
require_relative 'items/player_input'
|
11
|
+
require_relative 'items/laser'
|
12
|
+
require_relative 'items/player_info'
|
13
|
+
require_relative 'items/spectator_info'
|
14
|
+
require_relative 'items/client_info'
|
15
|
+
require_relative 'events/sound_world'
|
16
|
+
require_relative 'events/explosion'
|
17
|
+
require_relative 'events/spawn'
|
18
|
+
require_relative 'events/damage'
|
19
|
+
require_relative 'events/death'
|
20
|
+
require_relative 'events/hammer_hit'
|
21
|
+
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
|
31
|
+
|
32
|
+
class DDNetSnapItem
|
33
|
+
attr_accessor :notes, :name
|
34
|
+
|
35
|
+
@@registered_types = []
|
36
|
+
|
37
|
+
# TODO: rename to register uuid?!
|
38
|
+
def initialize(u, id)
|
39
|
+
@name = 'ddnet_uuid'
|
40
|
+
@notes = []
|
41
|
+
len = u.get_int
|
42
|
+
p = u.parsed.last
|
43
|
+
@notes.push([:green, p[:pos], p[:len], "len=#{len}"])
|
44
|
+
(0...len).each do |i|
|
45
|
+
val = u.get_int
|
46
|
+
p = u.parsed.last
|
47
|
+
col = (i % 2).zero? ? :bg_pink : :bg_yellow
|
48
|
+
@notes.push([col, p[:pos], p[:len], "val=#{val}"])
|
49
|
+
end
|
50
|
+
@@registered_types.push(id)
|
51
|
+
end
|
52
|
+
|
53
|
+
# parses registered ddnet items
|
54
|
+
def self.parse(u, _item_type)
|
55
|
+
id = u.get_int
|
56
|
+
p = u.parsed.last
|
57
|
+
notes = []
|
58
|
+
notes.push([:cyan, p[:pos], p[:len], "id=#{id}"])
|
59
|
+
len = u.get_int
|
60
|
+
p = u.parsed.last
|
61
|
+
notes.push([:green, p[:pos], p[:len], "len=#{len}"])
|
62
|
+
(0...len).each do |i|
|
63
|
+
val = u.get_int
|
64
|
+
p = u.parsed.last
|
65
|
+
col = (i % 2).zero? ? :bg_pink : :bg_yellow
|
66
|
+
notes.push([col, p[:pos], p[:len], "val=#{val}"])
|
67
|
+
end
|
68
|
+
notes
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.valid_type?(type)
|
72
|
+
@@registered_types.include?(type)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class SnapshotUnpacker
|
77
|
+
def initialize(client)
|
78
|
+
@client = client
|
79
|
+
end
|
80
|
+
|
81
|
+
def unpack_ddnet_item(u, notes)
|
82
|
+
id = u.get_int
|
83
|
+
p = u.parsed.last
|
84
|
+
notes.push([:cyan, p[:pos], p[:len], "id=#{id}"])
|
85
|
+
return nil if id < 0x4000 # ddnet offset uuid type
|
86
|
+
|
87
|
+
DDNetSnapItem.new(u, id)
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Given a NetChunk this method
|
92
|
+
# dissects the snapshot header
|
93
|
+
# and its payload (snap items)
|
94
|
+
#
|
95
|
+
# @return [Snapshot]
|
96
|
+
def snap_single(chunk)
|
97
|
+
u = Unpacker.new(chunk.data)
|
98
|
+
msg_id = u.get_int
|
99
|
+
msg_id >>= 1
|
100
|
+
|
101
|
+
num_parts = 1
|
102
|
+
part = 0
|
103
|
+
game_tick = u.get_int
|
104
|
+
delta_tick = u.get_int
|
105
|
+
part_size = 0
|
106
|
+
crc = 0
|
107
|
+
# complete_size = 0
|
108
|
+
# data = nil
|
109
|
+
|
110
|
+
# TODO: state check
|
111
|
+
|
112
|
+
if msg_id == NETMSG_SNAP
|
113
|
+
num_parts = u.get_int
|
114
|
+
part = u.get_int
|
115
|
+
end
|
116
|
+
|
117
|
+
unless msg_id == NETMSG_SNAPEMPTY
|
118
|
+
crc = u.get_int
|
119
|
+
part_size = u.get_int
|
120
|
+
end
|
121
|
+
|
122
|
+
snap_name = 'SNAP_INVALID'
|
123
|
+
case msg_id
|
124
|
+
when NETMSG_SNAP then snap_name = 'NETMSG_SNAP'
|
125
|
+
when NETMSG_SNAPSINGLE then snap_name = 'NETMSG_SNAPSINGLE'
|
126
|
+
when NETMSG_SNAPEMPTY then snap_name = 'NETMSG_SNAPEMPTY'
|
127
|
+
end
|
128
|
+
|
129
|
+
return unless msg_id == NETMSG_SNAPSINGLE
|
130
|
+
|
131
|
+
if @verbose
|
132
|
+
puts ">>> snap #{snap_name} (#{msg_id})"
|
133
|
+
puts " id=#{msg_id} game_tick=#{game_tick} delta_tick=#{delta_tick}"
|
134
|
+
puts " num_parts=#{num_parts} part=#{part} crc=#{crc} part_size=#{part_size}"
|
135
|
+
puts "\n header:"
|
136
|
+
end
|
137
|
+
|
138
|
+
header = []
|
139
|
+
notes = []
|
140
|
+
u.parsed.each_with_index do |parsed, index|
|
141
|
+
color = (index % 2).zero? ? :green : :pink
|
142
|
+
txt = "#{parsed[:type]} #{parsed[:value]}"
|
143
|
+
txt += " >> 1 = #{parsed[:value] >> 1}" if header.empty?
|
144
|
+
notes.push([color, parsed[:pos], parsed[:len], txt])
|
145
|
+
header += parsed[:raw]
|
146
|
+
end
|
147
|
+
|
148
|
+
if @verbose
|
149
|
+
hexdump_lines(header.pack('C*'), 1, notes, legend: :inline).each do |hex|
|
150
|
+
puts " #{hex}"
|
151
|
+
end
|
152
|
+
puts "\n payload:"
|
153
|
+
end
|
154
|
+
|
155
|
+
notes = []
|
156
|
+
|
157
|
+
data = u.get_raw
|
158
|
+
|
159
|
+
# tw decompresses all bytes at once
|
160
|
+
# and pads it with zeros to get the 4 byte aligned ints
|
161
|
+
# we just grab one int at a time cuz yolo
|
162
|
+
u = Unpacker.new(data)
|
163
|
+
num_removed_items = u.get_int
|
164
|
+
p = u.parsed.last
|
165
|
+
notes.push([:red, p[:pos], p[:len], "removed_items=#{num_removed_items}"])
|
166
|
+
|
167
|
+
num_item_deltas = u.get_int
|
168
|
+
p = u.parsed.last
|
169
|
+
notes.push([:blue, p[:pos], p[:len], "num_item_deltas=#{num_item_deltas}"])
|
170
|
+
|
171
|
+
zero = u.get_int
|
172
|
+
p = u.parsed.last
|
173
|
+
notes.push([:cyan, p[:pos], p[:len], "_zero=#{zero}"])
|
174
|
+
|
175
|
+
(0...num_removed_items).each do |i|
|
176
|
+
deleted = u.get_int
|
177
|
+
notes.push([:red, p[:pos], p[:len], "del[#{i}]=#{deleted}"])
|
178
|
+
end
|
179
|
+
|
180
|
+
invalid = false
|
181
|
+
item_type = u.get_int
|
182
|
+
id_parsed = u.parsed.last
|
183
|
+
snap_items = []
|
184
|
+
while item_type
|
185
|
+
obj = nil
|
186
|
+
if NetObj::PlayerInput.match_type?(item_type)
|
187
|
+
obj = NetObj::PlayerInput.new(u)
|
188
|
+
elsif NetObj::Projectile.match_type?(item_type)
|
189
|
+
obj = NetObj::Projectile.new(u)
|
190
|
+
elsif NetObj::Laser.match_type?(item_type)
|
191
|
+
obj = NetObj::Laser.new(u)
|
192
|
+
elsif NetObj::Pickup.match_type?(item_type)
|
193
|
+
obj = NetObj::Pickup.new(u)
|
194
|
+
elsif NetObj::Flag.match_type?(item_type)
|
195
|
+
obj = NetObj::Flag.new(u)
|
196
|
+
elsif NetObj::GameData.match_type?(item_type)
|
197
|
+
obj = NetObj::GameData.new(u)
|
198
|
+
elsif NetObj::GameDataTeam.match_type?(item_type)
|
199
|
+
obj = NetObj::GameDataTeam.new(u)
|
200
|
+
elsif NetObj::GameDataFlag.match_type?(item_type)
|
201
|
+
obj = NetObj::GameDataFlag.new(u)
|
202
|
+
elsif NetObj::Character.match_type?(item_type)
|
203
|
+
obj = NetObj::Character.new(u)
|
204
|
+
elsif NetObj::PlayerInfo.match_type?(item_type)
|
205
|
+
obj = NetObj::PlayerInfo.new(u)
|
206
|
+
elsif NetObj::SpectatorInfo.match_type?(item_type)
|
207
|
+
obj = NetObj::SpectatorInfo.new(u)
|
208
|
+
elsif NetObj::ClientInfo.match_type?(item_type)
|
209
|
+
obj = NetObj::ClientInfo.new(u)
|
210
|
+
elsif NetEvent::Explosion.match_type?(item_type)
|
211
|
+
obj = NetEvent::Explosion.new(u)
|
212
|
+
elsif NetEvent::SoundWorld.match_type?(item_type)
|
213
|
+
obj = NetEvent::SoundWorld.new(u)
|
214
|
+
elsif NetEvent::Spawn.match_type?(item_type)
|
215
|
+
obj = NetEvent::Spawn.new(u)
|
216
|
+
elsif NetEvent::Damage.match_type?(item_type)
|
217
|
+
obj = NetEvent::Damage.new(u)
|
218
|
+
elsif NetEvent::Death.match_type?(item_type)
|
219
|
+
obj = NetEvent::Death.new(u)
|
220
|
+
elsif NetEvent::HammerHit.match_type?(item_type)
|
221
|
+
obj = NetEvent::HammerHit.new(u)
|
222
|
+
elsif @verbose
|
223
|
+
puts "no match #{item_type}"
|
224
|
+
end
|
225
|
+
obj = unpack_ddnet_item(u, notes) if !obj && item_type.zero?
|
226
|
+
if obj
|
227
|
+
snap_items.push(obj)
|
228
|
+
notes += obj.notes
|
229
|
+
notes.push([
|
230
|
+
:green,
|
231
|
+
id_parsed[:pos],
|
232
|
+
id_parsed[:len],
|
233
|
+
"type=#{item_type} #{obj.name}"
|
234
|
+
])
|
235
|
+
elsif DDNetSnapItem.valid_type?(item_type)
|
236
|
+
notes.push([
|
237
|
+
:green,
|
238
|
+
id_parsed[:pos],
|
239
|
+
id_parsed[:len],
|
240
|
+
"type=#{item_type} ddnet_ex_reg"
|
241
|
+
])
|
242
|
+
notes += DDNetSnapItem.parse(u, item_type)
|
243
|
+
elsif item_type < 50 # TODO: i made up this magic number xd
|
244
|
+
# figure out what a sane
|
245
|
+
# limit for the type is
|
246
|
+
# something that is a bit
|
247
|
+
# future proof
|
248
|
+
# and also strict enough
|
249
|
+
# to alert when something
|
250
|
+
# goes wrong
|
251
|
+
# item with non pre-agreed size
|
252
|
+
# first int of the payload is the size of the payload
|
253
|
+
id = u.get_int
|
254
|
+
p = u.parsed.last
|
255
|
+
notes.push([:cyan, p[:pos], p[:len], "id=#{id}"])
|
256
|
+
len = u.get_int
|
257
|
+
p = u.parsed.last
|
258
|
+
notes.push([:green, p[:pos], p[:len], "len=#{len}"])
|
259
|
+
(0...len).each do |i|
|
260
|
+
val = u.get_int
|
261
|
+
p = u.parsed.last
|
262
|
+
col = (i % 2).zero? ? :bg_pink : :bg_yellow
|
263
|
+
notes.push([col, p[:pos], p[:len], "val=#{val}"])
|
264
|
+
end
|
265
|
+
else
|
266
|
+
invalid = true
|
267
|
+
notes.push([
|
268
|
+
:bg_red,
|
269
|
+
id_parsed[:pos],
|
270
|
+
id_parsed[:len],
|
271
|
+
"invalid_type=#{item_type}"
|
272
|
+
])
|
273
|
+
end
|
274
|
+
item_type = u.get_int
|
275
|
+
id_parsed = u.parsed.last
|
276
|
+
end
|
277
|
+
|
278
|
+
if @verbose
|
279
|
+
hexdump_lines(data.pack('C*'), 1, notes, legend: :inline).each do |hex|
|
280
|
+
puts " #{hex}"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
if invalid
|
285
|
+
# make sure if we did not print the hex already
|
286
|
+
# to print it now as error message
|
287
|
+
unless @verbose
|
288
|
+
hexdump_lines(data.pack('C*'), 1, notes, legend: :inline).each do |hex|
|
289
|
+
puts " #{hex}"
|
290
|
+
end
|
291
|
+
end
|
292
|
+
puts 'Error: got invalid snap item'
|
293
|
+
@client.disconnect
|
294
|
+
exit 1
|
295
|
+
end
|
296
|
+
|
297
|
+
snapshot = Snapshot.new(snap_items)
|
298
|
+
snapshot.game_tick = game_tick
|
299
|
+
snapshot
|
300
|
+
end
|
301
|
+
end
|