grueserve 0.0.1

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