First Commit

For Azeroth!
This commit is contained in:
Yehonal
2016-06-26 10:39:44 +02:00
commit e8e94a0a66
3777 changed files with 1419268 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,343 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CHANNEL_H
#define _CHANNEL_H
#include <list>
#include <map>
#include <string>
#include "Common.h"
#include "Opcodes.h"
#include "WorldPacket.h"
class Player;
#define CHANNEL_BAN_DURATION DAY*60
enum ChatNotify
{
CHAT_JOINED_NOTICE = 0x00, //+ "%s joined channel.";
CHAT_LEFT_NOTICE = 0x01, //+ "%s left channel.";
//CHAT_SUSPENDED_NOTICE = 0x01, // "%s left channel.";
CHAT_YOU_JOINED_NOTICE = 0x02, //+ "Joined Channel: [%s]"; -- You joined
//CHAT_YOU_CHANGED_NOTICE = 0x02, // "Changed Channel: [%s]";
CHAT_YOU_LEFT_NOTICE = 0x03, //+ "Left Channel: [%s]"; -- You left
CHAT_WRONG_PASSWORD_NOTICE = 0x04, //+ "Wrong password for %s.";
CHAT_NOT_MEMBER_NOTICE = 0x05, //+ "Not on channel %s.";
CHAT_NOT_MODERATOR_NOTICE = 0x06, //+ "Not a moderator of %s.";
CHAT_PASSWORD_CHANGED_NOTICE = 0x07, //+ "[%s] Password changed by %s.";
CHAT_OWNER_CHANGED_NOTICE = 0x08, //+ "[%s] Owner changed to %s.";
CHAT_PLAYER_NOT_FOUND_NOTICE = 0x09, //+ "[%s] Player %s was not found.";
CHAT_NOT_OWNER_NOTICE = 0x0A, //+ "[%s] You are not the channel owner.";
CHAT_CHANNEL_OWNER_NOTICE = 0x0B, //+ "[%s] Channel owner is %s.";
CHAT_MODE_CHANGE_NOTICE = 0x0C, //?
CHAT_ANNOUNCEMENTS_ON_NOTICE = 0x0D, //+ "[%s] Channel announcements enabled by %s.";
CHAT_ANNOUNCEMENTS_OFF_NOTICE = 0x0E, //+ "[%s] Channel announcements disabled by %s.";
// CHAT_MODERATION_ON_NOTICE = 0x0F, //+ "[%s] Channel moderation enabled by %s.";
// CHAT_MODERATION_OFF_NOTICE = 0x10, //+ "[%s] Channel moderation disabled by %s.";
CHAT_MUTED_NOTICE = 0x11, //+ "[%s] You do not have permission to speak.";
CHAT_PLAYER_KICKED_NOTICE = 0x12, //? "[%s] Player %s kicked by %s.";
CHAT_BANNED_NOTICE = 0x13, //+ "[%s] You are bannedStore from that channel.";
CHAT_PLAYER_BANNED_NOTICE = 0x14, //? "[%s] Player %s bannedStore by %s.";
CHAT_PLAYER_UNBANNED_NOTICE = 0x15, //? "[%s] Player %s unbanned by %s.";
CHAT_PLAYER_NOT_BANNED_NOTICE = 0x16, //+ "[%s] Player %s is not bannedStore.";
CHAT_PLAYER_ALREADY_MEMBER_NOTICE = 0x17, //+ "[%s] Player %s is already on the channel.";
CHAT_INVITE_NOTICE = 0x18, //+ "%2$s has invited you to join the channel '%1$s'.";
CHAT_INVITE_WRONG_FACTION_NOTICE = 0x19, //+ "Target is in the wrong alliance for %s.";
CHAT_WRONG_FACTION_NOTICE = 0x1A, //+ "Wrong alliance for %s.";
CHAT_INVALID_NAME_NOTICE = 0x1B, //+ "Invalid channel name";
CHAT_NOT_MODERATED_NOTICE = 0x1C, //+ "%s is not moderated";
CHAT_PLAYER_INVITED_NOTICE = 0x1D, //+ "[%s] You invited %s to join the channel";
CHAT_PLAYER_INVITE_BANNED_NOTICE = 0x1E, //+ "[%s] %s has been bannedStore.";
CHAT_THROTTLED_NOTICE = 0x1F, //+ "[%s] The number of messages that can be sent to this channel is limited, please wait to send another message.";
CHAT_NOT_IN_AREA_NOTICE = 0x20, //+ "[%s] You are not in the correct area for this channel."; -- The user is trying to send a chat to a zone specific channel, and they're not physically in that zone.
CHAT_NOT_IN_LFG_NOTICE = 0x21, //+ "[%s] You must be queued in looking for group before joining this channel."; -- The user must be in the looking for group system to join LFG chat channels.
CHAT_VOICE_ON_NOTICE = 0x22, //+ "[%s] Channel voice enabled by %s.";
CHAT_VOICE_OFF_NOTICE = 0x23 //+ "[%s] Channel voice disabled by %s.";
};
enum ChannelFlags
{
CHANNEL_FLAG_NONE = 0x00,
CHANNEL_FLAG_CUSTOM = 0x01,
// 0x02
CHANNEL_FLAG_TRADE = 0x04,
CHANNEL_FLAG_NOT_LFG = 0x08,
CHANNEL_FLAG_GENERAL = 0x10,
CHANNEL_FLAG_CITY = 0x20,
CHANNEL_FLAG_LFG = 0x40,
CHANNEL_FLAG_VOICE = 0x80
// General 0x18 = 0x10 | 0x08
// Trade 0x3C = 0x20 | 0x10 | 0x08 | 0x04
// LocalDefence 0x18 = 0x10 | 0x08
// GuildRecruitment 0x38 = 0x20 | 0x10 | 0x08
// LookingForGroup 0x50 = 0x40 | 0x10
};
enum ChannelDBCFlags
{
CHANNEL_DBC_FLAG_NONE = 0x00000,
CHANNEL_DBC_FLAG_INITIAL = 0x00001, // General, Trade, LocalDefense, LFG
CHANNEL_DBC_FLAG_ZONE_DEP = 0x00002, // General, Trade, LocalDefense, GuildRecruitment
CHANNEL_DBC_FLAG_GLOBAL = 0x00004, // WorldDefense
CHANNEL_DBC_FLAG_TRADE = 0x00008, // Trade, LFG
CHANNEL_DBC_FLAG_CITY_ONLY = 0x00010, // Trade, GuildRecruitment, LFG
CHANNEL_DBC_FLAG_CITY_ONLY2 = 0x00020, // Trade, GuildRecruitment, LFG
CHANNEL_DBC_FLAG_DEFENSE = 0x10000, // LocalDefense, WorldDefense
CHANNEL_DBC_FLAG_GUILD_REQ = 0x20000, // GuildRecruitment
CHANNEL_DBC_FLAG_LFG = 0x40000, // LFG
CHANNEL_DBC_FLAG_UNK1 = 0x80000 // General
};
enum ChannelMemberFlags
{
MEMBER_FLAG_NONE = 0x00,
MEMBER_FLAG_OWNER = 0x01,
MEMBER_FLAG_MODERATOR = 0x02,
MEMBER_FLAG_VOICED = 0x04,
MEMBER_FLAG_MUTED = 0x08,
MEMBER_FLAG_CUSTOM = 0x10,
MEMBER_FLAG_MIC_MUTED = 0x20
// 0x40
// 0x80
};
class ChannelRights
{
public:
ChannelRights() : flags(0), speakDelay(0) {}
ChannelRights(const uint32& f, const uint32& d, const std::string& jm, const std::string& sm, const std::set<uint32>& ml) : flags(f), speakDelay(d), joinMessage(jm), speakMessage(sm), moderators(ml) {}
uint32 flags;
uint32 speakDelay;
std::string joinMessage;
std::string speakMessage;
std::set<uint32> moderators;
};
enum eChannelRights
{
CHANNEL_RIGHT_FORCE_NO_ANNOUNCEMENTS = 0x001,
CHANNEL_RIGHT_FORCE_ANNOUNCEMENTS = 0x002,
CHANNEL_RIGHT_NO_OWNERSHIP = 0x004,
CHANNEL_RIGHT_CANT_SPEAK = 0x008,
CHANNEL_RIGHT_CANT_BAN = 0x010,
CHANNEL_RIGHT_CANT_KICK = 0x020,
CHANNEL_RIGHT_CANT_MUTE = 0x040,
CHANNEL_RIGHT_CANT_CHANGE_PASSWORD = 0x080,
CHANNEL_RIGHT_DONT_PRESERVE = 0x100,
};
class Channel
{
struct PlayerInfo
{
uint64 player;
uint8 flags;
uint32 lastSpeakTime; // pussywizard
Player* plrPtr; // pussywizard
bool HasFlag(uint8 flag) const { return flags & flag; }
void SetFlag(uint8 flag) { if (!HasFlag(flag)) flags |= flag; }
bool IsOwner() const { return flags & MEMBER_FLAG_OWNER; }
void SetOwner(bool state)
{
if (state) flags |= MEMBER_FLAG_OWNER;
else flags &= ~MEMBER_FLAG_OWNER;
}
bool IsModerator() const { return flags & MEMBER_FLAG_MODERATOR; }
void SetModerator(bool state)
{
if (state) flags |= MEMBER_FLAG_MODERATOR;
else flags &= ~MEMBER_FLAG_MODERATOR;
}
bool IsMuted() const { return flags & MEMBER_FLAG_MUTED; }
void SetMuted(bool state)
{
if (state) flags |= MEMBER_FLAG_MUTED;
else flags &= ~MEMBER_FLAG_MUTED;
}
bool IsAllowedToSpeak(uint32 speakDelay) // pussywizard
{
if (lastSpeakTime+speakDelay <= sWorld->GetGameTime())
{
lastSpeakTime = sWorld->GetGameTime();
return true;
}
else
return false;
}
};
public:
Channel(std::string const& name, uint32 channel_id, uint32 channelDBId, TeamId teamId = TEAM_NEUTRAL, bool announce = true);
std::string const& GetName() const { return _name; }
uint32 GetChannelId() const { return _channelId; }
bool IsConstant() const { return _channelId != 0; }
bool IsAnnounce() const { return _announce; }
bool IsLFG() const { return GetFlags() & CHANNEL_FLAG_LFG; }
std::string const& GetPassword() const { return _password; }
void SetPassword(std::string const& npassword) { _password = npassword; }
uint32 GetNumPlayers() const { return playersStore.size(); }
uint8 GetFlags() const { return _flags; }
bool HasFlag(uint8 flag) const { return _flags & flag; }
void JoinChannel(Player* player, std::string const& pass);
void LeaveChannel(Player* player, bool send = true);
void KickOrBan(Player const* player, std::string const& badname, bool ban);
void Kick(Player const* player, std::string const& badname) { KickOrBan(player, badname, false); }
void Ban(Player const* player, std::string const& badname) { KickOrBan(player, badname, true); }
void AddBan(uint32 guid, uint32 time) { bannedStore[guid] = time; }
void UnBan(Player const* player, std::string const& badname);
void UnBan(uint64 guid);
void Password(Player const* player, std::string const& pass);
void SetMode(Player const* player, std::string const& p2n, bool mod, bool set);
void SetOwner(uint64 guid, bool exclaim = true);
void SetOwner(Player const* player, std::string const& name);
void SendWhoOwner(uint64 guid);
void SetModerator(Player const* player, std::string const& newname) { SetMode(player, newname, true, true); }
void UnsetModerator(Player const* player, std::string const& newname) { SetMode(player, newname, true, false); }
void SetMute(Player const* player, std::string const& newname) { SetMode(player, newname, false, true); }
void UnsetMute(Player const* player, std::string const& newname) { SetMode(player, newname, false, false); }
void List(Player const* player);
void Announce(Player const* player);
void Say(uint64 guid, std::string const& what, uint32 lang);
void EveryoneSayToSelf(const char *what);
void Invite(Player const* player, std::string const& newp);
void Voice(uint64 guid1, uint64 guid2);
void DeVoice(uint64 guid1, uint64 guid2);
void JoinNotify(Player* p);
void LeaveNotify(Player* p);
void FlagsNotify(Player* p);
static void CleanOldChannelsInDB();
// pussywizard:
void AddWatching(Player* p);
void RemoveWatching(Player* p);
private:
// initial packet data (notify type and channel name)
void MakeNotifyPacket(WorldPacket* data, uint8 notify_type);
// type specific packet data
void MakeJoined(WorldPacket* data, uint64 guid); //+ 0x00
void MakeLeft(WorldPacket* data, uint64 guid); //+ 0x01
void MakeYouJoined(WorldPacket* data); //+ 0x02
void MakeYouLeft(WorldPacket* data); //+ 0x03
void MakeWrongPassword(WorldPacket* data); //? 0x04
void MakeNotMember(WorldPacket* data); //? 0x05
void MakeNotModerator(WorldPacket* data); //? 0x06
void MakePasswordChanged(WorldPacket* data, uint64 guid); //+ 0x07
void MakeOwnerChanged(WorldPacket* data, uint64 guid); //? 0x08
void MakePlayerNotFound(WorldPacket* data, std::string const& name); //+ 0x09
void MakeNotOwner(WorldPacket* data); //? 0x0A
void MakeChannelOwner(WorldPacket* data); //? 0x0B
void MakeModeChange(WorldPacket* data, uint64 guid, uint8 oldflags); //+ 0x0C
void MakeAnnouncementsOn(WorldPacket* data, uint64 guid); //+ 0x0D
void MakeAnnouncementsOff(WorldPacket* data, uint64 guid); //+ 0x0E
void MakeMuted(WorldPacket* data); //? 0x11
void MakePlayerKicked(WorldPacket* data, uint64 bad, uint64 good); //? 0x12
void MakeBanned(WorldPacket* data); //? 0x13
void MakePlayerBanned(WorldPacket* data, uint64 bad, uint64 good); //? 0x14
void MakePlayerUnbanned(WorldPacket* data, uint64 bad, uint64 good); //? 0x15
void MakePlayerNotBanned(WorldPacket* data, std::string const& name); //? 0x16
void MakePlayerAlreadyMember(WorldPacket* data, uint64 guid); //+ 0x17
void MakeInvite(WorldPacket* data, uint64 guid); //? 0x18
void MakeInviteWrongFaction(WorldPacket* data); //? 0x19
void MakeWrongFaction(WorldPacket* data); //? 0x1A
void MakeInvalidName(WorldPacket* data); //? 0x1B
void MakeNotModerated(WorldPacket* data); //? 0x1C
void MakePlayerInvited(WorldPacket* data, std::string const& name); //+ 0x1D
void MakePlayerInviteBanned(WorldPacket* data, std::string const& name);//? 0x1E
void MakeThrottled(WorldPacket* data); //? 0x1F
void MakeNotInArea(WorldPacket* data); //? 0x20
void MakeNotInLfg(WorldPacket* data); //? 0x21
void MakeVoiceOn(WorldPacket* data, uint64 guid); //+ 0x22
void MakeVoiceOff(WorldPacket* data, uint64 guid); //+ 0x23
void SendToAll(WorldPacket* data, uint64 guid = 0);
void SendToAllButOne(WorldPacket* data, uint64 who);
void SendToOne(WorldPacket* data, uint64 who);
void SendToAllWatching(WorldPacket* data);
bool IsOn(uint64 who) const { return playersStore.find(who) != playersStore.end(); }
bool IsBanned(uint64 guid) const;
void UpdateChannelInDB() const;
void UpdateChannelUseageInDB() const;
void AddChannelBanToDB(uint32 guid, uint32 time) const;
void RemoveChannelBanFromDB(uint32 guid) const;
uint8 GetPlayerFlags(uint64 guid) const
{
PlayerContainer::const_iterator itr = playersStore.find(guid);
return itr != playersStore.end() ? itr->second.flags : 0;
}
void SetModerator(uint64 guid, bool set)
{
PlayerInfo& pinfo = playersStore[guid];
if (pinfo.IsModerator() != set)
{
uint8 oldFlag = pinfo.flags;
pinfo.SetModerator(set);
WorldPacket data;
MakeModeChange(&data, guid, oldFlag);
SendToAll(&data);
FlagsNotify(pinfo.plrPtr);
}
}
void SetMute(uint64 guid, bool set)
{
PlayerInfo& pinfo = playersStore[guid];
if (pinfo.IsMuted() != set)
{
uint8 oldFlag = pinfo.flags;
pinfo.SetMuted(set);
WorldPacket data;
MakeModeChange(&data, guid, oldFlag);
SendToAll(&data);
}
}
typedef UNORDERED_MAP<uint64, PlayerInfo> PlayerContainer;
typedef UNORDERED_MAP<uint32, uint32> BannedContainer;
typedef UNORDERED_SET<Player*> PlayersWatchingContainer;
bool _announce;
bool _ownership;
bool _IsSaved;
uint8 _flags;
uint32 _channelId;
uint32 _channelDBId;
TeamId _teamId;
uint64 _ownerGUID;
std::string _name;
std::string _password;
ChannelRights _channelRights;
PlayerContainer playersStore;
BannedContainer bannedStore;
PlayersWatchingContainer playersWatchingStore;
};
#endif

View File

@@ -0,0 +1,201 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "ChannelMgr.h"
#include "Player.h"
#include "World.h"
ChannelMgr::~ChannelMgr()
{
for (ChannelMap::iterator itr = channels.begin(); itr != channels.end(); ++itr)
delete itr->second;
channels.clear();
}
ChannelMgr* ChannelMgr::forTeam(TeamId teamId)
{
if (teamId == TEAM_ALLIANCE)
return ACE_Singleton<AllianceChannelMgr, ACE_Null_Mutex>::instance();
if (teamId == TEAM_HORDE)
return ACE_Singleton<HordeChannelMgr, ACE_Null_Mutex>::instance();
return NULL;
}
void ChannelMgr::LoadChannels()
{
uint32 oldMSTime = getMSTime();
uint32 count = 0;
QueryResult result = CharacterDatabase.PQuery("SELECT channelId, name, team, announce, password FROM channels WHERE team = %u ORDER BY channelId ASC", _teamId);
if (!result)
{
sLog->outString(">> Loaded 0 channels for %s", _teamId == TEAM_ALLIANCE ? "Alliance" : "Horde");
sLog->outString();
return;
}
do
{
Field* fields = result->Fetch();
if (!fields)
break;
uint32 channelDBId = fields[0].GetUInt32();
std::string channelName = fields[1].GetString();
std::string password = fields[4].GetString();
std::wstring channelWName;
Utf8toWStr(channelName, channelWName);
Channel* newChannel = new Channel(channelName, 0, channelDBId, TeamId(fields[2].GetUInt32()), fields[3].GetUInt8());
newChannel->SetPassword(password);
channels[channelWName] = newChannel;
if (QueryResult banResult = CharacterDatabase.PQuery("SELECT playerGUID, banTime FROM channels_bans WHERE channelId = %u", channelDBId))
{
do
{
Field* banFields = banResult->Fetch();
if (!banFields)
break;
newChannel->AddBan(banFields[0].GetUInt32(), banFields[1].GetUInt32());
}
while (banResult->NextRow());
}
if (channelDBId > ChannelMgr::_channelIdMax)
ChannelMgr::_channelIdMax = channelDBId;
++count;
}
while (result->NextRow());
sLog->outString(">> Loaded %u channels for %s in %ums", count, _teamId == TEAM_ALLIANCE ? "Alliance" : "Horde", GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
Channel* ChannelMgr::GetJoinChannel(std::string const& name, uint32 channelId)
{
std::wstring wname;
Utf8toWStr(name, wname);
wstrToLower(wname);
ChannelMap::const_iterator i = channels.find(wname);
if (i == channels.end())
{
std::string chNameLower = name;
std::transform(chNameLower.begin(), chNameLower.end(), chNameLower.begin(), ::tolower);
Channel* nchan = new Channel(chNameLower, channelId, 0, _teamId);
channels[wname] = nchan;
return nchan;
}
return i->second;
}
Channel* ChannelMgr::GetChannel(std::string const& name, Player* player, bool pkt)
{
std::wstring wname;
Utf8toWStr(name, wname);
wstrToLower(wname);
ChannelMap::const_iterator i = channels.find(wname);
if (i == channels.end())
{
if (pkt)
{
WorldPacket data;
MakeNotOnPacket(&data, name);
player->GetSession()->SendPacket(&data);
}
return NULL;
}
return i->second;
}
uint32 ChannelMgr::_channelIdMax = 0;
ChannelMgr::ChannelRightsMap ChannelMgr::channels_rights;
ChannelRights ChannelMgr::channelRightsEmpty;
void ChannelMgr::LoadChannelRights()
{
uint32 oldMSTime = getMSTime();
channels_rights.clear();
QueryResult result = CharacterDatabase.Query("SELECT name, flags, speakdelay, joinmessage, delaymessage, moderators FROM channels_rights");
if (!result)
{
sLog->outString();
sLog->outString(">> Loaded 0 Channel Rights!");
return;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
std::set<uint32> moderators;
const char* moderatorList = fields[5].GetCString();
if (moderatorList)
{
Tokenizer tokens(moderatorList, ' ');
for (Tokenizer::const_iterator i = tokens.begin(); i != tokens.end(); ++i)
{
uint64 moderator_acc = atol(*i);
if (moderator_acc && ((uint32)moderator_acc) == moderator_acc)
moderators.insert((uint32)moderator_acc);
}
}
SetChannelRightsFor(fields[0].GetString(), fields[1].GetUInt32(), fields[2].GetUInt32(), fields[3].GetString(), fields[4].GetString(), moderators);
++count;
} while (result->NextRow());
sLog->outString(">> Loaded %d Channel Rights in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
const ChannelRights& ChannelMgr::GetChannelRightsFor(const std::string& name)
{
std::string nameStr = name;
std::transform(nameStr.begin(), nameStr.end(), nameStr.begin(), ::tolower);
ChannelRightsMap::const_iterator itr = channels_rights.find(nameStr);
if (itr != channels_rights.end())
return itr->second;
return channelRightsEmpty;
}
void ChannelMgr::SetChannelRightsFor(const std::string& name, const uint32& flags, const uint32& speakDelay, const std::string& joinmessage, const std::string& speakmessage, const std::set<uint32>& moderators)
{
std::string nameStr = name;
std::transform(nameStr.begin(), nameStr.end(), nameStr.begin(), ::tolower);
channels_rights[nameStr] = ChannelRights(flags, speakDelay, joinmessage, speakmessage, moderators);
}
void ChannelMgr::MakeNotOnPacket(WorldPacket* data, std::string const& name)
{
data->Initialize(SMSG_CHANNEL_NOTIFY, 1 + name.size());
(*data) << uint8(5) << name;

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TRINITY_CHANNELMGR_H
#define __TRINITY_CHANNELMGR_H
#include "Common.h"
#include "Channel.h"
#include <ace/Singleton.h>
#include <map>
#include <string>
#include "World.h"
#define MAX_CHANNEL_PASS_STR 31
class ChannelMgr
{
typedef UNORDERED_MAP<std::wstring, Channel*> ChannelMap;
typedef std::map<std::string, ChannelRights> ChannelRightsMap;
public:
ChannelMgr(TeamId teamId) : _teamId(teamId)
{ }
~ChannelMgr();
static ChannelMgr * forTeam(TeamId teamId);
Channel* GetJoinChannel(std::string const& name, uint32 channel_id);
Channel* GetChannel(std::string const& name, Player* p, bool pkt = true);
void LoadChannels();
static void LoadChannelRights();
static const ChannelRights& GetChannelRightsFor(const std::string& name);
static void SetChannelRightsFor(const std::string& name, const uint32& flags, const uint32& speakDelay, const std::string& joinmessage, const std::string& speakmessage, const std::set<uint32>& moderators);
static uint32 _channelIdMax;
private:
ChannelMap channels;
TeamId _teamId;
static ChannelRightsMap channels_rights;
static ChannelRights channelRightsEmpty; // when not found in the map, reference to this is returned
void MakeNotOnPacket(WorldPacket* data, std::string const& name);
};
class AllianceChannelMgr : public ChannelMgr { public: AllianceChannelMgr() : ChannelMgr(TEAM_ALLIANCE) {} };
class HordeChannelMgr : public ChannelMgr { public: HordeChannelMgr() : ChannelMgr(TEAM_HORDE) {} };
#endif

File diff suppressed because it is too large Load Diff

162
src/server/game/Chat/Chat.h Normal file
View File

@@ -0,0 +1,162 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef SUNWELLCORE_CHAT_H
#define SUNWELLCORE_CHAT_H
#include "SharedDefines.h"
#include "WorldSession.h"
#include <vector>
class ChatHandler;
class Creature;
class Group;
class Player;
class Unit;
class WorldSession;
class WorldObject;
struct GameTele;
class ChatCommand
{
public:
const char * Name;
uint32 SecurityLevel; // function pointer required correct align (use uint32)
bool AllowConsole;
bool (*Handler)(ChatHandler*, const char* args);
std::string Help;
ChatCommand* ChildCommands;
};
class ChatHandler
{
public:
WorldSession* GetSession() { return m_session; }
explicit ChatHandler(WorldSession* session) : m_session(session), sentErrorMessage(false) {}
virtual ~ChatHandler() { }
// Builds chat packet and returns receiver guid position in the packet to substitute in whisper builders
static size_t BuildChatPacket(WorldPacket& data, ChatMsg chatType, Language language, uint64 senderGUID, uint64 receiverGUID, std::string const& message, uint8 chatTag,
std::string const& senderName = "", std::string const& receiverName = "",
uint32 achievementId = 0, bool gmMessage = false, std::string const& channelName = "");
// Builds chat packet and returns receiver guid position in the packet to substitute in whisper builders
static size_t BuildChatPacket(WorldPacket& data, ChatMsg chatType, Language language, WorldObject const* sender, WorldObject const* receiver, std::string const& message, uint32 achievementId = 0, std::string const& channelName = "", LocaleConstant locale = DEFAULT_LOCALE);
static char* LineFromMessage(char*& pos) { char* start = strtok(pos, "\n"); pos = NULL; return start; }
// function with different implementation for chat/console
virtual const char *GetTrinityString(int32 entry) const;
virtual void SendSysMessage(const char *str);
void SendSysMessage(int32 entry);
void PSendSysMessage(const char *format, ...) ATTR_PRINTF(2, 3);
void PSendSysMessage(int32 entry, ...);
std::string PGetParseString(int32 entry, ...) const;
bool ParseCommands(const char* text);
static ChatCommand* getCommandTable();
bool isValidChatMessage(const char* msg);
void SendGlobalSysMessage(const char *str);
bool hasStringAbbr(const char* name, const char* part);
// function with different implementation for chat/console
virtual bool isAvailable(ChatCommand const& cmd) const;
virtual std::string GetNameLink() const { return GetNameLink(m_session->GetPlayer()); }
virtual bool needReportToTarget(Player* chr) const;
virtual LocaleConstant GetSessionDbcLocale() const;
virtual int GetSessionDbLocaleIndex() const;
bool HasLowerSecurity(Player* target, uint64 guid, bool strong = false);
bool HasLowerSecurityAccount(WorldSession* target, uint32 account, bool strong = false);
void SendGlobalGMSysMessage(const char *str);
Player* getSelectedPlayer();
Creature* getSelectedCreature();
Unit* getSelectedUnit();
WorldObject* getSelectedObject();
// Returns either the selected player or self if there is no selected player
Player* getSelectedPlayerOrSelf();
char* extractKeyFromLink(char* text, char const* linkType, char** something1 = NULL);
char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = NULL);
// if args have single value then it return in arg2 and arg1 == NULL
void extractOptFirstArg(char* args, char** arg1, char** arg2);
char* extractQuotedArg(char* args);
uint32 extractSpellIdFromLink(char* text);
uint64 extractGuidFromLink(char* text);
GameTele const* extractGameTeleFromLink(char* text);
bool GetPlayerGroupAndGUIDByName(const char* cname, Player* &player, Group* &group, uint64 &guid, bool offline = false);
std::string extractPlayerNameFromLink(char* text);
// select by arg (name/link) or in-game selection online/offline player
bool extractPlayerTarget(char* args, Player** player, uint64* player_guid = NULL, std::string* player_name = NULL);
std::string playerLink(std::string const& name) const { return m_session ? "|cffffffff|Hplayer:"+name+"|h["+name+"]|h|r" : name; }
std::string GetNameLink(Player* chr) const;
GameObject* GetNearbyGameObject();
GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid, uint32 entry);
bool HasSentErrorMessage() const { return sentErrorMessage; }
void SetSentErrorMessage(bool val){ sentErrorMessage = val; }
static bool LoadCommandTable() { return load_command_table; }
static void SetLoadCommandTable(bool val) { load_command_table = val; }
bool ShowHelpForCommand(ChatCommand* table, const char* cmd);
protected:
explicit ChatHandler() : m_session(NULL), sentErrorMessage(false) {} // for CLI subclass
static bool SetDataForCommandInTable(ChatCommand* table, const char* text, uint32 security, std::string const& help, std::string const& fullcommand);
bool ExecuteCommandInTable(ChatCommand* table, const char* text, std::string& fullcmd);
bool ShowHelpForSubCommands(ChatCommand* table, char const* cmd, char const* subcmd);
private:
WorldSession* m_session; // != NULL for chat command call and NULL for CLI command
// common global flag
static bool load_command_table;
bool sentErrorMessage;
};
class CliHandler : public ChatHandler
{
public:
typedef void Print(void*, char const*);
explicit CliHandler(void* callbackArg, Print* zprint) : m_callbackArg(callbackArg), m_print(zprint) {}
// overwrite functions
const char *GetTrinityString(int32 entry) const;
bool isAvailable(ChatCommand const& cmd) const;
void SendSysMessage(const char *str);
std::string GetNameLink() const;
bool needReportToTarget(Player* chr) const;
LocaleConstant GetSessionDbcLocale() const;
int GetSessionDbLocaleIndex() const;
private:
void* m_callbackArg;
Print* m_print;
};
#endif

View File

@@ -0,0 +1,692 @@
/*
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "ChatLink.h"
#include "SpellMgr.h"
#include "ObjectMgr.h"
#include "SpellInfo.h"
#include "DBCStores.h"
// Supported shift-links (client generated and server side)
// |color|Hachievement:achievement_id:player_guid:0:0:0:0:0:0:0:0|h[name]|h|r
// - client, item icon shift click, not used in server currently
// |color|Harea:area_id|h[name]|h|r
// |color|Hcreature:creature_guid|h[name]|h|r
// |color|Hcreature_entry:creature_id|h[name]|h|r
// |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r - client, at shift click in recipes list dialog
// |color|Hgameevent:id|h[name]|h|r
// |color|Hgameobject:go_guid|h[name]|h|r
// |color|Hgameobject_entry:go_id|h[name]|h|r
// |color|Hglyph:glyph_slot_id:glyph_prop_id|h[%s]|h|r - client, at shift click in glyphs dialog, GlyphSlot.dbc, GlyphProperties.dbc
// |color|Hitem:item_id:perm_ench_id:gem1:gem2:gem3:0:0:0:0:reporter_level|h[name]|h|r
// - client, item icon shift click
// |color|Hitemset:itemset_id|h[name]|h|r
// |color|Hplayer:name|h[name]|h|r - client, in some messages, at click copy only name instead link
// |color|Hquest:quest_id:quest_level|h[name]|h|r - client, quest list name shift-click
// |color|Hskill:skill_id|h[name]|h|r
// |color|Hspell:spell_id|h[name]|h|r - client, spellbook spell icon shift-click
// |color|Htalent:talent_id, rank|h[name]|h|r - client, talent icon shift-click
// |color|Htaxinode:id|h[name]|h|r
// |color|Htele:id|h[name]|h|r
// |color|Htitle:id|h[name]|h|r
// |color|Htrade:spell_id:cur_value:max_value:unk3int:unk3str|h[name]|h|r - client, spellbook profession icon shift-click
inline bool ReadUInt32(std::istringstream& iss, uint32& res)
{
iss >> std::dec >> res;
return !iss.fail() && !iss.eof();
}
inline bool ReadInt32(std::istringstream& iss, int32& res)
{
iss >> std::dec >> res;
return !iss.fail() && !iss.eof();
}
inline std::string ReadSkip(std::istringstream& iss, char term)
{
std::string res;
char c = iss.peek();
while (c != term && c != '\0')
{
res += c;
iss.ignore(1);
c = iss.peek();
}
return res;
}
inline bool CheckDelimiter(std::istringstream& iss, char delimiter, const char* context)
{
char c = iss.peek();
if (c != delimiter)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): invalid %s link structure ('%c' expected, '%c' found)", iss.str().c_str(), context, delimiter, c);
return false;
}
iss.ignore(1);
return true;
}
inline bool ReadHex(std::istringstream& iss, uint32& res, uint32 length)
{
std::istringstream::pos_type pos = iss.tellg();
iss >> std::hex >> res;
//uint32 size = uint32(iss.gcount());
if (length && uint32(iss.tellg() - pos) != length)
return false;
return !iss.fail() && !iss.eof();
}
#define DELIMITER ':'
#define PIPE_CHAR '|'
bool ChatLink::ValidateName(char* buffer, const char* /*context*/)
{
_name = buffer;
return true;
}
// |color|Hitem:item_id:perm_ench_id:gem1:gem2:gem3:0:random_property:0:reporter_level|h[name]|h|r
// |cffa335ee|Hitem:812:0:0:0:0:0:0:0:70|h[Glowing Brightwood Staff]|h|r
bool ItemChatLink::Initialize(std::istringstream& iss)
{
// Read item entry
uint32 itemEntry = 0;
if (!ReadUInt32(iss, itemEntry))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading item entry", iss.str().c_str());
return false;
}
// Validate item
_item = sObjectMgr->GetItemTemplate(itemEntry);
if (!_item)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid itemEntry %u in |item command", iss.str().c_str(), itemEntry);
return false;
}
// Validate item's color
if (_color != ItemQualityColors[_item->Quality])
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): linked item has color %u, but user claims %u", iss.str().c_str(), ItemQualityColors[_item->Quality], _color);
return false;
}
// Number of various item properties after item entry
const uint8 propsCount = 8;
const uint8 randomPropertyPosition = 5;
for (uint8 index = 0; index < propsCount; ++index)
{
if (!CheckDelimiter(iss, DELIMITER, "item"))
return false;
int32 id = 0;
if (!ReadInt32(iss, id))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading item property (%u)", iss.str().c_str(), index);
return false;
}
if (id && (index == randomPropertyPosition))
{
// Validate random property
if (id > 0)
{
_property = sItemRandomPropertiesStore.LookupEntry(id);
if (!_property)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid item property id %u in |item command", iss.str().c_str(), id);
return false;
}
}
else if (id < 0)
{
_suffix = sItemRandomSuffixStore.LookupEntry(-id);
if (!_suffix)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid item suffix id %u in |item command", iss.str().c_str(), -id);
return false;
}
}
}
_data[index] = id;
}
return true;
}
inline std::string ItemChatLink::FormatName(uint8 index, ItemLocale const* locale, char* const* suffixStrings) const
{
std::stringstream ss;
if (locale == NULL || index >= locale->Name.size())
ss << _item->Name1;
else
ss << locale->Name[index];
if (suffixStrings)
ss << ' ' << suffixStrings[index];
return ss.str();
}
bool ItemChatLink::ValidateName(char* buffer, const char* context)
{
ChatLink::ValidateName(buffer, context);
char* const* suffixStrings = _suffix ? _suffix->nameSuffix : (_property ? _property->nameSuffix : NULL);
bool res = (FormatName(LOCALE_enUS, NULL, suffixStrings) == buffer);
//if (!res)
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): linked item (id: %u) name wasn't found in any localization", context, _item->ItemId);
return res;
}
// |color|Hquest:quest_id:quest_level|h[name]|h|r
// |cff808080|Hquest:2278:47|h[The Platinum Discs]|h|r
bool QuestChatLink::Initialize(std::istringstream& iss)
{
// Read quest id
uint32 questId = 0;
if (!ReadUInt32(iss, questId))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading quest entry", iss.str().c_str());
return false;
}
// Validate quest
_quest = sObjectMgr->GetQuestTemplate(questId);
if (!_quest)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): quest template %u not found", iss.str().c_str(), questId);
return false;
}
// Check delimiter
if (!CheckDelimiter(iss, DELIMITER, "quest"))
return false;
// Read quest level
if (!ReadInt32(iss, _questLevel))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading quest level", iss.str().c_str());
return false;
}
// Validate quest level
if (_questLevel >= STRONG_MAX_LEVEL)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): quest level %d is too big", iss.str().c_str(), _questLevel);
return false;
}
return true;
}
bool QuestChatLink::ValidateName(char* buffer, const char* context)
{
ChatLink::ValidateName(buffer, context);
bool res = (_quest->GetTitle() == buffer);
//if (!res)
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): linked quest (id: %u) title wasn't found in any localization", context, _quest->GetQuestId());
return res;
}
// |color|Hspell:spell_id|h[name]|h|r
// |cff71d5ff|Hspell:21563|h[Command]|h|r
bool SpellChatLink::Initialize(std::istringstream& iss)
{
if (_color != CHAT_LINK_COLOR_SPELL)
return false;
// Read spell id
uint32 spellId = 0;
if (!ReadUInt32(iss, spellId))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading spell entry", iss.str().c_str());
return false;
}
// Validate spell
_spell = sSpellMgr->GetSpellInfo(spellId);
if (!_spell)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |spell command", iss.str().c_str(), spellId);
return false;
}
return true;
}
bool SpellChatLink::ValidateName(char* buffer, const char* context)
{
ChatLink::ValidateName(buffer, context);
// spells with that flag have a prefix of "$PROFESSION: "
if (_spell->HasAttribute(SPELL_ATTR0_TRADESPELL))
{
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(_spell->Id);
if (bounds.first == bounds.second)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): skill line not found for spell %u", context, _spell->Id);
return false;
}
SkillLineAbilityEntry const* skillInfo = bounds.first->second;
if (!skillInfo)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): skill line ability not found for spell %u", context, _spell->Id);
return false;
}
SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skillInfo->skillId);
if (!skillLine)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): skill line not found for skill %u", context, skillInfo->skillId);
return false;
}
for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
{
uint32 skillLineNameLength = strlen(skillLine->name[i]);
if (skillLineNameLength > 0 && strncmp(skillLine->name[i], buffer, skillLineNameLength) == 0)
{
// found the prefix, remove it to perform spellname validation below
// -2 = strlen(": ")
uint32 spellNameLength = strlen(buffer) - skillLineNameLength - 2;
memmove(buffer, buffer + skillLineNameLength + 2, spellNameLength + 1);
break;
}
}
}
bool res = false;
for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
if (*_spell->SpellName[i] && strcmp(_spell->SpellName[i], buffer) == 0)
{
res = true;
break;
}
//if (!res)
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): linked spell (id: %u) name wasn't found in any localization", context, _spell->Id);
return res;
}
// |color|Hachievement:achievement_id:player_guid:0:0:0:0:0:0:0:0|h[name]|h|r
// |cffffff00|Hachievement:546:0000000000000001:0:0:0:-1:0:0:0:0|h[Safe Deposit]|h|r
bool AchievementChatLink::Initialize(std::istringstream& iss)
{
if (_color != CHAT_LINK_COLOR_ACHIEVEMENT)
return false;
// Read achievemnt Id
uint32 achievementId = 0;
if (!ReadUInt32(iss, achievementId))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement entry", iss.str().c_str());
return false;
}
// Validate achievement
_achievement = sAchievementStore.LookupEntry(achievementId);
if (!_achievement)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid achivement id %u in |achievement command", iss.str().c_str(), achievementId);
return false;
}
// Check delimiter
if (!CheckDelimiter(iss, DELIMITER, "achievement"))
return false;
// Read HEX
if (!ReadHex(iss, _guid, 0))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): invalid hexadecimal number while reading char's guid", iss.str().c_str());
return false;
}
// Skip progress
const uint8 propsCount = 8;
for (uint8 index = 0; index < propsCount; ++index)
{
if (!CheckDelimiter(iss, DELIMITER, "achievement"))
return false;
if (!ReadUInt32(iss, _data[index]))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement property (%u)", iss.str().c_str(), index);
return false;
}
}
return true;
}
bool AchievementChatLink::ValidateName(char* buffer, const char* context)
{
ChatLink::ValidateName(buffer, context);
bool res = false;
for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
if (*_achievement->name[i] && strcmp(_achievement->name[i], buffer) == 0)
{
res = true;
break;
}
//if (!res)
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): linked achievement (id: %u) name wasn't found in any localization", context, _achievement->ID);
return res;
}
// |color|Htrade:spell_id:cur_value:max_value:player_guid:base64_data|h[name]|h|r
// |cffffd000|Htrade:4037:1:150:1:6AAAAAAAAAAAAAAAAAAAAAAOAADAAAAAAAAAAAAAAAAIAAAAAAAAA|h[Engineering]|h|r
bool TradeChatLink::Initialize(std::istringstream& iss)
{
if (_color != CHAT_LINK_COLOR_TRADE)
return false;
// Spell Id
uint32 spellId = 0;
if (!ReadUInt32(iss, spellId))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement entry", iss.str().c_str());
return false;
}
// Validate spell
_spell = sSpellMgr->GetSpellInfo(spellId);
if (!_spell)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |trade command", iss.str().c_str(), spellId);
return false;
}
// Check delimiter
if (!CheckDelimiter(iss, DELIMITER, "trade"))
return false;
// Minimum talent level
if (!ReadInt32(iss, _minSkillLevel))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading minimum talent level", iss.str().c_str());
return false;
}
// Check delimiter
if (!CheckDelimiter(iss, DELIMITER, "trade"))
return false;
// Maximum talent level
if (!ReadInt32(iss, _maxSkillLevel))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading maximum talent level", iss.str().c_str());
return false;
}
// Check delimiter
if (!CheckDelimiter(iss, DELIMITER, "trade"))
return false;
// Something hexadecimal
if (!ReadHex(iss, _guid, 0))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement's owner guid", iss.str().c_str());
return false;
}
// Skip base64 encoded stuff
_base64 = ReadSkip(iss, PIPE_CHAR);
return true;
}
// |color|Htalent:talent_id:rank|h[name]|h|r
// |cff4e96f7|Htalent:2232:-1|h[Taste for Blood]|h|r
bool TalentChatLink::Initialize(std::istringstream& iss)
{
if (_color != CHAT_LINK_COLOR_TALENT)
return false;
// Read talent entry
if (!ReadUInt32(iss, _talentId))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading talent entry", iss.str().c_str());
return false;
}
// Validate talent
TalentEntry const* talentInfo = sTalentStore.LookupEntry(_talentId);
if (!talentInfo)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid talent id %u in |talent command", iss.str().c_str(), _talentId);
return false;
}
// Validate talent's spell
_spell = sSpellMgr->GetSpellInfo(talentInfo->RankID[0]);
if (!_spell)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |trade command", iss.str().c_str(), talentInfo->RankID[0]);
return false;
}
// Delimiter
if (!CheckDelimiter(iss, DELIMITER, "talent"))
return false;
// Rank
if (!ReadInt32(iss, _rankId))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading talent rank", iss.str().c_str());
return false;
}
return true;
}
// |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r
// |cffffd000|Henchant:3919|h[Engineering: Rough Dynamite]|h|r
bool EnchantmentChatLink::Initialize(std::istringstream& iss)
{
if (_color != CHAT_LINK_COLOR_ENCHANT)
return false;
// Spell Id
uint32 spellId = 0;
if (!ReadUInt32(iss, spellId))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading enchantment spell entry", iss.str().c_str());
return false;
}
// Validate spell
_spell = sSpellMgr->GetSpellInfo(spellId);
if (!_spell)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |enchant command", iss.str().c_str(), spellId);
return false;
}
return true;
}
// |color|Hglyph:glyph_slot_id:glyph_prop_id|h[%s]|h|r
// |cff66bbff|Hglyph:21:762|h[Glyph of Bladestorm]|h|r
bool GlyphChatLink::Initialize(std::istringstream& iss)
{
if (_color != CHAT_LINK_COLOR_GLYPH)
return false;
// Slot
if (!ReadUInt32(iss, _slotId))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading slot id", iss.str().c_str());
return false;
}
// Check delimiter
if (!CheckDelimiter(iss, DELIMITER, "glyph"))
return false;
// Glyph Id
uint32 glyphId = 0;
if (!ReadUInt32(iss, glyphId))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading glyph entry", iss.str().c_str());
return false;
}
// Validate glyph
_glyph = sGlyphPropertiesStore.LookupEntry(glyphId);
if (!_glyph)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid glyph id %u in |glyph command", iss.str().c_str(), glyphId);
return false;
}
// Validate glyph's spell
_spell = sSpellMgr->GetSpellInfo(_glyph->SpellId);
if (!_spell)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |glyph command", iss.str().c_str(), _glyph->SpellId);
return false;
}
return true;
}
LinkExtractor::LinkExtractor(const char* msg) : _iss(msg)
{
}
LinkExtractor::~LinkExtractor()
{
for (Links::iterator itr = _links.begin(); itr != _links.end(); ++itr)
delete *itr;
_links.clear();
}
bool LinkExtractor::IsValidMessage()
{
const char validSequence[6] = "cHhhr";
const char* validSequenceIterator = validSequence;
char buffer[256];
std::istringstream::pos_type startPos = 0;
uint32 color = 0;
ChatLink* link = NULL;
while (!_iss.eof())
{
if (validSequence == validSequenceIterator)
{
link = NULL;
_iss.ignore(255, PIPE_CHAR);
startPos = _iss.tellg() - std::istringstream::pos_type(1);
}
else if (_iss.get() != PIPE_CHAR)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence aborted unexpectedly", _iss.str().c_str());
return false;
}
// pipe has always to be followed by at least one char
if (_iss.peek() == '\0')
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): pipe followed by '\\0'", _iss.str().c_str());
return false;
}
// no further pipe commands
if (_iss.eof())
break;
char commandChar;
_iss >> commandChar;
// | in normal messages is escaped by ||
if (commandChar != PIPE_CHAR)
{
if (commandChar == *validSequenceIterator)
{
if (validSequenceIterator == validSequence+4)
validSequenceIterator = validSequence;
else
++validSequenceIterator;
}
else
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): invalid sequence, expected '%c' but got '%c'", _iss.str().c_str(), *validSequenceIterator, commandChar);
return false;
}
}
else if (validSequence != validSequenceIterator)
{
// no escaped pipes in sequences
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got escaped pipe in sequence", _iss.str().c_str());
return false;
}
switch (commandChar)
{
case 'c':
if (!ReadHex(_iss, color, 8))
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): invalid hexadecimal number while reading color", _iss.str().c_str());
return false;
}
break;
case 'H':
// read chars up to colon = link type
_iss.getline(buffer, 256, DELIMITER);
if (_iss.eof())
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", _iss.str().c_str());
return false;
}
if (strcmp(buffer, "item") == 0)
link = new ItemChatLink();
else if (strcmp(buffer, "quest") == 0)
link = new QuestChatLink();
else if (strcmp(buffer, "trade") == 0)
link = new TradeChatLink();
else if (strcmp(buffer, "talent") == 0)
link = new TalentChatLink();
else if (strcmp(buffer, "spell") == 0)
link = new SpellChatLink();
else if (strcmp(buffer, "enchant") == 0)
link = new EnchantmentChatLink();
else if (strcmp(buffer, "achievement") == 0)
link = new AchievementChatLink();
else if (strcmp(buffer, "glyph") == 0)
link = new GlyphChatLink();
else
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): user sent unsupported link type '%s'", _iss.str().c_str(), buffer);
return false;
}
_links.push_back(link);
link->SetColor(color);
if (!link->Initialize(_iss))
return false;
break;
case 'h':
// if h is next element in sequence, this one must contain the linked text :)
if (*validSequenceIterator == 'h')
{
// links start with '['
if (_iss.get() != '[')
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): link caption doesn't start with '['", _iss.str().c_str());
return false;
}
_iss.getline(buffer, 256, ']');
if (_iss.eof())
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", _iss.str().c_str());
return false;
}
if (!link)
return false;
if (!link->ValidateName(buffer, _iss.str().c_str()))
return false;
}
break;
case 'r':
if (link)
link->SetBounds(startPos, _iss.tellg());
case '|':
// no further payload
break;
default:
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): got invalid command |%c", _iss.str().c_str(), commandChar);
return false;
}
}
// check if every opened sequence was also closed properly
if (validSequence != validSequenceIterator)
{
;//sLog->outDebug(LOG_FILTER_CHATSYS, "ChatHandler::isValidChatMessage('%s'): EOF in active sequence", _iss.str().c_str());
return false;
}
return true;
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright (C)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef SUNWELLCORE_CHATLINK_H
#define SUNWELLCORE_CHATLINK_H
#include "SharedDefines.h"
#include <sstream>
#include <list>
struct ItemLocale;
struct ItemTemplate;
struct ItemRandomSuffixEntry;
struct ItemRandomPropertiesEntry;
class SpellInfo;
struct AchievementEntry;
struct GlyphPropertiesEntry;
class Quest;
///////////////////////////////////////////////////////////////////////////////////////////////////
// ChatLink - abstract base class for various links
class ChatLink
{
public:
ChatLink() : _color(0), _startPos(0), _endPos(0) { }
virtual ~ChatLink() { }
void SetColor(uint32 color) { _color = color; }
// This will allow to extract the whole link string from the message, if necessary.
void SetBounds(std::istringstream::pos_type startPos, std::istringstream::pos_type endPos) { _startPos = startPos; _endPos = endPos; }
virtual bool Initialize(std::istringstream& iss) = 0;
virtual bool ValidateName(char* buffer, const char* context) = 0;
protected:
uint32 _color;
std::string _name;
std::istringstream::pos_type _startPos;
std::istringstream::pos_type _endPos;
};
// ItemChatLink - link to item
class ItemChatLink : public ChatLink
{
public:
ItemChatLink() : ChatLink(), _item(NULL), _suffix(NULL), _property(NULL)
{
memset(_data, 0, sizeof(_data));
}
virtual bool Initialize(std::istringstream& iss);
virtual bool ValidateName(char* buffer, const char* context);
protected:
std::string FormatName(uint8 index, ItemLocale const* locale, char* const* suffixStrings) const;
ItemTemplate const* _item;
int32 _data[8];
ItemRandomSuffixEntry const* _suffix;
ItemRandomPropertiesEntry const* _property;
};
// QuestChatLink - link to quest
class QuestChatLink : public ChatLink
{
public:
QuestChatLink() : ChatLink(), _quest(NULL), _questLevel(0) { }
virtual bool Initialize(std::istringstream& iss);
virtual bool ValidateName(char* buffer, const char* context);
protected:
Quest const* _quest;
int32 _questLevel;
};
// SpellChatLink - link to quest
class SpellChatLink : public ChatLink
{
public:
SpellChatLink() : ChatLink(), _spell(NULL) { }
virtual bool Initialize(std::istringstream& iss);
virtual bool ValidateName(char* buffer, const char* context);
protected:
SpellInfo const* _spell;
};
// AchievementChatLink - link to quest
class AchievementChatLink : public ChatLink
{
public:
AchievementChatLink() : ChatLink(), _guid(0), _achievement(NULL)
{
memset(_data, 0, sizeof(_data));
}
virtual bool Initialize(std::istringstream& iss);
virtual bool ValidateName(char* buffer, const char* context);
protected:
uint32 _guid;
AchievementEntry const* _achievement;
uint32 _data[8];
};
// TradeChatLink - link to trade info
class TradeChatLink : public SpellChatLink
{
public:
TradeChatLink() : SpellChatLink(), _minSkillLevel(0), _maxSkillLevel(0), _guid(0) { }
virtual bool Initialize(std::istringstream& iss);
private:
int32 _minSkillLevel;
int32 _maxSkillLevel;
uint32 _guid;
std::string _base64;
};
// TalentChatLink - link to talent
class TalentChatLink : public SpellChatLink
{
public:
TalentChatLink() : SpellChatLink(), _talentId(0), _rankId(0) { }
virtual bool Initialize(std::istringstream& iss);
private:
uint32 _talentId;
int32 _rankId;
};
// EnchantmentChatLink - link to enchantment
class EnchantmentChatLink : public SpellChatLink
{
public:
EnchantmentChatLink() : SpellChatLink() { }
virtual bool Initialize(std::istringstream& iss);
};
// GlyphChatLink - link to glyph
class GlyphChatLink : public SpellChatLink
{
public:
GlyphChatLink() : SpellChatLink(), _slotId(0), _glyph(NULL) { }
virtual bool Initialize(std::istringstream& iss);
private:
uint32 _slotId;
GlyphPropertiesEntry const* _glyph;
};
class LinkExtractor
{
public:
explicit LinkExtractor(const char* msg);
~LinkExtractor();
bool IsValidMessage();
private:
typedef std::list<ChatLink*> Links;
Links _links;
std::istringstream _iss;
};
#endif