motel 0.3 → 0.3.1

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/Rakefile CHANGED
@@ -9,7 +9,7 @@ require 'rake/gempackagetask'
9
9
 
10
10
 
11
11
  GEM_NAME="motel"
12
- PKG_VERSION='0.3'
12
+ PKG_VERSION='0.3.1'
13
13
  SIMRPC_SPEC='conf/motel-schema.xml'
14
14
 
15
15
  desc "Run all specs"
@@ -23,20 +23,22 @@ Rake::RDocTask.new do |rd|
23
23
  rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
24
24
  end
25
25
 
26
- PKG_FILES = FileList['bin/**/*', 'conf/motel-schema.xml', 'lib/**/*.rb',
26
+ PKG_FILES = FileList['conf/motel-schema.xml', 'lib/**/*.rb',
27
27
  'COPYING', 'LICENSE', 'Rakefile', 'README.rdoc', 'spec/**/*.rb' ]
28
28
 
29
29
  SPEC = Gem::Specification.new do |s|
30
30
  s.name = GEM_NAME
31
31
  s.version = PKG_VERSION
32
32
  s.files = PKG_FILES
33
+ s.executables << 'motel-server' << 'motel-client' << 'motel-rage-client'
33
34
 
34
35
  s.required_ruby_version = '>= 1.8.1'
35
36
  s.required_rubygems_version = Gem::Requirement.new(">= 1.3.3")
37
+ s.add_development_dependency('rspec', '~> 1.3.0')
36
38
 
37
39
  s.author = "Mohammed Morsi"
38
40
  s.email = "movitto@yahoo.com"
39
- s.date = %q{2010-03-14}
41
+ s.date = %q{2010-09-05}
40
42
  s.description = %q{Motel is a library to track and move the locations of objects in a 3D environment.}
41
43
  s.summary = %q{Motel is a library to track and move the locations of objects in a 3D environment.}
42
44
  s.homepage = %q{http://morsi.org/projects/motel}
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/ruby
2
+ # A simple motel client executable
3
+ # Executable to use the motel library to perform operations on
4
+ # a remote location server, simply printing out results
5
+ #
6
+ # Flags: (see below)
7
+ #
8
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
9
+ # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
10
+
11
+ require 'rubygems'
12
+ require 'optparse'
13
+ require 'motel'
14
+
15
+ include Motel
16
+ include Motel::MovementStrategies
17
+
18
+ ######################
19
+
20
+ def main()
21
+ # command line parameters
22
+ schema_file = nil
23
+ location = {:parent_id => nil,
24
+ :x => nil,
25
+ :y => nil,
26
+ :z => nil}
27
+ other_location_id = nil
28
+ movement_strategy_type = nil
29
+ movement_strategy = { :step_delay => nil,
30
+ :speed => nil,
31
+ :direction_vector_x => nil,
32
+ :direction_vector_y => nil,
33
+ :direction_vector_z => nil,
34
+ :relative_to => nil,
35
+ :eccentricity => nil,
36
+ :semi_latus_rectum => nil,
37
+ :direction_major_x => nil,
38
+ :direction_major_y => nil,
39
+ :direction_major_z => nil,
40
+ :direction_minor_x => nil,
41
+ :direction_minor_y => nil,
42
+ :direction_minor_z => nil}
43
+ request_target = nil
44
+
45
+ # setup cmd line options
46
+ opts = OptionParser.new do |opts|
47
+ opts.banner = "Usage: main.rb [command] [options]"
48
+
49
+ opts.on("-h", "--help", "Print this help message") do
50
+ puts opts
51
+ exit
52
+ end
53
+
54
+ opts.on("-s", "--schema [path]", "Motel Schema File") do |path|
55
+ schema_file = path
56
+ end
57
+
58
+ opts.separator ""
59
+ opts.separator "Commands:"
60
+ opts.on("-g", "--get", "get location specified by id")do
61
+ request_target = :get_location
62
+ end
63
+ opts.on("-c", "--create", "create location w/ id")do
64
+ request_target = :create_location
65
+ end
66
+ opts.on("-u", "--update", "update location specified by id w/ specified options")do
67
+ request_target = :update_location
68
+ end
69
+ opts.on("-m", "--subscribe-to-movement", "subscribe to movement updates to location specified by id")do
70
+ request_target = :subscribe_to_location_movement
71
+ end
72
+ opts.on("-r", "--subscribe-to-proximity", "subscribe to locations proximity events")do
73
+ request_target = :subscribe_to_locations_proximity
74
+ end
75
+
76
+ opts.separator ""
77
+ opts.separator "Location Options:"
78
+ opts.on("-i", "--id [location_id]", "Target location id") do |id|
79
+ location[:id] = id
80
+ end
81
+ opts.on("-o", "--other-id [location_id]", "Second location id for actions that require it") do |id|
82
+ other_location_id = id
83
+ end
84
+ opts.on("-p", "--parent-id [location_id]", "Target parent location id") do |id|
85
+ location[:parent_id] = id
86
+ end
87
+ opts.on("-x", "--xcoordinate [coordinate]", "Target location x coordinate") do |x|
88
+ location[:x] = x
89
+ end
90
+ opts.on("-y", "--ycoordinate [coordinate]", "Target location y coordinate") do |y|
91
+ location[:y] = y
92
+ end
93
+ opts.on("-z", "--zcoordinate [coordinate]", "Target location z coordinate") do |z|
94
+ location[:z] = z
95
+ end
96
+
97
+ opts.separator ""
98
+ opts.separator "Movement Strategy Options:"
99
+ opts.on("--movement-strategy-type [type]", "Movement strategy type") do |type|
100
+ movement_strategy_type = type
101
+ end
102
+ opts.on("--step-delay [delay]", "Movement strategy step delay") do |delay|
103
+ movement_strategy[:step_delay] = delay.to_f
104
+ end
105
+ opts.on("--speed [speed]", "Movement strategy speed") do |speed|
106
+ movement_strategy[:speed] = speed.to_f
107
+ end
108
+ opts.on("--direction-vector-x [x]", "Linear movement strategy direction vector x coordinate") do |x|
109
+ movement_strategy[:direction_vector_x] = x.to_f
110
+ end
111
+ opts.on("--direction-vector-y [y]", "Linear movement strategy direction vector y coordinate") do |y|
112
+ movement_strategy[:direction_vector_y] = y.to_f
113
+ end
114
+ opts.on("--direction-vector-z [z]", "Linear movement strategy direction vector z coordinate") do |z|
115
+ movement_strategy[:direction_vector_z] = z.to_f
116
+ end
117
+ opts.on("--relative-to [relative]", "Elliptical movement strategy relative to") do |relative|
118
+ movement_strategy[:relative_to] = relative
119
+ end
120
+ opts.on("--eccentricity [e]", "Elliptical movement strategy eccentricity") do |e|
121
+ movement_strategy[:eccentricity] = e
122
+ end
123
+ opts.on("--semi-latus-rectum [l]", "Elliptical movement strategy semi-latus-rectum") do |l|
124
+ movement_strategy[:semi_latus_rectum] = l
125
+ end
126
+ opts.on("--direction-major-x [x]", "Elliptical movement strategy major direction vector x coordinate") do |x|
127
+ movement_strategy[:direction_major_x] = x.to_f
128
+ end
129
+ opts.on("--direction-major-y [y]", "Elliptical movement strategy major direction vector y coordinate") do |y|
130
+ movement_strategy[:direction_major_y] = y.to_f
131
+ end
132
+ opts.on("--direction-major-z [z]", "Elliptical movement strategy major direction vector z coordinate") do |z|
133
+ movement_strategy[:direction_major_z] = z.to_f
134
+ end
135
+ opts.on("--direction-minor-x [x]", "Elliptical movement strategy minor direction vector x coordinate") do |x|
136
+ movement_strategy[:direction_minor_x] = x.to_f
137
+ end
138
+ opts.on("--direction-minor-y [y]", "Elliptical movement strategy minor direction vector y coordinate") do |y|
139
+ movement_strategy[:direction_minor_y] = y.to_f
140
+ end
141
+ opts.on("--direction-minor-z [z]", "Elliptical movement strategy minor direction vector z coordinate") do |z|
142
+ movement_strategy[:direction_minor_z] = z.to_f
143
+ end
144
+
145
+ end
146
+
147
+ # parse cmd line
148
+ begin
149
+ opts.parse!(ARGV)
150
+ rescue OptionParser::InvalidOption => e
151
+ puts opts
152
+ puts e.to_s
153
+ exit
154
+ end
155
+
156
+ if request_target.nil? || location[:id].nil? || schema_file.nil? #||
157
+ #request_target == :update && location[:x].nil? && location[:y].nil? && location[:z].nil? && location[:parent_id].nil?
158
+ puts opts
159
+ puts "must specify schema, a command to perform, a location id, and other required options"
160
+ exit
161
+ end
162
+
163
+ lid = location[:id]
164
+ location = Motel::Location.new :id => location[:id],
165
+ :parent_id => location[:parent_id],
166
+ :x => location[:x],
167
+ :y => location[:y],
168
+ :z => location[:z]
169
+
170
+ unless movement_strategy_type.nil?
171
+ movement_strategy = movement_strategy_type.camelize.constantize.new movement_strategy
172
+ location.movement_strategy = movement_strategy
173
+ end
174
+
175
+ args = []
176
+ case(request_target)
177
+ when :get_location
178
+ args.push location.id
179
+ when :create_location
180
+ args.push location
181
+ when :update_location
182
+ args.push location
183
+ when :subscribe_to_location_movement
184
+ args.push location.id
185
+ when :subscribe_to_locations_proximity
186
+ args.push location.id, other_location_id
187
+ end
188
+
189
+ # FIXME need configurable amqp broker ip/port
190
+ client = Motel::Client.new :schema_file => schema_file
191
+ result = client.request request_target, *args
192
+
193
+ if request_target == :subscribe_to_location_movement
194
+ client.on_location_moved = lambda { |loc|
195
+ puts "location moved:"
196
+ puts "#{loc}"
197
+ }
198
+ client.join
199
+
200
+ elsif request_target == :subscribe_to_locations_proximity
201
+ client.on_locations_proximity = lambda { |loc1, loc2|
202
+ puts "locations proximity:"
203
+ puts "#{loc1}/#{loc2}"
204
+ }
205
+ client.join
206
+ end
207
+
208
+ puts "server returned #{result}"
209
+ end
210
+
211
+ main()
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/ruby
2
+ # A motel client using the Ruby Advanced Gaming Engine library to display location
3
+ #
4
+ # Flags: (see below)
5
+ #
6
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
7
+ # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
8
+
9
+ require 'rubygems'
10
+ require 'optparse'
11
+ require 'rage'
12
+ require 'motel'
13
+
14
+ include Motel
15
+ include Motel::MovementStrategies
16
+
17
+
18
+ ######################
19
+
20
+ def main()
21
+ # command line parameters
22
+ schema_file = nil
23
+ mesh_file = nil
24
+ location_id = nil
25
+
26
+ # setup cmd line options
27
+ opts = OptionParser.new do |opts|
28
+ opts.banner = "Usage: main.rb [command] [options]"
29
+
30
+ opts.on("-h", "--help", "Print this help message") do
31
+ puts opts
32
+ exit
33
+ end
34
+
35
+ opts.on("-s", "--schema [path]", "Motel Schema File") do |path|
36
+ schema_file = path
37
+ end
38
+
39
+ opts.on("-i", "--id [id]", "ID of location to display") do |id|
40
+ location_id = id
41
+ end
42
+
43
+ opts.on("-m", "--mesh [uri]", "Mesh to display at location") do |uri|
44
+ mesh_file = uri
45
+ end
46
+ end
47
+
48
+ # parse cmd line
49
+ begin
50
+ opts.parse!(ARGV)
51
+ rescue OptionParser::InvalidOption => e
52
+ puts opts
53
+ puts e.to_s
54
+ exit
55
+ end
56
+
57
+ if schema_file.nil? || mesh_file.nil? || location_id.nil?
58
+ puts "must specify motel schema, location id, and mesh to display"
59
+ puts opts
60
+ exit
61
+ end
62
+
63
+ mesh = resource :type => :mesh, :uri => mesh_file
64
+
65
+ client = Motel::Client.new :schema_file => schema_file
66
+ result = client.request :subscribe_to_location_movement, location_id
67
+ client.on_location_moved = lambda { |loc, d, dx, dy, dz|
68
+ mesh.show_at :x => loc.x, :y => loc.y, :z => loc.z
69
+ }
70
+
71
+ window(:width => 512, :height => 512) { |win|
72
+ win.create_viewport
73
+ }.show
74
+
75
+ game.run
76
+
77
+ #client.join
78
+ end
79
+
80
+ main()
@@ -10,9 +10,6 @@
10
10
  # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
11
11
  # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
12
12
 
13
- CURRENT_DIR=File.dirname(__FILE__)
14
- $: << File.expand_path(CURRENT_DIR + "/../../lib")
15
-
16
13
  require 'rubygems'
17
14
  require 'optparse'
18
15
  require 'motel'
@@ -38,7 +38,7 @@
38
38
  <member name="parent_id" type="int" desc="location id of parent, null if this is a root location" ignore_null="true" />
39
39
  <member name="parent" type="obj" associated="Location" desc="parent location"/>
40
40
  <member name="children" type="array" associated="Location" desc="children locations"/>
41
- <member name="movement_strategy" type="obj" desc="associated movement strategy"/>
41
+ <member name="movement_strategy" type="obj" default="" desc="associated movement strategy"/>
42
42
  <member name="entity_id" type="int" desc="id of entity represented by this location" />
43
43
  <member name="entity_type" type="str" desc="type of entity represented by this location" />
44
44
  </class>
@@ -48,9 +48,9 @@
48
48
  <return_value name="location" type="obj" associated="Location" desc="Location corresponding to id or nil"/>
49
49
  </method>
50
50
 
51
- <method name="create_location" desc="create new location w/ specified id">
52
- <param name="location_id" type="int" desc="Location identifier"/>
53
- <return_value name="success" type="bool" desc="bool success"/>
51
+ <method name="create_location" desc="create new location">
52
+ <param name="location" type="obj" associated="Location" desc="Location to create"/>
53
+ <return_value name="location" type="obj" associated="Location" desc="Location create or nil"/>
54
54
  </method>
55
55
 
56
56
  <method name="update_location" desc="update running location w/ new location sharing the same id">
@@ -60,13 +60,38 @@
60
60
 
61
61
  <!-- TODO delete_location -->
62
62
 
63
- <method name="subscribe_to_location" desc="subscribe simrpc endpoint to updates to location of the specified id via the location_moved method">
63
+ <method name="subscribe_to_location_movement" desc="subscribe simrpc endpoint to location movement updates">
64
+ <param name="node_id" type="str" desc="Node identifier which to update of location movements"/>
64
65
  <param name="location_id" type="int" desc="Location identifier"/>
66
+ <param name="min_distance" type="float" default="0" desc="Min distance the location needs to have moved before notifying node"/>
67
+ <param name="min_x" type="float" default="0" desc="Min x-axis-distance the location needs to have moved before notifying node"/>
68
+ <param name="min_y" type="float" default="0" desc="Min y-axis-distance the location needs to have moved before notifying node"/>
69
+ <param name="min_z" type="float" default="0" desc="Min z-axis-distance the location needs to have moved before notifying node"/>
70
+ <return_value name="success" type="bool" desc="bool success"/>
71
+ </method>
72
+
73
+ <method name="subscribe_to_locations_proximity" desc="subscribe simrpc endpoint to location proximity events">
65
74
  <param name="node_id" type="str" desc="Node identifier which to update of location movements"/>
75
+ <param name="location_id1" type="int" desc="First location identifier"/>
76
+ <param name="location_id2" type="int" desc="Second location identifier"/>
77
+ <param name="event" type="str" default="proximity" desc="Proximity event to subscribe to, should be proximity, entered_proxity, or left_proximity" />
78
+ <param name="max_distance" type="float" default="0" desc="Max distance the locations need to be apart before notifying node"/>
79
+ <param name="max_x" type="float" default="0" desc="Max x-axis-distance the locations need to be apart before notifying node"/>
80
+ <param name="max_y" type="float" default="0" desc="Max y-axis-distance the locations need to be apart before notifying node"/>
81
+ <param name="max_z" type="float" default="0" desc="Max z-axis-distance the locations need to be apart before notifying node"/>
66
82
  <return_value name="success" type="bool" desc="bool success"/>
67
83
  </method>
68
84
 
69
85
  <method name="location_moved" desc="inform simrpc endpoint that location was moved">
70
86
  <param name="location" type="obj" associated="Location" desc="Location that was moved with new coordinates and original id"/>
87
+ <param name="distance" type="float" desc="Total distance location moved"/>
88
+ <param name="x_distance" type="float" desc="Distance location moved along the x axis"/>
89
+ <param name="y_distance" type="float" desc="Distance location moved along the y axis"/>
90
+ <param name="z_distance" type="float" desc="Distance location moved along the z axis"/>
91
+ </method>
92
+
93
+ <method name="locations_proximity" desc="inform simrpc endpoint of location proximity event">
94
+ <param name="location1" type="obj" associated="Location" desc="First location in proximity"/>
95
+ <param name="location2" type="obj" associated="Location" desc="Second location in proximity"/>
71
96
  </method>
72
97
  </schema>
@@ -4,10 +4,12 @@
4
4
  # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
5
5
 
6
6
  lib = File.dirname(__FILE__)
7
+ $: << lib + '/motel/'
7
8
 
8
9
  require lib + '/motel/exceptions'
10
+ require lib + '/motel/callbacks'
9
11
  require lib + '/motel/runner'
10
- require lib + '/motel/simrpc'
12
+ require lib + '/motel/simrpc_adapter'
11
13
 
12
14
  require lib + '/motel/dsl'
13
15
 
@@ -0,0 +1,139 @@
1
+ # Motel callback definitions
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 'motel/common'
7
+ require 'motel/location'
8
+
9
+ module Motel
10
+
11
+ module Callbacks
12
+
13
+ # TODO implement callbacks via callback conditions, optional conditions
14
+ # which may be added to a callback which all or some must be true to
15
+ # invoke callback (eg min movement, max proximity, min time passed, etc)
16
+
17
+ # Base Motel callback interface, provides access to invocable handler
18
+ class Base
19
+ # Accessor which will be invoked upon callback event
20
+ attr_accessor :handler
21
+
22
+ def initialize(args = {}, &block)
23
+ @handler = args[:handler] if args.has_key?(:handler)
24
+ @handler = block if block_given?
25
+ end
26
+
27
+ # FIXME XXX this should be *args instead of args = [] (remove [] around super calls below)
28
+ def invoke(args = [])
29
+ handler.call *args
30
+ end
31
+
32
+ end
33
+
34
+ # Invoked upon specified minimum location movement
35
+ class Movement < Base
36
+ # Minimum distance the location needs to move to trigger event
37
+ attr_accessor :min_distance
38
+
39
+ # Minimum x,y,z distance the location needs to move to trigger the event
40
+ attr_accessor :min_x, :min_y, :min_z
41
+
42
+ def initialize(args = {}, &block)
43
+ @min_distance = 0
44
+ @min_x = 0
45
+ @min_y = 0
46
+ @min_z = 0
47
+
48
+ @min_distance = args[:min_distance] if args.has_key?(:min_distance)
49
+ @min_x = args[:min_x] if args.has_key?(:min_x)
50
+ @min_y = args[:min_y] if args.has_key?(:min_y)
51
+ @min_z = args[:min_z] if args.has_key?(:min_z)
52
+
53
+ # store original coordinates internally,
54
+ # until minimum distances are satified
55
+ # and callback is invoked, then clear
56
+ @orig_x = @orig_y = @orig_z = nil
57
+
58
+ super(args, &block)
59
+ end
60
+
61
+ # Calculate distance between location and old coordinates, invoke handler w/ location if minimums are true
62
+ def invoke(new_location, old_x, old_y, old_z)
63
+ # unless original coordinates is nil, ignore old coordinates passed in
64
+ if @orig_x.nil?
65
+ @orig_x = old_x
66
+ @orig_y = old_y
67
+ @orig_z = old_z
68
+ end
69
+
70
+ dx = new_location.x - @orig_x
71
+ dy = new_location.y - @orig_y
72
+ dz = new_location.z - @orig_z
73
+ d = Math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
74
+
75
+ if d >= @min_distance && dx.abs >= @min_x && dy.abs >= @min_y && dz.abs >= @min_z
76
+ super([new_location, d, dx, dy, dz])
77
+ @orig_x = @orig_y = @orig_z = nil
78
+ end
79
+ end
80
+ end # class Movement
81
+
82
+ # Invoked upon specified maximum distance between locations
83
+ class Proximity < Base
84
+ # location which to compare to
85
+ attr_accessor :to_location
86
+
87
+ # proximity event which to trigger on
88
+ attr_accessor :event
89
+
90
+ # Max distance the locations needs to be apart to trigger event
91
+ attr_accessor :max_distance
92
+
93
+ # Max x,y,z distance the locations need to be to trigger the event
94
+ attr_accessor :max_x, :max_y, :max_z
95
+
96
+ def initialize(args = {}, &block)
97
+ @max_distance = 0
98
+ @max_x = 0
99
+ @max_y = 0
100
+ @max_z = 0
101
+ @to_location = nil
102
+ @event = :proximity
103
+
104
+ @max_distance = args[:max_distance] if args.has_key?(:max_distance)
105
+ @max_x = args[:max_x] if args.has_key?(:max_x)
106
+ @max_y = args[:max_y] if args.has_key?(:max_y)
107
+ @max_z = args[:max_z] if args.has_key?(:max_z)
108
+ @to_location = args[:to_location] if args.has_key?(:to_location)
109
+ @event = args[:event].intern if args.has_key?(:event)
110
+
111
+ # keep track of proximity state internally for different event types
112
+ @locations_in_proximity = false
113
+
114
+ super(args, &block)
115
+ end
116
+
117
+ # Calculate distance between specified location and stored one,
118
+ # invoke handler w/ specified location if they are within proximity
119
+ def invoke(location)
120
+ dx = (location.x - to_location.x).abs
121
+ dy = (location.y - to_location.y).abs
122
+ dz = (location.z - to_location.z).abs
123
+ d = Math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
124
+
125
+ currently_in_proximity = (d <= @max_distance) || (dx <= @max_x && dy <= @max_y && dz <= @max_z)
126
+ trigger_callback = (currently_in_proximity && (@event == :proximity ||
127
+ (@event == :entered_proximity &&
128
+ !@locations_in_proximity))) ||
129
+ (!currently_in_proximity && (@event == :left_proximity &&
130
+ @locations_in_proximity))
131
+
132
+ @locations_in_proximity = currently_in_proximity
133
+
134
+ super([location, to_location]) if trigger_callback
135
+ end
136
+ end # class Proximity
137
+
138
+ end # module Callbacks
139
+ end # module Motel