1 /* bzflag
2  * Copyright (c) 1993-2021 Tim Riker
3  *
4  * This package is free software;  you can redistribute it and/or
5  * modify it under the terms of the license found in the file
6  * named COPYING that should have accompanied this file.
7  *
8  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11  */
12 
13 // TimeLimit.cpp : bzfs plugin to change/set the match duration
14 //
15 
16 #include "bzfsAPI.h"
17 #include <sstream>
18 #include <stdio.h>
19 #include <string.h>
20 
21 #define MAX_TIMES 20
22 
23 class TimeLimit : public bz_Plugin, public bz_CustomSlashCommandHandler
24 {
25 public:
26 
Name()27     virtual const char* Name ()
28     {
29         return "Time Limit";
30     }
31     virtual void Init ( const char* config );
32     virtual void Cleanup ();
33     virtual void Event ( bz_EventData *eventData );
34     virtual bool SlashCommand ( int playerID, bz_ApiString, bz_ApiString, bz_APIStringList*);
35 
36 protected:
37 
38 private:
39 
40 };
41 
42 BZ_PLUGIN(TimeLimit)
43 
44 // variable to save the original -time value
45 float saveTimeLimit = 0;
46 
47 // list to hold the available match durations
48 bz_APIStringList* timeList = bz_newStringList();
49 
50 // Displays the available match durations
showMatchDurations(int playerID)51 void showMatchDurations(int playerID)
52 {
53     bz_sendTextMessagef (BZ_SERVER, playerID, "Not a valid match duration, valid match durations are : ");
54     for (unsigned i=0; i < timeList->size(); i++)
55         bz_sendTextMessagef (BZ_SERVER, playerID, "* %s minute(s)",timeList->get(i).c_str());
56 }
57 
58 
59 // Checks if it's a valid match duration or not
isValidTime(float timelimit)60 bool isValidTime ( float timelimit )
61 {
62     if ( timeList->size() == 0 ) return true;
63 
64     for (unsigned i=0; i < timeList->size(); i++)
65     {
66         if ( atof(timeList->get(i).c_str()) == timelimit )
67             return true;
68     }
69     return false;
70 }
71 
72 
Event(bz_EventData * eventData)73 void TimeLimit::Event ( bz_EventData *eventData )
74 {
75     switch (eventData->eventType)
76     {
77     case bz_ePlayerJoinEvent:
78     {
79         // if it's the first player that joins, then reset the time to default
80         if ( bz_getPlayerCount() == 1 && bz_isTimeManualStart() && !bz_isCountDownActive()  && !bz_isCountDownInProgress())
81             bz_setTimeLimit(saveTimeLimit);
82     }
83     break;
84 
85     //reset the time to default at gameover
86     case bz_eGameEndEvent:
87     {
88         bz_setTimeLimit(saveTimeLimit);
89     }
90     break;
91 
92     default:
93     {
94         // do nothing
95     }
96     }
97 
98 }
99 
100 
convertIntToString(const unsigned int integer)101 std::string convertIntToString(const unsigned int integer)
102 {
103     std::ostringstream ostr;
104 
105     ostr << integer;
106 
107     return ostr.str();
108 }
109 
110 
parseCommand(const char * commandLine)111 void parseCommand ( const char* commandLine )
112 {
113     const size_t len = strlen(commandLine);
114 
115     if (len != 0)
116     {
117         unsigned int range_begin, range_end;
118         char junk;
119 
120         if (sscanf(commandLine, "%u-%u%c", &range_begin, &range_end, &junk) == 2)
121         {
122             while (range_begin <= range_end)
123                 timeList->push_back(convertIntToString(range_begin++));
124         }
125         else if (strspn(commandLine, ",0123456789") == len)
126             timeList->tokenize(commandLine, ",", MAX_TIMES, false);
127     }
128 }
129 
130 
SlashCommand(int playerID,bz_ApiString cmd,bz_ApiString,bz_APIStringList * cmdParams)131 bool TimeLimit::SlashCommand ( int playerID, bz_ApiString cmd, bz_ApiString, bz_APIStringList* cmdParams )
132 {
133 
134     if (strcasecmp (cmd.c_str(), "timelimit"))
135         return false;
136 
137     // Check permissions
138     if (! bz_hasPerm(playerID,"TIMELIMIT"))
139     {
140         bz_sendTextMessagef (BZ_SERVER, playerID, "You do not have permission to run the timelimit command");
141         return true;
142     }
143 
144     // If the server is not configured for manual countdown the timelimit
145     // command can't be used
146     if (! bz_isTimeManualStart())
147     {
148         bz_sendTextMessagef (BZ_SERVER, playerID, "This server was not configured for manual clock countdowns");
149         return true;
150     }
151 
152     if (cmdParams->get(0).c_str()[0] == '\0')
153     {
154         bz_sendTextMessagef (BZ_SERVER, playerID, "Usage : /timelimit <minutes>|show|reset");
155         return true;
156     }
157 
158     // displaying the current timelimit
159     if (strcasecmp(cmdParams->get(0).c_str(),"show") == 0 )
160     {
161         bz_sendTextMessagef (BZ_SERVER, playerID,"Match duration is set to %.0f minute(s)",(bz_getTimeLimit() / 60));
162         return true;
163     }
164 
165     // check if there is already a countdown in progress or if a match is
166     // already in progress
167     if ( bz_isCountDownInProgress() )
168     {
169         bz_sendTextMessagef (BZ_SERVER, playerID,
170                              "There is a countdown already in progress, match duration can't be changed now");
171         return true;
172     }
173     else if ( bz_isCountDownActive() )
174     {
175         bz_sendTextMessagef (BZ_SERVER, playerID, "A game is already in progress, match duration can't be changed now");
176         return true;
177     }
178 
179     bz_BasePlayerRecord *playerRecord;
180     playerRecord = bz_getPlayerByIndex(playerID);
181 
182     // resets the timer to the default
183     if (strcasecmp(cmdParams->get(0).c_str(),"reset") == 0 )
184     {
185         bz_setTimeLimit(saveTimeLimit);
186         bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "Match duration reset to %.0f minute(s) by %s",(bz_getTimeLimit() / 60),
187                              playerRecord->callsign.c_str());
188         return true;
189     }
190 
191     unsigned i, nonumber=0;
192 
193     for (i=0; i < strlen(cmdParams->get(0).c_str()); i++)
194     {
195         if (isdigit(cmdParams->get(0).c_str()[i]) == 0) nonumber=1;
196     }
197 
198     if (nonumber == 0 )
199     {
200         float limit = (float)atof(cmdParams->get(0).c_str());
201         // Don't allow timelimit being equal or lower then 0
202         if (limit > 0 )
203         {
204 
205             if (! isValidTime(limit))
206             {
207 
208                 showMatchDurations(playerID);
209                 return true;
210             }
211 
212             bz_setTimeLimit(limit * 60);
213             bz_sendTextMessagef (BZ_SERVER, BZ_ALLUSERS, "Match duration set to %.0f minute(s) by %s",(bz_getTimeLimit() / 60),
214                                  playerRecord->callsign.c_str());
215         }
216         else
217         {
218             bz_sendTextMessagef (BZ_SERVER, playerID, "Match duration can't be equal or lower then 0");
219             return true;
220         }
221     }
222     else
223     {
224         bz_sendTextMessagef (BZ_SERVER, playerID, "Not a correct value");
225         return true;
226     }
227 
228     return true;
229 
230 }
231 
Init(const char * commandLine)232 void TimeLimit::Init ( const char* commandLine )
233 {
234     parseCommand(commandLine);
235 
236     saveTimeLimit = bz_getTimeLimit();
237 
238     bz_registerCustomSlashCommand ("timelimit",this);
239     Register(bz_ePlayerJoinEvent);
240     Register(bz_eGameEndEvent);
241 }
242 
243 
Cleanup(void)244 void TimeLimit::Cleanup ( void )
245 {
246 
247     // set default timelimit back before unloading
248     //bz_setTimeLimit(saveTimeLimit);
249 
250     bz_removeCustomSlashCommand ("timelimit");
251     Flush();
252 
253 
254     // set default timelimit back before unloading
255     bz_setTimeLimit(saveTimeLimit);
256 }
257 
258 // Local Variables: ***
259 // mode: C++ ***
260 // tab-width: 4 ***
261 // c-basic-offset: 4 ***
262 // indent-tabs-mode: nil ***
263 // End: ***
264 // ex: shiftwidth=4 tabstop=4
265