diff options
Diffstat (limited to 'src/trinitycore')
-rw-r--r-- | src/trinitycore/CliRunnable.cpp | 1265 | ||||
-rw-r--r-- | src/trinitycore/CliRunnable.h | 33 | ||||
-rw-r--r-- | src/trinitycore/Main.cpp | 164 | ||||
-rw-r--r-- | src/trinitycore/Makefile.am | 78 | ||||
-rw-r--r-- | src/trinitycore/Master.cpp | 492 | ||||
-rw-r--r-- | src/trinitycore/Master.h | 50 | ||||
-rw-r--r-- | src/trinitycore/RASocket.cpp | 251 | ||||
-rw-r--r-- | src/trinitycore/RASocket.h | 65 | ||||
-rw-r--r-- | src/trinitycore/TrinityCore.ico | bin | 0 -> 34494 bytes | |||
-rw-r--r-- | src/trinitycore/TrinityCore.rc | 85 | ||||
-rw-r--r-- | src/trinitycore/WorldRunnable.cpp | 82 | ||||
-rw-r--r-- | src/trinitycore/WorldRunnable.h | 33 | ||||
-rw-r--r-- | src/trinitycore/monitor-mangosd | 18 | ||||
-rw-r--r-- | src/trinitycore/resource.h | 15 | ||||
-rw-r--r-- | src/trinitycore/run-mangosd | 14 | ||||
-rw-r--r-- | src/trinitycore/trinitycore.conf.dist | 1027 |
16 files changed, 3672 insertions, 0 deletions
diff --git a/src/trinitycore/CliRunnable.cpp b/src/trinitycore/CliRunnable.cpp new file mode 100644 index 00000000000..2a92273f764 --- /dev/null +++ b/src/trinitycore/CliRunnable.cpp @@ -0,0 +1,1265 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * 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" +#include "PlayerDump.h" +#include "Player.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 CliIdleRestart(char*,pPrintf zprintf); +void CliRestart(char*,pPrintf zprintf); +void CliIdleShutdown(char*,pPrintf zprintf); +void CliShutdown(char*,pPrintf zprintf); +void CliBroadcast(char*,pPrintf); +void CliCreate(char*,pPrintf); +void CliDelete(char*,pPrintf); +void CliCharDelete(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); +void CliSetPassword(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"}, + {"chardelete", & CliCharDelete,"Delete character"}, + {"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"}, + {"setpass", & CliSetPassword,"Set password for account"}, + {"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"}, + {"idlerestart", & CliIdleRestart,"Restart server with some delay when there are no active connections remaining"}, + {"restart", & CliRestart,"Restart server with some delay"}, + {"idleshutdown", & CliIdleShutdown,"Shutdown server with some delay when there are no active connections remaining"}, + {"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) + +#if PLATFORM == PLATFORM_WINDOWS +int utf8printf(const char* str,...) +{ + UTF8PRINTF(stdout,str,1); + return 0; +} +#define UTF8ZPRINTF utf8printf +#else +#define UTF8ZPRINTF printf +#endif + +/// 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 $playerNameOrGUID\r\n"); + return; + } + + std::string name; + if(!consoleToUtf8(p2,name)) // convert from console encoding to utf8 + return; + + if(!normalizePlayerName(name)) + { + zprintf("Syntax is: writepdump $filename $playerNameOrGUID\r\n"); + return; + } + + uint32 guid = objmgr.GetPlayerGUIDByName(name); + if(!guid) + guid = atoi(p2); + + if(!guid) + { + zprintf("Syntax is: writepdump $filename $playerNameOrGUID\r\n"); + return; + } + + PlayerDumpWriter().WriteDump(file, guid); +} + +/// 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; + } + + uint32 account_id = objmgr.GetAccountByAccountName(acc); + if(!account_id) + { + account_id = atoi(acc); + if(account_id) + { + std::string acc_name; + if(!objmgr.GetAccountNameByAccount(account_id,acc_name)) + { + zprintf("Failed to load the character! Account not exist.\r\n"); + return; + } + } + else + { + zprintf("Failed to load the character! Account not exist.\r\n"); + return; + } + } + + char * name_str = strtok(NULL, " "); + char * guid_str = name_str ? strtok(NULL, " ") : NULL; + + uint32 guid = guid_str ? atoi(guid_str) : 0; + + std::string name; + if(name_str) + { + if(!consoleToUtf8(name_str,name)) // convert from console encoding to utf8 + return; + + if(!normalizePlayerName(name)) + { + zprintf("Syntax is: loadpdump $filename $account ($newname) ($newguid)\r\n"); + return; + } + } + + if(PlayerDumpReader().LoadDump(file, account_id, name, guid)) + zprintf("Character loaded successfully!\r\n"); + else + zprintf("Failed to load the character!\r\n"); +} + +/// 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(LANG_SCRIPTS_RELOADED); +} + +/// 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_str=strtok(command," "); + if(!account_name_str) + { + // \r\n is used because this function can also be called from RA + zprintf("Syntax is: delete $account\r\n"); + return; + } + + std::string account_name; + if(!consoleToUtf8(account_name_str,account_name)) // convert from console encoding to utf8 + return; + + AccountOpResult result = accmgr.DeleteAccount(accmgr.GetId(account_name)); + switch(result) + { + case AOR_OK: + zprintf("We deleted account: %s\r\n",account_name.c_str()); + break; + case AOR_NAME_NOT_EXIST: + zprintf("User %s does not exist\r\n",account_name.c_str()); + break; + case AOR_DB_INTERNAL_ERROR: + zprintf("User %s NOT deleted (probably sql file format was updated)\r\n",account_name.c_str()); + break; + default: + zprintf("User %s NOT deleted (unknown error)\r\n",account_name.c_str()); + break; + } +} + +void CliCharDelete(char*command,pPrintf zprintf) +{ + char *character_name_str = strtok(command," "); + + if(!character_name_str) + { + zprintf("Syntax is: chardelete $character_name\r\n"); + return; + } + + std::string character_name; + if(!consoleToUtf8(character_name_str,character_name)) // convert from console encoding to utf8 + return; + + if(!normalizePlayerName(character_name)) + { + zprintf("Syntax is: chardelete $character_name\r\n"); + return; + } + + Player *player = objmgr.GetPlayer(character_name.c_str()); + + uint64 character_guid; + uint32 account_id; + + if(player) + { + character_guid = player->GetGUID(); + account_id = player->GetSession()->GetAccountId(); + player->GetSession()->KickPlayer(); + } + else + { + character_guid = objmgr.GetPlayerGUIDByName(character_name); + if(!character_guid) + { + zprintf("Player %s not found!\r\n",character_name.c_str()); + return; + } + + account_id = objmgr.GetPlayerAccountIdByGUID(character_guid); + } + + Player::DeleteFromDB(character_guid, account_id, true); + zprintf("Player %s (Guid: %u AccountId: %u) deleted\r\n",character_name.c_str(),GUID_LOPART(character_guid),account_id); +} + +/// Broadcast a message to the World +void CliBroadcast(char *text,pPrintf zprintf) +{ + std::string textUtf8; + if(!consoleToUtf8(text,textUtf8)) // convert from console encoding to utf8 + return; + + sWorld.SendWorldText(LANG_SYSTEMMESSAGE,textUtf8.c_str()); + zprintf("Broadcasting to the world: %s\r\n",textUtf8.c_str()); +} + +/// Print the list of commands and associated description +void CliHelp(char*,pPrintf zprintf) +{ + for (unsigned int x=0;x<CliTotalCmds;x++) + zprintf("%-13s - %s.\r\n",Commands[x].cmd ,Commands[x].description); +} + +/// Exit the realm +void CliExit(char*,pPrintf zprintf) +{ + zprintf( "Exiting daemon...\r\n" ); + World::m_stopEvent = true; +} + +/// Restart the server (with some delay) as soon as no active connections remain on the server +void CliIdleRestart(char* command,pPrintf zprintf) +{ + char *args = strtok(command," "); + + if(!args) + { + zprintf("Syntax is: idlerestart $seconds|cancel\r\n"); + return; + } + + if(std::string(args)=="cancel") + { + sWorld.ShutdownCancel(); + } + else + { + + uint32 time = atoi(args); + + ///- Prevent interpret wrong arg value as 0 secs shutdown time + if(time==0 && (args[0]!='0' || args[1]!='\0') || time < 0) + { + zprintf("Syntax is: idlerestart $seconds|cancel\r\n"); + return; + } + + sWorld.ShutdownServ(time,SHUTDOWN_MASK_RESTART|SHUTDOWN_MASK_IDLE); + } +} + +/// Restart the server with some delay +void CliRestart(char* command,pPrintf zprintf) +{ + char *args = strtok(command," "); + + if(!args) + { + zprintf("Syntax is: restart $seconds|cancel\r\n"); + return; + } + + if(std::string(args)=="cancel") + { + sWorld.ShutdownCancel(); + } + else + { + int32 time = atoi(args); + + ///- Prevent interpret wrong arg value as 0 secs shutdown time + if(time==0 && (args[0]!='0' || args[1]!='\0') || time < 0) + { + zprintf("Syntax is: restart $seconds|cancel\r\n"); + return; + } + + sWorld.ShutdownServ(time,SHUTDOWN_MASK_RESTART); + } +} + +/// Shutdown the server (with some delay) as soon as no active connections remain on the server +void CliIdleShutdown(char* command,pPrintf zprintf) +{ + char *args = strtok(command," "); + + if(!args) + { + zprintf("Syntax is: idleshutdown $seconds|cancel\r\n"); + return; + } + + if(std::string(args)=="cancel") + { + sWorld.ShutdownCancel(); + } + else + { + + uint32 time = atoi(args); + + ///- Prevent interpret wrong arg value as 0 secs shutdown time + if(time==0 && (args[0]!='0' || args[1]!='\0') || time < 0) + { + zprintf("Syntax is: idleshutdown $seconds|cancel\r\n"); + return; + } + + sWorld.ShutdownServ(time,SHUTDOWN_MASK_IDLE); + } +} + +/// Shutdown the server with some delay +void CliShutdown(char* command,pPrintf zprintf) +{ + char *args = strtok(command," "); + + if(!args) + { + zprintf("Syntax is: shutdown $seconds|cancel\r\n"); + return; + } + + if(std::string(args)=="cancel") + { + sWorld.ShutdownCancel(); + } + else + { + int32 time = atoi(args); + + ///- Prevent interpret wrong arg value as 0 secs shutdown time + if(time==0 && (args[0]!='0' || args[1]!='\0') || time < 0) + { + zprintf("Syntax is: shutdown $seconds|cancel\r\n"); + return; + } + + sWorld.ShutdownServ(time); + } +} + +/// Display info on users currently in the realm +void CliInfo(char*,pPrintf zprintf) +{ + uint32 activeClientsNum = sWorld.GetActiveSessionCount(); + uint32 queuedClientsNum = sWorld.GetQueuedSessionCount(); + uint32 maxActiveClientsNum = sWorld.GetMaxActiveSessionCount(); + uint32 maxQueuedClientsNum = sWorld.GetMaxQueuedSessionCount(); + std::string timeStr = secsToTimeString(sWorld.GetUptime(),true); + + zprintf("Online players: %u (max: %u) queued: %u (max: %u) Uptime: %s\r\n",activeClientsNum,maxActiveClientsNum,queuedClientsNum,maxQueuedClientsNum,timeStr.c_str()); + + ///- Get the list of accounts ID logged to the realm + QueryResult *resultDB = CharacterDatabase.Query("SELECT name,account FROM characters WHERE online > 0"); + + if (!resultDB) + return; + + ///- Display the list of account/characters online + zprintf("=====================================================================\r\n"); + zprintf("| Account | Character | IP | GM | TBC |\r\n"); + zprintf("=====================================================================\r\n"); + + ///- 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(); + zprintf("|%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 + zprintf("|<Error> | %20s |<Error> |<Er>|<Err>|\r\n",name.c_str()); + + }while(resultDB->NextRow()); + + delete resultDB; + + zprintf("=====================================================================\r\n"); +} + +/// Display a list of banned accounts and ip addresses +void CliBanList(char*,pPrintf zprintf) +{ + bool found = false; + ///- Get the list of banned accounts and display them + QueryResult *result = loginDatabase.Query("SELECT id,username FROM account WHERE id IN (SELECT id FROM account_banned WHERE active = 1)"); + if(result) + { + found = true; + + zprintf("Currently Banned Accounts:\r\n"); + zprintf("===============================================================================\r\n"); + zprintf("| Account | BanDate | UnbanDate | Banned By | Ban Reason |\r\n"); + do + { + zprintf("-------------------------------------------------------------------------------\r\n"); + Field *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) + { + Field *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) + { + found = true; + + zprintf("Currently Banned IPs:\r\n"); + zprintf("===============================================================================\r\n"); + zprintf("| IP | BanDate | UnbanDate | Banned By | Ban Reason |\r\n"); + do + { + zprintf("-------------------------------------------------------------------------------\r\n"); + Field *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; + } + + if(!found) + 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_str = strtok((char*)command, " "); + char* nameOrIP_str = strtok(NULL, " "); + char* duration_str = strtok(NULL," "); + char* reason_str = strtok(NULL,""); + + if(!type_str||!nameOrIP_str||!duration_str||!reason_str)// ?!? input of single char "0"-"9" wouldn't detect when with: || !atoi(duration) + { + zprintf("Syntax: ban account|ip|character $AccountOrIpOrCharacter $duration[s|m|h|d] $reason \r\n"); + return; + } + + std::string type; + if(!consoleToUtf8(type_str,type)) // convert from console encoding to utf8 + return; + + std::string nameOrIP; + if(!consoleToUtf8(nameOrIP_str,nameOrIP)) // convert from console encoding to utf8 + return; + + std::string duration; + if(!consoleToUtf8(duration_str,duration)) // convert from console encoding to utf8 + return; + + std::string reason; + if(!consoleToUtf8(reason_str,reason)) // convert from console encoding to utf8 + return; + + switch (sWorld.BanAccount(type, nameOrIP, duration, reason, "Set by console.")) + { + case BAN_SUCCESS: + if(atoi(duration_str)>0) + zprintf("%s is banned for %s. Reason: %s.\r\n",nameOrIP.c_str(),secsToTimeString(TimeStringToSecs(duration_str),true,false).c_str(),reason.c_str()); + else + zprintf("%s is banned permanently. Reason: %s.\r\n",nameOrIP.c_str(),reason.c_str()); + break; + case BAN_NOTFOUND: + zprintf("%s %s not found\r\n", type.c_str(), nameOrIP.c_str()); + break; + case BAN_SYNTAX_ERROR: + zprintf("Syntax: ban account|ip|character $AccountOrIpOrCharacter $duration[s|m|h|d] $reason \r\n"); + break; + } +} + +/// Display %MaNGOS version +void CliVersion(char*,pPrintf zprintf) +{ + //<--maybe better append to info cmd + zprintf( "%s (world-daemon)\r\n", _FULLVERSION ); +} + +/// Unban an IP adress or a user account +void CliRemoveBan(char *command,pPrintf zprintf) +{ + ///- Get the command parameter + char *type_str = strtok(command," "); + char *nameorip_str = strtok(NULL," "); + if(!nameorip_str||!type_str) + { + zprintf("Syntax is: unban account|ip|character $nameorip\r\n"); + return; + } + + std::string type; + if(!consoleToUtf8(type_str,type)) // convert from console encoding to utf8 + return; + + std::string nameorip; + if(!consoleToUtf8(nameorip_str,nameorip)) // convert from console encoding to utf8 + return; + + if (!sWorld.RemoveBanAccount(type, nameorip)) + zprintf("%s %s not found\r\n", type.c_str(), nameorip.c_str()); + else + zprintf("We removed ban from %s: %s\r\n",type_str,nameorip.c_str()); +} + +/// 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 $account $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) + + std::string safe_account_name; + if(!consoleToUtf8(szAcc,safe_account_name)) // convert from console encoding to utf8 + return; + + ///- Convert Account name to Upper Format + AccountMgr::normilizeString(safe_account_name); + + ///- Escape the account name to allow quotes in names + 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 set %s gmlevel %d\r\n",safe_account_name.c_str(),lev); + } + else + { + zprintf("No account %s found\r\n",safe_account_name.c_str()); + } +} + +/// Set password for account +void CliSetPassword(char *command,pPrintf zprintf) +{ + ///- Get the command line arguments + char *szAcc = strtok(command," "); + char *szPassword1 = strtok(NULL," "); + char *szPassword2 = strtok(NULL," "); + + if(!szAcc||!szPassword1 || !szPassword2) + { + zprintf("Syntax is: setpass $account $password $password\r\n"); + return; + } + + std::string account_name; + if(!consoleToUtf8(szAcc,account_name)) // convert from console encoding to utf8 + return; + + std::string pass1; + if(!consoleToUtf8(szPassword1,pass1)) // convert from console encoding to utf8 + return; + + std::string pass2; + if(!consoleToUtf8(szPassword2,pass2)) // convert from console encoding to utf8 + return; + + uint32 acc_id = accmgr.GetId(szAcc); + if (!acc_id) + { + zprintf("Account '%s' does not exist!\r\n", account_name.c_str()); + return; + } + + if (pass1 != pass2) + { + zprintf("Password does not match the confirm password, password not changed!\r\n"); + return; + } + + AccountOpResult result = accmgr.ChangePassword(acc_id, pass1); + + switch(result) + { + case AOR_OK: + zprintf("The password was changed for account '%s' (ID: %u).\r\n",account_name.c_str(),acc_id); + break; + case AOR_PASS_TOO_LONG: + zprintf("Password can't be longer than 16 characters (client limit), password not changed!\r\n"); + break; + case AOR_NAME_NOT_EXIST: + zprintf("Account '%s' does not exist!\r\n", account_name.c_str()); + break; + case AOR_DB_INTERNAL_ERROR: + zprintf("Password not changed! (probably sql file format was updated)\r\n"); + break; + default: + zprintf("Password not changed! (unknown error\r\n"); + break; + } +} + +/// 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, " "); + char *szPassword = strtok(NULL, " "); + if(!szAcc || !szPassword) + { + zprintf("Syntax is: create $username $password\r\n"); + return; + } + + std::string account_name; + if(!consoleToUtf8(szAcc,account_name)) // convert from console encoding to utf8 + return; + + std::string password; + if(!consoleToUtf8(szPassword,password)) // convert from console encoding to utf8 + return; + + AccountOpResult result = accmgr.CreateAccount(account_name, password); + switch(result) + { + case AOR_OK: + zprintf("User %s with password %s created successfully\r\n",account_name.c_str(),password.c_str()); + break; + case AOR_NAME_TOO_LONG: + zprintf("Username %s is too long\r\n", account_name.c_str()); + break; + case AOR_NAME_ALREDY_EXIST: + zprintf("User %s already exists\r\n",account_name.c_str()); + break; + case AOR_DB_INTERNAL_ERROR: + zprintf("User %s with password %s NOT created (probably sql file format was updated)\r\n",account_name.c_str(),password.c_str()); + break; + default: + zprintf("User %s with password %s NOT created (unknown error)\r\n",account_name.c_str(),password.c_str()); + break; + } +} + +/// 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<CliTotalCmds;x++) + if(!strcmp(Commands[x].cmd,supposedCommand)) + { + sWorld.QueueCliCommand(new CliCommandHolder(&Commands[x], arguments, zprintf)); + bSuccess=true; + break; + } + + ///- Display an error message if the command is unknown + if(x==CliTotalCmds) + zprintf("Unknown command: %s\r\n", input); + } + } + if (!bSuccess) + zprintf("mangos>"); +} + +/// 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; + if(!consoleToUtf8(kickName,name)) // convert from console encoding to utf8 + return; + + if(!normalizePlayerName(name)) + return; + + 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; + if(!consoleToUtf8(charName,name)) // convert from console encoding to utf8 + return; + + if(!normalizePlayerName(name)) + return; + + std::string location; + if(!consoleToUtf8(locName,location)) // convert from console encoding to utf8 + return; + + 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(objmgr.GetMangosStringForDBCLocale(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,z,ort)) + { + zprintf(objmgr.GetMangosStringForDBCLocale(LANG_INVALID_TARGET_COORD),"\r\n",x,y,mapid); + return; + } + + Player *chr = objmgr.GetPlayer(name.c_str()); + if (chr) + { + + if(chr->IsBeingTeleported()==true) + { + zprintf(objmgr.GetMangosStringForDBCLocale(LANG_IS_TELEPORTED),"\r\n",chr->GetName()); + return; + } + + if(chr->isInFlight()) + { + zprintf(objmgr.GetMangosStringForDBCLocale(LANG_CHAR_IN_FLIGHT),"\r\n",chr->GetName()); + return; + } + + zprintf(objmgr.GetMangosStringForDBCLocale(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(objmgr.GetMangosStringForDBCLocale(LANG_TELEPORTING_TO),"\r\n",name.c_str(), objmgr.GetMangosStringForDBCLocale(LANG_OFFLINE), location.c_str()); + Player::SavePositionInDB(mapid,x,y,z,ort,MapManager::Instance().GetZoneId(mapid,x,y),guid); + } + else + zprintf(objmgr.GetMangosStringForDBCLocale(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 + { + std::string commandUtf8; + if(!consoleToUtf8(command,commandUtf8)) // convert from console encoding to utf8 + return; + + sWorld.SetMotd(commandUtf8); + zprintf("Message of the day changed to:\r\n%s\r\n", commandUtf8.c_str()); + } +} + +/// 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; + if(!consoleToUtf8(szAcc,safe_account_name)) // convert from console encoding to utf8 + return; + + ///- Convert Account name to Upper Format + AccountMgr::normilizeString(safe_account_name); + + ///- Escape the account name to allow quotes in names + 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 set %s to expansion allowed %d\r\n",safe_account_name.c_str(),lev); + + delete result; + } + else + { + zprintf("No account %s found\r\n",safe_account_name.c_str()); + } +} + +/// Save all players +void CliSave(char*,pPrintf zprintf) +{ + ///- Save players + ObjectAccessor::Instance().SaveAllPlayers(); + zprintf( objmgr.GetMangosStringForDBCLocale(LANG_PLAYERS_SAVED) ); + + ///- Send a message + sWorld.SendWorldText(LANG_PLAYERS_SAVED); +} + +/// Send a message to a player in game +void CliSend(char *playerN,pPrintf zprintf) +{ + ///- Get the command line arguments + char* name_str = strtok((char*)playerN, " "); + char* msg_str = strtok(NULL, ""); + + if(!name_str || !msg_str) + { + zprintf("Syntax: send $player $message (Player name is case sensitive)\r\n"); + return; + } + + std::string name; + if(!consoleToUtf8(name_str,name)) // convert from console encoding to utf8 + return; + + std::string msg; + if(!consoleToUtf8(msg_str,msg)) // convert from console encoding to utf8 + return; + + if(!normalizePlayerName(name)) + { + zprintf("Syntax: send $player $message (Player name is case sensitive)\r\n"); + return; + } + + ///- Find the player and check that he is not logging out. + Player *rPlayer = objmgr.GetPlayer(name.c_str()); + if(!rPlayer) + { + zprintf("Player %s not found!\r\n", name.c_str()); + return; + } + + if (rPlayer->GetSession()->isLogingOut()) + { + zprintf("Cannot send message while player %s is logging out!\r\n",name.c_str()); + return; + } + + ///- Send the message + //Use SendAreaTriggerMessage for fastest delivery. + rPlayer->GetSession()->SendAreaTriggerMessage("%s", msg.c_str()); + rPlayer->GetSession()->SendAreaTriggerMessage("|cffff0000[Message from administrator]:|r"); + + //Confirmation message + zprintf("Message '%s' sent to %s\r\n",msg.c_str(), name.c_str()); +} + +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 = "<unknown>"; 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,&UTF8ZPRINTF); + + if(sConfig.GetBoolDefault("BeepAtStart", true)) + { + 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(&UTF8ZPRINTF,command); + } + else if (feof(stdin)) + { + World::m_stopEvent = true; + } + } + + ///- End the database thread + WorldDatabase.ThreadEnd(); // free mySQL thread resources +} diff --git a/src/trinitycore/CliRunnable.h b/src/trinitycore/CliRunnable.h new file mode 100644 index 00000000000..2dc4a51d89a --- /dev/null +++ b/src/trinitycore/CliRunnable.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * 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 + +#ifndef __CLIRUNNABLE_H +#define __CLIRUNNABLE_H + +/// Command Line Interface handling thread +class CliRunnable : public ZThread::Runnable +{ + public: + void run(); +}; +#endif +/// @} diff --git a/src/trinitycore/Main.cpp b/src/trinitycore/Main.cpp new file mode 100644 index 00000000000..27e8c0861ce --- /dev/null +++ b/src/trinitycore/Main.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * 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 Mangos Daemon +/// @{ +/// \file + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "Config/ConfigEnv.h" +#include "Log.h" +#include "Master.h" + +#ifndef _TRINITY_CORE_CONFIG +# define _TRINITY_CORE_CONFIG "trinitycore.conf" +#endif //_TRINITY_CORE_CONFIG + +// Format is YYYYMMDDRR where RR is the change in the conf file +// for that day. +#ifndef _TRINITY_CORE_CONFVER +# define _TRINITY_CORE_CONFVER 2008022901 +#endif //_TRINITY_CORE_CONFVER + +#ifdef WIN32 +#include "ServiceWin32.h" +char serviceName[] = "mangosd"; +char serviceLongName[] = "MaNGOS world service"; +char serviceDescription[] = "Massive Network Game Object Server"; +/* + * -1 - not in service mode + * 0 - stopped + * 1 - running + * 2 - paused + */ +int m_ServiceStatus = -1; +#endif + +DatabaseType WorldDatabase; ///< Accessor to the world database +DatabaseType CharacterDatabase; ///< Accessor to the character database +DatabaseType loginDatabase; ///< Accessor to the realm/login database + +uint32 realmID; ///< Id of the realm + +/// Print out the usage string for this program on the console. +void usage(const char *prog) +{ + sLog.outString("Usage: \n %s [<options>]\n" + " -c config_file use config_file as configuration file\n\r" + #ifdef WIN32 + " Running as service functions:\n\r" + " --service run as service\n\r" + " -s install install service\n\r" + " -s uninstall uninstall service\n\r" + #endif + ,prog); +} + +/// Launch the mangos server +extern int main(int argc, char **argv) +{ + ///- Command line parsing to get the configuration file name + char const* cfg_file = _TRINITY_CORE_CONFIG; + int c=1; + while( c < argc ) + { + if( strcmp(argv[c],"-c") == 0) + { + if( ++c >= argc ) + { + sLog.outError("Runtime-Error: -c option requires an input argument"); + usage(argv[0]); + return 1; + } + else + cfg_file = argv[c]; + } + + #ifdef WIN32 + //////////// + //Services// + //////////// + if( strcmp(argv[c],"-s") == 0) + { + if( ++c >= argc ) + { + sLog.outError("Runtime-Error: -s option requires an input argument"); + usage(argv[0]); + return 1; + } + if( strcmp(argv[c],"install") == 0) + { + if (WinServiceInstall()) + sLog.outString("Installing service"); + return 1; + } + else if( strcmp(argv[c],"uninstall") == 0) + { + if(WinServiceUninstall()) + sLog.outString("Uninstalling service"); + return 1; + } + else + { + sLog.outError("Runtime-Error: unsupported option %s",argv[c]); + usage(argv[0]); + return 1; + } + } + if( strcmp(argv[c],"--service") == 0) + { + WinServiceRun(); + } + //// + #endif + ++c; + } + + if (!sConfig.SetSource(cfg_file)) + { + sLog.outError("Could not find configuration file %s.", cfg_file); + return 1; + } + + sLog.outString("Using configuration file %s.", cfg_file); + + uint32 confVersion = sConfig.GetIntDefault("ConfVersion", 0); + if (confVersion < _TRINITY_CORE_CONFVER) + { + sLog.outError("*****************************************************************************"); + sLog.outError(" WARNING: Your trinitycore.conf version indicates your conf file is out of date!"); + sLog.outError(" Please check for updates, as your current default values may cause"); + sLog.outError(" strange behavior."); + sLog.outError("*****************************************************************************"); + clock_t pause = 3000 + clock(); + + while (pause > clock()) {} + } + + ///- and run the 'Master' + /// \todo Why do we need this 'Master'? Can't all of this be in the Main as for Realmd? + return sMaster.Run(); + + // at sMaster return function exist with codes + // 0 - normal shutdown + // 1 - shutdown at error + // 2 - restart command used, this code can be used by restarter for restart mangosd +} + +/// @} diff --git a/src/trinitycore/Makefile.am b/src/trinitycore/Makefile.am new file mode 100644 index 00000000000..624bfb93c37 --- /dev/null +++ b/src/trinitycore/Makefile.am @@ -0,0 +1,78 @@ +# Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +# +# 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 + +## Process this file with automake to produce Makefile.in + +## Build world list daemon as standalone program +bin_PROGRAMS = trinity-core + +## Preprocessor flags +trinity_core_CPPFLAGS = \ +$(MYSQL_INCLUDES) \ +$(POSTGRE_INCLUDES) \ +-I$(top_srcdir)/dep/include \ +-I$(top_srcdir)/src/shared \ +-I$(top_srcdir)/src/framework \ +-I$(top_srcdir)/src/game \ +-D_TRINITY_CORE_CONFIG='"$(sysconfdir)/trinitycore.conf"' + +## Sources +trinity_core_SOURCES = \ +$(srcdir)/CliRunnable.cpp \ +$(srcdir)/CliRunnable.h \ +$(srcdir)/Main.cpp \ +$(srcdir)/Master.cpp \ +$(srcdir)/Master.h \ +$(srcdir)/RASocket.cpp \ +$(srcdir)/RASocket.h \ +$(srcdir)/WorldRunnable.cpp \ +$(srcdir)/WorldRunnable.h + +## Convenience libs to add +trinity_core_LDADD = \ +$(top_builddir)/src/game/libgame.a \ +$(top_builddir)/src/shared/libshared.a \ +$(top_builddir)/src/shared/vmap/libvmaps.a \ +$(top_builddir)/src/framework/libmangosframework.a \ +$(top_builddir)/dep/src/sockets/libmangossockets.a \ +$(top_builddir)/dep/src/zthread/libZThread.la \ +$(top_builddir)/dep/src/g3dlite/libg3dlite.a + +if USE_TSCRIPTS +trinity_core_LDADD += $(top_builddir)/src/bindings/scripts/libtrinityscript.la +else +trinity_core_LDADD += $(top_builddir)/src/bindings/interface/libtrinityscript.la +endif + +## Linker flags +trinity_core_LDFLAGS = $(MYSQL_LIBS) $(POSTGRE_LIBS) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic + +## Additional files to install +sysconf_DATA = \ + trinitycore.conf.dist + +## Prevend overwrite of the config file, if its already installed +install-data-hook: + @list='$(sysconf_DATA)'; for p in $$list; do \ + dest=`echo $$p | sed -e s/.dist//`; \ + if test -f $(DESTDIR)$(sysconfdir)/$$dest; then \ + echo "$@ will not overwrite existing $(DESTDIR)$(sysconfdir)/$$dest"; \ + else \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$dest"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$dest; \ + fi; \ + done + diff --git a/src/trinitycore/Master.cpp b/src/trinitycore/Master.cpp new file mode 100644 index 00000000000..cf094bbd048 --- /dev/null +++ b/src/trinitycore/Master.cpp @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * 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 + */ + +/** \file + \ingroup mangosd +*/ + +#include "Master.h" +#include "sockets/SocketHandler.h" +#include "sockets/ListenSocket.h" +#include "WorldSocket.h" +#include "WorldSocketMgr.h" +#include "WorldRunnable.h" +#include "World.h" +#include "Log.h" +#include "Timer.h" +#include <signal.h> +#include "Policies/SingletonImp.h" +#include "SystemConfig.h" +#include "Config/ConfigEnv.h" +#include "Database/DatabaseEnv.h" +#include "CliRunnable.h" +#include "RASocket.h" +#include "ScriptCalls.h" +#include "Util.h" + +#include "sockets/TcpSocket.h" +#include "sockets/Utility.h" +#include "sockets/Parse.h" +#include "sockets/Socket.h" + +#ifdef WIN32 +#include "ServiceWin32.h" +extern int m_ServiceStatus; +#endif + +/// \todo Warning disabling not useful under VC++2005. Can somebody say on which compiler it is useful? +#pragma warning(disable:4305) + +INSTANTIATE_SINGLETON_1( Master ); + +volatile uint32 Master::m_masterLoopCounter = 0; + +class FreezeDetectorRunnable : public ZThread::Runnable +{ +public: + FreezeDetectorRunnable() { _delaytime = 0; } + uint32 m_loops, m_lastchange; + uint32 w_loops, w_lastchange; + uint32 _delaytime; + void SetDelayTime(uint32 t) { _delaytime = t; } + void run(void) + { + if(!_delaytime) + return; + sLog.outString("Starting up anti-freeze thread (%u seconds max stuck time)...",_delaytime/1000); + m_loops = 0; + w_loops = 0; + m_lastchange = 0; + w_lastchange = 0; + while(!World::m_stopEvent) + { + ZThread::Thread::sleep(1000); + uint32 curtime = getMSTime(); + //DEBUG_LOG("anti-freeze: time=%u, counters=[%u; %u]",curtime,Master::m_masterLoopCounter,World::m_worldLoopCounter); + + // normal work + if(m_loops != Master::m_masterLoopCounter) + { + m_lastchange = curtime; + m_loops = Master::m_masterLoopCounter; + } + // possible freeze + else if(getMSTimeDiff(m_lastchange,curtime) > _delaytime) + { + sLog.outError("Main/Sockets Thread hangs, kicking out server!"); + *((uint32 volatile*)NULL) = 0; // bang crash + } + + // normal work + if(w_loops != World::m_worldLoopCounter) + { + w_lastchange = curtime; + w_loops = World::m_worldLoopCounter; + } + // possible freeze + else if(getMSTimeDiff(w_lastchange,curtime) > _delaytime) + { + sLog.outError("World Thread hangs, kicking out server!"); + *((uint32 volatile*)NULL) = 0; // bang crash + } + } + sLog.outString("Anti-freeze thread exiting without problems."); + } +}; + +Master::Master() +{ +} + +Master::~Master() +{ +} + +/// Main function +int Master::Run() +{ + sLog.outString( "%s (core-daemon)", _FULLVERSION ); + sLog.outString( "<Ctrl-C> to stop.\n" ); + + sLog.outTitle( " ______ __"); + sLog.outTitle( "/\\__ _\\ __ __/\\ \\__"); + sLog.outTitle( "\\/_/\\ \\/ _ __ /\\_\\ ___ /\\_\\ \\ ,_\\ __ __"); + sLog.outTitle( " \\ \\ \\/\\`'__\\/\\ \\ /' _ `\\/\\ \\ \\ \\/ /\\ \\/\\ \\"); + sLog.outTitle( " \\ \\ \\ \\ \\/ \\ \\ \\/\\ \\/\\ \\ \\ \\ \\ \\_\\ \\ \\_\\ \\"); + sLog.outTitle( " \\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\_\\ \\_\\ \\__\\\\/`____ \\"); + sLog.outTitle( " \\/_/\\/_/ \\/_/\\/_/\\/_/\\/_/\\/__/ `/___/> \\"); + sLog.outTitle( " C O R E /\\___/"); + sLog.outTitle( "http://TrinityCore.org \\/__/\n"); + + /// worldd PID file creation + std::string pidfile = sConfig.GetStringDefault("PidFile", ""); + if(!pidfile.empty()) + { + uint32 pid = CreatePIDFile(pidfile); + if( !pid ) + { + sLog.outError( "Cannot create PID file %s.\n", pidfile.c_str() ); + return 1; + } + + sLog.outString( "Daemon PID: %u\n", pid ); + } + + ///- Start the databases + if (!_StartDB()) + return 1; + + ///- Initialize the World + sWorld.SetInitialWorldSettings(); + + ///- Launch the world listener socket + port_t wsport = sWorld.getConfig(CONFIG_PORT_WORLD); + std::string bind_ip = sConfig.GetStringDefault("BindIP", "0.0.0.0"); + + SocketHandler h; + ListenSocket<WorldSocket> worldListenSocket(h); + if (worldListenSocket.Bind(bind_ip.c_str(),wsport)) + { + clearOnlineAccounts(); + sLog.outError("MaNGOS cannot bind to %s:%d",bind_ip.c_str(), wsport); + return 1; + } + + h.Add(&worldListenSocket); + + ///- Catch termination signals + _HookSignals(); + + ///- Launch WorldRunnable thread + ZThread::Thread t(new WorldRunnable); + t.setPriority ((ZThread::Priority )2); + + // set server online + loginDatabase.PExecute("UPDATE realmlist SET color = 0, population = 0 WHERE id = '%d'",realmID); + +#ifdef WIN32 + if (sConfig.GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/) +#else + if (sConfig.GetBoolDefault("Console.Enable", true)) +#endif + { + ///- Launch CliRunnable thread + ZThread::Thread td1(new CliRunnable); + } + + ///- Launch the RA listener socket + ListenSocket<RASocket> RAListenSocket(h); + if (sConfig.GetBoolDefault("Ra.Enable", false)) + { + port_t raport = sConfig.GetIntDefault( "Ra.Port", 3443 ); + std::string stringip = sConfig.GetStringDefault( "Ra.IP", "0.0.0.0" ); + ipaddr_t raip; + if(!Utility::u2ip(stringip, raip)) + sLog.outError( "MaNGOS RA can not bind to ip %s", stringip.c_str()); + else if (RAListenSocket.Bind(raip, raport)) + sLog.outError( "MaNGOS RA can not bind to port %d on %s", raport, stringip.c_str()); + else + { + h.Add(&RAListenSocket); + + sLog.outString("Starting Remote access listner on port %d on %s", raport, stringip.c_str()); + } + } + + ///- Handle affinity for multiple processors and process priority on Windows + #ifdef WIN32 + { + HANDLE hProcess = GetCurrentProcess(); + + uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0); + if(Aff > 0) + { + ULONG_PTR appAff; + ULONG_PTR sysAff; + + if(GetProcessAffinityMask(hProcess,&appAff,&sysAff)) + { + ULONG_PTR curAff = Aff & appAff; // remove non accessible processors + + if(!curAff ) + { + sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for mangosd. Accessible processors bitmask (hex): %x",Aff,appAff); + } + else + { + if(SetProcessAffinityMask(hProcess,curAff)) + sLog.outString("Using processors (bitmask, hex): %x", curAff); + else + sLog.outError("Can't set used processors (hex): %x",curAff); + } + } + sLog.outString(); + } + + bool Prio = sConfig.GetBoolDefault("ProcessPriority", false); + +// if(Prio && (m_ServiceStatus == -1)/* need set to default process priority class in service mode*/) + if(Prio) + { + if(SetPriorityClass(hProcess,HIGH_PRIORITY_CLASS)) + sLog.outString("TrinityCore process priority class set to HIGH"); + else + sLog.outError("ERROR: Can't set mangosd process priority class."); + sLog.outString(); + } + } + #endif + + uint32 realCurrTime, realPrevTime; + realCurrTime = realPrevTime = getMSTime(); + + uint32 socketSelecttime = sWorld.getConfig(CONFIG_SOCKET_SELECTTIME); + + // maximum counter for next ping + uint32 numLoops = (sConfig.GetIntDefault( "MaxPingTime", 30 ) * (MINUTE * 1000000 / socketSelecttime)); + uint32 loopCounter = 0; + + ///- Start up freeze catcher thread + uint32 freeze_delay = sConfig.GetIntDefault("MaxCoreStuckTime", 0); + if(freeze_delay) + { + FreezeDetectorRunnable *fdr = new FreezeDetectorRunnable(); + fdr->SetDelayTime(freeze_delay*1000); + ZThread::Thread t(fdr); + t.setPriority(ZThread::High); + } + + ///- Wait for termination signal + while (!World::m_stopEvent) + { + ++Master::m_masterLoopCounter; +#ifdef WIN32 + if (m_ServiceStatus == 0) World::m_stopEvent = true; + while (m_ServiceStatus == 2) Sleep(1000); +#endif + if (realPrevTime > realCurrTime) + realPrevTime = 0; + + realCurrTime = getMSTime(); + sWorldSocketMgr.Update( getMSTimeDiff(realPrevTime,realCurrTime) ); + realPrevTime = realCurrTime; + + h.Select(0, socketSelecttime); + + // ping if need + if( (++loopCounter) == numLoops ) + { + loopCounter = 0; + sLog.outDetail("Ping MySQL to keep connection alive"); + delete WorldDatabase.Query("SELECT 1 FROM command LIMIT 1"); + delete loginDatabase.Query("SELECT 1 FROM realmlist LIMIT 1"); + delete CharacterDatabase.Query("SELECT 1 FROM bugreport LIMIT 1"); + } + } + + // set server offline + loginDatabase.PExecute("UPDATE realmlist SET color = 2 WHERE id = '%d'",realmID); + + ///- Remove signal handling before leaving + _UnhookSignals(); + + // when the main thread closes the singletons get unloaded + // since worldrunnable uses them, it will crash if unloaded after master + t.wait(); + + ///- Clean database before leaving + clearOnlineAccounts(); + + ///- Wait for delay threads to end + CharacterDatabase.HaltDelayThread(); + WorldDatabase.HaltDelayThread(); + loginDatabase.HaltDelayThread(); + + sLog.outString( "Halting process..." ); + + #ifdef WIN32 + if (sConfig.GetBoolDefault("Console.Enable", true)) + { + // this only way to terminate CLI thread exist at Win32 (alt. way exist only in Windows Vista API) + //_exit(1); + // send keyboard input to safely unblock the CLI thread + INPUT_RECORD b[5]; + HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); + b[0].EventType = KEY_EVENT; + b[0].Event.KeyEvent.bKeyDown = TRUE; + b[0].Event.KeyEvent.uChar.AsciiChar = 'X'; + b[0].Event.KeyEvent.wVirtualKeyCode = 'X'; + b[0].Event.KeyEvent.wRepeatCount = 1; + + b[1].EventType = KEY_EVENT; + b[1].Event.KeyEvent.bKeyDown = FALSE; + b[1].Event.KeyEvent.uChar.AsciiChar = 'X'; + b[1].Event.KeyEvent.wVirtualKeyCode = 'X'; + b[1].Event.KeyEvent.wRepeatCount = 1; + + b[2].EventType = KEY_EVENT; + b[2].Event.KeyEvent.bKeyDown = TRUE; + b[2].Event.KeyEvent.dwControlKeyState = 0; + b[2].Event.KeyEvent.uChar.AsciiChar = '\r'; + b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN; + b[2].Event.KeyEvent.wRepeatCount = 1; + b[2].Event.KeyEvent.wVirtualScanCode = 0x1c; + + b[3].EventType = KEY_EVENT; + b[3].Event.KeyEvent.bKeyDown = FALSE; + b[3].Event.KeyEvent.dwControlKeyState = 0; + b[3].Event.KeyEvent.uChar.AsciiChar = '\r'; + b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN; + b[3].Event.KeyEvent.wVirtualScanCode = 0x1c; + b[3].Event.KeyEvent.wRepeatCount = 1; + DWORD numb; + BOOL ret = WriteConsoleInput(hStdIn, b, 4, &numb); + } + #endif + + // for some unknown reason, unloading scripts here and not in worldrunnable + // fixes a memory leak related to detaching threads from the module + UnloadScriptingModule(); + + return sWorld.GetShutdownMask() & SHUTDOWN_MASK_RESTART ? 2 : 0; +} + +/// Initialize connection to the databases +bool Master::_StartDB() +{ + ///- Get world database info from configuration file + std::string dbstring; + if(!sConfig.GetString("WorldDatabaseInfo", &dbstring)) + { + sLog.outError("Database not specified in configuration file"); + return false; + } + sLog.outString("World Database: %s", dbstring.c_str()); + + ///- Initialise the world database + if(!WorldDatabase.Initialize(dbstring.c_str())) + { + sLog.outError("Cannot connect to world database %s",dbstring.c_str()); + return false; + } + + if(!sConfig.GetString("CharacterDatabaseInfo", &dbstring)) + { + sLog.outError("Character Database not specified in configuration file"); + return false; + } + sLog.outString("Character Database: %s", dbstring.c_str()); + + ///- Initialise the Character database + if(!CharacterDatabase.Initialize(dbstring.c_str())) + { + sLog.outError("Cannot connect to Character database %s",dbstring.c_str()); + return false; + } + + ///- Get login database info from configuration file + if(!sConfig.GetString("LoginDatabaseInfo", &dbstring)) + { + sLog.outError("Login database not specified in configuration file"); + return false; + } + + ///- Initialise the login database + sLog.outString("Login Database: %s", dbstring.c_str() ); + if(!loginDatabase.Initialize(dbstring.c_str())) + { + sLog.outError("Cannot connect to login database %s",dbstring.c_str()); + return false; + } + + ///- Get the realm Id from the configuration file + realmID = sConfig.GetIntDefault("RealmID", 0); + if(!realmID) + { + sLog.outError("Realm ID not defined in configuration file"); + return false; + } + sLog.outString("Realm running as realm ID %d", realmID); + + ///- Clean the database before starting + clearOnlineAccounts(); + + QueryResult* result = WorldDatabase.Query("SELECT version FROM db_version LIMIT 1"); + if(result) + { + Field* fields = result->Fetch(); + + sLog.outString("Using %s", fields[0].GetString()); + delete result; + } + else + sLog.outString("Using unknown world database."); + + return true; +} + +/// Clear 'online' status for all accounts with characters in this realm +void Master::clearOnlineAccounts() +{ + // Cleanup online status for characters hosted at current realm + /// \todo Only accounts with characters logged on *this* realm should have online status reset. Move the online column from 'account' to 'realmcharacters'? + loginDatabase.PExecute( + "UPDATE account SET online = 0 WHERE online > 0 " + "AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = '%d')",realmID); + + + CharacterDatabase.Execute("UPDATE characters SET online = 0"); +} + +/// Handle termination signals +/** Put the World::m_stopEvent to 'true' if a termination signal is caught **/ +void Master::_OnSignal(int s) +{ + switch (s) + { + case SIGINT: + case SIGTERM: + #ifdef _WIN32 + case SIGBREAK: + #endif + World::m_stopEvent = true; + break; + } + + signal(s, _OnSignal); +} + +/// Define hook '_OnSignal' for all termination signals +void Master::_HookSignals() +{ + signal(SIGINT, _OnSignal); + signal(SIGTERM, _OnSignal); + #ifdef _WIN32 + signal(SIGBREAK, _OnSignal); + #endif +} + +/// Unhook the signals before leaving +void Master::_UnhookSignals() +{ + signal(SIGINT, 0); + signal(SIGTERM, 0); + #ifdef _WIN32 + signal(SIGBREAK, 0); + #endif +} diff --git a/src/trinitycore/Master.h b/src/trinitycore/Master.h new file mode 100644 index 00000000000..ce756545720 --- /dev/null +++ b/src/trinitycore/Master.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * 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 + +#ifndef _MASTER_H +#define _MASTER_H + +#include "Common.h" +#include "Policies/Singleton.h" + +/// Start the server +class Master +{ + public: + Master(); + ~Master(); + int Run(); + static volatile uint32 m_masterLoopCounter; + + private: + bool _StartDB(); + + void _HookSignals(); + void _UnhookSignals(); + static void _OnSignal(int s); + + void clearOnlineAccounts(); +}; + +#define sMaster MaNGOS::Singleton<Master>::Instance() +#endif +/// @} diff --git a/src/trinitycore/RASocket.cpp b/src/trinitycore/RASocket.cpp new file mode 100644 index 00000000000..8baa5d34e39 --- /dev/null +++ b/src/trinitycore/RASocket.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * 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 + */ + +/** \file + \ingroup mangosd +*/ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "Log.h" +#include "RASocket.h" +#include "World.h" +#include "Config/ConfigEnv.h" +#include "Util.h" +#include "AccountMgr.h" + +/// \todo Make this thread safe if in the future 2 admins should be able to log at the same time. +SOCKET r; + +#define dropclient {Sendf("I'm busy right now, come back later."); \ + SetCloseAndDelete(); \ + return; \ + } + +uint32 iSession=0; ///< Session number (incremented each time a new connection is made) +unsigned int iUsers=0; ///< Number of active administrators + +typedef int(* pPrintf)(const char*,...); + +void ParseCommand(pPrintf zprintf, char*command); + +/// RASocket constructor +RASocket::RASocket(ISocketHandler &h): TcpSocket(h) +{ + + ///- Increment the session number + iSess =iSession++ ; + + ///- Get the config parameters + bSecure = sConfig.GetBoolDefault( "RA.Secure", true ); + iMinLevel = sConfig.GetIntDefault( "RA.MinLevel", 3 ); + + ///- Initialize buffer and data + iInputLength=0; + buff=new char[RA_BUFF_SIZE]; + stage=NONE; +} + +/// RASocket destructor +RASocket::~RASocket() +{ + ///- Delete buffer and decrease active admins count + delete [] buff; + + sLog.outRALog("Connection was closed.\n"); + + if(stage==OK) + iUsers--; +} + +/// Accept an incoming connection +void RASocket::OnAccept() +{ + std::string ss=GetRemoteAddress(); + sLog.outRALog("Incoming connection from %s.\n",ss.c_str()); + ///- If there is already an active admin, drop the connection + if(iUsers) + dropclient + + ///- Else print Motd + Sendf("%s\r\n",sWorld.GetMotd()); +} + +/// Read data from the network +void RASocket::OnRead() +{ + ///- Read data and check input length + TcpSocket::OnRead(); + + unsigned int sz=ibuf.GetLength(); + if(iInputLength+sz>=RA_BUFF_SIZE) + { + sLog.outRALog("Input buffer overflow, possible DOS attack.\n"); + SetCloseAndDelete(); + return; + } + + ///- If there is already an active admin (other than you), drop the connection + if(stage!=OK && iUsers) + dropclient + + char *inp = new char [sz+1]; + ibuf.Read(inp,sz); + + /// \todo Can somebody explain this 'Linux bugfix'? + if(stage==NONE) + if(sz>4) //linux remote telnet + if(memcmp(inp ,"USER ",5)) + { + delete [] inp;return; + printf("lin bugfix"); + } //linux bugfix + + ///- Discard data after line break or line feed + bool gotenter=false; + unsigned int y=0; + for(;y<sz;y++) + if(inp[y]=='\r'||inp[y]=='\n') + { + gotenter=true; + break; + } + + //No buffer overflow (checked above) + memcpy(&buff[iInputLength],inp,y); + iInputLength+=y; + delete [] inp; + if(gotenter) + { + + buff[iInputLength]=0; + iInputLength=0; + switch(stage) + { + /// <ul> <li> If the input is 'USER <username>' + case NONE: + if(!memcmp(buff,"USER ",5)) //got "USER" cmd + { + szLogin=&buff[5]; + + ///- Get the gmlevel and password from the account table + std::string login = szLogin; + + ///- Convert Account name to Upper Format + AccountMgr::normilizeString(login); + + ///- Escape the Login to allow quotes in names + loginDatabase.escape_string(login); + + QueryResult* result = loginDatabase.PQuery("SELECT gmlevel FROM account WHERE username = '%s'",login.c_str()); + + ///- If the user is not found, deny access + if(!result) + { + Sendf("-No such user.\r\n"); + sLog.outRALog("User %s does not exist.\n",szLogin.c_str()); + if(bSecure)SetCloseAndDelete(); + } + else + { + Field *fields = result->Fetch(); + + //szPass=fields[0].GetString(); + + ///- if gmlevel is too low, deny access + if(fields[0].GetUInt32()<iMinLevel) + { + Sendf("-Not enough privileges.\r\n"); + sLog.outRALog("User %s has no privilege.\n",szLogin.c_str()); + if(bSecure)SetCloseAndDelete(); + } else + { + stage=LG; + } + delete result; + } + } + break; + ///<li> If the input is 'PASS <password>' (and the user already gave his username) + case LG: + if(!memcmp(buff,"PASS ",5)) //got "PASS" cmd + { //login+pass ok + ///- If password is correct, increment the number of active administrators + std::string login = szLogin; + std::string pw = &buff[5]; + + AccountMgr::normilizeString(login); + AccountMgr::normilizeString(pw); + loginDatabase.escape_string(login); + loginDatabase.escape_string(pw); + + QueryResult *check = loginDatabase.PQuery("SELECT 1 FROM account WHERE username = '%s' AND sha_pass_hash=SHA1(CONCAT(username,':','%s'))", login.c_str(), pw.c_str()); + if(check) + { + delete check; + r=GetSocket(); + stage=OK; + ++iUsers; + + Sendf("+Logged in.\r\n"); + sLog.outRALog("User %s has logged in.\n",szLogin.c_str()); + Sendf("mangos>"); + } + else + { + ///- Else deny access + Sendf("-Wrong pass.\r\n"); + sLog.outRALog("User %s has failed to log in.\n",szLogin.c_str()); + if(bSecure)SetCloseAndDelete(); + } + } + break; + ///<li> If user is logged, parse and execute the command + case OK: + if(strlen(buff)) + { + sLog.outRALog("Got '%s' cmd.\n",buff); + ParseCommand(&RASocket::zprintf , buff); + } + else + Sendf("mangos>"); + break; + ///</ul> + }; + + } +} + +/// Output function +int RASocket::zprintf( const char * szText, ... ) +{ + if( !szText ) return 0; + va_list ap; + va_start(ap, szText); + /// \todo Remove buffer length here. Can be >1024 (e.g. list of users) + char *megabuffer=new char[1024]; + unsigned int sz=vsnprintf(megabuffer,1024,szText,ap); + #ifdef RA_CRYPT + Encrypt(megabuffer,sz); + #endif + + send(r,megabuffer,sz,0); + delete [] megabuffer; + va_end(ap); + return 0; +} diff --git a/src/trinitycore/RASocket.h b/src/trinitycore/RASocket.h new file mode 100644 index 00000000000..d082c45112a --- /dev/null +++ b/src/trinitycore/RASocket.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * 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 + +#ifndef _RASOCKET_H +#define _RASOCKET_H + +#include "Common.h" +#include "sockets/TcpSocket.h" + +#define RA_BUFF_SIZE 1024 + +class ISocketHandler; + +/// Remote Administration socket +class RASocket: public TcpSocket +{ + public: + + RASocket(ISocketHandler& h); + ~RASocket(); + + void OnAccept(); + void OnRead(); + + private: + + char * buff; + std::string szLogin; + uint32 iSess; + unsigned int iInputLength; + bool bLog; + bool bSecure; //kick on wrong pass, non exist. user, user with no priv + //will protect from DOS, bruteforce attacks + //some 'smart' protection must be added for more scurity + uint8 iMinLevel; + enum + { + NONE, //initial value + LG, //only login was entered + OK, //both login and pass were given, and they are correct and user have enough priv. + }stage; + + static int zprintf( const char * szText, ... ); +}; +#endif +/// @} diff --git a/src/trinitycore/TrinityCore.ico b/src/trinitycore/TrinityCore.ico Binary files differnew file mode 100644 index 00000000000..8df5f0174bd --- /dev/null +++ b/src/trinitycore/TrinityCore.ico diff --git a/src/trinitycore/TrinityCore.rc b/src/trinitycore/TrinityCore.rc new file mode 100644 index 00000000000..7e00bb16d8b --- /dev/null +++ b/src/trinitycore/TrinityCore.rc @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * 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 + */ + +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APPICON ICON "TrinityCore.ico" + +///////////////////////////////////////////////////////////////////////////// +// Neutre (Par défaut système) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,1,6700,670 + PRODUCTVERSION 0,1,6700,670 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x0L + FILETYPE 0x0L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080004b0" + BEGIN + VALUE "FileDescription", "TrinityCore" + VALUE "FileVersion", "0, 1, 6700, 670" + VALUE "InternalName", "TrinityCore" + VALUE "LegalCopyright", "Copyright (C) 2008" + VALUE "OriginalFilename", "TrinityCore.exe" + VALUE "ProductName", "TrinityCore" + VALUE "ProductVersion", "0, 1, 6700, 670" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x800, 1200 + END +END +#endif diff --git a/src/trinitycore/WorldRunnable.cpp b/src/trinitycore/WorldRunnable.cpp new file mode 100644 index 00000000000..6dae1575ac2 --- /dev/null +++ b/src/trinitycore/WorldRunnable.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * 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 + */ + +/** \file + \ingroup mangosd +*/ + +#include "Common.h" +#include "World.h" +#include "WorldRunnable.h" +#include "Timer.h" +#include "ObjectAccessor.h" +#include "MapManager.h" + +#include "Database/DatabaseEnv.h" + +#ifdef WIN32 +#define WORLD_SLEEP_CONST 50 +#else +#define WORLD_SLEEP_CONST 100 //Is this still needed?? [On linux some time ago not working 50ms] +#endif + +/// Heartbeat for the World +void WorldRunnable::run() +{ + ///- Init new SQL thread for the world database + WorldDatabase.ThreadStart(); // let thread do safe mySQL requests (one connection call enough) + sWorld.InitResultQueue(); + + uint32 realCurrTime = 0; + uint32 realPrevTime = getMSTime(); + + uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST + + ///- While we have not World::m_stopEvent, update the world + while (!World::m_stopEvent) + { + ++World::m_worldLoopCounter; + realCurrTime = getMSTime(); + + uint32 diff = getMSTimeDiff(realPrevTime,realCurrTime); + + sWorld.Update( diff ); + realPrevTime = realCurrTime; + + // diff (D0) include time of previous sleep (d0) + tick time (t0) + // we want that next d1 + t1 == WORLD_SLEEP_CONST + // we can't know next t1 and then can use (t0 + d1) == WORLD_SLEEP_CONST requirement + // d1 = WORLD_SLEEP_CONST - t0 = WORLD_SLEEP_CONST - (D0 - d0) = WORLD_SLEEP_CONST + d0 - D0 + if (diff <= WORLD_SLEEP_CONST+prevSleepTime) + { + prevSleepTime = WORLD_SLEEP_CONST+prevSleepTime-diff; + ZThread::Thread::sleep(prevSleepTime); + } + else + prevSleepTime = 0; + } + + sWorld.KickAllQueued(); // kick all queued players (and prevent its login at kick in game players) + sWorld.KickAll(); // save and kick all players + sWorld.UpdateSessions( 1 ); // real players unload required UpdateSessions call + + MapManager::Instance().UnloadAll(); // unload all grids (including locked in memory) + + ///- End the database thread + WorldDatabase.ThreadEnd(); // free mySQL thread resources +} diff --git a/src/trinitycore/WorldRunnable.h b/src/trinitycore/WorldRunnable.h new file mode 100644 index 00000000000..2fec658f5d1 --- /dev/null +++ b/src/trinitycore/WorldRunnable.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * 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 + +#ifndef __WORLDRUNNABLE_H +#define __WORLDRUNNABLE_H + +/// Heartbeat thread for the World +class WorldRunnable : public ZThread::Runnable +{ + public: + void run(); +}; +#endif +/// @} diff --git a/src/trinitycore/monitor-mangosd b/src/trinitycore/monitor-mangosd new file mode 100644 index 00000000000..a740ae5e8fa --- /dev/null +++ b/src/trinitycore/monitor-mangosd @@ -0,0 +1,18 @@ +#!/bin/bash +# Massive Network Game Object Server +# Monitoring Script + +pid=`ps ax | awk '($5 ~ /mangos-worldd/) { print $1 }'` +cpu=`top -b -n 1 -p $pid | awk '($12 ~ /mangos-worldd/) { print $9 }'` +#echo $pid +#echo $cpu +intcpu=${cpu%.*} +#echo $intcpu +if [ "$intcpu" -gt "95" ] +then + kill -9 $pid + echo "Killed MaNGOS for exceeding it's cpu limit." + echo `date` ", Killed MaNGOS for $intcpu% CPU Usage." >> serverlog +else + echo "MaNGOS Passes the cpu test." +fi diff --git a/src/trinitycore/resource.h b/src/trinitycore/resource.h new file mode 100644 index 00000000000..7e7d8e4b76f --- /dev/null +++ b/src/trinitycore/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by TrinityCore.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/trinitycore/run-mangosd b/src/trinitycore/run-mangosd new file mode 100644 index 00000000000..f307bd9e1ad --- /dev/null +++ b/src/trinitycore/run-mangosd @@ -0,0 +1,14 @@ +#!/bin/bash +# Trinity Server +# autorestart Script + +while : +do + echo "TrinityCore daemon restarted" + echo `date` >> crash.log & + ./mangosd | tail -n 20 >> crash.log + echo " " >> crash.log & + pid=`ps ax | awk '($5 ~ /trinitycore/) { print $1 }'` + wait $pid + echo `date` ", TrinityCore daemon crashed and restarted." >> serverlog +done diff --git a/src/trinitycore/trinitycore.conf.dist b/src/trinitycore/trinitycore.conf.dist new file mode 100644 index 00000000000..17678bee08d --- /dev/null +++ b/src/trinitycore/trinitycore.conf.dist @@ -0,0 +1,1027 @@ +##################################### +# MaNGOS Configuration file # +##################################### +ConfVersion=2008080101 + +################################################################################################################### +# CONNECTIONS AND DIRECTORIES +# +# RealmID +# RealmID must match the realmlist inside the realmd database +# +# DataDir +# Data directory setting. +# Important: DataDir needs to be quoted, as it is a string which may contain space characters. +# Example: "@prefix@/share/mangos" +# +# LogsDir +# Logs directory setting. +# Important: Logs dir must exists, or all logs need to be disabled +# Default: "" - no log directory prefix, if used log names isn't absolute path +# then logs will be stored in current directory for run program. +# +# +# LoginDatabaseInfo +# WorldDatabaseInfo +# CharacterDatabaseInfo +# Database connection settings for the world server. +# Default: hostname;port;username;password;database +# .;somenumber;username;password;database - use named pipes at Windows +# Named pipes: mySQL required adding "enable-named-pipe" to [mysqld] section my.ini +# .;/path/to/unix_socket;username;password;database - use Unix sockets at Unix/Linux +# Unix sockets: experimental, not tested +# +# MaxPingTime +# Settings for maximum database-ping interval (minutes between pings) +# +# WorldServerPort +# Default WorldServerPort +# +# BindIP +# Bind World Server to IP/hostname +# +################################################################################################################### + +RealmID = 1 +DataDir = "." +LogsDir = "" +LoginDatabaseInfo = "127.0.0.1;3306;root;mangos;realmd" +WorldDatabaseInfo = "127.0.0.1;3306;root;mangos;mangos" +CharacterDatabaseInfo = "127.0.0.1;3306;root;mangos;characters" +MaxPingTime = 30 +WorldServerPort = 8085 +BindIP = "0.0.0.0" + +################################################################################################################### +# PERFORMANCE SETINGS +# +# UseProcessors +# Used processors mask for multi-processors system (Used only at Windows) +# Default: 0 (selected by OS) +# number (bitmask value of selected processors) +# +# ProcessPriority +# Process proirity setting (Used only at Windows) +# Default: 1 (HIGH) +# 0 (Normal) +# +# Compression +# Compression level for update packages sent to client (1..9) +# Default: 1 (speed) +# 9 (best compression) +# +# TcpNoDelay +# TCP Nagle algorithm setting +# Default: 0 (enable Nagle algorithm, less traffic, more latency) +# 1 (TCP_NO_DELAY, disable Nagle algorithm, more traffic but less latency) +# +# PlayerLimit +# Maximum number of players in the world. Excluding Mods, GM's and Admins +# Default: 100 +# 0 (for infinite players) +# -1 (for Mods, GM's and Admins only) +# -2 (for GM's and Admins only) +# -3 (for Admins only) +# +# SaveRespawnTimeImmediately +# Save respawn time for creatures at death and for gameobjects at use/open +# Default: 1 (save creature/gameobject respawn time without waiting grid unload) +# 0 (save creature/gameobject respawn time at grid unload) +# +# MaxOverspeedPings +# Maximum overspeed ping count before player kick (minimum is 2, 0 used for disable check) +# Default: 2 +# +# GridUnload +# Unload grids (if you have lot memory you can disable it to speed up player move to new grids second time) +# Default: 1 (unload grids) +# 0 (do not unload grids) +# +# SocketSelectTime +# Socket select time (in milliseconds) +# Default: 10000 +# +# GridCleanUpDelay +# Grid clean up delay (in milliseconds) +# Default: 300000 (5 min) +# +# MapUpdateInterval +# Map update interval (in milliseconds) +# Default: 100 +# +# ChangeWeatherInterval +# Weather update interval (in milliseconds) +# Default: 600000 (10 min) +# +# PlayerSaveInterval +# Player save interval (in milliseconds) +# Default: 900000 (15 min) +# +# vmap.enableLOS +# vmap.enableHeight +# Enable/Disable VMmap support for line of sight and height calculation +# Default: 1 (true) +# 0 (false) +# +# vmap.ignoreMapIds +# Map id that will be ignored by VMaps +# List of ids with delimiter ',' +# If more then one id is defined and spaces are included, the string has to be enclosed by " +# Example: "369,0,1,530" +# +# vmap.ignoreSpellIds +# These spells are ignored for LoS calculation +# List of ids with delimiter ',' +# +# DetectPosCollision +# Check final move position, summon position, etc for visible collision with other objects or +# wall (wall only if vmaps are enabled) +# Default: 1 (enable, required more CPU power usage) +# 0 (disable, less nice position selection but will less CPU power usage) +# +# TargetPosRecalculateRange +# Max distance from movement target point (+moving unit size) and targeted object (+size) +# after that new target movmeent point calculated. Max: melee attack range (5), min: contact range (0.5) +# More distance let have better performence, less distance let have more sensitive reaction at target move. +# Default: 1.5 +# +# UpdateUptimeInterval +# Update realm uptime period in minutes (for save data in 'uptime' table). Must be > 0 +# Default: 10 (minutes) +# +# MaxCoreStuckTime +# Periodically check if the process got freezed, if this is the case force crash after the specified +# amount of seconds. Must be > 0. Recommended > 10 secs if you use this. +# Default: 0 (Disabled) +# +# AddonChannel +# Permit/disable the use of the addon channel through the server +# (some client side addons can stop work correctly with disabled addon channel) +# Default: 1 (permit addon channel) +# 0 (do not permit addon channel) +# +################################################################################################################### + +UseProcessors = 0 +ProcessPriority = 1 +Compression = 1 +TcpNoDelay = 0 +PlayerLimit = 100 +SaveRespawnTimeImmediately = 1 +MaxOverspeedPings = 2 +GridUnload = 1 +SocketSelectTime = 10000 +GridCleanUpDelay = 300000 +MapUpdateInterval = 100 +ChangeWeatherInterval = 600000 +PlayerSaveInterval = 900000 +vmap.enableLOS = 0 +vmap.enableHeight = 0 +vmap.ignoreMapIds = "369" +vmap.ignoreSpellIds = "7720" +DetectPosCollision = 1 +TargetPosRecalculateRange = 1.5 +UpdateUptimeInterval = 10 +MaxCoreStuckTime = 0 +AddonChannel = 1 + +################################################################################################################### +# SERVER LOGGING +# +# LogSQL +# Enable logging of GM commands - all SQL code will be written to a log file +# All commands are written to a file: YYYY-MM-DD_logSQL.sql +# If a new day starts (00:00:00) then a new file is created - the old file will not be deleted. +# Default: 1 - Write SQL code to logfile +# 0 - Do not log +# +# PidFile +# World daemon PID file +# Default: "" - do not create PID file +# "./worldd.pid" - create PID file (recommended name) +# +# LogLevel +# Server console level of logging +# 0 = Minimum; 1 = Basic&Error; 2 = Detail; 3 = Full/Debug +# Default: 3 +# +# LogTime +# Include time in server console output [hh:mm:ss] +# Default: 0 (no time) +# 1 (print time) +# +# LogFile +# Logfile name +# Default: "Server.log" +# "" - Empty name disable creating log file +# +# LogTimestamp +# Logfile with timestamp of server start in name +# Default: 0 - no timestamp in name +# 1 - add timestamp in name in form Logname_YYYY-MM-DD_HH-MM-SS.Ext for Logname.Ext +# +# LogFileLevel +# Server file level of logging +# 0 = Minimum; 1 = Error; 2 = Detail; 3 = Full/Debug +# Default: 0 +# +# LogFilter_TransportMoves +# LogFilter_CreatureMoves +# LogFilter_VisibilityChanges +# Log filters +# Default: 1 - not include with any log level +# 0 - include in log if log level permit +# +# WorldLogFile +# Packet logging file for the worldserver +# Default: "world.log" +# +# DBErrorLogFile +# Log file of DB errors detected at server run +# Default: "DBErrors.log" +# +# CharLogFile +# Character operations logfile name +# Default: "Char.log" +# "" - Empty name disable creating log file +# +# CharLogTimestamp +# Logfile with timestamp of server start in name +# Default: 0 - no timestamp in name +# 1 - add timestamp in name in form Logname_YYYY-MM-DD_HH-MM-SS.Ext for Logname.Ext +# +# CharLogDump +# Write character dump before deleting in Char.log +# For restoration, cut character data from log starting from +# line == START DUMP == to line == END DUMP == (without its) in file and load it using loadpdump command +# Default: 0 - don't include dumping chars to log +# 1 - include dumping chars to log +# +# GmLogFile +# Log file of gm commands +# Default: "" (Disable) +# +# GmLogTimestamp +# Logfile with timestamp of server start in name +# Default: 0 - no timestamp in name +# 1 - add timestamp in name in form Logname_YYYY-MM-DD_HH-MM-SS.Ext for Logname.Ext +# +# RaLogFile +# Log file of RA commands +# Default: "Ra.log" +# "" - Empty name for disable +# +# LogColors +# Color for messages (format "normal_color details_color debug_color error_color) +# Colors: 0 - BLACK, 1 - RED, 2 - GREEN, 3 - BROWN, 4 - BLUE, 5 - MAGENTA, 6 - CYAN, 7 - GREY, +# 8 - YELLOW, 9 - LRED, 10 - LGREEN, 11 - LBLUE, 12 - LMAGENTA, 13 - LCYAN, 14 - WHITE +# Default: "" - none colors +# Example: "13 7 11 9" +# +################################################################################################################### + +LogSQL = 1 +PidFile = "" +LogLevel = 3 +LogTime = 0 +LogFile = "Server.log" +LogTimestamp = 0 +LogFileLevel = 0 +LogFilter_TransportMoves = 1 +LogFilter_CreatureMoves = 1 +LogFilter_VisibilityChanges = 1 +WorldLogFile = "world.log" +DBErrorLogFile = "DBErrors.log" +CharLogFile = "Char.log" +CharLogTimestamp = 0 +CharLogDump = 0 +GmLogFile = "" +GmLogTimestamp = 0 +RaLogFile = "" +LogColors = "" + +################################################################################################################### +# SERVER SETTINGS +# +# GameType +# Server realm style +# 0 = NORMAL;1 = PVP; 4 = NORMAL; 6 = RP; 8 = RPPVP +# also custom type: 16 FFA_PVP (free for all pvp mode like arena PvP in all zones except rest +# activated places and sanctuaries) +# +# RealmZone +# Server realm zone (set allowed alphabet in character names/etc). See also Strict*Names options. +# +# 1 Development - any language (Default) +# 2 United States - extended-Latin +# 3 Oceanic - extended-Latin +# 4 Latin America - extended-Latin +# 5 Tournament - basic-Latin at create, any at login +# 6 Korea - East-Asian +# 7 Tournament - basic-Latin at create, any at login +# 8 English - extended-Latin +# 9 German - extended-Latin +# 10 French - extended-Latin +# 11 Spanish - extended-Latin +# 12 Russian - Cyrillic +# 13 Tournament - basic-Latin at create, any at login +# 14 Taiwan - East-Asian +# 15 Tournament - basic-Latin at create, any at login +# 16 China - East-Asian +# 17 CN1 - basic-Latin at create, any at login +# 18 CN2 - basic-Latin at create, any at login +# 19 CN3 - basic-Latin at create, any at login +# 20 CN4 - basic-Latin at create, any at login +# 21 CN5 - basic-Latin at create, any at login +# 22 CN6 - basic-Latin at create, any at login +# 23 CN7 - basic-Latin at create, any at login +# 24 CN8 - basic-Latin at create, any at login +# 25 Tournament - basic-Latin at create, any at login +# 26 Test Server - any language +# 27 Tournament - basic-Latin at create, any at login +# 28 QA Server - any language +# 29 CN9 - basic-Latin at create, any at login +# +# Expansion +# Allow server use expansion content +# Default: 1 - check expansion maps existence, and if client support expansion and account have +# expansion setting then allow visit expansion maps, allow create new races character) +# 0 - not check expansion maps existence, not allow wisit its, not allow create new race +# characters, ignore account expansion setting) +# +# DBC.Locale +# DBC Language Settings +# 0 = English; 1 = Korean; 2 = French; 3 = German; 4 = Chinese; 5 = Taiwanese; 6 = Spanish; 7 = Spanish Mexico +# 8 = Russian; 255 = Auto Detect (Default) +# +# DeclinedNames +# Allow russian clients to set and use declined names +# Default: 0 - do not use declined names, except when the Russian RealmZone is set +# 1 - use declined names +# +# StrictPlayerNames +# Limit player name to language specific symbols set, not allow create characters, and set rename request and disconnect at not allowed symbols name +# Default: 0 disable (but limited server timezone dependent client check) +# 1 basic latin characters (strict) +# 2 realm zone specific (strict). See RealmZone setting. +# Note: In any case if you want correctly see character name at client this client must have apporopriate fonts +# (included in client by default, with active official localization or custom localization fonts in clientdir/Fonts). +# 3 basic latin characters + server timezone specific +# +# StrictCharterNames +# Limit guild/arena team charter names to language specific symbols set, not allow create charters with allowed symbols in name +# Default: 0 disable +# 1 basic latin characters (strict) +# 2 realm zone specific (strict). See RealmZone setting. +# Note: In any case if you want correctly see character name at client this client must have apporopriate fonts +# (included in client by default, with active official localization or custom localization fonts in clientdir/Fonts). +# 3 basic latin characters + server timezone specific +# +# StrictPetNames +# Limit pet names to language specific symbols set +# Default: 0 disable +# 1 basic latin characters (strict) +# 2 realm zone specific (strict). See RealmZone setting. +# Note: In any case if you want correctly see character name at client this client must have apporopriate fonts +# (included in client by default, with active official localization or custom localization fonts in clientdir/Fonts). +# 3 basic latin characters + server timezone specific +# +# CharactersCreatingDisabled +# Disable characters creating for specific team or any (non-player accounts not affected) +# Default: 0 - enabled +# 1 - disabled only for Alliance +# 2 - disabled only for Horde +# 3 - disabled for both teams +# +# CharactersPerAccount +# Limit numbers of characters per account (at all realms). +# Note: this setting limit character creating at _current_ realm base at characters amount at all realms +# Default: 50 +# The number must be >= CharactersPerRealm +# +# CharactersPerRealm +# Limit numbers of characters for account at realm +# Default: 10 (client limitation) +# The number must be between 1 and 10 +# +# SkipCinematics +# Disable in-game script movie at first character's login(allows to prevent buggy intro in case of custom start location coordinates) +# Default: 0 - show intro for each new characrer +# 1 - show intro only for first character of selected race +# 2 - disable intro show in all cases +# +# MaxPlayerLevel +# Max level that can be reached by player for experience (in range from 1 to 255). +# Change not recommended +# Default: 70 +# +# MaxHonorPoints +# Max honor points that player can have. +# Default: 75000 +# +# MaxArenaPoints +# Max arena points that player can have. +# Default: 5000 +# +# StartPlayerLevel +# Staring level that have character at creating (in range 1 to MaxPlayerLevel) +# Default: 1 +# +# ActivateWeather +# Activate weather system +# Default: 1 (true) +# 0 (false) +# +# Battleground.CastDeserter +# Cast or not Deserter spell at player who leave battleground in progress +# Default: 1 (true) +# 0 (false) +# +# Battleground.QueueAnnouncer.Enable +# Enable queue announcer posting to chat +# Default: 1 (true) +# 0 (false) +# +# Battleground.QueueAnnouncer.PlayerOnly +# Enable queue announcer posting to chat +# Default: 0 (false) +# 1 (true) +# +# CastUnstuck +# Allow cast or not Unstuck spell at .start or client Help option use +# Default: 1 (true) +# 0 (false) +# +# Instance.IgnoreLevel +# Ignore level requirement to enter instance +# Default: 0 (false) +# 1 (true) +# +# Instance.IgnoreRaid +# Ignore raid requirement to enter instance +# Default: 0 (false) +# 1 (true) +# +# Instance.ResetTimeHour +# The hour of the day (0-23) when the global instance resets occur. +# Default: 4 +# +# Instance.UnloadDelay +# Unload the instance map from memory after some time if no players are inside. +# Default: 1800000 (miliseconds, i.e 30 minutes) +# 0 (instance maps are kept in memory until they are reset) +# +# Quests.LowLevelHideDiff +# Quest level difference to hide for player low level quests: +# if player_level > quest_level + LowLevelQuestsHideDiff then quest "!" mark not show for quest giver +# Default: 4 +# -1 (show all available quests marks) +# +# Quests.HighLevelHideDiff +# Quest level difference to hide for player high level quests: +# if player_level < quest_min_level - HighLevelQuestsHideDiff then quest "!" mark not show for quest giver +# Default: 7 +# -1 (show all available quests marks) +# +# MaxPrimaryTradeSkill +# Max count that player can learn the primary trade skill. +# Default: 2 +# Max : 10 +# +# MinPetitionSigns +# Min signatures count to creating guild (0..9). +# Default: 9 +# +# MaxGroupXPDistance +# Max distance to creature for group memeber to get XP at creature death. +# Default: 74 +# +# MailDeliveryDelay +# Mail delivery delay time for item sending +# Default: 3600 sec (1 hour) +# +# SkillChance.Prospecting +# For prospecting skillup not possible by default, but can be allowed as custom setting +# Default: 0 - no skilups +# 1 - skilups possible +# +# Event.Announce +# Default: 0 (false) +# 1 (true) +# +# BeepAtStart +# Beep at mangosd start finished (mostly work only at Unix/Linux systems) +# Default: 1 (true) +# 0 (false) +# +# Motd +# Message of the Day. Displayed at worldlogin for every user ('@' for a newline). +# +################################################################################################################### + +GameType = 1 +RealmZone = 1 +Expansion = 1 +DBC.Locale = 255 +DeclinedNames = 0 +StrictPlayerNames = 0 +StrictCharterNames = 0 +StrictPetNames = 0 +CharactersCreatingDisabled = 0 +CharactersPerAccount = 50 +CharactersPerRealm = 10 +SkipCinematics = 0 +MaxPlayerLevel = 70 +MaxHonorPoints = 75000 +MaxArenaPoints = 5000 +StartPlayerLevel = 1 +ActivateWeather = 1 +Battleground.CastDeserter = 1 +Battleground.QueueAnnouncer.Enable = 1 +Battleground.QueueAnnouncer.PlayerOnly = 0 +CastUnstuck = 1 +Instance.IgnoreLevel = 0 +Instance.IgnoreRaid = 0 +Instance.ResetTimeHour = 4 +Instance.UnloadDelay = 1800000 +Quests.LowLevelHideDiff = 4 +Quests.HighLevelHideDiff = 7 +MaxPrimaryTradeSkill = 2 +MinPetitionSigns = 9 +MaxGroupXPDistance = 74 +MailDeliveryDelay = 3600 +SkillChance.Prospecting = 0 +Event.Announce = 0 +BeepAtStart = 1 +Motd = "Welcome to the Massive Network Game Object Server." + +################################################################################################################### +# PLAYER INTERACTION +# +# AllowTwoSide.Accounts +# Allow or not accounts to create characters in the 2 teams in any game type. +# Default: 0 (Not allowed) +# 1 (Allowed) +# +# AllowTwoSide.Interaction.Chat +# AllowTwoSide.Interaction.Channel +# AllowTwoSide.Interaction.Group +# AllowTwoSide.Interaction.Guild +# AllowTwoSide.Interaction.Auction +# AllowTwoSide.Interaction.Mail +# Allow or not common :chat(say,yell);channel(chat)group(join)guild(join);merge all auction houses for players from +# different teams, send mail to different team. +# Default: 0 (Not allowed) +# 1 (Allowed) +# +# AllowTwoSide.WhoList +# Allow or not show player from both team in who list. +# Default: 0 (Not allowed) +# 1 (Allowed) +# +# AllowTwoSide.AddFriend +# Allow or not adding friends from other team in friend list. +# Default: 0 (Not allowed) +# 1 (Allowed) +# +# TalentsInspecting +# Allow other players see character talents in inspect dialog (Characters in Gamemaster mode can +# inspect talents always) +# Default: 1 (allow) +# 0 (not allow) +# +################################################################################################################### + +AllowTwoSide.Accounts = 0 +AllowTwoSide.Interaction.Chat = 0 +AllowTwoSide.Interaction.Channel = 0 +AllowTwoSide.Interaction.Group = 0 +AllowTwoSide.Interaction.Guild = 0 +AllowTwoSide.Interaction.Auction = 0 +AllowTwoSide.Interaction.Mail = 0 +AllowTwoSide.WhoList = 0 +AllowTwoSide.AddFriend = 0 +TalentsInspecting = 1 + +################################################################################################################### +# CREATURE SETTINGS +# +# ThreatRadius +# Radius for creature to evade after being pulled away from combat start point +# If ThreatRadius is less than creature aggro radius then aggro radius will be used +# Default: 100 yards +# +# Rate.Creature.Aggro +# Aggro radius percent or off. +# Default: 1 - 100% +# 1.5 - 150% +# 0 - off (0%) +# +# CreatureFamilyAssistenceRadius +# Creature family assistence radius +# Default: 10 +# 0 - off +# +# WorldBossLevelDiff +# Difference for boss dynamic level with target +# Default: 3 +# +# Corpse.Decay.NORMAL +# Corpse.Decay.RARE +# Corpse.Decay.ELITE +# Corpse.Decay.RAREELITE +# Corpse.Decay.WORLDBOSS +# Seconds until creature corpse will decay without being looted or skinned. +# Default: 60, 300, 300, 300, 3600 +# +# Rate.Corpse.Decay.Looted +# Controls how long the creature corpse stays after it had been looted, as a multiplier of its Corpse.Decay.* config. +# Default: 0.1 +# +# Rate.Creature.Normal.Damage +# Rate.Creature.Elite.Elite.Damage +# Rate.Creature.Elite.RAREELITE.Damage +# Rate.Creature.Elite.WORLDBOSS.Damage +# Rate.Creature.Elite.RARE.Damage +# Creature Damage Rates. +# Examples: 2 - creatures will damage 2x, 1.7 - 1.7x. +# +# Rate.Creature.Normal.SpellDamage +# Rate.Creature.Elite.Elite.SpellDamage +# Rate.Creature.Elite.RAREELITE.SpellDamage +# Rate.Creature.Elite.WORLDBOSS.SpellDamag +# Rate.Creature.Elite.RARE.SpellDamage +# Creature Spell Damage Rates. +# Examples: 2 - creatures will damage with spells 2x, 1.7 - 1.7x. +# +# Rate.Creature.Normal.HP +# Rate.Creature.Elite.Elite.HP +# Rate.Creature.Elite.RAREELITE.HP +# Rate.Creature.Elite.WORLDBOSS.HP +# Rate.Creature.Elite.RARE.HP +# Creature Health Ammount Modifier. +# Examples: 2 - creatures have 2x health, 1.7 - 1.7x. +# +################################################################################################################### + +ThreatRadius = 100 +Rate.Creature.Aggro = 1 +CreatureFamilyAssistenceRadius = 10 +WorldBossLevelDiff = 3 +Corpse.Decay.NORMAL = 60 +Corpse.Decay.RARE = 300 +Corpse.Decay.ELITE = 300 +Corpse.Decay.RAREELITE = 300 +Corpse.Decay.WORLDBOSS = 3600 +Rate.Corpse.Decay.Looted = 0.1 +Rate.Creature.Normal.Damage = 1 +Rate.Creature.Elite.Elite.Damage = 1 +Rate.Creature.Elite.RAREELITE.Damage = 1 +Rate.Creature.Elite.WORLDBOSS.Damage = 1 +Rate.Creature.Elite.RARE.Damage = 1 +Rate.Creature.Normal.SpellDamage = 1 +Rate.Creature.Elite.Elite.SpellDamage = 1 +Rate.Creature.Elite.RAREELITE.SpellDamage = 1 +Rate.Creature.Elite.WORLDBOSS.SpellDamage = 1 +Rate.Creature.Elite.RARE.SpellDamage = 1 +Rate.Creature.Normal.HP = 1 +Rate.Creature.Elite.Elite.HP = 1 +Rate.Creature.Elite.RAREELITE.HP = 1 +Rate.Creature.Elite.WORLDBOSS.HP = 1 +Rate.Creature.Elite.RARE.HP = 1 + +################################################################################################################### +# CHAT SETTINGS +# +# ChatFakeMessagePreventing +# Chat protection from creating fake messages using a lot spaces (other invisible symbols), +# not applied to addon language messages, but can prevent working old addons +# that use normal languages for sending data to another clients. +# Default: 0 (disible fake messages preventing) +# 1 (enabled fake messages preventing) +# +# ChatFlood.MessageCount +# Chat anti-flood protection, haste message count to activate protection +# Default: 10 +# 0 (disible anti-flood protection) +# +# ChatFlood.MessageDelay +# Chat anti-flood protection, minimum message delay to count message +# Default: 1 (in secs) +# +# ChatFlood.MuteTime +# Chat anti-flood protection, mute time at activation flood protection (not saved) +# Default: 10 (in secs) +# +# Channel.RestrictedLfg +# Restrict use LookupForGroup channel only registered in LFG tool players +# Default: 1 (allow join to channel only if active in LFG) +# 0 (allow join to channel in any time) +# +# Channel.SilentlyGMJoin +# Silently join GM characters (security level > 1) to channels +# Default: 0 (join announcement in normal way) +# 1 (GM join without announcement) +# +################################################################################################################### + +ChatFakeMessagePreventing = 0 +ChatFlood.MessageCount = 10 +ChatFlood.MessageDelay = 1 +ChatFlood.MuteTime = 10 +Channel.RestrictedLfg = 1 +Channel.SilentlyGMJoin = 0 + +################################################################################################################### +# GAME MASTER SETTINGS +# +# GM.WhisperingTo +# Is GM accepting whispers from player by default or not. +# Default: 0 (false) +# 1 (true) +# +# GM.InGMList +# Is GM showed in GM list (if visible) in non-GM state (.gmoff) +# Default: 0 (false) +# 1 (true) +# +# GM.InWhoList +# Is GM showed in who list (if visible). +# Default: 0 (false) +# 1 (true) +# +# GM.LoginState +# GM mode at login +# Default: 2 (last save state) +# 0 (disable) +# 1 (enable) +# +# GM.LogTrade +# Include GM trade and trade slot enchanting operations in GM log if it enable +# Default: 1 (include) +# 0 (not include) +# +################################################################################################################### + +GM.WhisperingTo = 0 +GM.InGMList = 0 +GM.InWhoList = 0 +GM.LoginState = 2 +GM.LogTrade = 1 + +################################################################################################################### +# VISIBILITY AND RADIUSES +# +# Visibility.GroupMode +# Group visibility modes +# Default: 0 (standard setting: only members from same group can 100% auto detect invisible player) +# 1 (raid members 100% auto detect invisible player from same raid) +# 2 (players from same team can 100% auto detect invisible player) +# +# Visibility.Distance.Creature +# Visibility.Distance.Player +# Visibility distance for different in game object +# Max limited by active player zone: ~ 166 +# Min limit dependent from objects +# Default: 66 (cell size) +# Min limit is max aggro radius (45) * Rate.Creature.Aggro +# +# Visibility.Distance.Object +# Visible distance for gameobject, dynobject, bodies, corpses, bones +# Min limit is iteraction distance (5) +# +# Visibility.Distance.InFlight +# Visible distance for player in flight +# Min limit is 0 (not show any objects) +# +# Visibility.Distance.Grey.Unit +# Visibility grey distance for creatures/players (fast changing objects) +# addition to appropriate object type Visibility.Distance.* use in case visibility removing to +# object (except corpse around distences) If � is distance and G is grey distance then object +# make visible if distance to it <= D but make non visible if distance > D+G +# Default: 1 (yard) +# +# Visibility.Distance.Grey.Object +# Visibility grey distance for dynobjects/gameobjects/corpses/creature bodies +# Default: 10 (yards) +# +# +################################################################################################################### + +Visibility.GroupMode = 0 +Visibility.Distance.Creature = 66 +Visibility.Distance.Player = 66 +Visibility.Distance.Object = 66 +Visibility.Distance.InFlight = 66 +Visibility.Distance.Grey.Unit = 1 +Visibility.Distance.Grey.Object = 10 + +################################################################################################################### +# SERVER RATES +# +# Rate.Health +# Rate.Mana +# Rate.Rage.Income +# Rate.Rage.Loss +# Rate.Focus +# Rate.Loyalty +# Health and power regeneration and rage income from damage. +# Default: 1 +# +# Rate.Skill.Discovery +# Skill Discovery Rates +# Default: 1 +# +# Rate.Drop.Item.Poor +# Rate.Drop.Item.Normal +# Rate.Drop.Item.Uncommon +# Rate.Drop.Item.Rare +# Rate.Drop.Item.Epic +# Rate.Drop.Item.Legendary +# Rate.Drop.Item.Artifact +# Rate.Drop.Item.Referenced +# Rate.Drop.Money +# Drop rates (items by qualify and money) +# Default: 1 +# +# Rate.Drop.Money +# Drop rates +# Default: 1 +# +# Rate.XP.Kill +# Rate.XP.Quest +# Rate.XP.Explore +# XP rates +# Default: 1 +# +# Rate.XP.PastLevel70 +# XP needed per level past 70 (Rates below 1 not recommended) +# Default: 1 +# +# Rate.Rest.InGame +# Rate.Rest.Offline.InTavernOrCity +# Rate.Rest.Offline.InWilderness +# Resting points grow rates (1 - normal, 2 - double rate, 0.5 - half rate, etc) from standard values +# +# Rate.Damage.Fall +# Damage after fall rate. (1 - standard, 2 - double damage, 0.5 - half damage, etc) +# +# Rate.Auction.Time +# Rate.Auction.Deposit +# Rate.Auction.Cut +# Auction rates (auction time, deposit get at auction start, auction cut from price at auction end) +# +# Rate.Honor +# Honor gain rate +# +# Rate.Mining.Amount +# Rate.Mining.Next +# Mining Rates (Mining.Amount changes minimum/maximum usetimes of a deposit, +# Mining.Next changes chance to have next use of a deposit) +# +# Rate.Talent +# Talent Point rates +# Default: 1 +# +# Rate.Reputation.Gain +# Reputation Gain rate +# Default: 1 +# +# Rate.InstanceResetTime +# Multiplier for the number of days in between global raid/heroic instance resets. +# Default: 1 +# +# SkillGain.Crafting +# SkillGain.Defense +# SkillGain.Gathering +# SkillGain.Weapon +# crafting/defense/gathering/weapon skills gain at skill grow (1,2,...) +# Default: 1 +# +# SkillChance.Orange +# SkillChance.Yellow +# SkillChance.Green +# SkillChance.Grey +# Skill chance values (0..100) +# Default: 100-75-25-0 +# +# SkillChance.MiningSteps +# SkillChance.SkinningSteps +# For skinning and Mining chance decrease with skill level. +# Default: 0 - no decrease +# 75 - in 2 times each 75 skill points +# +# DurabilityLossChance.Damage +# Chance lost one from equiped items durability point at damage apply or receive. +# Default: 0.5 (100/0.5 = 200) Each 200 damage apply one from 19 possible equipped items +# +# DurabilityLossChance.Absorb +# Chance lost one from armor items durability point at damage absorb. +# Default: 0.5 (100/0.5 = 200) Each 200 absorbs apply one from 15 possible armor equipped items +# +# DurabilityLossChance.Parry +# Chance lost weapon durability point at parry. +# Default: 0.05 (100/0.05 = 2000) Each 2000 parry attacks main weapon lost point +# +# DurabilityLossChance.Block +# Chance lost sheild durability point at damage block. +# Default: 0.05 (100/0.05 = 2000) Each 2000 partly or full blocked attacks shield lost point +# +# Death.SicknessLevel +# Starting Character start gain sickness at spirit resurrection (1 min) +# Default: 11 +# -10 - character will have full time (10min) sickness at 1 level +# maxplayerlevel+1 - chaarcter will not have sickess at any level +# +# Death.CorpseReclaimDelay.PvP +# Death.CorpseReclaimDelay.PvE +# Enabled/disabled increase corpse reclaim delay at often PvP/PvE deaths +# Default: 1 (enabled) +# 0 (disabled) +# +################################################################################################################### + +Rate.Health = 1 +Rate.Mana = 1 +Rate.Rage.Income = 1 +Rate.Rage.Loss = 1 +Rate.Focus = 1 +Rate.Loyalty = 1 +Rate.Skill.Discovery = 1 +Rate.Drop.Item.Poor = 1 +Rate.Drop.Item.Normal = 1 +Rate.Drop.Item.Uncommon = 1 +Rate.Drop.Item.Rare = 1 +Rate.Drop.Item.Epic = 1 +Rate.Drop.Item.Legendary = 1 +Rate.Drop.Item.Artifact = 1 +Rate.Drop.Item.Referenced = 1 +Rate.Drop.Money = 1 +Rate.XP.Kill = 1 +Rate.XP.Quest = 1 +Rate.XP.Explore = 1 +Rate.XP.PastLevel70 = 1 +Rate.Rest.InGame = 1 +Rate.Rest.Offline.InTavernOrCity = 1 +Rate.Rest.Offline.InWilderness = 1 +Rate.Damage.Fall = 1 +Rate.Auction.Time = 1 +Rate.Auction.Deposit = 1 +Rate.Auction.Cut = 1 +Rate.Honor = 1 +Rate.Mining.Amount = 1 +Rate.Mining.Next = 1 +Rate.Talent = 1 +Rate.Reputation.Gain = 1 +Rate.InstanceResetTime = 1 +SkillGain.Crafting = 1 +SkillGain.Defense = 1 +SkillGain.Gathering = 1 +SkillGain.Weapon = 1 +SkillChance.Orange = 100 +SkillChance.Yellow = 75 +SkillChance.Green = 25 +SkillChance.Grey = 0 +SkillChance.MiningSteps = 0 +SkillChance.SkinningSteps = 0 +DurabilityLossChance.Damage = 0.5 +DurabilityLossChance.Absorb = 0.5 +DurabilityLossChance.Parry = 0.05 +DurabilityLossChance.Block = 0.05 +Death.SicknessLevel = 11 +Death.CorpseReclaimDelay.PvP = 1 +Death.CorpseReclaimDelay.PvE = 1 + +################################################################################################################### +# CONSOLE AND REMOTE ACCESS +# +# Console.Enable +# Enable console +# Default: 1 - on +# 0 - off +# +# Ra.Enable +# Enable remote console +# Default: 0 - off +# 1 - on +# +# Ra.IP +# Default remote console ip address, use 0.0.0.0 for every address +# +# Ra.Port +# Default remote console port +# +# Ra.MinLevel +# Minimum level that's required to login,3 by default +# +# Ra.Secure +# Kick client on wrong pass +# +################################################################################################################### + +Console.Enable = 1 +Ra.Enable = 0 +Ra.IP = 0.0.0.0 +Ra.Port = 3443 +Ra.MinLevel = 3 +Ra.Secure = 1 |