libsl 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.
- data/.gitignore +1 -0
- data/README.md +50 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/lib/_packets.rb +10346 -0
- data/lib/agent.rb +119 -0
- data/lib/client.rb +57 -0
- data/lib/dsl.rb +54 -0
- data/lib/events.rb +59 -0
- data/lib/libsl.rb +14 -0
- data/lib/network.rb +305 -0
- data/lib/packet.rb +279 -0
- data/lib/types.rb +490 -0
- data/libsl.gemspec +30 -0
- data/test/tc_packet.rb +99 -0
- data/test/tc_types.rb +342 -0
- data/test/ts_all.rb +2 -0
- metadata +101 -0
data/lib/packet.rb
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
|
2
|
+
module LibSL
|
3
|
+
class Packet
|
4
|
+
def self.packet_id()
|
5
|
+
0
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.decode(data)
|
9
|
+
flags, data = LLU8.decode(data)
|
10
|
+
sequence_number, data = LLSequenceNumber.decode(data)
|
11
|
+
extra, data = LLU8.decode(data)
|
12
|
+
if extra.value > 0 then
|
13
|
+
extra, data = LLFixed.decode(data, extra)
|
14
|
+
end
|
15
|
+
data = zero_decode(data) if flags.value & 0x80 == 0x80
|
16
|
+
packet_number, data = LLPacketNumber.decode(data)
|
17
|
+
klass = MESSAGE_MAP[packet_number.number]
|
18
|
+
throw "Unknown message type!" if klass.nil?
|
19
|
+
|
20
|
+
packet = klass.new
|
21
|
+
packet.zero_coded_flag = flags.value & 0x80 == 0x80
|
22
|
+
packet.reliable_flag = flags.value & 0x40 == 0x40
|
23
|
+
packet.resent_flag = flags.value & 0x20 == 0x20
|
24
|
+
packet.acks_flag = flags.value & 0x10 == 0x10
|
25
|
+
packet.sequence_number = sequence_number
|
26
|
+
data = packet.decode_msg(data)
|
27
|
+
if packet.acks_flag && data.length > 0 then
|
28
|
+
# Extract acks
|
29
|
+
ack_count = data[-1]
|
30
|
+
packet.acks = data.unpack('N'+ack_count)
|
31
|
+
end
|
32
|
+
packet
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.zero_decode(data)
|
36
|
+
# Decode 0x00 0xXX to a sequence of 0xXX zeros
|
37
|
+
decoded = []
|
38
|
+
zero = false
|
39
|
+
data.bytes do |byte|
|
40
|
+
if byte == 0x00
|
41
|
+
zero = true
|
42
|
+
elsif zero == true
|
43
|
+
byte.times{ decoded << 0x00 }
|
44
|
+
zero = false
|
45
|
+
else
|
46
|
+
decoded << byte
|
47
|
+
end
|
48
|
+
end
|
49
|
+
decoded.pack('C*')
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_accessor :acks, :zero_coded_flag, :reliable_flag, :resent_flag, :acks_flag,
|
53
|
+
:sequence_number, :resent_count
|
54
|
+
|
55
|
+
# Create a new packet
|
56
|
+
# @param [Hash] blocks (optional) Data blocks
|
57
|
+
# Example:
|
58
|
+
# {
|
59
|
+
# :Block1 => { # Block
|
60
|
+
# :Var1 => LLU8.new(0xff),
|
61
|
+
# :Var2 => LLVariable1.new("abcd")
|
62
|
+
# },
|
63
|
+
# :Block2 => [ # Block collection
|
64
|
+
# {
|
65
|
+
# :Var1 => LLFixed.new("block1"),
|
66
|
+
# :Var2 => LLS32.new(0x12345678)
|
67
|
+
# },
|
68
|
+
# {
|
69
|
+
# :Var1 => LLFixed.new("block2"),
|
70
|
+
# :Var2 => LLS32.new(0x12345678)
|
71
|
+
# }
|
72
|
+
# ]
|
73
|
+
# }
|
74
|
+
def initialize(blocks={})
|
75
|
+
@zero_coded_flag = false
|
76
|
+
@reliable_flag = false
|
77
|
+
@resent_flag = false
|
78
|
+
@acks_flag = false
|
79
|
+
@acks = []
|
80
|
+
@resent_count = 0
|
81
|
+
build_structure
|
82
|
+
blocks.each do |name, block|
|
83
|
+
case block
|
84
|
+
when Array then
|
85
|
+
block.each_index do |i|
|
86
|
+
collection = self.send(name)
|
87
|
+
case collection
|
88
|
+
when VariableBlockCollection then
|
89
|
+
b = collection.add
|
90
|
+
when FixedBlockCollection then
|
91
|
+
b = collection[i]
|
92
|
+
end
|
93
|
+
|
94
|
+
block[i].each do |v|
|
95
|
+
b.send((v[0].to_s + "=").to_sym, v[1])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
when Hash then
|
99
|
+
block.each do |v|
|
100
|
+
self.send(name).send((v[0].to_s + "=").to_sym, v[1])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def encode()
|
107
|
+
flags = 0
|
108
|
+
flags |= 0x80 if @zero_coded_flag
|
109
|
+
flags |= 0x40 if @reliable_flag
|
110
|
+
flags |= 0x20 if @resent_flag
|
111
|
+
flags |= 0x10 if @acks_flag
|
112
|
+
|
113
|
+
data = LLU8.new(flags).encode +
|
114
|
+
LLSequenceNumber.new(@sequence_number).encode +
|
115
|
+
LLU8.new(0).encode
|
116
|
+
pid = LLPacketNumber.new(self.class.packet_id).encode
|
117
|
+
data += pid
|
118
|
+
data += encode_msg
|
119
|
+
# append acks
|
120
|
+
data += ([*@acks] + [@acks.length]).pack("N#{@acks.length}C") if @acks_flag
|
121
|
+
data
|
122
|
+
end
|
123
|
+
|
124
|
+
# Subclasses have to override this method to build the message structure
|
125
|
+
# IMPORTANT: Use arrays to describe the structure, as hashes don't preserve the order
|
126
|
+
# Example:
|
127
|
+
# def build_structure()
|
128
|
+
# @blocks = [
|
129
|
+
# [:AgentData, Block.new([
|
130
|
+
# [:AgentID, :LLUUID],
|
131
|
+
# [:SessionID, :LLUUID]
|
132
|
+
# ])],
|
133
|
+
# [:SomeData, FixedBlockCollection.new(4, [
|
134
|
+
# [:Data, :LLVariable1]
|
135
|
+
# ])]
|
136
|
+
# ]
|
137
|
+
# end
|
138
|
+
def build_structure()
|
139
|
+
end
|
140
|
+
|
141
|
+
def decode_msg(data)
|
142
|
+
@blocks.each do |block|
|
143
|
+
data = block[1].decode(data)
|
144
|
+
end
|
145
|
+
data
|
146
|
+
rescue => e
|
147
|
+
puts "[ERROR] #{self.class.name}: #{self.inspect}"
|
148
|
+
raise e
|
149
|
+
end
|
150
|
+
|
151
|
+
def encode_msg()
|
152
|
+
data = ""
|
153
|
+
@blocks.each{ |block| data += block[1].encode }
|
154
|
+
data
|
155
|
+
end
|
156
|
+
|
157
|
+
def method_missing(name, *args)
|
158
|
+
block = @blocks.assoc(name)
|
159
|
+
throw "The method #{name} does not exist in #{self.class.name}" if block.nil?
|
160
|
+
block[1]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class Block
|
165
|
+
def initialize(structure)
|
166
|
+
@structure = structure
|
167
|
+
@data = []
|
168
|
+
@structure.length.times{ @data << nil }
|
169
|
+
end
|
170
|
+
|
171
|
+
def decode(data)
|
172
|
+
@data = []
|
173
|
+
@structure.each do |var|
|
174
|
+
begin
|
175
|
+
args = [data]
|
176
|
+
args << var[2] if var[1] == :LLFixed
|
177
|
+
value, data = LibSL.const_get(var[1]).decode(*args)
|
178
|
+
#rescue => e
|
179
|
+
# throw e
|
180
|
+
# throw "Type #{type.to_s} does not exist!"
|
181
|
+
end
|
182
|
+
@data << [var[0], value]
|
183
|
+
end
|
184
|
+
data
|
185
|
+
end
|
186
|
+
|
187
|
+
def encode()
|
188
|
+
throw "Data has to be an array." unless @data.is_a? Array
|
189
|
+
throw "Block is missing data." if @data.length != @structure.length
|
190
|
+
data = ""
|
191
|
+
@data.each{ |var| data += var[1].encode }
|
192
|
+
data
|
193
|
+
end
|
194
|
+
|
195
|
+
def method_missing(name, *args)
|
196
|
+
name = name.to_s
|
197
|
+
if name[-1..-1] == "=" then
|
198
|
+
# Setter
|
199
|
+
var = name[0..-2].to_sym
|
200
|
+
throw "The method #{name} does not exist in #{self.class.name}" if @structure.assoc(var).nil?
|
201
|
+
index = @structure.index{|v| v[0] == var}
|
202
|
+
@data[index] = [var, args[0]]
|
203
|
+
else
|
204
|
+
# Getter
|
205
|
+
var = name.to_sym
|
206
|
+
throw "The method #{name} does not exist in #{self.class.name}" if @structure.assoc(var).nil?
|
207
|
+
@data.assoc(var)[1]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class FixedBlockCollection
|
213
|
+
attr_reader :blocks
|
214
|
+
def initialize(size, structure)
|
215
|
+
@size = size
|
216
|
+
@blocks = []
|
217
|
+
@size.times{@blocks << Block.new(structure)}
|
218
|
+
end
|
219
|
+
|
220
|
+
def length()
|
221
|
+
@size
|
222
|
+
end
|
223
|
+
|
224
|
+
def [](index)
|
225
|
+
throw "Index out of range: #{index} is not in [0, #{length - 1}]" if index < 0 or index >= length
|
226
|
+
@blocks[index]
|
227
|
+
end
|
228
|
+
|
229
|
+
def each(&block)
|
230
|
+
@blocks.each{|block| yield block}
|
231
|
+
end
|
232
|
+
|
233
|
+
def decode(data)
|
234
|
+
@blocks.each{ |block| data = block.decode(data) }
|
235
|
+
data
|
236
|
+
end
|
237
|
+
|
238
|
+
def encode()
|
239
|
+
data = ""
|
240
|
+
@blocks.each{ |block| data += block.encode }
|
241
|
+
data
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
class VariableBlockCollection < FixedBlockCollection
|
246
|
+
def initialize(structure)
|
247
|
+
@structure = structure
|
248
|
+
@blocks = []
|
249
|
+
end
|
250
|
+
|
251
|
+
def length()
|
252
|
+
@blocks.length
|
253
|
+
end
|
254
|
+
|
255
|
+
def add()
|
256
|
+
@blocks << Block.new(@structure)
|
257
|
+
@blocks.last
|
258
|
+
end
|
259
|
+
|
260
|
+
def decode(data)
|
261
|
+
size, data = LLU8.decode(data)
|
262
|
+
size.value.times{ data = add.decode(data) }
|
263
|
+
data
|
264
|
+
end
|
265
|
+
|
266
|
+
def encode()
|
267
|
+
data = LLU8.new(length).encode
|
268
|
+
@blocks.each{ |block| data += block.encode }
|
269
|
+
data
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Include packet types generated from the specification
|
275
|
+
begin
|
276
|
+
require File.join File.dirname(__FILE__), "_packets"
|
277
|
+
rescue LoadError => e
|
278
|
+
puts "Packets not found! Please build them."
|
279
|
+
end
|