libsl 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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