/* * Ascent MMORPG Server * Copyright (C) 2005-2007 Ascent Team * * 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 3 of the License, or * 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, see . * */ #include "CConsole.h" #include "Log.h" #include "Master.h" #include "../game/StdAfx.h" #include "../shared/svn_revision.h" #ifndef WIN32 #include #endif createFileSingleton(Console); createFileSingleton(CConsole); void CConsole::Kill() { #ifdef WIN32 /* write the return keydown/keyup event */ DWORD dwTmp; INPUT_RECORD ir[2]; ir[0].EventType = KEY_EVENT; ir[0].Event.KeyEvent.bKeyDown = TRUE; ir[0].Event.KeyEvent.dwControlKeyState = 288; ir[0].Event.KeyEvent.uChar.AsciiChar = 13; ir[0].Event.KeyEvent.wRepeatCount = 1; ir[0].Event.KeyEvent.wVirtualKeyCode = 13; ir[0].Event.KeyEvent.wVirtualScanCode = 28; ir[1].EventType = KEY_EVENT; ir[1].Event.KeyEvent.bKeyDown = FALSE; ir[1].Event.KeyEvent.dwControlKeyState = 288; ir[1].Event.KeyEvent.uChar.AsciiChar = 13; ir[1].Event.KeyEvent.wRepeatCount = 1; ir[1].Event.KeyEvent.wVirtualKeyCode = 13; ir[1].Event.KeyEvent.wVirtualScanCode = 28; _thread->kill=true; WriteConsoleInput (GetStdHandle(STD_INPUT_HANDLE), ir, 2, & dwTmp); printf("Waiting for console thread to terminate....\n"); while(_thread != NULL) { Sleep(100); } printf("Console shut down.\n"); #endif } void CConsoleThread::run() { SetThreadName("Console Interpreter"); sCConsole._thread = this; size_t i = 0; char cmd[96]; while (kill != true) { // Make sure our buffer is clean to avoid Array bounds overflow memset(cmd,0,sizeof(cmd)); // Read in single line from "stdin" fgets(cmd, 80, stdin); if(kill) break; for( i = 0 ; i < 80 || cmd[i] != '\0' ; i++ ) { if( cmd[i] =='\n' ) { cmd[i]='\0'; sCConsole.ProcessCmd(cmd); fflush(stdin); break; } } } sCConsole._thread=NULL; } //------------------------------------------------------------------------------ // Protected methods: //------------------------------------------------------------------------------ // Process one command void CConsole::ProcessCmd(char *cmd) { typedef void (CConsole::*PTranslater)(char *str); struct SCmd { const char *name; PTranslater tr; }; SCmd cmds[] = { { "?", &CConsole::TranslateHelp}, { "help", &CConsole::TranslateHelp}, { "ver", &CConsole::TranslateVersion}, { "version", &CConsole::TranslateVersion}, { "uptime", &CConsole::GetUptime}, { "threads", &CConsole::TranslateThreads}, { "cancelshutdown", &CConsole::CancelShutdown }, { "status", &CConsole::ObjectStats }, { "announce", &CConsole::Announce }, { "wannounce", &CConsole::WideAnnounce }, { "saveall", &CConsole::SaveallPlayers }, { "quit", &CConsole::TranslateQuit}, { "exit", &CConsole::TranslateQuit}, { "kick", &CConsole::Kick}, { "banaccount", &CConsole::BanAccount}, { "banip", &CConsole::IPBan}, { "unbanip", &CConsole::IPUnBan}, { "playerinfo", &CConsole::PlayerInfo}, { "reloadscripts", &CConsole::ReloadGMScripts}, }; char cmd2[80]; strcpy(cmd2, cmd); for(size_t i = 0; i < strlen(cmd); ++i) cmd2[i] = tolower(cmd[i]); for (size_t i = 0; i < sizeof(cmds)/sizeof(SCmd); i++) if (strncmp(cmd2, cmds[i].name, strlen(cmds[i].name)) == 0) { (this->*(cmds[i].tr)) (cmd + strlen(cmds[i].name) +1); return; } printf("Console: Unknown console command (use \"help\" for help).\n"); } void CConsole::CancelShutdown(char *str) { printf("Shutdown aborted.\n"); WorldPacket data(20); data.SetOpcode(SMSG_SERVER_MESSAGE); data << uint32(SERVER_MSG_SHUTDOWN_CANCELLED); sWorld.SendGlobalMessage(&data); sMaster.m_ShutdownEvent = false; sMaster.m_ShutdownTimer = 0; } void CConsole::GetUptime(char *str) { uint32 count = objmgr._players.size(); sLog.outString("Console: Server has been running for %s There are currently %d online players.", sWorld.GetUptimeString().c_str(), count); } //------------------------------------------------------------------------------ // ver[sion] void CConsole::TranslateVersion(char *str) { ProcessVersion(); } void CConsole::ProcessVersion() { sLog.outString("Console: Server %s, Rev: %d", _FULLVERSION, g_getRevision()); } //------------------------------------------------------------------------------ // quit | exit void CConsole::TranslateQuit(char *str) { int delay = str != NULL ? atoi(str) : 5000; if(!delay) delay = 5000; else delay *= 1000; ProcessQuit(delay); } void CConsole::ProcessQuit(int delay) { sMaster.m_ShutdownTimer = delay; sMaster.m_ShutdownEvent = true; } //------------------------------------------------------------------------------ // help | ? void CConsole::TranslateHelp(char *str) { ProcessHelp(NULL); } void CConsole::ProcessHelp(char *command) { if (command == NULL) { sLog.outString("Console:--------help--------"); sLog.outString(" help, ?: print this text"); sLog.outString(" uptime: print uptime of the server"); sLog.outString(" version, ver: print version"); sLog.outString(" cancelshutdown: cancels the shutdown of the server"); sLog.outString(" announce: announces a msg to the server."); sLog.outString(" wannounce: announces a widescreen msg to the server"); sLog.outString(" saveall: saves all players"); sLog.outString(" kick: kicks a player with a reason"); sLog.outString(" banaccount: bans an account"); sLog.outString(" banip
[duration]: bans an ip"); sLog.outString(" unbanip
: unbans an ip"); sLog.outString(" playerinfo: gets info on an online player"); sLog.outString(" reloadscripts: reloads gamemonkey scripts"); sLog.outString(" quit, exit: close program"); } } //------------------------------------------------------------------------------ CConsoleThread::CConsoleThread() { kill=false; } void CConsole::TranslateThreads(char* str) { std::string threads = sThreadMgr.ShowStatus(); printf(threads.c_str()); } CConsoleThread::~CConsoleThread() { } void CConsole::ObjectStats(char *str) { printf("\n"); printf("Loaded object information:\n"); printf("\n"); } void CConsole::Announce(char* str) { if(!str) return; char msg[500]; snprintf(msg, 500, "%sConsole:%s%s", "|cff00ccff", "|r", str); sWorld.SendWorldText(msg, 0); } void CConsole::BanAccount(char* str) { sLogonCommHandler.LogonDatabaseSQLExecute("UPDATE accounts SET banned = 1 WHERE login = '%s'", str); sLog.outString("User %s banned!", str); sLogonCommHandler.LogonDatabaseReloadAccounts(); } void CConsole::IPBan(char* str) { char ip[16] = {0}; // IPv4 address uint32 dLength = 0; // duration of ban, 0 = permanent char dType = {0}; // duration type, defaults to minutes ( see convTimePeriod() ) // we require at least one argument, the network address to ban if ( sscanf(str, "%15s %u%c", ip, (unsigned int*)&dLength, &dType) < 1 ) { sLog.outString("usage: banip
[duration]"); return; } uint32 o1, o2, o3, o4; if ( sscanf(ip, "%3u.%3u.%3u.%3u", (unsigned int*)&o1, (unsigned int*)&o2, (unsigned int*)&o3, (unsigned int*)&o4) != 4 || o1 > 255 || o2 > 255 || o3 > 255 || o4 > 255) { sLog.outString("Invalid IPv4 address [%s]", ip); return; } time_t expire_time; if ( dLength == 0) // permanent ban expire_time = 0; else { time_t dPeriod = convTimePeriod(dLength, dType); if ( dPeriod == 0) { sLog.outString("Invalid ban duration"); return; } time( &expire_time ); expire_time += dPeriod; } sLog.outString("Adding [%s] to IP ban table, expires: %s", ip, (expire_time == 0)? "Never" : ctime( &expire_time )); sLogonCommHandler.LogonDatabaseSQLExecute("REPLACE INTO ipbans VALUES ('%s', %u);", WorldDatabase.EscapeString(ip).c_str(), (uint32)expire_time); sLogonCommHandler.LogonDatabaseReloadAccounts(); } void CConsole::IPUnBan(char* str) { char ip[16] = {0}; // IPv4 address // we require at least one argument, the network address to unban if ( sscanf(str, "%15s", ip) < 1) { sLog.outString("usage: unbanip
"); return; } /** * We can afford to be less fussy with the validty of the IP address given since * we are only attempting to remove it. * Sadly, we can only blindly execute SQL statements on the logonserver so we have * no idea if the address existed and so the account/IPBanner cache requires reloading. */ sLog.outString("Removing [%s] from IP ban table if it exists", ip); sLogonCommHandler.LogonDatabaseSQLExecute("DELETE FROM ipbans WHERE ip = '%s';", WorldDatabase.EscapeString(ip).c_str()); sLogonCommHandler.LogonDatabaseReloadAccounts(); return; } void CConsole::PlayerInfo(char* str) { char player[100]; if(sscanf(str, "%s", player) != 1) return; Player * _plr = objmgr.GetPlayer(player, false); if(!_plr) { sLog.outString("Cannot find online player %s", str); return; } if(!_plr) return; if(!_plr->GetSession()) { sLog.outString("ERROR: this player hasn't got any session !"); return; } WorldSession* sess = _plr->GetSession(); static const char* _classes[12] = {"None","Warrior", "Paladin", "Hunter", "Rogue", "Priest", "None", "Shaman", "Mage", "Warlock", "None", "Druid"}; static const char* _races[12] = {"None","Human","Orc","Dwarf","Night Elf","Undead","Tauren","Gnome","Troll","None","Blood Elf","Draenei"}; sLog.outColor(TGREEN, "Name: "); sLog.outColor(TNORMAL, "%s\n", _plr->GetName()); sLog.outColor(TGREEN, "Account: "); sLog.outColor(TNORMAL, "%s\n", sess->GetAccountName().c_str()); sLog.outColor(TGREEN, "Level: "); sLog.outColor(TNORMAL, "%d\n", _plr->getLevel()); sLog.outColor(TGREEN, "Race: "); sLog.outColor(TNORMAL, "%s\n", _races[_plr->getRace()]); sLog.outColor(TGREEN, "Class: "); sLog.outColor(TNORMAL, "%s\n", _classes[_plr->getClass()]); sLog.outColor(TGREEN, "Map: "); sLog.outColor(TNORMAL, "%d\n", _plr->GetMapId()); sLog.outColor(TGREEN, "Banned: "); sLog.outColor(TNORMAL, "%s\n", (_plr->IsBanned() ? "Yes" : "No")); } void CConsole::Kick(char* str) { char player[100]; char reason[256]; if(sscanf(str, "%s %s", player, reason) != 2) return; Player * _plr = objmgr.GetPlayer(player, false); if(!_plr) { sLog.outColor(TRED, "Unable to find player %s\n", player); return; } _plr->BroadcastMessage("|cff00ccffYou have been kicked for |cffff0000%s", reason); _plr->Kick(6000); } void CConsole::WideAnnounce(char *str) { if(!str) return; char msg[500]; snprintf(msg, 500, "%sConsole:%s%s", "|cff00ccff", "|r", str); sWorld.SendWorldText(msg, 0); sWorld.SendWorldWideScreenText(msg, 0); } void CConsole::SaveallPlayers(char *str) { PlayerStorageMap::const_iterator itr; uint32 stime = now(); uint32 count = 0; objmgr._playerslock.AcquireReadLock(); for (itr = objmgr._players.begin(); itr != objmgr._players.end(); itr++) { if(itr->second->GetSession()) { itr->second->SaveToDB(false); count++; } } objmgr._playerslock.ReleaseReadLock(); char msg[100]; snprintf(msg, 100, "Saved all %d online players in %d msec.", (unsigned int)count, (unsigned int)((uint32)now() - stime)); sWorld.SendWorldText(msg); sWorld.SendWorldWideScreenText(msg); } void CConsole::ReloadGMScripts(char * str) { ScriptSystem->Reload(); } static char ConsoleBuffer[65536]; const char * Console::GetLine(uint32 Delay) { if(PollConsole(Delay)) { #ifdef WIN32 DWORD Bytes_Read; DWORD Result = ReadFile(GetStdHandle(STD_INPUT_HANDLE), ConsoleBuffer, 65536, &Bytes_Read, NULL); if(Bytes_Read != 0 && Result) return ConsoleBuffer; else return NULL; #else struct termios initial_settings, new_settings; tcgetattr(0,&initial_settings); tcgetattr(0,&new_settings); new_settings.c_lflag &= ~ICANON; new_settings.c_lflag &= ~ECHO; new_settings.c_lflag &= ~ISIG; tcsetattr(0,TCSANOW,&new_settings); int br = read(fileno(stdin), ConsoleBuffer, 65536); tcsetattr(0,TCSANOW,&initial_settings); if(br != 0) return ConsoleBuffer; #endif } return NULL; } bool Console::PollConsole(uint32 Time) { /* This is platform-dependant, unfortunately due to window's gayness of treating file descriptors differently. */ #ifndef WIN32 fd_set fds; timeval tv; struct termios initial_settings, new_settings; tcgetattr(0,&initial_settings); tcgetattr(0,&new_settings); new_settings.c_lflag &= ~ICANON; new_settings.c_lflag &= ~ECHO; new_settings.c_lflag &= ~ISIG; tcsetattr(0,TCSANOW,&new_settings); FD_ZERO(&fds); FD_SET(fileno(stdin), &fds); tv.tv_sec = Time / 1000; tv.tv_usec = (Time % 1000) * 1000; int result = select(1, &fds, NULL, NULL, &tv); tcsetattr(0,TCSANOW,&initial_settings); if(result > 0) return true; else return false; #else uint32 e = getMSTime() + Time; uint32 n = getMSTime(); while(n < e) { if(GetAsyncKeyState(VK_RETURN) != 0) return true; Sleep(100); } return false; #endif } bool Console::PollForD() { #ifndef WIN32 const char * buf = GetLine(1000); if(!buf || buf[0] != 27) return false; return true; #else uint32 e = getMSTime() + 1000; uint32 n = getMSTime(); while(n < e) { if(GetAsyncKeyState(VK_F1) != 0) return true; Sleep(100); n = getMSTime(); } return false; #endif } bool Console::WaitForSpace() { #ifndef WIN32 const char * buf = GetLine(100000); if(buf && buf[0] != ' ') WaitForSpace(); return true; #else while(true) { if(GetAsyncKeyState(VK_SPACE) != 0) return true; Sleep(100); } return false; #endif }