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
|
@@ -0,0 +1,178 @@
|
|
|
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 'pathname'
|
|
20
|
+
require 'yaml'
|
|
21
|
+
require 'geo'
|
|
22
|
+
|
|
23
|
+
module Grueserve
|
|
24
|
+
|
|
25
|
+
class Map
|
|
26
|
+
|
|
27
|
+
def self.blocks?(distance, profile)
|
|
28
|
+
return true if profile >= 1
|
|
29
|
+
return false if profile <= 0
|
|
30
|
+
return rand < (profile - (1.0 / ((distance / 100) + (1.0 / profile))))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
include Debuggable
|
|
34
|
+
|
|
35
|
+
attr_reader :teams, :application
|
|
36
|
+
|
|
37
|
+
def initialize(path, application = nil)
|
|
38
|
+
@path = path
|
|
39
|
+
@application = application
|
|
40
|
+
@attributes = YAML.load(@path.join("attributes.yaml").read)
|
|
41
|
+
init_lines("movement_obstacles")
|
|
42
|
+
init_lines("visibility_obstacles")
|
|
43
|
+
init_lines("bullet_obstacles")
|
|
44
|
+
init_lines("hearing_obstacles")
|
|
45
|
+
init_lines("fortifications")
|
|
46
|
+
init_areas("speed_areas")
|
|
47
|
+
init_areas("protection_areas")
|
|
48
|
+
init_areas("hidden_areas")
|
|
49
|
+
@hearing_distance_cutoff = (@attributes["hearing_distance_cutoff"] || 100).to_f
|
|
50
|
+
@viewing_distance_limit = (@attributes["viewing_distance_limit"] || 1000).to_f
|
|
51
|
+
@teams = @attributes["teams"].split
|
|
52
|
+
@start_points = @teams.inject({}) do |sum, team|
|
|
53
|
+
sum.merge(team => @attributes["#{team}_start_points"].split.collect do |point_string|
|
|
54
|
+
coords = point_string.split(/,/)
|
|
55
|
+
Geo::Point.new(coords.first.to_f, coords.last.to_f)
|
|
56
|
+
end)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def size_of_team(team)
|
|
61
|
+
members = 0
|
|
62
|
+
each_client do |client|
|
|
63
|
+
members += 1 if client.team == team
|
|
64
|
+
end
|
|
65
|
+
return members
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def smallest_team
|
|
69
|
+
return @teams.sort do |team1,team2|
|
|
70
|
+
size_of_team(team1) <=> size_of_team(team2)
|
|
71
|
+
end.first
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def send_binaries(client)
|
|
75
|
+
Pathname.glob(@path.join("sprites").join("**").join("*")).each do |sprite_frame|
|
|
76
|
+
client.send_bin(sprite_frame.basename, sprite_frame.read)
|
|
77
|
+
end
|
|
78
|
+
client.send_bin("map", img.read)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def protection_factor(location)
|
|
82
|
+
rval = 1.0
|
|
83
|
+
if area = protection_areas.first_container(location)
|
|
84
|
+
rval *= self[area] / 1000.0
|
|
85
|
+
end
|
|
86
|
+
return rval
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def start_point(team, not_point = nil)
|
|
90
|
+
points_to_choose_from = @start_points[team].clone
|
|
91
|
+
points_to_choose_from.delete(not_point) if not_point && points_to_choose_from.size > 1
|
|
92
|
+
the_point = points_to_choose_from[rand(points_to_choose_from.size)]
|
|
93
|
+
return Geo::Point.new(the_point.x, the_point.y)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def img
|
|
97
|
+
@path.join(@attributes["map"])
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def bullet_intersection(line)
|
|
101
|
+
definite_stop = bullet_obstacles.closest_intersection(line)
|
|
102
|
+
line.p2 = definite_stop.first if definite_stop
|
|
103
|
+
fortifications.intersections(line).sort do |a,b|
|
|
104
|
+
a.first.to(line.p1).abs <=> b.first.to(line.p1).abs
|
|
105
|
+
end.each do |possible_stop|
|
|
106
|
+
if self.class.blocks?(line.p1.to(possible_stop.first).abs, self[possible_stop.last] / 1000.0)
|
|
107
|
+
return possible_stop.first
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
return line.p2
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def noise_damp(line)
|
|
114
|
+
obstacles_damp = hearing_obstacles.intersections(line).inject(1.0) do |sum, obstacle|
|
|
115
|
+
sum * self[obstacle.last] / 1000.0
|
|
116
|
+
end
|
|
117
|
+
(1.0 / ([(line.abs / @hearing_distance_cutoff),1].max ** 2)) * obstacles_damp
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
def init_areas(name)
|
|
123
|
+
triangle_set = self.send(name.to_sym)
|
|
124
|
+
if stringData = @attributes[name]
|
|
125
|
+
stringData.split(/:/).each do |segment|
|
|
126
|
+
pointString, valueString = segment.split(/=/)
|
|
127
|
+
value = @attributes["#{name}_default_value"].to_i
|
|
128
|
+
value = valueString.to_i if valueString
|
|
129
|
+
points = pointString.split
|
|
130
|
+
middle = point_from_string(points.shift)
|
|
131
|
+
(points.size - 1).times do |i|
|
|
132
|
+
p1 = points[i]
|
|
133
|
+
p1 = points.first if p1 == "loop"
|
|
134
|
+
p1 = point_from_string(p1)
|
|
135
|
+
p2 = points[i + 1]
|
|
136
|
+
p2 = points.first if p2 == "loop"
|
|
137
|
+
p2 = point_from_string(p2)
|
|
138
|
+
area = Geo::Triangle.new(middle, p1, p2)
|
|
139
|
+
log_debug("adding area #{area.inspect} with value #{value} to @#{name}")
|
|
140
|
+
triangle_set << area
|
|
141
|
+
self[area] = value
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def init_lines(name)
|
|
148
|
+
line_set = self.send(name.to_sym)
|
|
149
|
+
if stringData = @attributes[name]
|
|
150
|
+
stringData.split(/:/).each do |segment|
|
|
151
|
+
pointString, valueString = segment.split(/=/)
|
|
152
|
+
value = @attributes["#{name}_default_value"].to_i
|
|
153
|
+
value = valueString.to_i if valueString
|
|
154
|
+
points = segment.split
|
|
155
|
+
(points.size - 1).times do |i|
|
|
156
|
+
p1 = points[i]
|
|
157
|
+
p1 = points.first if p1 == "loop"
|
|
158
|
+
p1 = point_from_string(p1)
|
|
159
|
+
p2 = points[i + 1]
|
|
160
|
+
p2 = points.first if p2 == "loop"
|
|
161
|
+
p2 = point_from_string(p2)
|
|
162
|
+
o = Geo::Line.new(p1, p2)
|
|
163
|
+
log_debug("adding obstacle #{o.inspect} with value #{value} to @#{name}")
|
|
164
|
+
line_set << o
|
|
165
|
+
self[o] = value
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def point_from_string(s)
|
|
172
|
+
x,y = s.split(/,/)
|
|
173
|
+
return Geo::Point.new(x.to_i,y.to_i)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
|
|
19
|
+
module Grueserve
|
|
20
|
+
|
|
21
|
+
module Rated
|
|
22
|
+
|
|
23
|
+
class Rate
|
|
24
|
+
def initialize(n)
|
|
25
|
+
@n = n
|
|
26
|
+
@times = {}
|
|
27
|
+
end
|
|
28
|
+
def times_a_second(&block)
|
|
29
|
+
time = (@times[:second] ||= Time.now.to_f)
|
|
30
|
+
if Time.now.to_f - time > 1.0 / @n
|
|
31
|
+
@times[:second] = Time.now.to_f
|
|
32
|
+
yield
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def max_rate_of(n)
|
|
38
|
+
@rates ||= {}
|
|
39
|
+
@rates[n] ||= Rate.new(n)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
@@ -0,0 +1,201 @@
|
|
|
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 'socket'
|
|
20
|
+
require 'pp'
|
|
21
|
+
require 'monitor'
|
|
22
|
+
require 'xml/libxml'
|
|
23
|
+
|
|
24
|
+
module Grueserve
|
|
25
|
+
|
|
26
|
+
class Server
|
|
27
|
+
|
|
28
|
+
module ClientSocket
|
|
29
|
+
|
|
30
|
+
class PolicyFileRequest < RuntimeError
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
include MonitorMixin
|
|
34
|
+
include Debuggable
|
|
35
|
+
|
|
36
|
+
attr_accessor :buffer, :bufferSize, :client, :server
|
|
37
|
+
|
|
38
|
+
def init_client_socket(client, server)
|
|
39
|
+
mon_initialize
|
|
40
|
+
@client = client
|
|
41
|
+
@server = server
|
|
42
|
+
@buffer = ""
|
|
43
|
+
@bufferSize = 0
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def readTo(char)
|
|
47
|
+
rval = ""
|
|
48
|
+
while (next_char = self.read_nonblock(1)) != char
|
|
49
|
+
rval += next_char
|
|
50
|
+
if rval == "<policy-file-request/>\0"
|
|
51
|
+
log_debug("Received policy file request #{rval.inspect}")
|
|
52
|
+
raise PolicyFileRequest.new
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
return rval
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def handle_input
|
|
59
|
+
begin
|
|
60
|
+
@bufferSize = readTo(":").to_i if @bufferSize == 0
|
|
61
|
+
log_trace("Expecting package of size #{@bufferSize}")
|
|
62
|
+
gotten = self.read_nonblock(@bufferSize - @buffer.size)
|
|
63
|
+
log_trace("Received buffer #{gotten.inspect}")
|
|
64
|
+
@buffer += gotten
|
|
65
|
+
if @buffer.size == @bufferSize
|
|
66
|
+
log_debug("Received complete buffer from #{self}: #{@buffer.inspect}")
|
|
67
|
+
doc = XML::Parser.string(@buffer).parse
|
|
68
|
+
client.handle_input(doc)
|
|
69
|
+
@buffer = ""
|
|
70
|
+
@bufferSize = 0
|
|
71
|
+
end
|
|
72
|
+
rescue Exception => e
|
|
73
|
+
server.kill_socket(self)
|
|
74
|
+
raise e
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
include Debuggable
|
|
81
|
+
|
|
82
|
+
def initialize(options = {})
|
|
83
|
+
@port = options.delete(:port) || 25999
|
|
84
|
+
raise "#{self.class}.new requires an :application argument." unless options.include?(:application)
|
|
85
|
+
@application = options.delete(:application)
|
|
86
|
+
@frequency = options.delete(:frequency) || 100
|
|
87
|
+
raise "Unknown options to #{self.class}: #{options.inspect}" unless options.empty?
|
|
88
|
+
@sockets = []
|
|
89
|
+
@sockets_to_close = Set.new
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def start!
|
|
93
|
+
start_updating!
|
|
94
|
+
start_listening!
|
|
95
|
+
start_serving!
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def start_updating!
|
|
99
|
+
@updater ||= nil
|
|
100
|
+
@updater.kill if @updater.respond_to?(:kill)
|
|
101
|
+
@updater = Thread.new do
|
|
102
|
+
counter = 0
|
|
103
|
+
timer = Time.now
|
|
104
|
+
sleep_sum = 0
|
|
105
|
+
loop do
|
|
106
|
+
begin
|
|
107
|
+
start_time = Time.now
|
|
108
|
+
@application.map.update!
|
|
109
|
+
if (diff = (1.0 / @frequency) - ((stop_time = Time.now).to_f - start_time.to_f)) > 0
|
|
110
|
+
sleep_sum += diff
|
|
111
|
+
sleep(diff)
|
|
112
|
+
end
|
|
113
|
+
counter += 1
|
|
114
|
+
if Time.now - timer > 10
|
|
115
|
+
avg_frequency = counter / (Time.now.to_f - timer.to_f)
|
|
116
|
+
avg_sleep = sleep_sum / (Time.now.to_f - timer.to_f)
|
|
117
|
+
log_info("Avg frequency: #{avg_frequency}, sleeping #{(avg_sleep * 100).to_i}% of the time")
|
|
118
|
+
report
|
|
119
|
+
reset
|
|
120
|
+
timer = Time.now
|
|
121
|
+
counter = 0
|
|
122
|
+
sleep_sum = 0
|
|
123
|
+
end
|
|
124
|
+
rescue Exception => e
|
|
125
|
+
log_warn(e)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def kill_socket(socket)
|
|
132
|
+
socket.client.shutdown
|
|
133
|
+
@sockets.delete(socket)
|
|
134
|
+
@sockets_to_close << socket
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def start_listening!
|
|
138
|
+
@listener ||= nil
|
|
139
|
+
@listener.kill if @listener.respond_to?(:kill)
|
|
140
|
+
@listener = Thread.new do
|
|
141
|
+
loop do
|
|
142
|
+
begin
|
|
143
|
+
to_close = @sockets_to_close.to_a
|
|
144
|
+
while (socket = to_close.shift)
|
|
145
|
+
socket.close unless socket.closed?
|
|
146
|
+
end
|
|
147
|
+
read_ready, write_read, errors = select(@sockets, nil, @sockets, 1)
|
|
148
|
+
unless errors.nil?
|
|
149
|
+
errors.each do |socket|
|
|
150
|
+
log_warn("Going to drop #{socket} from #{@sockets.inspect} since it got an error")
|
|
151
|
+
kill_socket(socket)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
unless read_ready.nil?
|
|
155
|
+
read_ready.each do |socket|
|
|
156
|
+
if socket.eof?
|
|
157
|
+
log_info("Going to drop #{socket} from #{@sockets.inspect} since it is closed")
|
|
158
|
+
kill_socket(socket)
|
|
159
|
+
else
|
|
160
|
+
begin
|
|
161
|
+
socket.handle_input
|
|
162
|
+
rescue ClientSocket::PolicyFileRequest => e
|
|
163
|
+
log_info("Got policy file request from #{socket.inspect}")
|
|
164
|
+
socket.client.handle_policy_request
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
rescue Exception => e
|
|
170
|
+
log_warn(e)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
log_debug("Started listener")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def start_serving!
|
|
178
|
+
log_debug("Starting serving")
|
|
179
|
+
@server ||= nil
|
|
180
|
+
@server.close if @server.respond_to?(:close)
|
|
181
|
+
@server = TCPServer.new(@port)
|
|
182
|
+
loop do
|
|
183
|
+
begin
|
|
184
|
+
socket = @server.accept_nonblock
|
|
185
|
+
socket.extend(ClientSocket)
|
|
186
|
+
socket.init_client_socket(Client.new(:socket => socket, :application => @application), self)
|
|
187
|
+
log_info("Received #{socket}, adding it to #{@sockets.inspect}")
|
|
188
|
+
@sockets << socket
|
|
189
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR => e
|
|
190
|
+
log_trace("Got TCPServer error: #{e}")
|
|
191
|
+
IO.select([@server])
|
|
192
|
+
retry
|
|
193
|
+
rescue Exception => e
|
|
194
|
+
log_warn(e)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
end
|
|
@@ -0,0 +1,181 @@
|
|
|
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 Soldier
|
|
21
|
+
|
|
22
|
+
class Aim
|
|
23
|
+
attr_accessor :point, :precision
|
|
24
|
+
def initialize(point, precision)
|
|
25
|
+
@point = point
|
|
26
|
+
@precision = precision
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
RELOAD = "RELOAD"
|
|
31
|
+
|
|
32
|
+
def self.extend_object(o)
|
|
33
|
+
super
|
|
34
|
+
o.init_soldier
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def init_soldier
|
|
38
|
+
self.behaviour = "soldier"
|
|
39
|
+
@reloading = false
|
|
40
|
+
@aim = nil
|
|
41
|
+
subscribe("LBUTTON", :handle_lbutton)
|
|
42
|
+
subscribe("RELOAD", :handle_reload)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def refill
|
|
46
|
+
super
|
|
47
|
+
self.ammo = max_ammo
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def ammo
|
|
51
|
+
@ammo
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def ammo=(x)
|
|
55
|
+
@ammo = x
|
|
56
|
+
self.extra_status = " Ammo: #{@ammo}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def max_ammo
|
|
60
|
+
32
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def base_grenade_precision_factor
|
|
64
|
+
2
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def grenade_precision_delta
|
|
68
|
+
0.1
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def grenade_max_precision_factor
|
|
72
|
+
5
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def reload_time
|
|
76
|
+
3
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def throwing_speed
|
|
80
|
+
100
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def max_throwing_distance
|
|
84
|
+
200
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def reload
|
|
88
|
+
if !@reloading && @aim.nil
|
|
89
|
+
@reloading = true
|
|
90
|
+
broadcast_noise("reload", body.origo)
|
|
91
|
+
thread_with_rescue do
|
|
92
|
+
sleep(reload_time)
|
|
93
|
+
self.ammo = max_ammo
|
|
94
|
+
@reloading = false
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def throw_grenade
|
|
100
|
+
this_fuzz_factor = @aim.point.abs / @aim.precision
|
|
101
|
+
loc = body.origo + Geo::Point.new(@aim.point.x + (this_fuzz_factor * fuzz),
|
|
102
|
+
@aim.point.y + (this_fuzz_factor * fuzz))
|
|
103
|
+
send_throw(body.origo.to(loc), throwing_speed)
|
|
104
|
+
@aim = nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def handle_lbutton(command)
|
|
108
|
+
unless @reloading
|
|
109
|
+
if command.nil?
|
|
110
|
+
unless @aim.nil?
|
|
111
|
+
throw_grenade
|
|
112
|
+
end
|
|
113
|
+
else
|
|
114
|
+
if current_command.include?("SHIFT")
|
|
115
|
+
max_rate_of(10).times_a_second do
|
|
116
|
+
if @aim.nil?
|
|
117
|
+
new_point = Geo::Point.new(command["x"].to_i, command["y"].to_i) - body.origo
|
|
118
|
+
new_point.abs = max_throwing_distance if new_point.abs > max_throwing_distance
|
|
119
|
+
@aim = Aim.new(new_point, base_grenade_precision_factor)
|
|
120
|
+
else
|
|
121
|
+
@aim.precision += grenade_precision_delta unless @aim.precision > grenade_max_precision_factor
|
|
122
|
+
end
|
|
123
|
+
aim_point = body.origo + @aim.point
|
|
124
|
+
send_aim(aim_point.x, aim_point.y, @aim.point.abs / @aim.precision)
|
|
125
|
+
end
|
|
126
|
+
else
|
|
127
|
+
if @aim.nil?
|
|
128
|
+
if ammo > 0
|
|
129
|
+
max_rate_of(8).times_a_second do
|
|
130
|
+
shot_line = body.origo.to(command["x"].to_i, command["y"].to_i)
|
|
131
|
+
this_fuzz_factor = shoot_fuzz_factor
|
|
132
|
+
angle = fuzz * this_fuzz_factor + shot_line.angle
|
|
133
|
+
send_aim(command["x"], command["y"], Math.tan(this_fuzz_factor) * shot_line.abs)
|
|
134
|
+
fire_bullet(angle)
|
|
135
|
+
self.ammo -= 1
|
|
136
|
+
end
|
|
137
|
+
else
|
|
138
|
+
max_rate_of(1).times_a_second do
|
|
139
|
+
broadcast_noise("dry_shot", body.origo)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
else
|
|
143
|
+
throw_grenade
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def handle_reload(command)
|
|
151
|
+
unless command.nil?
|
|
152
|
+
reload
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def damage
|
|
157
|
+
2
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def shoot_fuzz_factor
|
|
161
|
+
rval = 1.0
|
|
162
|
+
if moving?
|
|
163
|
+
if state == :dodging
|
|
164
|
+
rval *= 4
|
|
165
|
+
else
|
|
166
|
+
rval *= 2
|
|
167
|
+
end
|
|
168
|
+
else
|
|
169
|
+
rval *= 0.5 if state == :dodging
|
|
170
|
+
end
|
|
171
|
+
return rval / precision
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def precision
|
|
175
|
+
30.0
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
end
|