Merge branch 'master' into 330
[getmangos.git] / src / game / ChatHandler.cpp
blobd7bf68a46d44ca155b1f3981744964bbe621a9d7
1 /*
2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Common.h"
20 #include "Log.h"
21 #include "WorldPacket.h"
22 #include "WorldSession.h"
23 #include "World.h"
24 #include "Opcodes.h"
25 #include "ObjectMgr.h"
26 #include "Chat.h"
27 #include "Database/DatabaseEnv.h"
28 #include "ChannelMgr.h"
29 #include "Group.h"
30 #include "Guild.h"
31 #include "ObjectAccessor.h"
32 #include "ScriptCalls.h"
33 #include "Player.h"
34 #include "SpellAuras.h"
35 #include "Language.h"
36 #include "Util.h"
37 #include "GridNotifiersImpl.h"
38 #include "CellImpl.h"
40 bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang)
42 if (lang != LANG_ADDON)
44 // strip invisible characters for non-addon messages
45 if(sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
46 stripLineInvisibleChars(msg);
48 if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && GetSecurity() < SEC_MODERATOR
49 && !ChatHandler(this).isValidChatMessage(msg.c_str()))
51 sLog.outError("Player %s (GUID: %u) sent a chatmessage with an invalid link: %s", GetPlayer()->GetName(),
52 GetPlayer()->GetGUIDLow(), msg.c_str());
53 if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
54 KickPlayer();
55 return false;
59 return true;
62 void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
64 uint32 type;
65 uint32 lang;
67 recv_data >> type;
68 recv_data >> lang;
70 if(type >= MAX_CHAT_MSG_TYPE)
72 sLog.outError("CHAT: Wrong message type received: %u", type);
73 return;
76 sLog.outDebug("CHAT: packet received. type %u, lang %u", type, lang );
78 // prevent talking at unknown language (cheating)
79 LanguageDesc const* langDesc = GetLanguageDescByID(lang);
80 if(!langDesc)
82 SendNotification(LANG_UNKNOWN_LANGUAGE);
83 return;
85 if(langDesc->skill_id != 0 && !_player->HasSkill(langDesc->skill_id))
87 // also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language)
88 Unit::AuraList const& langAuras = _player->GetAurasByType(SPELL_AURA_COMPREHEND_LANGUAGE);
89 bool foundAura = false;
90 for(Unit::AuraList::const_iterator i = langAuras.begin(); i != langAuras.end(); ++i)
92 if((*i)->GetModifier()->m_miscvalue == int32(lang))
94 foundAura = true;
95 break;
98 if(!foundAura)
100 SendNotification(LANG_NOT_LEARNED_LANGUAGE);
101 return;
105 if(lang == LANG_ADDON)
107 // Disabled addon channel?
108 if(!sWorld.getConfig(CONFIG_ADDON_CHANNEL))
109 return;
111 // LANG_ADDON should not be changed nor be affected by flood control
112 else
114 // send in universal language if player in .gmon mode (ignore spell effects)
115 if (_player->isGameMaster())
116 lang = LANG_UNIVERSAL;
117 else
119 // send in universal language in two side iteration allowed mode
120 if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT))
121 lang = LANG_UNIVERSAL;
122 else
124 switch(type)
126 case CHAT_MSG_PARTY:
127 case CHAT_MSG_RAID:
128 case CHAT_MSG_RAID_LEADER:
129 case CHAT_MSG_RAID_WARNING:
130 // allow two side chat at group channel if two side group allowed
131 if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP))
132 lang = LANG_UNIVERSAL;
133 break;
134 case CHAT_MSG_GUILD:
135 case CHAT_MSG_OFFICER:
136 // allow two side chat at guild channel if two side guild allowed
137 if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD))
138 lang = LANG_UNIVERSAL;
139 break;
143 // but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used)
144 Unit::AuraList const& ModLangAuras = _player->GetAurasByType(SPELL_AURA_MOD_LANGUAGE);
145 if(!ModLangAuras.empty())
146 lang = ModLangAuras.front()->GetModifier()->m_miscvalue;
149 if (!_player->CanSpeak())
151 std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
152 SendNotification(GetMangosString(LANG_WAIT_BEFORE_SPEAKING), timeStr.c_str());
153 return;
156 if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND)
157 GetPlayer()->UpdateSpeakTime();
160 switch(type)
162 case CHAT_MSG_SAY:
163 case CHAT_MSG_EMOTE:
164 case CHAT_MSG_YELL:
166 std::string msg;
167 recv_data >> msg;
169 if(msg.empty())
170 break;
172 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
173 break;
175 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
176 return;
178 if(msg.empty())
179 break;
181 if(type == CHAT_MSG_SAY)
182 GetPlayer()->Say(msg, lang);
183 else if(type == CHAT_MSG_EMOTE)
184 GetPlayer()->TextEmote(msg);
185 else if(type == CHAT_MSG_YELL)
186 GetPlayer()->Yell(msg, lang);
187 } break;
189 case CHAT_MSG_WHISPER:
191 std::string to, msg;
192 recv_data >> to;
193 recv_data >> msg;
195 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
196 return;
198 if(msg.empty())
199 break;
201 if(!normalizePlayerName(to))
203 SendPlayerNotFoundNotice(to);
204 break;
207 Player *player = sObjectMgr.GetPlayer(to.c_str());
208 uint32 tSecurity = GetSecurity();
209 uint32 pSecurity = player ? player->GetSession()->GetSecurity() : SEC_PLAYER;
210 if (!player || (tSecurity == SEC_PLAYER && pSecurity > SEC_PLAYER && !player->isAcceptWhispers()))
212 SendPlayerNotFoundNotice(to);
213 return;
216 if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && tSecurity == SEC_PLAYER && pSecurity == SEC_PLAYER )
218 uint32 sidea = GetPlayer()->GetTeam();
219 uint32 sideb = player->GetTeam();
220 if( sidea != sideb )
222 SendPlayerNotFoundNotice(to);
223 return;
227 GetPlayer()->Whisper(msg, lang, player->GetGUID());
228 } break;
230 case CHAT_MSG_PARTY:
231 case CHAT_MSG_PARTY_LEADER:
233 std::string msg;
234 recv_data >> msg;
236 if(msg.empty())
237 break;
239 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
240 break;
242 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
243 return;
245 if(msg.empty())
246 break;
248 // if player is in battleground, he cannot say to battleground members by /p
249 Group *group = GetPlayer()->GetOriginalGroup();
250 if(!group)
252 group = _player->GetGroup();
253 if(!group || group->isBGGroup())
254 return;
257 if((type == CHAT_MSG_PARTY_LEADER) && !group->IsLeader(_player->GetGUID()))
258 return;
260 WorldPacket data;
261 ChatHandler::FillMessageData(&data, this, type, lang, NULL, 0, msg.c_str(), NULL);
262 group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID()));
263 } break;
265 case CHAT_MSG_GUILD:
267 std::string msg;
268 recv_data >> msg;
270 if(msg.empty())
271 break;
273 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
274 break;
276 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
277 return;
279 if(msg.empty())
280 break;
282 if (GetPlayer()->GetGuildId())
283 if (Guild *guild = sObjectMgr.GetGuildById(GetPlayer()->GetGuildId()))
284 guild->BroadcastToGuild(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
285 } break;
287 case CHAT_MSG_OFFICER:
289 std::string msg;
290 recv_data >> msg;
292 if(msg.empty())
293 break;
295 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
296 break;
298 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
299 return;
301 if(msg.empty())
302 break;
304 if (GetPlayer()->GetGuildId())
305 if (Guild *guild = sObjectMgr.GetGuildById(GetPlayer()->GetGuildId()))
306 guild->BroadcastToOfficers(this, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL);
307 } break;
309 case CHAT_MSG_RAID:
311 std::string msg;
312 recv_data >> msg;
314 if(msg.empty())
315 break;
317 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
318 break;
320 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
321 return;
323 if(msg.empty())
324 break;
326 // if player is in battleground, he cannot say to battleground members by /ra
327 Group *group = GetPlayer()->GetOriginalGroup();
328 if(!group)
330 group = GetPlayer()->GetGroup();
331 if(!group || group->isBGGroup() || !group->isRaidGroup())
332 return;
335 WorldPacket data;
336 ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(), NULL);
337 group->BroadcastPacket(&data, false);
338 } break;
339 case CHAT_MSG_RAID_LEADER:
341 std::string msg;
342 recv_data >> msg;
344 if(msg.empty())
345 break;
347 if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
348 break;
350 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
351 return;
353 if(msg.empty())
354 break;
356 // if player is in battleground, he cannot say to battleground members by /ra
357 Group *group = GetPlayer()->GetOriginalGroup();
358 if(!group)
360 group = GetPlayer()->GetGroup();
361 if(!group || group->isBGGroup() || !group->isRaidGroup() || !group->IsLeader(_player->GetGUID()))
362 return;
365 WorldPacket data;
366 ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(), NULL);
367 group->BroadcastPacket(&data, false);
368 } break;
370 case CHAT_MSG_RAID_WARNING:
372 std::string msg;
373 recv_data >> msg;
375 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
376 return;
378 if(msg.empty())
379 break;
381 Group *group = GetPlayer()->GetGroup();
382 if(!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())))
383 return;
385 WorldPacket data;
386 //in battleground, raid warning is sent only to players in battleground - code is ok
387 ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(), NULL);
388 group->BroadcastPacket(&data, false);
389 } break;
391 case CHAT_MSG_BATTLEGROUND:
393 std::string msg;
394 recv_data >> msg;
396 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
397 return;
399 if(msg.empty())
400 break;
402 // battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
403 Group *group = GetPlayer()->GetGroup();
404 if(!group || !group->isBGGroup())
405 return;
407 WorldPacket data;
408 ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(), NULL);
409 group->BroadcastPacket(&data, false);
410 } break;
412 case CHAT_MSG_BATTLEGROUND_LEADER:
414 std::string msg;
415 recv_data >> msg;
417 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
418 return;
420 if(msg.empty())
421 break;
423 // battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
424 Group *group = GetPlayer()->GetGroup();
425 if(!group || !group->isBGGroup() || !group->IsLeader(GetPlayer()->GetGUID()))
426 return;
428 WorldPacket data;
429 ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(), NULL);
430 group->BroadcastPacket(&data, false);
431 } break;
433 case CHAT_MSG_CHANNEL:
435 std::string channel, msg;
436 recv_data >> channel;
437 recv_data >> msg;
439 if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
440 return;
442 if(msg.empty())
443 break;
445 if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
446 if(Channel *chn = cMgr->GetChannel(channel, _player))
447 chn->Say(_player->GetGUID(), msg.c_str(), lang);
448 } break;
450 case CHAT_MSG_AFK:
452 std::string msg;
453 recv_data >> msg;
455 if((msg.empty() || !_player->isAFK()) && !_player->isInCombat() )
457 if(!_player->isAFK())
459 if(msg.empty())
460 msg = GetMangosString(LANG_PLAYER_AFK_DEFAULT);
461 _player->afkMsg = msg;
463 _player->ToggleAFK();
464 if(_player->isAFK() && _player->isDND())
465 _player->ToggleDND();
467 } break;
469 case CHAT_MSG_DND:
471 std::string msg;
472 recv_data >> msg;
474 if(msg.empty() || !_player->isDND())
476 if(!_player->isDND())
478 if(msg.empty())
479 msg = GetMangosString(LANG_PLAYER_DND_DEFAULT);
480 _player->dndMsg = msg;
482 _player->ToggleDND();
483 if(_player->isDND() && _player->isAFK())
484 _player->ToggleAFK();
486 } break;
488 default:
489 sLog.outError("CHAT: unknown message type %u, lang: %u", type, lang);
490 break;
494 void WorldSession::HandleEmoteOpcode( WorldPacket & recv_data )
496 if(!GetPlayer()->isAlive())
497 return;
499 uint32 emote;
500 recv_data >> emote;
501 GetPlayer()->HandleEmoteCommand(emote);
504 namespace MaNGOS
506 class EmoteChatBuilder
508 public:
509 EmoteChatBuilder(Player const& pl, uint32 text_emote, uint32 emote_num, Unit const* target)
510 : i_player(pl), i_text_emote(text_emote), i_emote_num(emote_num), i_target(target) {}
512 void operator()(WorldPacket& data, int32 loc_idx)
514 char const* nam = i_target ? i_target->GetNameForLocaleIdx(loc_idx) : NULL;
515 uint32 namlen = (nam ? strlen(nam) : 0) + 1;
517 data.Initialize(SMSG_TEXT_EMOTE, (20+namlen));
518 data << i_player.GetGUID();
519 data << (uint32)i_text_emote;
520 data << i_emote_num;
521 data << (uint32)namlen;
522 if( namlen > 1 )
523 data.append(nam, namlen);
524 else
525 data << (uint8)0x00;
528 private:
529 Player const& i_player;
530 uint32 i_text_emote;
531 uint32 i_emote_num;
532 Unit const* i_target;
534 } // namespace MaNGOS
536 void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data )
538 if(!GetPlayer()->isAlive())
539 return;
541 if (!GetPlayer()->CanSpeak())
543 std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
544 SendNotification(GetMangosString(LANG_WAIT_BEFORE_SPEAKING), timeStr.c_str());
545 return;
548 uint32 text_emote, emoteNum;
549 uint64 guid;
551 recv_data >> text_emote;
552 recv_data >> emoteNum;
553 recv_data >> guid;
555 EmotesTextEntry const *em = sEmotesTextStore.LookupEntry(text_emote);
556 if (!em)
557 return;
559 uint32 emote_anim = em->textid;
561 switch(emote_anim)
563 case EMOTE_STATE_SLEEP:
564 case EMOTE_STATE_SIT:
565 case EMOTE_STATE_KNEEL:
566 case EMOTE_ONESHOT_NONE:
567 break;
568 default:
569 GetPlayer()->HandleEmoteCommand(emote_anim);
570 break;
573 Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
575 CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
577 Cell cell(p);
578 cell.data.Part.reserved = ALL_DISTRICT;
579 cell.SetNoCreate();
581 MaNGOS::EmoteChatBuilder emote_builder(*GetPlayer(), text_emote, emoteNum, unit);
582 MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > emote_do(emote_builder);
583 MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > > emote_worker(GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), emote_do);
584 TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > >, WorldTypeMapContainer> message(emote_worker);
585 CellLock<GridReadGuard> cell_lock(cell, p);
586 cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE));
588 GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE, text_emote, 0, unit);
590 //Send scripted event call
591 if (unit && unit->GetTypeId() == TYPEID_UNIT && ((Creature*)unit)->AI())
592 ((Creature*)unit)->AI()->ReceiveEmote(GetPlayer(), text_emote);
595 void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data )
597 uint64 iguid;
598 uint8 unk;
599 //sLog.outDebug("WORLD: Received CMSG_CHAT_IGNORED");
601 recv_data >> iguid;
602 recv_data >> unk; // probably related to spam reporting
604 Player *player = sObjectMgr.GetPlayer(iguid);
605 if(!player || !player->GetSession())
606 return;
608 WorldPacket data;
609 ChatHandler::FillMessageData(&data, this, CHAT_MSG_IGNORED, LANG_UNIVERSAL, NULL, GetPlayer()->GetGUID(), GetPlayer()->GetName(), NULL);
610 player->GetSession()->SendPacket(&data);
613 void WorldSession::SendPlayerNotFoundNotice(std::string name)
615 WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, name.size()+1);
616 data << name;
617 SendPacket(&data);