From 75d3497d3869052f53ca935c753f6de96e74f23b Mon Sep 17 00:00:00 2001 From: hondacrx Date: Sun, 2 Jan 2022 13:44:00 -0500 Subject: [PATCH] Core/Chat: Custom channel preservation rewrite. Channel data is now loaded at startup, and written to the DB periodically, instead of both things happening in real time. Port From (https://github.com/TrinityCore/TrinityCore/commit/8839fa3fe24e72e4506e94389663b82ab2292649) --- Source/Framework/Constants/SharedConst.cs | 6 +- .../Database/Databases/CharacterDatabase.cs | 9 +- Source/Game/Chat/Channels/Channel.cs | 77 ++++++------- Source/Game/Chat/Channels/ChannelManager.cs | 108 ++++++++++++------ Source/Game/Server/WorldConfig.cs | 1 + Source/Game/Server/WorldManager.cs | 21 +++- Source/WorldServer/WorldServer.conf.dist | 7 ++ 7 files changed, 142 insertions(+), 87 deletions(-) diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index ff0bc8a7a..2c364244f 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -887,11 +887,12 @@ namespace Framework.Constants public enum WorldStates { CurrencyResetTime = 20001, // Next currency reset time - WeeklyQuestResetTime = 20002, // Next weekly reset time + WeeklyQuestResetTime = 20002, // Next weekly quest reset time BGDailyResetTime = 20003, // Next daily BG reset time CleaningFlags = 20004, // Cleaning Flags GuildDailyResetTime = 20006, // Next guild cap reset time - MonthlyQuestResetTime = 20007, // Next monthly reset time + MonthlyQuestResetTime = 20007, // Next monthly quest reset time + DailyQuestResetTime = 2008, // Next daily quest reset time // Cata specific custom worldstates GuildWeeklyResetTime = 20050, // Next guild week reset time } @@ -1575,6 +1576,7 @@ namespace Framework.Constants PortInstance, PortWorld, PreserveCustomChannelDuration, + PreserveCustomChannelInterval, PreserveCustomChannels, PreventRenameCustomization, PvpTokenCount, diff --git a/Source/Framework/Database/Databases/CharacterDatabase.cs b/Source/Framework/Database/Databases/CharacterDatabase.cs index 1db072dd5..24aee207a 100644 --- a/Source/Framework/Database/Databases/CharacterDatabase.cs +++ b/Source/Framework/Database/Databases/CharacterDatabase.cs @@ -318,11 +318,11 @@ namespace Framework.Database " ON DUPLICATE KEY UPDATE LogGuid = VALUES (LogGuid), EventType = VALUES (EventType), PlayerGuid = VALUES (PlayerGuid), Flags = VALUES (Flags), Value = VALUES (Value), Timestamp = VALUES (Timestamp)"); // Chat channel handling - PrepareStatement(CharStatements.SEL_CHANNEL, "SELECT name, announce, ownership, password, bannedList FROM channels WHERE name = ? AND team = ?"); - PrepareStatement(CharStatements.INS_CHANNEL, "INSERT INTO channels(name, team, lastUsed) VALUES (?, ?, UNIX_TIMESTAMP())"); - PrepareStatement(CharStatements.UPD_CHANNEL, "UPDATE channels SET announce = ?, ownership = ?, password = ?, bannedList = ?, lastUsed = UNIX_TIMESTAMP() WHERE name = ? AND team = ?"); + PrepareStatement(CharStatements.UPD_CHANNEL, "INSERT INTO channels (name, team, announce, ownership, password, bannedList, lastUsed) VALUES (?, ?, ?, ?, ?, ?, UNIX_TIMESTAMP()) " + + "ON DUPLICATE KEY UPDATE announce=VALUES(announce), ownership=VALUES(ownership), password=VALUES(password), bannedList=VALUES(bannedList), lastUsed=VALUES(lastUsed)"); PrepareStatement(CharStatements.UPD_CHANNEL_USAGE, "UPDATE channels SET lastUsed = UNIX_TIMESTAMP() WHERE name = ? AND team = ?"); PrepareStatement(CharStatements.UPD_CHANNEL_OWNERSHIP, "UPDATE channels SET ownership = ? WHERE name LIKE ?"); + PrepareStatement(CharStatements.DEL_CHANNEL, "DELETE FROM channels WHERE name = ? AND team = ?"); PrepareStatement(CharStatements.DEL_OLD_CHANNELS, "DELETE FROM channels WHERE ownership = 1 AND lastUsed + ? < UNIX_TIMESTAMP()"); // Equipmentsets @@ -1020,11 +1020,10 @@ namespace Framework.Database SEL_GUILD_ACHIEVEMENT_CRITERIA, INS_GUILD_NEWS, - SEL_CHANNEL, - INS_CHANNEL, UPD_CHANNEL, UPD_CHANNEL_USAGE, UPD_CHANNEL_OWNERSHIP, + DEL_CHANNEL, DEL_OLD_CHANNELS, UPD_EQUIP_SET, diff --git a/Source/Game/Chat/Channels/Channel.cs b/Source/Game/Chat/Channels/Channel.cs index 940517100..5a23c3494 100644 --- a/Source/Game/Chat/Channels/Channel.cs +++ b/Source/Game/Chat/Channels/Channel.cs @@ -55,7 +55,6 @@ namespace Game.Chat { _announceEnabled = true; _ownershipEnabled = true; - _persistentChannel = WorldConfig.GetBoolValue(WorldCfg.PreserveCustomChannels); _channelFlags = ChannelFlags.Custom; _channelTeam = team; _channelGuid = guid; @@ -101,45 +100,39 @@ namespace Game.Chat return result; } - void UpdateChannelInDB() + public void UpdateChannelInDB() { - if (_persistentChannel) + long now = GameTime.GetGameTime(); + if (_isDirty) { string banlist = ""; foreach (var iter in _bannedStore) banlist += iter.GetRawValue().ToHexString() + ' '; PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHANNEL); - stmt.AddValue(0, _announceEnabled); - stmt.AddValue(1, _ownershipEnabled); - stmt.AddValue(2, _channelPassword); - stmt.AddValue(3, banlist); - stmt.AddValue(4, _channelName); - stmt.AddValue(5, (uint)_channelTeam); + stmt.AddValue(0, _channelName); + stmt.AddValue(1, (uint)_channelTeam); + stmt.AddValue(2, _announceEnabled); + stmt.AddValue(3, _ownershipEnabled); + stmt.AddValue(4, _channelPassword); + stmt.AddValue(5, banlist); DB.Characters.Execute(stmt); - - Log.outDebug(LogFilter.ChatSystem, "Channel({0}) updated in database", _channelName); } - } - - void UpdateChannelUseageInDB() - { - PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHANNEL_USAGE); - stmt.AddValue(0, _channelName); - stmt.AddValue(1, (uint)_channelTeam); - DB.Characters.Execute(stmt); - } - - public static void CleanOldChannelsInDB() - { - if (WorldConfig.GetIntValue(WorldCfg.PreserveCustomChannelDuration) > 0) + else if (_nextActivityUpdateTime <= now) { - PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_OLD_CHANNELS); - stmt.AddValue(0, (long)(WorldConfig.GetIntValue(WorldCfg.PreserveCustomChannelDuration) * Time.Day)); - DB.Characters.Execute(stmt); - - Log.outDebug(LogFilter.ChatSystem, "Cleaned out unused custom chat channels."); + if (!_playersStore.Empty()) + { + PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHANNEL_USAGE); + stmt.AddValue(0, _channelName); + stmt.AddValue(1, (uint)_channelTeam); + DB.Characters.Execute(stmt); + } } + else + return; + + _isDirty = false; + _nextActivityUpdateTime = now + RandomHelper.URand(1 * Time.Minute, 6 * Time.Minute) * Math.Max(1u, WorldConfig.GetUIntValue(WorldCfg.PreserveCustomChannelInterval)); } public void JoinChannel(Player player, string pass = "") @@ -188,6 +181,8 @@ namespace Game.Chat } bool newChannel = _playersStore.Empty(); + if (newChannel) + _nextActivityUpdateTime = 0; // force activity update on next channel tick PlayerInfo playerInfo = new(); playerInfo.SetInvisible(!player.IsGMVisible()); @@ -205,10 +200,6 @@ namespace Game.Chat // Custom channel handling if (!IsConstant()) { - // Update last_used timestamp in db - if (!_playersStore.Empty()) - UpdateChannelUseageInDB(); - // If the channel has no owner yet and ownership is allowed, set the new owner. // or if the owner was a GM with .gm visible off // don't do this if the new player is, too, an invis GM, unless the channel was empty @@ -261,9 +252,6 @@ namespace Game.Chat if (!IsConstant()) { - // Update last_used timestamp in db - UpdateChannelUseageInDB(); - // If the channel owner left and there are still playersStore inside, pick a new owner // do not pick invisible gm owner unless there are only invisible gms in that channel (rare) if (changeowner && _ownershipEnabled && !_playersStore.Empty()) @@ -332,7 +320,7 @@ namespace Game.Chat if (ban && !IsBanned(victim)) { _bannedStore.Add(victim); - UpdateChannelInDB(); + _isDirty = true; if (!player.GetSession().HasPermission(RBACPermissions.SilentlyJoinChannel)) { @@ -391,7 +379,7 @@ namespace Game.Chat ChannelNameBuilder builder1 = new(this, new PlayerUnbannedAppend(good, victim)); SendToAll(builder1); - UpdateChannelInDB(); + _isDirty = true; } public void Password(Player player, string pass) @@ -418,7 +406,7 @@ namespace Game.Chat ChannelNameBuilder builder1 = new(this, new PasswordChangedAppend(guid)); SendToAll(builder1); - UpdateChannelInDB(); + _isDirty = true; } void SetMode(Player player, string p2n, bool mod, bool set) @@ -601,7 +589,7 @@ namespace Game.Chat SendToAll(builder); } - UpdateChannelInDB(); + _isDirty = true; } public void Say(ObjectGuid guid, string what, Language lang) @@ -741,7 +729,7 @@ namespace Game.Chat SendToAll(ownerBuilder); } - UpdateChannelInDB(); + _isDirty = true; } } @@ -864,6 +852,9 @@ namespace Game.Chat bool IsAnnounce() { return _announceEnabled; } public void SetAnnounce(bool announce) { _announceEnabled = announce; } + // will be saved to DB on next channel save interval + public void SetDirty() { _isDirty = true; } + public void SetPassword(string npassword) { _channelPassword = npassword; } public bool CheckPassword(string password) { return _channelPassword.IsEmpty() || (_channelPassword == password); } @@ -893,9 +884,11 @@ namespace Game.Chat return info != null ? info.GetFlags() : 0; } + bool _isDirty; // whether the channel needs to be saved to DB + long _nextActivityUpdateTime; + bool _announceEnabled; bool _ownershipEnabled; - bool _persistentChannel; bool _isOwnerInvisible; ChannelFlags _channelFlags; diff --git a/Source/Game/Chat/Channels/ChannelManager.cs b/Source/Game/Chat/Channels/ChannelManager.cs index 967573619..72cbd8bab 100644 --- a/Source/Game/Chat/Channels/ChannelManager.cs +++ b/Source/Game/Chat/Channels/ChannelManager.cs @@ -33,6 +33,70 @@ namespace Game.Chat _guidGenerator = new ObjectGuidGenerator(HighGuid.ChatChannel); } + /*static*/ + public static void LoadFromDB() + { + 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 = DB.Characters.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 = DB.Characters.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)) @@ -59,6 +123,12 @@ namespace Game.Chat 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()) @@ -86,15 +156,7 @@ namespace Game.Chat return null; Channel newChannel = new(CreateCustomChannelGuid(), name, _team); - - if (WorldConfig.GetBoolValue(WorldCfg.PreserveCustomChannels)) - { - PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHANNEL); - stmt.AddValue(0, name); - stmt.AddValue(1, (uint)_team); - DB.Characters.Execute(stmt); - Log.outDebug(LogFilter.ChatSystem, $"Channel({name}) saved in database"); - } + newChannel.SetDirty(); _customChannels[name.ToLower()] = newChannel; return newChannel; @@ -102,33 +164,7 @@ namespace Game.Chat public Channel GetCustomChannel(string name) { - string channelName = name.ToLower(); - if (_customChannels.TryGetValue(channelName, out Channel channel)) - return channel; - else if (WorldConfig.GetBoolValue(WorldCfg.PreserveCustomChannels)) - { - PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHANNEL); - stmt.AddValue(0, channelName); - stmt.AddValue(1, (uint)_team); - SQLResult result = DB.Characters.Query(stmt); - if (!result.IsEmpty()) - { - string dbName = result.Read(0); // may be different - channel names are case insensitive - bool dbAnnounce = result.Read(1); - bool dbOwnership = result.Read(2); - string dbPass = result.Read(3); - string dbBanned = result.Read(4); - - channel = new Channel(CreateCustomChannelGuid(), dbName, _team, dbBanned); - channel.SetAnnounce(dbAnnounce); - channel.SetOwnership(dbOwnership); - channel.SetPassword(dbPass); - _customChannels.Add(channelName, channel); - return channel; - } - } - - return null; + return _customChannels.LookupByKey(name.ToLower()); } public Channel GetChannel(uint channelId, string name, Player player, bool notify = true, AreaTableRecord zoneEntry = null) diff --git a/Source/Game/Server/WorldConfig.cs b/Source/Game/Server/WorldConfig.cs index 7b88a7c21..f64ea2a4f 100644 --- a/Source/Game/Server/WorldConfig.cs +++ b/Source/Game/Server/WorldConfig.cs @@ -224,6 +224,7 @@ namespace Game Values[WorldCfg.MailLevelReq] = GetDefaultValue("LevelReq.Mail", 1); Values[WorldCfg.PreserveCustomChannels] = GetDefaultValue("PreserveCustomChannels", false); Values[WorldCfg.PreserveCustomChannelDuration] = GetDefaultValue("PreserveCustomChannelDuration", 14); + Values[WorldCfg.PreserveCustomChannelInterval] = GetDefaultValue("PreserveCustomChannelInterval", 5); Values[WorldCfg.GridUnload] = GetDefaultValue("GridUnload", true); Values[WorldCfg.BasemapLoadGrids] = GetDefaultValue("BaseMapLoadAllGrids", false); if ((bool)Values[WorldCfg.BasemapLoadGrids] && (bool)Values[WorldCfg.GridUnload]) diff --git a/Source/Game/Server/WorldManager.cs b/Source/Game/Server/WorldManager.cs index d2e4c95a2..6e12a1dab 100644 --- a/Source/Game/Server/WorldManager.cs +++ b/Source/Game/Server/WorldManager.cs @@ -1005,6 +1005,8 @@ namespace Game m_timers[WorldTimers.WhoList].SetInterval(5 * Time.InMilliseconds); // update who list cache every 5 seconds + m_timers[WorldTimers.ChannelSave].SetInterval(WorldConfig.GetIntValue(WorldCfg.PreserveCustomChannelInterval) * Time.Minute * Time.InMilliseconds); + //to set mailtimer to return mails every day between 4 and 5 am //mailtimer is increased when updating auctions //one second is 1000 -(tested on win system) @@ -1027,8 +1029,8 @@ namespace Game // Delete all characters which have been deleted X days before Player.DeleteOldCharacters(); - // Delete all custom channels which haven't been used for PreserveCustomChannelDuration days. - Channel.CleanOldChannelsInDB(); + Log.outInfo(LogFilter.ServerLoading, "Initializing chat channels..."); + ChannelManager.LoadFromDB(); Log.outInfo(LogFilter.ServerLoading, "Initializing Opcodes..."); PacketManager.Initialize(); @@ -1291,6 +1293,20 @@ namespace Game Global.WhoListStorageMgr.Update(); } + if (m_timers[WorldTimers.ChannelSave].Passed()) + { + m_timers[WorldTimers.ChannelSave].Reset(); + + if (WorldConfig.GetBoolValue(WorldCfg.PreserveCustomChannels)) + { + ChannelManager mgr1 = ChannelManager.ForTeam(Team.Alliance); + mgr1.SaveToDB(); + ChannelManager mgr2 = ChannelManager.ForTeam(Team.Horde); + if (mgr1 != mgr2) + mgr2.SaveToDB(); + } + } + // Handle daily quests reset time if (currentGameTime > m_NextDailyQuestReset) { @@ -2514,6 +2530,7 @@ namespace Game GuildSave, Blackmarket, WhoList, + ChannelSave, Max } diff --git a/Source/WorldServer/WorldServer.conf.dist b/Source/WorldServer/WorldServer.conf.dist index 734599fa5..cd1be7856 100644 --- a/Source/WorldServer/WorldServer.conf.dist +++ b/Source/WorldServer/WorldServer.conf.dist @@ -1908,6 +1908,13 @@ PartyLevelReq = 1 PreserveCustomChannels = 1 +# +# PreserveCustomChannelInterval +# Description: Interval (in minutes) at which custom channel data is saved to the database +# Default: 5 minutes + +PreserveCustomChannelInterval = 5 + # # PreserveCustomChannelDuration # Description: Time (in days) that needs to pass before the customs chat channels get