1 /***
2 * Jetrix TetriNET Server
3 * Copyright (C) 2001-2005 Emmanuel Bourg
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 package net.jetrix.filter;
21
22 import static net.jetrix.GameState.*;
23
24 import java.util.*;
25 import java.text.*;
26
27 import net.jetrix.*;
28 import net.jetrix.messages.channel.*;
29 import net.jetrix.messages.channel.specials.*;
30 import org.apache.commons.lang.time.StopWatch;
31
32 /***
33 * A filter computing and displaying the number of pieces dropped per minute
34 * by each player.
35 *
36 * @author Emmanuel Bourg
37 * @version $Revision: 799 $, $Date: 2009-02-18 17:28:08 +0100 (Wed, 18 Feb 2009) $
38 */
39 public class StatsFilter extends GenericFilter
40 {
41 private List<PlayerStats> stats;
42 private StopWatch stopWatch;
43
44 private static DecimalFormat df = new DecimalFormat("0.00");
45
46 public void init()
47 {
48 stopWatch = new StopWatch();
49 stats = new ArrayList<PlayerStats>(6);
50 for (int i = 0; i < 6; i++)
51 {
52 stats.add(null);
53 }
54 }
55
56 public void onMessage(StartGameMessage m, List<Message> out)
57 {
58 if (getChannel().getGameState() == STOPPED)
59 {
60
61 stopWatch.reset();
62 stopWatch.start();
63
64
65 for (int i = 0; i < 6; i++)
66 {
67 if (getChannel().getClient(i + 1) != null)
68 {
69 stats.set(i, new PlayerStats());
70 }
71 else
72 {
73 stats.set(i, null);
74 }
75 }
76 }
77
78 out.add(m);
79 }
80
81 public void onMessage(EndGameMessage m, List<Message> out)
82 {
83
84 out.add(m);
85
86 if (getChannel().getGameState() != STOPPED)
87 {
88 stopWatch.stop();
89 displayStats(out);
90 }
91 }
92
93 public void onMessage(PauseMessage m, List<Message> out)
94 {
95 if (getChannel().getGameState() == STARTED)
96 {
97
98 stopWatch.suspend();
99 }
100
101 out.add(m);
102 }
103
104 public void onMessage(ResumeMessage m, List<Message> out)
105 {
106 if (getChannel().getGameState() == PAUSED)
107 {
108
109 stopWatch.resume();
110 }
111
112 out.add(m);
113 }
114
115 public void onMessage(FieldMessage m, List<Message> out)
116 {
117
118 PlayerStats playerStats = stats.get(m.getSlot() - 1);
119 if (playerStats != null && (stopWatch.getTime() > 1500))
120 {
121 playerStats.blockCount++;
122 }
123
124 out.add(m);
125 }
126
127 public void onMessage(LinesAddedMessage m, List<Message> out)
128 {
129 out.add(m);
130
131 updateStats(m);
132
133
134 removeBlock(m);
135 }
136
137 public void onSpecial(SpecialMessage m, List<Message> out)
138 {
139 if (!(m instanceof LinesAddedMessage))
140 {
141
142 PlayerStats playerStats = stats.get(m.getSlot() - 1);
143 playerStats.specialsReceived++;
144
145
146 playerStats.blockCount = playerStats.blockCount - 2;
147
148
149 playerStats = stats.get(m.getFromSlot() - 1);
150 playerStats.specialsSent++;
151 }
152 }
153
154 public void onMessage(LevelMessage m, List<Message> out)
155 {
156 out.add(m);
157 PlayerStats playerStats = stats.get(m.getSlot() - 1);
158 if (playerStats != null)
159 {
160 playerStats.level = m.getLevel();
161 }
162 }
163
164 public void onMessage(LeaveMessage m, List<Message> out)
165 {
166 out.add(m);
167
168 stats.set(m.getSlot() - 1, null);
169 }
170
171 public void onMessage(PlayerLostMessage m, List<Message> out)
172 {
173 out.add(m);
174 PlayerStats playerStats = stats.get(m.getSlot() - 1);
175 if (playerStats != null)
176 {
177 playerStats.playing = false;
178 playerStats.timePlayed = stopWatch.getTime();
179 }
180 }
181
182 /***
183 * Decrease the block count of players receiving an add to all message since
184 * they will send back a field message assimilated by mistake as a block fall.
185 */
186 private void removeBlock(SpecialMessage message)
187 {
188 int slot = message.getFromSlot();
189
190
191 String team = null;
192
193 if (message.getSource() != null && message.getSource() instanceof Client)
194 {
195 Client client = (Client) message.getSource();
196 team = client.getUser().getTeam();
197 }
198
199
200 for (int i = 1; i <= 6; i++)
201 {
202 Client client = getChannel().getClient(i);
203 if (i != slot && client != null)
204 {
205 User user = client.getUser();
206
207
208 if (user.isPlaying() && (user.getTeam() == null || !user.getTeam().equals(team)))
209 {
210 PlayerStats playerStats = stats.get(i - 1);
211 playerStats.blockCount--;
212 }
213 }
214 }
215 }
216
217 /***
218 * Update the stats of the player sending the specified message.
219 *
220 * @param message
221 */
222 private void updateStats(LinesAddedMessage message)
223 {
224 if (message.getFromSlot() > 0)
225 {
226 PlayerStats playerStats = stats.get(message.getFromSlot() - 1);
227 if (playerStats != null)
228 {
229 playerStats.linesAdded += message.getLinesAdded();
230 if (message.getLinesAdded() == 4)
231 {
232 playerStats.tetrisCount++;
233 }
234 }
235 }
236 }
237
238 private void displayStats(List<Message> out)
239 {
240 for (int slot = 1; slot <= 6; slot++)
241 {
242 PlayerStats playerStats = stats.get(slot - 1);
243 User user = getChannel().getPlayer(slot);
244
245 if (playerStats != null && user != null)
246 {
247
248 if (playerStats.playing)
249 {
250 playerStats.timePlayed = stopWatch.getTime();
251 }
252
253
254 String bpm = df.format(playerStats.getBlocksPerMinute());
255
256 StringBuilder text = new StringBuilder();
257 text.append("<purple>" + user.getName() + "</purple> : ");
258 text.append(playerStats.blockCount + " <aqua>blocks @<red>" + bpm + "</red> bpm, ");
259 text.append("<black>" + playerStats.linesAdded + "</black> added, ");
260 text.append("<black>" + playerStats.tetrisCount + "</black> tetris");
261 if (getChannel().getConfig().getSettings().getSpecialAdded() > 0)
262 {
263
264 text.append(", <black>" + playerStats.specialsSent + " / " + playerStats.specialsReceived + "</black> specials");
265 }
266
267 out.add(new PlineMessage(text.toString()));
268 }
269 }
270
271
272 PlineMessage time = new PlineMessage();
273 time.setText("<brown>Total game time: <black>" + df.format(stopWatch.getTime() / 1000f) + "</black> seconds");
274 out.add(time);
275 }
276
277 public String getName()
278 {
279 return "Stats Filter";
280 }
281
282 public String getDescription()
283 {
284 return "Displays stats about the game (pieces dropped per minute, lines added to all, time played, etc";
285 }
286
287 public String getVersion()
288 {
289 return "1.1";
290 }
291
292 public String getAuthor()
293 {
294 return "Emmanuel Bourg";
295 }
296
297 private class PlayerStats
298 {
299 long timePlayed;
300 int tetrisCount;
301 int linesAdded;
302 int blockCount;
303 int level;
304 int specialsSent;
305 int specialsReceived;
306 boolean playing = true;
307
308 public double getBlocksPerMinute()
309 {
310 return (double) blockCount * 60000 / (double) timePlayed;
311 }
312 }
313
314 }