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
data/lib/network.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
GAME_VERSION = '0.7.5'
|
4
|
+
GAME_NETVERSION_HASH_FORCED = '802f1be60a05665f'
|
5
|
+
GAME_NETVERSION = "0.7 #{GAME_NETVERSION_HASH_FORCED}".freeze
|
6
|
+
CLIENT_VERSION = 0x0705
|
7
|
+
|
8
|
+
NETMSG_NULL = 0
|
9
|
+
NETMSG_INFO = 1
|
10
|
+
NETMSG_MAP_CHANGE = 2 # sent when client should switch map
|
11
|
+
NETMSG_MAP_DATA = 3 # map transfer, contains a chunk of the map file
|
12
|
+
NETMSG_SERVERINFO = 4
|
13
|
+
NETMSG_CON_READY = 5 # connection is ready, client should send start info
|
14
|
+
NETMSG_SNAP = 6 # normal snapshot, multiple parts
|
15
|
+
NETMSG_SNAPEMPTY = 7 # empty snapshot
|
16
|
+
NETMSG_SNAPSINGLE = 8 # ?
|
17
|
+
NETMSG_SNAPSMALL = 9
|
18
|
+
NETMSG_INPUTTIMING = 10 # reports how off the input was
|
19
|
+
NETMSG_RCON_AUTH_ON = 11 # rcon authentication enabled
|
20
|
+
NETMSG_RCON_AUTH_OFF = 12 # rcon authentication disabled
|
21
|
+
NETMSG_RCON_LINE = 13 # line that should be printed to the remote console
|
22
|
+
NETMSG_RCON_CMD_ADD = 14
|
23
|
+
NETMSG_RCON_CMD_REM = 15
|
24
|
+
|
25
|
+
NETMSG_MAPLIST_ENTRY_ADD = 29 # TODO: 0.8: move up
|
26
|
+
NETMSG_MAPLIST_ENTRY_REM = 30
|
27
|
+
|
28
|
+
# sent by client
|
29
|
+
NETMSG_READY = 18
|
30
|
+
NETMSG_ENTERGAME = 19
|
31
|
+
NETMSG_INPUT = 20 # contains the inputdata from the client
|
32
|
+
NETMSG_RCON_CMD = 21
|
33
|
+
NETMSG_RCON_AUTH = 22
|
34
|
+
NETMSG_REQUEST_MAP_DATA = 23
|
35
|
+
|
36
|
+
NETMSG_INVALID = 0
|
37
|
+
NETMSGTYPE_SV_MOTD = 1
|
38
|
+
NETMSGTYPE_SV_BROADCAST = 2
|
39
|
+
NETMSGTYPE_SV_CHAT = 3
|
40
|
+
NETMSGTYPE_SV_TEAM = 4
|
41
|
+
NETMSGTYPE_SV_KILLMSG = 5
|
42
|
+
NETMSGTYPE_SV_TUNEPARAMS = 6
|
43
|
+
NETMSGTYPE_SV_EXTRAPROJECTILE = 7
|
44
|
+
NETMSGTYPE_SV_READYTOENTER = 8
|
45
|
+
NETMSGTYPE_SV_WEAPONPICKUP = 19
|
46
|
+
NETMSGTYPE_SV_EMOTICON = 10
|
47
|
+
NETMSGTYPE_SV_VOTECLEAROPTIONS = 11
|
48
|
+
NETMSGTYPE_SV_VOTEOPTIONLISTADD = 12
|
49
|
+
NETMSGTYPE_SV_VOTEOPTIONADD = 13
|
50
|
+
NETMSGTYPE_SV_VOTEOPTIONREMOVE = 14
|
51
|
+
NETMSGTYPE_SV_VOTESET = 15
|
52
|
+
NETMSGTYPE_SV_VOTESTATUS = 16
|
53
|
+
NETMSGTYPE_SV_SERVERSETTINGS = 17
|
54
|
+
NETMSGTYPE_SV_CLIENTINFO = 18
|
55
|
+
NETMSGTYPE_SV_GAMEINFO = 19
|
56
|
+
NETMSGTYPE_SV_CLIENTDROP = 20
|
57
|
+
NETMSGTYPE_SV_GAMEMSG = 21
|
58
|
+
NETMSGTYPE_DE_CLIENTENTER = 22
|
59
|
+
NETMSGTYPE_DE_CLIENTLEAVE = 23
|
60
|
+
NETMSGTYPE_CL_SAY = 24
|
61
|
+
NETMSGTYPE_CL_SETTEAM = 25
|
62
|
+
NETMSGTYPE_CL_SETSPECTATORMODE = 26
|
63
|
+
NETMSGTYPE_CL_STARTINFO = 27
|
64
|
+
NETMSGTYPE_CL_KILL = 28
|
65
|
+
NETMSGTYPE_CL_READYCHANGE = 29
|
66
|
+
NETMSGTYPE_CL_EMOTICON = 30
|
67
|
+
NETMSGTYPE_CL_VOTE = 31
|
68
|
+
NETMSGTYPE_CL_CALLVOTE = 32
|
69
|
+
NETMSGTYPE_SV_SKINCHANGE = 33
|
70
|
+
NETMSGTYPE_CL_SKINCHANGE = 34
|
71
|
+
NETMSGTYPE_SV_RACEFINISH = 35
|
72
|
+
NETMSGTYPE_SV_CHECKPOINT = 36
|
73
|
+
NETMSGTYPE_SV_COMMANDINFO = 37
|
74
|
+
NETMSGTYPE_SV_COMMANDINFOREMOVE = 38
|
75
|
+
NETMSGTYPE_CL_COMMAND = 39
|
76
|
+
NUM_NETMSGTYPES = 40
|
77
|
+
|
78
|
+
NET_CTRLMSG_KEEPALIVE = 0
|
79
|
+
NET_CTRLMSG_CONNECT = 1
|
80
|
+
NET_CTRLMSG_ACCEPT = 2
|
81
|
+
NET_CTRLMSG_CLOSE = 4
|
82
|
+
NET_CTRLMSG_TOKEN = 5
|
83
|
+
|
84
|
+
NET_MAX_SEQUENCE = 1 << 10
|
85
|
+
|
86
|
+
NET_CONNSTATE_OFFLINE = 0
|
87
|
+
NET_CONNSTATE_TOKEN = 1
|
88
|
+
NET_CONNSTATE_CONNECT = 2
|
89
|
+
NET_CONNSTATE_PENDING = 3
|
90
|
+
NET_CONNSTATE_ONLINE = 4
|
91
|
+
NET_CONNSTATE_ERROR = 5
|
92
|
+
|
93
|
+
NET_MAX_CHUNKHEADERSIZE = 3
|
94
|
+
|
95
|
+
NET_PACKETHEADERSIZE = 7
|
96
|
+
NET_PACKETHEADERSIZE_CONNLESS = NET_PACKETHEADERSIZE + 2
|
97
|
+
NET_MAX_PACKETHEADERSIZE = NET_PACKETHEADERSIZE_CONNLESS
|
98
|
+
|
99
|
+
NET_MAX_PACKETSIZE = 1400
|
100
|
+
NET_MAX_PAYLOAD = NET_MAX_PACKETSIZE - NET_MAX_PACKETHEADERSIZE
|
101
|
+
|
102
|
+
CHAT_NONE = 0
|
103
|
+
CHAT_ALL = 1
|
104
|
+
CHAT_TEAM = 2
|
105
|
+
CHAT_WHISPER = 3
|
106
|
+
NUM_CHATS = 4
|
107
|
+
|
108
|
+
TARGET_SERVER = -1
|
109
|
+
|
110
|
+
PACKET_HEADER_SIZE = 7
|
111
|
+
VITAL_CHUNK_HEADER_SIZE = 3
|
112
|
+
NON_VITAL_CHUNK_HEADER_SIZE = 2
|
113
|
+
|
114
|
+
MAX_CLIENTS = 64
|
115
|
+
MAX_PLAYERS = 16
|
116
|
+
|
117
|
+
MAP_CHUNK_SIZE = NET_MAX_PAYLOAD - NET_MAX_CHUNKHEADERSIZE - 4 # msg type
|
118
|
+
|
119
|
+
SERVER_TICK_SPEED = 50
|
120
|
+
|
121
|
+
# should this be moved to some snap related file?
|
122
|
+
|
123
|
+
NETOBJ_INVALID = 0
|
124
|
+
NETOBJTYPE_PLAYERINPUT = 1
|
125
|
+
NETOBJTYPE_PROJECTILE = 2
|
126
|
+
NETOBJTYPE_LASER = 3
|
127
|
+
NETOBJTYPE_PICKUP = 4
|
128
|
+
NETOBJTYPE_FLAG = 5
|
129
|
+
NETOBJTYPE_GAMEDATA = 6
|
130
|
+
NETOBJTYPE_GAMEDATATEAM = 7
|
131
|
+
NETOBJTYPE_GAMEDATAFLAG = 8
|
132
|
+
NETOBJTYPE_CHARACTERCORE = 9
|
133
|
+
NETOBJTYPE_CHARACTER = 10
|
134
|
+
NETOBJTYPE_PLAYERINFO = 11
|
135
|
+
NETOBJTYPE_SPECTATORINFO = 12
|
136
|
+
NETOBJTYPE_DE_CLIENTINFO = 13
|
137
|
+
NETOBJTYPE_DE_GAMEINFO = 14
|
138
|
+
NETOBJTYPE_DE_TUNEPARAMS = 15
|
139
|
+
NETEVENTTYPE_COMMON = 16
|
140
|
+
NETEVENTTYPE_EXPLOSION = 17
|
141
|
+
NETEVENTTYPE_SPAWN = 18
|
142
|
+
NETEVENTTYPE_HAMMERHIT = 19
|
143
|
+
NETEVENTTYPE_DEATH = 20
|
144
|
+
NETEVENTTYPE_SOUNDWORLD = 21
|
145
|
+
NETEVENTTYPE_DAMAGE = 22
|
146
|
+
NETOBJTYPE_PLAYERINFORACE = 23
|
147
|
+
NETOBJTYPE_GAMEDATARACE = 24
|
148
|
+
NUM_NETOBJTYPE = 25
|
data/lib/packer.rb
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'array'
|
4
|
+
|
5
|
+
SANITIZE = 1
|
6
|
+
SANITIZE_CC = 2
|
7
|
+
SKIP_START_WHITESPACES = 4
|
8
|
+
|
9
|
+
class Packer
|
10
|
+
# Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign
|
11
|
+
def self.pack_int(num)
|
12
|
+
# the first byte can fit 6 bits
|
13
|
+
# because the first two bits are extended and sign
|
14
|
+
# so if you have a number bigger than 63
|
15
|
+
# it needs two bytes
|
16
|
+
# which are also least significant byte first etc
|
17
|
+
# so we do not support that YET
|
18
|
+
#
|
19
|
+
# the first too big number 64 is represented as those bits
|
20
|
+
# 10000000 00000001
|
21
|
+
# ^^^ ^ ^^ ^
|
22
|
+
# ||\ / | \ /
|
23
|
+
# || \ / not \ /
|
24
|
+
# || \ extended
|
25
|
+
# || \ /
|
26
|
+
# || \ /
|
27
|
+
# || \ /
|
28
|
+
# || \/
|
29
|
+
# || /\
|
30
|
+
# || / \
|
31
|
+
# || / \
|
32
|
+
# || 0000001 000000
|
33
|
+
# || |
|
34
|
+
# || v
|
35
|
+
# || 64
|
36
|
+
# ||
|
37
|
+
# |positive
|
38
|
+
# extended
|
39
|
+
sign = '0'
|
40
|
+
if num.negative?
|
41
|
+
sign = '1'
|
42
|
+
num += 1
|
43
|
+
end
|
44
|
+
num = num.abs
|
45
|
+
return pack_big_int(sign, num) if num > 63 || num < -63
|
46
|
+
|
47
|
+
ext = '0'
|
48
|
+
bits = ext + sign + num.to_s(2).rjust(6, '0')
|
49
|
+
[bits.to_i(2)]
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.pack_big_int(sign, num)
|
53
|
+
num_bits = num.to_s(2)
|
54
|
+
first = "1#{sign}#{num_bits[-6..]}"
|
55
|
+
|
56
|
+
num_bits = num_bits[0..-7]
|
57
|
+
bytes = []
|
58
|
+
num_bits.chars.groups_of(7).each do |seven_bits|
|
59
|
+
# mark all as extended
|
60
|
+
bytes << "1#{seven_bits.join.rjust(7, '0')}"
|
61
|
+
end
|
62
|
+
# least significant first
|
63
|
+
bytes = bytes.reverse
|
64
|
+
# mark last byte as unextended
|
65
|
+
bytes[-1][0] = '0'
|
66
|
+
([first] + bytes).map { |b| b.to_i(2) }
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.pack_str(str)
|
70
|
+
str.chars.map(&:ord) + [0x00]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Unpacker
|
75
|
+
attr_reader :prev, :data, :parsed
|
76
|
+
|
77
|
+
def initialize(data)
|
78
|
+
@data = data
|
79
|
+
@prev = [] # all the data already unpacked
|
80
|
+
@parsed = []
|
81
|
+
# TODO: delete parsed or document it with examples
|
82
|
+
# @parsed.push({ type: 'string', value: str, raw:, len:, pos: })
|
83
|
+
@red_bytes = 0
|
84
|
+
# { type: 'int', value: 1, raw: "\x01", len: 1, pos: 0 }
|
85
|
+
if data.instance_of?(String)
|
86
|
+
@data = data.unpack('C*')
|
87
|
+
elsif data.instance_of?(Array)
|
88
|
+
@data = data
|
89
|
+
else
|
90
|
+
raise 'Error: Unpacker expects array of integers or byte string'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def str_sanitize(str)
|
95
|
+
letters = str.chars
|
96
|
+
letters.map! do |c|
|
97
|
+
c.ord < 32 && c != "\r" && c != "\n" && c != "\t" ? ' ' : c
|
98
|
+
end
|
99
|
+
letters.join
|
100
|
+
end
|
101
|
+
|
102
|
+
def str_sanitize_cc(str)
|
103
|
+
letters = str.chars
|
104
|
+
letters.map! do |c|
|
105
|
+
c.ord < 32 ? ' ' : c
|
106
|
+
end
|
107
|
+
letters.join
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_string(sanitize = SANITIZE)
|
111
|
+
return nil if @data.nil?
|
112
|
+
|
113
|
+
str = ''
|
114
|
+
raw = []
|
115
|
+
len = 0
|
116
|
+
pos = @red_bytes
|
117
|
+
@data.each_with_index do |byte, index|
|
118
|
+
@prev.push(byte)
|
119
|
+
raw.push(raw)
|
120
|
+
@red_bytes += 1
|
121
|
+
len += 1
|
122
|
+
if byte.zero?
|
123
|
+
@data = index == @data.length - 1 ? nil : @data[(index + 1)..]
|
124
|
+
str = str_sanitize(str) unless (sanitize & SANITIZE).zero?
|
125
|
+
str = str_sanitize_cc(str) unless (sanitize & SANITIZE_CC).zero?
|
126
|
+
@parsed.push({ type: 'string', value: str, raw:, len:, pos: })
|
127
|
+
return str
|
128
|
+
end
|
129
|
+
str += byte.chr
|
130
|
+
end
|
131
|
+
# raise "get_string() failed to find null terminator"
|
132
|
+
# return empty string in case of error
|
133
|
+
''
|
134
|
+
end
|
135
|
+
|
136
|
+
def get_int
|
137
|
+
return nil if @data.nil?
|
138
|
+
return nil if @data.empty?
|
139
|
+
|
140
|
+
# TODO: make this more performant
|
141
|
+
# it should not read in ALL bytes
|
142
|
+
# of the WHOLE packed data
|
143
|
+
# it should be max 4 bytes
|
144
|
+
# because bigger ints are not sent anyways
|
145
|
+
bytes = @data.map { |byte| byte.to_s(2).rjust(8, '0') }
|
146
|
+
first = bytes[0]
|
147
|
+
|
148
|
+
sign = first[1]
|
149
|
+
bits = []
|
150
|
+
parsed = { type: 'int' }
|
151
|
+
|
152
|
+
# extended
|
153
|
+
if first[0] == '1'
|
154
|
+
bits << first[2..]
|
155
|
+
bytes = bytes[1..]
|
156
|
+
consumed = 1
|
157
|
+
bytes.each do |eigth_bits|
|
158
|
+
bits << eigth_bits[1..]
|
159
|
+
consumed += 1
|
160
|
+
|
161
|
+
break if eigth_bits[0] == '0'
|
162
|
+
end
|
163
|
+
bits = bits.reverse
|
164
|
+
@prev += @data[0...consumed]
|
165
|
+
parsed[:raw] = @data[0...consumed]
|
166
|
+
parsed[:len] = consumed
|
167
|
+
parsed[:pos] = @red_bytes
|
168
|
+
@red_bytes += consumed
|
169
|
+
@data = @data[consumed..]
|
170
|
+
else # single byte
|
171
|
+
bits = [first[2..]]
|
172
|
+
@prev.push(@data[0])
|
173
|
+
parsed[:raw] = [@data[0]]
|
174
|
+
parsed[:len] = 1
|
175
|
+
parsed[:pos] = @red_bytes
|
176
|
+
@red_bytes += 1
|
177
|
+
@data = @data[1..]
|
178
|
+
end
|
179
|
+
num = bits.join.to_i(2)
|
180
|
+
parsed[:value] = sign == '1' ? -(num + 1) : num
|
181
|
+
@parsed.push(parsed)
|
182
|
+
parsed[:value]
|
183
|
+
end
|
184
|
+
|
185
|
+
def get_raw(size = -1)
|
186
|
+
len = size == -1 ? @data.size : size
|
187
|
+
@prev += @data[...len]
|
188
|
+
pos = @red_bytes
|
189
|
+
@red_bytes += len
|
190
|
+
@parsed.push({ type: 'raw', value: @data[...len], raw: @data[...len], len:, pos: })
|
191
|
+
# TODO: error if size exceeds @data.size
|
192
|
+
@data.shift(len)
|
193
|
+
end
|
194
|
+
end
|
data/lib/packet.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'models/net_addr'
|
4
|
+
require_relative 'models/packet_flags'
|
5
|
+
|
6
|
+
require 'huffman_tw'
|
7
|
+
|
8
|
+
# Class holding the parsed packet data
|
9
|
+
class Packet
|
10
|
+
attr_reader :flags, :payload, :addr
|
11
|
+
attr_accessor :client_id, :client
|
12
|
+
|
13
|
+
def initialize(data, prefix = '')
|
14
|
+
# @data and @payload
|
15
|
+
# are strings representing the raw bytes
|
16
|
+
#
|
17
|
+
# @prefix is a String that will be displayed
|
18
|
+
# when printing the packet
|
19
|
+
# use '>' and '<' for example to indicate
|
20
|
+
# network direction (client/server)
|
21
|
+
@prefix = prefix
|
22
|
+
@addr = NetAddr.new(nil, nil)
|
23
|
+
@huffman = Huffman.new
|
24
|
+
@client_id = nil
|
25
|
+
@client = nil
|
26
|
+
@data = data
|
27
|
+
flags_byte = @data[0].unpack('B*')
|
28
|
+
@flags = PacketFlags.new(flags_byte.first[2..5]).hash
|
29
|
+
@payload = @data[PACKET_HEADER_SIZE..]
|
30
|
+
return unless flags_compressed
|
31
|
+
|
32
|
+
@payload = @huffman.decompress(@payload.unpack('C*'))
|
33
|
+
@payload = @payload.pack('C*')
|
34
|
+
end
|
35
|
+
|
36
|
+
def annotate_first_row(bytes)
|
37
|
+
header = bytes[0..2].join(' ').yellow
|
38
|
+
token = bytes[3..6].join(' ').green
|
39
|
+
payload = bytes[7..].join(' ')
|
40
|
+
puts @prefix + " data: #{[header, token, payload].join(' ')}"
|
41
|
+
print "#{@prefix} "
|
42
|
+
print 'header'.ljust(3 * 3, ' ').yellow
|
43
|
+
print 'token'.ljust(4 * 3, ' ').green
|
44
|
+
puts 'data'
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
puts "#{@prefix}Packet"
|
49
|
+
puts @prefix + " flags: #{@flags}"
|
50
|
+
bytes = str_hex(@data).split
|
51
|
+
# TODO: check terminal size?
|
52
|
+
max_width = 14
|
53
|
+
rows = bytes.groups_of(max_width)
|
54
|
+
annotate_first_row(rows.first)
|
55
|
+
rows[1..].each do |row|
|
56
|
+
print "#{@prefix} "
|
57
|
+
puts row.join(' ')
|
58
|
+
end
|
59
|
+
puts ''
|
60
|
+
end
|
61
|
+
|
62
|
+
def flags_compressed
|
63
|
+
@flags[:compressed]
|
64
|
+
end
|
65
|
+
|
66
|
+
def flags_connless
|
67
|
+
@flags[:connection] == false
|
68
|
+
end
|
69
|
+
|
70
|
+
def flags_control
|
71
|
+
@flags[:control]
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_event_base'
|
4
|
+
|
5
|
+
class NetEvent
|
6
|
+
class Damage < SnapEventBase
|
7
|
+
attr_accessor :client_id, :angle, :health_ammount, :armor_amount, :self
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
client_id
|
12
|
+
angle
|
13
|
+
health_ammount
|
14
|
+
armor_amount
|
15
|
+
self
|
16
|
+
]
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.match_type?(type)
|
21
|
+
type == NETEVENTTYPE_DAMAGE
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_event_base'
|
4
|
+
|
5
|
+
class NetEvent
|
6
|
+
class Death < SnapEventBase
|
7
|
+
attr_accessor :client_id
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
client_id
|
12
|
+
]
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.match_type?(type)
|
17
|
+
type == NETEVENTTYPE_DEATH
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetEvent
|
6
|
+
class Explosion < SnapEventBase
|
7
|
+
def initialize(hash_or_raw)
|
8
|
+
@field_names = []
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.match_type?(type)
|
13
|
+
type == NETEVENTTYPE_EXPLOSION
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetEvent
|
6
|
+
class HammerHit < SnapEventBase
|
7
|
+
def initialize(hash_or_raw)
|
8
|
+
@field_names = []
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.match_type?(type)
|
13
|
+
type == NETEVENTTYPE_HAMMERHIT
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_event_base'
|
4
|
+
|
5
|
+
class NetEvent
|
6
|
+
class SoundWorld < SnapEventBase
|
7
|
+
attr_accessor :sound_id
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
sound_id
|
12
|
+
]
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.match_type?(type)
|
17
|
+
type == NETEVENTTYPE_SOUNDWORLD
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetEvent
|
6
|
+
class Spawn < SnapEventBase
|
7
|
+
def initialize(hash_or_raw)
|
8
|
+
@field_names = []
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.match_type?(type)
|
13
|
+
type == NETEVENTTYPE_SPAWN
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetObj
|
6
|
+
class Character < SnapItemBase
|
7
|
+
attr_accessor :tick, :x, :y, :vel_x, :vel_y, :angle, :direction, :jumped, :hooked_player, :hook_state, :hook_tick,
|
8
|
+
:hook_x, :hook_y, :hook_dx, :hook_dy,
|
9
|
+
:health, :armor, :ammo_count, :weapon, :emote, :attack_tick, :triggered_events
|
10
|
+
|
11
|
+
def initialize(hash_or_raw)
|
12
|
+
@field_names = %i[
|
13
|
+
tick
|
14
|
+
x
|
15
|
+
y
|
16
|
+
vel_x
|
17
|
+
vel_y
|
18
|
+
angle
|
19
|
+
direction
|
20
|
+
jumped
|
21
|
+
hooked_player
|
22
|
+
hook_state
|
23
|
+
hook_tick
|
24
|
+
hook_x
|
25
|
+
hook_y
|
26
|
+
hook_dx
|
27
|
+
hook_dy
|
28
|
+
health
|
29
|
+
armor
|
30
|
+
ammo_count
|
31
|
+
weapon
|
32
|
+
emote
|
33
|
+
attack_tick
|
34
|
+
triggered_events
|
35
|
+
]
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.match_type?(type)
|
40
|
+
type == NETOBJTYPE_CHARACTER
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetObj
|
6
|
+
class ClientInfo < SnapItemBase
|
7
|
+
attr_accessor :local, :team
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
local
|
12
|
+
team
|
13
|
+
]
|
14
|
+
super
|
15
|
+
raise 'ClientInfo includes strings that is not supported yet'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.match_type?(type)
|
19
|
+
type == NETOBJTYPE_DE_CLIENTINFO
|
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 Flag < SnapItemBase
|
7
|
+
attr_accessor :x, :y, :team
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
x
|
12
|
+
y
|
13
|
+
team
|
14
|
+
]
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.match_type?(type)
|
19
|
+
type == NETOBJTYPE_FLAG
|
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 GameData < SnapItemBase
|
7
|
+
attr_accessor :game_start_tick, :game_state_flags, :game_state_end_tick
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
game_start_tick
|
12
|
+
game_state_flags
|
13
|
+
game_state_end_tick
|
14
|
+
]
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.match_type?(type)
|
19
|
+
type == NETOBJTYPE_GAMEDATA
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetObj
|
6
|
+
class GameDataFlag < SnapItemBase
|
7
|
+
attr_accessor :flag_carrier_red, :flag_carrier_blue,
|
8
|
+
:flag_drop_tick_red, :flag_drop_tick_blue
|
9
|
+
|
10
|
+
def initialize(hash_or_raw)
|
11
|
+
@field_names = %i[
|
12
|
+
flag_carrier_red
|
13
|
+
flag_carrier_blue
|
14
|
+
flag_drop_tick_red
|
15
|
+
flag_drop_tick_blue
|
16
|
+
]
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.match_type?(type)
|
21
|
+
type == NETOBJTYPE_GAMEDATAFLAG
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../snap_item_base'
|
4
|
+
|
5
|
+
class NetObj
|
6
|
+
class GameDataTeam < SnapItemBase
|
7
|
+
attr_accessor :teamscore_red, :teamscore_blue
|
8
|
+
|
9
|
+
def initialize(hash_or_raw)
|
10
|
+
@field_names = %i[
|
11
|
+
teamscore_red
|
12
|
+
teamscore_blue
|
13
|
+
]
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.match_type?(type)
|
18
|
+
type == NETOBJTYPE_GAMEDATATEAM
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|