001    /**
002     * Jetrix TetriNET Server
003     * Copyright (C) 2004  Emmanuel Bourg
004     *
005     * This program is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU General Public License
007     * as published by the Free Software Foundation; either version 2
008     * of the License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     * GNU General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program; if not, write to the Free Software
017     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
018     */
019    
020    package net.jetrix.filter;
021    
022    import static java.lang.Math.*;
023    import static net.jetrix.GameState.*;
024    
025    import net.jetrix.*;
026    import net.jetrix.config.*;
027    import net.jetrix.messages.channel.*;
028    import net.jetrix.messages.channel.specials.*;
029    
030    import java.util.*;
031    
032    import org.apache.commons.lang.time.StopWatch;
033    
034    /**
035     * Sudden death mode. This filter implements the well known sudden death mode
036     * from tetrinetx: after a given time, lines are added to all players at a
037     * specified rate until the game ends.
038     *
039     * @author Emmanuel Bourg
040     * @version $Revision: 798 $, $Date: 2009-02-18 16:24:28 +0100 (Wed, 18 Feb 2009) $
041     */
042    public class SuddenDeathFilter extends GenericFilter
043    {
044        private StopWatch stopWatch;
045        private Timer timer;
046    
047        public void init()
048        {
049            stopWatch = new StopWatch();
050        }
051    
052        public void onMessage(StartGameMessage m, List<Message> out)
053        {
054            out.add(m);
055    
056            if (getChannel().getGameState() == STOPPED)
057            {
058                stopWatch.reset();
059                stopWatch.start();
060            }
061    
062            Settings settings = getChannel().getConfig().getSettings();
063            int time = settings.getSuddenDeathTime() * 1000;
064    
065            if (time > 0)
066            {
067                // start the monitoring thread
068                timer = new Timer();
069                timer.schedule(new Task(time), max(0, time - Task.WARNING_DELAY * 1000), 200);
070            }
071        }
072    
073        public void onMessage(EndGameMessage m, List<Message> out)
074        {
075            out.add(m);
076    
077            if (getChannel().getGameState() != STOPPED)
078            {
079                stopWatch.stop();
080            }
081    
082            if (timer != null)
083            {
084                // kill the monitoring thread
085                timer.cancel();
086                timer = null;
087            }
088        }
089    
090        public void onMessage(PauseMessage m, List<Message> out)
091        {
092            out.add(m);
093    
094            if (getChannel().getGameState() == STARTED)
095            {
096                stopWatch.suspend();
097            }
098        }
099    
100        public void onMessage(ResumeMessage m, List<Message> out)
101        {
102            out.add(m);
103    
104            if (getChannel().getGameState() == PAUSED)
105            {
106                stopWatch.resume();
107            }
108        }
109    
110        private class Task extends TimerTask
111        {
112            public static final int WARNING_DELAY = 60;
113    
114            private boolean suddenDeathEnabled;
115            private long nextTriggerTime;
116            private long nextWarningTime;
117    
118            public Task(long nextTriggerTime)
119            {
120                this.nextTriggerTime = nextTriggerTime;
121                this.nextWarningTime = nextTriggerTime - WARNING_DELAY * 1000;
122            }
123    
124            public void run()
125            {
126                Settings settings = getChannel().getConfig().getSettings();
127    
128                if (stopWatch.getTime() >= nextTriggerTime)
129                {
130                    if (!suddenDeathEnabled)
131                    {
132                        // enable the sudden death mode
133                        suddenDeathEnabled = true;
134    
135                        // notify the players
136                        GmsgMessage gmsg = new GmsgMessage();
137                        String message = settings.getSuddenDeathMessage();
138                        if (message.startsWith("key:"))
139                        {
140                            gmsg.setKey(message.substring(4));
141                        }
142                        else
143                        {
144                            gmsg.setText(message);
145                        }
146    
147                        getChannel().send(gmsg);
148    
149                        GmsgMessage rate = new GmsgMessage();
150                        rate.setKey("filter.suddendeath.rate", settings.getSuddenDeathLinesAdded(), settings.getSuddenDeathDelay());
151                        getChannel().send(rate);
152                    }
153    
154                    // add the lines
155                    sendLines(settings.getSuddenDeathLinesAdded());
156    
157                    // update the next time the lines will be added
158                    nextTriggerTime = nextTriggerTime + settings.getSuddenDeathDelay() * 1000;
159                }
160                else if (stopWatch.getTime() >= nextWarningTime && !suddenDeathEnabled)
161                {
162                    // warn the players
163                    GmsgMessage gmsg = new GmsgMessage();
164                    gmsg.setKey("filter.suddendeath.warning", Math.ceil((nextTriggerTime - stopWatch.getTime()) / 1000d));
165                    getChannel().send(gmsg);
166    
167                    nextWarningTime = nextWarningTime + WARNING_DELAY * 1000;
168                }
169            }
170    
171            /**
172             * Add lines to all players in the channel
173             *
174             * @param count the number of lines to add
175             */
176            private void sendLines(int count)
177            {
178                Channel channel = getChannel();
179    
180                if (count == 1)
181                {
182                    channel.send(new OneLineAddedMessage());
183                }
184                if (count >= 4)
185                {
186                    channel.send(new FourLinesAddedMessage());
187                    sendLines(count - 4);
188                }
189                else if (count >= 2)
190                {
191                    channel.send(new TwoLinesAddedMessage());
192                    sendLines(count - 2);
193                }
194            }
195        }
196    
197        public String getName()
198        {
199            return "Sudden Death";
200        }
201    
202        public String getDescription()
203        {
204            return "Sudden death mode for never ending games";
205        }
206    
207        public String getVersion()
208        {
209            return "1.0";
210        }
211    
212        public String getAuthor()
213        {
214            return "Emmanuel Bourg";
215        }
216    }