/* * Copyright (C) 2005,2006,2007 MaNGOS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /// \addtogroup mangosd /// @{ /// \file #include "Common.h" #include "Language.h" #include "Log.h" #include "World.h" #include "ScriptCalls.h" #include "GlobalEvents.h" #include "ObjectMgr.h" #include "WorldSession.h" #include "SystemConfig.h" #include "Config/ConfigEnv.h" #include "Util.h" #include "AccountMgr.h" #include "CliRunnable.h" #include "MapManager.h" //CliCommand and CliCommandHolder are defined in World.h to avoid cyclic deps //func prototypes must be defined void CliHelp(char*,pPrintf); void CliInfo(char*,pPrintf); void CliBan(char*,pPrintf); void CliBanList(char*,pPrintf); void CliRemoveBan(char*,pPrintf); void CliSetGM(char*,pPrintf); void CliListGM(char*,pPrintf); void CliVersion(char*,pPrintf); void CliExit(char*,pPrintf); void CliIdleShutdown(char*,pPrintf zprintf); void CliShutdown(char*,pPrintf zprintf); void CliBroadcast(char*,pPrintf); void CliCreate(char*,pPrintf); void CliDelete(char*,pPrintf); void CliLoadScripts(char*,pPrintf); void CliKick(char*,pPrintf); void CliTele(char*,pPrintf); void CliMotd(char*,pPrintf); void CliCorpses(char*,pPrintf); void CliSetLogLevel(char*,pPrintf); void CliUpTime(char*,pPrintf); void CliSetTBC(char*,pPrintf); void CliWritePlayerDump(char*,pPrintf); void CliLoadPlayerDump(char*,pPrintf); void CliSave(char*,pPrintf); void CliSend(char*,pPrintf); void CliPLimit(char*,pPrintf); /// Table of known commands const CliCommand Commands[]= { {"help", & CliHelp,"Display this help message"}, {"broadcast", & CliBroadcast,"Announce in-game message"}, {"create", & CliCreate,"Create account"}, {"delete", & CliDelete,"Delete account and characters"}, {"info", & CliInfo,"Display Server infomation"}, {"uptime", & CliUpTime, "Displays the server uptime"}, {"motd", & CliMotd,"Change or display motd"}, {"kick", & CliKick,"Kick user"}, {"ban", & CliBan,"Ban account|ip"}, {"listbans", & CliBanList,"List bans"}, {"unban", & CliRemoveBan,"Remove ban from account|ip"}, {"setgm", & CliSetGM,"Edit user privileges"}, {"setbc", & CliSetTBC,"Set user expansion allowed"}, {"listgm", & CliListGM,"Display user privileges"}, {"loadscripts", & CliLoadScripts,"Load script library"}, {"setloglevel", & CliSetLogLevel,"Set Log Level"}, {"corpses", & CliCorpses,"Manually call corpses erase global even code"}, {"version", & CliVersion,"Display server version"}, {"idleshutdown", & CliIdleShutdown,"Shutdown server with some delay when not active connections at server"}, {"shutdown", & CliShutdown,"Shutdown server with some delay"}, {"exit", & CliExit,"Shutdown server NOW"}, {"writepdump", &CliWritePlayerDump,"Write a player dump to a file"}, {"loadpdump", &CliLoadPlayerDump,"Load a player dump from a file"}, {"saveall", &CliSave,"Save all players"}, {"send", &CliSend,"Send message to a player"}, {"tele", &CliTele,"Teleport player to location"}, {"plimit", &CliPLimit,"Show or set player login limitations"} }; /// \todo Need some pragma pack? Else explain why in a comment. #define CliTotalCmds sizeof(Commands)/sizeof(CliCommand) /// Create a character dump file void CliWritePlayerDump(char*command,pPrintf zprintf) { char * file = strtok(command, " "); char * p2 = strtok(NULL, " "); if(!file || !p2) { zprintf("Syntax is: writepdump $filename $playerGUID\r\n"); return; } objmgr.WritePlayerDump(file, atoi(p2)); } /// Load a character from a dump file void CliLoadPlayerDump(char*command,pPrintf zprintf) { char * file = strtok(command, " "); char * acc = strtok(NULL, " "); if (!file ||!acc) { zprintf("Syntax is: loadpdump $filename $account ($newname) ($newguid)\r\n"); return; } char * name = strtok(NULL, " "); char * guid = name ? strtok(NULL, " ") : NULL; if(objmgr.LoadPlayerDump(file, atoi(acc), name ? name : "", guid ? atoi(guid) : 0)) zprintf("Character loaded successfully!"); else zprintf("Failed to load the character!"); } /// Reload the scripts and notify the players void CliLoadScripts(char*command,pPrintf zprintf) { char const *del=strtok(command," "); if (!del) del=""; if(!LoadScriptingModule(del)) // Error report is already done by LoadScriptingModule return; sWorld.SendWorldText("|cffff0000[System Message]:|rScripts reloaded", NULL); } /// Delete a user account and all associated characters in this realm /// \todo This function has to be enhanced to respect the login/realm split (delete char, delete account chars in realm, delete account chars in realm then delete account void CliDelete(char*command,pPrintf zprintf) { ///- Get the account name from the command line char *account_name=strtok(command," "); if(!account_name) { // \r\n is used because this function can also be called from RA zprintf("Syntax is: delete $account\r\n"); return; } int result = accmgr.DeleteAccount(accmgr.GetId(account_name)); if(result == -1) zprintf("User %s NOT deleted (probably sql file format was updated)\r\n",account_name); if(result == 1) zprintf("User %s does not exist\r\n",account_name); else if(result == 0) zprintf("We deleted account: %s\r\n",account_name); } /// Broadcast a message to the World void CliBroadcast(char *text,pPrintf zprintf) { std::string str = LANG_SYSTEMMESSAGE; str += text; sWorld.SendWorldText(str.c_str(), NULL); zprintf("Broadcasting to the world:%s\r\n",str.c_str()); } /// Print the list of commands and associated description void CliHelp(char*,pPrintf zprintf) { for (unsigned int x=0;x 0"); if (!resultDB) { int maxUsers = sWorld.GetMaxSessionCount(); std::string timeStr = secsToTimeString(sWorld.GetUptime(),true); zprintf("Online users: 0 (max: %d) Uptime: %s\r\n",maxUsers,timeStr.c_str()); return; } int linesize = 1+15+2+20+3+15+2+4+1+5+3; // see format string char* buf = new char[resultDB->GetRowCount()*linesize+1]; char* bufPos = buf; ///- Circle through accounts do { Field *fieldsDB = resultDB->Fetch(); std::string name = fieldsDB[0].GetCppString(); uint32 account = fieldsDB[1].GetUInt32(); ///- Get the username, last IP and GM level of each account // No SQL injection. account is uint32. // 0 1 2 3 QueryResult *resultLogin = loginDatabase.PQuery("SELECT `username`,`last_ip`,`gmlevel`,`tbc` FROM `account` WHERE `id` = '%u'",account); if(resultLogin) { Field *fieldsLogin = resultLogin->Fetch(); bufPos+=sprintf(bufPos,"|%15s| %20s | %15s |%4d|%5d|\r\n", fieldsLogin[0].GetString(),name.c_str(),fieldsLogin[1].GetString(),fieldsLogin[2].GetUInt32(),fieldsLogin[3].GetUInt32()); delete resultLogin; } else bufPos += sprintf(bufPos,"| | %20s | |||\r\n",name.c_str()); }while(resultDB->NextRow()); *bufPos = '\0'; ///- Display the list of account/characters online std::string timeStr = secsToTimeString(sWorld.GetUptime(),true); uint32 maxUsers = sWorld.GetMaxSessionCount(); zprintf("Online users: %u (max: %u) Uptime: %s\r\n",uint32(resultDB->GetRowCount()),maxUsers,timeStr.c_str()); zprintf("=====================================================================\r\n"); zprintf("| Account | Character | IP | GM | TBC |\r\n"); zprintf("=====================================================================\r\n"); zprintf("%s",buf); zprintf("=====================================================================\r\n"); delete resultDB; delete[] buf; } /// Display a list of banned accounts and ip addresses void CliBanList(char*,pPrintf zprintf) { ///- Get the list of banned accounts and display them Field *fields; QueryResult *result = loginDatabase.Query("SELECT `id`,`username` FROM `account` WHERE `id` IN (SELECT `id` FROM `account_banned` WHERE `active` = 1)"); if(result) { zprintf("Actual Banned Accounts:\r\n"); zprintf("===============================================================================\r\n"); zprintf("| Account | BanDate | UnbanDate | Banned By | Banned reason |\r\n"); Field *fields2; do { zprintf("-------------------------------------------------------------------------------\r\n"); fields = result->Fetch(); // No SQL injection. id is uint32. QueryResult *banInfo = loginDatabase.PQuery("SELECT `bandate`,`unbandate`,`bannedby`,`banreason` FROM `account_banned` WHERE `id` = %u AND `active` = 1 ORDER BY `unbandate`", fields[0].GetUInt32()); if (banInfo) { fields2 = banInfo->Fetch(); do { time_t t_ban = fields2[0].GetUInt64(); tm* aTm_ban = localtime(&t_ban); zprintf("|%-15.15s|", fields[1].GetString()); zprintf("%02d-%02d-%02d %02d:%02d|", aTm_ban->tm_year%100, aTm_ban->tm_mon+1, aTm_ban->tm_mday, aTm_ban->tm_hour, aTm_ban->tm_min); if ( fields2[0].GetUInt64() == fields2[1].GetUInt64() ) zprintf(" permanent |"); else { time_t t_unban = fields2[1].GetUInt64(); tm* aTm_unban = localtime(&t_unban); zprintf("%02d-%02d-%02d %02d:%02d|",aTm_unban->tm_year%100, aTm_unban->tm_mon+1, aTm_unban->tm_mday, aTm_unban->tm_hour, aTm_unban->tm_min); } zprintf("%-15.15s|%-15.15s|\r\n",fields2[2].GetString(),fields2[3].GetString()); }while ( banInfo->NextRow() ); delete banInfo; } }while( result->NextRow() ); zprintf("===============================================================================\r\n"); delete result; } ///- Get the list of banned IP addresses and display them result = loginDatabase.Query( "SELECT `ip`,`bandate`,`unbandate`,`bannedby`,`banreason` FROM `ip_banned` WHERE (`bandate`=`unbandate` OR `unbandate`>UNIX_TIMESTAMP()) ORDER BY `unbandate`" ); if(result) { zprintf("Actual Banned IPs:\r\n"); zprintf("===============================================================================\r\n"); zprintf("| IP | BanDate | UnbanDate | Banned By | Banned reason |\r\n"); do { zprintf("-------------------------------------------------------------------------------\r\n"); fields = result->Fetch(); time_t t_ban = fields[1].GetUInt64(); tm* aTm_ban = localtime(&t_ban); zprintf("|%-15.15s|", fields[0].GetString()); zprintf("%02d-%02d-%02d %02d:%02d|", aTm_ban->tm_year%100, aTm_ban->tm_mon+1, aTm_ban->tm_mday, aTm_ban->tm_hour, aTm_ban->tm_min); if ( fields[1].GetUInt64() == fields[2].GetUInt64() ) zprintf(" permanent |"); else { time_t t_unban = fields[2].GetUInt64(); tm* aTm_unban = localtime(&t_unban); zprintf("%02d-%02d-%02d %02d:%02d|", aTm_unban->tm_year%100, aTm_unban->tm_mon+1, aTm_unban->tm_mday, aTm_unban->tm_hour, aTm_unban->tm_min); } zprintf("%-15.15s|%-15.15s|\r\n", fields[3].GetString(), fields[4].GetString()); }while( result->NextRow() ); zprintf("===============================================================================\r\n"); delete result; } //there is result already deleted pointer! , do not use it if(!result) zprintf("We do not have banned users\r\n"); } /// Ban an IP address or a user account void CliBan(char*command,pPrintf zprintf) { ///- Get the command parameter char* type = strtok((char*)command, " "); char* nameOrIP = strtok(NULL, " "); char* reason = strtok(NULL," "); char* duration = strtok(NULL," "); if(!type||!nameOrIP||!reason) // ?!? input of single char "0"-"9" wouldn't detect when with: || !atoi(duration) { zprintf("Syntax: ban account|ip|character $AccountOrIpOrCharacter $reason ($duration[s|m|h|d]) \r\n"); return; } if(!duration) duration="0"; switch (sWorld.BanAccount(type, nameOrIP, duration, reason, "Set by console.")) { case BAN_SUCCESS: if(atoi(duration)>0) zprintf("%s is banned for %s. Reason: %s.\r\n",nameOrIP,secsToTimeString(TimeStringToSecs(duration),true,false).c_str(),reason); else zprintf("%s is banned permanently. Reason: %s.\r\n",nameOrIP,reason); break; case BAN_NOTFOUND: zprintf("%s %s not found\r\n", type, nameOrIP); break; case BAN_SYNTAX_ERROR: zprintf("Syntax: ban account|ip|character $AccountOrIpOrCharacter $reason ($duration[s|m|h|d]) \r\n"); break; } } /// Display %MaNGOS version void CliVersion(char*,pPrintf zprintf) { //<--maybe better append to info cmd zprintf( "MaNGOS daemon version is %s\r\n", _FULLVERSION ); } /// Unban an IP adress or a user account void CliRemoveBan(char *command,pPrintf zprintf) { ///- Get the command parameter char *type = strtok(command," "); char *nameorip = strtok(NULL," "); if(!nameorip||!type) { zprintf("Syntax is: unban account|ip|character $nameorip\r\n"); return; } if (!sWorld.RemoveBanAccount(type, nameorip)) zprintf("%s %s not found\r\n", type, nameorip); else zprintf("We removed ban from %s: %s\r\n",type,nameorip); } /// Display the list of GMs void CliListGM(char*,pPrintf zprintf) { ///- Get the accounts with GM Level >0 Field *fields; QueryResult *result = loginDatabase.Query( "SELECT `username`,`gmlevel` FROM `account` WHERE `gmlevel` > 0" ); if(result) { zprintf("Current gamemasters:\r\n"); zprintf("========================\r\n"); zprintf("| Account | GM |\r\n"); zprintf("========================\r\n"); ///- Circle through them. Display username and GM level do { fields = result->Fetch(); zprintf("|%15s|", fields[0].GetString()); zprintf("%6s|\r\n",fields[1].GetString()); }while( result->NextRow() ); zprintf("========================\r\n"); delete result; } else { zprintf("NO gamemasters\r\n"); } } /// Set the GM level of an account void CliSetGM(char *command,pPrintf zprintf) { ///- Get the command line arguments char *szAcc = strtok(command," "); char *szLevel = strtok(NULL," "); if(!szAcc||!szLevel) //wrong syntax 'setgm' without name { zprintf("Syntax is: setgm $character $number (0 - normal, 3 - gamemaster)>\r\n"); return; } //wow it's ok,let's hope it was integer given int lev=atoi(szLevel); //get int anyway (0 if error) ///- Escape the account name to allow quotes in names std::string safe_account_name=szAcc; loginDatabase.escape_string(safe_account_name); ///- Try to find the account, then update the GM level // No SQL injection (account name is escaped) QueryResult *result = loginDatabase.PQuery("SELECT `id` FROM `account` WHERE `username` = '%s'",safe_account_name.c_str()); if (result) { Field *fields = result->Fetch(); uint32 account_id = fields[0].GetUInt32(); delete result; WorldSession* session = sWorld.FindSession(account_id); if(session) session->SetSecurity(lev); // No SQL injection (account name is escaped) loginDatabase.PExecute("UPDATE `account` SET `gmlevel` = '%d' WHERE `username` = '%s'",lev,safe_account_name.c_str()); zprintf("We added %s gmlevel %d\r\n",szAcc,lev); } else { zprintf("No account %s found\r\n",szAcc); } } /// Create an account void CliCreate(char *command,pPrintf zprintf) { //I see no need in this function (why would an admin personally create accounts //instead of using account registration page or accessing db directly?) //but still let it be ///- %Parse the command line arguments char *szAcc = strtok(command, " "); if(!szAcc) { zprintf("Syntax is: create $username $password\r\n"); return; } if(strlen(szAcc)>16) { zprintf("Account cannot be longer than 16 characters.\r\n"); return; } char *szPassword = strtok(NULL, " "); if(!szPassword) { zprintf("Syntax is: create $username $password\r\n"); return; } int result = accmgr.CreateAccount(szAcc, szPassword); if(result == -1) zprintf("User %s with password %s NOT created (probably sql file format was updated)\r\n",szAcc,szPassword); else if(result == 1) zprintf("Username %s is too long\r\n", szAcc); else if(result == 2) zprintf("User %s already exists\r\n",szAcc); else if(result == 0) zprintf("User %s with password %s created successfully\r\n",szAcc,szPassword); } /// Command parser and dispatcher void ParseCommand( pPrintf zprintf, char* input) { unsigned int x; bool bSuccess=false; if (!input) return; unsigned int l=strlen(input); char *supposedCommand=NULL,* arguments=(char*)(""); if(l) { ///- Get the command and the arguments supposedCommand = strtok(input," "); if (supposedCommand) { if (l>strlen(supposedCommand)) arguments=&input[strlen(supposedCommand)+1]; ///- Circle through the command table and, if found, put the command in the queue for ( x=0;x"); } /// Kick a character out of the realm void CliKick(char*command,pPrintf zprintf) { char *kickName = strtok(command, " "); if (!kickName) { zprintf("Syntax is: kick $charactername\r\n"); return; } std::string name = kickName; normalizePlayerName(name); sWorld.KickPlayer(name); } /// Teleport a character to location void CliTele(char*command,pPrintf zprintf) { char *charName = strtok(command, " "); char *locName = strtok(NULL, " "); if (!charName || !locName) { zprintf("Syntax is: tele $charactername $location\r\n"); return; } std::string name = charName; normalizePlayerName(name); std::string location = locName; WorldDatabase.escape_string(location); QueryResult *result = WorldDatabase.PQuery("SELECT `position_x`,`position_y`,`position_z`,`orientation`,`map` FROM `game_tele` WHERE `name` = '%s'",location.c_str()); if (!result) { zprintf(LANG_COMMAND_TELE_NOTFOUND "\r\n"); return; } Field *fields = result->Fetch(); float x = fields[0].GetFloat(); float y = fields[1].GetFloat(); float z = fields[2].GetFloat(); float ort = fields[3].GetFloat(); int mapid = fields[4].GetUInt16(); delete result; if(!MapManager::IsValidMapCoord(mapid,x,y)) { zprintf(LANG_INVALID_TARGET_COORD "\r\n",x,y,mapid); return; } Player *chr = objmgr.GetPlayer(name.c_str()); if (chr) { if(chr->IsBeingTeleported()==true) { zprintf(LANG_IS_TELEPORTED "\r\n",chr->GetName()); return; } if(chr->isInFlight()) { zprintf(LANG_CHAR_IN_FLIGHT "\r\n",chr->GetName()); return; } zprintf(LANG_TELEPORTING_TO "\r\n",chr->GetName(),"", location.c_str()); chr->SaveRecallPosition(); chr->TeleportTo(mapid,x,y,z,chr->GetOrientation()); } else if (uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str())) { zprintf(LANG_TELEPORTING_TO "\r\n",name.c_str(), LANG_OFFLINE, location.c_str()); Player::SavePositionInDB(mapid,x,y,z,ort,MapManager::Instance().GetZoneId(mapid,x,y),guid); } else zprintf(LANG_NO_PLAYER "\r\n",name.c_str()); } /// Display/Define the 'Message of the day' for the realm void CliMotd(char*command,pPrintf zprintf) { if (strlen(command) == 0) { zprintf("Current Message of the day: \r\n%s\r\n", sWorld.GetMotd()); return; } else { sWorld.SetMotd(command); zprintf("Message of the day changed to:\r\n%s\r\n", command); } } /// Comment me /// \todo What is CorpsesErase for? void CliCorpses(char*,pPrintf) { CorpsesErase(); } /// Set the level of logging void CliSetLogLevel(char*command,pPrintf zprintf) { char *NewLevel = strtok(command, " "); if (!NewLevel) { zprintf("Syntax is: setloglevel $loglevel\r\n"); return; } sLog.SetLogLevel(NewLevel); } /// Display the server uptime void CliUpTime(char*,pPrintf zprintf) { uint32 uptime = sWorld.GetUptime(); std::string suptime = secsToTimeString(uptime,true,(uptime > 86400)); zprintf("Server has been up for: %s\r\n", suptime.c_str()); } /// Set/Unset the TBC flag for an account void CliSetTBC(char *command,pPrintf zprintf) { ///- Get the command line arguments char *szAcc = strtok(command," "); char *szTBC = strtok(NULL," "); if(!szAcc||!szTBC) { zprintf("Syntax is: setbc $account $number (0 - normal, 1 - tbc)>\r\n"); return; } int lev=atoi(szTBC); //get int anyway (0 if error) if((lev > 1)|| (lev < 0)) { zprintf("Syntax is: setbc $account $number (0 - normal, 1 - tbc)>\r\n"); return; } ///- Escape the account name to allow quotes in names std::string safe_account_name=szAcc; loginDatabase.escape_string(safe_account_name); // No SQL injection (account name is escaped) QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM `account` WHERE `username` = '%s'",safe_account_name.c_str()); if (result) { // No SQL injection (account name is escaped) loginDatabase.PExecute("UPDATE `account` SET `tbc` = '%d' WHERE `username` = '%s'",lev,safe_account_name.c_str()); zprintf("We added %s to expansion allowed %d\r\n",szAcc,lev); delete result; } else { zprintf("No account %s found\r\n",szAcc); } } /// Save all players void CliSave(char*,pPrintf zprintf) { ///- Save players ObjectAccessor::Instance().SaveAllPlayers(); zprintf( "All Players Saved \r\n" ); ///- Send a message sWorld.SendWorldText("Players saved!", NULL); } /// Send a message to a player in game void CliSend(char *playerN,pPrintf zprintf) { ///- Get the command line arguments char* plr = strtok((char*)playerN, " "); char* msg = strtok(NULL, ""); if(!plr || !msg) { zprintf("Syntax: send $player $message (Player names case sensitive)\r\n"); return; } ///- Find the player and check that he is not logging out. Player *rPlayer = objmgr.GetPlayer(plr); if(!rPlayer) { zprintf("Player %s not found!\r\n", plr); return; } if (rPlayer->GetSession()->isLogingOut()) { zprintf("Cannot send message while player %s is logging out!\r\n",plr); return; } ///- Send the message //Use SendAreaTriggerMessage for fastest delivery. rPlayer->GetSession()->SendAreaTriggerMessage("%s", msg); rPlayer->GetSession()->SendAreaTriggerMessage("|cffff0000[Message from administrator]:|r"); //Confirmation message zprintf("Message '%s' sent to %s\r\n",msg , plr); } void CliPLimit(char *args,pPrintf zprintf) { if(*args) { char* param = strtok((char*)args, " "); if(!param || !*param) return; int l = strlen(param); if( strncmp(param,"player",l) == 0 ) sWorld.SetPlayerLimit(-SEC_PLAYER); else if(strncmp(param,"moderator",l) == 0 ) sWorld.SetPlayerLimit(-SEC_MODERATOR); else if(strncmp(param,"gamemaster",l) == 0 ) sWorld.SetPlayerLimit(-SEC_GAMEMASTER); else if(strncmp(param,"administrator",l) == 0 ) sWorld.SetPlayerLimit(-SEC_ADMINISTRATOR); else if(strncmp(param,"reset",l) == 0 ) sWorld.SetPlayerLimit(sConfig.GetIntDefault("PlayerLimit", DEFAULT_PLAYER_LIMIT)); else { int val = atoi(param); if(val < -SEC_ADMINISTRATOR) val = -SEC_ADMINISTRATOR; sWorld.SetPlayerLimit(val); } // kick all low security level players if(sWorld.GetPlayerAmountLimit() > SEC_PLAYER) sWorld.KickAllLess(sWorld.GetPlayerSecurityLimit()); } uint32 pLimit = sWorld.GetPlayerAmountLimit(); AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit(); char const* secName = ""; switch(allowedAccountType) { case SEC_PLAYER: secName = "Player"; break; case SEC_MODERATOR: secName = "Moderator"; break; case SEC_GAMEMASTER: secName = "Gamemaster"; break; case SEC_ADMINISTRATOR: secName = "Administrator"; break; default: secName = ""; break; } zprintf("Player limits: amount %u, min. security level %s.\r\n",pLimit,secName); } /// @} #ifdef linux // Non-blocking keypress detector, when return pressed, return 1, else always return 0 int kb_hit_return() { struct timeval tv; fd_set fds; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); return FD_ISSET(STDIN_FILENO, &fds); } #endif /// %Thread start void CliRunnable::run() { ///- Init new SQL thread for the world database (one connection call enough) WorldDatabase.ThreadStart(); // let thread do safe mySQL requests char commandbuf[256]; ///- Display the list of available CLI functions then beep sLog.outString(); /// \todo Shoudn't we use here also the sLog singleton? CliHelp(NULL,&printf); if(sConfig.GetIntDefault("BeepAtStart", 1) > 0) { printf("\a"); // \a = Alert } // print this here the first time // later it will be printed after command queue updates printf("mangos>"); ///- As long as the World is running (no World::m_stopEvent), get the command line and handle it while (!World::m_stopEvent) { fflush(stdout); #ifdef linux while (!kb_hit_return() && !World::m_stopEvent) // With this, we limit CLI to 10commands/second usleep(100); if (World::m_stopEvent) break; #endif char *command = fgets(commandbuf,sizeof(commandbuf),stdin); if (command != NULL) { for(int x=0;command[x];x++) if(command[x]=='\r'||command[x]=='\n') { command[x]=0; break; } //// \todo Shoudn't we use here also the sLog singleton? ParseCommand(&printf,command); } else if (feof(stdin)) { World::m_stopEvent = true; } } ///- End the database thread WorldDatabase.ThreadEnd(); // free mySQL thread resources }