/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * 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 . */ #include "ChannelMgr.h" #include "Channel.h" #include "ChannelPackets.h" #include "DatabaseEnv.h" #include "DB2Stores.h" #include "Log.h" #include "Player.h" #include "RealmList.h" #include "World.h" #include "WorldSession.h" AreaTableEntry const* ChannelMgr::SpecialLinkedArea; ChannelMgr::ChannelMgr(Team team) : _team(team), _guidGenerator(HighGuid::ChatChannel) { } ChannelMgr::~ChannelMgr() { for (auto itr = _channels.begin(); itr != _channels.end(); ++itr) delete itr->second; for (auto itr = _customChannels.begin(); itr != _customChannels.end(); ++itr) delete itr->second; } /*static*/ void ChannelMgr::LoadFromDB() { SpecialLinkedArea = sAreaTableStore.AssertEntry(3459); ASSERT(SpecialLinkedArea->GetFlags().HasFlag(AreaFlags::LinkedChatSpecialArea)); if (!sWorld->getBoolConfig(CONFIG_PRESERVE_CUSTOM_CHANNELS)) { TC_LOG_INFO("server.loading", ">> Loaded 0 custom chat channels. Custom channel saving is disabled."); return; } uint32 oldMSTime = getMSTime(); if (uint32 days = sWorld->getIntConfig(CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION)) { CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_OLD_CHANNELS); stmt->setUInt32(0, days * DAY); CharacterDatabase.Execute(stmt); } QueryResult result = CharacterDatabase.Query("SELECT name, team, announce, ownership, password, bannedList FROM channels"); if (!result) { TC_LOG_INFO("server.loading", ">> Loaded 0 custom chat channels. DB table `channels` is empty."); return; } std::vector> toDelete; uint32 count = 0; do { Field* fields = result->Fetch(); std::string dbName = fields[0].GetString(); Team team = Team(fields[1].GetUInt32()); bool dbAnnounce = fields[2].GetBool(); bool dbOwnership = fields[3].GetBool(); std::string dbPass = fields[4].GetString(); std::string dbBanned = fields[5].GetString(); std::wstring channelName; if (!Utf8toWStr(dbName, channelName)) { TC_LOG_ERROR("server.loading", "Failed to load custom chat channel '{}' from database - invalid utf8 sequence? Deleted.", dbName); toDelete.push_back({ dbName, team }); continue; } ChannelMgr* mgr = ForTeam(team); if (!mgr) { TC_LOG_ERROR("server.loading", "Failed to load custom chat channel '{}' from database - invalid team {}. Deleted.", dbName, team); toDelete.push_back({ dbName, team }); continue; } Channel* channel = new Channel(mgr->CreateCustomChannelGuid(), dbName, team, dbBanned); channel->SetAnnounce(dbAnnounce); channel->SetOwnership(dbOwnership); channel->SetPassword(dbPass); mgr->_customChannels.emplace(channelName, channel); ++count; } while (result->NextRow()); for (std::pair const& pair : toDelete) { CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHANNEL); stmt->setString(0, pair.first); stmt->setUInt32(1, pair.second); CharacterDatabase.Execute(stmt); } TC_LOG_INFO("server.loading", ">> Loaded {} custom chat channels in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); } /*static*/ ChannelMgr* ChannelMgr::ForTeam(Team team) { static ChannelMgr allianceChannelMgr(ALLIANCE); static ChannelMgr hordeChannelMgr(HORDE); static ChannelMgr neutralChannelMgr(PANDARIA_NEUTRAL); if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) return &neutralChannelMgr; // cross-faction switch (team) { case HORDE: return &hordeChannelMgr; case ALLIANCE: return &allianceChannelMgr; case PANDARIA_NEUTRAL: return &neutralChannelMgr; default: break; } return nullptr; } Channel* ChannelMgr::GetChannelForPlayerByNamePart(std::string const& namePart, Player* playerSearcher) { std::wstring channelNamePart; if (!Utf8toWStr(namePart, channelNamePart)) return nullptr; wstrToLower(channelNamePart); for (Channel* channel : playerSearcher->GetJoinedChannels()) { std::string chanName = channel->GetName(playerSearcher->GetSession()->GetSessionDbcLocale()); std::wstring channelNameW; if (!Utf8toWStr(chanName, channelNameW)) continue; wstrToLower(channelNameW); if (!channelNameW.compare(0, channelNamePart.size(), channelNamePart)) return channel; } return nullptr; } void ChannelMgr::SaveToDB() { for (auto pair : _customChannels) pair.second->UpdateChannelInDB(); } Channel* ChannelMgr::GetChannelForPlayerByGuid(ObjectGuid channelGuid, Player* playerSearcher) { for (Channel* channel : playerSearcher->GetJoinedChannels()) if (channel->GetGUID() == channelGuid) return channel; return nullptr; } Channel* ChannelMgr::GetSystemChannel(uint32 channelId, AreaTableEntry const* zoneEntry /* = nullptr */) { ObjectGuid channelGuid = CreateBuiltinChannelGuid(channelId, zoneEntry); auto itr = _channels.find(channelGuid); if (itr != _channels.end()) return itr->second; Channel* newChannel = new Channel(channelGuid, channelId, _team, zoneEntry); _channels[channelGuid] = newChannel; return newChannel; } Channel* ChannelMgr::CreateCustomChannel(std::string const& name) { std::wstring channelName; if (!Utf8toWStr(name, channelName)) return nullptr; wstrToLower(channelName); Channel*& c = _customChannels[channelName]; if (c) return nullptr; Channel* newChannel = new Channel(CreateCustomChannelGuid(), name, _team); newChannel->SetDirty(); c = newChannel; return newChannel; } Channel* ChannelMgr::GetCustomChannel(std::string const& name) const { std::wstring channelName; if (!Utf8toWStr(name, channelName)) return nullptr; wstrToLower(channelName); auto itr = _customChannels.find(channelName); if (itr != _customChannels.end()) return itr->second; return nullptr; } Channel* ChannelMgr::GetChannel(uint32 channelId, std::string const& name, Player* player, bool notify /*= true*/, AreaTableEntry const* zoneEntry /*= nullptr*/) const { Channel* result = nullptr; if (channelId) // builtin { auto itr = _channels.find(CreateBuiltinChannelGuid(channelId, zoneEntry)); if (itr != _channels.end()) result = itr->second; } else // custom { std::wstring channelName; if (!Utf8toWStr(name, channelName)) return nullptr; wstrToLower(channelName); auto itr = _customChannels.find(channelName); if (itr != _customChannels.end()) result = itr->second; } if (!result && notify) { std::string channelName = name; Channel::GetChannelName(channelName, channelId, player->GetSession()->GetSessionDbcLocale(), zoneEntry); SendNotOnChannelNotify(player, channelName); } return result; } void ChannelMgr::LeftChannel(uint32 channelId, AreaTableEntry const* zoneEntry) { auto itr = _channels.find(CreateBuiltinChannelGuid(channelId, zoneEntry)); if (itr == _channels.end()) return; Channel* channel = itr->second; if (!channel->GetNumPlayers()) { _channels.erase(itr); delete channel; } } void ChannelMgr::SendNotOnChannelNotify(Player const* player, std::string const& name) { WorldPackets::Channel::ChannelNotify notify; notify.Type = CHAT_NOT_MEMBER_NOTICE; notify._Channel = name; player->SendDirectMessage(notify.Write()); } ObjectGuid ChannelMgr::CreateCustomChannelGuid() { return ObjectGuid::Create(false, false, 0, _team == ALLIANCE ? 3 : 5, _guidGenerator.Generate()); } ObjectGuid ChannelMgr::CreateBuiltinChannelGuid(uint32 channelId, AreaTableEntry const* zoneEntry /*= nullptr*/) const { ChatChannelsEntry const* channelEntry = sChatChannelsStore.AssertEntry(channelId); uint32 zoneId = 0; if (zoneEntry && channelEntry->GetFlags().HasFlag(ChatChannelFlags::ZoneBased) && !channelEntry->GetFlags().HasFlag(ChatChannelFlags::LinkedChannel)) zoneId = zoneEntry->ID; if (channelEntry->GetFlags().HasFlag(ChatChannelFlags::GlobalForTournament)) if (std::shared_ptr currentRealm = sRealmList->GetCurrentRealm()) if (Cfg_CategoriesEntry const* category = sCfgCategoriesStore.LookupEntry(currentRealm->Timezone)) if (category->GetFlags().HasFlag(CfgCategoriesFlags::Tournament)) zoneId = 0; return ObjectGuid::Create(true, channelEntry->GetFlags().HasFlag(ChatChannelFlags::LinkedChannel), zoneId, _team == ALLIANCE ? 3 : 5, channelId); }