// Copyright (c) CypherCore All rights reserved. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. using Framework.Constants; using Framework.Database; using Game.DataStorage; using Game.Entities; using Game.Networking.Packets; using System; using System.Collections.Generic; namespace Game.Chat { public class ChannelManager { public static AreaTableRecord SpecialLinkedArea { get; private set; } public ChannelManager(Team team) { _team = team; _guidGenerator = new ObjectGuidGenerator(HighGuid.ChatChannel); } public static void LoadFromDB() { SpecialLinkedArea = CliDB.AreaTableStorage.LookupByKey(3459); Cypher.Assert(SpecialLinkedArea.HasFlag(AreaFlags.LinkedChatSpecialArea)); if (!WorldConfig.GetBoolValue(WorldCfg.PreserveCustomChannels)) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 custom chat channels. Custom channel saving is disabled."); return; } uint oldMSTime = Time.GetMSTime(); uint days = WorldConfig.GetUIntValue(WorldCfg.PreserveCustomChannelDuration); if (days != 0) { PreparedStatement stmt = CharacterDatabase.GetPreparedStatement(CharStatements.DEL_OLD_CHANNELS); stmt.AddValue(0, days * Time.Day); DB.Characters.Execute(stmt); } SQLResult result = DB.Characters.Query("SELECT name, team, announce, ownership, password, bannedList FROM channels"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 custom chat channels. DB table `channels` is empty."); return; } List<(string name, Team team)> toDelete = new(); uint count = 0; do { string dbName = result.Read(0); // may be different - channel names are case insensitive Team team = (Team)result.Read(1); bool dbAnnounce = result.Read(2); bool dbOwnership = result.Read(3); string dbPass = result.Read(4); string dbBanned = result.Read(5); ChannelManager mgr = ForTeam(team); if (mgr == null) { Log.outError(LogFilter.ServerLoading, $"Failed to load custom chat channel '{dbName}' from database - invalid team {team}. Deleted."); toDelete.Add((dbName, team)); continue; } Channel channel = new Channel(mgr.CreateCustomChannelGuid(), dbName, team, dbBanned); channel.SetAnnounce(dbAnnounce); channel.SetOwnership(dbOwnership); channel.SetPassword(dbPass); mgr._customChannels.Add(dbName, channel); ++count; } while (result.NextRow()); foreach (var (name, team) in toDelete) { PreparedStatement stmt = CharacterDatabase.GetPreparedStatement(CharStatements.DEL_CHANNEL); stmt.AddValue(0, name); stmt.AddValue(1, (uint)team); DB.Characters.Execute(stmt); } Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} custom chat channels in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); } public static ChannelManager ForTeam(Team team) { if (WorldConfig.GetBoolValue(WorldCfg.AllowTwoSideInteractionChannel)) return neutralChannelMgr; // cross-faction return team switch { Team.Alliance => allianceChannelMgr, Team.Horde => hordeChannelMgr, Team.PandariaNeutral => neutralChannelMgr, _ => null }; } public static Channel GetChannelForPlayerByNamePart(string namePart, Player playerSearcher) { foreach (Channel channel in playerSearcher.GetJoinedChannels()) { string chanName = channel.GetName(playerSearcher.GetSession().GetSessionDbcLocale()); if (chanName.ToLower().Equals(namePart.ToLower())) return channel; } return null; } public void SaveToDB() { foreach (var pair in _customChannels) pair.Value.UpdateChannelInDB(); } public static Channel GetChannelForPlayerByGuid(ObjectGuid channelGuid, Player playerSearcher) { foreach (Channel channel in playerSearcher.GetJoinedChannels()) if (channel.GetGUID() == channelGuid) return channel; return null; } public Channel GetSystemChannel(uint channelId, AreaTableRecord zoneEntry = null) { ObjectGuid channelGuid = CreateBuiltinChannelGuid(channelId, zoneEntry); var currentChannel = _channels.LookupByKey(channelGuid); if (currentChannel != null) return currentChannel; Channel newChannel = new Channel(channelGuid, channelId, _team, zoneEntry); _channels[channelGuid] = newChannel; return newChannel; } public Channel CreateCustomChannel(string name) { if (_customChannels.ContainsKey(name.ToLower())) return null; Channel newChannel = new(CreateCustomChannelGuid(), name, _team); newChannel.SetDirty(); _customChannels[name.ToLower()] = newChannel; return newChannel; } public Channel GetCustomChannel(string name) { return _customChannels.LookupByKey(name.ToLower()); } public Channel GetChannel(uint channelId, string name, Player player, bool notify = true, AreaTableRecord zoneEntry = null) { Channel result = null; if (channelId != 0) // builtin { var channel = _channels.LookupByKey(CreateBuiltinChannelGuid(channelId, zoneEntry)); if (channel != null) result = channel; } else // custom { var channel = _customChannels.LookupByKey(name.ToLower()); if (channel != null) result = channel; } if (result == null && notify) { string channelName = name; Channel.GetChannelName(ref channelName, channelId, player.GetSession().GetSessionDbcLocale(), zoneEntry); SendNotOnChannelNotify(player, channelName); } return result; } public void LeftChannel(uint channelId, AreaTableRecord zoneEntry) { var guid = CreateBuiltinChannelGuid(channelId, zoneEntry); var channel = _channels.LookupByKey(guid); if (channel == null) return; if (channel.GetNumPlayers() == 0) _channels.Remove(guid); } public static void SendNotOnChannelNotify(Player player, string name) { ChannelNotify notify = new(); notify.Type = ChatNotify.NotMemberNotice; notify.Channel = name; player.SendPacket(notify); } ObjectGuid CreateCustomChannelGuid() { ulong high = 0; high |= (ulong)HighGuid.ChatChannel << 58; high |= (ulong)Global.RealmMgr.GetCurrentRealmId().Index << 42; high |= (ulong)(_team == Team.Alliance ? 3 : 5) << 4; ObjectGuid channelGuid = new(); channelGuid.SetRawValue(high, _guidGenerator.Generate()); return channelGuid; } ObjectGuid CreateBuiltinChannelGuid(uint channelId, AreaTableRecord zoneEntry = null) { ChatChannelsRecord channelEntry = CliDB.ChatChannelsStorage.LookupByKey(channelId); uint zoneId = 0; if (zoneEntry != null && channelEntry.HasFlag(ChatChannelFlags.ZoneBased) && !channelEntry.HasFlag(ChatChannelFlags.LinkedChannel)) zoneId = zoneEntry.Id; if (channelEntry.HasFlag(ChatChannelFlags.GlobalForTournament)) { var currentRealm = Global.RealmMgr.GetCurrentRealm(); if (currentRealm != null) { var category = CliDB.CfgCategoriesStorage.LookupByKey(currentRealm.Timezone); if (category != null && category.HasFlag(CfgCategoriesFlags.Tournament)) zoneId = 0; } } return ObjectGuid.Create(HighGuid.ChatChannel, true, channelEntry.HasFlag(ChatChannelFlags.LinkedChannel), (ushort)zoneId, (byte)(_team == Team.Alliance ? 3 : 5), channelId); } Dictionary _customChannels = new(); Dictionary _channels = new(); Team _team; ObjectGuidGenerator _guidGenerator; static ChannelManager allianceChannelMgr = new(Team.Alliance); static ChannelManager hordeChannelMgr = new(Team.Horde); static ChannelManager neutralChannelMgr = new(Team.PandariaNeutral); } }