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.
- data/bin/grueserve +6 -0
- data/bin/grueserve_path2lines +59 -0
- data/example/attributes.yaml +28 -0
- data/example/map.gif +0 -0
- data/example/sprites/black_soldier_dead.gif +0 -0
- data/example/sprites/black_soldier_dodging_0PI1_1.gif +0 -0
- data/example/sprites/black_soldier_dodging_0PI1_2.gif +0 -0
- data/example/sprites/black_soldier_dodging_0PI1_3.gif +0 -0
- data/example/sprites/black_soldier_dodging_1PI1_1.gif +0 -0
- data/example/sprites/black_soldier_dodging_1PI1_2.gif +0 -0
- data/example/sprites/black_soldier_dodging_1PI1_3.gif +0 -0
- data/example/sprites/black_soldier_dodging_1PI2_1.gif +0 -0
- data/example/sprites/black_soldier_dodging_1PI2_2.gif +0 -0
- data/example/sprites/black_soldier_dodging_1PI2_3.gif +0 -0
- data/example/sprites/black_soldier_dodging_3PI2_1.gif +0 -0
- data/example/sprites/black_soldier_dodging_3PI2_2.gif +0 -0
- data/example/sprites/black_soldier_dodging_3PI2_3.gif +0 -0
- data/example/sprites/black_soldier_standing_0PI1_1.gif +0 -0
- data/example/sprites/black_soldier_standing_0PI1_2.gif +0 -0
- data/example/sprites/black_soldier_standing_0PI1_3.gif +0 -0
- data/example/sprites/black_soldier_standing_1PI1_1.gif +0 -0
- data/example/sprites/black_soldier_standing_1PI1_2.gif +0 -0
- data/example/sprites/black_soldier_standing_1PI1_3.gif +0 -0
- data/example/sprites/black_soldier_standing_1PI2_1.gif +0 -0
- data/example/sprites/black_soldier_standing_1PI2_2.gif +0 -0
- data/example/sprites/black_soldier_standing_1PI2_3.gif +0 -0
- data/example/sprites/black_soldier_standing_3PI2_1.gif +0 -0
- data/example/sprites/black_soldier_standing_3PI2_2.gif +0 -0
- data/example/sprites/black_soldier_standing_3PI2_3.gif +0 -0
- data/example/sprites/brown_soldier_dead.gif +0 -0
- data/example/sprites/brown_soldier_dodging_0PI1_1.gif +0 -0
- data/example/sprites/brown_soldier_dodging_0PI1_2.gif +0 -0
- data/example/sprites/brown_soldier_dodging_0PI1_3.gif +0 -0
- data/example/sprites/brown_soldier_dodging_1PI1_1.gif +0 -0
- data/example/sprites/brown_soldier_dodging_1PI1_2.gif +0 -0
- data/example/sprites/brown_soldier_dodging_1PI1_3.gif +0 -0
- data/example/sprites/brown_soldier_dodging_1PI2_1.gif +0 -0
- data/example/sprites/brown_soldier_dodging_1PI2_2.gif +0 -0
- data/example/sprites/brown_soldier_dodging_1PI2_3.gif +0 -0
- data/example/sprites/brown_soldier_dodging_3PI2_1.gif +0 -0
- data/example/sprites/brown_soldier_dodging_3PI2_2.gif +0 -0
- data/example/sprites/brown_soldier_dodging_3PI2_3.gif +0 -0
- data/example/sprites/brown_soldier_standing_0PI1_1.gif +0 -0
- data/example/sprites/brown_soldier_standing_0PI1_2.gif +0 -0
- data/example/sprites/brown_soldier_standing_0PI1_3.gif +0 -0
- data/example/sprites/brown_soldier_standing_1PI1_1.gif +0 -0
- data/example/sprites/brown_soldier_standing_1PI1_2.gif +0 -0
- data/example/sprites/brown_soldier_standing_1PI1_3.gif +0 -0
- data/example/sprites/brown_soldier_standing_1PI2_1.gif +0 -0
- data/example/sprites/brown_soldier_standing_1PI2_2.gif +0 -0
- data/example/sprites/brown_soldier_standing_1PI2_3.gif +0 -0
- data/example/sprites/brown_soldier_standing_3PI2_1.gif +0 -0
- data/example/sprites/brown_soldier_standing_3PI2_2.gif +0 -0
- data/example/sprites/brown_soldier_standing_3PI2_3.gif +0 -0
- data/example/sprites/explosion_1.gif +0 -0
- data/example/sprites/explosion_10.gif +0 -0
- data/example/sprites/explosion_11.gif +0 -0
- data/example/sprites/explosion_12.gif +0 -0
- data/example/sprites/explosion_13.gif +0 -0
- data/example/sprites/explosion_14.gif +0 -0
- data/example/sprites/explosion_15.gif +0 -0
- data/example/sprites/explosion_16.gif +0 -0
- data/example/sprites/explosion_17.gif +0 -0
- data/example/sprites/explosion_18.gif +0 -0
- data/example/sprites/explosion_19.gif +0 -0
- data/example/sprites/explosion_2.gif +0 -0
- data/example/sprites/explosion_20.gif +0 -0
- data/example/sprites/explosion_21.gif +0 -0
- data/example/sprites/explosion_22.gif +0 -0
- data/example/sprites/explosion_23.gif +0 -0
- data/example/sprites/explosion_24.gif +0 -0
- data/example/sprites/explosion_25.gif +0 -0
- data/example/sprites/explosion_26.gif +0 -0
- data/example/sprites/explosion_27.gif +0 -0
- data/example/sprites/explosion_3.gif +0 -0
- data/example/sprites/explosion_4.gif +0 -0
- data/example/sprites/explosion_5.gif +0 -0
- data/example/sprites/explosion_6.gif +0 -0
- data/example/sprites/explosion_7.gif +0 -0
- data/example/sprites/explosion_8.gif +0 -0
- data/example/sprites/explosion_9.gif +0 -0
- data/example/sprites/green_soldier_dead.gif +0 -0
- data/example/sprites/green_soldier_dodging_0PI1_1.gif +0 -0
- data/example/sprites/green_soldier_dodging_0PI1_2.gif +0 -0
- data/example/sprites/green_soldier_dodging_0PI1_3.gif +0 -0
- data/example/sprites/green_soldier_dodging_1PI1_1.gif +0 -0
- data/example/sprites/green_soldier_dodging_1PI1_2.gif +0 -0
- data/example/sprites/green_soldier_dodging_1PI1_3.gif +0 -0
- data/example/sprites/green_soldier_dodging_1PI2_1.gif +0 -0
- data/example/sprites/green_soldier_dodging_1PI2_2.gif +0 -0
- data/example/sprites/green_soldier_dodging_1PI2_3.gif +0 -0
- data/example/sprites/green_soldier_dodging_3PI2_1.gif +0 -0
- data/example/sprites/green_soldier_dodging_3PI2_2.gif +0 -0
- data/example/sprites/green_soldier_dodging_3PI2_3.gif +0 -0
- data/example/sprites/green_soldier_standing_0PI1_1.gif +0 -0
- data/example/sprites/green_soldier_standing_0PI1_2.gif +0 -0
- data/example/sprites/green_soldier_standing_0PI1_3.gif +0 -0
- data/example/sprites/green_soldier_standing_1PI1_1.gif +0 -0
- data/example/sprites/green_soldier_standing_1PI1_2.gif +0 -0
- data/example/sprites/green_soldier_standing_1PI1_3.gif +0 -0
- data/example/sprites/green_soldier_standing_1PI2_1.gif +0 -0
- data/example/sprites/green_soldier_standing_1PI2_2.gif +0 -0
- data/example/sprites/green_soldier_standing_1PI2_3.gif +0 -0
- data/example/sprites/green_soldier_standing_3PI2_1.gif +0 -0
- data/example/sprites/green_soldier_standing_3PI2_2.gif +0 -0
- data/example/sprites/green_soldier_standing_3PI2_3.gif +0 -0
- data/ext/extconf.rb +93 -0
- data/ext/grueserve_ext.c +2083 -0
- data/lib/grueserve.rb +28 -0
- data/lib/grueserve/application.rb +72 -0
- data/lib/grueserve/client.rb +339 -0
- data/lib/grueserve/debuggable.rb +114 -0
- data/lib/grueserve/map.rb +178 -0
- data/lib/grueserve/rated.rb +44 -0
- data/lib/grueserve/server.rb +201 -0
- data/lib/grueserve/soldier.rb +181 -0
- metadata +179 -0
data/lib/grueserve.rb
ADDED
|
@@ -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
|