Index: sql/MangChat.sql =================================================================== --- sql/MangChat.sql (revision 0) +++ sql/MangChat.sql (revision 0) @@ -0,0 +1,46 @@ +-- MySQL dump 10.11 +-- +-- Host: localhost Database: mangos + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `irc_inchan` +-- + +DROP TABLE IF EXISTS `irc_inchan`; +CREATE TABLE `irc_inchan` ( + `guid` int(11) unsigned NOT NULL default '0' COMMENT 'Global Unique Identifier', + `name` varchar(12) NOT NULL default '', + `team` smallint(3) unsigned NOT NULL default '0', + PRIMARY KEY (`guid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='IRC Module system'; + +-- +-- Table structure for table `irc_logedin` +-- + +DROP TABLE IF EXISTS `irc_logedin`; +CREATE TABLE `irc_logedin` ( + `account` int(11) unsigned NOT NULL default '0' COMMENT 'Account Identifier', + `nick` varchar(50) NOT NULL default '', + `gmlevel` tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (`account`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='IRC Module system'; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; Index: src/game/Channel.cpp =================================================================== --- src/game/Channel.cpp (revision 3815) +++ src/game/Channel.cpp (working copy) @@ -16,9 +16,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "Common.h" #include "Channel.h" #include "ObjectMgr.h" #include "Chat.h" +#include "IRCClient.h" Channel::Channel(std::string _name) : name(_name), announce(true), constant(false), moderate(false), m_ownerGUID(0), password("") @@ -30,6 +32,13 @@ constant = true; // built-in channel announce = false; // no join/leave announces } + std::string tempname = _name; + std::transform(tempname.begin(), tempname.end(), tempname.begin(), towlower); + if (sIRC.WOW_CHAN == tempname) + { + constant = true; // built-in channel + announce = false; // no join/leave announces + } } void Channel::Join(uint64 p, const char *pass, uint32 unk1) Index: src/game/CharacterHandler.cpp =================================================================== --- src/game/CharacterHandler.cpp (revision 3815) +++ src/game/CharacterHandler.cpp (working copy) @@ -34,6 +34,8 @@ #include "ObjectAccessor.h" #include "Transports.h" #include "Group.h" +#include "IRCClient.h" +#include "Config/ConfigEnv.h" // check used symbols in player name at creating and rename std::string notAllowedChars = "\t\v\b\f\a\n\r\\\"\'\? <>[](){}_=+-|/!@#$%^&*~`.,0123456789\0"; @@ -563,8 +565,51 @@ if(sWorld.IsShutdowning()) sWorld.ShutdownMsg(true,pCurrChar); + bool announceallowed = true; if(pCurrChar->isGameMaster()) + { SendNotification("GM mode is ON"); + //retreive the toggle value from the conf + std::string GMLoginMessage = sConfig.GetStringDefault("GMAnnounceMessage","|cffff0000[$Rank $Name has come Online]|r"); + std::string GMLoginIRCMessage = sConfig.GetStringDefault("GMAnnounceIRCMessage","12>>04 $Rank $Name has come Online"); + std::string GMRankMod= sConfig.GetStringDefault("GMRankMod","Mod"); + std::string GMRankGM= sConfig.GetStringDefault("GMRankGM","GM"); + std::string GMRankAdmin= sConfig.GetStringDefault("GMRankAdmin","Admin"); + //retreive the custom message from the conf + + std::string GMRank;//String to store readable GM rank 1=mod 2=gm 3=admin + + switch(GetSecurity())//switch case to determine what rank the "gm" is + { + case 1: GMRank = GMRankMod;break; + case 2: GMRank = GMRankGM;break; + case 3: GMRank = GMRankAdmin;break; + } + + if((sIRC.BOTMASK & 32)!= 0) + { + announceallowed = false; + std::string str = sIRC.MakeMsg(sIRC.MakeMsg(GMLoginMessage, "$Name", pCurrChar->GetName()), "$Rank", GMRank); + sWorld.SendWorldText(str.c_str(), NULL); + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.MakeMsg(GMLoginIRCMessage, "$Name", pCurrChar->GetName()), "$Rank", GMRank)); + sLog.outBasic("GM '%s' logged in and announced",pCurrChar->GetName()); + } + else sLog.outBasic("Not announced xD"); + } + + if (announceallowed) + { + std::string pname = pCurrChar->GetName(); + if ((sIRC.BOTMASK & 8)!= 0) + { + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str(),sIRC.MakeMsg(sIRC.WoW_comeonline, "$Name", pname)); + } + if ((sIRC.BOTMASK & 16)!= 0) + { + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.IRC_comeonline, "$Name", pname)); + } + } + m_playerLoading = false; pCurrChar->SendAllowMove(); Index: src/game/Chat.cpp =================================================================== --- src/game/Chat.cpp (revision 3815) +++ src/game/Chat.cpp (working copy) @@ -175,6 +175,7 @@ { "guid", 2, &ChatHandler::HandleGUIDCommand, "", NULL }, { "help", 0, &ChatHandler::HandleHelpCommand, "", NULL }, { "info", 0, &ChatHandler::HandleInfoCommand, "", NULL }, + { "ircpm", 0, &ChatHandler::HandleIRCpmCommand, "", NULL }, { "npcinfo", 3, &ChatHandler::HandleNpcInfoCommand, "", NULL }, { "npcinfoset", 3, &ChatHandler::HandleNpcInfoSetCommand, "", NULL }, { "addvendoritem",2,&ChatHandler::HandleAddVendorItemCommand, "", NULL }, Index: src/game/Chat.h =================================================================== --- src/game/Chat.h (revision 3815) +++ src/game/Chat.h (working copy) @@ -136,6 +136,7 @@ bool HandleDismountCommand(const char* args); bool HandleSaveCommand(const char* args); bool HandleGMListCommand(const char* args); + bool HandleIRCpmCommand(const char* args); bool HandleNamegoCommand(const char* args); bool HandleGonameCommand(const char* args); Index: src/game/ChatHandler.cpp =================================================================== --- src/game/ChatHandler.cpp (revision 3815) +++ src/game/ChatHandler.cpp (working copy) @@ -31,6 +31,7 @@ #include "MapManager.h" #include "ObjectAccessor.h" #include "ScriptCalls.h" +#include "IRCClient.h" #include "Player.h" #include "Language.h" @@ -277,14 +278,24 @@ recv_data >> msg; - if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if (sChatHandler.ParseCommands(msg.c_str(), this) > 0) + break; + + // if two side interraction is disabled and this is our channel, send to both channel manager instances, in LANG_UNIVERSAL. + if(sIRC.ChannelMsg(GetPlayer(), channel, msg) && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) { - if(Channel *chn = cMgr->GetChannel(channel,_player)) - { - chn->Say(_player->GetGUID(),msg.c_str(),lang); - GetPlayer()->UpdateSpeakTime(); - } + sWorld.SendWorldMessage(channel.c_str(), msg, _player->GetGUID()); + GetPlayer()->UpdateSpeakTime(); } + else // otherwise let mangos handle it normally. + { + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channel,_player)) + { + chn->Say(_player->GetGUID(),msg.c_str(),lang); + GetPlayer()->UpdateSpeakTime(); + } + } } break; case CHAT_MSG_AFK: Index: src/game/IRCClient.cpp =================================================================== --- src/game/IRCClient.cpp (revision 0) +++ src/game/IRCClient.cpp (revision 0) @@ -0,0 +1,1014 @@ +/* + * MangChat Enhanced v0.2 + * Based on MANGCHAT a Threaded IRC Client for MaNGOS V0.1 by Cybrax + * + * MANGCHAT is a Internet Relay Chat client intergrated in mangos + * it allows you to have chat messages send from and to irc within + * the wow client. making it possible for players to get support + * from irc directly ingame or have several servers join the same + * irc channel and have "cross server chat". the client uses c + * sockets to send and receieve data from and to the irc server + * + * MangChat Enhanced aims to add many features to MangChat, + * including total "gm" control from irc, in channel and in game + * announces of level ups, deaths, resurrects, and so on. + * MangChat Enhanced features are designed to be fully integrated + * into the MaNGOS core system as well as be easly customised or + * disabled from the MaNGOS conf file. + * + * This software is currently still under development the client + * has been tested locally with 1 Windows XP server (Visual Studio) + * and 1 UNIX Debian 3.1 (g++) server. + * + * 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 + * + * MANGCHAT is: + * Copyright (C) 2007 VisualDreams + * Written and Develloped by Cybrax. dev@visualdreams.nl + * + * MangChat was enhanced by Lice , Dj_baby & Sanaell + * with help and support from the MaNGOS project community. + * + * PLEASE RETAIN THE COPYRIGHT OF THE AUTHOR. + * + */ +#include "IRCClient.h" +#include "IRCCommands.h" +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "Config/ConfigEnv.h" +#include "Policies/SingletonImp.h" +#include "ObjectAccessor.h" +#include "WorldSession.h" +#include "WorldPacket.h" +#include "Log.h" +#include "Chat.h" +#include "ObjectMgr.h" +#include "Guild.h" +#include "MapManager.h" + +#include + +#pragma warning(disable:4305) +INSTANTIATE_SINGLETON_1( IRCClient ); + +/*------------------------------------------ +Threaded Function and declaration for the +socket handler they are practicly simiair +however the sockets and threading differs +from win and unix. These functions are run +on a seperate thread to "fake" asynchronus +IO. the thread constantly listens on the +socket for incomming data from IRC. +------------------------------------------*/ +#ifdef _WIN32 + #include + + // Thread Declarations + // These comments apply to the unix part aswell + // they only slightly differ in names for some parts + + DWORD nThreadID; + HANDLE hThread; + DWORD WINAPI SocketHandler(void* ptr); + // Thread Function + DWORD WINAPI SocketHandler(void* ptr) + { + char szBuffer[MAXDATASIZE]; + // We want to constantly keep looking for data + // so we create a loop + while(sIRC.AcceptData == true) + { + // reset the buffer + ZeroMemory(szBuffer,MAXDATASIZE); + + // check for incomming data on the socket + int nBytesRecv = recv(sIRC.sockfd, szBuffer, MAXDATASIZE - 1, 0 ); + if ( nBytesRecv == -1 ) + { + sLog.outError("Connection lost."); + // end the loop + sIRC.AcceptData = false; + } + else + { + if (-1 == nBytesRecv) + { + sLog.outError("Error occurred while receiving from socket."); + } + else + { + // Recieve was successfull send the data to RecvData function + sIRC.RecvData(szBuffer); + } + } + } + return 0; + } +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + pthread_t hThread; + int nThreadID; + void *SocketHandler( void *ptr ); + void *SocketHandler( void *ptr ) + { + char szBuffer[MAXDATASIZE]; + while(sIRC.AcceptData == true) + { + memset ( szBuffer, 0, MAXDATASIZE + 1 ); + int nBytesRecv = recv(sIRC.sockfd, szBuffer, MAXDATASIZE - 1, 0 ); + if ( nBytesRecv == -1 ) + { + sLog.outError("Connection lost."); + sIRC.AcceptData = false; + } + else + { + if (-1 == nBytesRecv) + { + // socket error + sLog.outError("Error occurred while receiving from socket."); + } + else + { + sIRC.RecvData(szBuffer); + } + } + } + return 0; + } +#endif + +/*------------------------------------------ +IRCClient Constructor +------------------------------------------*/ +IRCClient::IRCClient() +{ +} + +/*------------------------------------------ +IRCClient Destructor +------------------------------------------*/ +IRCClient::~IRCClient() +{ +} + +/*------------------------------------------ +IRCClient::IRCcmd + +Checks in chan commands from irc +------------------------------------------*/ +bool IRCClient::IRCcmd(std::string SndLine, std::string outputChan, std::string from, std::string type) +{ + std::size_t pos = SndLine.find(" "); + std::string cmd = SndLine.substr(0, pos); + std::transform(cmd.begin(), cmd.end(), cmd.begin(), towlower); + std::string param = ""; + if (pos != std::string::npos) + param = SndLine.substr(pos + 1); + int level = 0; + std::string query = "SELECT * FROM `irc_logedin` WHERE `nick` = '" + from + "'"; + QueryResult *result = sDatabase.Query(query.c_str()); + if (result) + { + Field *fields = result->Fetch(); + level = fields[2].GetInt32(); + delete result; + } + if ((sIRC.IRC_CMDS_MASK & 1) != 0) // if level 0 commands are active + { + if (cmd == sIRC.IRC_CMDS_PREFIX + "online") + { + IRCcommand::HandleOnlineIRCCommand(param, outputChan, type); + return true; + } + + if (cmd == sIRC.IRC_CMDS_PREFIX + "inchan") + { + IRCcommand::HandleInChanIRCCommand(outputChan, type); + return true; + } + + if (cmd == sIRC.IRC_CMDS_PREFIX + "player") + { + IRCcommand::HandlePlayerIRCCommand(param, outputChan, type); + return true; + } + + if (cmd == sIRC.IRC_CMDS_PREFIX + "info") + { + IRCcommand::HandleInfoIRCCommand(outputChan, type); + return true; + } + } + + if (((sIRC.IRC_CMDS_MASK & 2) != 0) && (cmd == sIRC.IRC_CMDS_PREFIX + "pm")) // if the pm system is active + { + IRCcommand::HandlePmIRCCommand(param, outputChan, level, from, type); + return true; + } + + if ((sIRC.IRC_CMDS_MASK & 4) != 0) // if gm commands are active + { + if (cmd == sIRC.IRC_CMDS_PREFIX + "kick") + { + IRCcommand::HandleKickIRCCommand(param, outputChan, level, type); + return true; + } + + if (cmd == sIRC.IRC_CMDS_PREFIX + "saveall") + { + IRCcommand::HandleSaveAllIRCCommand(outputChan, level, type); + return true; + } + + if (cmd == sIRC.IRC_CMDS_PREFIX + "tele") + { + IRCcommand::HandleTeleIRCCommand(param, outputChan, level, type); + return true; + } + + if (cmd == sIRC.IRC_CMDS_PREFIX + "revive") + { + IRCcommand::HandleReviveIRCCommand(param, outputChan, level, type); + return true; + } + + if (cmd == sIRC.IRC_CMDS_PREFIX + "kill") + { + IRCcommand::HandleKillIRCCommand(param, outputChan, level, type); + return true; + } + + if (cmd == sIRC.IRC_CMDS_PREFIX + "announce") + { + IRCcommand::HandleAnnounceIRCCommand(param, outputChan, level, type); + return true; + } + + if (cmd == sIRC.IRC_CMDS_PREFIX + "notify") + { + IRCcommand::HandleNotifyIRCCommand(param, outputChan, level, type); + return true; + } + } + + if ((sIRC.IRC_CMDS_MASK & 6) != 0) // loging in and out will only be possible if the pm system or the gm commands are active. + { + if (cmd == sIRC.IRC_CMDS_PREFIX + "login") + { + IRCcommand::HandleLoginIRCCommand(param, outputChan, from, type); + return true; + } + + if (cmd == sIRC.IRC_CMDS_PREFIX + "logout") + { + IRCcommand::HandleLogoutIRCCommand(outputChan, from, type); + return true; + } + } + + return false; +} + +/*------------------------------------------ +IRCClient::GetData + +Processes the lines from IRC +IRC uses a verry simplt protocol where the +data is split by spaces once we found the +spaces we can split them up and use or +analyze the variables +------------------------------------------*/ +void IRCClient::GetData(std::string SndLine) { + + if(SndLine.substr(0, 4) == "PING") + { + // we revceived a ping request + // we need to return pong + what the server sent us + std::string ret = "PONG " + SndLine.substr(4, SndLine.size() - 4); + sLog.outDebug("IRC: [PING] %s", ret.c_str()); + SendData(ret.c_str()); + } + else + { + // we receieved another type of message + sLog.outDebug("IRC: [IN] %s", SndLine.c_str()); + + // if the first character contains : + // we receievd a normal irc message + if(SndLine.substr(0,1)==":") + { + // find the spaces in the line + // and extract the values + int pos1 = SndLine.find(" "); + int pos2 = SndLine.find(" ", pos1 + 1); + std::string part1 = SndLine.substr(1, pos1 - 1); + std::string part2 = SndLine.substr(pos1 + 1, pos2 - pos1 - 1); + std::string rest = "", part3 = ""; + // username contains nick and host adress extract it + std::string szUser = GetUser(part1); + + // user ignore check + std::string ignorecheck = " " + szUser + " "; + std::transform(ignorecheck.begin(), ignorecheck.end(), ignorecheck.begin(), towlower); + if (sIRC.IGNORE_LIST.find(ignorecheck) != std::string::npos) return; + + // make evrything lowercase to ensure compare matches + std::transform(part2.begin(), part2.end(), part2.begin(), towlower); + + if(part2 == sIRC.IRC_CODE) + { + // we receieved the irc connectcode + sLog.outDebug("IRC: Connectcode received: %s", IRC_CODE.c_str()); + /* + std::string modes = sConfig.GetStringDefault("IRCUSERMODES", "") + if (modes != "") + { + SendData("MODE " + sIRC.IRC_NICK + " :" + modes); + } + */ + std::string ircpass = sConfig.GetStringDefault("IRCNICKPASS", ""); + if(sConfig.GetIntDefault("IRCQUAKENET", 0) == 1) + { + // if we use quakenet authenticate now + if (ircpass != "") + SendData("PRIVMSG Q@CServe.quakenet.org :AUTH " + sIRC.IRC_NICK + " " + ircpass + "\n"); + } + else + { + // if we use nickserv authenticate now + if (ircpass != "") + SendData("PRIVMSG nickserv :IDENTIFY " + ircpass + "\n"); + } + // time to join the channel + SendData("JOIN #" + sIRC.IRC_CHAN + "\n"); + } + + if(part2 == "join") + { + int pos3 = SndLine.find(":", pos2); + part3 = SndLine.substr(pos3 + 1); + std::transform(part3.begin(), part3.end(), part3.begin(), towlower); + if ((szUser == sIRC.IRC_NICK) && (part3 == "#" + sIRC.IRC_CHAN)) + { + if(sConfig.GetIntDefault("IRCSTARTMSGON", 0) == 1) + { + SendPMsg("#" + sIRC.IRC_CHAN, sConfig.GetStringDefault("IRCSTARTMSG", "Server Online")); + } + } + else if ((szUser == sIRC.IRC_NICK) && (sIRC.IRC_LDEF == 1) && (part3 == "#" + sIRC.IRC_DEF_CHAN)) + { + // if we have to leave the default channel do this now + SendData("PART #" + sIRC.IRC_DEF_CHAN + "\n"); + } + else if ((sIRC.BOTMASK & 1) != 0) + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str(), sIRC.MakeMsg(sIRC.WoW_joinedIRC, "$Name", szUser)); + // someone joined the channel on irc + } + + if ((part2 == "part") || (part2 == "quit")) + { + std::string query = "DELETE FROM `irc_logedin` WHERE `nick` = '" + szUser + "'"; + sDatabase.PExecute(query.c_str()); + if ((sIRC.BOTMASK & 1) != 0) + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str(), sIRC.MakeMsg(sIRC.WoW_leftIRC, "$Name", szUser)); + } + + if (part2 == "nick") + { + std::string query = "DELETE FROM `irc_logedin` WHERE `nick` = '" + szUser + "'"; + sDatabase.PExecute(query.c_str()); + int pos3 = SndLine.find(":", pos2); + part3 = SndLine.substr(pos3 + 1); + if ((sIRC.BOTMASK & 1) != 0) + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str(), sIRC.MakeMsg(sIRC.MakeMsg(sIRC.WoW_changedIRCnick, "$Name", szUser), "$NewName", part3)); + } + + if (part2 == "kick") + { + int pos3 = SndLine.find(" ", pos2 + 1); + int pos4 = SndLine.find(" ", pos3 + 1); + part3 = SndLine.substr(pos2 + 1, pos3 - pos2 - 1); + szUser = SndLine.substr(pos3 + 1, pos4 - pos3 -1); + std::transform(part3.begin(), part3.end(), part3.begin(), towlower); + std::string query = "DELETE FROM `irc_logedin` WHERE `nick` = '" + szUser + "'"; + sDatabase.PExecute(query.c_str()); + if(part3 == "#" + sIRC.IRC_CHAN) + { + if (((sIRC.BOTMASK & 1) != 0) && (szUser != sIRC.IRC_NICK)) //if someone is kicked from the chan + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str(), sIRC.MakeMsg(sIRC.WoW_leftIRC, "$Name", szUser)); + else if (sConfig.GetIntDefault("auto_rejoin_kick", 1) == 1) //If we are kicked and auto rejoin is 1 join channel back + { + SendData("JOIN #" + sIRC.IRC_CHAN + "\n"); + } + } + } + + if(part2 == "privmsg") + { + int pos3 = SndLine.find(" ", pos2 + 1); + part3 = SndLine.substr(pos2 + 1, pos3 - pos2 - 1); + rest = SndLine.substr(pos3 + 1, SndLine.size() - pos3 - 2); + + std::transform(part3.begin(), part3.end(), part3.begin(), towlower); + std::string chat = rest.substr(1, rest.size()); + + // we receieved chat from the irc channel + if(part3 == "#" + sIRC.IRC_CHAN) + { + std::string WorldMsg = "[" + szUser + "]" + chat; + sLog.outDebug("IRC: [IN] %s", WorldMsg.c_str()); + + // check if its a command or not. + if (IRCcmd(chat, "#" + sIRC.IRC_CHAN, szUser)) return; + + std::string str; + if (chat.substr(1, 7) == "ACTION ") + str = sIRC.MakeMsg(sIRC.MakeMsg(sIRC.WoW_IRCaction, "$Name", szUser), "$Msg", chat.substr(8, chat.length() - 9)); + else + str = sIRC.MakeMsg(sIRC.MakeMsg(sIRC.WoW_IRCmsg, "$Name", szUser), "$Msg", chat); + // we use the custom made function here because we have to loop + // thry the player sessoins and check of they are on the channel + // i couldnt figure out how to make it work in this class + // so i had to use world + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str() , sIRC.IRCcol2WoW(str.c_str())); + //sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str() , sIRC.IRCcol2WoW(sIRC.Hybrid2UTF(str.c_str()))); + } + + // we received a PM + std::string ircnick = sIRC.IRC_NICK; + std::transform(ircnick.begin(), ircnick.end(), ircnick.begin(), towlower); + if(part3 == ircnick) + { + sLog.outDebug("IRC: Private message received: %s", szUser.c_str()); + // check if its a command or not. + if (IRCcmd(chat, szUser, szUser, "NOTICE")) return; + } + } + } + } +} + +/*------------------------------------------ +IRCClient::ChannelBot + +Public function for announcing to the irc +channel +------------------------------------------*/ +void IRCClient::ChannelBot(std::string Msg) +{ + if(sIRC.Connected && sIRC.IRC_Login) + { + Msg = sIRC.Delink(Msg); + Msg = sIRC.WoWcol2IRC(Msg); + SendMsg(Msg); + } +} + +void IRCClient::SendIRCData(std::string data) +{ + if(sIRC.Connected && sIRC.IRC_Login) + { + sLog.outDebug("IRC [OUT]: %s", data.c_str()); + SendData(data.c_str()); + } +} + +void IRCClient::SendIRCpm(Player *From, std::string To, std::string Msg) +{ + if(sIRC.Connected && sIRC.IRC_Login) + { + char plevel [5]; + sprintf(plevel, "%u", From->getLevel()); + Msg = sIRC.Delink(Msg); + Msg = sIRC.WoWcol2IRC(Msg); + SendData("NOTICE " + To + " :13" + sIRC.MakeMsg(sIRC.MakeMsg(sIRC.MakeMsg(sIRC.IRC_WoWmsg, "$Name", From->GetName()), "$Msg", Msg), "$Lev", plevel) + "\n"); + } +} + +void IRCClient::HandleChannelJoin(Player *p) +{ + std::string pname = p->GetName(); + std::string pteam = "12"; + if (p->GetTeam() == 67) + pteam = "4"; + sDatabase.PExecute("DELETE FROM `irc_inchan` WHERE `guid` = %d", p->GetGUID()); + std::string query = "INSERT INTO `irc_inchan` VALUES(%d, '" + pname + "', " + pteam + ")"; + sDatabase.PExecute(query.c_str(), p->GetGUID()); + if ((sIRC.BOTMASK & 2)!= 0) + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.IRC_joinedWoWchan, "$Name", p->GetName())); + if ((sIRC.BOTMASK & 4)!= 0) + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str(), sIRC.MakeMsg(sIRC.WoW_joinedWoWchan, "$Name", p->GetName()), 0, p->GetSession()); +} + +void IRCClient::HandleChannelPart(Player *p) +{ + QueryResult *result = sDatabase.PQuery("SELECT * FROM `irc_inchan` WHERE `guid` = %d", p->GetGUID()); + if (result) + { + delete result; + sDatabase.PExecute("DELETE FROM `irc_inchan` WHERE `guid` = %d", p->GetGUID()); + if ((sIRC.BOTMASK & 2)!= 0) + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.IRC_leftWoWchan, "$Name", p->GetName())); + if ((sIRC.BOTMASK & 4)!= 0) + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str(), sIRC.MakeMsg(sIRC.WoW_leftWoWchan, "$Name", p->GetName()), 0, p->GetSession()); + } +} + +/*------------------------------------------ +IRCClient::ChannelMsg + +This function is called by the ChatHandler +whenever a player talks on a channel the +info is sent to this function. if the +channel matches the one set in the config +it will send the message to irc +------------------------------------------*/ +bool IRCClient::ChannelMsg(Player *plr, std::string Channel, std::string Msg) +{ + std::transform(Channel.begin(), Channel.end(), Channel.begin(), towlower); + if(Channel == sIRC.WOW_CHAN) + { + if(sIRC.Connected && sIRC.IRC_Login) + { + char plevel [5]; + sprintf(plevel, "%u", plr->getLevel()); + Msg = sIRC.Delink(Msg); + Msg = sIRC.WoWcol2IRC(Msg); + SendMsg(sIRC.MakeMsg(sIRC.MakeMsg(sIRC.MakeMsg(sIRC.IRC_WoWmsg, "$Name", plr->GetName()), "$Msg", Msg), "$Lev", plevel)); + } + return true; + } + return false; +} + +/*------------------------------------------ +IRCClient::GetUser + +This extracts the username from the lines +recieved from irc. because irc sends the +username and hostinfo in one line. +------------------------------------------*/ +std::string IRCClient::GetUser(std::string szU) +{ + // username and host are seperated by ! find the + // character and return the part before it + int pos = szU.find("!"); + return szU.substr(0, pos); +} + +std::string IRCClient::MakeMsg(std::string msg, std::string var, std::string val) +{ + std::size_t start = msg.find(var); + if (start != std::string::npos) + msg.replace(start, var.length(), val); + return msg; +} + +std::string IRCClient::Delink(std::string msg) +{ + std::size_t pos; + while ((pos = msg.find("|Hitem")) != std::string::npos) + { + msg.replace(pos, msg.find("|h") - pos + 2, "\x2"); + msg.replace(msg.find("|h"), 2, "\x2"); + } + return msg; +} + +std::string IRCClient::WoWcol2IRC(std::string msg) +{ + std::size_t pos; + char IRCCol[8][4] = { "\xF", "\xF", "\x3\x31\x34", "\x3\x30\x33", "\x3\x31\x32", "\x3\x30\x36", "\x3\x30\x37", "\x3\x30\x34"}; + char WoWCol[8][12] = { "|r", "|cffffffff", "|cff9d9d9d", "|cff1eff00", "|cff0070dd", "|cffa335ee", "|cffff8000", "|cffe6cc80"}; + for (int i=0; i<=7; i++) + { + while ((pos = msg.find(WoWCol[i])) != std::string::npos) + { + if (i == 0) + msg.replace(pos, 2, IRCCol[i]); + else + msg.replace(pos, 10, IRCCol[i]); + } + } + return msg; +} + +std::string IRCClient::IRCcol2WoW(std::string msg) +{ + std::size_t pos; + char IRCCol[16][4] = { "\x3\x30", "\x3\x31", "\x3\x32", "\x3\x33", "\x3\x34", "\x3\x35", "\x3\x36", "\x3\x37", "\x3\x38", "\x3\x39", "\x3\x31\x30", "\x3\x31\x31", "\x3\x31\x32", "\x3\x31\x33", "\x3\x31\x34", "\x3\x31\x35"}; + char IRCCol2[10][4] = { "\x3\x30\x30", "\x3\x30\x31", "\x3\x30\x32", "\x3\x30\x33", "\x3\x30\x34", "\x3\x30\x35", "\x3\x30\x36", "\x3\x30\x37", "\x3\x30\x38", "\x3\x30\x39"}; + char WoWcol[16][12] = { "|cffffffff", "|cff000000", "|cff00007f", "|cff009300", "|cffff0000", "|cff7f0000", "|cff9c009c", "|cfffc9300", "|cffffff00", "|cff00fc00", "|cff009393", "|cff00ffff", "|cff0000fc", "|cffff00ff", "|cff7f7f7f", "|cffd2d2d2"}; + for (int i=15; i>=0; i--) + { + if (i<10) + { + while ((pos = msg.find(IRCCol2[i])) != std::string::npos) + { + msg.replace(pos, 3, WoWcol[i]); + } + while ((pos = msg.find(IRCCol[i])) != std::string::npos) + { + msg.replace(pos, 2, WoWcol[i]); + } + + } + else + { + while ((pos = msg.find(IRCCol[i])) != std::string::npos) + { + msg.replace(pos, 3, WoWcol[i]); + } + } + } + + while ((pos = msg.find("\x3")) != std::string::npos) + { + msg.replace(pos, 1, "|r"); + } + while ((pos = msg.find("\xF")) != std::string::npos) + { + msg.replace(pos, 1, "|r"); + } + + return msg; +} + +std::string IRCClient::Hybrid2UTF(std::string Msg) +{ + /*char * tempchar = NULL; + + tempchar = (char*)malloc(strlen(Msg.c_str())+1); + + strcpy(tempchar,Msg.c_str()); + + BSTR in_text; + int nLen = MultiByteToWideChar(28591, 0, tempchar, lstrlen(tempchar), NULL, NULL); + in_text = SysAllocStringLen(NULL, nLen); + MultiByteToWideChar(28591, 0, tempchar, lstrlen(tempchar), in_text, nLen); + + //change to UTF.. + char *out_text = NULL; + int nLen2 = WideCharToMultiByte(65001, 0, in_text, -1, out_text, 0, NULL, NULL); + out_text = (char*)malloc(nLen2+1); + WideCharToMultiByte(65001, 0, in_text, -1, out_text, nLen2, NULL, NULL); + + Msg = out_text; + + free(tempchar); + SysFreeString(in_text); + free(out_text);*/ + + return Msg; +} +/*------------------------------------------ +IRCClient::LoadConfig + +Loads the configuration set in mangosd.conf +------------------------------------------*/ +bool IRCClient::LoadConfig() +{ + int ConfCnt = 0; + sLog.outBasic("Loading IRC Configuration\n"); + + IRC_HOST = sConfig.GetStringDefault("IRCHost", ""); + if(IRC_HOST.size() != 0) { ConfCnt++; } + + IRC_Port = sConfig.GetIntDefault("IRCPort", 0); + if(IRC_Port != 0) { ConfCnt++; } + + sIRC.IRC_CODE = sConfig.GetStringDefault("IRCCONNECTCODE", "001"); + sIRC.IRC_LDEF = sConfig.GetIntDefault("IRCLeaveDefChannel", 0); + sIRC.IRC_NICK = sConfig.GetStringDefault("IRCNICK", "mangosirc"); + sIRC.IRC_DEF_CHAN = sConfig.GetStringDefault("IRCDEFCHAN", "lobby"); + sIRC.IRC_CHAN = sConfig.GetStringDefault("IRCCHAN", "world"); + sIRC.WOW_CHAN = sConfig.GetStringDefault("WOWCHAN", "world"); + sIRC.BOTMASK = sConfig.GetIntDefault("BotMask", 0); + sIRC.IRC_CMDS_MASK = sConfig.GetIntDefault("IRC_Cmds_Mask", 0); + sIRC.IRC_CMDS_PREFIX = sConfig.GetStringDefault("IRC_Cmds_Prefix", "."); + sIRC.IGNORE_LIST = " " + sConfig.GetStringDefault("IRCIgnorList", "") + " "; + + // IRC Text Varriables: + sIRC.WoW_joinedIRC = sConfig.GetStringDefault("WoW_joinedIRC", ">> $Name joined IRC."); + sIRC.WoW_leftIRC = sConfig.GetStringDefault("WoW_leftIRC", "<< $Name left IRC."); + sIRC.WoW_changedIRCnick = sConfig.GetStringDefault("WoW_changedIRCnick", "<> $Name is now known as $NewName."); + sIRC.IRC_joinedWoWchan = sConfig.GetStringDefault("IRC_joinedWoWchan", "12>>04 $Name Joined The Channel"); + sIRC.WoW_joinedWoWchan = sConfig.GetStringDefault("WoW_joinedWoWchan", ">> $Name Joined The Channel"); + sIRC.IRC_leftWoWchan = sConfig.GetStringDefault("IRC_leftWoWchan", "12<<04 $Name Left The Channel"); + sIRC.WoW_leftWoWchan = sConfig.GetStringDefault("WoW_leftWoWchan", "<< $Name Left The Channel"); + sIRC.IRC_comeonline = sConfig.GetStringDefault("IRC_comeonline", "12>>04 $Name has come Online"); + sIRC.WoW_comeonline = sConfig.GetStringDefault("WoW_comeonline", ">> $Name has come Online"); + sIRC.IRC_goneoffline = sConfig.GetStringDefault("IRC_goneoffline", "12<<04 $Name has gone Offline"); + sIRC.WoW_goneoffline = sConfig.GetStringDefault("WoW_goneoffline", "<< $Name has gone Offline"); + sIRC.IRC_died = sConfig.GetStringDefault("IRC_died", "12<>04 $Name has joined the Spirits!"); + sIRC.WoW_died = sConfig.GetStringDefault("WoW_died", "<> $Name has joined the Spirits!"); + sIRC.IRC_revive = sConfig.GetStringDefault("IRC_revive", "12<>04 $Name has rejoined the Living!"); + sIRC.WoW_revive = sConfig.GetStringDefault("WoW_revive", "<> $Name has rejoined the Living!"); + sIRC.IRC_levelup = sConfig.GetStringDefault("IRC_levelup", "12<>04 $Name has gained Level $Lev!"); + sIRC.WoW_levelup = sConfig.GetStringDefault("WoW_levelup", "<> $Name has gained Level $Lev!"); + sIRC.IRC_WoWmsg = sConfig.GetStringDefault("IRC_WoWmsg", "[$Name] $Msg"); + sIRC.WoW_IRCmsg = sConfig.GetStringDefault("WoW_IRCmsg", "[$Name]: $Msg"); + sIRC.WoW_IRCaction = sConfig.GetStringDefault("WoW_IRCaction", "*$Name $Msg"); + sIRC.IRC_sysmsg = sConfig.GetStringDefault("IRC_sysmsg", "04,08/!\\System Message/!\\ $Msg 04,08/!\\System Message/!\\"); + sIRC.IRC_sysnotice = sConfig.GetStringDefault("IRC_sysnotice", "04,08/!\\System Notice/!\\ $Msg 04,08/!\\System Notice/!\\"); + + if(ConfCnt == 2) + { + // make sure we have at least a host and port + // or we cant connect + return true; + } + else + { + // config missing + sLog.outError("ERROR MISSING CONFIGURATION FOR HOST AND OR PORT AND OR GMPASS\n"); + return false; + } +} + +/*------------------------------------------ +IRCClient::run + +function called by ZThread +when we create the thread +------------------------------------------*/ +void IRCClient::run() +{ + sLog.outBasic("\n%s\n%s\n%s\n%s\n%s\n%s\n", + "***************************************", + "# MANGCHAT Threaded IRC CLient #", + "# (C) 2007 VisuaDreams #", + "# Written by Cybrax #", + "# Enhanced by Lice, Dj_baby & Sanaell #", + "***************************************"); + + // Thread started load config + if(!LoadConfig()) + { + return; + } + // clear irc tables + sDatabase.PExecute("DELETE FROM `irc_inchan`"); + sDatabase.PExecute("DELETE FROM `irc_logedin`"); + + sIRC.IRC_Login = false; + sIRC.Connected = false; + + if(sConfig.GetIntDefault("IRCON", 0)) + { + if(Connect(IRC_HOST.c_str(), IRC_Port)) + { + // If connections success + sLog.outBasic("IRC: Connected"); + // send hello to let the server know were here + if(SendData("HELLO\n")) + { + // begin with password first + std::string ircpass = sConfig.GetStringDefault("IRCSERVERPASS", "mangosirc"); + if(SendData("PASS " + ircpass + "\n")) + { + // send nickname + if(SendData("NICK " + sIRC.IRC_NICK + "\n")) + { + // send username and some required info + char hostname[128]; + gethostname(hostname, sizeof(hostname)); + if(SendData("USER " + sConfig.GetStringDefault("IRCUSER", "mangosirc") + " " + (std::string)hostname + " " + " MaNGOS :MaNGOS IRC 0.2\n")) + { + // login was a success + sLog.outBasic("IRC: Login succeeded\n"); + sIRC.IRC_Login = true; + }// send username + }// send nickname + } // password + } // send hello + + if(sIRC.IRC_Login == true) + { + sIRC.AcceptData = true; + // if we are logged in we need to start the listener thread and keep them alive + char *tmsg = ""; // bogus message to be passed to prevent errors + #ifdef _WIN32 + // create the thread + hThread = CreateThread(0, 0, SocketHandler, (void*)tmsg, 0, &nThreadID); + //wait for it to finish + WaitForSingleObject(hThread, INFINITE); + #else + // create the thread + nThreadID = pthread_create( &hThread, NULL, &SocketHandler, (void*)tmsg); + //wait for it to finish + pthread_join( hThread, NULL); + #endif + } + // The thread ended connection lost + sLog.outError("Connection died\n"); + Disconnect(); + } + else + { + // unable to login + sLog.outError("Unable to connect to %s\n", IRC_HOST.c_str()); + } + } + // close and cloeanup the socket + return; +} + +/*------------------------------------------ +IRCClient::Connect + +Initiate the connection to the irc server +returns true on success +returns false on error +------------------------------------------*/ +bool IRCClient::Connect(const char *host, int port) +{ + sLog.outBasic("***** Attempting IRC Connection *\n"); + + #ifdef _WIN32 + // if we use WIN32 we must initialize the winsock library + WSADATA wsaData; //WSAData + if(WSAStartup(MAKEWORD(2,0),&wsaData) != 0) + { + sLog.outError("IRC Error: Winsock Initialization Error"); + return false; + } + #endif + + struct hostent *he; + struct sockaddr_in their_addr; + + // get the hostname + if ((he=gethostbyname(host)) == NULL) + { + sLog.outError("IRC Error: Host Resolve Error"); + return false; + } + + // Create the socket + if ((sIRC.sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) + { + sLog.outError("IRC Error: Socket Error"); + return false; + } + + int on = 1; + if ( setsockopt ( sIRC.sockfd, SOL_SOCKET, SO_REUSEADDR, ( const char* ) &on, sizeof ( on ) ) == -1 ) + { + sLog.outError("IRC Error: Invalid Socket"); + return false; + } + + // set the socket to asynchronous IO and NON Blocking + #ifdef _WIN32 + u_long iMode = 0; + ioctlsocket(sIRC.sockfd, FIONBIO, &iMode); + #else + fcntl(sIRC.sockfd, F_SETFL, O_NONBLOCK); // set to non-blocking + fcntl(sIRC.sockfd, F_SETFL, O_ASYNC); // set to asynchronous I/O + #endif + + // construct data to be passed to the socket connect function + // this is port and host + + their_addr.sin_family = AF_INET; // host byte order + their_addr.sin_port = htons(port); // short, network byte order + their_addr.sin_addr = *((struct in_addr *)he->h_addr); + memset(&(their_addr.sin_zero), '\0', 8); // zero the rest of the struct + + // attempt a connection to the server + if (connect(sIRC.sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) + { + sLog.outError("IRC Error: Cannot connect to %s", host); + return false; + } + // Connection success + sIRC.Connected = true; + return true; +} + +/*------------------------------------------ +IRCClient::RecvData + +this function takes the data from the +socket and puts it in a stringstream +we do this because the data can contain +more then 1 line and we need to process +each line seperately +------------------------------------------*/ +void IRCClient::RecvData(std::string SockData) +{ + // the data receieved in the socket can have more then 1 line + // put the data into a stringstream and send each line + // to the GetData function + std::string reply; + std::istringstream iss(SockData); + while(getline(iss, reply)) + { + GetData(reply); + } +} + +/*------------------------------------------ +IRCClient::SendMsg + +This function constructs a message a +channel on the IRC server +------------------------------------------*/ +bool IRCClient::SendMsg(std::string SndLine) { + sLog.outDebug("IRC [OUT]: %s", SndLine.c_str()); + return SendData("PRIVMSG #" + sIRC.IRC_CHAN + " :" + SndLine + "\n"); +} + +/*------------------------------------------ +IRCClient::SendPMsg + +This function constructs a message for +a user on the IRC server +------------------------------------------*/ +bool IRCClient::SendPMsg(std::string user, std::string SndLine) { + sLog.outDebug("IRC [OUT]: %s", SndLine.c_str()); + return SendData("PRIVMSG " + user + " :" + SndLine + "\n"); +} + +/*------------------------------------------ +IRCClient::SendData + +This function sends the data passed to the +socket its similair to the previous function +however excepts a string as argument + +Note that we use the same function name +twice however the argument is different this +is called Function Overloading C++ will +automaticly detect the argument type passed +and select the right function +------------------------------------------*/ +bool IRCClient::SendData(const char *data) { + // send data on the socket + if (send(sIRC.sockfd, data, strlen(data), 0) == -1) + { + sLog.outError("IRC Error: Socket Receieve ** \n"); + Disconnect(); + return false; + } + return true; +} + +bool IRCClient::SendData(std::string data) +{ + // send data on the socket + // function overloaded + return SendData(data.c_str()); +} + +/*------------------------------------------ +IRCClient::Disconnect + +Function to close and cleanup the socket +------------------------------------------*/ +void IRCClient::Disconnect() +{ + close(sIRC.sockfd); + #ifdef _WIN32 + WSACleanup(); + #endif +} + +/*------------------------------------------ +IRCSocket Constructor +------------------------------------------*/ +IRCSocket::IRCSocket() +{ +} + +/*------------------------------------------ +IRCSocket Destructor +------------------------------------------*/ +IRCSocket::~IRCSocket() +{ +} Index: src/game/IRCClient.h =================================================================== --- src/game/IRCClient.h (revision 0) +++ src/game/IRCClient.h (revision 0) @@ -0,0 +1,155 @@ +/* + * MangChat Enhanced v0.2 + * Based on MANGCHAT a Threaded IRC Client for MaNGOS V0.1 by Cybrax + * + * MANGCHAT is a Internet Relay Chat client intergrated in mangos + * it allows you to have chat messages send from and to irc within + * the wow client. making it possible for players to get support + * from irc directly ingame or have several servers join the same + * irc channel and have "cross server chat". the client uses c + * sockets to send and receieve data from and to the irc server + * + * MangChat Enhanced aims to add many features to MangChat, + * including total "gm" control from irc, in channel and in game + * announces of level ups, deaths, resurrects, and so on. + * MangChat Enhanced features are designed to be fully integrated + * into the MaNGOS core system as well as be easly customised or + * disabled from the MaNGOS conf file. + * + * This software is currently still under development the client + * has been tested locally with 1 Windows XP server (Visual Studio) + * and 1 UNIX Debian 3.1 (g++) server. + * + * 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 + * + * MANGCHAT is: + * Copyright (C) 2007 VisualDreams + * Written and Develloped by Cybrax. dev@visualdreams.nl + * + * MangChat was enhanced by Lice , Dj_baby & Sanaell + * with help and support from the MaNGOS project community. + * + * PLEASE RETAIN THE COPYRIGHT OF THE AUTHOR. + * + */ + +#ifndef _IRC_CLIENT_H +#define _IRC_CLIENT_H + +#include "Policies/Singleton.h" +#include "Player.h" + +#define MAXDATASIZE 512 +#define IRC_CHANNEL_JOIN 0 +#define IRC_CHANNEL_LEAVE 1 + +class IRCClient : public ZThread::Runnable +{ +public: + IRCClient(); + ~IRCClient(); + void run(); + void RecvData(std::string SockData); + void SendIRCpm(Player *From, std::string To, std::string Msg); + void ChannelBot(std::string Msg); + void SendIRCData(std::string data); + void HandleChannelJoin(Player *p); + void HandleChannelPart(Player *p); + +public: + bool Connected; + bool IRC_Login; + bool AcceptData; + bool ChannelMsg(Player *plr, std::string Channel, std::string Msg); + +public: + int sockfd; + int IRC_LDEF; + int BOTMASK; + int IRC_CMDS_MASK; + +public: + std::string MakeMsg(std::string msg, std::string var, std::string val); + std::string Delink(std::string msg); + std::string WoWcol2IRC(std::string msg); + std::string IRCcol2WoW(std::string msg); + std::string Hybrid2UTF(std::string Msg); + + std::string IRC_NICK; + std::string IRC_CMDS_PREFIX; + std::string IGNORE_LIST; + std::string IRC_CODE; + std::string IRC_DEF_CHAN; + std::string IRC_CHAN; + std::string WOW_CHAN; + + // IRC Text Variables + std::string WoW_joinedIRC; + std::string WoW_leftIRC; + std::string WoW_changedIRCnick; + std::string IRC_joinedWoWchan; + std::string WoW_joinedWoWchan; + std::string IRC_leftWoWchan; + std::string WoW_leftWoWchan; + std::string IRC_comeonline; + std::string WoW_comeonline; + std::string IRC_goneoffline; + std::string WoW_goneoffline; + std::string IRC_died; + std::string WoW_died; + std::string IRC_revive; + std::string WoW_revive; + std::string IRC_levelup; + std::string WoW_levelup; + std::string IRC_WoWmsg; + std::string WoW_IRCmsg; + std::string WoW_IRCaction; + std::string IRC_sysmsg; + std::string IRC_sysnotice; + +private: + void Disconnect(); + void GetData(std::string SndLine); + +private: + bool LoadConfig(); + bool Connect(const char *host, int port); + bool SendMsg(std::string SndLine); + bool SendPMsg(std::string user, std::string SndLine); + bool SendData(const char *data); + bool SendData(std::string data); + bool IRCcmd(std::string SndLine, std::string outputChan, std::string from, std::string type="PRIVMSG"); + +private: + std::string GetUser(std::string szU); + +private: + int IRC_Port; + +private: + std::string IRC_HOST; +}; + +#define sIRC MaNGOS::Singleton::Instance() + + +class IRCSocket +{ +public: + IRCSocket(); + ~IRCSocket(); +}; + +#endif Index: src/game/IRCCommands.cpp =================================================================== --- src/game/IRCCommands.cpp (revision 0) +++ src/game/IRCCommands.cpp (revision 0) @@ -0,0 +1,483 @@ +/* + * MangChat Enhanced v0.2 + * Based on MANGCHAT a Threaded IRC Client for MaNGOS V0.1 by Cybrax + * + * MANGCHAT is a Internet Relay Chat client intergrated in mangos + * it allows you to have chat messages send from and to irc within + * the wow client. making it possible for players to get support + * from irc directly ingame or have several servers join the same + * irc channel and have "cross server chat". the client uses c + * sockets to send and receieve data from and to the irc server + * + * MangChat Enhanced aims to add many features to MangChat, + * including total "gm" control from irc, in channel and in game + * announces of level ups, deaths, resurrects, and so on. + * MangChat Enhanced features are designed to be fully integrated + * into the MaNGOS core system as well as be easly customised or + * disabled from the MaNGOS conf file. + * + * This software is currently still under development the client + * has been tested locally with 1 Windows XP server (Visual Studio) + * and 1 UNIX Debian 3.1 (g++) server. + * + * 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 + * + * MANGCHAT is: + * Copyright (C) 2007 VisualDreams + * Written and Develloped by Cybrax. dev@visualdreams.nl + * + * MangChat was enhanced by Lice , Dj_baby & Sanaell + * with help and support from the MaNGOS project community. + * + * PLEASE RETAIN THE COPYRIGHT OF THE AUTHOR. + * + */ + +#include "IRCCommands.h" +#include "IRCClient.h" +#include "Common.h" +#include "Language.h" +#include "Database/DatabaseEnv.h" +#include "Config/ConfigEnv.h" +#include "Policies/SingletonImp.h" +#include "ObjectAccessor.h" +#include "WorldSession.h" +#include "WorldPacket.h" +#include "Log.h" +#include "Chat.h" +#include "ObjectMgr.h" +#include "Guild.h" +#include "MapManager.h" + +void IRCcommand::HandlePlayerIRCCommand(std::string param, std::string outputChan,std::string type) +{ + int pos = param.find(" "); + if (param == "") + { + sIRC.SendIRCData(type + " " + outputChan + " :" + "Syntax: " + sIRC.IRC_CMDS_PREFIX + "player playername" + "\n"); + return; + } + std::string pname = param.substr(0, pos); + normalizePlayerName(pname); + sDatabase.escape_string(pname); + std::string sqlquery = "SELECT `guid`, `name`, `race`, `class`, `online`, SUBSTRING_INDEX(SUBSTRING_INDEX(`data`, ' ' , 35), ' ' , -1) AS `level`, SUBSTRING_INDEX(SUBSTRING_INDEX(`data`, ' ' , 230), ' ' , -1) AS `guildid`, SUBSTRING_INDEX(SUBSTRING_INDEX(`data`, ' ' , 231), ' ' , -1) AS `guildrank`, SUBSTRING_INDEX(SUBSTRING_INDEX(`data`, ' ' , 859), ' ' , -1) AS `xp`, SUBSTRING_INDEX(SUBSTRING_INDEX(`data`, ' ' , 860), ' ' , -1) AS `maxxp`, SUBSTRING_INDEX(SUBSTRING_INDEX(data, ' ' , 1327), ' ' , -1) AS `gold` FROM `character` WHERE `name` LIKE '" + pname + "';"; + QueryResult *result = sDatabase.Query(sqlquery.c_str()); + if(result) + { + Field *fields = result->Fetch(); + std::string pguid = fields[0].GetCppString(); + pname = fields[1].GetCppString(); + uint32 praceid = fields[2].GetUInt32(); + uint32 pclassid = fields[3].GetUInt32(); + std::string ponline = (fields[4].GetInt32() == 1 ? "\x3\x30\x33Online" : "\x3\x30\x34Offline\xF"); + std::string plevel = fields[5].GetCppString(); + uint32 pguildid = fields[6].GetUInt32(); + uint32 pguildrank = fields[7].GetUInt32(); + std::string pxp = fields[8].GetCppString(); + std::string pmaxxp = fields[9].GetCppString(); + unsigned int money = fields[10].GetInt32(); + delete result; + + std::string guildinfo = ""; + if (pguildid != 0) + { + Guild* guild = objmgr.GetGuildById(pguildid); + if (guild) + { + guildinfo = ", " + guild->GetRankName(pguildrank) + " of " + guild->GetName(); + } + } + + ChrRacesEntry const* prace = sChrRacesStore.LookupEntry(praceid); + ChrClassesEntry const* pclass = sChrClassesStore.LookupEntry(pclassid); + + if (atoi(plevel.c_str()) < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + plevel += " (" + pxp + "/" + pmaxxp + ")"; + + unsigned int gold = money / 10000; + unsigned int silv = (money % 10000) / 100; + unsigned int cop = (money % 10000) % 100; + char tempgold [100]; + sprintf(tempgold, "\x2\x3\x30\x37%ug \x3\x31\x34%us \x3\x30\x35%uc\xF", gold, silv, cop); + + if (ponline == "\x3\x30\x33Online") + { + Player * player = ObjectAccessor::Instance().FindPlayerByName(pname.c_str()); + if (player) + { + AreaTableEntry const* area = GetAreaEntryByAreaID(player->GetAreaId()); + ponline += " in " + (std::string) area->area_name[sWorld.GetDBClang()]; + if (area->zone != 0) + { + AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone); + ponline += " (" + (std::string)zone->area_name[sWorld.GetDBClang()] + ")"; + } + } + } + + sIRC.SendIRCData(type + " " + outputChan + " :" + "About " + pname + guildinfo + "\n"); + sIRC.SendIRCData(type + " " + outputChan + " :" + "\x2Race:\x2\x3\x31\x30 " + (std::string)prace->name[sWorld.GetDBClang()] + "\xF |\x2 Class:\x2\x3\x31\x30 " + (std::string)pclass->name[sWorld.GetDBClang()] + "\xF |\x2 Level:\x2\x3\x31\x30 " + plevel + "\xF |\x2 Money:\x2 " + tempgold + "\xF |\x2 Status:\x2 " + ponline + "\n"); + + } + else + sIRC.SendIRCData(type + " " + outputChan + " :" + "Unknown Player: " + pname + "\n"); + +} + +void IRCcommand::HandleOnlineIRCCommand(std::string param, std::string outputChan,std::string type) +{ + std::transform(param.begin(), param.end(), param.begin(), towlower); + bool showHorde = false; + bool showAlli = false; + if (param.length() > 0) + { + if (param.substr(0, 3) == "all") + { + showHorde = true; + showAlli = true; + } + else if (param.at(0) == 'h') + showHorde = true; + else if (param.at(0) == 'a') + showAlli = true; + } + uint32 playersonline = 0; + std::string output = ":"; + char buffer[80]; + ObjectAccessor::PlayersMapType& m = ObjectAccessor::Instance().GetPlayers(); + for(ObjectAccessor::PlayersMapType::iterator itr = m.begin(); itr != m.end(); ++itr) + { + if (((itr->second->GetTeam() == 67) && showHorde) || ((itr->second->GetTeam() == 469) && showAlli)) + { + std::string pname = itr->second->GetName(); + if (itr->second->GetSession()->GetSecurity() > 0) + { + sprintf(buffer, "%c%s%c", 31, pname.c_str(), 31); + pname = buffer; + } + if (itr->second->GetSession()->GetPlayer()->isGameMaster()) + { + sprintf(buffer, "%c03%s%c", 3, pname.c_str(), 3); + pname = buffer; + } + switch (itr->second->GetTeam()) + { + case 67: //horde + sprintf (buffer, "%c04%s%c", 3, pname.c_str(), 3); + pname = buffer; + break; + + case 469: //alliance + sprintf (buffer, "%c12%s%c", 3, pname.c_str(), 3); + pname = buffer; + break; + default: + break; + } + + sprintf (buffer, " %s(%d)", pname.c_str(), itr->second->getLevel()); + + output += buffer; + playersonline++; + } + if (!showHorde && !showAlli) + playersonline++; + } + sprintf (buffer, "%c%d Player(s) Online", 2, playersonline); + output = (output == ":" ? buffer : buffer + output); + sIRC.SendIRCData(type + " " + outputChan + " :" + output + "\n"); +} + +void IRCcommand::HandleInChanIRCCommand(std::string outputChan,std::string type) +{ + QueryResult *result = sDatabase.Query("SELECT * FROM irc_inchan ORDER BY `name`"); + if (!result) + { + sIRC.SendIRCData(type + " " + outputChan + " :No Players are in the Channel\n"); + return; + } + std::string output = "\x02Players in the Channel:"; + for (uint64 i=0; i < result->GetRowCount(); i++) + { + Field *fields = result->Fetch(); + output += " \x03" + fields[2].GetCppString() + fields[1].GetCppString() + "\x03"; + result->NextRow(); + } + delete result; + sIRC.SendIRCData(type + " " + outputChan + " :" + output + "\n"); +} + +void IRCcommand::HandleInfoIRCCommand(std::string outputChan,std::string type) +{ + char clientsNum [50]; + sprintf(clientsNum, "%u", sWorld.GetSessionCount()); + char maxClientsNum [50]; + sprintf(maxClientsNum, "%u", sWorld.GetMaxSessionCount()); + std::string str = secsToTimeString(sWorld.GetUptime()); + + sIRC.SendIRCData(type + " " + outputChan + " :Number of Players Online: " + (std::string)clientsNum + " (Max since last restart: " + (std::string)maxClientsNum + ")" + " Uptime: " + str + "\n"); +} + +void IRCcommand::HandleKickIRCCommand(std::string param, std::string outputChan, int level, std::string type) +{ + if (level < 2) + { + sIRC.SendIRCData(type + " " + outputChan + " :You Dont Have Access To That Command\n"); + return; + } + int pos = param.find(" "); + param = param.substr(0, pos); + normalizePlayerName(param); + Player* chr = ObjectAccessor::Instance().FindPlayerByName( param.c_str() ); + if (chr) + { + chr->GetSession()->KickPlayer(); + sIRC.SendIRCData(type + " " + outputChan + " :" + param + " was kicked.\n"); + } + else + sIRC.SendIRCData(type + " " + outputChan + " :" + param + " was not online.\n"); +} + +void IRCcommand::HandleSaveAllIRCCommand(std::string outputChan, int level, std::string type) +{ + if (level < 1) + { + sIRC.SendIRCData(type + " " + outputChan + " :You Dont Have Access To That Command\n"); + return; + } + ObjectAccessor::Instance().SaveAllPlayers(); + sIRC.SendIRCData(type + " " + outputChan + " :All Players Saved\n"); +} + +void IRCcommand::HandleTeleIRCCommand(std::string param, std::string outputChan, int level, std::string type) +{ + if (level < 1) + { + sIRC.SendIRCData(type + " " + outputChan + " :You Dont Have Access To That Command\n"); + return; + } + int pos = param.find(" "); + std::string player = param.substr(0, pos); + normalizePlayerName(player); + Player* chr = ObjectAccessor::Instance().FindPlayerByName( player.c_str() ); + if (chr) + { + if(chr->isInFlight()) + { + sIRC.SendIRCData(type + " " + outputChan + " :4[ERROR]: " + player + " is flying and cannot be transported\n"); + } + else + { + std::string location = param.substr(pos + 1, param.size() - pos - 1); + if(location != "") + { + sDatabase.escape_string(location); + std::string sqlquery = "SELECT `position_x`,`position_y`,`position_z`,`orientation`,`map` FROM `game_tele` WHERE `name` = '" + location + "';"; + QueryResult *result = sDatabase.Query(sqlquery.c_str()); + if (!result) + { + sIRC.SendIRCData(type + " " + outputChan + " :4[ERROR]: Tele location not found: " + location + "\n"); + } + else + { + 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)) + sIRC.SendIRCData(type + " " + outputChan + " :4[ERROR]: Invalid tele location: " + location + "\n"); + else + { + chr->SetRecallPosition(chr->GetMapId(),chr->GetPositionX(),chr->GetPositionY(),chr->GetPositionZ(),chr->GetOrientation()); + chr->TeleportTo(mapid, x, y, z, ort); + sIRC.SendIRCData(type + " " + outputChan + " :13[" + player + "]: teleported to " + location + "\n"); + } + } + } + else + sIRC.SendIRCData(type + " " + outputChan + " :4[ERROR]: Please Suply tele location\n"); + } + } + else + sIRC.SendIRCData(type + " " + outputChan + " :4[ERROR]: " + player + " is not online\n"); +} + +void IRCcommand::HandleReviveIRCCommand(std::string param, std::string outputChan, int level, std::string type) +{ + if (level < 3) + { + sIRC.SendIRCData(type + " " + outputChan + " :You Dont Have Access To That Command\n"); + return; + } + int pos = param.find(" "); + param = param.substr(0, pos); + normalizePlayerName(param); + Player* chr = ObjectAccessor::Instance().FindPlayerByName( param.c_str() ); + if (chr) + { + if(chr->isDead()) + { + chr->ResurrectPlayer(0.5f); + chr->SpawnCorpseBones(); + chr->SaveToDB(); + sIRC.SendIRCData(type + " " + outputChan + " :" + param + " resurrected\n"); + } + else + sIRC.SendIRCData(type + " " + outputChan + " :" + param + " is not Dead\n"); + } + else + sIRC.SendIRCData(type + " " + outputChan + " :" + param + " Is not online\n"); +} + +void IRCcommand::HandleKillIRCCommand(std::string param, std::string outputChan, int level, std::string type) +{ + if (level < 3) + { + sIRC.SendIRCData(type + " " + outputChan + " :You Dont Have Access To That Command\n"); + return; + } + int pos = param.find(" "); + param = param.substr(0, pos); + normalizePlayerName(param); + Player* chr = ObjectAccessor::Instance().FindPlayerByName( param.c_str() ); + if (chr) + { + if(chr->isAlive()) + { + chr->DealDamage(chr, chr->GetHealth(), DIRECT_DAMAGE, 0, NULL, 0, false); + chr->SaveToDB(); + sIRC.SendIRCData(type + " " + outputChan + " :" + param + " killed\n"); + } + else + sIRC.SendIRCData(type + " " + outputChan + " :" + param + " is already Dead\n"); + } + else + sIRC.SendIRCData(type + " " + outputChan + " :" + param + " Is not online\n"); +} + +void IRCcommand::HandleAnnounceIRCCommand(std::string param, std::string outputChan, int level, std::string type) +{ + if (level < 1) + { + sIRC.SendIRCData(type + " " + outputChan + " :You Dont Have Access To That Command\n"); + return; + } + std::string str = LANG_SYSTEMMESSAGE + sIRC.IRCcol2WoW(param); + sWorld.SendWorldText(str.c_str(), NULL); + + if ((sIRC.BOTMASK & 1024)!= 0) + { + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.IRC_sysmsg, "$Msg", param)); + } +} + +void IRCcommand::HandleNotifyIRCCommand(std::string param, std::string outputChan, int level, std::string type) +{ + if (level < 1) + { + sIRC.SendIRCData(type + " " + outputChan + " :You Dont Have Access To That Command\n"); + return; + } + std::string str = LANG_GLOBAL_NOTIFY + sIRC.IRCcol2WoW(param); + + WorldPacket data(SMSG_NOTIFICATION, (str.size()+1)); + data << str; + sWorld.SendGlobalMessage(&data); + + if ((sIRC.BOTMASK & 1024)!= 0) + { + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.IRC_sysnotice, "$Msg", str.substr(15))); + } +} + +void IRCcommand::HandlePmIRCCommand(std::string param, std::string outputChan, int level, std::string from, std::string type) +{ + int pos = param.find(" "); + std::string player = param.substr(0, pos); + normalizePlayerName(player); + Player* chr = ObjectAccessor::Instance().FindPlayerByName( player.c_str() ); + if (chr) + { + if(chr->isAcceptWhispers() || chr->GetSession()->GetSecurity() <= level) + { + std::string msg = param.substr(pos + 1, param.size() - pos - 1); + + std::string chat = "|cffCC4ACC" + sIRC.IRCcol2WoW(sIRC.MakeMsg(sIRC.MakeMsg(sIRC.WoW_IRCmsg, "$Name", from), "$Msg", msg)) + "|r"; + // construct a packet + WorldPacket data(SMSG_MESSAGECHAT, 200); + data << (uint8)CHAT_MSG_SYSTEM; + data << (uint32)LANG_UNIVERSAL; + data << chr->GetSession()->GetPlayer()->GetGUID(); + data << (uint32) strlen(chat.c_str()) + 1; + data << chat.c_str(); + data << (uint8)0; + + // send the packet to the player + chr->GetSession()->SendPacket(&data); + sIRC.SendIRCData(type + " " + outputChan + " :13To [" + player + "]: " + msg + "\n"); + } + else + sIRC.SendIRCData(type + " " + outputChan + " :" + player + " is not accepting private messages\n"); + } + else + sIRC.SendIRCData(type + " " + outputChan + " :" + player + " is not online\n"); +} + +void IRCcommand::HandleLoginIRCCommand(std::string param, std::string outputChan, std::string from, std::string type) +{ + std::string query = "DELETE FROM `irc_logedin` WHERE `nick` = '" + from + "'"; + sDatabase.PExecute(query.c_str()); + std::size_t pos = param.find(" "); + std::string user = param.substr(0, pos); + if (pos == std::string::npos) + { + sIRC.SendIRCData(type + " " + outputChan + " :You must specify a username and password\n"); + return; + } + std::string pass = param.substr(pos + 1); + loginDatabase.escape_string(user); + loginDatabase.escape_string(pass); + query = "SELECT id, gmlevel FROM `account` WHERE `username` LIKE '" + user + "' AND `password` = '" + pass + "'"; + QueryResult *result = loginDatabase.Query(query.c_str()); + if (!result) + { + sIRC.SendIRCData(type + " " + outputChan + " :Wrong User or Pass\n"); + return; + } + Field *fields = result->Fetch(); + query = "DELETE FROM `irc_logedin` WHERE `account` = " + fields[0].GetCppString(); + sDatabase.PExecute(query.c_str()); + query = "INSERT INTO `irc_logedin` VALUES(" + fields[0].GetCppString() + ", '" + from + "', " + fields[1].GetCppString() + ")"; + sDatabase.PExecute(query.c_str()); + delete result; + sIRC.SendIRCData(type + " " + outputChan + " :You are now loged in as " + user + "\n"); +} +void IRCcommand::HandleLogoutIRCCommand(std::string outputChan, std::string from, std::string type) +{ + std::string query = "SELECT * FROM `irc_logedin` WHERE `nick` = '" + from + "'"; + QueryResult *result = sDatabase.Query(query.c_str()); + if (!result) + { + sIRC.SendIRCData(type + " " + outputChan + " :You are not loged in.\n"); + return; + } + delete result; + query = "DELETE FROM `irc_logedin` WHERE `nick` = '" + from + "'"; + sDatabase.PExecute(query.c_str()); + sIRC.SendIRCData(type + " " + outputChan + " :You are now loged out.\n"); +} \ No newline at end of file Index: src/game/IRCCommands.h =================================================================== --- src/game/IRCCommands.h (revision 0) +++ src/game/IRCCommands.h (revision 0) @@ -0,0 +1,69 @@ +/* + * MangChat Enhanced v0.2 + * Based on MANGCHAT a Threaded IRC Client for MaNGOS V0.1 by Cybrax + * + * MANGCHAT is a Internet Relay Chat client intergrated in mangos + * it allows you to have chat messages send from and to irc within + * the wow client. making it possible for players to get support + * from irc directly ingame or have several servers join the same + * irc channel and have "cross server chat". the client uses c + * sockets to send and receieve data from and to the irc server + * + * MangChat Enhanced aims to add many features to MangChat, + * including total "gm" control from irc, in channel and in game + * announces of level ups, deaths, resurrects, and so on. + * MangChat Enhanced features are designed to be fully integrated + * into the MaNGOS core system as well as be easly customised or + * disabled from the MaNGOS conf file. + * + * This software is currently still under development the client + * has been tested locally with 1 Windows XP server (Visual Studio) + * and 1 UNIX Debian 3.1 (g++) server. + * + * 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 + * + * MANGCHAT is: + * Copyright (C) 2007 VisualDreams + * Written and Develloped by Cybrax. dev@visualdreams.nl + * + * MangChat was enhanced by Lice , Dj_baby & Sanaell + * with help and support from the MaNGOS project community. + * + * PLEASE RETAIN THE COPYRIGHT OF THE AUTHOR. + * + */ + +#include "Common.h" + +namespace IRCcommand +{ + // chan commands, default return type == PRIVMSG + void HandlePlayerIRCCommand(std::string param, std::string outputChan, std::string type="PRIVMSG"); + void HandleOnlineIRCCommand(std::string param, std::string outputChan, std::string type="PRIVMSG"); + void HandleInChanIRCCommand(std::string outputChan, std::string type="PRIVMSG"); + void HandleInfoIRCCommand(std::string outputChan, std::string type="PRIVMSG"); + + // priv commands, default return type == NOTICE + void HandleKickIRCCommand(std::string param, std::string outputChan, int level, std::string type="NOTICE"); + void HandleSaveAllIRCCommand(std::string outputChan, int level, std::string type="NOTICE"); + void HandleTeleIRCCommand(std::string param, std::string outputChan, int level, std::string type="NOTICE"); + void HandleReviveIRCCommand(std::string param, std::string outputChan, int level, std::string type="NOTICE"); + void HandleKillIRCCommand(std::string param, std::string outputChan, int level, std::string type="NOTICE"); + void HandleAnnounceIRCCommand(std::string param, std::string outputChan, int level, std::string type="NOTICE"); + void HandleNotifyIRCCommand(std::string param, std::string outputChan, int level, std::string type="NOTICE"); + void HandlePmIRCCommand(std::string param, std::string outputChan, int level, std::string from, std::string type="NOTICE"); + void HandleLoginIRCCommand(std::string param, std::string outputChan, std::string from, std::string type="NOTICE"); + void HandleLogoutIRCCommand(std::string outputChan, std::string from, std::string type="NOTICE"); +} Index: src/game/Level0.cpp =================================================================== --- src/game/Level0.cpp (revision 3815) +++ src/game/Level0.cpp (working copy) @@ -29,6 +29,7 @@ #include "ObjectAccessor.h" #include "Language.h" #include "SpellAuras.h" +#include "IRCClient.h" bool ChatHandler::ShowHelpForCommand(ChatCommand *table, const char* cmd) { @@ -365,3 +366,22 @@ SendSysMessage(LANG_BAD_VALUE); return true; } + +bool ChatHandler::HandleIRCpmCommand(const char* args) +{ + std::string Msg = args; + if ((sIRC.IRC_CMDS_MASK & 2) == 0) + { + Msg = "WoW <-> iRC pm system disabled"; + PSendSysMessage(Msg.c_str()); + return true; + } + if (Msg.find(" ") == std::string::npos) + return false; + std::string To = Msg.substr(0, Msg.find(" ")); + Msg = Msg.substr(Msg.find(" ") + 1); + sIRC.SendIRCpm(m_session->GetPlayer(), To, Msg); + Msg = "|cffCC4ACCTo [" + To + "]: " + Msg + "|r"; + PSendSysMessage(Msg.c_str()); + return true; +} Index: src/game/Level1.cpp =================================================================== --- src/game/Level1.cpp (revision 3815) +++ src/game/Level1.cpp (working copy) @@ -31,6 +31,7 @@ #include "Language.h" #include "RedZoneDistrict.h" #include "Transports.h" +#include "IRCClient.h" bool ChatHandler::HandleSayCommand(const char* args) { @@ -107,7 +108,8 @@ std::string str = LANG_SYSTEMMESSAGE; str += args; sWorld.SendWorldText(str.c_str(), NULL); - + if ((sIRC.BOTMASK & 1024)!= 0) + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.IRC_sysmsg, "$Msg", args)); return true; } @@ -122,7 +124,8 @@ WorldPacket data(SMSG_NOTIFICATION, (str.size()+1)); data << str; sWorld.SendGlobalMessage(&data); - + if ((sIRC.BOTMASK & 1024)!= 0) + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.IRC_sysnotice, "$Msg", args)); return true; } Index: src/game/Makefile.am =================================================================== --- src/game/Makefile.am (revision 3815) +++ src/game/Makefile.am (working copy) @@ -115,6 +115,10 @@ HomeMovementGenerator.h \ IdleMovementGenerator.cpp \ IdleMovementGenerator.h \ + IRCClient.cpp \ + IRCClient.h \ + IRCCommands.cpp \ + IRCCommands.h \ Item.cpp \ Item.h \ ItemHandler.cpp \ Index: src/game/Player.cpp =================================================================== --- src/game/Player.cpp (revision 3815) +++ src/game/Player.cpp (working copy) @@ -33,6 +33,7 @@ #include "UpdateData.h" #include "Channel.h" #include "Chat.h" +#include "IRCClient.h" #include "MapManager.h" #include "MapInstanced.h" #include "ObjectMgr.h" @@ -1904,6 +1905,15 @@ level += 1; + std::string pname = GetName(); + char temp [5]; + sprintf(temp, "%u", level); + std::string plevel = temp; + if ((sIRC.BOTMASK & 256)!= 0) + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str(),sIRC.MakeMsg(sIRC.MakeMsg(sIRC.WoW_levelup, "$Name", pname), "$Lev", plevel)); + if ((sIRC.BOTMASK & 512)!= 0) + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.MakeMsg(sIRC.IRC_levelup, "$Name", pname), "$Lev", plevel)); + InitStatsForLevel(level); InitTalentForLevel(); @@ -3144,6 +3154,12 @@ void Player::ResurrectPlayer(float restore_percent, bool updateToWorld) { + std::string pname = GetName(); + if ((sIRC.BOTMASK & 64)!= 0) + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str(),sIRC.MakeMsg(sIRC.WoW_revive, "$Name", pname)); + if ((sIRC.BOTMASK & 128)!= 0) + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.IRC_revive, "$Name", pname)); + WorldPacket data(SMSG_SH_POSITION, 4*4); // remove spirit healer position data << uint32(-1); data << uint32(0); @@ -3237,6 +3253,12 @@ void Player::KillPlayer() { + std::string pname = GetName(); + if ((sIRC.BOTMASK & 64)!= 0) + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str(),sIRC.MakeMsg(sIRC.WoW_died, "$Name", pname)); + if ((sIRC.BOTMASK & 128)!= 0) + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.IRC_died, "$Name", pname)); + if(InBattleGround()) { BattleGround* bg = sBattleGroundMgr.GetBattleGround(GetBattleGroundId()); @@ -3483,12 +3505,25 @@ void Player::JoinedChannel(Channel *c) { + std::string channame = c->GetName(); + std::transform(channame.begin(), channame.end(), channame.begin(), towlower); + if (channame == sIRC.WOW_CHAN) + { + sIRC.HandleChannelJoin(this); + } m_channels.push_back(c); } void Player::LeftChannel(Channel *c) { m_channels.remove(c); + + std::string channame = c->GetName(); + std::transform(channame.begin(), channame.end(), channame.begin(), towlower); + if (channame == sIRC.WOW_CHAN) + { + sIRC.HandleChannelPart(this); + } } void Player::CleanupChannels() Index: src/game/World.cpp =================================================================== --- src/game/World.cpp (revision 3815) +++ src/game/World.cpp (working copy) @@ -46,6 +46,7 @@ #include "TargetedMovementGenerator.h" #include "RedZoneDistrict.h" #include "WaypointMovementGenerator.h" +#include "ChannelMgr.h" INSTANTIATE_SINGLETON_1( World ); @@ -879,6 +880,48 @@ return; } +void World::SendWorldMessage(const char *channel, std::string chat, uint64 guid, WorldSession *self) +{ + /* + Why is this function needed? + tobe able to send a message you need a valid ingame GUID + snce the IRC client isnt logged in we cant properly send message + or it would result in non existing players + also i didnt manage to figure out how to get the sessions in my own clas + so this function was born + */ + SessionMap::iterator itr; + for (itr = m_sessions.begin(); itr != m_sessions.end(); itr++) + { + // Loop thru all online sessions + if (itr->second && itr->second->GetPlayer() && itr->second->GetPlayer()->IsInWorld() && itr->second != self) + { + // check if the player is in the world + if(ChannelMgr* cMgr = channelMgr(itr->second->GetPlayer()->GetTeam())) + { + // check if the player is grouped + if(Channel *chn = cMgr->GetChannel(channel, itr->second->GetPlayer())) + { + // if the player is on our channel + + WorldPacket data; + + data.Initialize(SMSG_MESSAGECHAT); + data << (uint8)14; + data << (uint32)0; + data << channel; + data << guid; + data << (uint32) (strlen(chat.c_str()) + 1); + data << chat.c_str(); + data << (uint8)0; + + itr->second->SendPacket(&data); + } + } + } + } +} + /// Send a packet to all players (except self if mentionned) void World::SendGlobalMessage(WorldPacket *packet, WorldSession *self, uint32 team) { Index: src/game/World.h =================================================================== --- src/game/World.h (revision 3815) +++ src/game/World.h (working copy) @@ -206,6 +206,7 @@ void SendWorldText(const char *text, WorldSession *self = 0); void SendGlobalMessage(WorldPacket *packet, WorldSession *self = 0, uint32 team = 0); + void SendWorldMessage(const char *channel, std::string chat, uint64 guid = 0, WorldSession *self = 0); void SendZoneMessage(uint32 zone, WorldPacket *packet, WorldSession *self = 0, uint32 team = 0); void SendZoneText(uint32 zone, const char *text, WorldSession *self = 0, uint32 team = 0); void SendServerMessage(uint32 type, const char *text = "", Player* player = NULL); Index: src/game/WorldSession.cpp =================================================================== --- src/game/WorldSession.cpp (revision 3815) +++ src/game/WorldSession.cpp (working copy) @@ -37,6 +37,8 @@ #include "ObjectAccessor.h" #include "BattleGroundMgr.h" #include "Language.h" // for CMSG_DISMOUNT handler +#include "IRCClient.h" +#include "ChannelMgr.h" /// WorldSession constructor WorldSession::WorldSession(uint32 id, WorldSocket *sock, uint32 sec) : _player(NULL), _socket(sock), @@ -647,6 +649,15 @@ data<<_player->GetGUID(); _player->BroadcastPacketToFriendListers(&data); + std::string pname = _player->GetName(); + + sIRC.HandleChannelPart(_player); + + if ((sIRC.BOTMASK & 8)!= 0) + sWorld.SendWorldMessage(sIRC.WOW_CHAN.c_str(),sIRC.MakeMsg(sIRC.WoW_goneoffline, "$Name", pname)); + if ((sIRC.BOTMASK & 16)!= 0) + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.IRC_goneoffline, "$Name", pname)); + ///- Delete the player object delete _player; _player = 0; Index: src/mangosd/CliRunnable.cpp =================================================================== --- src/mangosd/CliRunnable.cpp (revision 3815) +++ src/mangosd/CliRunnable.cpp (working copy) @@ -31,6 +31,7 @@ #include "SystemConfig.h" #include "Config/ConfigEnv.h" #include "Util.h" +#include "../game/IRCClient.h" #include "CliRunnable.h" @@ -217,6 +218,8 @@ str += text; sWorld.SendWorldText(str.c_str(), NULL); zprintf("Broadcasting to the world:%s\r\n",str.c_str()); + if ((sIRC.BOTMASK & 1024)!= 0) + sIRC.ChannelBot(sIRC.MakeMsg(sIRC.IRC_sysmsg, "$Msg", text)); } /// Print the list of commands and associated description Index: src/mangosd/mangosd.conf.in =================================================================== --- src/mangosd/mangosd.conf.in (revision 3815) +++ src/mangosd/mangosd.conf.in (working copy) @@ -338,3 +338,122 @@ # Beep at mangosd start finished (mostly work only at Unix/Linux systems) # Default: 1 BeepAtStart = 1 + +##################### +# IRC Configuration # +##################### + +# IRCON - enable (1) or disable (0) the IRC extension. +IRCON = 1 + +# IRCCONNECTCODE - what code to wait for before starting to identify / join channels +# 001 == Welcome to Network msg +# 375 == Beginning of MOTD +# 376 == End of MOTD +IRCCONNECTCODE = "001" + +# IRCPort - irc server port to connect to +IRCPort = 6667 + +# IRCHost - irc server's host / ip +IRCHost = "irc.station51.net" + +# IRCSERVERPASS - the pass requierd to connect to the server +IRCSERVERPASS = "MyServerPass" + +# IRCNICK - the bots nick on irc +IRCNICK = "MaNGOServ" + +# IRCUSER - the irc username +IRCUSER = "MaNGOS" + +# IRCNICKPASS - the pass for identifying to nickserv or quakenet +IRCNICKPASS = "MyDumbPass" + +# IRCQUAKENET - 0 == identify to nickserv, 1 == identify to quakenet +IRCQUAKENET = 0 + +# IRCDEFCHAN - channel to leave if IRCLeaveDefChannel == 1, must be lowercase +IRCDEFCHAN = "lobby" +IRCLeaveDefChannel = 0 + +# IRCCHAN - the linked IRC channel, lowercase, no hash +IRCCHAN = "mangostest" + +# WOWCHAN - the linked ingame channel, lowercase +WOWCHAN = "mangostest" + +# IRCIgnorList - irc nicks to ignonre, use this if you dont want other channel bots to be echoed to the wow channel, seperate with spaces. +IRCIgnorList = "" + +# IRCSTARTMSG - Msg to display in the irc channel when the bot comes online if IRCSTARTMSGON == 1 +IRCSTARTMSG = "Server Online" +IRCSTARTMSGON = 1 + +# BotMask - This mask defines what the bot announces. if its 0 everything is disabled... simply add the values of the elements you want to create this mask. +# eg: irc join/leaves are 1 and wow join/leaves are 2, if you want both of these active and nothing else, the the mode is 3. +# display irc joins/leaves/nickchanges ingame: 1 +# display wow chan join/leaves in irc: 2 +# display wow chan join/leaves in wow: 4 +# display wow come online/gone offline ingame: 8 +# display wow come online/gone offline in irc: 16 +# Announce gm logons ingame and irc (when the gm is in gmmode): 32 +# display death/resurrects ingame: 64 +# display death/resurrects in irc: 128 +# display level ups ingame: 256 +# display level ups in irc: 512 +# display system msgs and notices in irc: 1024 +BotMask = 2047 + +# IRC_Cmds_Mask - this defines which commands will be active, IRC_Cmds_Prefix defines the command prefix. +# level 0 commands (.online, .info ....): 1 +# irc <-> wow pms: 2 +# gm commands: 4 +IRC_Cmds_Mask = 7 +IRC_Cmds_Prefix = "." + +# rejoin channel on kick +# 0 = disable +# 1 = enable +auto_rejoin_kick = 1 + +#################################################### +# IRC Text Variables: +# anything here with its name starting with WoW is displayed ingame, anything starting with IRC is displayed in the irc channel. +# $Name == Player Name or IRC Nick +# $Lev == Level +# $Msg == Message +# $Rank == GM Rank +WoW_joinedIRC = ">> $Name joined IRC." +WoW_leftIRC = "<< $Name left IRC." +WoW_changedIRCnick = "<> $Name is now known as $NewName." +IRC_joinedWoWchan = "12>>04 $Name Joined The Channel" +WoW_joinedWoWchan = ">> $Name Joined The Channel" +IRC_leftWoWchan = "12<<04 $Name Left The Channel" +WoW_leftWoWchan = "<< $Name Left The Channel" +IRC_comeonline = "12>>04 $Name has come Online" +WoW_comeonline = ">> $Name has come Online" +IRC_goneoffline = "12<<04 $Name has gone Offline" +WoW_goneoffline = "<< $Name has gone Offline" +IRC_died = "12<>04 $Name has joined the Spirits!" +WoW_died = "<> $Name has joined the Spirits!" +IRC_revive = "12<>04 $Name has rejoined the Living!" +WoW_revive = "<> $Name has rejoined the Living!" +IRC_levelup = "12<>04 $Name has gained Level $Lev!" +WoW_levelup = "<> $Name has gained Level $Lev!" +IRC_WoWmsg = "[$Name] $Msg" +WoW_IRCmsg = "[$Name]: $Msg" +WoW_IRCaction = "*$Name $Msg" +IRC_sysmsg = "04,08/!\System Message/!\ $Msg 04,08/!\System Message/!\" +IRC_sysnotice = "04,08/!\System Notice/!\ $Msg 04,08/!\System Notice/!\" +GMAnnounceMessage = "|cffff0000[$Rank $Name has come Online]|r" +GMAnnounceIRCMessage = "12>>04 $Rank $Name has come Online" +GMRankMod = "Mod" +GMRankGM = "GM" +GMRankAdmin = "Admin" +# IRC Text Variables End +#################################################### + +######################### +# IRC Configuration End # +######################### Index: src/mangosd/Master.cpp =================================================================== --- src/mangosd/Master.cpp (revision 3815) +++ src/mangosd/Master.cpp (working copy) @@ -34,6 +34,7 @@ #include "SystemConfig.h" #include "Config/ConfigEnv.h" #include "Database/DatabaseEnv.h" +#include "../game/IRCClient.h" #include "CliRunnable.h" #include "RASocket.h" #include "ScriptCalls.h" @@ -178,6 +179,9 @@ uint32 numLoops = (sConfig.GetIntDefault( "MaxPingTime", 30 ) * (MINUTE * 1000000 / socketSelecttime)); uint32 loopCounter = 0; + ZThread::Thread irc(new IRCClient); + irc.setPriority ((ZThread::Priority )2); + ///- Wait for termination signal while (!World::m_stopEvent) { Index: src/realmd/Makefile.am =================================================================== --- src/realmd/Makefile.am (revision 3815) +++ src/realmd/Makefile.am (working copy) @@ -30,7 +30,7 @@ RealmList.h ## Link realm list daemon against the shared library -mangos_realmd_LDADD = ../shared/libmangosdatabase.a ../shared/libmangosconfig.a ../shared/libmangosauth.a ../shared/libmangosshared.a ../shared/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la +mangos_realmd_LDADD = ../shared/libmangosdatabase.a ../shared/libmangosconfig.a ../shared/libmangosauth.a ../shared/libmangosshared.a ../shared/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zlib/libzlib.a ../../dep/src/zthread/libZThread.la mangos_realmd_LDFLAGS = -L../../dep/src/zthread $(MYSQL_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) ## Additional files to include when running 'make dist' Index: src/shared/Database/DBCfmt.cpp =================================================================== --- src/shared/Database/DBCfmt.cpp (revision 3815) +++ src/shared/Database/DBCfmt.cpp (working copy) @@ -16,11 +16,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -const char AreaTableEntryfmt[]="iiinixxxxxixxxxxxxxxixxxxxx"; +const char AreaTableEntryfmt[]="iiinixxxxxissssssssxixxxxxx"; const char BankBagSlotPricesEntryfmt[]="ni"; const char BattlemasterListEntryfmt[]="niixxxxxxiiiixxssssssssx"; const char ChatChannelsEntryfmt[]="iixssssssssxxxxxxxxxx"; // data not sorted (more compact store) -const char ChrClassesEntryfmt[]="nxxixxxxxxxxxxxxxx"; +const char ChrClassesEntryfmt[]="nxxixssssssssxxxxx"; const char ChrRacesEntryfmt[]="nxixiixxixxxxissssssssxxxxx"; const char CreatureFamilyfmt[]="nxxxxxiissssssssxx"; const char EmoteEntryfmt[]="nxixxxxxxxxxxxxxxxx"; Index: src/shared/Database/DBCStructure.h =================================================================== --- src/shared/Database/DBCStructure.h (revision 3815) +++ src/shared/Database/DBCStructure.h (working copy) @@ -41,7 +41,7 @@ uint32 flags; // 4, unknown value but 312 for all cities // 5-9 unused int32 area_level; //10 - //char* area_name[8]; //11-18, unused + char* area_name[8]; //11-18, unused //19, string flags, unused uint32 team; //20, }; @@ -84,7 +84,7 @@ // 1-2, unused uint32 powerType; // 3 // 4, unused - //char* name[8]; // 5-12, unused + char* name[8]; // 5-12, unused // 13 string flags, unused }; Index: win/mangosdVC71.sln =================================================================== --- win/mangosdVC71.sln (revision 3815) +++ win/mangosdVC71.sln (working copy) @@ -34,6 +34,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "realmd", "VC71\realmd.vcproj", "{563E9905-3657-460C-AE63-0AC39D162E23}" ProjectSection(ProjectDependencies) = postProject {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} = {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} = {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} {90297C34-F231-4DF4-848E-A74BCC0E40ED} = {90297C34-F231-4DF4-848E-A74BCC0E40ED} {1DC6C4DA-A028-41F3-877D-D5400C594F88} = {1DC6C4DA-A028-41F3-877D-D5400C594F88} {262199E8-EEDF-4700-A1D1-E9CC901CF480} = {262199E8-EEDF-4700-A1D1-E9CC901CF480} Index: win/mangosdVC80.sln =================================================================== --- win/mangosdVC80.sln (revision 3815) +++ win/mangosdVC80.sln (working copy) @@ -1,9 +1,9 @@ Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual C++ Express 2005 +# Visual Studio 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "game", "VC80\game.vcproj", "{1DC6C4DA-A028-41F3-877D-D5400C594F88}" ProjectSection(ProjectDependencies) = postProject + {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} = {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} {90297C34-F231-4DF4-848E-A74BCC0E40ED} = {90297C34-F231-4DF4-848E-A74BCC0E40ED} - {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} = {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shared", "VC80\shared.vcproj", "{90297C34-F231-4DF4-848E-A74BCC0E40ED}" @@ -13,11 +13,11 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mangosd", "VC80\mangosd.vcproj", "{A3A04E47-43A2-4C08-90B3-029CEF558594}" ProjectSection(ProjectDependencies) = postProject + {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} = {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} + {90297C34-F231-4DF4-848E-A74BCC0E40ED} = {90297C34-F231-4DF4-848E-A74BCC0E40ED} + {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} = {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} + {1DC6C4DA-A028-41F3-877D-D5400C594F88} = {1DC6C4DA-A028-41F3-877D-D5400C594F88} {262199E8-EEDF-4700-A1D1-E9CC901CF480} = {262199E8-EEDF-4700-A1D1-E9CC901CF480} - {1DC6C4DA-A028-41F3-877D-D5400C594F88} = {1DC6C4DA-A028-41F3-877D-D5400C594F88} - {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} = {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} - {90297C34-F231-4DF4-848E-A74BCC0E40ED} = {90297C34-F231-4DF4-848E-A74BCC0E40ED} - {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} = {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zthread", "VC80\zthread.vcproj", "{262199E8-EEDF-4700-A1D1-E9CC901CF480}" @@ -28,11 +28,12 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "realmd", "VC80\realmd.vcproj", "{563E9905-3657-460C-AE63-0AC39D162E23}" ProjectSection(ProjectDependencies) = postProject + {A3A04E47-43A2-4C08-90B3-029CEF558594} = {A3A04E47-43A2-4C08-90B3-029CEF558594} {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} = {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} + {90297C34-F231-4DF4-848E-A74BCC0E40ED} = {90297C34-F231-4DF4-848E-A74BCC0E40ED} + {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} = {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} + {1DC6C4DA-A028-41F3-877D-D5400C594F88} = {1DC6C4DA-A028-41F3-877D-D5400C594F88} {262199E8-EEDF-4700-A1D1-E9CC901CF480} = {262199E8-EEDF-4700-A1D1-E9CC901CF480} - {1DC6C4DA-A028-41F3-877D-D5400C594F88} = {1DC6C4DA-A028-41F3-877D-D5400C594F88} - {A3A04E47-43A2-4C08-90B3-029CEF558594} = {A3A04E47-43A2-4C08-90B3-029CEF558594} - {90297C34-F231-4DF4-848E-A74BCC0E40ED} = {90297C34-F231-4DF4-848E-A74BCC0E40ED} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "script", "VC80\script.vcproj", "{4205C8A9-79B7-4354-8064-F05FB9CA0C96}" Index: win/VC71/game.vcproj =================================================================== --- win/VC71/game.vcproj (revision 3815) +++ win/VC71/game.vcproj (working copy) @@ -709,6 +709,21 @@ + + + + + + + + + + Index: win/VC80/game.vcproj =================================================================== --- win/VC80/game.vcproj (revision 3815) +++ win/VC80/game.vcproj (working copy) @@ -968,6 +968,26 @@ > + + + + + + + + + + Index: win/VC80/mangosd.vcproj =================================================================== --- win/VC80/mangosd.vcproj (revision 3815) +++ win/VC80/mangosd.vcproj (working copy) @@ -1,7 +1,7 @@