AzerothCore 3.3.5a
OpenSource WoW Emulator
Loading...
Searching...
No Matches
mail_commandscript Class Reference
Inheritance diagram for mail_commandscript:
CommandScript ScriptObject

Public Member Functions

 mail_commandscript ()
 
ChatCommandTable GetCommands () const override
 
- Public Member Functions inherited from ScriptObject
virtual bool IsDatabaseBound () const
 
virtual bool isAfterLoadScript () const
 
virtual void checkValidity ()
 
std::string const & GetName () const
 
uint16 GetTotalAvailableHooks ()
 

Static Public Member Functions

static char const * GetMailTypeString (uint8 messageType)
 
static char const * GetMailStationeryString (uint8 stationery)
 
static std::string GetItemListString (std::vector< MailItemInfo > const &items)
 
static bool HandleMailListCommand (ChatHandler *handler, Optional< PlayerIdentifier > target)
 
static bool HandleMailReturnCommand (ChatHandler *handler, PlayerIdentifier target, uint32 mailId)
 

Additional Inherited Members

- Protected Member Functions inherited from CommandScript
 CommandScript (char const *name)
 
- Protected Member Functions inherited from ScriptObject
 ScriptObject (char const *name, uint16 totalAvailableHooks=0)
 
virtual ~ScriptObject ()=default
 

Detailed Description

Constructor & Destructor Documentation

◆ mail_commandscript()

mail_commandscript::mail_commandscript ( )
inline
39: CommandScript("mail_commandscript") { }
Definition CommandScript.h:25

Member Function Documentation

◆ GetCommands()

ChatCommandTable mail_commandscript::GetCommands ( ) const
inlineoverridevirtual

Implements CommandScript.

42 {
43 static ChatCommandTable mailCommandTable =
44 {
47 };
48
49 static ChatCommandTable commandTable =
50 {
51 { "mail", mailCommandTable }
52 };
53
54 return commandTable;
55 }
static bool HandleMailReturnCommand(ChatHandler *handler, PlayerIdentifier target, uint32 mailId)
Definition cs_mail.cpp:190
static bool HandleMailListCommand(ChatHandler *handler, Optional< PlayerIdentifier > target)
Definition cs_mail.cpp:101
std::vector< ChatCommandBuilder > ChatCommandTable
Definition ChatCommand.h:46
@ RBAC_PERM_COMMAND_MAIL_LIST
Definition RBAC.h:694
@ RBAC_PERM_COMMAND_MAIL_RETURN
Definition RBAC.h:695

References HandleMailListCommand(), HandleMailReturnCommand(), rbac::RBAC_PERM_COMMAND_MAIL_LIST, and rbac::RBAC_PERM_COMMAND_MAIL_RETURN.

◆ GetItemListString()

static std::string mail_commandscript::GetItemListString ( std::vector< MailItemInfo > const &  items)
inlinestatic
86 {
87 if (items.empty())
88 return "none";
89
90 std::string result;
91 for (size_t i = 0; i < items.size(); ++i)
92 {
93 if (i > 0)
94 result += ", ";
95
96 result += Acore::StringFormat("{}(guid:{})", items[i].item_template, items[i].item_guid);
97 }
98 return result;
99 }
std::string StringFormat(FormatStringView fmt, Args &&... args)
Default AC string format function.
Definition StringFormat.h:44

References Acore::StringFormat().

Referenced by HandleMailListCommand().

◆ GetMailStationeryString()

static char const * mail_commandscript::GetMailStationeryString ( uint8  stationery)
inlinestatic
71 {
72 switch (stationery)
73 {
74 case MAIL_STATIONERY_TEST: return "Test";
75 case MAIL_STATIONERY_DEFAULT: return "Default";
76 case MAIL_STATIONERY_GM: return "GM";
77 case MAIL_STATIONERY_AUCTION: return "Auction";
78 case MAIL_STATIONERY_VAL: return "Valentine";
79 case MAIL_STATIONERY_CHR: return "Christmas";
80 case MAIL_STATIONERY_ORP: return "Orphan";
81 default: return "Unknown";
82 }
83 }
@ MAIL_STATIONERY_TEST
Definition Mail.h:57
@ MAIL_STATIONERY_GM
Definition Mail.h:59
@ MAIL_STATIONERY_CHR
Definition Mail.h:62
@ MAIL_STATIONERY_ORP
Definition Mail.h:63
@ MAIL_STATIONERY_DEFAULT
Definition Mail.h:58
@ MAIL_STATIONERY_VAL
Definition Mail.h:61
@ MAIL_STATIONERY_AUCTION
Definition Mail.h:60

References MAIL_STATIONERY_AUCTION, MAIL_STATIONERY_CHR, MAIL_STATIONERY_DEFAULT, MAIL_STATIONERY_GM, MAIL_STATIONERY_ORP, MAIL_STATIONERY_TEST, and MAIL_STATIONERY_VAL.

Referenced by HandleMailListCommand().

◆ GetMailTypeString()

static char const * mail_commandscript::GetMailTypeString ( uint8  messageType)
inlinestatic
58 {
59 switch (messageType)
60 {
61 case MAIL_NORMAL: return "Normal";
62 case MAIL_AUCTION: return "Auction";
63 case MAIL_CREATURE: return "Creature";
64 case MAIL_GAMEOBJECT: return "GameObject";
65 case MAIL_CALENDAR: return "Calendar";
66 default: return "Unknown";
67 }
68 }
@ MAIL_AUCTION
Definition Mail.h:38
@ MAIL_GAMEOBJECT
Definition Mail.h:40
@ MAIL_CREATURE
Definition Mail.h:39
@ MAIL_CALENDAR
Definition Mail.h:41
@ MAIL_NORMAL
Definition Mail.h:37

References MAIL_AUCTION, MAIL_CALENDAR, MAIL_CREATURE, MAIL_GAMEOBJECT, and MAIL_NORMAL.

Referenced by HandleMailListCommand().

◆ HandleMailListCommand()

static bool mail_commandscript::HandleMailListCommand ( ChatHandler handler,
Optional< PlayerIdentifier target 
)
inlinestatic
102 {
103 if (!target)
104 target = PlayerIdentifier::FromTargetOrSelf(handler);
105
106 if (!target)
107 return false;
108
109 Player* player = target->GetConnectedPlayer();
110
111 if (player)
112 {
113 PlayerMails const& mails = player->GetMails();
114
115 if (mails.empty())
116 {
117 handler->PSendSysMessage(LANG_MAIL_LIST_EMPTY, handler->playerLink(target->GetName()));
118 return true;
119 }
120
121 handler->PSendSysMessage(LANG_MAIL_LIST_HEADER, handler->playerLink(target->GetName()));
122
123 for (Mail const* mail : mails)
124 {
125 std::string senderName;
126 if (mail->messageType == MAIL_NORMAL)
127 sCharacterCache->GetCharacterNameByGuid(ObjectGuid(HighGuid::Player, mail->sender), senderName);
128
129 std::string expireStr = Acore::Time::TimeToTimestampStr(Seconds(mail->expire_time));
130 std::string deliverStr = Acore::Time::TimeToTimestampStr(Seconds(mail->deliver_time));
131
133 mail->messageID, GetMailTypeString(mail->messageType), GetMailStationeryString(mail->stationery),
134 mail->mailTemplateId, mail->sender, senderName, mail->receiver,
135 expireStr, deliverStr, mail->money, mail->COD, mail->checked, GetItemListString(mail->items));
136 }
137 }
138 else
139 {
140 ObjectGuid::LowType lowGuid = target->GetGUID().GetCounter();
141
142 QueryResult result = CharacterDatabase.Query(
143 "SELECT id, messageType, sender, receiver, subject, body, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId"
144 " FROM mail WHERE receiver = {} AND deliver_time <= {} ORDER BY id DESC",
145 lowGuid, GameTime::GetGameTime().count());
146
147 if (!result)
148 {
149 handler->PSendSysMessage(LANG_MAIL_LIST_EMPTY, handler->playerLink(target->GetName()));
150 return true;
151 }
152
153 handler->PSendSysMessage(LANG_MAIL_LIST_HEADER, handler->playerLink(target->GetName()));
154
155 do
156 {
157 Field* fields = result->Fetch();
158 uint32 messageID = fields[0].Get<uint32>();
159 uint8 messageType = fields[1].Get<uint8>();
160 uint32 sender = fields[2].Get<uint32>();
161 uint32 receiver = fields[3].Get<uint32>();
162 // fields[4] = subject (skipped)
163 // fields[5] = body (skipped)
164 uint32 expireTime = fields[6].Get<uint32>();
165 uint32 deliverTime = fields[7].Get<uint32>();
166 uint32 money = fields[8].Get<uint32>();
167 uint32 cod = fields[9].Get<uint32>();
168 uint32 checked = fields[10].Get<uint32>();
169 uint8 stationery = fields[11].Get<uint8>();
170 uint16 mailTemplate = fields[12].Get<uint16>();
171
172 std::string senderName;
173 if (messageType == MAIL_NORMAL)
174 sCharacterCache->GetCharacterNameByGuid(ObjectGuid(HighGuid::Player, sender), senderName);
175
176 std::string expireStr = Acore::Time::TimeToTimestampStr(Seconds(expireTime));
177 std::string deliverStr = Acore::Time::TimeToTimestampStr(Seconds(deliverTime));
178
179 // For offline players we don't have item details loaded
181 messageID, GetMailTypeString(messageType), GetMailStationeryString(stationery),
182 mailTemplate, sender, senderName, receiver,
183 expireStr, deliverStr, money, cod, checked, "N/A (offline)");
184 } while (result->NextRow());
185 }
186
187 return true;
188 }
#define sCharacterCache
Definition CharacterCache.h:88
std::shared_ptr< ResultSet > QueryResult
Definition DatabaseEnvFwd.h:27
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
Definition DatabaseEnv.cpp:21
std::uint8_t uint8
Definition Define.h:109
std::uint32_t uint32
Definition Define.h:107
std::uint16_t uint16
Definition Define.h:108
std::chrono::seconds Seconds
Seconds shorthand typedef.
Definition Duration.h:30
@ LANG_MAIL_LIST_ENTRY
Definition Language.h:1223
@ LANG_MAIL_LIST_HEADER
Definition Language.h:1222
@ LANG_MAIL_LIST_EMPTY
Definition Language.h:1224
std::deque< Mail * > PlayerMails
Definition Player.h:68
std::string playerLink(std::string const &name) const
Definition Chat.h:231
void PSendSysMessage(std::string_view str, bool escapeCharacters=false)
Definition Chat.cpp:219
Class used to access individual fields of database query result.
Definition Field.h:98
std::enable_if_t< std::is_arithmetic_v< T >, T > Get() const
Definition Field.h:112
Definition ObjectGuid.h:121
uint32 LowType
Definition ObjectGuid.h:125
Definition Player.h:1086
PlayerMails const & GetMails() const
Definition Player.h:1677
static std::string GetItemListString(std::vector< MailItemInfo > const &items)
Definition cs_mail.cpp:85
static char const * GetMailTypeString(uint8 messageType)
Definition cs_mail.cpp:57
static char const * GetMailStationeryString(uint8 stationery)
Definition cs_mail.cpp:70
AC_COMMON_API std::string TimeToTimestampStr(Seconds time=0s, std::string_view fmt={})
Definition Timer.cpp:272
Seconds GetGameTime()
Definition GameTime.cpp:38
static Optional< PlayerIdentifier > FromTargetOrSelf(ChatHandler *handler)
Definition ChatCommandTags.h:189
Definition Mail.h:167

References CharacterDatabase, Acore::ChatCommands::PlayerIdentifier::FromTargetOrSelf(), Field::Get(), GameTime::GetGameTime(), GetItemListString(), Player::GetMails(), GetMailStationeryString(), GetMailTypeString(), LANG_MAIL_LIST_EMPTY, LANG_MAIL_LIST_ENTRY, LANG_MAIL_LIST_HEADER, MAIL_NORMAL, Player, ChatHandler::playerLink(), ChatHandler::PSendSysMessage(), sCharacterCache, and Acore::Time::TimeToTimestampStr().

Referenced by GetCommands().

◆ HandleMailReturnCommand()

static bool mail_commandscript::HandleMailReturnCommand ( ChatHandler handler,
PlayerIdentifier  target,
uint32  mailId 
)
inlinestatic
191 {
192 // Query mail data from DB so this works for offline players
193 QueryResult result = CharacterDatabase.Query(
194 "SELECT messageType, sender, receiver, subject, body, money, mailTemplateId, checked, deliver_time"
195 " FROM mail WHERE id = {}", mailId);
196
197 if (!result)
198 {
200 return true;
201 }
202
203 Field* fields = result->Fetch();
204 uint8 messageType = fields[0].Get<uint8>();
205 uint32 sender = fields[1].Get<uint32>();
206 uint32 receiver = fields[2].Get<uint32>();
207 std::string subject = fields[3].Get<std::string>();
208 std::string body = fields[4].Get<std::string>();
209 uint32 money = fields[5].Get<uint32>();
210 uint16 mailTemplate = fields[6].Get<uint16>();
211 uint32 checked = fields[7].Get<uint32>();
212 uint32 deliverTime = fields[8].Get<uint32>();
213
214 // Reject undelivered mail, same as the core handler
215 if (deliverTime > GameTime::GetGameTime().count())
216 {
218 return true;
219 }
220
221 // Verify the mail belongs to the target player
222 if (receiver != target.GetGUID().GetCounter())
223 {
225 return true;
226 }
227
228 if (messageType != MAIL_NORMAL)
229 {
231 return true;
232 }
233
234 if (!sender)
235 {
237 return true;
238 }
239
240 if (checked & MAIL_CHECK_MASK_RETURNED)
241 {
243 return true;
244 }
245
246 Player* player = target.GetConnectedPlayer();
247
248 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
249
250 // Collect attachments before the script-hook check and any deletion, so a hook
251 // veto can still abort the command with nothing changed.
252 // mail_items is flushed to the DB on every item take, so it is authoritative even
253 // for online players; mails the session never loaded (expired at login, delivered
254 // mid-login, inserted externally) are returned with their items too. Same query
255 // shape as CHAR_SEL_MAILITEMS (LEFT JOIN to handle dangling mail_items) and same
256 // logic as Player::_LoadMailedItem. Items the session already has loaded are
257 // reused and stay owned by the session; the bool marks objects loaded (and owned)
258 // here.
259 std::vector<std::pair<Item*, bool /*loadedFromDb*/>> attachments;
260
261 QueryResult itemResult = CharacterDatabase.Query(
262 "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments,"
263 " randomPropertyId, durability, playedTime, text, mi.item_guid, itemEntry, ii.owner_guid"
264 " FROM mail_items mi LEFT JOIN item_instance ii ON mi.item_guid = ii.guid"
265 " WHERE mi.mail_id = {}", mailId);
266
267 if (itemResult)
268 {
269 do
270 {
271 Field* itemFields = itemResult->Fetch();
272 uint32 itemGuid = itemFields[11].Get<uint32>();
273 uint32 itemEntry = itemFields[12].Get<uint32>();
274
275 // Prefer the item object the session already has loaded over creating a duplicate
276 if (Item* item = player ? player->GetMItem(itemGuid) : nullptr)
277 {
278 attachments.emplace_back(item, false);
279 continue;
280 }
281
282 // Handle dangling mail_items (missing item_instance)
283 if (!itemEntry)
284 {
285 LOG_ERROR("misc", "cs_mail: Mail #{} has dangling mail_items row for item_guid {}. Cleaning up.", mailId, itemGuid);
286
288 delStmt->SetData(0, itemGuid);
289 trans->Append(delStmt);
290 continue;
291 }
292
293 ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);
294 if (!proto)
295 {
296 LOG_ERROR("misc", "cs_mail: Mail #{} has unknown item (entry: {}, guid: {}). Cleaning up.", mailId, itemEntry, itemGuid);
297
299 delStmt->SetData(0, itemGuid);
300 trans->Append(delStmt);
301
302 // The mail is going away, so drop the unloadable item_instance row too
303 Item::DeleteFromDB(trans, itemGuid);
304 continue;
305 }
306
307 Item* item = NewItemOrBag(proto);
308 ObjectGuid ownerGuid = itemFields[13].Get<uint32>()
309 ? ObjectGuid::Create<HighGuid::Player>(itemFields[13].Get<uint32>())
310 : ObjectGuid::Empty;
311
312 if (!item->LoadFromDB(itemGuid, ownerGuid, itemFields, itemEntry))
313 {
314 LOG_ERROR("misc", "cs_mail: Item (GUID: {}) in mail #{} failed to load. Cleaning up.", itemGuid, mailId);
315
317 delStmt->SetData(0, itemGuid);
318 trans->Append(delStmt);
319
320 // Queue the row cleanup on the shared transaction instead of executing it
321 // immediately; SaveToDB in ITEM_REMOVED state also frees the object
322 item->FSetState(ITEM_REMOVED);
323 item->SaveToDB(trans);
324 continue;
325 }
326
327 attachments.emplace_back(item, true);
328 } while (itemResult->NextRow());
329 }
330
331 // Run the same script hook as the client return handler for online targets,
332 // covering DB-loaded attachments as well, before any deletion
333 if (player)
334 {
335 ObjectGuid senderGuid = ObjectGuid(HighGuid::Player, sender);
336 bool blocked = false;
337
338 if (attachments.empty())
339 blocked = !sScriptMgr->OnPlayerCanSendMail(player, senderGuid, ObjectGuid::Empty, subject, body, money, 0, nullptr);
340
341 for (auto const& [item, loadedFromDb] : attachments)
342 {
343 if (blocked)
344 break;
345
346 blocked = !sScriptMgr->OnPlayerCanSendMail(player, senderGuid, ObjectGuid::Empty, subject, body, money, 0, item);
347 }
348
349 if (blocked)
350 {
351 for (auto const& [item, loadedFromDb] : attachments)
352 if (loadedFromDb)
353 delete item;
354
356 return true;
357 }
358 }
359
360 // Same logic as WorldSession::HandleReturnToSender
362 stmt->SetData(0, mailId);
363 trans->Append(stmt);
364
365 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
366 stmt->SetData(0, mailId);
367 trans->Append(stmt);
368
369 MailDraft draft(subject, body);
370 if (mailTemplate)
371 draft = MailDraft(mailTemplate, false);
372
373 if (player)
374 {
375 // Drop the mail from the session's loaded state if present.
376 // RemoveMail only erases from the deque, it does not delete the object.
377 Mail* m = player->GetMail(mailId);
378 player->RemoveMail(mailId);
379 delete m;
380 }
381
382 for (auto const& [item, loadedFromDb] : attachments)
383 {
384 if (!loadedFromDb)
385 player->RemoveMItem(item->GetGUID().GetCounter());
386
387 draft.AddItem(item);
388 }
389
390 uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(ObjectGuid(HighGuid::Player, receiver));
391 draft.AddMoney(money).SendReturnToSender(accountId, receiver, sender, trans);
392
393 CharacterDatabase.CommitTransaction(trans);
394
395 sMailMgr->OnMailDeleted(receiver);
396
397 handler->PSendSysMessage(LANG_MAIL_RETURN_SUCCESS, mailId, handler->playerLink(target.GetName()));
398 return true;
399 }
Item * NewItemOrBag(ItemTemplate const *proto)
Definition Bag.h:67
@ CHAR_DEL_INVALID_MAIL_ITEM
Definition CharacterDatabase.h:113
@ CHAR_DEL_MAIL_BY_ID
Definition CharacterDatabase.h:110
@ CHAR_DEL_MAIL_ITEM_BY_ID
Definition CharacterDatabase.h:386
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
Definition DatabaseEnvFwd.h:69
@ ITEM_REMOVED
Definition Item.h:212
@ LANG_MAIL_RETURN_NOT_NORMAL
Definition Language.h:1227
@ LANG_MAIL_RETURN_ALREADY_RETURNED
Definition Language.h:1229
@ LANG_MAIL_RETURN_HOOK_BLOCKED
Definition Language.h:1230
@ LANG_MAIL_RETURN_NO_SENDER
Definition Language.h:1228
@ LANG_MAIL_RETURN_NOT_FOUND
Definition Language.h:1226
@ LANG_MAIL_RETURN_SUCCESS
Definition Language.h:1225
#define LOG_ERROR(filterType__,...)
Definition Log.h:145
#define sMailMgr
Definition MailMgr.h:85
@ MAIL_CHECK_MASK_RETURNED
Definition Mail.h:48
#define sObjectMgr
Definition ObjectMgr.h:1730
#define sScriptMgr
Definition ScriptMgr.h:743
void SendErrorMessage(uint32 entry)
Definition Chat.cpp:224
Definition Item.h:220
virtual bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field *fields, uint32 entry)
Definition Item.cpp:420
virtual void SaveToDB(CharacterDatabaseTransaction trans)
Definition Item.cpp:337
void FSetState(ItemUpdateState state)
Definition Item.h:330
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
Definition Item.cpp:519
Definition Mail.h:119
LowType GetCounter() const
Definition ObjectGuid.h:149
static ObjectGuid const Empty
Definition ObjectGuid.h:123
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:114
void RemoveMail(uint32 id)
Definition Player.cpp:2943
bool RemoveMItem(ObjectGuid::LowType itemLowGuid)
Definition Player.h:1706
Item * GetMItem(ObjectGuid::LowType itemLowGuid)
Definition Player.h:1693
Mail * GetMail(uint32 id)
Definition Player.cpp:3965
Acore::Types::is_default< T > SetData(const uint8 index, T value)
Definition PreparedStatement.h:77
Definition PreparedStatement.h:157
ObjectGuid GetGUID() const
Definition ChatCommandTags.h:181
std::string const & GetName() const
Definition ChatCommandTags.h:180
Player * GetConnectedPlayer() const
Definition ChatCommandTags.h:183
Definition ItemTemplate.h:619

References MailDraft::AddItem(), MailDraft::AddMoney(), CHAR_DEL_INVALID_MAIL_ITEM, CHAR_DEL_MAIL_BY_ID, CHAR_DEL_MAIL_ITEM_BY_ID, CharacterDatabase, Item::DeleteFromDB(), ObjectGuid::Empty, Item::FSetState(), Field::Get(), Acore::ChatCommands::PlayerIdentifier::GetConnectedPlayer(), ObjectGuid::GetCounter(), GameTime::GetGameTime(), Acore::ChatCommands::PlayerIdentifier::GetGUID(), Player::GetMail(), Player::GetMItem(), Acore::ChatCommands::PlayerIdentifier::GetName(), ITEM_REMOVED, LANG_MAIL_RETURN_ALREADY_RETURNED, LANG_MAIL_RETURN_HOOK_BLOCKED, LANG_MAIL_RETURN_NO_SENDER, LANG_MAIL_RETURN_NOT_FOUND, LANG_MAIL_RETURN_NOT_NORMAL, LANG_MAIL_RETURN_SUCCESS, Item::LoadFromDB(), LOG_ERROR, MAIL_CHECK_MASK_RETURNED, MAIL_NORMAL, NewItemOrBag(), Player, ChatHandler::playerLink(), ChatHandler::PSendSysMessage(), Player::RemoveMail(), Player::RemoveMItem(), Item::SaveToDB(), sCharacterCache, ChatHandler::SendErrorMessage(), MailDraft::SendReturnToSender(), PreparedStatementBase::SetData(), sMailMgr, sObjectMgr, and sScriptMgr.

Referenced by GetCommands().


The documentation for this class was generated from the following file: