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 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