diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs
index f043a3ad9..9c0ba2e31 100644
--- a/Source/Framework/Constants/SharedConst.cs
+++ b/Source/Framework/Constants/SharedConst.cs
@@ -142,11 +142,19 @@ namespace Framework.Constants
public const uint VoidStorageMaxWithdraw = 9;
public const byte VoidStorageMaxSlot = 160;
+ ///
+ /// Calendar Const
+ ///
+ public const uint CalendarMaxEvents = 30;
+ public const uint CalendarMaxGuildEvents = 100;
+ public const uint CalendarMaxInvites = 100;
+ public const uint CalendarCreateEventCooldown = 5;
+ public const uint CalendarOldEventsDeletionTime = 1 * Time.Month;
+ public const uint CalendarDefaultResponseTime = 946684800; // 01/01/2000 00:00:00
+
///
/// Misc Const
///
- public const uint CalendarMaxInvites = 100;
- public const uint CalendarDefaultResponseTime = 946684800; // 01/01/2000 00:00:00
public const Locale DefaultLocale = Locale.enUS;
public const int MaxAccountTutorialValues = 8;
public const int MinAuctionTime = (12 * Time.Hour);
@@ -892,7 +900,8 @@ namespace Framework.Constants
CleaningFlags = 20004, // Cleaning Flags
GuildDailyResetTime = 20006, // Next guild cap reset time
MonthlyQuestResetTime = 20007, // Next monthly quest reset time
- DailyQuestResetTime = 2008, // Next daily quest reset time
+ DailyQuestResetTime = 20008, // Next daily quest reset time
+ DailyCalendarDeletionOldEventsTime = 20009, // Next daily calendar deletions of old events time
// Cata specific custom worldstates
GuildWeeklyResetTime = 20050, // Next guild week reset time
}
@@ -1427,6 +1436,7 @@ namespace Framework.Constants
CacheDataQueries,
CalculateCreatureZoneAreaData,
CalculateGameobjectZoneAreaData,
+ CalendarDeleteOldEventsHour,
CastUnstuck,
CharacterCreatingDisableAlliedRaceAchievementRequirement,
CharacterCreatingDisabled,
diff --git a/Source/Game/Calendar/CalendarManager.cs b/Source/Game/Calendar/CalendarManager.cs
index f7cd10b6c..492901337 100644
--- a/Source/Game/Calendar/CalendarManager.cs
+++ b/Source/Game/Calendar/CalendarManager.cs
@@ -146,13 +146,24 @@ namespace Game
return;
}
+ RemoveEvent(calendarEvent, remover);
+ }
+
+ void RemoveEvent(CalendarEvent calendarEvent, ObjectGuid remover)
+ {
+ if (calendarEvent == null)
+ {
+ SendCalendarCommandResult(remover, CalendarError.EventInvalid);
+ return;
+ }
+
SendCalendarEventRemovedAlert(calendarEvent);
SQLTransaction trans = new();
PreparedStatement stmt;
MailDraft mail = new(calendarEvent.BuildCalendarMailSubject(remover), calendarEvent.BuildCalendarMailBody());
- var eventInvites = _invites[eventId];
+ var eventInvites = _invites[calendarEvent.EventId];
for (int i = 0; i < eventInvites.Count; ++i)
{
CalendarInvite invite = eventInvites[i];
@@ -166,10 +177,10 @@ namespace Game
mail.SendMailTo(trans, new MailReceiver(invite.InviteeGuid.GetCounter()), new MailSender(calendarEvent), MailCheckMask.Copied);
}
- _invites.Remove(eventId);
+ _invites.Remove(calendarEvent.EventId);
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CALENDAR_EVENT);
- stmt.AddValue(0, eventId);
+ stmt.AddValue(0, calendarEvent.EventId);
trans.Append(stmt);
DB.Characters.CommitTransaction(trans);
@@ -329,6 +340,42 @@ namespace Game
return inviteId;
}
+ public void DeleteOldEvents()
+ {
+ long oldEventsTime = GameTime.GetGameTime() - SharedConst.CalendarOldEventsDeletionTime;
+
+ foreach (var calendarEvent in _events)
+ {
+ if (calendarEvent.Date < oldEventsTime)
+ RemoveEvent(calendarEvent, ObjectGuid.Empty);
+ }
+ }
+
+ public List GetEventsCreatedBy(ObjectGuid guid, bool includeGuildEvents = false)
+ {
+ List result = new();
+ foreach (var calendarEvent in _events)
+ if (calendarEvent.OwnerGuid == guid && (includeGuildEvents || (!calendarEvent.IsGuildEvent() && !calendarEvent.IsGuildAnnouncement())))
+ result.Add(calendarEvent);
+
+ return result;
+ }
+
+ public List GetGuildEvents(ulong guildId)
+ {
+ List result = new();
+
+ if (guildId == 0)
+ return result;
+
+ foreach (var calendarEvent in _events)
+ if (calendarEvent.IsGuildEvent() || calendarEvent.IsGuildAnnouncement())
+ if (calendarEvent.GuildId == guildId)
+ result.Add(calendarEvent);
+
+ return result;
+ }
+
public List GetPlayerEvents(ObjectGuid guid)
{
List events = new();
@@ -342,8 +389,9 @@ namespace Game
events.Add(Event);
}
}
+
Player player = Global.ObjAccessor.FindPlayer(guid);
- if (player)
+ if (player?.GetGuildId() != 0)
{
foreach (var calendarEvent in _events)
if (calendarEvent.GuildId == player.GetGuildId())
@@ -643,6 +691,15 @@ namespace Game
public class CalendarInvite
{
+ public ulong InviteId { get; set; }
+ public ulong EventId { get; set; }
+ public ObjectGuid InviteeGuid { get; set; }
+ public ObjectGuid SenderGuid { get; set; }
+ public long ResponseTime { get; set; }
+ public CalendarInviteStatus Status { get; set; }
+ public CalendarModerationRank Rank { get; set; }
+ public string Note { get; set; }
+
public CalendarInvite()
{
InviteId = 1;
@@ -680,19 +737,21 @@ namespace Game
if (InviteId != 0 && EventId != 0)
Global.CalendarMgr.FreeInviteId(InviteId);
}
-
- public ulong InviteId { get; set; }
- public ulong EventId { get; set; }
- public ObjectGuid InviteeGuid { get; set; }
- public ObjectGuid SenderGuid { get; set; }
- public long ResponseTime { get; set; }
- public CalendarInviteStatus Status { get; set; }
- public CalendarModerationRank Rank { get; set; }
- public string Note { get; set; }
}
public class CalendarEvent
{
+ public ulong EventId { get; set; }
+ public ObjectGuid OwnerGuid { get; set; }
+ public ulong GuildId { get; set; }
+ public CalendarEventType EventType { get; set; }
+ public int TextureId { get; set; }
+ public long Date { get; set; }
+ public CalendarFlags Flags { get; set; }
+ public string Title { get; set; }
+ public string Description { get; set; }
+ public long LockDate { get; set; }
+
public CalendarEvent(CalendarEvent calendarEvent, ulong eventId)
{
EventId = eventId;
@@ -746,15 +805,7 @@ namespace Game
public bool IsGuildAnnouncement() { return Flags.HasAnyFlag(CalendarFlags.WithoutInvites); }
public bool IsLocked() { return Flags.HasAnyFlag(CalendarFlags.InvitesLocked); }
- public ulong EventId { get; set; }
- public ObjectGuid OwnerGuid { get; set; }
- public ulong GuildId { get; set; }
- public CalendarEventType EventType { get; set; }
- public int TextureId { get; set; }
- public long Date { get; set; }
- public CalendarFlags Flags { get; set; }
- public string Title { get; set; }
- public string Description { get; set; }
- public long LockDate { get; set; }
+ public static bool IsGuildEvent(uint flags) { return (flags & (uint)CalendarFlags.GuildEvent) != 0; }
+ public static bool IsGuildAnnouncement(uint flags) { return (flags & (uint)CalendarFlags.WithoutInvites) != 0; }
}
}
diff --git a/Source/Game/Handlers/CalendarHandler.cs b/Source/Game/Handlers/CalendarHandler.cs
index 5a70bc906..3caf5b56d 100644
--- a/Source/Game/Handlers/CalendarHandler.cs
+++ b/Source/Game/Handlers/CalendarHandler.cs
@@ -124,17 +124,51 @@ namespace Game
// prevent events in the past
// To Do: properly handle timezones and remove the "- time_t(86400L)" hack
if (calendarAddEvent.EventInfo.Time < (GameTime.GetGameTime() - 86400L))
+ {
+ Global.CalendarMgr.SendCalendarCommandResult(guid, CalendarError.EventPassed);
return;
+ }
+
+ // If the event is a guild event, check if the player is in a guild
+ if (CalendarEvent.IsGuildEvent(calendarAddEvent.EventInfo.Flags) || CalendarEvent.IsGuildAnnouncement(calendarAddEvent.EventInfo.Flags))
+ {
+ if (_player.GetGuildId() == 0)
+ {
+ Global.CalendarMgr.SendCalendarCommandResult(guid, CalendarError.GuildPlayerNotInGuild);
+ return;
+ }
+ }
+
+ // Check if the player reached the max number of events allowed to create
+ if (CalendarEvent.IsGuildEvent(calendarAddEvent.EventInfo.Flags) || CalendarEvent.IsGuildAnnouncement(calendarAddEvent.EventInfo.Flags))
+ {
+ if (Global.CalendarMgr.GetGuildEvents(_player.GetGuildId()).Count >= SharedConst.CalendarMaxGuildEvents)
+ {
+ Global.CalendarMgr.SendCalendarCommandResult(guid, CalendarError.GuildEventsExceeded);
+ return;
+ }
+ }
+ else
+ {
+ if (Global.CalendarMgr.GetEventsCreatedBy(guid).Count >= SharedConst.CalendarMaxEvents)
+ {
+ Global.CalendarMgr.SendCalendarCommandResult(guid, CalendarError.EventsExceeded);
+ return;
+ }
+ }
+
+ if (GetCalendarEventCreationCooldown() > GameTime.GetGameTime())
+ {
+ Global.CalendarMgr.SendCalendarCommandResult(guid, CalendarError.Internal);
+ return;
+ }
+ SetCalendarEventCreationCooldown(GameTime.GetGameTime() + SharedConst.CalendarCreateEventCooldown);
CalendarEvent calendarEvent = new(Global.CalendarMgr.GetFreeEventId(), guid, 0, (CalendarEventType)calendarAddEvent.EventInfo.EventType, calendarAddEvent.EventInfo.TextureID,
calendarAddEvent.EventInfo.Time, (CalendarFlags)calendarAddEvent.EventInfo.Flags, calendarAddEvent.EventInfo.Title, calendarAddEvent.EventInfo.Description, 0);
if (calendarEvent.IsGuildEvent() || calendarEvent.IsGuildAnnouncement())
- {
- Player creator = Global.ObjAccessor.FindPlayer(guid);
- if (creator)
- calendarEvent.GuildId = creator.GetGuildId();
- }
+ calendarEvent.GuildId = _player.GetGuildId();
if (calendarEvent.IsGuildAnnouncement())
{
@@ -209,11 +243,57 @@ namespace Game
// prevent events in the past
// To Do: properly handle timezones and remove the "- time_t(86400L)" hack
if (calendarCopyEvent.Date < (GameTime.GetGameTime() - 86400L))
+ {
+ Global.CalendarMgr.SendCalendarCommandResult(guid, CalendarError.EventPassed);
return;
-
+ }
+
CalendarEvent oldEvent = Global.CalendarMgr.GetEvent(calendarCopyEvent.EventID);
if (oldEvent != null)
{
+ // Ensure that the player has access to the event
+ if (oldEvent.IsGuildEvent() || oldEvent.IsGuildAnnouncement())
+ {
+ if (oldEvent.GuildId != _player.GetGuildId())
+ {
+ Global.CalendarMgr.SendCalendarCommandResult(guid, CalendarError.EventInvalid);
+ return;
+ }
+ }
+ else
+ {
+ if (oldEvent.OwnerGuid != guid)
+ {
+ Global.CalendarMgr.SendCalendarCommandResult(guid, CalendarError.EventInvalid);
+ return;
+ }
+ }
+
+ // Check if the player reached the max number of events allowed to create
+ if (oldEvent.IsGuildEvent() || oldEvent.IsGuildAnnouncement())
+ {
+ if (Global.CalendarMgr.GetGuildEvents(_player.GetGuildId()).Count >= SharedConst.CalendarMaxGuildEvents)
+ {
+ Global.CalendarMgr.SendCalendarCommandResult(guid, CalendarError.GuildEventsExceeded);
+ return;
+ }
+ }
+ else
+ {
+ if (Global.CalendarMgr.GetEventsCreatedBy(guid).Count >= SharedConst.CalendarMaxEvents)
+ {
+ Global.CalendarMgr.SendCalendarCommandResult(guid, CalendarError.EventsExceeded);
+ return;
+ }
+ }
+
+ if (GetCalendarEventCreationCooldown() > GameTime.GetGameTime())
+ {
+ Global.CalendarMgr.SendCalendarCommandResult(guid, CalendarError.Internal);
+ return;
+ }
+ SetCalendarEventCreationCooldown(GameTime.GetGameTime() + SharedConst.CalendarCreateEventCooldown);
+
CalendarEvent newEvent = new(oldEvent, Global.CalendarMgr.GetFreeEventId());
newEvent.Date = calendarCopyEvent.Date;
Global.CalendarMgr.AddEvent(newEvent, CalendarSendEventType.Copy);
diff --git a/Source/Game/Server/WorldManager.cs b/Source/Game/Server/WorldManager.cs
index f4f0d7424..f01f42f9a 100644
--- a/Source/Game/Server/WorldManager.cs
+++ b/Source/Game/Server/WorldManager.cs
@@ -1072,9 +1072,12 @@ namespace Game
InitQuestResetTimes();
CheckScheduledResetTimes();
- Log.outInfo(LogFilter.ServerLoading, "Calculate random Battlegroundreset time...");
+ Log.outInfo(LogFilter.ServerLoading, "Calculate random battleground reset time...");
InitRandomBGResetTime();
+ Log.outInfo(LogFilter.ServerLoading, "Calculate deletion of old calendar events time...");
+ InitCalendarOldEventsDeletionTime();
+
Log.outInfo(LogFilter.ServerLoading, "Calculate Guild cap reset time...");
InitGuildResetTime();
@@ -1310,6 +1313,9 @@ namespace Game
if (currentGameTime > m_NextRandomBGReset)
ResetRandomBG();
+ if (currentGameTime > m_NextCalendarOldEventsDeletionTime)
+ CalendarDeleteOldEvents();
+
if (currentGameTime > m_NextGuildReset)
ResetGuildCap();
@@ -2114,6 +2120,23 @@ namespace Game
SetWorldState(WorldStates.BGDailyResetTime, (ulong)m_NextRandomBGReset);
}
+ void InitCalendarOldEventsDeletionTime()
+ {
+ long now = GameTime.GetGameTime();
+ long nextDeletionTime = Time.GetLocalHourTimestamp(now, WorldConfig.GetUIntValue(WorldCfg.CalendarDeleteOldEventsHour));
+ long currentDeletionTime = GetWorldState(WorldStates.DailyCalendarDeletionOldEventsTime);
+
+ // If the reset time saved in the worldstate is before now it means the server was offline when the reset was supposed to occur.
+ // In this case we set the reset time in the past and next world update will do the reset and schedule next one in the future.
+ if (currentDeletionTime < now)
+ m_NextCalendarOldEventsDeletionTime = nextDeletionTime - Time.Day;
+ else
+ m_NextCalendarOldEventsDeletionTime = nextDeletionTime;
+
+ if (currentDeletionTime == 0)
+ SetWorldState(WorldStates.DailyCalendarDeletionOldEventsTime, (ulong)m_NextCalendarOldEventsDeletionTime);
+ }
+
void InitGuildResetTime()
{
long gtime = GetWorldState(WorldStates.GuildDailyResetTime);
@@ -2193,6 +2216,15 @@ namespace Game
SetWorldState(WorldStates.BGDailyResetTime, (ulong)m_NextRandomBGReset);
}
+ void CalendarDeleteOldEvents()
+ {
+ Log.outInfo(LogFilter.Misc, "Calendar deletion of old events.");
+
+ m_NextCalendarOldEventsDeletionTime = m_NextCalendarOldEventsDeletionTime + Time.Day;
+ SetWorldState(WorldStates.DailyCalendarDeletionOldEventsTime, (ulong)m_NextCalendarOldEventsDeletionTime);
+ Global.CalendarMgr.DeleteOldEvents();
+ }
+
void ResetGuildCap()
{
m_NextGuildReset += Time.Day;
@@ -2489,6 +2521,7 @@ namespace Game
long m_NextWeeklyQuestReset;
long m_NextMonthlyQuestReset;
long m_NextRandomBGReset;
+ long m_NextCalendarOldEventsDeletionTime;
long m_NextGuildReset;
long m_NextCurrencyReset;
diff --git a/Source/Game/Server/WorldSession.cs b/Source/Game/Server/WorldSession.cs
index f98153c17..681a755b1 100644
--- a/Source/Game/Server/WorldSession.cs
+++ b/Source/Game/Server/WorldSession.cs
@@ -890,6 +890,10 @@ namespace Game
public uint GetRecruiterId() { return recruiterId; }
public bool IsARecruiter() { return isRecruiter; }
+ // Packets cooldown
+ public long GetCalendarEventCreationCooldown() { return _calendarEventCreationCooldown; }
+ public void SetCalendarEventCreationCooldown(long cooldown) { _calendarEventCreationCooldown = cooldown; }
+
// Battle Pets
public BattlePetMgr GetBattlePetMgr() { return _battlePetMgr; }
public CollectionMgr GetCollectionMgr() { return _collectionMgr; }
@@ -967,6 +971,9 @@ namespace Game
ConnectToKey _instanceConnectKey;
+ // Packets cooldown
+ long _calendarEventCreationCooldown;
+
BattlePetMgr _battlePetMgr;
Task> _realmAccountLoginCallback;
diff --git a/Source/WorldServer/WorldServer.conf.dist b/Source/WorldServer/WorldServer.conf.dist
index 6f519c409..dd0885cd5 100644
--- a/Source/WorldServer/WorldServer.conf.dist
+++ b/Source/WorldServer/WorldServer.conf.dist
@@ -1034,6 +1034,14 @@ Quests.DailyResetTime = 3
Quests.WeeklyResetWDay = 3
+#
+# Calendar.DeleteOldEventsHour
+# Description: Hour of the day when the daily deletion of old calendar events occurs.
+# Range: 0-23
+# Default: 6 - (06:00 AM)
+
+Calendar.DeleteOldEventsHour = 6
+
#
# Guild.EventLogRecordsCount
# Description: Number of log entries for guild events that are stored per guild. Old entries