root/trunk/twcore/src/twcore/core/HubBot.java @ 3402

Revision 3402, 46.4 KB (checked in by Maverick, 12 months ago)

Added methods getAllowedCommandsCount() and getCommandsCount() to CommandInterpreter for use in !help interface of HubBot

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1package twcore.core;
2import java.io.BufferedReader;
3import java.io.FileNotFoundException;
4import java.io.FileReader;
5import java.io.IOException;
6import java.util.Date;
7import java.util.HashSet;
8import java.util.LinkedHashMap;
9import java.util.TimerTask;
10
11import twcore.core.command.CommandInterpreter;
12import twcore.core.events.ArenaJoined;
13import twcore.core.events.FileArrived;
14import twcore.core.events.LoggedOn;
15import twcore.core.events.Message;
16import twcore.core.util.Tools;
17
18/**
19 * Bot designed to spawn other bots (both manually and automatically from
20 * autoload.cfg).  It also sets access privileges based on the server's access
21 * lists.  The hub bot is essential for the operation of all other bots
22 * in TWCore.
23 */
24public class HubBot extends SubspaceBot {
25    private BotQueue            m_botQueue;             // Queue of bots to spawn
26    private ThreadGroup         m_kingGroup;            // Thread grouping the
27                                                        // hub belongs to
28    private CommandInterpreter  m_commandInterpreter;   // Handles commands
29
30    private TimerTask           m_smartShutdownTask;    // Periodically disconnects idle bots before shutdown
31    private int                 m_smartShutdownRate = 10 * 1000;    // How often to check for idle bots, in ms
32
33    private TimerTask           m_billerDownTask;       // Periodically displays biller down message
34    private int                 m_billerDownRate = 5 * 60 * 1000;   // How often to display biller down msg, in ms
35
36    private long                                spawnutime;                             // Stores the unix timestamp when the bot gets spawned for !uptime use
37
38    /**
39     * Creates the hub bot's thread grouping, registers commands, sets up
40     * accepted events, and starts the bot queue thread.  Once HubBot has
41     * logged on (LoggedOn event received) it begins loading other bots.
42     * @param botAction Reference to BotAction object from Session
43     */
44    public HubBot( BotAction botAction ){
45
46        super( botAction );
47        m_kingGroup = new ThreadGroup( "KingGroup" );
48        m_commandInterpreter = new CommandInterpreter( botAction );
49        registerCommands();
50        EventRequester events = m_botAction.getEventRequester();
51        events.request( EventRequester.MESSAGE );
52        events.request( EventRequester.LOGGED_ON );
53        events.request( EventRequester.ARENA_JOINED );
54        events.request( EventRequester.FILE_ARRIVED );
55        try {
56            Thread.sleep( 5000 );
57        } catch( InterruptedException ie ){
58            Tools.printLog( "Interrupted exception while starting HubBot. (Should never occur!)" );
59        }
60        m_botQueue = new BotQueue( m_kingGroup, botAction );
61        m_botQueue.start();
62    }
63
64    /**
65     * Registers all commands needed for operation of the HubBot.
66     */
67    public void registerCommands(){
68        int acceptedMessages = Message.PRIVATE_MESSAGE | Message.REMOTE_PRIVATE_MESSAGE;
69       
70        // Bot+
71        int accessRequired = OperatorList.BOT_LEVEL; 
72        m_commandInterpreter.registerCommand( "!spawn", acceptedMessages, this, "handleSpawnMessage", accessRequired );
73
74        // Outsider+
75        accessRequired = OperatorList.OUTSIDER_LEVEL; 
76        m_commandInterpreter.registerCommand( "!help", acceptedMessages, this, "handleHelp", accessRequired );
77        m_commandInterpreter.registerCommand( "!listbots", acceptedMessages, this, "handleListBots", accessRequired );
78        m_commandInterpreter.registerCommand( "!waitinglist", acceptedMessages, this, "handleShowWaitingList", accessRequired );
79        m_commandInterpreter.registerCommand( "!billerdown", acceptedMessages, this, "handleBillerDownCommand", accessRequired );
80       
81        // Moderator+
82        m_commandInterpreter.registerCommand( "!uptime", acceptedMessages, this, "handleUptimeCommand", accessRequired );
83        m_commandInterpreter.registerCommand( "!dbstatus", acceptedMessages, this, "handleDbStatus", accessRequired );
84        m_commandInterpreter.registerCommand( "!version", acceptedMessages, this, "handleVersion", accessRequired );
85       
86        // Highmod+
87        accessRequired = OperatorList.HIGHMOD_LEVEL; 
88        m_commandInterpreter.registerCommand( "!spawnmax", acceptedMessages, this, "handleSpawnMaxMessage", accessRequired );
89        m_commandInterpreter.registerCommand( "!spawnauto", acceptedMessages, this, "handleSpawnAutoMessage", accessRequired );
90        m_commandInterpreter.registerCommand( "!remove", acceptedMessages, this, "handleRemove", accessRequired );
91        m_commandInterpreter.registerCommand( "!removetype", acceptedMessages, this, "handleRemoveType", accessRequired );
92       
93        // Smod+
94        accessRequired = OperatorList.SMOD_LEVEL;
95        m_commandInterpreter.registerCommand( "!updateaccess", acceptedMessages, this, "handleUpdateAccess", accessRequired );
96        m_commandInterpreter.registerCommand( "!listoperators", acceptedMessages, this, "handleListOperators", accessRequired );
97        m_commandInterpreter.registerCommand( "!recycleserver", acceptedMessages, this, "handleRecycleCommand", accessRequired );
98       
99        // Sysop+
100        accessRequired = OperatorList.SYSOP_LEVEL;
101        m_commandInterpreter.registerCommand( "!forcespawn", acceptedMessages, this, "handleForceSpawnMessage", accessRequired );
102        m_commandInterpreter.registerCommand( "!shutdowncore", acceptedMessages, this, "handleShutdownCommand", accessRequired );
103        m_commandInterpreter.registerCommand( "!smartshutdown", acceptedMessages, this, "handleSmartShutdownCommand", accessRequired );
104        m_commandInterpreter.registerCommand( "!shutdownidlebots", acceptedMessages, this, "handleShutdownIdleBotsCommand", accessRequired );
105        m_commandInterpreter.registerCommand( "!shutdownallbots", acceptedMessages, this, "handleShutdownAllBotsCommand", accessRequired );
106       
107        m_commandInterpreter.registerDefaultCommand( Message.PRIVATE_MESSAGE, this, "handleInvalidMessage" );
108        m_commandInterpreter.registerDefaultCommand( Message.REMOTE_PRIVATE_MESSAGE, this, "handleInvalidMessage" );
109    }
110
111    /**
112     * Runs necessary setup, loads all access lists, and adds all bots in
113     * autoload.cfg to the bot loading queue.
114     */
115    public void handleEvent( LoggedOn event ){
116        // Stores the unix timestamp for use with !uptime
117        spawnutime = new Date().getTime();
118
119        m_botAction.joinArena( m_botAction.getGeneralSettings().getString("Arena") );
120       
121        // This is for the Message class where the alertcommands are stored
122        m_botAction.sendUnfilteredPublicMessage( "*g*misc:alertcommand" );
123        m_botAction.sendUnfilteredPublicMessage( "?chat=" + m_botAction.getGeneralSettings().getString( "Chat name" ) );
124        initOperators();
125        autoSpawnBots(false);
126       
127    }
128   
129    /**
130     * Reads bot types from autoload.cfg and spawns the bots
131     */
132    private void autoSpawnBots(boolean checkAlreadySpawned) {
133        try {
134            BufferedReader reader = new BufferedReader( new FileReader( m_botAction.getCoreCfg( "autoload.cfg" ) ) );
135            LinkedHashMap<String, Integer> autoLoads = new LinkedHashMap<String, Integer>();
136            // using LinkedHashMap implementation so the order of autoload.cfg is used when spawning bots
137           
138            String line = "";
139           
140            while( (line = reader.readLine()) != null ){
141                if( line == null || line.length() == 0 ){
142                    continue;
143                }
144               
145                if( !line.startsWith("#") && !line.startsWith("[") ){
146                    String bottype = line.trim().toLowerCase();
147                   
148                    // Add bot to TreeMap or increase the number of bots on the TreeMap
149                    if(autoLoads.containsKey(bottype)) {
150                        autoLoads.put(bottype, autoLoads.get(bottype)+1);
151                    } else {
152                        autoLoads.put(bottype, 1);
153                    }
154                }
155            }
156           
157            for(String bottype : autoLoads.keySet()) {
158                int number = autoLoads.get(bottype);
159                int alreadySpawned = 0;
160               
161                if(checkAlreadySpawned) {
162                    // Check the botQueue how many times the bot is already spawned
163                    alreadySpawned = m_botQueue.getBotCount(bottype);
164                }
165               
166                if(alreadySpawned < number) {
167                    for(int i = 0 ; i < (number - alreadySpawned) ; i++) {
168                        m_botQueue.spawnBot( bottype, null );
169                    }
170                }
171            }
172        } catch( FileNotFoundException fnfe ){
173            Tools.printStackTrace( "File not found: autoload.cfg  ;  ", fnfe );
174        } catch( IOException ioe) {
175            Tools.printStackTrace( "IOException occured while reading autoload.cfg : ", ioe);
176        }
177    }
178
179    /**
180     * Sends online message once arena has been joined successfully.
181     */
182    public void handleEvent( ArenaJoined event ){
183        m_botAction.sendChatMessage(m_botAction.getBotName() + " bot hub is now online.");
184    }
185
186    /**
187     * Sends any messages received to the command interpreter for handling.
188     * @param event Event received
189     */
190    public void handleEvent( Message event ){
191        m_commandInterpreter.handleEvent( event );
192    }
193
194    /**
195     * After receiving access lists, parses them using the operator list reader.
196     * @param event Event received
197     */
198    public void handleEvent( FileArrived event ){
199
200        // Auto assign operators after the file has been downloaded from subgame
201        if(     event.getFileName().equals( "moderate.txt" ) || 
202                event.getFileName().equals( "smod.txt" ) ||
203                event.getFileName().equals( "sysop.txt" )) {
204            m_botAction.getOperatorList().autoAssignFile( m_botAction.getDataFile( event.getFileName() ) );
205           
206        }
207    }
208
209    /**
210     * Clears all current access lists, and sets up the new lists based on access
211     * CFG files and the three server-based access lists.
212     */
213    public void initOperators(){
214        Tools.printLog("Initializing operators.cfg ...");
215       
216        try {
217            m_botAction.getOperatorList().clear();
218            m_botAction.getOperatorList().init( m_botAction.getCoreCfg("operators.cfg" ) );
219           
220        } catch (IOException ioe) {
221            System.err.println("FATAL: IO Exception occured while initializing operators from operators.cfg: "+ ioe.getMessage());
222            System.err.println("FATAL: No operators loaded, shutting down TWCore.");
223            m_botAction.die();
224        }
225       
226        Tools.printLog("Done initializing operators from operators.cfg");
227       
228        // Initiate process to auto-assign operators using the subgame staff files
229        m_botAction.sendUnfilteredPublicMessage( "*getfile sysop.txt" );
230        m_botAction.sendUnfilteredPublicMessage( "*getfile smod.txt" );
231        m_botAction.sendUnfilteredPublicMessage( "*getfile moderate.txt" );
232       
233       
234    }
235
236    /**
237     * Sends a line to bot development chat when a message not matching any
238     * valid command is received.
239     * @param messager Name of the player who sent the message
240     * @param message Text of the message
241     */
242    public void handleInvalidMessage( String messager, String message ){
243        m_botAction.sendChatMessage( 1, messager + " said this: " + message );
244    }
245
246    /**
247     * Removes a bot based on login name.  The case must be exact.
248     * @param messager Name of the player who sent the command
249     * @param message Bot to remove
250     */
251    public void handleRemove( String messager, String message ){
252        message = message.trim();
253
254        m_botAction.sendPrivateMessage( messager, "attempting to remove " + message + "...  " + (m_botAction.getGeneralSettings().getInt( "FastDisconnect" ) == 0?"  This may take 30 seconds or more.":"" ) );
255       
256        String operationSuccess = m_botQueue.removeBot( message, "force-disconnected by " + messager );
257        if( operationSuccess.startsWith("has disconnected") ) {
258            m_botAction.sendPrivateMessage( messager, "'" + message + "' removed successfully." );
259            m_botAction.sendChatMessage( 1, messager + " force-disconnected " + message + ".");
260            System.gc();
261        } else {
262            m_botAction.sendPrivateMessage( messager, "Bot has NOT been removed.  Use exact casing of the name, i.e., !remove TWDBot." );
263        }
264    }
265
266    /**
267     * Removes all bots of a given type.
268     * @param messager Name of the player who sent the command
269     * @param message Type of bot to remove
270     */
271    public void handleRemoveType( String messager, String message ){
272        message = message.trim();
273
274        m_botAction.sendPrivateMessage( messager, "Removing all bots of type " + message + "." +
275                (m_botAction.getGeneralSettings().getInt( "FastDisconnect" ) == 0?"  This may take on average 30 seconds per bot ...":"" ) );
276        m_botAction.sendChatMessage( 1, messager + " is force-disconnecting all bots of type " + message );
277        m_botQueue.hardRemoveAllBotsOfType( message, messager );
278        m_botAction.sendPrivateMessage( messager, "Removed all bots of type " + message + " (if possible).  Count reset to 0." );
279        System.gc();
280    }
281
282    /**
283     * Displays in PM the list of bots waiting to be spawned.
284     * @param messager Name of the player who sent the request
285     * @param message Text of the message following the command
286     */
287    public void handleShowWaitingList( String messager, String message ){
288        m_botQueue.listWaitingList( messager );
289    }
290
291    /**
292     * Sends a request to update all access levels based on access files.
293     * @param messager Name of the player who sent the command
294     * @param message Text of the message
295     */
296    public void handleUpdateAccess( String messager, String message ){
297        initOperators();
298        m_botAction.sendSmartPrivateMessage( messager, "Updating access levels..." );
299        m_botAction.sendChatMessage( 1, "Updating access levels at " + messager + "'s request" );
300    }
301
302    /**
303     * Lists bot names of a given bot type
304     * @param messager Name of the player who sent the command
305     * @param message Bot type to list
306     */
307    public void handleListBots( String messager, String message ){
308        String className = message.trim();
309
310        if( className.length() > 0 ){
311            m_botQueue.listBots( className, messager );
312        } else {
313                m_botQueue.listBotTypes( messager );
314        }
315    }
316
317    /**
318     * Lists operators registered with this hub/bots
319     * @param messager Name of the player who sent the command
320     * @param message Bot type to list
321     */
322    public void handleListOperators( String messager, String message ){
323        int linelength = 63;
324
325        OperatorList list = m_botAction.getOperatorList();
326
327        // Owners
328        HashSet<String> owners = (list.getAllOfAccessLevel(OperatorList.OWNER_LEVEL));
329        m_botAction.sendSmartPrivateMessage( messager, "Owners ("+owners.size()+")");
330        if(owners.size() > 0) {
331                String pm = "  ";
332
333                for(String owner:owners) {
334                        if(pm.length() < linelength) {
335                                pm += owner + ", ";
336                        } else {
337                                m_botAction.sendSmartPrivateMessage(messager, pm);
338                                pm = "  ";
339                        }
340                }
341                if(pm.length()>0) {
342                        m_botAction.sendSmartPrivateMessage(messager, pm);
343                }
344        }
345
346        // Sysops
347        HashSet<String> sysops = (list.getAllOfAccessLevel(OperatorList.SYSOP_LEVEL));
348        m_botAction.sendSmartPrivateMessage( messager, " ");
349                m_botAction.sendSmartPrivateMessage( messager, "System operators ("+sysops.size()+")");
350        if(sysops.size() > 0) {
351                String pm = "  ";
352
353                for(String sysop:sysops) {
354                        if(pm.length() < linelength) {
355                                pm += sysop + ", ";
356                        } else {
357                                m_botAction.sendSmartPrivateMessage(messager, pm);
358                                pm = "  ";
359                        }
360                }
361                if(pm.length()>0) {
362                        m_botAction.sendSmartPrivateMessage(messager, pm);
363                }
364        }
365
366        // Smods
367        HashSet<String> smods = (list.getAllOfAccessLevel(OperatorList.SMOD_LEVEL));
368        m_botAction.sendSmartPrivateMessage( messager, " ");
369                m_botAction.sendSmartPrivateMessage( messager, "Super Moderators ("+smods.size()+")");
370        if(smods.size() > 0) {
371                String pm = "  ";
372
373                for(String smod:smods) {
374                        if(pm.length() < linelength) {
375                                pm += smod + ", ";
376                        } else {
377                                m_botAction.sendSmartPrivateMessage(messager, pm);
378                                pm = "  ";
379                        }
380                }
381                if(pm.length()>0) {
382                        m_botAction.sendSmartPrivateMessage(messager, pm);
383                }
384        }
385       
386        // Developers
387        HashSet<String> devs = (list.getAllOfAccessLevel(OperatorList.DEV_LEVEL));
388        m_botAction.sendSmartPrivateMessage( messager, " ");
389                m_botAction.sendSmartPrivateMessage( messager, "Developers ("+devs.size()+")");
390        if(devs.size() > 0) {
391                String pm = "  ";
392
393                for(String dev:devs) {
394                        if(pm.length() < linelength) {
395                                pm += dev + ", ";
396                        } else {
397                                m_botAction.sendSmartPrivateMessage(messager, pm);
398                                pm = "  ";
399                        }
400                }
401                if(pm.length()>0) {
402                        m_botAction.sendSmartPrivateMessage(messager, pm);
403                }
404        }
405       
406        // Highmods
407        HashSet<String> highmods = (list.getAllOfAccessLevel(OperatorList.HIGHMOD_LEVEL));
408        m_botAction.sendSmartPrivateMessage( messager, " ");
409                m_botAction.sendSmartPrivateMessage( messager, "High Moderators ("+highmods.size()+")");
410        if(highmods.size() > 0) {
411                String pm = "  ";
412
413                for(String highmod:highmods) {
414                        if(pm.length() < linelength) {
415                                pm += highmod + ", ";
416                        } else {
417                                m_botAction.sendSmartPrivateMessage(messager, pm);
418                                pm = "  ";
419                        }
420                }
421                if(pm.length()>0) {
422                        m_botAction.sendSmartPrivateMessage(messager, pm);
423                }
424        }
425
426        // Mods
427        HashSet<String> mods = (list.getAllOfAccessLevel(OperatorList.MODERATOR_LEVEL));
428        m_botAction.sendSmartPrivateMessage( messager, " ");
429                m_botAction.sendSmartPrivateMessage( messager, "Moderators ("+mods.size()+")");
430        if(mods.size() > 0) {
431                String pm = "  ";
432
433                for(String mod:mods) {
434                        if(pm.length() < linelength) {
435                                pm += mod + ", ";
436                        } else {
437                                m_botAction.sendSmartPrivateMessage(messager, pm);
438                                pm = "  ";
439                        }
440                }
441                if(pm.length()>0) {
442                        m_botAction.sendSmartPrivateMessage(messager, pm);
443                }
444        }
445
446        // ERs
447        HashSet<String> ers = (list.getAllOfAccessLevel(OperatorList.ER_LEVEL));
448        m_botAction.sendSmartPrivateMessage( messager, " ");
449                m_botAction.sendSmartPrivateMessage( messager, "ERs ("+ers.size()+")");
450        if(ers.size() > 0) {
451                String pm = "  ";
452
453                for(String er:ers) {
454                        if(pm.length() < linelength) {
455                                pm += er + ", ";
456                        } else {
457                                m_botAction.sendSmartPrivateMessage(messager, pm);
458                                pm = "  ";
459                        }
460                }
461                if(pm.length()>0) {
462                        m_botAction.sendSmartPrivateMessage(messager, pm);
463                }
464        }
465       
466        // Outsiders
467        HashSet<String> outsiders = (list.getAllOfAccessLevel(OperatorList.OUTSIDER_LEVEL));
468        m_botAction.sendSmartPrivateMessage( messager, " ");
469                m_botAction.sendSmartPrivateMessage( messager, "Outsiders ("+outsiders.size()+")");
470        if(outsiders.size() > 0) {
471                String pm = "  ";
472
473                for(String outsider:outsiders) {
474                        if(pm.length() < linelength) {
475                                pm += outsider + ", ";
476                        } else {
477                                m_botAction.sendSmartPrivateMessage(messager, pm);
478                                pm = "  ";
479                        }
480                }
481                if(pm.length()>0) {
482                        m_botAction.sendSmartPrivateMessage(messager, pm);
483                }
484        }
485
486        // Bots
487        HashSet<String> bots = (list.getAllOfAccessLevel(OperatorList.BOT_LEVEL));
488        m_botAction.sendSmartPrivateMessage( messager, " ");
489                m_botAction.sendSmartPrivateMessage( messager, "Operators that have identified themselves as bots ("+bots.size()+")");
490        if(bots.size() > 0) {
491                String pm = "  ";
492
493                for(String bot:bots) {
494                        if(pm.length() < linelength) {
495                                pm += bot + ", ";
496                        } else {
497                                m_botAction.sendSmartPrivateMessage(messager, pm);
498                                pm = "  ";
499                        }
500                }
501                if(pm.length()>0) {
502                        m_botAction.sendSmartPrivateMessage(messager, pm);
503                }
504        }
505    }
506
507    /**
508     * Returns the uptime of this bot
509     * @param messager Name of the player who sent the command
510     * @param message is irrelevant
511     */
512    public void handleUptimeCommand( String messager, String message ){
513        // Calculate the uptime by using the spawnutime variable (ms)
514        m_botAction.sendSmartPrivateMessage(messager, "Uptime: "+ Tools.getTimeDiffString(spawnutime, false) + ".");
515    }
516
517    /**
518     * Shuts down the core immediately.
519     * @param messager Name of the player who sent the command
520     * @param message is irrelevant
521     */
522    public void handleBillerDownCommand( String messager, String message ){
523        if( !m_botAction.getOperatorList().isModerator( messager ) ) {
524            if( !message.equals("off") && !message.equals(m_botAction.getGeneralSettings().getNonNullString("BillerDownPassword"))) {
525                m_botAction.sendSmartPrivateMessage( messager, "Invalid password." );
526                m_botAction.sendChatMessage( 1, messager + " provided an invalid password for sending out the biller down message.");
527                return;
528            }
529        }
530
531        if( message.equals("off") ) {
532            if( m_billerDownTask == null ) {
533                m_botAction.sendSmartPrivateMessage( messager, "There is no biller down zone message currently being sent out." );
534                return;
535            }
536            try {
537                if( m_botAction.cancelTask(m_billerDownTask) == false )
538                    m_billerDownTask.cancel();
539            } catch (Exception e) {
540                m_botAction.sendSmartPrivateMessage( messager, "Disable failed.  Try again in a few moments." );
541                return;
542            }
543            m_botAction.cancelTask(m_billerDownTask);
544            m_billerDownTask = null;
545            m_botAction.sendSmartPrivateMessage( messager, "Biller down zone message disabled.  '!billerdown <password>' to turn back on." );
546            m_botAction.sendChatMessage( "Biller down zone message disabled by " + messager + ".  '!billerdown <password>' to turn back on." );
547            return;
548        }
549
550        if( m_billerDownTask != null ) {
551            m_botAction.sendSmartPrivateMessage( messager, "Message is already being sent out.  Use '!billerdown off' to disable." );
552            return;
553        }
554
555        m_botAction.sendSmartPrivateMessage( messager, "Sending out the biller down zone message approximately every " + (m_billerDownRate / 60000.0) + " minutes.  '!billerdown off' to disable." );
556        m_botAction.sendChatMessage( "Biller down zone message initiated by " + messager + ".  Message sent out every " + (m_billerDownRate / 60000.0) + "minutes.  '!billerdown off' to disable." );
557        Tools.printLog( "Biller down zone message initiated by " + messager + "." );
558
559        m_billerDownTask = new TimerTask() {
560            public void run() {
561                m_botAction.sendZoneMessage( "NOTICE: The billing server (player database) is temporarily down (beyond our control).  ?commands (?chat etc) are disabled and entering players will have ^ before their name.  Please be patient until normal service is restored.", Tools.Sound.BEEP1 );
562            }
563        };
564        // Send first after one second (also confirming !billerdown command, while the rest every 5 minutes)
565        m_botAction.scheduleTaskAtFixedRate(m_billerDownTask, Tools.TimeInMillis.SECOND, m_billerDownRate);
566    }
567
568    /**
569     * Recycles the server if the player has the correct password.
570     * @param messager Name of the player who sent the command
571     * @param message is irrelevant
572     */
573    public void handleRecycleCommand( String messager, String message ){
574        if( !message.equals(m_botAction.getGeneralSettings().getNonNullString("RecyclePassword"))) {
575            m_botAction.sendSmartPrivateMessage( messager, "Invalid password." );
576            m_botAction.sendChatMessage( 1, messager + " provided an invalid password for recycling the server.");
577            return;
578        }
579        m_botAction.sendZoneMessage( "NOTICE: Server recycling all users to regain full functionality ... please log in again in a few moments. -TWStaff", Tools.Sound.BEEP1 );
580        m_botAction.sendUnfilteredPublicMessage("*recycle");
581    }
582
583    /**
584     * Shuts down the core immediately.
585     * @param messager Name of the player who sent the command
586     * @param message is irrelevant
587     */
588    public void handleShutdownCommand( String messager, String message ){
589        m_botAction.sendSmartPrivateMessage( messager, "Shutting down the core ..." +
590                (m_botAction.getGeneralSettings().getInt( "FastDisconnect" ) == 0?"  This may take on average 30 seconds per bot ...":"" ) );
591
592        m_botAction.sendChatMessage( 1, "--- CORE SHUTDOWN initiated by " + messager + " ---" );
593        System.out.println();
594        System.out.println( "=== Shutdown initiated ===" );
595        String upString = Tools.getTimeDiffString( spawnutime, true);
596        Tools.printLog( "Beginning shutdown by " + messager + ".");
597        Tools.printLog( "Total uptime: " + upString );
598        m_botQueue.shutdownAllBots();
599        m_botAction.sendChatMessage( 1, "--- Shutdown complete.  Uptime: " + upString + " ---" );
600        try {
601            Thread.sleep(3000);
602        } catch( InterruptedException e ){
603        }
604        m_botAction.die();
605    }
606
607    /**
608     * Shuts down the core gradually by disconnecting bots when they are no longer in use.
609     * @param messager Name of the player who sent the command
610     * @param message is irrelevant
611     */
612    public void handleSmartShutdownCommand( String messager, String message ){
613        m_botAction.sendSmartPrivateMessage( messager, "Beginning gradual shutdown of the core ..." );
614        m_botAction.sendChatMessage( 1, "--- SMART CORE SHUTDOWN initiated by " + messager + " ---" );
615        m_botAction.sendChatMessage( 1, "   (Bots will be disconnected as they become idle.)" );
616        System.out.println();
617        System.out.println( "=== Smart shutdown initiated ===" );
618        Tools.printLog( "Beginning smart shutdown by " + messager + ".");
619
620        m_smartShutdownTask = new TimerTask() {
621            public void run() {
622                if( m_botQueue.shutdownIdleBots() ) {
623                    String upString = Tools.getTimeDiffString( spawnutime, true);
624                    Tools.printLog( "Total uptime: " + upString );
625                    m_botAction.sendChatMessage( 1, "--- Smart Shutdown complete.  Uptime: " + upString + " ---" );
626                    this.cancel();
627                    try {
628                        Thread.sleep(3000);
629                    } catch( InterruptedException e ){
630                    }
631                    m_botAction.die();
632                }
633            }
634        };
635        m_botAction.scheduleTask(m_smartShutdownTask, 100, m_smartShutdownRate );
636
637    }
638
639    /**
640     * Shuts down any idle bots, without continuing to check for when busy bots become idle.
641     * @param messager Name of the player who sent the command
642     * @param message is irrelevant
643     */
644    public void handleShutdownIdleBotsCommand( String messager, String message ){
645        m_botAction.sendSmartPrivateMessage( messager, "Shutting down all idle bots ..." );
646        m_botAction.sendChatMessage( 1, "Idle bot shutdown initiated by " + messager + ".  All idle bots will be removed." );
647        Tools.printLog( "Idle bot shutdown by " + messager + ".");
648
649        m_botQueue.shutdownIdleBots();
650    }
651
652    /**
653     * Shuts down any idle bots, without continuing to check for when busy bots become idle.
654     * @param messager Name of the player who sent the command
655     * @param message is irrelevant
656     */
657    public void handleShutdownAllBotsCommand( String messager, String message ){
658        m_botAction.sendSmartPrivateMessage( messager, "Shutting down all bots ..." );
659        m_botAction.sendChatMessage( 1, "Full bot shutdown initiated by " + messager + ".  All bots will be removed, but the Hub will be left online." );
660        Tools.printLog( "Full bot shutdown by " + messager + ".");
661
662        m_botQueue.shutdownAllBots();
663    }
664
665    /**
666     * Sends an appropriate help message based on access privileges.
667     * @param messager Name of the player who sent the command
668     * @param message Text of the message
669     */
670    public void handleHelp( String messager, String message ) {
671        OperatorList operatorList = m_botAction.getOperatorList();
672        int accessLevel = operatorList.getAccessLevel(messager);
673       
674        int accessCommandNumber = m_commandInterpreter.getAllowedCommandsCount(accessLevel)-1; // excluding !help
675        int totalCommandsNumber = m_commandInterpreter.getCommandsCount()-1; // excluding !help
676        String argument = message.replace("!", "").trim();
677       
678        // Access: Sysop [lvl 5]
679        // You have access to 12 / 18 commands.
680        // +-------------------------==  BOT CONTROL  ==--------------------------+
681        // | !spawn  !spawnmax  !spawnauto  !forcespawn  !waitinglist  !listbots  |
682        // | !remove  !removetype                                                 |
683        // | !shutdowncore  !smartshutdown  !shutdownidlebots  !shutdownallbots   |
684        // |                                                                      |
685        // +----------------------==  ACCESS MANAGEMENT  ==-----------------------+
686        // | !updateaccess  !listoperators                                        |
687        // |                                                                      |
688        // +--------------------==  SERVER TROUBLESHOOTING  ==--------------------+
689        // | !billerdown  !recycleserver                                          |
690        // |                                                                      |
691        // +---------------------------==  STATUS  ==-----------------------------+
692        // | !uptime  !dbstatus                                                   |
693        // |                                                                      |
694        // +-----------------------------------------------------TWCore rev 4444--+
695        // Private message '!help <command>' for more information.
696       
697       
698        // Access: Sysop [lvl 5]
699        // You have access to 12 / 18 commands.
700        // -----------------------------------------------
701        // BOT CONTROL:      !spawn !spawnmax !spawnauto !forcespawn !waitinglist !listbots
702        //                   !remove !removetype
703        //                   !shutdowncore !smartshutdown !shutdownidlebots !shutdownallbots
704        // ACCESS CONTROL:   !updateaccess !listoperators
705        // SERVER TROUBLESH: !billerdown !recycleserver
706        // STATUS:           !uptime !dbstatus !version
707        // -----------------------------------------------
708        // Private message '!help <command>' for more information.
709       
710
711        if(argument.length() == 0) {
712                        m_botAction.sendSmartPrivateMessage( messager, "Access: "+operatorList.getAccessLevelName(accessLevel) );
713                        m_botAction.sendSmartPrivateMessage( messager, "You have access to "+accessCommandNumber+" / "+totalCommandsNumber+" commands.");
714               
715                        m_botAction.sendSmartPrivateMessage( messager, "-----------------------------------------------");
716                if(operatorList.isOutsider(messager) && !operatorList.isHighmod(messager)) {
717                        m_botAction.sendSmartPrivateMessage( messager, "BOT CONTROL:      !spawn !waitinglist !listbots");
718                }
719                if(operatorList.isHighmod(messager) && !operatorList.isSysop(messager)) {
720                        m_botAction.sendSmartPrivateMessage( messager, "BOT CONTROL:      !spawn !spawnmax !spawnauto !waitinglist !listbots");
721                        m_botAction.sendSmartPrivateMessage( messager, "                  !remove  !removetype");
722                }
723                if(operatorList.isSysop(messager)) {
724                        m_botAction.sendSmartPrivateMessage( messager, "BOT CONTROL:      !spawn !spawnmax !spawnauto !forcespawn !waitinglist !listbots");
725                        m_botAction.sendSmartPrivateMessage( messager, "                  !remove !removetype");
726                        m_botAction.sendSmartPrivateMessage( messager, "                  !shutdowncore !smartshutdown !shutdownidlebots !shutdownallbots");
727                }
728                if(operatorList.isSmod(messager)) {
729                        m_botAction.sendSmartPrivateMessage( messager, "ACCESS CONTROL:   !updateaccess !listoperators");
730                }
731                if(operatorList.isOutsider(messager) && !operatorList.isSmod(messager))
732                        m_botAction.sendSmartPrivateMessage( messager, "SERVER TROUBLESH: !billerdown");
733                if(operatorList.isSmod(messager))
734                        m_botAction.sendSmartPrivateMessage( messager, "SERVER TROUBLESH: !billerdown !recycleserver");
735                if(operatorList.isModerator(messager)) {
736                        m_botAction.sendSmartPrivateMessage( messager, "STATUS:           !uptime !dbstatus !version");
737                }
738                        m_botAction.sendSmartPrivateMessage( messager, "-----------------------------------------------");
739                        m_botAction.sendSmartPrivateMessage( messager, "Message ::!help <command> for more information.");
740                       
741        } else {
742                // Command details
743                // !help
744                if(argument.equalsIgnoreCase("help")) {
745                        m_botAction.sendSmartPrivateMessage( messager , "Displays available commands or shows extra information when a command has been specified.");
746                        m_botAction.sendSmartPrivateMessage( messager , "Symbols used in command information: <> required  [] optional  -s[=assign] switches");
747                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.OUTSIDER_LEVEL));
748                        m_botAction.sendSmartPrivateMessage( messager , " !help [command]");
749                }
750                // ----- Bot control command details ------
751                // !spawn
752                else if (argument.equalsIgnoreCase("spawn")) {
753                        m_botAction.sendSmartPrivateMessage( messager , "Spawns a new bot of the specified bot type.");
754                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.OUTSIDER_LEVEL));
755                        m_botAction.sendSmartPrivateMessage( messager , " !spawn <bot type>");
756                       
757                }
758                // !spawnmax
759                else if (argument.equalsIgnoreCase("spawnmax")) {
760                        m_botAction.sendSmartPrivateMessage( messager , "Spawns the maximum allowed number of bots of the specified bot type.");
761                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.HIGHMOD_LEVEL));
762                        m_botAction.sendSmartPrivateMessage( messager , " !spawnmax <bot type>");
763                }
764                // !spawnauto
765                else if (argument.equalsIgnoreCase("spawnauto")) {
766                        m_botAction.sendSmartPrivateMessage( messager , "Spawns all bots of the specified bot type that should already be spawned on startup.");
767                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.HIGHMOD_LEVEL));
768                        m_botAction.sendSmartPrivateMessage( messager , " !spawnauto <bot type>");
769                }
770                // !forcespawn
771                else if (argument.equalsIgnoreCase("forcespawn")) {
772                        m_botAction.sendSmartPrivateMessage( messager , "Force-spawns a bot (ignore count) of specified bot type using the specified username and password.");
773                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.SYSOP_LEVEL));
774                        m_botAction.sendSmartPrivateMessage( messager , " !forcespawn <bot type> <username> <password>  ()");
775                }
776                // !waitinglist
777                else if (argument.equalsIgnoreCase("waitinglist")) {
778                        m_botAction.sendSmartPrivateMessage( messager , "Displays the list of bots that are waiting to be spawned.");
779                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.OUTSIDER_LEVEL));
780                        m_botAction.sendSmartPrivateMessage( messager , " !waitinglist");
781                }
782                // !listbots
783                else if (argument.equalsIgnoreCase("listbots")) {
784                        m_botAction.sendSmartPrivateMessage( messager , "Returns current spawned bot types or the spawned bots of specified bot type, including arena and who spawned it.");
785                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.OUTSIDER_LEVEL));
786                        m_botAction.sendSmartPrivateMessage( messager , " !listbots [bot type]");
787                }
788                // !remove
789                else if (argument.equalsIgnoreCase("remove")) {
790                        m_botAction.sendSmartPrivateMessage( messager , "Forces a removal of the specified bot from the zone.");
791                        m_botAction.sendSmartPrivateMessage( messager , "Note: You must use exact casing for the <botname>. For example, 'TWDBot'");
792                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.HIGHMOD_LEVEL));
793                        m_botAction.sendSmartPrivateMessage( messager , " !remove <botname>");
794                }
795                // !removetype
796                else if (argument.equalsIgnoreCase("removetype")) {
797                        m_botAction.sendSmartPrivateMessage( messager , "Forces a removal of all bots of the specified type from the zone and resets the bot's count.");
798                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.HIGHMOD_LEVEL));
799                        m_botAction.sendSmartPrivateMessage( messager , " !removetype <bottype>");
800                }
801                // !shutdowncore
802                else if (argument.equalsIgnoreCase("shutdowncore")) {
803                        m_botAction.sendSmartPrivateMessage( messager , "Performs a clean shutdown of all the bots and the entire core. Disable restart scripts first.");
804                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.SYSOP_LEVEL));
805                        m_botAction.sendSmartPrivateMessage( messager , " !shutdowncore");
806                }
807                // !smartshutdown
808                else if (argument.equalsIgnoreCase("smartshutdown")) {
809                        m_botAction.sendSmartPrivateMessage( messager , "Shuts down all bots as they become idle, then shuts down the core.");
810                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.SYSOP_LEVEL));
811                        m_botAction.sendSmartPrivateMessage( messager , " !smartshutdown");
812                }
813                // !shutdownidlebots
814                else if (argument.equalsIgnoreCase("shutdownidlebots")) {
815                        m_botAction.sendSmartPrivateMessage( messager , "Kills all idle bots, leaving any running bots and the hub online.");
816                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.SYSOP_LEVEL));
817                        m_botAction.sendSmartPrivateMessage( messager , " !shutdownidlebots");
818                }
819                // !shutdownallbots
820                else if (argument.equalsIgnoreCase("shutdownallbots")) {
821                        m_botAction.sendSmartPrivateMessage( messager , "Kills all running bots, leaving the hub online.");
822                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.SYSOP_LEVEL));
823                        m_botAction.sendSmartPrivateMessage( messager , " !shutdownallbots");
824                }
825                // !updateaccess
826                else if (argument.equalsIgnoreCase("updateaccess")) {
827                        m_botAction.sendSmartPrivateMessage( messager , "Rereads the moderate.txt, smod.txt, and sysop.txt server files so that all bot access levels are updated.");
828                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.SMOD_LEVEL));
829                        m_botAction.sendSmartPrivateMessage( messager , " !updateaccess");
830                }
831                // !listoperators
832                else if (argument.equalsIgnoreCase("listoperators")) {
833                        m_botAction.sendSmartPrivateMessage( messager , "Lists all registered operators on this bot and bot spawns.");
834                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.SMOD_LEVEL));
835                        m_botAction.sendSmartPrivateMessage( messager , " !listoperators");
836                }
837                // !billerdown
838                else if (argument.equalsIgnoreCase("billerdown")) {
839                        m_botAction.sendSmartPrivateMessage( messager , "Sends periodic message about biller being down. Moderators and higher are not required to enter a <password>.");
840                        m_botAction.sendSmartPrivateMessage( messager , "Specify 'off' to disable the periodic advertisement.");
841                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.OUTSIDER_LEVEL));
842                        m_botAction.sendSmartPrivateMessage( messager , " !billerdown <password>");
843                        m_botAction.sendSmartPrivateMessage( messager , " !billerdown off");
844                }
845                // !recycleserver
846                else if (argument.equalsIgnoreCase("recycleserver")) {
847                        m_botAction.sendSmartPrivateMessage( messager , "Recycles the server after correct password is given.");
848                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.SMOD_LEVEL));
849                        m_botAction.sendSmartPrivateMessage( messager , " !recycleserver <password>");
850                }
851                // !uptime
852                else if (argument.equalsIgnoreCase("uptime")) {
853                        m_botAction.sendSmartPrivateMessage( messager , "Returns the current uptime of this core.");
854                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.MODERATOR_LEVEL));
855                        m_botAction.sendSmartPrivateMessage( messager , " !uptime");
856                }
857                // !dbstatus
858                else if (argument.equalsIgnoreCase("dbstatus")) {
859                        m_botAction.sendSmartPrivateMessage( messager , "Shows status of database connections.");
860                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.MODERATOR_LEVEL));
861                        m_botAction.sendSmartPrivateMessage( messager , " !dbstatus");
862                }
863                // !version
864                else if (argument.equalsIgnoreCase("version")) {
865                        m_botAction.sendSmartPrivateMessage( messager , "Shows the revision number of this bot.");
866                        m_botAction.sendSmartPrivateMessage( messager , "Access required: " + operatorList.getAccessLevelName(OperatorList.MODERATOR_LEVEL));
867                        m_botAction.sendSmartPrivateMessage( messager , " !version");
868                }
869                else {
870                        m_botAction.sendSmartPrivateMessage( messager , "Syntax error. Please message !help <command> for more information.");
871                }
872        }
873    }
874
875    /**
876     * Spawns a bot of a given type.  User interface wrapper for spawn().
877     * @param messager Name of the player who sent the command
878     * @param message Bot type to spawn
879     */
880    public void handleSpawnMessage( String messager, String message ){
881        String className = message.trim();
882       
883        if( className.length() > 0 ){
884            m_botQueue.spawnBot( className, messager );
885        } else {
886            m_botAction.sendSmartPrivateMessage( messager, "Usage: !spawn <bot type>" );
887        }
888    }
889
890    /**
891     * Forces the spawn of a bot of a given type by manually supplying a login.
892     * @param messager Name of the player who sent the command
893     * @param message Bot to spawn and relevant login info
894     */
895    public void handleForceSpawnMessage( String messager, String message ){
896        String args[] = message.split( " " );
897        if( args.length == 3 ) {
898            String className = args[0];
899            String login = args[1];
900            String password = args[2];
901            m_botQueue.spawnBot( className, login, password, messager );
902        } else {
903            m_botAction.sendSmartPrivateMessage( messager, "Usage: !forcespawn <bot type> <login> <password>" );
904        }
905    }
906   
907    /**
908     * Spawns the maximum number of a bot of a given type.
909     * @param messager Name of the player who sent the command
910     * @param message Bot type to spawn
911     */
912    public void handleSpawnMaxMessage( String messager, String message ){
913        String bottype = message.toLowerCase().trim();
914        BotSettings botInfo = m_botAction.getCoreData().getBotConfig(bottype);
915        Integer maxBots = botInfo.getInteger("Max Bots");
916       
917        if( botInfo == null ){
918            m_botAction.sendChatMessage( 1, messager + " tried to spawn bot of type " + message + ".  Invalid bot type or missing CFG file." );
919            m_botAction.sendSmartPrivateMessage( messager, "That bot type does not exist, or the CFG file for it is missing." );
920            return;
921        }
922        if( maxBots == null ){
923            m_botAction.sendChatMessage( 1, messager + " tried to spawn bot of type " + message + ".  Invalid settings file. (MaxBots improperly defined)" );
924            m_botAction.sendSmartPrivateMessage( messager, "The CFG file for that bot type is invalid. (MaxBots improperly defined)" );
925            return;
926        }
927        if( m_botQueue.getBotCount(message.toLowerCase()) >= maxBots){
928                m_botAction.sendChatMessage( 1, messager + " tried to spawn a new bot of type " + message + ".  Maximum number already reached (" + maxBots + ")");
929                m_botAction.sendSmartPrivateMessage( messager, "Maximum number of bots of this type (" + maxBots + ") has been reached." );
930                return;
931        }
932        m_botAction.sendSmartPrivateMessage( messager, "Spawning the maximum allowed number bots of type " + message.toLowerCase());
933        m_botAction.sendChatMessage( 1, messager + " is in queue to spawn the maximum allowed number bots of type " + message.toLowerCase());
934       
935        if( bottype.length() > 0 ) {
936            while(m_botQueue.getBotCount(bottype) < maxBots)
937                m_botQueue.spawnBot( bottype, null);
938        } else {
939            m_botAction.sendSmartPrivateMessage( messager, "Usage: !spawnmax <bot type>" );
940        }
941    }
942   
943    /**
944     * Spawns all bots on the autoloader that aren't currently spawned.
945     * @param messager
946     * @param message
947     */
948    public void handleSpawnAutoMessage( String messager, String message ){
949        m_botAction.sendSmartPrivateMessage( messager, "Spawning all bots from the autoloader that are not currently spawned.");
950        m_botAction.sendChatMessage( 1, messager + " is in queue to spawn all bots from the autoloader.");
951       
952        autoSpawnBots(true);
953       
954        m_botAction.sendSmartPrivateMessage( messager, "Done spawning all bots from the autoloader.");
955    }
956   
957    /**
958     * Displays the current SQL pool status.
959     * @param messager
960     * @param message
961     */
962    public void handleDbStatus( String messager, String message ) {
963        m_botAction.smartPrivateMessageSpam( messager, m_botAction.getCoreData().getSQLManager().getPoolStatus() );
964    }
965   
966    /**
967     * Displays the revision number of this file
968     * @param messager
969     * @param message
970     */
971    public void handleVersion( String messager, String message ) {
972        // Sample output of Revision keyword: $Revision$
973        String version = "$Revision$".substring(11).replace(" $","");
974        m_botAction.sendSmartPrivateMessage( messager , "TWCore revision "+version);
975        m_botAction.sendSmartPrivateMessage( messager , "More information about the latest change on http://www.twcore.org/changeset/"+version);
976    }
977}
Note: See TracBrowser for help on using the browser.