grueserve 0.0.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.
Files changed (117) hide show
  1. data/bin/grueserve +6 -0
  2. data/bin/grueserve_path2lines +59 -0
  3. data/example/attributes.yaml +28 -0
  4. data/example/map.gif +0 -0
  5. data/example/sprites/black_soldier_dead.gif +0 -0
  6. data/example/sprites/black_soldier_dodging_0PI1_1.gif +0 -0
  7. data/example/sprites/black_soldier_dodging_0PI1_2.gif +0 -0
  8. data/example/sprites/black_soldier_dodging_0PI1_3.gif +0 -0
  9. data/example/sprites/black_soldier_dodging_1PI1_1.gif +0 -0
  10. data/example/sprites/black_soldier_dodging_1PI1_2.gif +0 -0
  11. data/example/sprites/black_soldier_dodging_1PI1_3.gif +0 -0
  12. data/example/sprites/black_soldier_dodging_1PI2_1.gif +0 -0
  13. data/example/sprites/black_soldier_dodging_1PI2_2.gif +0 -0
  14. data/example/sprites/black_soldier_dodging_1PI2_3.gif +0 -0
  15. data/example/sprites/black_soldier_dodging_3PI2_1.gif +0 -0
  16. data/example/sprites/black_soldier_dodging_3PI2_2.gif +0 -0
  17. data/example/sprites/black_soldier_dodging_3PI2_3.gif +0 -0
  18. data/example/sprites/black_soldier_standing_0PI1_1.gif +0 -0
  19. data/example/sprites/black_soldier_standing_0PI1_2.gif +0 -0
  20. data/example/sprites/black_soldier_standing_0PI1_3.gif +0 -0
  21. data/example/sprites/black_soldier_standing_1PI1_1.gif +0 -0
  22. data/example/sprites/black_soldier_standing_1PI1_2.gif +0 -0
  23. data/example/sprites/black_soldier_standing_1PI1_3.gif +0 -0
  24. data/example/sprites/black_soldier_standing_1PI2_1.gif +0 -0
  25. data/example/sprites/black_soldier_standing_1PI2_2.gif +0 -0
  26. data/example/sprites/black_soldier_standing_1PI2_3.gif +0 -0
  27. data/example/sprites/black_soldier_standing_3PI2_1.gif +0 -0
  28. data/example/sprites/black_soldier_standing_3PI2_2.gif +0 -0
  29. data/example/sprites/black_soldier_standing_3PI2_3.gif +0 -0
  30. data/example/sprites/brown_soldier_dead.gif +0 -0
  31. data/example/sprites/brown_soldier_dodging_0PI1_1.gif +0 -0
  32. data/example/sprites/brown_soldier_dodging_0PI1_2.gif +0 -0
  33. data/example/sprites/brown_soldier_dodging_0PI1_3.gif +0 -0
  34. data/example/sprites/brown_soldier_dodging_1PI1_1.gif +0 -0
  35. data/example/sprites/brown_soldier_dodging_1PI1_2.gif +0 -0
  36. data/example/sprites/brown_soldier_dodging_1PI1_3.gif +0 -0
  37. data/example/sprites/brown_soldier_dodging_1PI2_1.gif +0 -0
  38. data/example/sprites/brown_soldier_dodging_1PI2_2.gif +0 -0
  39. data/example/sprites/brown_soldier_dodging_1PI2_3.gif +0 -0
  40. data/example/sprites/brown_soldier_dodging_3PI2_1.gif +0 -0
  41. data/example/sprites/brown_soldier_dodging_3PI2_2.gif +0 -0
  42. data/example/sprites/brown_soldier_dodging_3PI2_3.gif +0 -0
  43. data/example/sprites/brown_soldier_standing_0PI1_1.gif +0 -0
  44. data/example/sprites/brown_soldier_standing_0PI1_2.gif +0 -0
  45. data/example/sprites/brown_soldier_standing_0PI1_3.gif +0 -0
  46. data/example/sprites/brown_soldier_standing_1PI1_1.gif +0 -0
  47. data/example/sprites/brown_soldier_standing_1PI1_2.gif +0 -0
  48. data/example/sprites/brown_soldier_standing_1PI1_3.gif +0 -0
  49. data/example/sprites/brown_soldier_standing_1PI2_1.gif +0 -0
  50. data/example/sprites/brown_soldier_standing_1PI2_2.gif +0 -0
  51. data/example/sprites/brown_soldier_standing_1PI2_3.gif +0 -0
  52. data/example/sprites/brown_soldier_standing_3PI2_1.gif +0 -0
  53. data/example/sprites/brown_soldier_standing_3PI2_2.gif +0 -0
  54. data/example/sprites/brown_soldier_standing_3PI2_3.gif +0 -0
  55. data/example/sprites/explosion_1.gif +0 -0
  56. data/example/sprites/explosion_10.gif +0 -0
  57. data/example/sprites/explosion_11.gif +0 -0
  58. data/example/sprites/explosion_12.gif +0 -0
  59. data/example/sprites/explosion_13.gif +0 -0
  60. data/example/sprites/explosion_14.gif +0 -0
  61. data/example/sprites/explosion_15.gif +0 -0
  62. data/example/sprites/explosion_16.gif +0 -0
  63. data/example/sprites/explosion_17.gif +0 -0
  64. data/example/sprites/explosion_18.gif +0 -0
  65. data/example/sprites/explosion_19.gif +0 -0
  66. data/example/sprites/explosion_2.gif +0 -0
  67. data/example/sprites/explosion_20.gif +0 -0
  68. data/example/sprites/explosion_21.gif +0 -0
  69. data/example/sprites/explosion_22.gif +0 -0
  70. data/example/sprites/explosion_23.gif +0 -0
  71. data/example/sprites/explosion_24.gif +0 -0
  72. data/example/sprites/explosion_25.gif +0 -0
  73. data/example/sprites/explosion_26.gif +0 -0
  74. data/example/sprites/explosion_27.gif +0 -0
  75. data/example/sprites/explosion_3.gif +0 -0
  76. data/example/sprites/explosion_4.gif +0 -0
  77. data/example/sprites/explosion_5.gif +0 -0
  78. data/example/sprites/explosion_6.gif +0 -0
  79. data/example/sprites/explosion_7.gif +0 -0
  80. data/example/sprites/explosion_8.gif +0 -0
  81. data/example/sprites/explosion_9.gif +0 -0
  82. data/example/sprites/green_soldier_dead.gif +0 -0
  83. data/example/sprites/green_soldier_dodging_0PI1_1.gif +0 -0
  84. data/example/sprites/green_soldier_dodging_0PI1_2.gif +0 -0
  85. data/example/sprites/green_soldier_dodging_0PI1_3.gif +0 -0
  86. data/example/sprites/green_soldier_dodging_1PI1_1.gif +0 -0
  87. data/example/sprites/green_soldier_dodging_1PI1_2.gif +0 -0
  88. data/example/sprites/green_soldier_dodging_1PI1_3.gif +0 -0
  89. data/example/sprites/green_soldier_dodging_1PI2_1.gif +0 -0
  90. data/example/sprites/green_soldier_dodging_1PI2_2.gif +0 -0
  91. data/example/sprites/green_soldier_dodging_1PI2_3.gif +0 -0
  92. data/example/sprites/green_soldier_dodging_3PI2_1.gif +0 -0
  93. data/example/sprites/green_soldier_dodging_3PI2_2.gif +0 -0
  94. data/example/sprites/green_soldier_dodging_3PI2_3.gif +0 -0
  95. data/example/sprites/green_soldier_standing_0PI1_1.gif +0 -0
  96. data/example/sprites/green_soldier_standing_0PI1_2.gif +0 -0
  97. data/example/sprites/green_soldier_standing_0PI1_3.gif +0 -0
  98. data/example/sprites/green_soldier_standing_1PI1_1.gif +0 -0
  99. data/example/sprites/green_soldier_standing_1PI1_2.gif +0 -0
  100. data/example/sprites/green_soldier_standing_1PI1_3.gif +0 -0
  101. data/example/sprites/green_soldier_standing_1PI2_1.gif +0 -0
  102. data/example/sprites/green_soldier_standing_1PI2_2.gif +0 -0
  103. data/example/sprites/green_soldier_standing_1PI2_3.gif +0 -0
  104. data/example/sprites/green_soldier_standing_3PI2_1.gif +0 -0
  105. data/example/sprites/green_soldier_standing_3PI2_2.gif +0 -0
  106. data/example/sprites/green_soldier_standing_3PI2_3.gif +0 -0
  107. data/ext/extconf.rb +93 -0
  108. data/ext/grueserve_ext.c +2083 -0
  109. data/lib/grueserve.rb +28 -0
  110. data/lib/grueserve/application.rb +72 -0
  111. data/lib/grueserve/client.rb +339 -0
  112. data/lib/grueserve/debuggable.rb +114 -0
  113. data/lib/grueserve/map.rb +178 -0
  114. data/lib/grueserve/rated.rb +44 -0
  115. data/lib/grueserve/server.rb +201 -0
  116. data/lib/grueserve/soldier.rb +181 -0
  117. metadata +179 -0
@@ -0,0 +1,28 @@
1
+ # grueserve - a game server in c and ruby for the gruesome game
2
+ # Copyright (C) 2008 Martin Kihlgren <zond at troja dot ath dot cx>
3
+ #
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+
18
+ $: << File.dirname(File.expand_path(__FILE__))
19
+
20
+ require 'grueserve_ext'
21
+ require 'grueserve/debuggable.rb'
22
+ require 'grueserve/map.rb'
23
+ require 'grueserve/rated.rb'
24
+ require 'grueserve/soldier.rb'
25
+ require 'grueserve/client.rb'
26
+ require 'grueserve/server.rb'
27
+ require 'grueserve/application.rb'
28
+
@@ -0,0 +1,72 @@
1
+ # grueserve - a game server in c and ruby for the gruesome game
2
+ # Copyright (C) 2008 Martin Kihlgren <zond at troja dot ath dot cx>
3
+ #
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+
18
+ require 'pathname'
19
+ require 'optparse'
20
+
21
+ module Grueserve
22
+
23
+ class Application
24
+
25
+ include Debuggable
26
+
27
+ attr_accessor :delay, :port, :timeout, :debug, :map_path, :map
28
+
29
+ def initialize
30
+ @map_path = Pathname.new(__FILE__).parent.parent.parent.join("example")
31
+ @port = 25999
32
+ @debug = Grueserve::Debuggable::LEVELS[Grueserve::Debuggable::LEVELS.size / 2]
33
+ @delay = 0.1
34
+ @timeout = 20.0
35
+ end
36
+
37
+ def go
38
+ ARGV.options do |o|
39
+ o.set_summary_indent(' ')
40
+ o.banner = "Usage: #{$0} [OPTIONS]"
41
+ o.define_head "Serves gruesome games."
42
+ o.separator ""
43
+ o.on("-p", "--port=PORT", Integer, :REQUIRED, "TCP port to listen on, defaults to #{@port}.") do |@port| end
44
+ o.on("-D", "--delay=DELAY", Float, "The delay constant to use when prioritizing connected players against connecting players. Defaults to #{@delay}.") do |@delay| end
45
+ o.on("-t", "--timeout=TIMEOUT", Float, "The timeout for broken and disconnected clients. Defaults to #{@timeout}.") do |@timeout| end
46
+ o.on("-m", "--map=MAP", String, "Directory where the map to serve can be found, defaults to #{@map_path}.") do |m|
47
+ @map_path = Pathname.new(m, self)
48
+ end
49
+ o.on("-d", "--debug=LEVEL", String, "Debug level to use, choose from '#{Grueserve::Debuggable::LEVELS.join(", ")}', defaults to #{@debug}.") do |d|
50
+ @debug = d.to_sym
51
+ end
52
+ o.separator ""
53
+ o.on_tail("-h", "--help", "Show this help message.") do
54
+ puts o
55
+ exit
56
+ end
57
+ o.parse!
58
+ start_server!
59
+ end
60
+ end
61
+
62
+ def start_server!
63
+ Debuggable.level = @debug
64
+ log_info("Starting Grueserve::Server serving map #{@map} on port #{@port} with debug level #{@debug}")
65
+ @map = Grueserve::Map.new(@map_path, self)
66
+ @server = Grueserve::Server.new(:port => @port, :application => self)
67
+ @server.start!
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,339 @@
1
+ # grueserve - a game server in c and ruby for the gruesome game
2
+ # Copyright (C) 2008 Martin Kihlgren <zond at troja dot ath dot cx>
3
+ #
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+
18
+ require 'rubygems'
19
+ require 'set'
20
+ require 'digest/sha1'
21
+ require 'geo'
22
+ require 'xml/libxml'
23
+
24
+ module Grueserve
25
+
26
+ class Client
27
+
28
+ class Command
29
+ def initialize(node)
30
+ self.name = node.name
31
+ node.each_attr do |attribute|
32
+ self[attribute.name] = attribute.value
33
+ end
34
+ end
35
+ end
36
+
37
+ class CommandSet
38
+ def initialize(node = nil)
39
+ unless node.nil?
40
+ node.each_child do |child|
41
+ self << Command.new(child)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ class Location < Geo::Point
48
+ attr_accessor :client
49
+ def initialize(x,y,client)
50
+ super(x,y)
51
+ @client = client
52
+ end
53
+ end
54
+
55
+ include Debuggable
56
+ include Rated
57
+
58
+ attr_accessor :ready, :application, :client_threads, :last_start_point
59
+
60
+ def initialize(options)
61
+ self.socket = options.delete(:socket)
62
+ @application = options.delete(:application)
63
+ raise "Unknown options to #{self.class}.new: #{options.inspect}" unless options.empty?
64
+ self.map = @application.map
65
+ self.team = map.smallest_team
66
+ @last_start_point = @application.map.start_point(self.team)
67
+ @client_threads = Set.new
68
+ end
69
+
70
+ def new_message
71
+ msg = XML::Document.new
72
+ root = XML::Node.new("MESSAGE")
73
+ msg.root = root
74
+ return msg
75
+ end
76
+ def thread_with_rescue(&block)
77
+ new_thread = Thread.new do
78
+ begin
79
+ yield
80
+ rescue Exception => e
81
+ log_fatal(e)
82
+ ensure
83
+ self.client_threads.delete(Thread.current)
84
+ end
85
+ end
86
+ self.client_threads << new_thread
87
+ end
88
+ def shutdown
89
+ if registered?
90
+ self.unregister!
91
+ client_threads.each do |thread|
92
+ thread.kill
93
+ end
94
+ viewers.each do |viewer|
95
+ viewer.send_disappearance(self)
96
+ end
97
+ broadcast_message("#{nickname} disconnected")
98
+ end
99
+ end
100
+
101
+ def handle_ready
102
+ self.body.relocate(last_start_point)
103
+ refill
104
+ self.register!
105
+ broadcast_message("#{nickname} connected")
106
+ update_viewing
107
+ end
108
+
109
+ def handle_policy_request
110
+ msg = XML::Document.new
111
+ root = XML::Node.new("cross-domain-policy")
112
+ access = XML::Node.new("allow-access-from")
113
+ access["domain"] = "*"
114
+ access["to-port"] = "*"
115
+ root << access
116
+ msg.root = root
117
+ send_xml(msg.to_s)
118
+ end
119
+
120
+ def handle_helo(node)
121
+ self.nickname = node["nickname"]
122
+ self.nickname = "Anonymous" if self.nickname.empty?
123
+ application.map.send_binaries(self)
124
+ end
125
+
126
+ def send_bin(key, val)
127
+ log_debug("Going to send bin to #{self}: #{key.inspect}")
128
+ log_trace(" => #{val.inspect}")
129
+ send("bin:#{key}:#{val}")
130
+ end
131
+
132
+ def send_xml(message)
133
+ log_debug("Going to send xml to #{self}: #{message.inspect}")
134
+ send("xml:#{message}")
135
+ end
136
+
137
+ def fire_bullet(angle)
138
+ bullet_line = body.origo.to(1000,0)
139
+ bullet_line.angle = angle
140
+ bullet_line.p2 = application.map.bullet_intersection(bullet_line)
141
+ calculate_bullet_effects(bullet_line)
142
+ end
143
+
144
+ def calculate_bullet_effects(bullet_line)
145
+ viewers = map.viewers_of(bullet_line.p1) + map.viewers_of(bullet_line.p2)
146
+ viewers.each do |viewer|
147
+ viewer.send_bullet(bullet_line)
148
+ end
149
+ broadcast_noise("shot", bullet_line.p1);
150
+ Set.new(map.body_parts.intersections(bullet_line)).each do |intersection|
151
+ unless (victim = map[intersection.last]) == self
152
+ victim.shot_at(self, bullet_line)
153
+ end
154
+ end
155
+ end
156
+
157
+ def send_aim(x, y, radius)
158
+ msg = new_message
159
+ aim = XML::Node.new("AIM")
160
+ aim["x"] = x.to_i.to_s
161
+ aim["y"] = y.to_i.to_s
162
+ aim["radius"] = radius.to_i.to_s
163
+ msg.root << aim
164
+ send_xml(msg.to_s)
165
+ end
166
+
167
+ def send_throw(thro_line, speed)
168
+ msg = new_message
169
+ thro = XML::Node.new("THROW")
170
+ thro["x1"] = thro_line.p1.x.to_i.to_s
171
+ thro["y1"] = thro_line.p1.y.to_i.to_s
172
+ thro["x2"] = thro_line.p2.x.to_i.to_s
173
+ thro["y2"] = thro_line.p2.y.to_i.to_s
174
+ thro["speed"] = speed.to_i.to_s
175
+ msg.root << thro
176
+ send_xml(msg.to_s)
177
+ end
178
+
179
+ def send_bullet(bullet_line)
180
+ msg = new_message
181
+ bullet = XML::Node.new("BULLET")
182
+ bullet["x1"] = bullet_line.p1.x.to_i.to_s
183
+ bullet["y1"] = bullet_line.p1.y.to_i.to_s
184
+ bullet["x2"] = bullet_line.p2.x.to_i.to_s
185
+ bullet["y2"] = bullet_line.p2.y.to_i.to_s
186
+ msg.root << bullet
187
+ send_xml(msg.to_s)
188
+ end
189
+
190
+ def broadcast_noise(sound, source)
191
+ self.map.each_client do |client|
192
+ noise_line = client.body.origo.to(source)
193
+ noise_damp = application.map.noise_damp(noise_line)
194
+ client.send_noise(sound, noise_damp, (noise_line.abs > 0 ? noise_line.angle : nil))
195
+ end
196
+ end
197
+
198
+ def broadcast_message(m)
199
+ self.map.each_client do |client|
200
+ client.send_message(m)
201
+ end
202
+ end
203
+
204
+ def send_message(m)
205
+ msg = new_message
206
+ broadcast = XML::Node.new("BROADCAST")
207
+ broadcast["message"] = m
208
+ msg.root << broadcast
209
+ send_xml(msg.to_s)
210
+ end
211
+
212
+ def send_noise(sound, volume = 1.0, angle = nil)
213
+ msg = new_message
214
+ noise = XML::Node.new("NOISE")
215
+ noise["angle"] = angle.to_s
216
+ noise["volume"] = volume.to_s
217
+ noise["sound"] = sound
218
+ msg.root << noise
219
+ send_xml(msg.to_s)
220
+ end
221
+
222
+ #
223
+ # Uses the Box-Muller transform to generate a bit of normal distribution with variance 1
224
+ #
225
+ def fuzz
226
+ Math.sqrt(-2 * Math.log(rand)) * Math.cos(2 * Math::PI * rand)
227
+ end
228
+
229
+ def handle_input(message)
230
+ case message.root.name
231
+ when "MESSAGE"
232
+ message.root.each_child do |child|
233
+ case child.name
234
+ when "HELO"
235
+ extend(Soldier)
236
+ handle_helo(child)
237
+ when "READY"
238
+ handle_ready
239
+ when "COMMAND"
240
+ self.current_command = CommandSet.new(child)
241
+ when "PING"
242
+ handle_ping
243
+ end
244
+ end
245
+ else
246
+ log_warn("Got unknown message: #{message.to_s}")
247
+ end
248
+ end
249
+
250
+ def handle_ping
251
+ self.ping!
252
+ end
253
+
254
+ def killed_by(client)
255
+ self.score -= 1
256
+ self.deaths += 1
257
+ set_position(body.origo, :dead)
258
+ broadcast_noise("scream", body.origo)
259
+ thread_with_rescue do
260
+ sleep(dead_time)
261
+ revive
262
+ end
263
+ end
264
+
265
+ def adjusted_damage(dmg)
266
+ return dmg
267
+ end
268
+
269
+ def shot_at(client, bullet_line)
270
+ if hit_me?(bullet_line)
271
+ if self.state != :dead
272
+ self.hitpoints -= adjusted_damage(client.damage)
273
+ if self.hitpoints <= 0
274
+ killed_by(client)
275
+ client.killed(self)
276
+ end
277
+ end
278
+ end
279
+ end
280
+
281
+ def hit_me?(bullet_line)
282
+ return !Map.blocks?(bullet_line.p1.to(body.origo).abs, 1.0 - profile)
283
+ end
284
+
285
+ def killed(client)
286
+ self.kills += 1
287
+ if client.team == self.team
288
+ broadcast_message("#{nickname} teamkilled #{client.nickname}")
289
+ self.score -= 2
290
+ else
291
+ broadcast_message("#{nickname} killed #{client.nickname}")
292
+ self.score += 2
293
+ end
294
+ end
295
+
296
+ def revive
297
+ new_position = application.map.start_point(self.team, self.last_start_point)
298
+ set_position(new_position, :standing)
299
+ self.last_start_point = new_position
300
+ refill
301
+ end
302
+
303
+ def refill
304
+ self.hitpoints = max_hitpoints
305
+ end
306
+
307
+ def standing_profile
308
+ 1.0
309
+ end
310
+
311
+ def damage
312
+ 0.0
313
+ end
314
+
315
+ def dead_time
316
+ 5
317
+ end
318
+
319
+ def max_hitpoints
320
+ 10
321
+ end
322
+
323
+ def speed
324
+ rval = running_speed
325
+ rval /= 5.0 if state == :dodging
326
+ rval *= application.map.speed_factor(body.origo)
327
+ return rval
328
+ end
329
+
330
+ def profile
331
+ rval = standing_profile
332
+ rval /= 3.0 if state == :dodging
333
+ rval *= application.map.protection_factor(body.origo)
334
+ return rval
335
+ end
336
+
337
+ end
338
+
339
+ end
@@ -0,0 +1,114 @@
1
+ # grueserve - a game server in c and ruby for the gruesome game
2
+ # Copyright (C) 2008 Martin Kihlgren <zond at troja dot ath dot cx>
3
+ #
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+
18
+ module Grueserve
19
+
20
+ module Debuggable
21
+
22
+ LEVELS = [:trace, :debug, :info, :warn, :fatal]
23
+
24
+ @level = LEVELS.last
25
+ @@counters = {}
26
+ @@timers = {}
27
+ @@maxes = {}
28
+
29
+ def self.level=(level)
30
+ if LEVELS.include?(level)
31
+ @level = level
32
+ else
33
+ raise "The only debug levels allowed are '#{LEVELS.join(", ")}'."
34
+ end
35
+ end
36
+
37
+ def self.level
38
+ @level
39
+ end
40
+
41
+ def time(name, &block)
42
+ @@counters[name] ||= 0
43
+ @@timers[name] ||= 0
44
+ @@maxes[name] ||= 0
45
+ t = Time.now.to_f
46
+ @@timers[name] -= t
47
+ rval = yield
48
+ t2 = Time.now.to_f
49
+ @@timers[name] += t2
50
+ @@counters[name] += 1
51
+ @@maxes[name] = t2 - t if t2 - t > @@maxes[name]
52
+ return rval
53
+ end
54
+
55
+ def reset_for(name)
56
+ @@counters[name] = 0
57
+ @@timers[name] = 0
58
+ @@maxes[name] = 0
59
+ end
60
+
61
+ def reset
62
+ @@timers = {}
63
+ @@counters = {}
64
+ @@maxes = {}
65
+ end
66
+
67
+ def report_for(name)
68
+ @@timers[name] ||= 0
69
+ @@counters[name] ||= 0
70
+ @@maxes[name] ||= 0
71
+ log_info("#{name}: total: #{@@counters[name]} in #{@@timers[name]}, avg: #{@@counters[name] == 0 ? "0" : @@timers[name] / @@counters[name]}, max: #{@@maxes[name]}")
72
+ end
73
+
74
+ def report
75
+ @@timers.keys.each do |k|
76
+ report_for(k)
77
+ end
78
+ end
79
+
80
+ def method_missing(meth, *args)
81
+ if match = meth.to_s.match(/log_(.*)$/)
82
+ level = match[1].to_sym
83
+ if LEVELS.include?(level) && args.size == 1
84
+ Debuggable.output(level, *args)
85
+ else
86
+ super
87
+ end
88
+ else
89
+ super
90
+ end
91
+ end
92
+
93
+ def self.source_part(caller_ary)
94
+ caller_ary[2].gsub(/^.*\/(grueserve\/.*\.rb.*)$/, '\1')
95
+ end
96
+
97
+ def self.format(s, level)
98
+ STDERR.puts("#{Time.now}\t#{level}\t#{source_part(caller)}\t#{s}")
99
+ end
100
+
101
+ def self.output(level, m)
102
+ if LEVELS.index(@level) <= LEVELS.index(level)
103
+ format(m, level)
104
+ if m.respond_to?(:backtrace)
105
+ m.backtrace.each do |line|
106
+ format(line, level)
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+ end