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/lib/agent.rb ADDED
@@ -0,0 +1,119 @@
1
+
2
+
3
+ module LibSL
4
+ class AgentManager
5
+ attr_accessor :client
6
+ attr_reader :position, :look_at, :region_handle, :region_name
7
+
8
+ # New AgentManager
9
+ # @param [Client] client The client
10
+ def initialize(client)
11
+ @client = client
12
+ @position = LLVector3.new(0, 0, 0)
13
+ @look_at = LLVector3.new(0, 0, 0)
14
+ @region_handle = LLU64.new(0)
15
+ @region_name = ""
16
+ init_handlers
17
+ end
18
+
19
+
20
+ # Complete a transfer to a sim (e.g. after login or teleport)
21
+ # @param [Simulator] sim The sim you have been transfered to
22
+ def move_to_sim(simulator)
23
+
24
+ # EventManager.register_handler(EventHandler.new(Proc.new do |type|
25
+ # EventMachine::add_periodic_timer(2) do
26
+ # packet = AgentUpdatePacket.new({
27
+ # :AgentData => {
28
+ # :AgentID => @client.network_manager.agent_id,
29
+ # :SessionID => @client.network_manager.session_id,
30
+ # :BodyRotation => LLQuaternion.new(1, 1, 1),
31
+ # :HeadRotation => LLQuaternion.new(1, 1, 1),
32
+ # :State => LLU8.new(0),
33
+ # :CameraCenter => @position,
34
+ # :CameraAtAxis => LLVector3.new(),
35
+ # :CameraLeftAxis => LLVector3.new(),
36
+ # :CameraUpAxis => LLVector3.new(),
37
+ # :Far => LLF32.new(64),
38
+ # :ControlFlags => LLU32.new(0),
39
+ # :Flags => LLU8.new(0)
40
+ # }
41
+ # })
42
+ # @client.network_manager.send_packet packet, true
43
+ # end
44
+ # end, :movement_complete))
45
+
46
+ packet = CompleteAgentMovementPacket.new({
47
+ :AgentData => {
48
+ :AgentID => @client.network_manager.agent_id,
49
+ :SessionID => @client.network_manager.session_id,
50
+ :CircuitCode => @client.network_manager.circuit_code
51
+ }
52
+ })
53
+ simulator.send_packet(packet)
54
+ end
55
+
56
+ # Send a chat message to the simulator
57
+ # @param [Symbol] type The chat type (:normal, :whisper or :shout)
58
+ # @param [Integer] channel (optional) should be 0 (public)
59
+ # or >0 (for scripts listening on that channel)
60
+ def chat(message, type=:normal, channel=0)
61
+ type = case type
62
+ when :whisper then 0
63
+ when :shout then 2
64
+ else 1
65
+ end
66
+ throw "Channel has to be >=0, #{channel} given!" if channel < 0
67
+ packet = ChatFromViewerPacket.new({
68
+ :AgentData => {
69
+ :AgentID => @client.network_manager.agent_id,
70
+ :SessionID => @client.network_manager.session_id
71
+ },
72
+ :ChatData => {
73
+ :Message => message,
74
+ :Type => type,
75
+ :Channel => channel
76
+ }
77
+ })
78
+ @client.network_manager.send_packet packet
79
+ end
80
+
81
+ # Send an instant message to another agent
82
+ # @param [String] message the message to send
83
+ # @param [LLUUID] to The recepients id
84
+ def im(message, to)
85
+ end
86
+
87
+ # Send Linden$ to another agent
88
+ # @param [Integer] amount The amount of L$ to send
89
+ # @param [LLUUID] to The recepient
90
+ def send_money(amount, to)
91
+ end
92
+
93
+ private
94
+
95
+ def init_handlers()
96
+ handler = EventHandler.new(Proc.new do |type, packet, sim|
97
+ @position = packet.Data.Position
98
+ @look_at = packet.Data.LookAt
99
+ @region_handle = packet.Data.RegionHandle
100
+ sim.send_packet(RegionHandshakeReplyPacket.new({
101
+ :AgentData => {
102
+ :AgentID => @client.network_manager.agent_id,
103
+ :SessionID => @client.network_manager.session_id
104
+ },
105
+ :RegionInfo => {
106
+ :Flags => LLU32.new(0)
107
+ }
108
+ }))
109
+ EventManager.fire_event :movement_complete, sim
110
+ end, :AgentMovementCompletePacket_Received)
111
+ EventManager.register_handler(handler)
112
+
113
+ handler = EventHandler.new(Proc.new do |type, packet, sim|
114
+ @region_name = packet.RegionInfo.SimName.data
115
+ end, :RegionHandshakePacket_Received)
116
+ EventManager.register_handler(handler)
117
+ end
118
+ end
119
+ end
data/lib/client.rb ADDED
@@ -0,0 +1,57 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+
5
+ %w{ types packet events agent network dsl }.each { |file|
6
+ require File.expand_path File.join(File.dirname(__FILE__), file)
7
+ }
8
+
9
+ require 'rubygems'
10
+ require 'eventmachine'
11
+
12
+ module LibSL
13
+ class Client
14
+ attr_accessor :event_manager, :network_manager, :agent_manager
15
+
16
+ def initialize()
17
+ @is_setup = false
18
+ #@event_manager = EventManager.new self
19
+ @network_manager = NetworkManager.new self
20
+ @agent_manager = AgentManager.new self
21
+ end
22
+
23
+ def login()
24
+ @network_manager.login @firstname, @lastname, @password, @start_location, @grid
25
+ end
26
+
27
+ def setup(firstname, lastname, password, start='last', grid=:agni)
28
+ @firstname = firstname
29
+ @lastname = lastname
30
+ @password = password
31
+ @start_location = start
32
+ @grid = grid
33
+ @is_setup = true
34
+ end
35
+
36
+ def setup?()
37
+ @is_setup
38
+ end
39
+
40
+ def run()
41
+ login
42
+ rescue LoginError => e
43
+ puts "[ERROR] #{e.message}"
44
+ EventMachine::stop_event_loop()
45
+ end
46
+
47
+ def stop()
48
+ EventManager::fire_event(:stopping)
49
+ @network_manager.logout
50
+ # Wait for all sims to disconnect
51
+ EventMachine::add_timer(5) {
52
+ EventManager::fire_event(:stopped)
53
+ EventMachine::stop_event_loop()
54
+ }
55
+ end
56
+ end
57
+ end
data/lib/dsl.rb ADDED
@@ -0,0 +1,54 @@
1
+
2
+ module LibSL
3
+ module DSL
4
+ # Client getter
5
+ def client
6
+ @client ||= Client.new
7
+ end
8
+ module_function :client
9
+
10
+ # Setup the client
11
+ # @param [String] firstname Avatar firstname or account username
12
+ # @param [String] lastname (optional) Account lastname. Or 'Resident' if a username
13
+ # is given
14
+ # @param [String] password Password
15
+ # @param [String] start (optional) The starting location to connect to. 'last' or 'home' or [REGION NAME]&[X]&[Y]
16
+ # @param [Symbol] grid (optional) The grid to connect to (:agni for Main grid or :aditi for beta grid)
17
+ def setup(firstname, lastname, password, start='last', grid=:agni)
18
+ client.setup firstname, lastname, password, start, grid
19
+ end
20
+
21
+ # Handle the :ready event (syntactic sugar)
22
+ # @param [Proc] cb (optional) An optional Proc that is called when the event is fired
23
+ # @yield [Symbol] Event type
24
+ # @yield [*args] Event specific arguments
25
+ def when_ready(cb=nil, &block)
26
+ handle(:ready, cb, &block)
27
+ end
28
+
29
+ def when_stopping(cb=nil, &block)
30
+ handle(:stopping, cb, &block)
31
+ end
32
+
33
+ def when_stopped(cb=nil, &block)
34
+ handle(:stopped, cb, &block)
35
+ end
36
+
37
+ # Handle the event type given a Proc cb or a block
38
+ # @param [Symbol] type The event type
39
+ def handle(type, cb=nil, &block)
40
+ cb ||= Proc.new
41
+ EventManager::register_handler EventHandler.new(Proc.new, type)
42
+ end
43
+
44
+ # Send packet
45
+ # @param [Packet] packet
46
+ def send_packet(packet, reliable=false)
47
+ client.network_manager.send_packet(packet, reliable)
48
+ end
49
+
50
+ def shutdown
51
+ client.stop
52
+ end
53
+ end
54
+ end
data/lib/events.rb ADDED
@@ -0,0 +1,59 @@
1
+
2
+ module LibSL
3
+ class EventHandler
4
+ attr_accessor :types
5
+
6
+ # New event handler
7
+ # @param [Proc] handler Called to handle an event when it is fired
8
+ # @param [Symbol/Array] type (optional) The type(s) of event to handle
9
+ def initialize(handler, type=:all)
10
+ @handler = handler
11
+ @types = [*type]
12
+ end
13
+
14
+ # Check if this handler can handle one specific event
15
+ # @param [Symbol] type Event type
16
+ def accept?(type)
17
+ if @types.include? :all
18
+ return true
19
+ elsif @types.include? type
20
+ return true
21
+ else
22
+ return false
23
+ end
24
+ end
25
+
26
+ # Handle the event
27
+ # @param [Symbol] type Event type
28
+ # @param [args] args (optional) Arguments passed to the handler
29
+ def handle(type, *args)
30
+ @handler.call(type, *args)
31
+ end
32
+ end
33
+
34
+ class EventManager
35
+ def self.handlers
36
+ @@handlers ||= []
37
+ @@handlers
38
+ end
39
+
40
+ # Register an event handler if it does not already exist
41
+ # @param [EventHandler] handler The handler
42
+ def self.register_handler(handler)
43
+ handlers << handler unless handlers.include? handler
44
+ end
45
+
46
+ # Removes an event handler
47
+ # @param [EventHandler] handler The handler
48
+ def self.remove_handler(handler)
49
+ handlers.delete handler
50
+ end
51
+
52
+ # Fires an event that will be handled by registered handlers
53
+ # @param [Symbol] type Event type
54
+ # @param [args] args (optional) Arguments passed to the handlers
55
+ def self.fire_event(type, *args)
56
+ handlers.each { |h| h.handle(type, *args) if h.accept?(type) }
57
+ end
58
+ end
59
+ end
data/lib/libsl.rb ADDED
@@ -0,0 +1,14 @@
1
+ require File.join File.dirname(__FILE__), "client"
2
+
3
+ include LibSL
4
+ include LibSL::DSL
5
+
6
+ at_exit do
7
+ unless client.setup?
8
+ throw "You need to setup the client!"
9
+ end
10
+
11
+ trap(:INT) {client.stop}
12
+ trap(:TERM) {client.stop}
13
+ EventMachine::run { client.run }
14
+ end
data/lib/network.rb ADDED
@@ -0,0 +1,305 @@
1
+ require 'xmlrpc/client'
2
+ require 'digest/md5'
3
+ require 'observer'
4
+ require 'rubygems'
5
+ require 'eventmachine'
6
+
7
+ module LibSL
8
+ class CircuitHandler < EventMachine::Connection
9
+ # Handles the connection (or circuit in SL)
10
+ # @param [Simulator] sim The simulator this connection is managed for
11
+ def initialize(sim)
12
+ @sim = sim
13
+ end
14
+
15
+ # Called when data is received from the circuit
16
+ # @param [String] data The received data
17
+ def receive_data(data)
18
+ packet = Packet::decode(data)
19
+ @sim.packet_received packet
20
+ end
21
+
22
+ def unbind
23
+ EventManager::fire_event :disconnected, @sim
24
+ end
25
+ end
26
+
27
+ class Simulator
28
+ attr_accessor :client, :packets_sent_reliably, :packets_received_reliably, :connected
29
+ attr_reader :connect_packet_ack_handler
30
+
31
+ # Manages the connection to a Simulator
32
+ # @param [Client] client The client
33
+ # @param [String] ip The simulator ip
34
+ # @param [Integer] port The simulator port
35
+ # @param [LLU32] circuit_code The circuit code
36
+ # @param [LLUUID] session_id The current session id
37
+ # @param [LLUUID] agent_id The id of the agent that logs into the sim
38
+ def initialize(client, ip, port, circuit_code, session_id, agent_id)
39
+ @client = client
40
+ @connected = false
41
+ @sim_ip = ip
42
+ @sim_port = port
43
+ @circuit_code = circuit_code
44
+ @session_id = session_id
45
+ @agent_id = agent_id
46
+ @sequence_number = 0
47
+ @connection = EventMachine::open_datagram_socket "0.0.0.0", 0, CircuitHandler, self
48
+ @packets_sent_reliably = {}
49
+ @packets_received_reliably = {}
50
+
51
+ # Start ack timers
52
+ @ack_timer = EventMachine::add_periodic_timer(1) do
53
+ send_acks
54
+ end
55
+ @resend_timer = EventMachine::add_periodic_timer 2 do
56
+ @packets_sent_reliably.each { |sequence_num, packet|
57
+ if packet.resent_count < 3
58
+ send_packet(packet, false, true)
59
+ else
60
+ @packets_sent_reliably.delete sequence_num
61
+ end
62
+ }
63
+ end
64
+ end
65
+
66
+ # Open the connection to the simulator
67
+ def connect(move_to=true)
68
+
69
+ # First PacketAck means connection is established
70
+ @connect_packet_ack_handler = EventHandler.new(Proc.new do |type, packet, sim|
71
+ EventManager::remove_handler(sim.connect_packet_ack_handler)
72
+ # Add ping handler
73
+ EventManager::register_handler(EventHandler.new(Proc.new do |type, packet, sim|
74
+ packet = CompletePingCheckPacket.new({
75
+ :PingID => {
76
+ :PingID => packet.PingID.PingID
77
+ }
78
+ })
79
+ send_packet packet
80
+ end, :StartPingCheckPacket_Received))
81
+ connected = true
82
+ sim.client.agent_manager.move_to_sim(sim) if move_to
83
+ end, :PacketAckPacket_Received)
84
+ EventManager::register_handler(@connect_packet_ack_handler)
85
+
86
+ packet = UseCircuitCodePacket.new({
87
+ :CircuitCode => {
88
+ :Code => @circuit_code,
89
+ :SessionID => @session_id,
90
+ :ID => @agent_id
91
+ }
92
+ })
93
+ send_packet packet, true
94
+ end
95
+
96
+ # Check whether we are connected to the sim
97
+ def connected?
98
+ @connected
99
+ end
100
+
101
+ def connected=(val)
102
+ @connected = val
103
+ type = val ? :connected : :disconnected
104
+ EventManager::fire_event(type, self)
105
+ end
106
+
107
+ # Disconnect from the sim
108
+ def disconnect
109
+ send_acks
110
+ #send_packet CloseCircuitPacket.new
111
+ self.connected = false
112
+ @connection.close_connection_after_writing
113
+ end
114
+
115
+ # Send a packet to the simulator
116
+ # @param [Packet] packet The packet to send to the sim
117
+ # @param [Bool] reliable Whether to send the packet reliably
118
+ # @param [Bool] resend Whether the packet has already been sent and should
119
+ # be resent
120
+ def send_packet(packet, reliable=false, resend=false)
121
+ # If we resend a packet we keep the flags as they were
122
+ packet.resent_flag = resend
123
+ packet.resent_count += 1 if resend
124
+ unless resend
125
+ @sequence_number += 1
126
+ packet.reliable_flag = reliable
127
+ packet.sequence_number = @sequence_number
128
+ end
129
+
130
+ # Add packet to the reliably sent packets map
131
+ @packets_sent_reliably[packet.sequence_number] = packet if packet.reliable_flag
132
+
133
+ append_acks packet
134
+ data = packet.encode()
135
+ @connection.send_datagram data, @sim_ip, @sim_port
136
+ end
137
+
138
+ # Called when a packet is received from the simulator
139
+ # @param [Packet] packet The received packet
140
+ def packet_received(packet)
141
+ @packets_received_reliably[packet.sequence_number.number] = packet if packet.reliable_flag
142
+ type = (packet.class.name.split("::")[-1] + "_Received").to_sym
143
+ EventManager::fire_event(:PacketReceived, packet, self)
144
+ EventManager::fire_event(type, packet, self)
145
+ end
146
+
147
+ def append_acks(packet)
148
+ return if @packets_received_reliably.length == 0
149
+ packet.acks = @packets_received_reliably.keys
150
+ packet.acks_flag = true
151
+ end
152
+
153
+ def send_acks()
154
+ return if @packets_received_reliably.length == 0
155
+ packet = PacketAckPacket.new
156
+ @packets_received_reliably.keys.each do |seq|
157
+ packet.Packets.add.ID = LLU32.new(seq)
158
+ @packets_received_reliably.delete seq
159
+ end
160
+ send_packet packet
161
+ end
162
+ end
163
+
164
+ class LoginError < StandardError; end
165
+ class SessionNotTimedoutError < LoginError; end
166
+ class RequiredUpdateError < LoginError; end
167
+ class OptionalUpdateError < LoginError; end
168
+ class LoginFailureError < LoginError; end
169
+ class AccountBannedError < LoginError; end
170
+
171
+ class NetworkManager
172
+
173
+ GRIDS = {
174
+ :agni => "https://login.agni.lindenlab.com/cgi-bin/login.cgi",
175
+ :aditi => "https://login.aditi.lindenlab.com/cgi-bin/login.cgi"
176
+ }
177
+
178
+ attr_accessor :client, :sims
179
+ attr_reader :first_name, :last_name, :circuit_code, :sims,
180
+ :session_id, :secure_session_id, :agent_id, :message_of_the_day,
181
+ :ready_handler
182
+
183
+ # Initialize the network manager
184
+ # @param [Client] client The client
185
+ def initialize(client)
186
+ @client = client
187
+ @sims = []
188
+ end
189
+
190
+ # Login to the grid
191
+ # @param [String] first Avatar firstname or account username
192
+ # @param [String] last (optional) Avatar lastname or "Resident"
193
+ # @param [String] pass The password
194
+ # @param [String] start (optional) The starting location to connect to
195
+ # @param [Symbol] grid (optional) The grid to connect to (:agni or :aditi)
196
+ def login(first, last, pass, start="last", grid=:agni)
197
+ client = XMLRPC::Client.new2(GRIDS[grid])
198
+ client.http_header_extra = {"Content-Type" => "text/xml"}
199
+
200
+ os = "Win"
201
+ os = "Mac" if RUBY_PLATFORM.downcase.include?("darwin")
202
+ os = "Lin" if RUBY_PLATFORM.downcase.include?("linux")
203
+
204
+ res = client.call("login_to_simulator", {
205
+ "first" => first,
206
+ "last" => last,
207
+ "passwd" => "$1$" + Digest::MD5.hexdigest(pass),
208
+ "start" => start,
209
+ "channel" => "libsl",
210
+ "version" => "1.0",
211
+ "platform" => os,
212
+ "mac" => 6.times.map{sprintf("%02x", rand(255))}.join(":"),
213
+ "options" => [],
214
+ "id0" => LLUUID.new.to_s,
215
+ "agree_to_tos" => "true",
216
+ "read_critical" => "true",
217
+ "viewer_digest" => "00000000-0000-0000-0000-000000000000"})
218
+
219
+ unless res["login"] == "true"
220
+ case res["reason"]
221
+ when "presence" then raise SessionNotTimedoutError, res["message"]
222
+ when "update" then raise RequiredUpdateError, res["message"]
223
+ when "optional" then raise OptionalUpdateError, res["message"]
224
+ when "key" then raise LoginFailureError, res["message"]
225
+ when "ban" then raise AccountBannedError, res["message"]
226
+ else raise LoginError, res["message"]
227
+ end
228
+ end
229
+
230
+ @circuit_code = LLU32.new(res["circuit_code"])
231
+ @session_id = LLUUID.new res["session_id"]
232
+ @secure_session_id = LLUUID.new res["secure_session_id"]
233
+ @agent_id = LLUUID.new res["agent_id"]
234
+ @first_name = res["first_name"]
235
+ @last_name = res["last_name"]
236
+ @message_of_the_day = res["message"]
237
+ setup_handlers
238
+ connect_to_simulator res["sim_ip"], res["sim_port"]
239
+ end
240
+
241
+ # Logout the client and disconnect from each connected simulator
242
+ def logout
243
+ EventManager::register_handler(EventHandler.new(Proc.new do |type, sim|
244
+ sim.client.network_manager.sims.delete sim
245
+ end, :disconnected))
246
+
247
+ EventManager::register_handler(EventHandler.new(Proc.new do |type, packet, sim|
248
+ sim.client.network_manager.sims.each { |sim|
249
+ sim.disconnect
250
+ }
251
+ EventManager::fire_event(:logout)
252
+ end, :LogoutReplyPacket_Received))
253
+ packet = LogoutRequestPacket.new({
254
+ :AgentData => {
255
+ :AgentID => @agent_id,
256
+ :SessionID => @session_id
257
+ }
258
+ })
259
+ send_packet packet, true
260
+ end
261
+
262
+ def setup_handlers()
263
+ # PacketAck handler
264
+ EventManager::register_handler(EventHandler.new(Proc.new do |type, packet, sim|
265
+ packet.Packets.each{|b| sim.packets_sent_reliably.delete(b.ID.value)}
266
+ end, :PacketAckPacket_Received))
267
+ # Handle acks on received packets
268
+ EventManager::register_handler(EventHandler.new(Proc.new do |type, packet, sim|
269
+ packet.acks.each{|ack| sim.packets_sent_reliably.delete(ack)}
270
+ end, :PacketReceived))
271
+ # Ready handler
272
+ @ready_handler = EventManager::register_handler(EventHandler.new(Proc.new do |type, sim|
273
+ EventManager::remove_handler(sim.client.network_manager.ready_handler)
274
+ EventManager::fire_event(:ready, sim.client)
275
+ end, :movement_complete))
276
+ end
277
+
278
+ # Connects to a simulator
279
+ # @param [String] ip The simulator ip
280
+ # @param [Integer] port The simulator port
281
+ def connect_to_simulator(ip, port)
282
+ @sims << Simulator.new(@client, ip, port, @circuit_code, @session_id, @agent_id)
283
+ @sims.last.connect
284
+ @active_sim = @sims.last
285
+ end
286
+
287
+ # Activates a simulator as the main simulator
288
+ # @param [Simulator] sim The simulator to activate
289
+ def activate_simulator(sim)
290
+ @sims << sim unless @sims.include? sim
291
+ sim.connect unless sim.connected?
292
+ @active_sim = sim
293
+ AgentManager.move_to_sim sim
294
+ end
295
+
296
+ # Send a packet to a specific simulator
297
+ # @param [Packet] packet The packet to send
298
+ # @param [Bool] reliable (optional) Whether to send the packet reliably
299
+ # @param [Simulator] sim (optional) The sim to send the packet to (or the active sim)
300
+ def send_packet(packet, reliable=false, sim=nil)
301
+ sim = @active_sim if sim.nil?
302
+ @active_sim.send_packet packet, reliable
303
+ end
304
+ end
305
+ end