motel 0.3 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +5 -3
- data/bin/motel-client +211 -0
- data/bin/motel-rage-client +80 -0
- data/bin/{server/main.rb → motel-server} +0 -3
- data/conf/motel-schema.xml +30 -5
- data/lib/motel.rb +3 -1
- data/lib/motel/callbacks.rb +139 -0
- data/lib/motel/location.rb +23 -4
- data/lib/motel/movement_strategies/elliptical.rb +1 -0
- data/lib/motel/movement_strategies/linear.rb +1 -0
- data/lib/motel/movement_strategy.rb +4 -3
- data/lib/motel/runner.rb +136 -38
- data/lib/motel/simrpc_adapter.rb +190 -0
- data/spec/callbacks_spec.rb +156 -0
- data/spec/dsl_spec.rb +5 -5
- data/spec/location_spec.rb +15 -10
- data/spec/runner_spec.rb +24 -10
- data/spec/simrpc_spec.rb +142 -21
- data/spec/spec_helper.rb +1 -0
- metadata +48 -13
- data/bin/clients/simple/main.rb +0 -131
- data/lib/motel/simrpc.rb +0 -131
- data/lib/motel/thread_pool.rb +0 -51
data/lib/motel/location.rb
CHANGED
@@ -7,6 +7,8 @@ require 'motel/movement_strategy'
|
|
7
7
|
|
8
8
|
module Motel
|
9
9
|
|
10
|
+
# FIXME Motel locations need concurrent access protection, add here (?)
|
11
|
+
|
10
12
|
# A Location defines an optional parent location and the x,y,z
|
11
13
|
# cartesian coordinates of the location relative to that parent.
|
12
14
|
# If parent is not specified x,y,z are ignored and this location
|
@@ -27,6 +29,9 @@ class Location
|
|
27
29
|
# array of callbacks to be invoked on movement
|
28
30
|
attr_accessor :movement_callbacks
|
29
31
|
|
32
|
+
# Array of callbacks to be invoked on proximity
|
33
|
+
attr_accessor :proximity_callbacks
|
34
|
+
|
30
35
|
# a generic association which this location can belong to
|
31
36
|
attr_accessor :entity
|
32
37
|
|
@@ -34,8 +39,13 @@ class Location
|
|
34
39
|
# default to the stopped movement strategy
|
35
40
|
@movement_strategy = MovementStrategies::Stopped.instance
|
36
41
|
@movement_callbacks = []
|
42
|
+
@proximity_callbacks = []
|
37
43
|
@children = []
|
38
44
|
|
45
|
+
@x = nil
|
46
|
+
@y = nil
|
47
|
+
@z = nil
|
48
|
+
|
39
49
|
@id = args[:id] if args.has_key? :id
|
40
50
|
@parent_id = args[:parent_id] if args.has_key? :parent_id
|
41
51
|
@x = args[:x] if args.has_key? :x
|
@@ -44,10 +54,6 @@ class Location
|
|
44
54
|
@parent = args[:parent] if args.has_key? :parent
|
45
55
|
@parent.children.push self unless @parent.nil? || @parent.children.include?(self)
|
46
56
|
@movement_strategy = args[:movement_strategy] if args.has_key? :movement_strategy
|
47
|
-
|
48
|
-
@x = 0 if @x.nil?
|
49
|
-
@y = 0 if @y.nil?
|
50
|
-
@z = 0 if @z.nil?
|
51
57
|
end
|
52
58
|
|
53
59
|
# update this location's attributes to match other's set attributes
|
@@ -60,6 +66,11 @@ class Location
|
|
60
66
|
@parent_id = location.parent_id unless location.parent_id.nil?
|
61
67
|
end
|
62
68
|
|
69
|
+
# return this locations coordinates in an array
|
70
|
+
def coordinates
|
71
|
+
[@x, @y, @z]
|
72
|
+
end
|
73
|
+
|
63
74
|
# return this location's root location
|
64
75
|
def root
|
65
76
|
return self if parent.nil?
|
@@ -95,6 +106,14 @@ class Location
|
|
95
106
|
return parent.total_z + z
|
96
107
|
end
|
97
108
|
|
109
|
+
# return the distance between this location and specified other
|
110
|
+
def -(location)
|
111
|
+
dx = x - location.x
|
112
|
+
dy = y - location.y
|
113
|
+
dz = z - location.z
|
114
|
+
Math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
|
115
|
+
end
|
116
|
+
|
98
117
|
end
|
99
118
|
|
100
119
|
end # module Motel
|
@@ -46,6 +46,7 @@ class Elliptical < MovementStrategy
|
|
46
46
|
@direction_minor_x = 0 if @direction_minor_x.nil?
|
47
47
|
@direction_minor_y = 1 if @direction_minor_y.nil?
|
48
48
|
@direction_minor_z = 0 if @direction_minor_z.nil?
|
49
|
+
super(args)
|
49
50
|
|
50
51
|
@direction_major_x, @direction_major_y, @direction_major_z =
|
51
52
|
normalize(@direction_major_x, @direction_major_y, @direction_major_z)
|
@@ -23,6 +23,7 @@ class Linear < MovementStrategy
|
|
23
23
|
@direction_vector_y = args[:direction_vector_y] if args.has_key? :direction_vector_y
|
24
24
|
@direction_vector_z = args[:direction_vector_z] if args.has_key? :direction_vector_z
|
25
25
|
@speed = args[:speed] if args.has_key? :speed
|
26
|
+
super(args)
|
26
27
|
|
27
28
|
# normalize direction vector
|
28
29
|
@direction_vector_x, @direction_vector_y, @direction_vector_z =
|
@@ -11,13 +11,14 @@ module Motel
|
|
11
11
|
# MovementStrategy subclasses define the rules and params which
|
12
12
|
# a location changes its position.
|
13
13
|
class MovementStrategy
|
14
|
+
attr_accessor :id, :type
|
15
|
+
|
14
16
|
attr_accessor :step_delay
|
15
17
|
|
16
18
|
def initialize(args = {})
|
17
|
-
@step_delay =
|
18
|
-
@movement_callbacks = []
|
19
|
+
@step_delay = 1
|
19
20
|
|
20
|
-
@step_delay = args[:step_delay] if args.has_key? :step_delay
|
21
|
+
@step_delay = args[:step_delay] if args.has_key?(:step_delay) && !args[:step_delay].nil?
|
21
22
|
end
|
22
23
|
|
23
24
|
# default movement strategy is to do nothing
|
data/lib/motel/runner.rb
CHANGED
@@ -6,7 +6,6 @@
|
|
6
6
|
# Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
|
7
7
|
|
8
8
|
require 'singleton'
|
9
|
-
require 'motel/thread_pool'
|
10
9
|
|
11
10
|
module Motel
|
12
11
|
|
@@ -17,44 +16,79 @@ module Motel
|
|
17
16
|
class Runner
|
18
17
|
include Singleton
|
19
18
|
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
# for testing purposes
|
24
|
-
attr_reader :thread_pool, :terminate, :run_thread
|
19
|
+
# For testing purposes
|
20
|
+
attr_reader :terminate, :run_thread
|
25
21
|
|
26
22
|
def initialize(args = {})
|
23
|
+
# is set to true upon runner termination
|
27
24
|
@terminate = false
|
28
|
-
|
29
|
-
|
25
|
+
|
26
|
+
# TODO use ruby tree to store locations w/ heirarchy
|
27
|
+
# management queues, locations to be scheduled and locations to be run
|
28
|
+
@schedule_queue = []
|
29
|
+
@run_queue = []
|
30
|
+
|
31
|
+
|
32
|
+
# locks protecting queues from concurrent access and conditions indicating queues have items
|
33
|
+
@schedule_lock = Mutex.new
|
34
|
+
@run_lock = Mutex.new
|
35
|
+
@schedule_cv = ConditionVariable.new
|
36
|
+
@run_cv = ConditionVariable.new
|
30
37
|
|
31
38
|
@run_thread = nil
|
32
|
-
@run_delay = 2 # FIXME scale delay (only needed if locations is empty or has very few simple elements)
|
33
39
|
end
|
34
40
|
|
41
|
+
# Return complete list of locations being managed/tracked
|
42
|
+
def locations
|
43
|
+
# need conccurrent protection here, or copy the elements into another array and return that?
|
44
|
+
@schedule_queue + @run_queue
|
45
|
+
end
|
46
|
+
|
47
|
+
|
35
48
|
# Empty the list of locations being managed/tracked
|
36
49
|
def clear
|
37
|
-
@
|
38
|
-
@
|
39
|
-
|
50
|
+
@schedule_lock.synchronize {
|
51
|
+
@run_lock.synchronize {
|
52
|
+
@schedule_queue.clear
|
53
|
+
@run_queue.clear
|
54
|
+
}}
|
40
55
|
end
|
41
56
|
|
42
|
-
#
|
57
|
+
# Add location to runner to be managed, after this is called, the location's
|
43
58
|
# movement strategy's move method will be invoked periodically
|
44
59
|
def run(location)
|
45
|
-
@
|
46
|
-
|
47
|
-
|
60
|
+
@schedule_lock.synchronize {
|
61
|
+
# autogenerate location.id if nil
|
62
|
+
if location.id.nil?
|
63
|
+
@run_lock.synchronize {
|
64
|
+
i = 1
|
65
|
+
until false
|
66
|
+
break if @schedule_queue.find { |l| l.id == i }.nil? && @run_queue.find { |l| l.id == i }.nil?
|
67
|
+
i += 1
|
68
|
+
end
|
69
|
+
location.id = i
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
Logger.debug "adding location #{location.id} to runner queue"
|
74
|
+
@schedule_queue.push location
|
75
|
+
@schedule_cv.signal
|
48
76
|
}
|
77
|
+
return location
|
78
|
+
end
|
79
|
+
|
80
|
+
# Wrapper around run, except return 'self' when done
|
81
|
+
def <<(location)
|
82
|
+
run(location)
|
83
|
+
return self
|
49
84
|
end
|
50
85
|
|
51
86
|
# Start moving the locations. If :async => true is passed in, this will immediately
|
52
87
|
# return, else this will block until stop is called.
|
53
88
|
def start(args = {})
|
54
|
-
num_threads = 5
|
55
|
-
num_threads = args[:num_threads] if args.has_key? :num_threads
|
89
|
+
@num_threads = 5
|
90
|
+
@num_threads = args[:num_threads] if args.has_key? :num_threads
|
56
91
|
@terminate = false
|
57
|
-
@thread_pool = ThreadPool.new(num_threads)
|
58
92
|
|
59
93
|
if args.has_key?(:async) && args[:async]
|
60
94
|
Logger.debug "starting async motel runner"
|
@@ -70,7 +104,12 @@ class Runner
|
|
70
104
|
def stop
|
71
105
|
Logger.debug "stopping motel runner"
|
72
106
|
@terminate = true
|
73
|
-
@
|
107
|
+
@schedule_lock.synchronize {
|
108
|
+
@schedule_cv.signal
|
109
|
+
}
|
110
|
+
@run_lock.synchronize {
|
111
|
+
@run_cv.signal
|
112
|
+
}
|
74
113
|
join
|
75
114
|
Logger.debug "motel runner stopped"
|
76
115
|
end
|
@@ -85,35 +124,94 @@ class Runner
|
|
85
124
|
|
86
125
|
# Internal helper method performing main runner operations
|
87
126
|
def run_cycle
|
88
|
-
#
|
89
|
-
|
127
|
+
# location ids which are currently being run -> their run timestamp
|
128
|
+
location_timestamps = {}
|
129
|
+
|
130
|
+
# scheduler thread, to add locations to the run queue
|
131
|
+
scheduler = Thread.new {
|
132
|
+
until @terminate
|
133
|
+
tqueue = []
|
134
|
+
locs_to_run = []
|
135
|
+
empty_queue = true
|
136
|
+
min_delay = nil
|
137
|
+
|
138
|
+
@schedule_lock.synchronize {
|
139
|
+
# if no locations are to be scheduled, block until there are
|
140
|
+
@schedule_cv.wait(@schedule_lock) if @schedule_queue.empty?
|
141
|
+
@schedule_queue.each { |l| tqueue << l }
|
142
|
+
}
|
143
|
+
|
144
|
+
# run through each location to be scheduled to run, see which ones are due
|
145
|
+
tqueue.each { |loc|
|
146
|
+
location_timestamps[loc.id] = Time.now unless location_timestamps.has_key?(loc.id)
|
147
|
+
locs_to_run << loc if loc.movement_strategy.step_delay < Time.now - location_timestamps[loc.id]
|
148
|
+
}
|
149
|
+
|
150
|
+
# add those the the run queue, signal runner to start operations if blocking
|
151
|
+
@schedule_lock.synchronize {
|
152
|
+
@run_lock.synchronize{
|
153
|
+
locs_to_run.each { |loc| @run_queue << loc ; @schedule_queue.delete(loc) }
|
154
|
+
empty_queue = (@schedule_queue.size == 0)
|
155
|
+
@run_cv.signal unless locs_to_run.empty?
|
156
|
+
}
|
157
|
+
}
|
90
158
|
|
159
|
+
# if there are locations still to be scheduled, sleep for the smallest step_delay
|
160
|
+
unless empty_queue
|
161
|
+
# we use locations instead of @schedule_queue here since a when the scheduler is
|
162
|
+
# sleeping a loc w/ a smaller step_delay may complete running and be added back to the scheduler
|
163
|
+
min_delay= locations.sort { |a,b|
|
164
|
+
a.movement_strategy.step_delay <=> b.movement_strategy.step_delay
|
165
|
+
}.first.movement_strategy.step_delay
|
166
|
+
sleep min_delay
|
167
|
+
end
|
168
|
+
end
|
169
|
+
}
|
170
|
+
|
171
|
+
# until we are told to stop
|
91
172
|
until @terminate
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
173
|
+
locs_to_schedule = []
|
174
|
+
tqueue = []
|
175
|
+
|
176
|
+
@run_lock.synchronize{
|
177
|
+
# wait until we have locations to run
|
178
|
+
@run_cv.wait(@run_lock) if @run_queue.empty?
|
179
|
+
@run_queue.each { |l| tqueue << l }
|
96
180
|
}
|
97
181
|
|
98
|
-
|
99
|
-
|
100
|
-
|
182
|
+
# run through each location to be run, perform actual movement, invoke callbacks
|
183
|
+
tqueue.each { |loc|
|
184
|
+
Logger.debug "runner moving location #{loc.id} at #{loc.coordinates.join(",")} via #{loc.movement_strategy.class.to_s}"
|
101
185
|
|
102
|
-
|
103
|
-
|
186
|
+
# store the old location coordinates for comparison after the movement
|
187
|
+
old_coords = [loc.x, loc.y, loc.z]
|
104
188
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
}
|
189
|
+
elapsed = Time.now - location_timestamps[loc.id]
|
190
|
+
loc.movement_strategy.move loc, elapsed
|
191
|
+
location_timestamps[loc.id] = Time.now
|
109
192
|
|
110
|
-
|
111
|
-
|
193
|
+
# TODO invoke these async so as not to hold up the runner
|
194
|
+
# make sure to keep these in sync w/ those invoked in the simrpc adapter "update_location" handler
|
195
|
+
loc.movement_callbacks.each { |callback|
|
196
|
+
callback.invoke(loc, *old_coords)
|
112
197
|
}
|
198
|
+
loc.proximity_callbacks.each { |callback|
|
199
|
+
callback.invoke(loc)
|
200
|
+
}
|
201
|
+
|
202
|
+
locs_to_schedule << loc
|
113
203
|
}
|
114
204
|
|
115
|
-
|
205
|
+
# add locations back to schedule queue
|
206
|
+
@run_lock.synchronize{
|
207
|
+
@schedule_lock.synchronize{
|
208
|
+
locs_to_schedule.each { |loc| @schedule_queue << loc ; @run_queue.delete(loc) }
|
209
|
+
@schedule_cv.signal unless locs_to_schedule.empty?
|
210
|
+
}
|
211
|
+
}
|
116
212
|
end
|
213
|
+
|
214
|
+
scheduler.join
|
117
215
|
end
|
118
216
|
|
119
217
|
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# Motel simrpc adapter
|
2
|
+
#
|
3
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
|
5
|
+
|
6
|
+
require 'simrpc'
|
7
|
+
|
8
|
+
module Motel
|
9
|
+
|
10
|
+
# Motel::Server defines a server endpoint which manages locations
|
11
|
+
# and responds to simrpc requests
|
12
|
+
class Server
|
13
|
+
def initialize(args = {})
|
14
|
+
simrpc_args = args
|
15
|
+
simrpc_args[:id] = "location-server"
|
16
|
+
|
17
|
+
# create a simprc node
|
18
|
+
@simrpc_node = Simrpc::Node.new(simrpc_args)
|
19
|
+
|
20
|
+
# register handlers for the various motel simrpc methods
|
21
|
+
@simrpc_node.handle_method("get_location") { |location_id|
|
22
|
+
Logger.info "received get location #{location_id} request"
|
23
|
+
loc = nil
|
24
|
+
begin
|
25
|
+
loc = Runner.instance.locations.find { |loc| loc.id == location_id }
|
26
|
+
# FIXME traverse all of loc's descendants, and if remote location
|
27
|
+
# server is specified, send request to get child location, swapping
|
28
|
+
# it in for the one thats there
|
29
|
+
rescue Exception => e
|
30
|
+
Logger.warn "get location #{location_id} failed w/ exception #{e}"
|
31
|
+
end
|
32
|
+
Logger.info "get location #{location_id} request returning #{loc}"
|
33
|
+
loc
|
34
|
+
}
|
35
|
+
|
36
|
+
@simrpc_node.handle_method("create_location") { |location|
|
37
|
+
Logger.info "received create location request"
|
38
|
+
location = Location.new if location.nil?
|
39
|
+
ret = location
|
40
|
+
begin
|
41
|
+
location.x = 0 if location.x.nil?
|
42
|
+
location.y = 0 if location.y.nil?
|
43
|
+
location.z = 0 if location.z.nil?
|
44
|
+
|
45
|
+
# TODO decendants support w/ remote option (create additional locations on other servers)
|
46
|
+
Runner.instance.run location
|
47
|
+
|
48
|
+
rescue Exception => e
|
49
|
+
Logger.warn "create location failed w/ exception #{e}"
|
50
|
+
ret = nil
|
51
|
+
end
|
52
|
+
Logger.info "create location request created and returning #{ret.id}"
|
53
|
+
ret
|
54
|
+
}
|
55
|
+
|
56
|
+
@simrpc_node.handle_method("update_location") { |location|
|
57
|
+
Logger.info "received update location #{location.id} request"
|
58
|
+
success = true
|
59
|
+
if location.nil?
|
60
|
+
success = false
|
61
|
+
else
|
62
|
+
rloc = Runner.instance.locations.find { |loc| loc.id == location.id }
|
63
|
+
begin
|
64
|
+
# store the old location coordinates for comparison after the movement
|
65
|
+
old_coords = [location.x, location.y, location.z]
|
66
|
+
|
67
|
+
# FIXME XXX big problem/bug here, client must always specify location.movement_strategy, else location constructor will set it to stopped
|
68
|
+
# FIXME this should halt location movement, update location, then start it again
|
69
|
+
Logger.info "updating location #{location.id} with #{location}/#{location.movement_strategy}"
|
70
|
+
rloc.update(location)
|
71
|
+
|
72
|
+
# FIXME trigger location movement & proximity callbacks (make sure to keep these in sync w/ those invoked the the runner)
|
73
|
+
# right now we can't do this because a single simrpc node can't handle multiple sent message response, see FIXME XXX in lib/simrpc/node.rb
|
74
|
+
#rloc.movement_callbacks.each { |callback|
|
75
|
+
# callback.invoke(rloc, *old_coords)
|
76
|
+
#}
|
77
|
+
#rloc.proximity_callbacks.each { |callback|
|
78
|
+
# callback.invoke(rloc)
|
79
|
+
#}
|
80
|
+
|
81
|
+
rescue Exception => e
|
82
|
+
Logger.warn "update location #{location.id} failed w/ exception #{e}"
|
83
|
+
success = false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
Logger.info "update location #{location.id} returning #{success}"
|
87
|
+
success
|
88
|
+
}
|
89
|
+
|
90
|
+
@simrpc_node.handle_method("subscribe_to_location_movement") { |client_id, location_id, min_distance, min_x, min_y, min_z|
|
91
|
+
Logger.info "subscribe client #{client_id} to location #{location_id} movement request received"
|
92
|
+
loc = Runner.instance.locations.find { |loc| loc.id == location_id }
|
93
|
+
success = true
|
94
|
+
if loc.nil?
|
95
|
+
success = false
|
96
|
+
else
|
97
|
+
callback = Callbacks::Movement.new :min_distance => min_distance, :min_x => min_x, :min_y => min_y, :min_z => min_z,
|
98
|
+
:handler => lambda { |location, d, dx, dy, dz|
|
99
|
+
# send location to client
|
100
|
+
@simrpc_node.send_method("location_moved", client_id, location, d, dx, dy, dz)
|
101
|
+
}
|
102
|
+
loc.movement_callbacks.push callback
|
103
|
+
end
|
104
|
+
Logger.info "subscribe client #{client_id} to location #{location_id} movement returning #{success}"
|
105
|
+
success
|
106
|
+
}
|
107
|
+
|
108
|
+
@simrpc_node.handle_method("subscribe_to_locations_proximity") { |client_id, location1_id, location2_id, event, max_distance, max_x, max_y, max_z|
|
109
|
+
Logger.info "subscribe client #{client_id} to location #{location1_id}/#{location2_id} proximity request received"
|
110
|
+
loc1 = Runner.instance.locations.find { |loc| loc.id == location1_id }
|
111
|
+
loc2 = Runner.instance.locations.find { |loc| loc.id == location2_id }
|
112
|
+
success = true
|
113
|
+
if loc1.nil? || loc2.nil?
|
114
|
+
success = false
|
115
|
+
else
|
116
|
+
callback = Callbacks::Proximity.new :to_location => loc2, :event => event, :max_distance => max_distance, :max_x => max_x, :max_y => max_y, :max_z => max_z,
|
117
|
+
:handler => lambda { |location1, location2|
|
118
|
+
# send locations to client
|
119
|
+
@simrpc_node.send_method("locations_proximity", client_id, location1, location2)
|
120
|
+
}
|
121
|
+
loc1.proximity_callbacks.push callback
|
122
|
+
end
|
123
|
+
Logger.info "subscribe client #{client_id} to location #{location1_id}/#{location2_id} proximity request returning #{success}"
|
124
|
+
success
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
def terminate
|
129
|
+
@simrpc_node.terminate
|
130
|
+
end
|
131
|
+
|
132
|
+
def join
|
133
|
+
@simrpc_node.join
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Client defines a client endpoint that performs
|
138
|
+
# a request against a Motel Server
|
139
|
+
class Client
|
140
|
+
# Set to a callable object that will take a location and distance moved
|
141
|
+
attr_writer :on_location_moved
|
142
|
+
|
143
|
+
# Set to a callable object that will take two locations
|
144
|
+
attr_writer :on_locations_proximity
|
145
|
+
|
146
|
+
# Initialize the client with various args, all of which are passed onto Simrpc::Node constructor
|
147
|
+
def initialize(args = {})
|
148
|
+
simrpc_args = args
|
149
|
+
simrpc_args[:destination] = "location-server"
|
150
|
+
|
151
|
+
@simrpc_node = Simrpc::Node.new(simrpc_args)
|
152
|
+
end
|
153
|
+
|
154
|
+
def join
|
155
|
+
@simrpc_node.join
|
156
|
+
end
|
157
|
+
|
158
|
+
def request(target, *args)
|
159
|
+
method_missing(target, *args)
|
160
|
+
end
|
161
|
+
|
162
|
+
# pass simrpc method requests right onto the simrpc node
|
163
|
+
def method_missing(method_id, *args)
|
164
|
+
# special case for subsscribe_to_location,
|
165
|
+
if method_id == :subscribe_to_location_movement
|
166
|
+
# add simrpc node id onto args list
|
167
|
+
args.unshift @simrpc_node.id
|
168
|
+
|
169
|
+
# handle location updates from the server, & issue subscribe request
|
170
|
+
@simrpc_node.handle_method("location_moved") { |location, d, dx, dy, dz|
|
171
|
+
Logger.info "location #{location.id} moved"
|
172
|
+
@on_location_moved.call(location, d, dx, dy, dz) unless @on_location_moved.nil?
|
173
|
+
}
|
174
|
+
|
175
|
+
elsif method_id == :subscribe_to_locations_proximity
|
176
|
+
# add simrpc node id onto args list
|
177
|
+
args.unshift @simrpc_node.id
|
178
|
+
|
179
|
+
# handle location proximity events from the server, & issue subscribe request
|
180
|
+
@simrpc_node.handle_method("locations_proximity") { |location1, location2|
|
181
|
+
Logger.info "location #{location1.id}/#{location2.id} proximity"
|
182
|
+
@on_locations_proximity.call(location1, location2) unless @on_locations_proximity.nil?
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
@simrpc_node.method_missing(method_id, *args)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|