diff options
Diffstat (limited to 'src')
28 files changed, 3087 insertions, 1265 deletions
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 3f5a21f2888..e700a8b1708 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -554,7 +554,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTENDER = 647, RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUEST_LOOT_TEMPLATE = 648, RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTSTARTER = 649, - RBAC_PERM_COMMAND_RELOAD_GM_TICKETS = 650, + RBAC_PERM_COMMAND_RELOAD_SUPPORT_SYSTEM = 650, RBAC_PERM_COMMAND_RELOAD_GOSSIP_MENU = 651, RBAC_PERM_COMMAND_RELOAD_GOSSIP_MENU_OPTION = 652, RBAC_PERM_COMMAND_RELOAD_ITEM_ENCHANTMENT_TEMPLATE = 653, @@ -703,6 +703,41 @@ enum RBACPermissions RBAC_PERM_COMMAND_INSTANCE_GET_BOSS_STATE = 796, RBAC_PERM_COMMAND_PVPSTATS = 797, RBAC_PERM_COMMAND_MODIFY_XP = 798, + RBAC_PERM_COMMAND_GO_BUG_TICKET = 799, + RBAC_PERM_COMMAND_GO_COMPLAINT_TICKET = 800, + RBAC_PERM_COMMAND_GO_SUGGESTION_TICKET = 801, + RBAC_PERM_COMMAND_TICKET_BUG = 802, + RBAC_PERM_COMMAND_TICKET_COMPLAINT = 803, + RBAC_PERM_COMMAND_TICKET_SUGGESTION = 804, + RBAC_PERM_COMMAND_TICKET_BUG_ASSIGN = 805, + RBAC_PERM_COMMAND_TICKET_BUG_CLOSE = 806, + RBAC_PERM_COMMAND_TICKET_BUG_CLOSEDLIST = 807, + RBAC_PERM_COMMAND_TICKET_BUG_COMMENT = 808, + RBAC_PERM_COMMAND_TICKET_BUG_DELETE = 809, + RBAC_PERM_COMMAND_TICKET_BUG_LIST = 810, + RBAC_PERM_COMMAND_TICKET_BUG_UNASSIGN = 811, + RBAC_PERM_COMMAND_TICKET_BUG_VIEW = 812, + RBAC_PERM_COMMAND_TICKET_COMPLAINT_ASSIGN = 813, + RBAC_PERM_COMMAND_TICKET_COMPLAINT_CLOSE = 814, + RBAC_PERM_COMMAND_TICKET_COMPLAINT_CLOSEDLIST = 815, + RBAC_PERM_COMMAND_TICKET_COMPLAINT_COMMENT = 816, + RBAC_PERM_COMMAND_TICKET_COMPLAINT_DELETE = 817, + RBAC_PERM_COMMAND_TICKET_COMPLAINT_LIST = 818, + RBAC_PERM_COMMAND_TICKET_COMPLAINT_UNASSIGN = 819, + RBAC_PERM_COMMAND_TICKET_COMPLAINT_VIEW = 820, + RBAC_PERM_COMMAND_TICKET_SUGGESTION_ASSIGN = 821, + RBAC_PERM_COMMAND_TICKET_SUGGESTION_CLOSE = 822, + RBAC_PERM_COMMAND_TICKET_SUGGESTION_CLOSEDLIST = 823, + RBAC_PERM_COMMAND_TICKET_SUGGESTION_COMMENT = 824, + RBAC_PERM_COMMAND_TICKET_SUGGESTION_DELETE = 825, + RBAC_PERM_COMMAND_TICKET_SUGGESTION_LIST = 826, + RBAC_PERM_COMMAND_TICKET_SUGGESTION_UNASSIGN = 827, + RBAC_PERM_COMMAND_TICKET_SUGGESTION_VIEW = 828, + RBAC_PERM_COMMAND_TICKET_RESET_ALL = 829, + RBAC_PERM_COMMAND_TICKET_RESET_GM = 830, + RBAC_PERM_COMMAND_TICKET_RESET_BUG = 831, + RBAC_PERM_COMMAND_TICKET_RESET_COMPLAINT = 832, + RBAC_PERM_COMMAND_TICKET_RESET_SUGGESTION = 833, // custom permissions 1000+ RBAC_PERM_MAX diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt index 2773178da08..18c710c2a20 100644 --- a/src/server/game/CMakeLists.txt +++ b/src/server/game/CMakeLists.txt @@ -43,9 +43,9 @@ file(GLOB_RECURSE sources_Scripting Scripting/*.cpp Scripting/*.h) file(GLOB_RECURSE sources_Server Server/*.cpp Server/*.h) file(GLOB_RECURSE sources_Skills Skills/*.cpp Skills/*.h) file(GLOB_RECURSE sources_Spells Spells/*.cpp Spells/*.h) +file(GLOB_RECURSE sources_Support Support/*.cpp Support/*.h) file(GLOB_RECURSE sources_Texts Texts/*.cpp Texts/*.h) file(GLOB_RECURSE sources_Tools Tools/*.cpp Tools/*.h) -file(GLOB_RECURSE sources_Tickets Tickets/*.cpp Tickets/*.h) file(GLOB_RECURSE sources_Warden Warden/*.cpp Warden/*.h) file(GLOB_RECURSE sources_Weather Weather/*.cpp Weather/*.h) file(GLOB_RECURSE sources_World World/*.cpp World/*.h) @@ -94,9 +94,9 @@ set(game_STAT_SRCS ${sources_Server} ${sources_Skills} ${sources_Spells} + ${sources_Support} ${sources_Texts} ${sources_Tools} - ${sources_Tickets} ${sources_Warden} ${sources_Weather} ${sources_World} @@ -198,9 +198,9 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/Skills ${CMAKE_CURRENT_SOURCE_DIR}/Spells ${CMAKE_CURRENT_SOURCE_DIR}/Spells/Auras + ${CMAKE_CURRENT_SOURCE_DIR}/Support ${CMAKE_CURRENT_SOURCE_DIR}/Texts ${CMAKE_CURRENT_SOURCE_DIR}/Tools - ${CMAKE_CURRENT_SOURCE_DIR}/Tickets ${CMAKE_CURRENT_SOURCE_DIR}/Warden ${CMAKE_CURRENT_SOURCE_DIR}/Warden/Modules ${CMAKE_CURRENT_SOURCE_DIR}/Weather diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a25111df730..b46783deffb 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -3243,17 +3243,12 @@ void Player::SendMailResult(uint32 mailId, MailResponseType mailAction, MailResp result.Command = mailAction; result.ErrorCode = mailError; - switch (mailError) + if (mailError == MAIL_ERR_EQUIP_ERROR) + result.BagResult = equipError; + else if (mailAction == MAIL_ITEM_TAKEN) { - case MAIL_ERR_EQUIP_ERROR: - result.BagResult = equipError; - break; - case MAIL_ITEM_TAKEN: - result.AttachID = item_guid; - result.QtyInInventory = item_count; - break; - default: - break; + result.AttachID = item_guid; + result.QtyInInventory = item_count; } GetSession()->SendPacket(result.Write()); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index ba6b064d2f3..7a63d38f4fa 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1096,14 +1096,20 @@ void WorldSession::SendFeatureSystemStatus() features.BrowserEnabled = false; // Has to be false, otherwise client will crash if "Customer Support" is opened features.EuropaTicketSystemStatus.HasValue = true; - features.EuropaTicketSystemStatus.Value.ThrottleState.MaxTries = 5; - features.EuropaTicketSystemStatus.Value.ThrottleState.PerMilliseconds = 5; - features.EuropaTicketSystemStatus.Value.ThrottleState.TryCount = 0; - features.EuropaTicketSystemStatus.Value.ThrottleState.LastResetTimeBeforeNow = time(nullptr) - 5000; + features.EuropaTicketSystemStatus.Value.ThrottleState.MaxTries = 10; + features.EuropaTicketSystemStatus.Value.ThrottleState.PerMilliseconds = 60000; + features.EuropaTicketSystemStatus.Value.ThrottleState.TryCount = 1; + features.EuropaTicketSystemStatus.Value.ThrottleState.LastResetTimeBeforeNow = 111111; + features.ComplaintStatus = 0; + features.TutorialsEnabled = true; + features.UnkBit90 = true; /// END OF DUMMY VALUES - features.EuropaTicketSystemStatus.Value.SubmitBugEnabled = sWorld->getBoolConfig(CONFIG_TICKET_SUBMIT_BUG); - features.EuropaTicketSystemStatus.Value.TicketSystemEnabled = sWorld->getBoolConfig(CONFIG_TICKET_SUBMIT_TICKET); + features.EuropaTicketSystemStatus.Value.TicketsEnabled = sWorld->getBoolConfig(CONFIG_SUPPORT_TICKETS_ENABLED); + features.EuropaTicketSystemStatus.Value.BugsEnabled = sWorld->getBoolConfig(CONFIG_SUPPORT_BUGS_ENABLED); + features.EuropaTicketSystemStatus.Value.ComplaintsEnabled = sWorld->getBoolConfig(CONFIG_SUPPORT_COMPLAINTS_ENABLED); + features.EuropaTicketSystemStatus.Value.SuggestionsEnabled = sWorld->getBoolConfig(CONFIG_SUPPORT_SUGGESTIONS_ENABLED); + features.CharUndeleteEnabled = sWorld->getBoolConfig(CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_ENABLED); features.BpayStoreEnabled = sWorld->getBoolConfig(CONFIG_FEATURE_SYSTEM_BPAY_STORE_ENABLED); diff --git a/src/server/game/Handlers/TicketHandler.cpp b/src/server/game/Handlers/TicketHandler.cpp index 5b2e197ae7a..55bbe96f15c 100644 --- a/src/server/game/Handlers/TicketHandler.cpp +++ b/src/server/game/Handlers/TicketHandler.cpp @@ -22,17 +22,17 @@ #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" -#include "TicketMgr.h" +#include "SupportMgr.h" #include "TicketPackets.h" #include "Util.h" #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" -void WorldSession::HandleGMTicketCreateOpcode(WorldPacket& recvData) +void WorldSession::HandleGMTicketCreateOpcode(WorldPackets::Ticket::GMTicketCreate& packet) { // Don't accept tickets if the ticket queue is disabled. (Ticket UI is greyed out but not fully dependable) - if (sTicketMgr->GetStatus() == GMTICKET_QUEUE_STATUS_DISABLED) + if (sSupportMgr->GetSupportSystemStatus() == GMTICKET_QUEUE_STATUS_DISABLED) return; if (GetPlayer()->getLevel() < sWorld->getIntConfig(CONFIG_TICKET_LEVEL_REQ)) @@ -42,140 +42,101 @@ void WorldSession::HandleGMTicketCreateOpcode(WorldPacket& recvData) } GMTicketResponse response = GMTICKET_RESPONSE_CREATE_ERROR; - GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()); + GmTicket* ticket = sSupportMgr->GetGmTicketByPlayerGuid(GetPlayer()->GetGUID()); if (ticket && ticket->IsCompleted()) - sTicketMgr->CloseTicket(ticket->GetId(), GetPlayer()->GetGUID()); + sSupportMgr->CloseTicket<GmTicket>(ticket->GetId(), GetPlayer()->GetGUID()); // Player must not have ticket if (!ticket || ticket->IsClosed()) { - uint32 mapId; - float x, y, z; - std::string message; - uint32 needResponse; - bool needMoreHelp; - uint32 count; - std::list<uint32> times; - uint32 decompressedSize; std::string chatLog; - recvData >> mapId; - recvData >> x >> y >> z; - recvData >> message; - - recvData >> needResponse; - recvData >> needMoreHelp; - - recvData >> count; - - for (uint32 i = 0; i < count; i++) + if (packet.DataLength > 0 && packet.ChatHistoryData.DecompressedSize < 0xFFFF) { - uint32 time; - recvData >> time; - times.push_back(time); - } - - recvData >> decompressedSize; - - if (count && decompressedSize && decompressedSize < 0xFFFF) - { - uint32 pos = recvData.rpos(); ByteBuffer dest; - dest.resize(decompressedSize); + dest.resize(size_t(packet.ChatHistoryData.DecompressedSize)); - uLongf realSize = decompressedSize; - if (uncompress(dest.contents(), &realSize, recvData.contents() + pos, recvData.size() - pos) == Z_OK) - { + uLongf realSize = packet.ChatHistoryData.DecompressedSize; + + if (uncompress(dest.contents(), &realSize, packet.ChatHistoryData.Data.contents(), packet.ChatHistoryData.Data.size()) == Z_OK) dest >> chatLog; - } else { TC_LOG_ERROR("network", "CMSG_GM_TICKET_CREATE possibly corrupt. Uncompression failed."); - recvData.rfinish(); return; } - - recvData.rfinish(); // Will still have compressed data in buffer. } ticket = new GmTicket(GetPlayer()); - ticket->SetPosition(mapId, x, y, z); - ticket->SetMessage(message); - ticket->SetGmAction(needResponse, needMoreHelp); + ticket->SetPosition(packet.Map, packet.Pos); + ticket->SetDescription(packet.Description); + ticket->SetGmAction(packet.NeedResponse, packet.NeedMoreHelp); - if (!chatLog.empty()) - ticket->SetChatLog(times, chatLog); + //TODO: more reasearch needed + //if (!chatLog.empty()) + //ticket->SetChatLog(times, chatLog); - sTicketMgr->AddTicket(ticket); - sTicketMgr->UpdateLastChange(); + sSupportMgr->AddTicket(ticket); + sSupportMgr->UpdateLastChange(); sWorld->SendGMText(LANG_COMMAND_TICKETNEW, GetPlayer()->GetName().c_str(), ticket->GetId()); response = GMTICKET_RESPONSE_CREATE_SUCCESS; } - - WorldPacket data(SMSG_GM_TICKET_UPDATE, 4); - data << uint32(response); - SendPacket(&data); + sSupportMgr->SendGmTicketUpdate(this, response); } -void WorldSession::HandleGMTicketUpdateOpcode(WorldPacket& recvData) +void WorldSession::HandleGMTicketUpdateTextOpcode(WorldPackets::Ticket::GMTicketUpdateText& packet) { - std::string message; - recvData >> message; - GMTicketResponse response = GMTICKET_RESPONSE_UPDATE_ERROR; - if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID())) + if (GmTicket* ticket = sSupportMgr->GetGmTicketByPlayerGuid(GetPlayer()->GetGUID())) { SQLTransaction trans = SQLTransaction(NULL); - ticket->SetMessage(message); + ticket->SetDescription(packet.Description); ticket->SaveToDB(trans); sWorld->SendGMText(LANG_COMMAND_TICKETUPDATED, GetPlayer()->GetName().c_str(), ticket->GetId()); response = GMTICKET_RESPONSE_UPDATE_SUCCESS; } - - WorldPacket data(SMSG_GM_TICKET_UPDATE, 4); - data << uint32(response); - SendPacket(&data); + sSupportMgr->SendGmTicketUpdate(this, response); } -void WorldSession::HandleGMTicketDeleteOpcode(WorldPacket & /*recvData*/) +void WorldSession::HandleGMTicketDeleteOpcode(WorldPackets::Ticket::GMTicketDelete& /*packet*/) { - if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID())) + if (GmTicket* ticket = sSupportMgr->GetGmTicketByPlayerGuid(GetPlayer()->GetGUID())) { - WorldPacket data(SMSG_GM_TICKET_UPDATE, 4); - data << uint32(GMTICKET_RESPONSE_TICKET_DELETED); - SendPacket(&data); + sSupportMgr->SendGmTicketUpdate(this, GMTICKET_RESPONSE_TICKET_DELETED); sWorld->SendGMText(LANG_COMMAND_TICKETPLAYERABANDON, GetPlayer()->GetName().c_str(), ticket->GetId()); - sTicketMgr->CloseTicket(ticket->GetId(), GetPlayer()->GetGUID()); - sTicketMgr->SendTicket(this, NULL); + sSupportMgr->CloseTicket<GmTicket>(ticket->GetId(), GetPlayer()->GetGUID()); + sSupportMgr->SendGmTicket(this, NULL); } } void WorldSession::HandleGMTicketGetCaseStatusOpcode(WorldPackets::Ticket::GMTicketGetCaseStatus& /*packet*/) { - // TODO: Implement GmCase and handle this packet correctly + // TODO: Implement GmCase and handle this packet properly + WorldPackets::Ticket::GMTicketCaseStatus status; + status.OldestTicketTime = time(nullptr); + SendPacket(status.Write()); } void WorldSession::HandleGMTicketGetTicketOpcode(WorldPackets::Ticket::GMTicketGetTicket& /*packet*/) { SendQueryTimeResponse(); - if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID())) + if (GmTicket* ticket = sSupportMgr->GetGmTicketByPlayerGuid(GetPlayer()->GetGUID())) { if (ticket->IsCompleted()) - // TODO: Update SMSG_GM_TICKET_RESPONSE ticket->SendResponse(this); else - sTicketMgr->SendTicket(this, ticket); + sSupportMgr->SendGmTicket(this, ticket); } else - sTicketMgr->SendTicket(this, NULL); + sSupportMgr->SendGmTicket(this, NULL); } void WorldSession::HandleGMTicketSystemStatusOpcode(WorldPackets::Ticket::GMTicketGetSystemStatus& /*packet*/) @@ -183,100 +144,93 @@ void WorldSession::HandleGMTicketSystemStatusOpcode(WorldPackets::Ticket::GMTick // Note: This only disables the ticket UI at client side and is not fully reliable // Note: This disables the whole customer support UI after trying to send a ticket in disabled state (MessageBox: "GM Help Tickets are currently unavaiable."). UI remains disabled until the character relogs. WorldPackets::Ticket::GMTicketSystemStatus response; - response.Status = sTicketMgr->GetStatus() ? GMTICKET_QUEUE_STATUS_ENABLED : GMTICKET_QUEUE_STATUS_DISABLED; + response.Status = sSupportMgr->GetSupportSystemStatus() ? GMTICKET_QUEUE_STATUS_ENABLED : GMTICKET_QUEUE_STATUS_DISABLED; SendPacket(response.Write()); } -void WorldSession::HandleGMSurveySubmit(WorldPacket& recvData) +void WorldSession::HandleGMSurveySubmit(WorldPackets::Ticket::GMSurveySubmit& /*packet*/) { - uint32 nextSurveyID = sTicketMgr->GetNextSurveyID(); - // just put the survey into the database - uint32 mainSurvey; // GMSurveyCurrentSurvey.dbc, column 1 (all 9) ref to GMSurveySurveys.dbc - recvData >> mainSurvey; + /*uint32 nextSurveyID = sSupportMgr->GetNextSurveyID(); std::unordered_set<uint32> surveyIds; SQLTransaction trans = CharacterDatabase.BeginTransaction(); - // sub_survey1, r1, comment1, sub_survey2, r2, comment2, sub_survey3, r3, comment3, sub_survey4, r4, comment4, sub_survey5, r5, comment5, sub_survey6, r6, comment6, sub_survey7, r7, comment7, sub_survey8, r8, comment8, sub_survey9, r9, comment9, sub_survey10, r10, comment10, - for (uint8 i = 0; i < 15; i++) + + for (auto const& q : packet.SurveyQuestion) { - uint32 subSurveyId; // ref to i'th GMSurveySurveys.dbc field (all fields in that dbc point to fields in GMSurveyQuestions.dbc) - recvData >> subSurveyId; - if (!subSurveyId) + if (!q.QuestionID) break; - uint8 rank; // probably some sort of ref to GMSurveyAnswers.dbc - recvData >> rank; - std::string comment; // comment ("Usage: GMSurveyAnswerSubmit(question, rank, comment)") - recvData >> comment; - // make sure the same sub survey is not added to DB twice - if (!surveyIds.insert(subSurveyId).second) + if (!surveyIds.insert(q.QuestionID).second) continue; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SUBSURVEY); stmt->setUInt32(0, nextSurveyID); - stmt->setUInt32(1, subSurveyId); - stmt->setUInt32(2, rank); - stmt->setString(3, comment); + stmt->setUInt32(1, q.QuestionID); // ref to i'th GMSurveySurveys.dbc field (all fields in that dbc point to fields in GMSurveyQuestions.dbc) + stmt->setUInt32(2, q.Answer); // probably some sort of ref to GMSurveyAnswers.dbc + stmt->setString(3, q.AnswerComment); // comment ("Usage: GMSurveyAnswerSubmit(question, rank, comment)") trans->Append(stmt); } - std::string comment; // just a guess - recvData >> comment; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SURVEY); stmt->setUInt64(0, GetPlayer()->GetGUID().GetCounter()); stmt->setUInt32(1, nextSurveyID); - stmt->setUInt32(2, mainSurvey); - stmt->setString(3, comment); - + stmt->setUInt32(2, packet.SurveyID); // GMSurveyCurrentSurvey.dbc, column 1 (all 9) ref to GMSurveySurveys.dbc + stmt->setString(3, packet.Comment); trans->Append(stmt); - CharacterDatabase.CommitTransaction(trans); + CharacterDatabase.CommitTransaction(trans);*/ } -void WorldSession::HandleReportLag(WorldPacket& recvData) +void WorldSession::HandleGMResponseResolve(WorldPackets::Ticket::GMTicketResponseResolve& /*packet*/) { - // just put the lag report into the database... - // can't think of anything else to do with it - uint32 lagType, mapId; - recvData >> lagType; - recvData >> mapId; - float x, y, z; - recvData >> x; - recvData >> y; - recvData >> z; - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_LAG_REPORT); - stmt->setUInt64(0, GetPlayer()->GetGUID().GetCounter()); - stmt->setUInt8 (1, lagType); - stmt->setUInt16(2, mapId); - stmt->setFloat (3, x); - stmt->setFloat (4, y); - stmt->setFloat (5, z); - stmt->setUInt32(6, GetLatency()); - stmt->setUInt32(7, time(NULL)); - CharacterDatabase.Execute(stmt); -} - -void WorldSession::HandleGMResponseResolve(WorldPacket& /*recvPacket*/) -{ - // empty packet - if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID())) + if (GmTicket* ticket = sSupportMgr->GetGmTicketByPlayerGuid(GetPlayer()->GetGUID())) { - uint8 getSurvey = 0; + bool showSurvey = false; if (float(rand_chance()) < sWorld->getFloatConfig(CONFIG_CHANCE_OF_GM_SURVEY)) - getSurvey = 1; + showSurvey = true; - WorldPacket data(SMSG_GM_TICKET_STATUS_UPDATE, 4); - data << uint8(getSurvey); - SendPacket(&data); + WorldPackets::Ticket::GMTicketResolveResponse response; + response.ShowSurvey = showSurvey; + SendPacket(response.Write()); - WorldPacket data2(SMSG_GM_TICKET_UPDATE, 4); - data2 << uint32(GMTICKET_RESPONSE_TICKET_DELETED); - SendPacket(&data2); + sSupportMgr->SendGmTicketUpdate(this, GMTICKET_RESPONSE_TICKET_DELETED); - sTicketMgr->CloseTicket(ticket->GetId(), GetPlayer()->GetGUID()); - sTicketMgr->SendTicket(this, NULL); + sSupportMgr->CloseTicket<GmTicket>(ticket->GetId(), GetPlayer()->GetGUID()); + sSupportMgr->SendGmTicket(this, NULL); } } + +void WorldSession::HandleSupportTicketSubmitBug(WorldPackets::Ticket::SupportTicketSubmitBug& packet) +{ + BugTicket* ticket = new BugTicket(GetPlayer()); + ticket->SetPosition(packet.Header.MapID, packet.Header.Position); + ticket->SetFacing(packet.Header.Facing); + ticket->SetNote(packet.Note); + + sSupportMgr->AddTicket(ticket); +} + +void WorldSession::HandleSupportTicketSubmitSuggestion(WorldPackets::Ticket::SupportTicketSubmitSuggestion& packet) +{ + SuggestionTicket* ticket = new SuggestionTicket(GetPlayer()); + ticket->SetPosition(packet.Header.MapID, packet.Header.Position); + ticket->SetFacing(packet.Header.Facing); + ticket->SetNote(packet.Note); + + sSupportMgr->AddTicket(ticket); +} + +void WorldSession::HandleSupportTicketSubmitComplaint(WorldPackets::Ticket::SupportTicketSubmitComplaint& packet) +{ + ComplaintTicket* comp = new ComplaintTicket(GetPlayer()); + comp->SetPosition(packet.Header.MapID, packet.Header.Position); + comp->SetFacing(packet.Header.Facing); + comp->SetChatLog(packet.ChatLog); + comp->SetTargetCharacterGuid(packet.TargetCharacterGUID); + comp->SetComplaintType(GMSupportComplaintType(packet.ComplaintType)); + comp->SetNote(packet.Note); + + sSupportMgr->AddTicket(comp); + +} diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index ef4035469a9..446ee2606e1 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -985,7 +985,8 @@ enum TrinityStrings LANG_ACCOUNT_ALREADY_LINKED = 1187, LANG_ACCOUNT_BNET_UNLINKED = 1188, LANG_ACCOUNT_BNET_NOT_LINKED = 1189, - // Room for more level 3 1190-1199 not used + LANG_DISALLOW_TICKETS_CONFIG = 1190, + // Room for more level 3 1191-1199 not used // Debug commands LANG_CINEMATIC_NOT_EXIST = 1200, diff --git a/src/server/game/Server/Packets/LFGPackets.cpp b/src/server/game/Server/Packets/LFGPackets.cpp new file mode 100644 index 00000000000..4e7bc35b55a --- /dev/null +++ b/src/server/game/Server/Packets/LFGPackets.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "LFGPackets.h" + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::LFG::RideTicket& ticket) +{ + data >> ticket.RequesterGuid; + data >> ticket.Id; + data >> ticket.Type; + data >> ticket.Time; + + return data; +} diff --git a/src/server/game/Server/Packets/LFGPackets.h b/src/server/game/Server/Packets/LFGPackets.h new file mode 100644 index 00000000000..e62cb47b41a --- /dev/null +++ b/src/server/game/Server/Packets/LFGPackets.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef LFGPackets_h__ +#define LFGPackets_h__ + +#include "ObjectGuid.h" + +namespace WorldPackets +{ + namespace LFG + { + struct RideTicket + { + ObjectGuid RequesterGuid; + int32 Id = 0; + int32 Type = 0; + time_t Time = 0; + }; + } +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::LFG::RideTicket& ticket); + +#endif // LFGPackets_h__ diff --git a/src/server/game/Server/Packets/SystemPackets.cpp b/src/server/game/Server/Packets/SystemPackets.cpp index 1a01c2d28c6..a88895fb35c 100644 --- a/src/server/game/Server/Packets/SystemPackets.cpp +++ b/src/server/game/Server/Packets/SystemPackets.cpp @@ -39,18 +39,20 @@ WorldPacket const* WorldPackets::System::FeatureSystemStatus::Write() _worldPacket.WriteBit(SessionAlert.HasValue); _worldPacket.WriteBit(RecruitAFriendSendingEnabled); _worldPacket.WriteBit(CharUndeleteEnabled); - _worldPacket.WriteBit(UnkBit21); - _worldPacket.WriteBit(UnkBit22); + _worldPacket.WriteBit(RestrictedAccount); + _worldPacket.WriteBit(TutorialsEnabled); _worldPacket.WriteBit(UnkBit90); _worldPacket.WriteBit(TwitterEnabled); _worldPacket.WriteBit(UnkBit61); + _worldPacket.FlushBits(); + if (EuropaTicketSystemStatus.HasValue) { - _worldPacket.WriteBit(EuropaTicketSystemStatus.Value.UnkBit0); - _worldPacket.WriteBit(EuropaTicketSystemStatus.Value.UnkBit1); - _worldPacket.WriteBit(EuropaTicketSystemStatus.Value.TicketSystemEnabled); - _worldPacket.WriteBit(EuropaTicketSystemStatus.Value.SubmitBugEnabled); + _worldPacket.WriteBit(EuropaTicketSystemStatus.Value.TicketsEnabled); + _worldPacket.WriteBit(EuropaTicketSystemStatus.Value.BugsEnabled); + _worldPacket.WriteBit(EuropaTicketSystemStatus.Value.ComplaintsEnabled); + _worldPacket.WriteBit(EuropaTicketSystemStatus.Value.SuggestionsEnabled); _worldPacket << uint32(EuropaTicketSystemStatus.Value.ThrottleState.MaxTries); _worldPacket << uint32(EuropaTicketSystemStatus.Value.ThrottleState.PerMilliseconds); diff --git a/src/server/game/Server/Packets/SystemPackets.h b/src/server/game/Server/Packets/SystemPackets.h index 897726575a4..d20d35649fb 100644 --- a/src/server/game/Server/Packets/SystemPackets.h +++ b/src/server/game/Server/Packets/SystemPackets.h @@ -37,10 +37,10 @@ namespace WorldPackets struct EuropaTicketConfig { - bool UnkBit0 = false; - bool UnkBit1 = false; - bool TicketSystemEnabled = false; - bool SubmitBugEnabled = false; + bool TicketsEnabled = false; + bool BugsEnabled = false; + bool ComplaintsEnabled = false; + bool SuggestionsEnabled = false; SavedThrottleObjectState ThrottleState; }; @@ -76,8 +76,8 @@ namespace WorldPackets bool BpayStoreDisabledByParentalControls = false; bool TwitterEnabled = false; - bool UnkBit21 = false; - bool UnkBit22 = false; + bool RestrictedAccount = false; + bool TutorialsEnabled = false; bool UnkBit90 = false; bool UnkBit61 = false; }; diff --git a/src/server/game/Server/Packets/TicketPackets.cpp b/src/server/game/Server/Packets/TicketPackets.cpp index 8567cebd736..1a4d4d00c0a 100644 --- a/src/server/game/Server/Packets/TicketPackets.cpp +++ b/src/server/game/Server/Packets/TicketPackets.cpp @@ -15,8 +15,21 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "LFGPackets.h" +#include "PacketUtilities.h" #include "TicketPackets.h" +using namespace WorldPackets; + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Ticket::SupportTicketHeader& header) +{ + data >> header.MapID; + data >> header.Position; + data >> header.Facing; + + return data; +} + WorldPacket const* WorldPackets::Ticket::GMTicketSystemStatus::Write() { _worldPacket << int32(Status); @@ -80,3 +93,281 @@ WorldPacket const* WorldPackets::Ticket::GMTicketGetTicketResponse::Write() return &_worldPacket; } + +void WorldPackets::Ticket::GMTicketCreate::Read() +{ + _worldPacket >> Map; + _worldPacket >> Pos; + _worldPacket >> Flags; + + uint16 descLength = _worldPacket.ReadBits(11); + Description = _worldPacket.ReadString(descLength); + _worldPacket.ResetBitPos(); + + NeedMoreHelp = _worldPacket.ReadBit(); + NeedResponse = _worldPacket.ReadBit(); + _worldPacket >> DataLength; + + if (DataLength > 0) + { + _worldPacket >> ChatHistoryData.TextCount; + for (uint8 i = 0; i < ChatHistoryData.TextCount; ++i) + ChatHistoryData.Sent.push_back(_worldPacket.read<uint32>()); + + _worldPacket >> ChatHistoryData.DecompressedSize; + + //Note: don't ask why, but it works... + uint32 realLength = DataLength - ((ChatHistoryData.TextCount * 4) + 5); + ChatHistoryData.Data.resize(realLength); + _worldPacket.read(ChatHistoryData.Data.contents(), realLength); + } +} + +void WorldPackets::Ticket::GMTicketUpdateText::Read() +{ + uint16 descLength = _worldPacket.ReadBits(11); + Description = _worldPacket.ReadString(descLength); +} + +WorldPacket const* WorldPackets::Ticket::GMTicketUpdate::Write() +{ + _worldPacket << uint8(Result); + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Ticket::GMTicketStatusUpdate::Write() +{ + _worldPacket << int32(StatusInt); + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Ticket::GMTicketResponse::Write() +{ + _worldPacket << uint32(TicketID); + _worldPacket << uint32(ResponseID); + + _worldPacket.WriteBits(Description.size(), 11); + _worldPacket.WriteBits(ResponseText.size(), 14); + _worldPacket.FlushBits(); + + _worldPacket.WriteString(Description); + _worldPacket.WriteString(ResponseText); + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Ticket::GMTicketResolveResponse::Write() +{ + _worldPacket.WriteBit(ShowSurvey); + + _worldPacket.FlushBits(); + return &_worldPacket; +} + +void WorldPackets::Ticket::GMSurveySubmit::Read() +{ + _worldPacket >> SurveyID; + + SurveyQuestion.resize(_worldPacket.ReadBits(4)); + uint16 commentLength = _worldPacket.ReadBits(11); + + for (auto& q : SurveyQuestion) + { + _worldPacket >> q.QuestionID; + _worldPacket >> q.Answer; + q.AnswerComment = _worldPacket.ReadString(_worldPacket.ReadBits(11)); + } + + Comment = _worldPacket.ReadString(commentLength); +} + +void WorldPackets::Ticket::SupportTicketSubmitBug::Read() +{ + _worldPacket >> Header; + Note = _worldPacket.ReadString(_worldPacket.ReadBits(10)); +} + +void WorldPackets::Ticket::SupportTicketSubmitSuggestion::Read() +{ + _worldPacket >> Header; + Note = _worldPacket.ReadString(_worldPacket.ReadBits(10)); +} + +WorldPackets::Ticket::SupportTicketSubmitComplaint::SupportTicketChatLine::SupportTicketChatLine(ByteBuffer& data) +{ + data >> Timestamp; + uint16 textLength = data.ReadBits(12); + data.ResetBitPos(); + Text = data.ReadString(textLength); +} + +WorldPackets::Ticket::SupportTicketSubmitComplaint::SupportTicketChatLine::SupportTicketChatLine(uint32 timestamp, std::string const& text) +{ + Timestamp = timestamp; + Text = text; +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Ticket::SupportTicketSubmitComplaint::SupportTicketChatLine& line) +{ + data >> line.Timestamp; + uint16 textLength = data.ReadBits(12); + data.ResetBitPos(); + line.Text = data.ReadString(textLength); + + return data; +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Ticket::SupportTicketSubmitComplaint::SupportTicketChatLog& chatlog) +{ + uint32 linesCount = data.read<uint32>(); + + for (uint32 i = 0; i < linesCount; i++) + chatlog.Lines.emplace_back(data); + + bool hasReportLineIndex = data.ReadBit(); + if (hasReportLineIndex) + chatlog.ReportLineIndex.Set(data.read<uint32>()); + + data.ResetBitPos(); + + return data; +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Ticket::SupportTicketSubmitComplaint::SupportTicketMailInfo& mail) +{ + data >> mail.MailID; + uint16 bodyLength = data.ReadBits(13); + uint16 subjectLength = data.ReadBits(9); + + mail.MailBody = data.ReadString(bodyLength); + mail.MailSubject = data.ReadString(subjectLength); + + return data; +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Ticket::SupportTicketSubmitComplaint::SupportTicketCalendarEventInfo& event) +{ + data >> event.EventID; + data >> event.InviteID; + uint8 titleLength = data.ReadBits(8); + + event.EventTitle = data.ReadString(titleLength); + + return data; +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Ticket::SupportTicketSubmitComplaint::SupportTicketPetInfo& pet) +{ + data >> pet.PetID; + uint8 nameLength = data.ReadBits(8); + + pet.PetName = data.ReadString(nameLength); + + return data; +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Ticket::SupportTicketSubmitComplaint::SupportTicketGuildInfo& guild) +{ + data >> guild.GuildID; + uint8 nameLength = data.ReadBits(8); + + guild.GuildName = data.ReadString(nameLength); + + return data; +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Ticket::SupportTicketSubmitComplaint::Struct5E4383& str) +{ + data >> str.RideTicket; + data >> str._40; + data >> str._56; + data >> str._72; + + uint8 _88Length = data.ReadBits(8); + uint8 _217Length = data.ReadBits(8); + uint8 _1242Length = data.ReadBits(8); + + str._88 = data.ReadString(_88Length); + str._217 = data.ReadString(_217Length); + str._1242 = data.ReadString(_1242Length); + + return data; +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Ticket::SupportTicketSubmitComplaint::Struct5E3DFB& str) +{ + data >> str.RideTicket; + + uint16 _32Length = data.ReadBits(9); + data.ResetBitPos(); + + str._32 = data.ReadString(_32Length); + + return data; +} + +void WorldPackets::Ticket::SupportTicketSubmitComplaint::Read() +{ + _worldPacket >> Header; + _worldPacket >> ChatLog; + _worldPacket >> TargetCharacterGUID; + ComplaintType = _worldPacket.ReadBits(5); + + uint16 noteLength = _worldPacket.ReadBits(10); + bool hasMailInfo = _worldPacket.ReadBit(); + bool hasCalendarInfo = _worldPacket.ReadBit(); + bool hasPetInfo = _worldPacket.ReadBit(); + bool hasGuildInfo = _worldPacket.ReadBit(); + bool has5E4383 = _worldPacket.ReadBit(); + bool has5E3DFB = _worldPacket.ReadBit(); + + _worldPacket.ResetBitPos(); + + Note = _worldPacket.ReadString(noteLength); + + if (hasMailInfo) + { + _worldPacket >> MailInfo.Value; + MailInfo.HasValue = true; + } + + if (hasCalendarInfo) + { + _worldPacket >> CalenderInfo.Value; + CalenderInfo.HasValue = true; + } + + if (hasPetInfo) + { + _worldPacket >> PetInfo.Value; + PetInfo.HasValue = true; + } + + if (hasGuildInfo) + { + _worldPacket >> GuildInfo.Value; + GuildInfo.HasValue = true; + } + + if (has5E4383) + { + _worldPacket >> _5E4383.Value; + _5E4383.HasValue = true; + } + + if (has5E3DFB) + { + _worldPacket >> _5E3DFB.Value; + _5E3DFB.HasValue = true; + } +} + +WorldPacket const* WorldPackets::Ticket::ComplaintResult::Write() +{ + _worldPacket << uint32(ComplaintType); + _worldPacket << uint8(Result); + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/TicketPackets.h b/src/server/game/Server/Packets/TicketPackets.h index 8a5ae862f84..e11adf912ed 100644 --- a/src/server/game/Server/Packets/TicketPackets.h +++ b/src/server/game/Server/Packets/TicketPackets.h @@ -19,11 +19,20 @@ #define TicketPackets_h__ #include "Packet.h" +#include "LFGPackets.h" +#include <G3D/Vector3.h> namespace WorldPackets { namespace Ticket { + struct SupportTicketHeader + { + int32 MapID = 0; + G3D::Vector3 Position; + float Facing = 0.0f; + }; + class GMTicketGetSystemStatus final : public ClientPacket { public: @@ -107,6 +116,120 @@ namespace WorldPackets Optional<GMTicketInfo> Info; }; + class GMTicketCreate final : public ClientPacket + { + public: + struct ChatHistoryData + { + uint8 TextCount = 0; + std::vector<uint32> Sent; + uint32 DecompressedSize = 0; + ByteBuffer Data; + }; + + GMTicketCreate(WorldPacket&& packet) : ClientPacket(CMSG_GM_TICKET_CREATE, std::move(packet)) { } + + void Read() override; + + int32 Map = 0; + G3D::Vector3 Pos; + uint8 Flags = 0; + std::string Description; + bool NeedMoreHelp = false; + bool NeedResponse = false; + uint32 DataLength = 0; + ChatHistoryData ChatHistoryData; ///< Compressed Data + }; + + class GMTicketUpdateText final : public ClientPacket + { + public: + GMTicketUpdateText(WorldPacket&& packet) : ClientPacket(CMSG_GM_TICKET_UPDATE_TEXT, std::move(packet)) { } + + void Read() override; + + std::string Description; + }; + + class GMTicketUpdate final : public ServerPacket + { + public: + GMTicketUpdate() : ServerPacket(SMSG_GM_TICKET_UPDATE, 1) { } + + WorldPacket const* Write() override; + + uint8 Result = 0; + }; + + class GMTicketStatusUpdate final : public ServerPacket + { + public: + GMTicketStatusUpdate() : ServerPacket(SMSG_GM_TICKET_STATUS_UPDATE, 4) { } + + WorldPacket const* Write() override; + + int32 StatusInt = 0; + }; + + class GMTicketDelete final : public ClientPacket + { + public: + GMTicketDelete(WorldPacket&& packet) : ClientPacket(CMSG_GM_TICKET_DELETE_TICKET, std::move(packet)) { } + + void Read() override { } + }; + + class GMTicketResponse final : public ServerPacket + { + public: + GMTicketResponse() : ServerPacket(SMSG_GM_TICKET_RESPONSE, 12) { } + + WorldPacket const* Write() override; + + uint32 TicketID = 0; + uint32 ResponseID = 0; + std::string Description; + std::string ResponseText; + }; + + class GMTicketResponseResolve final : public ClientPacket + { + public: + GMTicketResponseResolve(WorldPacket&& packet) : ClientPacket(CMSG_GM_TICKET_RESPONSE_RESOLVE, std::move(packet)) { } + + void Read() override { } + }; + + class GMTicketResolveResponse final : public ServerPacket + { + public: + GMTicketResolveResponse() : ServerPacket(SMSG_GM_TICKET_RESOLVE_RESPONSE, 1) { } + + WorldPacket const* Write() override; + + bool ShowSurvey = false; + }; + + class GMSurveySubmit final : public ClientPacket + { + public: + struct GMSurveyQuestion + { + int32 QuestionID = 0; + uint8 Answer = 0; + std::string AnswerComment; + }; + + GMSurveySubmit(WorldPacket&& packet) : ClientPacket(CMSG_GM_SURVEY_SUBMIT, std::move(packet)) { } + + void Read() override; + + int32 SurveyID = 0; + std::vector<GMSurveyQuestion> SurveyQuestion; + std::string Comment; + + }; + class GMTicketAcknowledgeSurvey final : public ClientPacket { public: @@ -124,6 +247,118 @@ namespace WorldPackets WorldPacket const* Write() { return &_worldPacket; } }; + + class SupportTicketSubmitBug final : public ClientPacket + { + public: + SupportTicketSubmitBug(WorldPacket&& packet) : ClientPacket(CMSG_SUPPORT_TICKET_SUBMIT_BUG, std::move(packet)) { } + + void Read() override; + + SupportTicketHeader Header; + std::string Note; + }; + + class SupportTicketSubmitSuggestion final : public ClientPacket + { + public: + SupportTicketSubmitSuggestion(WorldPacket&& packet) : ClientPacket(CMSG_SUPPORT_TICKET_SUBMIT_SUGGESTION, std::move(packet)) { } + + void Read() override; + + SupportTicketHeader Header; + std::string Note; + }; + + class SupportTicketSubmitComplaint final : public ClientPacket + { + public: + struct SupportTicketChatLine + { + SupportTicketChatLine(ByteBuffer& data); + SupportTicketChatLine(uint32 timestamp, std::string const& text); + + uint32 Timestamp = 0; + std::string Text; + }; + + struct SupportTicketChatLog + { + std::vector<SupportTicketChatLine> Lines; + Optional<uint32> ReportLineIndex; + }; + + struct SupportTicketMailInfo + { + int32 MailID = 0; + std::string MailSubject; + std::string MailBody; + }; + + struct SupportTicketCalendarEventInfo + { + ObjectGuid EventID; + ObjectGuid InviteID; + std::string EventTitle; + }; + + struct SupportTicketPetInfo + { + ObjectGuid PetID; + std::string PetName; + }; + + struct SupportTicketGuildInfo + { + ObjectGuid GuildID; + std::string GuildName; + }; + + struct Struct5E4383 + { + WorldPackets::LFG::RideTicket RideTicket; + ObjectGuid _40; + ObjectGuid _56; + ObjectGuid _72; + std::string _88; + std::string _217; + std::string _1242; + }; + + struct Struct5E3DFB + { + WorldPackets::LFG::RideTicket RideTicket; + std::string _32; + }; + + SupportTicketSubmitComplaint(WorldPacket&& packet) : ClientPacket(CMSG_SUPPORT_TICKET_SUBMIT_COMPLAINT, std::move(packet)) { } + + void Read() override; + + SupportTicketHeader Header; + SupportTicketChatLog ChatLog; + ObjectGuid TargetCharacterGUID; + uint8 ComplaintType = 0; + std::string Note; + Optional<SupportTicketMailInfo> MailInfo; + Optional<SupportTicketCalendarEventInfo> CalenderInfo; + Optional<SupportTicketPetInfo> PetInfo; + Optional<SupportTicketGuildInfo> GuildInfo; + Optional<Struct5E4383> _5E4383; + Optional<Struct5E3DFB> _5E3DFB; + + }; + + class ComplaintResult final : public ServerPacket + { + public: + ComplaintResult() : ServerPacket(SMSG_COMPLAINT_RESULT, 9) { } + + WorldPacket const* Write() override; + + uint32 ComplaintType = 0; + uint8 Result = 0; + }; } } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index de285fb1ed6..43ffe3e223e 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -379,20 +379,19 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_GET_UNDELETE_COOLDOWN_STATUS, STATUS_AUTHED, PROCESS_THREADUNSAFE, WorldPackets::Character::GetUndeleteCooldownStatus, &WorldSession::HandleGetUndeleteCooldownStatus); DEFINE_OPCODE_HANDLER_OLD(CMSG_GHOST, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_INVIS, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_LAG_REPORT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleReportLag ); + DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_LAG_REPORT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_NUKE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_SET_SECURITY_GROUP, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_SURVEY_SUBMIT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMSurveySubmit ); + DEFINE_HANDLER(CMSG_GM_SURVEY_SUBMIT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Ticket::GMSurveySubmit, &WorldSession::HandleGMSurveySubmit); DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_ACKNOWLEDGE_SURVEY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); //DEFINE_HANDLER(CMSG_GM_TICKET_ACKNOWLEDGE_SURVEY, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Ticket::GMTicketAcknowledgeSurvey, &WorldSession::HandleGMTicketAcknowledgeSurveyOpcode); - DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_CREATE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketCreateOpcode ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_DELETE_TICKET, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketDeleteOpcode ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_GET_CASE_STATUS, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - //DEFINE_HANDLER(CMSG_GM_TICKET_GET_CASE_STATUS, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Ticket::GMTicketGetCaseStatus, &WorldSession::HandleGMTicketGetCaseStatusOpcode); - DEFINE_HANDLER(CMSG_GM_TICKET_GET_SYSTEM_STATUS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Ticket::GMTicketGetSystemStatus, &WorldSession::HandleGMTicketSystemStatusOpcode); - DEFINE_HANDLER(CMSG_GM_TICKET_GET_TICKET, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Ticket::GMTicketGetTicket, &WorldSession::HandleGMTicketGetTicketOpcode); - DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_RESPONSE_RESOLVE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMResponseResolve ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_UPDATE_TEXT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketUpdateOpcode ); + DEFINE_HANDLER(CMSG_GM_TICKET_CREATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::GMTicketCreate, &WorldSession::HandleGMTicketCreateOpcode); + DEFINE_HANDLER(CMSG_GM_TICKET_DELETE_TICKET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::GMTicketDelete, &WorldSession::HandleGMTicketDeleteOpcode); + DEFINE_HANDLER(CMSG_GM_TICKET_GET_CASE_STATUS, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Ticket::GMTicketGetCaseStatus, &WorldSession::HandleGMTicketGetCaseStatusOpcode); + DEFINE_HANDLER(CMSG_GM_TICKET_GET_SYSTEM_STATUS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::GMTicketGetSystemStatus, &WorldSession::HandleGMTicketSystemStatusOpcode); + DEFINE_HANDLER(CMSG_GM_TICKET_GET_TICKET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::GMTicketGetTicket, &WorldSession::HandleGMTicketGetTicketOpcode); + DEFINE_HANDLER(CMSG_GM_TICKET_RESPONSE_RESOLVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::GMTicketResponseResolve, &WorldSession::HandleGMResponseResolve); + DEFINE_HANDLER(CMSG_GM_TICKET_UPDATE_TEXT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::GMTicketUpdateText, &WorldSession::HandleGMTicketUpdateTextOpcode); DEFINE_HANDLER(CMSG_GOSSIP_HELLO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::Hello, &WorldSession::HandleGossipHelloOpcode); DEFINE_HANDLER(CMSG_GOSSIP_SELECT_OPTION, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::NPC::GossipSelectOption, &WorldSession::HandleGossipSelectOptionOpcode); DEFINE_OPCODE_HANDLER_OLD(CMSG_GRANT_LEVEL, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGrantLevel ); @@ -804,9 +803,9 @@ void OpcodeTable::Initialize() DEFINE_OPCODE_HANDLER_OLD(CMSG_STOP_DANCE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_STORE_LOOT_IN_SLOT, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_SUMMON_RESPONSE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleSummonResponseOpcode ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_SUPPORT_TICKET_SUBMIT_BUG, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_SUPPORT_TICKET_SUBMIT_COMPLAINT, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_SUPPORT_TICKET_SUBMIT_SUGGESTION, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); + DEFINE_HANDLER(CMSG_SUPPORT_TICKET_SUBMIT_BUG, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::SupportTicketSubmitBug, &WorldSession::HandleSupportTicketSubmitBug); + DEFINE_HANDLER(CMSG_SUPPORT_TICKET_SUBMIT_COMPLAINT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::SupportTicketSubmitComplaint, &WorldSession::HandleSupportTicketSubmitComplaint); + DEFINE_HANDLER(CMSG_SUPPORT_TICKET_SUBMIT_SUGGESTION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::SupportTicketSubmitSuggestion, &WorldSession::HandleSupportTicketSubmitSuggestion); DEFINE_OPCODE_HANDLER_OLD(CMSG_SUSPEND_COMMS_ACK, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER_OLD(CMSG_SUSPEND_TOKEN_RESPONSE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_HANDLER(CMSG_SWAP_INV_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::SwapInvItem, &WorldSession::HandleSwapInvItemOpcode); @@ -1239,14 +1238,14 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_MESSAGECHAT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_PLAYER_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_REQUEST_PLAYER_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_CASE_STATUS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_GET_TICKET_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_RESOLVE_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_RESPONSE_ERROR, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_STATUS_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_SYSTEM_STATUS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_CASE_STATUS, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_GET_TICKET_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_RESOLVE_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_RESPONSE_ERROR, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_STATUS_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_SYSTEM_STATUS, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GOD_MODE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GOSSIP_COMPLETE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GOSSIP_MESSAGE, STATUS_NEVER, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h index 7bc9488dd26..f4bfac9225e 100644 --- a/src/server/game/Server/Protocol/Opcodes.h +++ b/src/server/game/Server/Protocol/Opcodes.h @@ -292,13 +292,13 @@ enum OpcodeClient : uint32 CMSG_GM_SET_SECURITY_GROUP = 0xBADD, CMSG_GM_SURVEY_SUBMIT = 0xBADD, CMSG_GM_TICKET_ACKNOWLEDGE_SURVEY = 0xBADD, - CMSG_GM_TICKET_CREATE = 0xBADD, - CMSG_GM_TICKET_DELETE_TICKET = 0xBADD, - CMSG_GM_TICKET_GET_CASE_STATUS = 0xBADD, - CMSG_GM_TICKET_GET_SYSTEM_STATUS = 0xBADD, - CMSG_GM_TICKET_GET_TICKET = 0xBADD, - CMSG_GM_TICKET_RESPONSE_RESOLVE = 0xBADD, - CMSG_GM_TICKET_UPDATE_TEXT = 0xBADD, + CMSG_GM_TICKET_CREATE = 0x19A4, + CMSG_GM_TICKET_DELETE_TICKET = 0x1B39, + CMSG_GM_TICKET_GET_CASE_STATUS = 0x17E1, + CMSG_GM_TICKET_GET_SYSTEM_STATUS = 0x1BB9, + CMSG_GM_TICKET_GET_TICKET = 0x1939, + CMSG_GM_TICKET_RESPONSE_RESOLVE = 0x19FB, + CMSG_GM_TICKET_UPDATE_TEXT = 0x19EB, CMSG_GOSSIP_HELLO = 0x0483, CMSG_GOSSIP_SELECT_OPTION = 0xBADD, CMSG_GRANT_LEVEL = 0xBADD, @@ -712,9 +712,9 @@ enum OpcodeClient : uint32 CMSG_STOP_DANCE = 0xBADD, CMSG_STORE_LOOT_IN_SLOT = 0xBADD, CMSG_SUMMON_RESPONSE = 0xBADD, - CMSG_SUPPORT_TICKET_SUBMIT_BUG = 0xBADD, - CMSG_SUPPORT_TICKET_SUBMIT_COMPLAINT = 0xBADD, - CMSG_SUPPORT_TICKET_SUBMIT_SUGGESTION = 0xBADD, + CMSG_SUPPORT_TICKET_SUBMIT_BUG = 0x11BB, + CMSG_SUPPORT_TICKET_SUBMIT_COMPLAINT = 0x1BB1, + CMSG_SUPPORT_TICKET_SUBMIT_SUGGESTION = 0x1B63, CMSG_SUSPEND_COMMS_ACK = 0x123C, CMSG_SUSPEND_TOKEN_RESPONSE = 0x1273, CMSG_SWAP_INV_ITEM = 0x00C5, @@ -1171,14 +1171,14 @@ enum OpcodeServer : uint32 SMSG_GM_PLAYER_INFO = 0xBADD, SMSG_GM_REQUEST_PLAYER_INFO = 0xBADD, SMSG_GM_SUMMON = 0xBADD, - SMSG_GM_TICKET_CASE_STATUS = 0xBADD, - SMSG_GM_TICKET_GET_TICKET_RESPONSE = 0xBADD, - SMSG_GM_TICKET_RESOLVE_RESPONSE = 0xBADD, - SMSG_GM_TICKET_RESPONSE = 0xBADD, - SMSG_GM_TICKET_RESPONSE_ERROR = 0xBADD, - SMSG_GM_TICKET_STATUS_UPDATE = 0xBADD, - SMSG_GM_TICKET_SYSTEM_STATUS = 0xBADD, - SMSG_GM_TICKET_UPDATE = 0xBADD, + SMSG_GM_TICKET_CASE_STATUS = 0x086B, + SMSG_GM_TICKET_GET_TICKET_RESPONSE = 0x0183, + SMSG_GM_TICKET_RESOLVE_RESPONSE = 0x0869, + SMSG_GM_TICKET_RESPONSE = 0x1729, + SMSG_GM_TICKET_RESPONSE_ERROR = 0x0A33, + SMSG_GM_TICKET_STATUS_UPDATE = 0x0B74, + SMSG_GM_TICKET_SYSTEM_STATUS = 0x134C, + SMSG_GM_TICKET_UPDATE = 0x193A, SMSG_GOD_MODE = 0xBADD, SMSG_GOSSIP_COMPLETE = 0xBADD, SMSG_GOSSIP_MESSAGE = 0x0117, diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 54c995589a4..c7ea26b7adf 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -358,10 +358,18 @@ namespace WorldPackets namespace Ticket { + class GMSurveySubmit; + class GMTicketAcknowledgeSurvey; + class GMTicketCreate; + class GMTicketDelete; class GMTicketGetSystemStatus; class GMTicketGetCaseStatus; class GMTicketGetTicket; - class GMTicketAcknowledgeSurvey; + class GMTicketResponseResolve; + class GMTicketUpdateText; + class SupportTicketSubmitBug; + class SupportTicketSubmitSuggestion; + class SupportTicketSubmitComplaint; } namespace Trade @@ -840,15 +848,17 @@ class WorldSession void HandleLogoutCancelOpcode(WorldPackets::Character::LogoutCancel& logoutCancel); // GM Ticket opcodes - void HandleGMTicketCreateOpcode(WorldPacket& recvPacket); - void HandleGMTicketUpdateOpcode(WorldPacket& recvPacket); - void HandleGMTicketDeleteOpcode(WorldPacket& recvPacket); + void HandleGMTicketCreateOpcode(WorldPackets::Ticket::GMTicketCreate& packet); + void HandleGMTicketUpdateTextOpcode(WorldPackets::Ticket::GMTicketUpdateText& packet); + void HandleGMTicketDeleteOpcode(WorldPackets::Ticket::GMTicketDelete& packet); void HandleGMTicketGetCaseStatusOpcode(WorldPackets::Ticket::GMTicketGetCaseStatus& packet); void HandleGMTicketGetTicketOpcode(WorldPackets::Ticket::GMTicketGetTicket& packet); void HandleGMTicketSystemStatusOpcode(WorldPackets::Ticket::GMTicketGetSystemStatus& packet); - void HandleGMSurveySubmit(WorldPacket& recvPacket); - void HandleReportLag(WorldPacket& recvPacket); - void HandleGMResponseResolve(WorldPacket& recvPacket); + void HandleGMSurveySubmit(WorldPackets::Ticket::GMSurveySubmit& packet); + void HandleGMResponseResolve(WorldPackets::Ticket::GMTicketResponseResolve& packet); + void HandleSupportTicketSubmitBug(WorldPackets::Ticket::SupportTicketSubmitBug& packet); + void HandleSupportTicketSubmitSuggestion(WorldPackets::Ticket::SupportTicketSubmitSuggestion& packet); + void HandleSupportTicketSubmitComplaint(WorldPackets::Ticket::SupportTicketSubmitComplaint& packet); void HandleTogglePvP(WorldPacket& recvPacket); diff --git a/src/server/game/Support/SupportMgr.cpp b/src/server/game/Support/SupportMgr.cpp new file mode 100644 index 00000000000..9183c2f7fde --- /dev/null +++ b/src/server/game/Support/SupportMgr.cpp @@ -0,0 +1,1088 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ +#include "Chat.h" +#include "Language.h" +#include "SupportMgr.h" + +inline time_t GetAge(uint64 t) { return (time(nullptr) - t) / DAY; } + +Ticket::Ticket() : _id(0), _mapId(0), _createTime(0) { } + +Ticket::Ticket(Player* player) : _id(0), _mapId(0), _createTime(time(nullptr)) +{ + _playerGuid = player->GetGUID(); +} + +Ticket::~Ticket() { } + +void Ticket::TeleportTo(Player* player) const +{ + player->TeleportTo(_mapId, _pos.x, _pos.y, _pos.z, 0.0f, 0); +} + +std::string Ticket::FormatViewMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName, const char* szCompletedName) const +{ + std::stringstream ss; + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id); + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str()); + if (szClosedName) + ss << handler.PGetParseString(LANG_COMMAND_TICKETCLOSED, szClosedName); + if (szAssignedToName) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, szAssignedToName); + if (szUnassignedName) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTUNASSIGNED, szUnassignedName); + if (szDeletedName) + ss << handler.PGetParseString(LANG_COMMAND_TICKETDELETED, szDeletedName); + return ss.str(); +} + +GmTicket::GmTicket() : _lastModifiedTime(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _viewed(false), _needResponse(false), _needMoreHelp(false) { } + +GmTicket::GmTicket(Player* player) : Ticket(player), _lastModifiedTime(time(nullptr)), _completed(false), _escalatedStatus(TICKET_UNASSIGNED), +_viewed(false), _needResponse(false), _needMoreHelp(false) +{ + _id = sSupportMgr->GenerateGmTicketId(); +} + +GmTicket::~GmTicket() { } + +void GmTicket::SetGmAction(uint32 needResponse, bool needMoreHelp) +{ + _needResponse = (needResponse == 17); // Requires GM response. 17 = true, 1 = false (17 is default) + _needMoreHelp = needMoreHelp; // Requests further GM interaction on a ticket to which a GM has already responded. Basically means "has a new ticket" +} + +void GmTicket::SetUnassigned() +{ + _assignedTo.Clear(); + switch (_escalatedStatus) + { + case TICKET_ASSIGNED: _escalatedStatus = TICKET_UNASSIGNED; + break; + case TICKET_ESCALATED_ASSIGNED: _escalatedStatus = TICKET_IN_ESCALATION_QUEUE; + break; + case TICKET_UNASSIGNED: + case TICKET_IN_ESCALATION_QUEUE: + default: + break; + } +} + +void GmTicket::SetChatLog(std::list<uint32> time, std::string const& log) +{ + std::stringstream ss(log); + std::stringstream newss; + std::string line; + while (std::getline(ss, line) && !time.empty()) + { + newss << secsToTimeString(time.front()) << ": " << line << "\n"; + time.pop_front(); + } + + _chatLog = newss.str(); +} + +void GmTicket::SendResponse(WorldSession* session) const +{ + WorldPackets::Ticket::GMTicketResponse resp; + resp.TicketID = GetId(); + resp.ResponseID = 2; //TODO : research + resp.Description = GetDescription(); + resp.ResponseText = GetResponse(); + + session->SendPacket(resp.Write()); +} + +void GmTicket::LoadFromDB(Field* fields) +{ + uint8 idx = 0; + _id = fields[ idx].GetUInt32(); + _playerGuid = ObjectGuid::Create<HighGuid::Player>(fields[++idx].GetUInt64()); + _description = fields[++idx].GetString(); + _createTime = fields[++idx].GetUInt32(); + _mapId = fields[++idx].GetUInt16(); + _pos.x = fields[++idx].GetFloat(); + _pos.y = fields[++idx].GetFloat(); + _pos.z = fields[++idx].GetFloat(); + _lastModifiedTime = fields[++idx].GetUInt32(); + + int64 closedBy = fields[++idx].GetInt64(); + if (closedBy == 0) + _closedBy = ObjectGuid::Empty; + else if (closedBy < 0) + _closedBy.SetRawValue(0, uint64(closedBy)); + else + _closedBy = ObjectGuid::Create<HighGuid::Player>(uint64(closedBy)); + + uint64 assignedTo = fields[++idx].GetUInt64(); + if (assignedTo == 0) + _assignedTo = ObjectGuid::Empty; + else + _assignedTo = ObjectGuid::Create<HighGuid::Player>(assignedTo); + + _comment = fields[++idx].GetString(); + _response = fields[++idx].GetString(); + _completed = fields[++idx].GetBool(); + _escalatedStatus = GMTicketEscalationStatus(fields[++idx].GetUInt8()); + _viewed = fields[++idx].GetBool(); + _needMoreHelp = fields[++idx].GetBool(); +} + +void GmTicket::SaveToDB(SQLTransaction& trans) const +{ + uint8 idx = 0; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_TICKET); + stmt->setUInt32(idx, _id); + stmt->setUInt64(++idx, _playerGuid.GetCounter()); + stmt->setString(++idx, _description); + stmt->setUInt32(++idx, _createTime); + stmt->setUInt16(++idx, _mapId); + stmt->setFloat(++idx, _pos.x); + stmt->setFloat(++idx, _pos.y); + stmt->setFloat(++idx, _pos.z); + stmt->setUInt32(++idx, uint32(_lastModifiedTime)); + stmt->setInt64(++idx, int64(_closedBy.GetCounter())); + stmt->setUInt64(++idx, _assignedTo.GetCounter()); + stmt->setString(++idx, _comment); + stmt->setString(++idx, _response); + stmt->setBool(++idx, _completed); + stmt->setUInt8(++idx, uint8(_escalatedStatus)); + stmt->setBool(++idx, _viewed); + stmt->setBool(++idx, _needMoreHelp); + + CharacterDatabase.ExecuteOrAppend(trans, stmt); +} + +void GmTicket::DeleteFromDB() +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_TICKET); + stmt->setUInt32(0, _id); + CharacterDatabase.Execute(stmt); +} + +std::string GmTicket::FormatViewMessageString(ChatHandler& handler, bool detailed) const +{ + time_t curTime = time(nullptr); + + std::stringstream ss; + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id); + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str()); + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(curTime - _createTime, true, false)).c_str()); + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(curTime - _lastModifiedTime, true, false)).c_str()); + + std::string name; + if (ObjectMgr::GetPlayerNameByGUID(_assignedTo, name)) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, name.c_str()); + + if (detailed) + { + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, _description.c_str()); + if (!_comment.empty()) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, _comment.c_str()); + if (!_response.empty()) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTRESPONSE, _response.c_str()); + } + return ss.str(); +} + +std::string GmTicket::FormatViewMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName, const char* szCompletedName) const +{ + std::stringstream ss; + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id); + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str()); + if (szClosedName) + ss << handler.PGetParseString(LANG_COMMAND_TICKETCLOSED, szClosedName); + if (szAssignedToName) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, szAssignedToName); + if (szUnassignedName) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTUNASSIGNED, szUnassignedName); + if (szDeletedName) + ss << handler.PGetParseString(LANG_COMMAND_TICKETDELETED, szDeletedName); + if (szCompletedName) + ss << handler.PGetParseString(LANG_COMMAND_TICKETCOMPLETED, szCompletedName); + return ss.str(); +} + +BugTicket::BugTicket() : _facing(0.0f) { } + +BugTicket::BugTicket(Player* player) : Ticket(player) +{ + _id = sSupportMgr->GenerateBugId(); +} + +BugTicket::~BugTicket() { } + +void BugTicket::LoadFromDB(Field* fields) +{ + uint8 idx = 0; + _id = fields[ idx].GetUInt32(); + _playerGuid = ObjectGuid::Create<HighGuid::Player>(fields[++idx].GetUInt64()); + _note = fields[++idx].GetString(); + _createTime = fields[++idx].GetUInt32(); + _mapId = fields[++idx].GetUInt32(); + _pos.x = fields[++idx].GetFloat(); + _pos.y = fields[++idx].GetFloat(); + _pos.z = fields[++idx].GetFloat(); + _facing = fields[++idx].GetFloat(); + + int64 closedBy = fields[++idx].GetInt64(); + if (closedBy == 0) + _closedBy = ObjectGuid::Empty; + else if (closedBy < 0) + _closedBy.SetRawValue(0, uint64(closedBy)); + else + _closedBy = ObjectGuid::Create<HighGuid::Player>(uint64(closedBy)); + + uint64 assignedTo = fields[++idx].GetUInt64(); + if (assignedTo == 0) + _assignedTo = ObjectGuid::Empty; + else + _assignedTo = ObjectGuid::Create<HighGuid::Player>(assignedTo); + + _comment = fields[++idx].GetString(); +} + +void BugTicket::SaveToDB(SQLTransaction& trans) const +{ + uint8 idx = 0; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_BUG); + stmt->setUInt32(idx, _id); + stmt->setUInt64(++idx, _playerGuid.GetCounter()); + stmt->setString(++idx, _note); + stmt->setUInt32(++idx, _mapId); + stmt->setFloat(++idx, _pos.x); + stmt->setFloat(++idx, _pos.y); + stmt->setFloat(++idx, _pos.z); + stmt->setFloat(++idx, _facing); + stmt->setInt64(++idx, _closedBy.GetCounter()); + stmt->setUInt64(++idx, _assignedTo.GetCounter()); + stmt->setString(++idx, _comment); + + CharacterDatabase.ExecuteOrAppend(trans, stmt); +} + +void BugTicket::DeleteFromDB() +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_BUG); + stmt->setUInt32(0, _id); + CharacterDatabase.Execute(stmt); +} + +std::string BugTicket::FormatViewMessageString(ChatHandler& handler, bool detailed) const +{ + time_t curTime = time(nullptr); + + std::stringstream ss; + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id); + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str()); + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(curTime - _createTime, true, false)).c_str()); + + std::string name; + if (ObjectMgr::GetPlayerNameByGUID(_assignedTo, name)) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, name.c_str()); + + if (detailed) + { + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, _note.c_str()); + if (!_comment.empty()) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, _comment.c_str()); + } + return ss.str(); +} + +ComplaintTicket::ComplaintTicket() : _facing(0.0f), _complaintType(GMTICKET_SUPPORT_COMPLAINT_TYPE_NONE) { } + +ComplaintTicket::ComplaintTicket(Player* player) : Ticket(player), _facing(0.0f), _complaintType(GMTICKET_SUPPORT_COMPLAINT_TYPE_NONE) +{ + _id = sSupportMgr->GenerateComplaintId(); +} + +ComplaintTicket::~ComplaintTicket() { } + +void ComplaintTicket::LoadFromDB(Field* fields) +{ + uint8 idx = 0; + _id = fields[ idx].GetUInt32(); + _playerGuid = ObjectGuid::Create<HighGuid::Player>(fields[++idx].GetUInt64()); + _note = fields[++idx].GetString(); + _createTime = fields[++idx].GetUInt32(); + _mapId = fields[++idx].GetUInt32(); + _pos.x = fields[++idx].GetFloat(); + _pos.y = fields[++idx].GetFloat(); + _pos.z = fields[++idx].GetFloat(); + _facing = fields[++idx].GetFloat(); + _targetCharacterGuid = ObjectGuid::Create<HighGuid::Player>(fields[++idx].GetUInt64()); + _complaintType = GMSupportComplaintType(fields[++idx].GetUInt8()); + int32 reportLineIndex = fields[++idx].GetInt32(); + if (reportLineIndex != -1) + _chatLog.ReportLineIndex.Set(reportLineIndex); + + int64 closedBy = fields[++idx].GetInt64(); + if (closedBy == 0) + _closedBy = ObjectGuid::Empty; + else if (closedBy < 0) + _closedBy.SetRawValue(0, uint64(closedBy)); + else + _closedBy = ObjectGuid::Create<HighGuid::Player>(uint64(closedBy)); + + uint64 assignedTo = fields[++idx].GetUInt64(); + if (assignedTo == 0) + _assignedTo = ObjectGuid::Empty; + else + _assignedTo = ObjectGuid::Create<HighGuid::Player>(assignedTo); + + _comment = fields[++idx].GetString(); +} + +void ComplaintTicket::LoadChatLineFromDB(Field* fields) +{ + _chatLog.Lines.emplace_back(fields[0].GetUInt32(), fields[1].GetString()); +} + +void ComplaintTicket::SaveToDB(SQLTransaction& trans) const +{ + bool isInTransaction = bool(trans); + if (!isInTransaction) + trans = CharacterDatabase.BeginTransaction(); + + uint8 idx = 0; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_COMPLAINT); + stmt->setUInt32(idx, _id); + stmt->setUInt64(++idx, _playerGuid.GetCounter()); + stmt->setString(++idx, _note); + stmt->setUInt32(++idx, _mapId); + stmt->setFloat(++idx, _pos.x); + stmt->setFloat(++idx, _pos.y); + stmt->setFloat(++idx, _pos.z); + stmt->setFloat(++idx, _facing); + stmt->setUInt64(++idx, _targetCharacterGuid.GetCounter()); + stmt->setUInt8(++idx, _complaintType); + if (_chatLog.ReportLineIndex.HasValue) + stmt->setInt32(++idx, _chatLog.ReportLineIndex.Value); + else + stmt->setInt32(++idx, -1); // empty ReportLineIndex + stmt->setInt64(++idx, _closedBy.GetCounter()); + stmt->setUInt64(++idx, _assignedTo.GetCounter()); + stmt->setString(++idx, _comment); + trans->Append(stmt); + + uint32 lineIndex = 0; + for (auto const& c : _chatLog.Lines) + { + idx = 0; + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_COMPLAINT_CHATLINE); + stmt->setUInt32(idx, _id); + stmt->setUInt32(++idx, lineIndex); + stmt->setUInt32(++idx, c.Timestamp); + stmt->setString(++idx, c.Text); + + trans->Append(stmt); + ++lineIndex; + } + + if (!isInTransaction) + CharacterDatabase.CommitTransaction(trans); +} + +void ComplaintTicket::DeleteFromDB() +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_COMPLAINT); + stmt->setUInt32(0, _id); + CharacterDatabase.Execute(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_COMPLAINT_CHATLOG); + stmt->setUInt32(0, _id); +} + +std::string ComplaintTicket::FormatViewMessageString(ChatHandler& handler, bool detailed) const +{ + time_t curTime = time(nullptr); + + std::stringstream ss; + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id); + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str()); + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(curTime - _createTime, true, false)).c_str()); + + std::string name; + if (ObjectMgr::GetPlayerNameByGUID(_assignedTo, name)) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, name.c_str()); + + if (detailed) + { + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, _note.c_str()); + if (!_comment.empty()) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, _comment.c_str()); + } + return ss.str(); +} + +SuggestionTicket::SuggestionTicket() : _facing(0.0f) { } + +SuggestionTicket::SuggestionTicket(Player* player) : Ticket(player) +{ + _id = sSupportMgr->GenerateSuggestionId(); +} + +SuggestionTicket::~SuggestionTicket() { } + +void SuggestionTicket::LoadFromDB(Field* fields) +{ + uint8 idx = 0; + _id = fields[ idx].GetUInt32(); + _playerGuid = ObjectGuid::Create<HighGuid::Player>(fields[++idx].GetUInt64()); + _note = fields[++idx].GetString(); + _createTime = fields[++idx].GetUInt32(); + _mapId = fields[++idx].GetUInt32(); + _pos.x = fields[++idx].GetFloat(); + _pos.y = fields[++idx].GetFloat(); + _pos.z = fields[++idx].GetFloat(); + _facing = fields[++idx].GetFloat(); + + int64 closedBy = fields[++idx].GetInt64(); + if (closedBy == 0) + _closedBy = ObjectGuid::Empty; + else if (closedBy < 0) + _closedBy.SetRawValue(0, uint64(closedBy)); + else + _closedBy = ObjectGuid::Create<HighGuid::Player>(uint64(closedBy)); + + uint64 assignedTo = fields[++idx].GetUInt64(); + if (assignedTo == 0) + _assignedTo = ObjectGuid::Empty; + else + _assignedTo = ObjectGuid::Create<HighGuid::Player>(assignedTo); + + _comment = fields[++idx].GetString(); +} + +void SuggestionTicket::SaveToDB(SQLTransaction& trans) const +{ + uint8 idx = 0; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_SUGGESTION); + stmt->setUInt32(idx, _id); + stmt->setUInt64(++idx, _playerGuid.GetCounter()); + stmt->setString(++idx, _note); + stmt->setUInt32(++idx, _mapId); + stmt->setFloat(++idx, _pos.x); + stmt->setFloat(++idx, _pos.y); + stmt->setFloat(++idx, _pos.z); + stmt->setFloat(++idx, _facing); + stmt->setInt64(++idx, _closedBy.GetCounter()); + stmt->setUInt64(++idx, _assignedTo.GetCounter()); + stmt->setString(++idx, _comment); + + CharacterDatabase.ExecuteOrAppend(trans, stmt); +} + +void SuggestionTicket::DeleteFromDB() +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_SUGGESTION); + stmt->setUInt32(0, _id); + CharacterDatabase.Execute(stmt); +} + +std::string SuggestionTicket::FormatViewMessageString(ChatHandler& handler, bool detailed) const +{ + time_t curTime = time(nullptr); + + std::stringstream ss; + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id); + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str()); + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(curTime - _createTime, true, false)).c_str()); + + std::string name; + if (ObjectMgr::GetPlayerNameByGUID(_assignedTo, name)) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, name.c_str()); + + if (detailed) + { + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, _note.c_str()); + if (!_comment.empty()) + ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, _comment.c_str()); + } + return ss.str(); +} + +SupportMgr::SupportMgr() : _lastGmTicketId(0), _lastBugId(0), _lastComplaintId(0), _lastSuggestionId(0), _openGmTicketCount(0), +_openBugTicketCount(0), _openComplaintTicketCount(0), _openSuggestionTicketCount(0) { } + +SupportMgr::~SupportMgr() +{ + for (auto const& t : _gmTicketList) + delete t.second; + + for (auto const& b : _bugTicketList) + delete b.second; + + for (auto const& c : _complaintTicketList) + delete c.second; + + for (auto const& s : _suggestionTicketList) + delete s.second; +} + +void SupportMgr::Initialize() +{ + SetSupportSystemStatus(sWorld->getBoolConfig(CONFIG_SUPPORT_ENABLED)); + SetTicketSystemStatus(sWorld->getBoolConfig(CONFIG_SUPPORT_TICKETS_ENABLED)); + SetBugSystemStatus(sWorld->getBoolConfig(CONFIG_SUPPORT_BUGS_ENABLED)); + SetComplaintSystemStatus(sWorld->getBoolConfig(CONFIG_SUPPORT_COMPLAINTS_ENABLED)); + SetSuggestionSystemStatus(sWorld->getBoolConfig(CONFIG_SUPPORT_SUGGESTIONS_ENABLED)); +} + +template<> +GmTicket* SupportMgr::GetTicket<GmTicket>(uint32 ticketId) +{ + GmTicketList::const_iterator itr = _gmTicketList.find(ticketId); + if (itr != _gmTicketList.end()) + return itr->second; + + return nullptr; + +} + +template<> +BugTicket* SupportMgr::GetTicket<BugTicket>(uint32 bugId) +{ + BugTicketList::const_iterator itr = _bugTicketList.find(bugId); + if (itr != _bugTicketList.end()) + return itr->second; + + return nullptr; + +} + +template<> +ComplaintTicket* SupportMgr::GetTicket<ComplaintTicket>(uint32 complaintId) +{ + ComplaintTicketList::const_iterator itr = _complaintTicketList.find(complaintId); + if (itr != _complaintTicketList.end()) + return itr->second; + + return nullptr; +} + +template<> +SuggestionTicket* SupportMgr::GetTicket<SuggestionTicket>(uint32 suggestionId) +{ + SuggestionTicketList::const_iterator itr = _suggestionTicketList.find(suggestionId); + if (itr != _suggestionTicketList.end()) + return itr->second; + + return nullptr; + +} + +template<> +uint32 SupportMgr::GetOpenTicketCount<GmTicket>() const { return _openGmTicketCount; } + +template<> +uint32 SupportMgr::GetOpenTicketCount<BugTicket>() const { return _openBugTicketCount; } + +template<> +uint32 SupportMgr::GetOpenTicketCount<ComplaintTicket>() const { return _openComplaintTicketCount; } + +template<> +uint32 SupportMgr::GetOpenTicketCount<SuggestionTicket>() const { return _openSuggestionTicketCount; } + +void SupportMgr::LoadGmTickets() +{ + uint32 oldMSTime = getMSTime(); + + for (auto const& c : _gmTicketList) + delete c.second; + _gmTicketList.clear(); + + _lastGmTicketId = 0; + _openGmTicketCount = 0; + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_TICKETS); + PreparedQueryResult result = CharacterDatabase.Query(stmt); + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 GM tickets. DB table `gm_ticket` is empty!"); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + GmTicket* ticket = new GmTicket(); + ticket->LoadFromDB(fields); + + if (!ticket->IsClosed()) + ++_openGmTicketCount; + + uint32 id = ticket->GetId(); + if (_lastGmTicketId < id) + _lastGmTicketId = id; + + _gmTicketList[id] = ticket; + ++count; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u GM tickets in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + +void SupportMgr::LoadBugTickets() +{ + uint32 oldMSTime = getMSTime(); + + for (auto const& c : _bugTicketList) + delete c.second; + _bugTicketList.clear(); + + _lastBugId = 0; + _openBugTicketCount = 0; + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_BUGS); + PreparedQueryResult result = CharacterDatabase.Query(stmt); + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 GM bugs. DB table `gm_bug` is empty!"); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + BugTicket* bug = new BugTicket(); + bug->LoadFromDB(fields); + + if (!bug->IsClosed()) + ++_openBugTicketCount; + + uint32 id = bug->GetId(); + if (_lastBugId < id) + _lastBugId = id; + + _bugTicketList[id] = bug; + ++count; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u GM bugs in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + +void SupportMgr::LoadComplaintTickets() +{ + uint32 oldMSTime = getMSTime(); + + for (auto const& c : _complaintTicketList) + delete c.second; + _complaintTicketList.clear(); + + _lastComplaintId = 0; + _openComplaintTicketCount = 0; + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_COMPLAINTS); + PreparedQueryResult result = CharacterDatabase.Query(stmt); + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 GM complaints. DB table `gm_complaint` is empty!"); + return; + } + + uint32 count = 0; + PreparedStatement* chatLogStmt; + PreparedQueryResult chatLogResult; + do + { + Field* fields = result->Fetch(); + ComplaintTicket* complaint = new ComplaintTicket(); + complaint->LoadFromDB(fields); + + if (!complaint->IsClosed()) + ++_openComplaintTicketCount; + + uint32 id = complaint->GetId(); + if (_lastComplaintId < id) + _lastComplaintId = id; + + chatLogStmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_COMPLAINT_CHATLINES); + chatLogResult = CharacterDatabase.Query(stmt); + + if (chatLogResult) + { + do + { + Field* chatLineFields = chatLogResult->Fetch(); + complaint->LoadChatLineFromDB(chatLineFields); + } while (chatLogResult->NextRow()); + } + + _complaintTicketList[id] = complaint; + ++count; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u GM complaints in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + +void SupportMgr::LoadSuggestionTickets() +{ + uint32 oldMSTime = getMSTime(); + + for (auto const& c : _suggestionTicketList) + delete c.second; + _suggestionTicketList.clear(); + + _lastSuggestionId = 0; + _openSuggestionTicketCount = 0; + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_SUGGESTIONS); + PreparedQueryResult result = CharacterDatabase.Query(stmt); + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 GM suggestions. DB table `gm_suggestion` is empty!"); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + SuggestionTicket* suggestion = new SuggestionTicket(); + suggestion->LoadFromDB(fields); + + if (!suggestion->IsClosed()) + ++_openSuggestionTicketCount; + + uint32 id = suggestion->GetId(); + if (_lastSuggestionId < id) + _lastSuggestionId = id; + + _suggestionTicketList[id] = suggestion; + ++count; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u GM suggestions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + +void SupportMgr::AddTicket(GmTicket* ticket) +{ + _gmTicketList[ticket->GetId()] = ticket; + if (!ticket->IsClosed()) + ++_openGmTicketCount; + + SQLTransaction trans = SQLTransaction(nullptr); + ticket->SaveToDB(trans); +} + +void SupportMgr::AddTicket(BugTicket* ticket) +{ + _bugTicketList[ticket->GetId()] = ticket; + if (!ticket->IsClosed()) + ++_openBugTicketCount; + + SQLTransaction trans = SQLTransaction(nullptr); + ticket->SaveToDB(trans); +} + +void SupportMgr::AddTicket(ComplaintTicket* ticket) +{ + _complaintTicketList[ticket->GetId()] = ticket; + if (!ticket->IsClosed()) + ++_openComplaintTicketCount; + + SQLTransaction trans = SQLTransaction(nullptr); + ticket->SaveToDB(trans); +} + +void SupportMgr::AddTicket(SuggestionTicket* ticket) +{ + _suggestionTicketList[ticket->GetId()] = ticket; + if (!ticket->IsClosed()) + ++_openSuggestionTicketCount; + + SQLTransaction trans = SQLTransaction(nullptr); + ticket->SaveToDB(trans); +} + +template<> +void SupportMgr::RemoveTicket<GmTicket>(uint32 ticketId) +{ + if (GmTicket* ticket = GetTicket<GmTicket>(ticketId)) + { + ticket->DeleteFromDB(); + _gmTicketList.erase(ticketId); + delete ticket; + } +} + +template<> +void SupportMgr::RemoveTicket<BugTicket>(uint32 ticketId) +{ + if (BugTicket* ticket = GetTicket<BugTicket>(ticketId)) + { + ticket->DeleteFromDB(); + _bugTicketList.erase(ticketId); + delete ticket; + } +} + +template<> +void SupportMgr::RemoveTicket<ComplaintTicket>(uint32 ticketId) +{ + if (ComplaintTicket* ticket = GetTicket<ComplaintTicket>(ticketId)) + { + ticket->DeleteFromDB(); + _complaintTicketList.erase(ticketId); + delete ticket; + } +} + +template<> +void SupportMgr::RemoveTicket<SuggestionTicket>(uint32 ticketId) +{ + if (SuggestionTicket* ticket = GetTicket<SuggestionTicket>(ticketId)) + { + ticket->DeleteFromDB(); + _suggestionTicketList.erase(ticketId); + delete ticket; + } +} + +template<> +void SupportMgr::CloseTicket<GmTicket>(uint32 ticketId, ObjectGuid closedBy) +{ + if (GmTicket* ticket = GetTicket<GmTicket>(ticketId)) + { + SQLTransaction trans = SQLTransaction(NULL); + ticket->SetClosedBy(closedBy); + if (!closedBy.IsEmpty()) + --_openGmTicketCount; + ticket->SaveToDB(trans); + } +} + +template<> +void SupportMgr::CloseTicket<BugTicket>(uint32 ticketId, ObjectGuid closedBy) +{ + if (BugTicket* ticket = GetTicket<BugTicket>(ticketId)) + { + SQLTransaction trans = SQLTransaction(NULL); + ticket->SetClosedBy(closedBy); + if (!closedBy.IsEmpty()) + --_openBugTicketCount; + ticket->SaveToDB(trans); + } +} + +template<> +void SupportMgr::CloseTicket<ComplaintTicket>(uint32 ticketId, ObjectGuid closedBy) +{ + if (ComplaintTicket* ticket = GetTicket<ComplaintTicket>(ticketId)) + { + SQLTransaction trans = SQLTransaction(NULL); + ticket->SetClosedBy(closedBy); + if (!closedBy.IsEmpty()) + --_openComplaintTicketCount; + ticket->SaveToDB(trans); + } +} + +template<> +void SupportMgr::CloseTicket<SuggestionTicket>(uint32 ticketId, ObjectGuid closedBy) +{ + if (SuggestionTicket* ticket = GetTicket<SuggestionTicket>(ticketId)) + { + SQLTransaction trans = SQLTransaction(NULL); + ticket->SetClosedBy(closedBy); + if (!closedBy.IsEmpty()) + --_openSuggestionTicketCount; + ticket->SaveToDB(trans); + } +} + +template<> +void SupportMgr::ResetTickets<GmTicket>() +{ + for (auto const& c : _gmTicketList) + delete c.second; + _gmTicketList.clear(); + + _lastGmTicketId = 0; + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_TICKETS); + CharacterDatabase.Execute(stmt); +} + +template<> +void SupportMgr::ResetTickets<BugTicket>() +{ + for (auto const& c : _bugTicketList) + delete c.second; + _bugTicketList.clear(); + + _lastBugId = 0; + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_BUGS); + CharacterDatabase.Execute(stmt); +} + +template<> +void SupportMgr::ResetTickets<ComplaintTicket>() +{ + for (auto const& c : _complaintTicketList) + delete c.second; + _complaintTicketList.clear(); + + _lastComplaintId = 0; + + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + trans->Append(CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_COMPLAINTS)); + trans->Append(CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_COMPLAINT_CHATLOGS)); + CharacterDatabase.CommitTransaction(trans); +} + +template<> +void SupportMgr::ResetTickets<SuggestionTicket>() +{ + for (auto const& c : _suggestionTicketList) + delete c.second; + _suggestionTicketList.clear(); + + _lastSuggestionId = 0; + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_SUGGESTIONS); + CharacterDatabase.Execute(stmt); +} + +template<> +void SupportMgr::ShowList<GmTicket>(ChatHandler& handler, bool onlineOnly) const +{ + handler.SendSysMessage(onlineOnly ? LANG_COMMAND_TICKETSHOWONLINELIST : LANG_COMMAND_TICKETSHOWLIST); + for (GmTicketList::const_iterator itr = _gmTicketList.begin(); itr != _gmTicketList.end(); ++itr) + if (!itr->second->IsClosed() && !itr->second->IsCompleted()) + if (!onlineOnly || itr->second->GetPlayer()) + handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str()); +} + +template<> +void SupportMgr::ShowList<GmTicket>(ChatHandler& handler) const +{ + handler.SendSysMessage(LANG_COMMAND_TICKETSHOWLIST); + for (GmTicketList::const_iterator itr = _gmTicketList.begin(); itr != _gmTicketList.end(); ++itr) + if (!itr->second->IsClosed() && !itr->second->IsCompleted()) + handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str()); +} + +template<> +void SupportMgr::ShowList<BugTicket>(ChatHandler& handler) const +{ + handler.SendSysMessage(LANG_COMMAND_TICKETSHOWLIST); + for (BugTicketList::const_iterator itr = _bugTicketList.begin(); itr != _bugTicketList.end(); ++itr) + if (!itr->second->IsClosed()) + handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str()); +} + +template<> +void SupportMgr::ShowList<ComplaintTicket>(ChatHandler& handler) const +{ + handler.SendSysMessage(LANG_COMMAND_TICKETSHOWLIST); + for (ComplaintTicketList::const_iterator itr = _complaintTicketList.begin(); itr != _complaintTicketList.end(); ++itr) + if (!itr->second->IsClosed()) + handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str()); +} + +template<> +void SupportMgr::ShowList<SuggestionTicket>(ChatHandler& handler) const +{ + handler.SendSysMessage(LANG_COMMAND_TICKETSHOWLIST); + for (SuggestionTicketList::const_iterator itr = _suggestionTicketList.begin(); itr != _suggestionTicketList.end(); ++itr) + if (!itr->second->IsClosed()) + handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str()); +} + +template<> +void SupportMgr::ShowClosedList<GmTicket>(ChatHandler& handler) const +{ + handler.SendSysMessage(LANG_COMMAND_TICKETSHOWCLOSEDLIST); + for (GmTicketList::const_iterator itr = _gmTicketList.begin(); itr != _gmTicketList.end(); ++itr) + if (itr->second->IsClosed()) + handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str()); +} + +template<> +void SupportMgr::ShowClosedList<BugTicket>(ChatHandler& handler) const +{ + handler.SendSysMessage(LANG_COMMAND_TICKETSHOWCLOSEDLIST); + for (BugTicketList::const_iterator itr = _bugTicketList.begin(); itr != _bugTicketList.end(); ++itr) + if (itr->second->IsClosed()) + handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str()); +} + +template<> +void SupportMgr::ShowClosedList<ComplaintTicket>(ChatHandler& handler) const +{ + handler.SendSysMessage(LANG_COMMAND_TICKETSHOWCLOSEDLIST); + for (ComplaintTicketList::const_iterator itr = _complaintTicketList.begin(); itr != _complaintTicketList.end(); ++itr) + if (itr->second->IsClosed()) + handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str()); +} + +template<> +void SupportMgr::ShowClosedList<SuggestionTicket>(ChatHandler& handler) const +{ + handler.SendSysMessage(LANG_COMMAND_TICKETSHOWCLOSEDLIST); + for (SuggestionTicketList::const_iterator itr = _suggestionTicketList.begin(); itr != _suggestionTicketList.end(); ++itr) + if (itr->second->IsClosed()) + handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str()); +} + +void SupportMgr::ShowGmEscalatedList(ChatHandler& handler) const +{ + handler.SendSysMessage(LANG_COMMAND_TICKETSHOWESCALATEDLIST); + for (GmTicketList::const_iterator itr = _gmTicketList.begin(); itr != _gmTicketList.end(); ++itr) + if (!itr->second->IsClosed() && itr->second->GetEscalatedStatus() == TICKET_IN_ESCALATION_QUEUE) + handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str()); +} + +void SupportMgr::SendGmTicket(WorldSession* session, GmTicket* ticket) const +{ + WorldPackets::Ticket::GMTicketGetTicketResponse response; + + if (ticket) + { + response.Result = GMTICKET_STATUS_HASTEXT; + response.Info.HasValue = true; + + response.Info.Value.TicketID = ticket->GetId(); + response.Info.Value.TicketDescription = ticket->GetDescription(); + response.Info.Value.Category = ticket->GetEscalatedStatus(); + response.Info.Value.TicketOpenTime = GetAge(ticket->GetLastModifiedTime()); + response.Info.Value.OldestTicketTime = sSupportMgr->GetOldestOpenTicket() ? GetAge(sSupportMgr->GetOldestOpenTicket()->GetLastModifiedTime()) : float(0); + response.Info.Value.UpdateTime = GetAge(sSupportMgr->GetLastChange()); + response.Info.Value.AssignedToGM = ticket->IsAssigned(); + response.Info.Value.OpenedByGM = ticket->IsViewed(); + response.Info.Value.WaitTimeOverrideMessage = ""; + response.Info.Value.WaitTimeOverrideMinutes = 0; + } + else + response.Result = GMTICKET_STATUS_DEFAULT; + + session->SendPacket(response.Write()); +} + +void SupportMgr::SendGmTicketUpdate(WorldSession* session, GMTicketResponse response) const +{ + WorldPackets::Ticket::GMTicketUpdate update; + update.Result = response; + + session->SendPacket(update.Write()); +} diff --git a/src/server/game/Support/SupportMgr.h b/src/server/game/Support/SupportMgr.h new file mode 100644 index 00000000000..eddb3cebf75 --- /dev/null +++ b/src/server/game/Support/SupportMgr.h @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef SupportMgr_h__ +#define SupportMgr_h__ + +#include "ObjectMgr.h" +#include "Player.h" +#include "TicketPackets.h" + +class ChatHandler; + +// from blizzard lua +enum GMTicketSystemStatus +{ + GMTICKET_QUEUE_STATUS_DISABLED = 0, + GMTICKET_QUEUE_STATUS_ENABLED = 1 +}; + +enum GMTicketStatus +{ + GMTICKET_STATUS_HASTEXT = 0x06, + GMTICKET_STATUS_DEFAULT = 0x0A +}; + +enum GMTicketResponse +{ + GMTICKET_RESPONSE_ALREADY_EXIST = 1, + GMTICKET_RESPONSE_CREATE_SUCCESS = 2, + GMTICKET_RESPONSE_CREATE_ERROR = 3, + GMTICKET_RESPONSE_UPDATE_SUCCESS = 4, + GMTICKET_RESPONSE_UPDATE_ERROR = 5, + GMTICKET_RESPONSE_TICKET_DELETED = 9 +}; + +// from blizzard lua +enum GMTicketOpenedByGMStatus +{ + GMTICKET_OPENEDBYGM_STATUS_NOT_OPENED = 0, // ticket has never been opened by a gm + GMTICKET_OPENEDBYGM_STATUS_OPENED = 1 // ticket has been opened by a gm +}; + +// from Blizzard LUA: +// GMTICKET_ASSIGNEDTOGM_STATUS_NOT_ASSIGNED = 0; -- ticket is not currently assigned to a gm +// GMTICKET_ASSIGNEDTOGM_STATUS_ASSIGNED = 1; -- ticket is assigned to a normal gm +// GMTICKET_ASSIGNEDTOGM_STATUS_ESCALATED = 2; -- ticket is in the escalation queue +// 3 is a custom value and should never actually be sent +enum GMTicketEscalationStatus +{ + TICKET_UNASSIGNED = 0, + TICKET_ASSIGNED = 1, + TICKET_IN_ESCALATION_QUEUE = 2, + TICKET_ESCALATED_ASSIGNED = 3 +}; + +enum GMSupportComplaintType +{ + GMTICKET_SUPPORT_COMPLAINT_TYPE_NONE = 0, + GMTICKET_SUPPORT_COMPLAINT_TYPE_LANGUAGE = 2, + GMTICKET_SUPPORT_COMPLAINT_TYPE_PLAYERNAME = 4, + GMTICKET_SUPPORT_COMPLAINT_TYPE_CHEAT = 15, + GMTICKET_SUPPORT_COMPLAINT_TYPE_GUILDNAME = 23, + GMTICKET_SUPPORT_COMPLAINT_TYPE_SPAMMING = 24 +}; + +using ChatLog = WorldPackets::Ticket::SupportTicketSubmitComplaint::SupportTicketChatLog; + +class Ticket +{ +public: + Ticket(); + Ticket(Player* player); + virtual ~Ticket(); + + bool IsClosed() const { return !_closedBy.IsEmpty(); } + bool IsFromPlayer(ObjectGuid guid) const { return guid == _playerGuid; } + bool IsAssigned() const { return !_assignedTo.IsEmpty(); } + bool IsAssignedTo(ObjectGuid guid) const { return guid == _assignedTo; } + bool IsAssignedNotTo(ObjectGuid guid) const { return IsAssigned() && !IsAssignedTo(guid); } + + uint32 GetId() const { return _id; } + ObjectGuid GetPlayerGuid() const { return _playerGuid; } + Player* GetPlayer() const { return ObjectAccessor::FindPlayer(_playerGuid); } + Player* GetAssignedPlayer() const { return ObjectAccessor::FindPlayer(_assignedTo); } + ObjectGuid GetAssignedToGUID() const { return _assignedTo; } + std::string GetAssignedToName() const + { + std::string name; + if (!_assignedTo.IsEmpty()) + ObjectMgr::GetPlayerNameByGUID(_assignedTo, name); + + return name; + } + std::string const& GetComment() { return _comment; } + + virtual void SetAssignedTo(ObjectGuid guid) { _assignedTo = guid; } + virtual void SetAssignedTo(ObjectGuid /*guid*/, bool /*isAdmin*/) { } + virtual void SetUnassigned() { _assignedTo.Clear(); } + void SetClosedBy(ObjectGuid value) { _closedBy = value; } + void SetComment(std::string const& comment) { _comment = comment; } + void SetPosition(uint32 mapId, G3D::Vector3& pos) + { + _mapId = mapId; + _pos = pos; + } + + virtual void LoadFromDB(Field* fields) = 0; + virtual void SaveToDB(SQLTransaction& trans) const = 0; + virtual void DeleteFromDB() = 0; + + void TeleportTo(Player* player) const; + + virtual std::string FormatViewMessageString(ChatHandler& handler, bool detailed = false) const = 0; + virtual std::string FormatViewMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName, const char* szCompletedName) const; + +protected: + uint32 _id; + ObjectGuid _playerGuid; + uint16 _mapId; + G3D::Vector3 _pos; + uint64 _createTime; + ObjectGuid _closedBy; // 0 = Open, -1 = Console, playerGuid = player abandoned ticket, other = GM who closed it. + ObjectGuid _assignedTo; + std::string _comment; +}; + + +class GmTicket : public Ticket +{ +public: + GmTicket(); + GmTicket(Player* player); + ~GmTicket(); + + bool IsCompleted() const { return _completed; } + bool IsViewed() const { return _viewed; } + + bool GetNeedMoreHelp() const { return _needMoreHelp; } + std::string const& GetDescription() const { return _description; } + uint64 GetLastModifiedTime() const { return _lastModifiedTime; } + GMTicketEscalationStatus GetEscalatedStatus() const { return _escalatedStatus; } + std::string const& GetResponse() const { return _response; } + + void SetAssignedTo(ObjectGuid guid, bool isAdmin) override + { + _assignedTo = guid; + if (isAdmin && _escalatedStatus == TICKET_IN_ESCALATION_QUEUE) + _escalatedStatus = TICKET_ESCALATED_ASSIGNED; + else if (_escalatedStatus == TICKET_UNASSIGNED) + _escalatedStatus = TICKET_ASSIGNED; + } + void SetEscalatedStatus(GMTicketEscalationStatus escalatedStatus) { _escalatedStatus = escalatedStatus; } + void SetCompleted() { _completed = true; } + void SetDescription(std::string const& description) + { + _description = description; + _lastModifiedTime = uint64(time(NULL)); + } + void SetViewed() { _viewed = true; } + void SetGmAction(uint32 needResponse, bool needMoreHelp); + void SetUnassigned() override; + + void AppendResponse(std::string const& response) { _response += response; } + + void SetChatLog(std::list<uint32> time, std::string const& log); + std::string const& GetChatLog() const { return _chatLog; } + + void SendResponse(WorldSession* session) const; + + void LoadFromDB(Field* fields) override; + void SaveToDB(SQLTransaction& trans) const override; + void DeleteFromDB() override; + + std::string FormatViewMessageString(ChatHandler& handler, bool detailed = false) const override; + std::string FormatViewMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName, const char* szCompletedName) const override; + +private: + std::string _description; + uint64 _lastModifiedTime; + bool _completed; + GMTicketEscalationStatus _escalatedStatus; + bool _viewed; + bool _needResponse; /// @todo find out the use of this, and then store it in DB + bool _needMoreHelp; + std::string _response; + std::string _chatLog; // No need to store in db, will be refreshed every session client side +}; + +class BugTicket : public Ticket +{ +public: + BugTicket(); + BugTicket(Player* player); + ~BugTicket(); + + std::string const& GetNote() const { return _note; } + + void SetFacing(float facing) { _facing = facing; } + void SetNote(std::string const& note) { _note = note; } + + void LoadFromDB(Field* fields) override; + void SaveToDB(SQLTransaction& trans) const override; + void DeleteFromDB() override; + + using Ticket::FormatViewMessageString; + std::string FormatViewMessageString(ChatHandler& handler, bool detailed = false) const override; + +private: + float _facing; + std::string _note; +}; + +class ComplaintTicket : public Ticket +{ +public: + ComplaintTicket(); + ComplaintTicket(Player* player); + ~ComplaintTicket(); + + ObjectGuid GetTargetCharacterGuid() const { return _targetCharacterGuid; } + GMSupportComplaintType GetComplaintType() const { return _complaintType; } + std::string const& GetNote() const { return _note; } + + void SetFacing(float facing) { _facing = facing; } + void SetTargetCharacterGuid(ObjectGuid targetCharacterGuid) + { + _targetCharacterGuid = targetCharacterGuid; + } + void SetComplaintType(GMSupportComplaintType type) { _complaintType = type; } + void SetChatLog(ChatLog const& log) { _chatLog = log; } + void SetNote(std::string const& note) { _note = note; } + + void LoadFromDB(Field* fields) override; + void LoadChatLineFromDB(Field* fields); + void SaveToDB(SQLTransaction& trans) const override; + void DeleteFromDB() override; + + using Ticket::FormatViewMessageString; + std::string FormatViewMessageString(ChatHandler& handler, bool detailed = false) const override; + +private: + float _facing; + ObjectGuid _targetCharacterGuid; + GMSupportComplaintType _complaintType; + ChatLog _chatLog; + std::string _note; +}; + +class SuggestionTicket : public Ticket +{ +public: + SuggestionTicket(); + SuggestionTicket(Player* player); + ~SuggestionTicket(); + + std::string const& GetNote() const { return _note; } + void SetNote(std::string const& note) { _note = note; } + + void SetFacing(float facing) { _facing = facing; } + + void LoadFromDB(Field* fields) override; + void SaveToDB(SQLTransaction& trans) const override; + void DeleteFromDB() override; + + using Ticket::FormatViewMessageString; + std::string FormatViewMessageString(ChatHandler& handler, bool detailed = false) const override; + +private: + float _facing; + std::string _note; +}; + +typedef std::map<uint32, BugTicket*> BugTicketList; +typedef std::map<uint32, ComplaintTicket*> ComplaintTicketList; +typedef std::map<uint32, SuggestionTicket*> SuggestionTicketList; +typedef std::map<uint32, GmTicket*> GmTicketList; + +class SupportMgr +{ +private: + SupportMgr(); + ~SupportMgr(); + +public: + static SupportMgr* instance() + { + static SupportMgr instance; + return &instance; + } + + template<typename T> + T* GetTicket(uint32 ticketId); + + GmTicket* GetGmTicketByPlayerGuid(ObjectGuid playerGuid) const + { + for (auto const& c : _gmTicketList) + if (c.second->GetPlayerGuid() == playerGuid && !c.second->IsClosed()) + return c.second; + + return nullptr; + } + + ComplaintTicketList GetComplaintsByPlayerGuid(ObjectGuid playerGuid) const + { + ComplaintTicketList ret; + for (auto const& c : _complaintTicketList) + if (c.second->GetPlayerGuid() == playerGuid) + ret.insert(c); + + return ret; + } + + GmTicket* GetOldestOpenTicket() + { + for (GmTicketList::const_iterator itr = _gmTicketList.begin(); itr != _gmTicketList.end(); ++itr) + if (itr->second && !itr->second->IsClosed() && !itr->second->IsCompleted()) + return itr->second; + + return nullptr; + } + + void Initialize(); + + bool GetSupportSystemStatus() { return _supportSystemStatus; } + bool GetTicketSystemStatus() { return _supportSystemStatus && _ticketSystemStatus; } + bool GetBugSystemStatus() { return _supportSystemStatus && _bugSystemStatus; } + bool GetComplaintSystemStatus() { return _supportSystemStatus && _complaintSystemStatus; } + bool GetSuggestionSystemStatus() { return _supportSystemStatus && _suggestionSystemStatus; } + uint64 GetLastChange() const { return _lastChange; } + template<typename T> + uint32 GetOpenTicketCount() const; + + void SetSupportSystemStatus(bool status) { _supportSystemStatus = status; } + void SetTicketSystemStatus(bool status) { _ticketSystemStatus = status; } + void SetBugSystemStatus(bool status) { _bugSystemStatus = status; } + void SetComplaintSystemStatus(bool status) { _complaintSystemStatus = status; } + void SetSuggestionSystemStatus(bool status) { _suggestionSystemStatus = status; } + + void LoadGmTickets(); + void LoadBugTickets(); + void LoadComplaintTickets(); + void LoadSuggestionTickets(); + + void AddTicket(GmTicket* ticket); + void AddTicket(BugTicket* ticket); + void AddTicket(ComplaintTicket* ticket); + void AddTicket(SuggestionTicket* ticket); + + template<typename T> + void RemoveTicket(uint32 ticketId); + + template<typename T> + void CloseTicket(uint32 ticketId, ObjectGuid closedBy); + + template<typename T> + void ResetTickets(); + + template<typename T> + void ShowList(ChatHandler& handler) const; + + template<typename T> + void ShowList(ChatHandler& handler, bool onlineOnly) const; + + template<typename T> + void ShowClosedList(ChatHandler& handler) const; + + void ShowGmEscalatedList(ChatHandler& handler) const; + + void UpdateLastChange() { _lastChange = uint64(time(nullptr)); } + + void SendGmTicket(WorldSession* session, GmTicket* ticket) const; + void SendGmTicketUpdate(WorldSession* session, GMTicketResponse response) const; + + uint32 GenerateGmTicketId() { return ++_lastGmTicketId; } + uint32 GenerateBugId() { return ++_lastBugId; } + uint32 GenerateComplaintId() { return ++_lastComplaintId; } + uint32 GenerateSuggestionId() { return ++_lastSuggestionId; } + +private: + bool _supportSystemStatus; + bool _ticketSystemStatus; + bool _bugSystemStatus; + bool _complaintSystemStatus; + bool _suggestionSystemStatus; + GmTicketList _gmTicketList; + BugTicketList _bugTicketList; + ComplaintTicketList _complaintTicketList; + SuggestionTicketList _suggestionTicketList; + uint32 _lastGmTicketId; + uint32 _lastBugId; + uint32 _lastComplaintId; + uint32 _lastSuggestionId; + uint32 _openGmTicketCount; + uint32 _openBugTicketCount; + uint32 _openComplaintTicketCount; + uint32 _openSuggestionTicketCount; + uint64 _lastChange; +}; + +#define sSupportMgr SupportMgr::instance() + +#endif // SupportMgr_h__ diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp deleted file mode 100644 index 84dac6cc175..00000000000 --- a/src/server/game/Tickets/TicketMgr.cpp +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#include "Common.h" -#include "TicketMgr.h" -#include "TicketPackets.h" -#include "DatabaseEnv.h" -#include "Log.h" -#include "Language.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Chat.h" -#include "World.h" -#include "Player.h" -#include "Opcodes.h" - -inline time_t GetAge(uint64 t) { return (time(NULL) - t) / DAY; } - -/////////////////////////////////////////////////////////////////////////////////////////////////// -// GM ticket -GmTicket::GmTicket() : _id(0), _posX(0), _posY(0), _posZ(0), _mapId(0), _createTime(0), _lastModifiedTime(0), - _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _viewed(false), - _needResponse(false), _needMoreHelp(false) { } - -GmTicket::GmTicket(Player* player) : _posX(0), _posY(0), _posZ(0), _mapId(0), _createTime(time(NULL)), _lastModifiedTime(time(NULL)), - _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _viewed(false), - _needResponse(false), _needMoreHelp(false) -{ - _id = sTicketMgr->GenerateTicketId(); - _playerName = player->GetName(); - _playerGuid = player->GetGUID(); -} - -GmTicket::~GmTicket() { } - -bool GmTicket::LoadFromDB(Field* fields) -{ - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - // ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, haveTicket - uint8 index = 0; - _id = fields[ index].GetUInt32(); - _playerGuid = ObjectGuid::Create<HighGuid::Player>(fields[++index].GetUInt64()); - _playerName = fields[++index].GetString(); - _message = fields[++index].GetString(); - _createTime = fields[++index].GetUInt32(); - _mapId = fields[++index].GetUInt16(); - _posX = fields[++index].GetFloat(); - _posY = fields[++index].GetFloat(); - _posZ = fields[++index].GetFloat(); - _lastModifiedTime = fields[++index].GetUInt32(); - int64 closedBy = fields[++index].GetInt64(); - if (closedBy < 0) - _closedBy.SetRawValue(0, uint64(closedBy)); - else - _closedBy = ObjectGuid::Create<HighGuid::Player>(uint64(closedBy)); - - _assignedTo = ObjectGuid::Create<HighGuid::Player>(fields[++index].GetUInt64()); - _comment = fields[++index].GetString(); - _response = fields[++index].GetString(); - _completed = fields[++index].GetBool(); - _escalatedStatus = GMTicketEscalationStatus(fields[++index].GetUInt8()); - _viewed = fields[++index].GetBool(); - _needMoreHelp = fields[++index].GetBool(); - return true; -} - -void GmTicket::SaveToDB(SQLTransaction& trans) const -{ - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - // ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, completed, escalated, viewed - uint8 index = 0; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_TICKET); - stmt->setUInt32( index, _id); - stmt->setUInt64(++index, _playerGuid.GetCounter()); - stmt->setString(++index, _playerName); - stmt->setString(++index, _message); - stmt->setUInt32(++index, uint32(_createTime)); - stmt->setUInt16(++index, _mapId); - stmt->setFloat (++index, _posX); - stmt->setFloat (++index, _posY); - stmt->setFloat (++index, _posZ); - stmt->setUInt32(++index, uint32(_lastModifiedTime)); - stmt->setInt64 (++index, int64(_closedBy.GetCounter())); - stmt->setUInt64(++index, _assignedTo.GetCounter()); - stmt->setString(++index, _comment); - stmt->setString(++index, _response); - stmt->setBool (++index, _completed); - stmt->setUInt8 (++index, uint8(_escalatedStatus)); - stmt->setBool (++index, _viewed); - stmt->setBool (++index, _needMoreHelp); - - CharacterDatabase.ExecuteOrAppend(trans, stmt); -} - -void GmTicket::DeleteFromDB() -{ - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_TICKET); - stmt->setUInt32(0, _id); - CharacterDatabase.Execute(stmt); -} - -void GmTicket::SendResponse(WorldSession* session) const -{ - WorldPacket data(SMSG_GM_TICKET_RESPONSE); - data << uint32(1); // responseID - data << uint32(_id); // ticketID - data << _message.c_str(); - - size_t len = _response.size(); - char const* s = _response.c_str(); - - for (int i = 0; i < 4; i++) - { - if (len) - { - size_t writeLen = std::min<size_t>(len, 3999); - data.append(s, writeLen); - - len -= writeLen; - s += writeLen; - } - - data << uint8(0); - } - - session->SendPacket(&data); -} - -std::string GmTicket::FormatMessageString(ChatHandler& handler, bool detailed) const -{ - time_t curTime = time(NULL); - - std::stringstream ss; - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id); - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, _playerName.c_str()); - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(curTime - _createTime, true, false)).c_str()); - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(curTime - _lastModifiedTime, true, false)).c_str()); - - std::string name; - if (ObjectMgr::GetPlayerNameByGUID(_assignedTo, name)) - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, name.c_str()); - - if (detailed) - { - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, _message.c_str()); - if (!_comment.empty()) - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, _comment.c_str()); - if (!_response.empty()) - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTRESPONSE, _response.c_str()); - } - return ss.str(); -} - -std::string GmTicket::FormatMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName, const char* szCompletedName) const -{ - std::stringstream ss; - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id); - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, _playerName.c_str()); - if (szClosedName) - ss << handler.PGetParseString(LANG_COMMAND_TICKETCLOSED, szClosedName); - if (szAssignedToName) - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, szAssignedToName); - if (szUnassignedName) - ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTUNASSIGNED, szUnassignedName); - if (szDeletedName) - ss << handler.PGetParseString(LANG_COMMAND_TICKETDELETED, szDeletedName); - if (szCompletedName) - ss << handler.PGetParseString(LANG_COMMAND_TICKETCOMPLETED, szCompletedName); - return ss.str(); -} - -void GmTicket::SetUnassigned() -{ - _assignedTo.Clear(); - switch (_escalatedStatus) - { - case TICKET_ASSIGNED: _escalatedStatus = TICKET_UNASSIGNED; break; - case TICKET_ESCALATED_ASSIGNED: _escalatedStatus = TICKET_IN_ESCALATION_QUEUE; break; - case TICKET_UNASSIGNED: - case TICKET_IN_ESCALATION_QUEUE: - default: - break; - } -} - -void GmTicket::SetPosition(uint32 mapId, float x, float y, float z) -{ - _mapId = mapId; - _posX = x; - _posY = y; - _posZ = z; -} - -void GmTicket::SetGmAction(uint32 needResponse, bool needMoreHelp) -{ - _needResponse = (needResponse == 17); // Requires GM response. 17 = true, 1 = false (17 is default) - _needMoreHelp = needMoreHelp; // Requests further GM interaction on a ticket to which a GM has already responded. Basically means "has a new ticket" -} - -void GmTicket::TeleportTo(Player* player) const -{ - player->TeleportTo(_mapId, _posX, _posY, _posZ, 0.0f, 0); -} - -void GmTicket::SetChatLog(std::list<uint32> time, std::string const& log) -{ - std::stringstream ss(log); - std::stringstream newss; - std::string line; - while (std::getline(ss, line) && !time.empty()) - { - newss << secsToTimeString(time.front()) << ": " << line << "\n"; - time.pop_front(); - } - - _chatLog = newss.str(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Ticket manager -TicketMgr::TicketMgr() : _status(true), _lastTicketId(0), _lastSurveyId(0), _openTicketCount(0), - _lastChange(time(NULL)) { } - -TicketMgr::~TicketMgr() -{ - for (GmTicketList::const_iterator itr = _ticketList.begin(); itr != _ticketList.end(); ++itr) - delete itr->second; -} - -void TicketMgr::Initialize() -{ - SetStatus(sWorld->getBoolConfig(CONFIG_TICKET_SYSTEM_STATUS)); -} - -void TicketMgr::ResetTickets() -{ - for (GmTicketList::const_iterator itr = _ticketList.begin(); itr != _ticketList.end();) - { - if (itr->second->IsClosed()) - { - uint32 ticketId = itr->second->GetId(); - ++itr; - sTicketMgr->RemoveTicket(ticketId); - } - else - ++itr; - } - - _lastTicketId = 0; - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_TICKETS); - - CharacterDatabase.Execute(stmt); -} - -void TicketMgr::LoadTickets() -{ - uint32 oldMSTime = getMSTime(); - - for (GmTicketList::const_iterator itr = _ticketList.begin(); itr != _ticketList.end(); ++itr) - delete itr->second; - _ticketList.clear(); - - _lastTicketId = 0; - _openTicketCount = 0; - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_TICKETS); - PreparedQueryResult result = CharacterDatabase.Query(stmt); - if (!result) - { - TC_LOG_INFO("server.loading", ">> Loaded 0 GM tickets. DB table `gm_tickets` is empty!"); - - return; - } - - uint32 count = 0; - do - { - Field* fields = result->Fetch(); - GmTicket* ticket = new GmTicket(); - if (!ticket->LoadFromDB(fields)) - { - delete ticket; - continue; - } - if (!ticket->IsClosed()) - ++_openTicketCount; - - // Update max ticket id if necessary - uint32 id = ticket->GetId(); - if (_lastTicketId < id) - _lastTicketId = id; - - _ticketList[id] = ticket; - ++count; - } while (result->NextRow()); - - TC_LOG_INFO("server.loading", ">> Loaded %u GM tickets in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - -} - -void TicketMgr::LoadSurveys() -{ - // we don't actually load anything into memory here as there's no reason to - _lastSurveyId = 0; - - uint32 oldMSTime = getMSTime(); - if (QueryResult result = CharacterDatabase.Query("SELECT MAX(surveyId) FROM gm_surveys")) - _lastSurveyId = (*result)[0].GetUInt32(); - - TC_LOG_INFO("server.loading", ">> Loaded GM Survey count from database in %u ms", GetMSTimeDiffToNow(oldMSTime)); - -} - -void TicketMgr::AddTicket(GmTicket* ticket) -{ - _ticketList[ticket->GetId()] = ticket; - if (!ticket->IsClosed()) - ++_openTicketCount; - SQLTransaction trans = SQLTransaction(NULL); - ticket->SaveToDB(trans); -} - -void TicketMgr::CloseTicket(uint32 ticketId, ObjectGuid source) -{ - if (GmTicket* ticket = GetTicket(ticketId)) - { - SQLTransaction trans = SQLTransaction(NULL); - ticket->SetClosedBy(source); - if (!source.IsEmpty()) - --_openTicketCount; - ticket->SaveToDB(trans); - } -} - -void TicketMgr::RemoveTicket(uint32 ticketId) -{ - if (GmTicket* ticket = GetTicket(ticketId)) - { - ticket->DeleteFromDB(); - _ticketList.erase(ticketId); - delete ticket; - } -} - -void TicketMgr::ShowList(ChatHandler& handler, bool onlineOnly) const -{ - handler.SendSysMessage(onlineOnly ? LANG_COMMAND_TICKETSHOWONLINELIST : LANG_COMMAND_TICKETSHOWLIST); - for (GmTicketList::const_iterator itr = _ticketList.begin(); itr != _ticketList.end(); ++itr) - if (!itr->second->IsClosed() && !itr->second->IsCompleted()) - if (!onlineOnly || itr->second->GetPlayer()) - handler.SendSysMessage(itr->second->FormatMessageString(handler).c_str()); -} - -void TicketMgr::ShowClosedList(ChatHandler& handler) const -{ - handler.SendSysMessage(LANG_COMMAND_TICKETSHOWCLOSEDLIST); - for (GmTicketList::const_iterator itr = _ticketList.begin(); itr != _ticketList.end(); ++itr) - if (itr->second->IsClosed()) - handler.SendSysMessage(itr->second->FormatMessageString(handler).c_str()); -} - -void TicketMgr::ShowEscalatedList(ChatHandler& handler) const -{ - handler.SendSysMessage(LANG_COMMAND_TICKETSHOWESCALATEDLIST); - for (GmTicketList::const_iterator itr = _ticketList.begin(); itr != _ticketList.end(); ++itr) - if (!itr->second->IsClosed() && itr->second->GetEscalatedStatus() == TICKET_IN_ESCALATION_QUEUE) - handler.SendSysMessage(itr->second->FormatMessageString(handler).c_str()); -} - -void TicketMgr::SendTicket(WorldSession* session, GmTicket* ticket) const -{ - WorldPackets::Ticket::GMTicketGetTicketResponse response; - - if (ticket) - { - response.Result = GMTICKET_STATUS_HASTEXT; - response.Info.HasValue = true; - - response.Info.Value.TicketID = ticket->GetId(); - response.Info.Value.TicketDescription = ticket->GetMessage(); - response.Info.Value.Category = ticket->GetNeedMoreHelp(); - response.Info.Value.TicketOpenTime = GetAge(ticket->GetLastModifiedTime()); - response.Info.Value.OldestTicketTime = sTicketMgr->GetOldestOpenTicket() ? GetAge(sTicketMgr->GetOldestOpenTicket()->GetLastModifiedTime()) : float(0); - response.Info.Value.UpdateTime = GetAge(sTicketMgr->GetLastChange()); - response.Info.Value.AssignedToGM = ticket->IsAssigned(); - response.Info.Value.OpenedByGM = ticket->IsViewed(); - response.Info.Value.WaitTimeOverrideMessage = ""; - response.Info.Value.WaitTimeOverrideMinutes = 0; - } - else - response.Result = GMTICKET_STATUS_DEFAULT; - - session->SendPacket(response.Write()); -} diff --git a/src/server/game/Tickets/TicketMgr.h b/src/server/game/Tickets/TicketMgr.h deleted file mode 100644 index 577f29062b8..00000000000 --- a/src/server/game/Tickets/TicketMgr.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#ifndef _TICKETMGR_H -#define _TICKETMGR_H - -#include <string> - -#include "ObjectMgr.h" - -class ChatHandler; - -// from blizzard lua -enum GMTicketSystemStatus -{ - GMTICKET_QUEUE_STATUS_DISABLED = 0, - GMTICKET_QUEUE_STATUS_ENABLED = 1 -}; - -enum GMTicketStatus -{ - GMTICKET_STATUS_HASTEXT = 0x06, - GMTICKET_STATUS_DEFAULT = 0x0A -}; - -enum GMTicketResponse -{ - GMTICKET_RESPONSE_ALREADY_EXIST = 1, - GMTICKET_RESPONSE_CREATE_SUCCESS = 2, - GMTICKET_RESPONSE_CREATE_ERROR = 3, - GMTICKET_RESPONSE_UPDATE_SUCCESS = 4, - GMTICKET_RESPONSE_UPDATE_ERROR = 5, - GMTICKET_RESPONSE_TICKET_DELETED = 9 -}; - -// from Blizzard LUA: -// GMTICKET_ASSIGNEDTOGM_STATUS_NOT_ASSIGNED = 0; -- ticket is not currently assigned to a gm -// GMTICKET_ASSIGNEDTOGM_STATUS_ASSIGNED = 1; -- ticket is assigned to a normal gm -// GMTICKET_ASSIGNEDTOGM_STATUS_ESCALATED = 2; -- ticket is in the escalation queue -// 3 is a custom value and should never actually be sent -enum GMTicketEscalationStatus -{ - TICKET_UNASSIGNED = 0, - TICKET_ASSIGNED = 1, - TICKET_IN_ESCALATION_QUEUE = 2, - TICKET_ESCALATED_ASSIGNED = 3 -}; - -// from blizzard lua -enum GMTicketOpenedByGMStatus -{ - GMTICKET_OPENEDBYGM_STATUS_NOT_OPENED = 0, // ticket has never been opened by a gm - GMTICKET_OPENEDBYGM_STATUS_OPENED = 1 // ticket has been opened by a gm -}; - -enum LagReportType -{ - LAG_REPORT_TYPE_LOOT = 1, - LAG_REPORT_TYPE_AUCTION_HOUSE = 2, - LAG_REPORT_TYPE_MAIL = 3, - LAG_REPORT_TYPE_CHAT = 4, - LAG_REPORT_TYPE_MOVEMENT = 5, - LAG_REPORT_TYPE_SPELL = 6 -}; - -class GmTicket -{ -public: - GmTicket(); - GmTicket(Player* player); - ~GmTicket(); - - bool IsClosed() const { return !_closedBy.IsEmpty(); } - bool IsCompleted() const { return _completed; } - bool IsFromPlayer(ObjectGuid guid) const { return guid == _playerGuid; } - bool IsAssigned() const { return !_assignedTo.IsEmpty(); } - bool IsAssignedTo(ObjectGuid guid) const { return guid == _assignedTo; } - bool IsAssignedNotTo(ObjectGuid guid) const { return IsAssigned() && !IsAssignedTo(guid); } - bool IsViewed() const { return _viewed; } - - uint32 GetId() const { return _id; } - Player* GetPlayer() const { return ObjectAccessor::FindPlayer(_playerGuid); } - std::string const& GetPlayerName() const { return _playerName; } - std::string const& GetMessage() const { return _message; } - bool GetNeedMoreHelp() const { return _needMoreHelp; } - Player* GetAssignedPlayer() const { return ObjectAccessor::FindPlayer(_assignedTo); } - ObjectGuid GetAssignedToGUID() const { return _assignedTo; } - std::string GetAssignedToName() const - { - std::string name; - // save queries if ticket is not assigned - if (!_assignedTo.IsEmpty()) - ObjectMgr::GetPlayerNameByGUID(_assignedTo, name); - - return name; - } - uint64 GetLastModifiedTime() const { return _lastModifiedTime; } - GMTicketEscalationStatus GetEscalatedStatus() const { return _escalatedStatus; } - - void SetEscalatedStatus(GMTicketEscalationStatus escalatedStatus) { _escalatedStatus = escalatedStatus; } - void SetAssignedTo(ObjectGuid guid, bool isAdmin) - { - _assignedTo = guid; - if (isAdmin && _escalatedStatus == TICKET_IN_ESCALATION_QUEUE) - _escalatedStatus = TICKET_ESCALATED_ASSIGNED; - else if (_escalatedStatus == TICKET_UNASSIGNED) - _escalatedStatus = TICKET_ASSIGNED; - } - void SetClosedBy(ObjectGuid value) { _closedBy = value; } - void SetCompleted() { _completed = true; } - void SetMessage(std::string const& message) - { - _message = message; - _lastModifiedTime = uint64(time(NULL)); - } - void SetComment(std::string const& comment) { _comment = comment; } - void SetViewed() { _viewed = true; } - void SetUnassigned(); - void SetPosition(uint32 mapId, float x, float y, float z); - void SetGmAction(uint32 needResponse, bool needMoreHelp); - - void AppendResponse(std::string const& response) { _response += response; } - - bool LoadFromDB(Field* fields); - void SaveToDB(SQLTransaction& trans) const; - void DeleteFromDB(); - - void WritePacket(WorldPacket& data) const; - void SendResponse(WorldSession* session) const; - - void TeleportTo(Player* player) const; - std::string FormatMessageString(ChatHandler& handler, bool detailed = false) const; - std::string FormatMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName, const char* szCompletedName) const; - - void SetChatLog(std::list<uint32> time, std::string const& log); - std::string const& GetChatLog() const { return _chatLog; } - -private: - uint32 _id; - ObjectGuid _playerGuid; - std::string _playerName; - float _posX; - float _posY; - float _posZ; - uint16 _mapId; - std::string _message; - uint64 _createTime; - uint64 _lastModifiedTime; - ObjectGuid _closedBy; // 0 = Open, -1 = Console, playerGuid = player abandoned ticket, other = GM who closed it. - ObjectGuid _assignedTo; - std::string _comment; - bool _completed; - GMTicketEscalationStatus _escalatedStatus; - bool _viewed; - bool _needResponse; /// @todo find out the use of this, and then store it in DB - bool _needMoreHelp; - std::string _response; - std::string _chatLog; // No need to store in db, will be refreshed every session client side -}; -typedef std::map<uint32, GmTicket*> GmTicketList; - -class TicketMgr -{ -private: - TicketMgr(); - ~TicketMgr(); - -public: - static TicketMgr* instance() - { - static TicketMgr instance; - return &instance; - } - - void LoadTickets(); - void LoadSurveys(); - - GmTicket* GetTicket(uint32 ticketId) - { - GmTicketList::iterator itr = _ticketList.find(ticketId); - if (itr != _ticketList.end()) - return itr->second; - - return NULL; - } - - GmTicket* GetTicketByPlayer(ObjectGuid playerGuid) - { - for (GmTicketList::const_iterator itr = _ticketList.begin(); itr != _ticketList.end(); ++itr) - if (itr->second && itr->second->IsFromPlayer(playerGuid) && !itr->second->IsClosed()) - return itr->second; - - return NULL; - } - - GmTicket* GetOldestOpenTicket() - { - for (GmTicketList::const_iterator itr = _ticketList.begin(); itr != _ticketList.end(); ++itr) - if (itr->second && !itr->second->IsClosed() && !itr->second->IsCompleted()) - return itr->second; - - return NULL; - } - - void AddTicket(GmTicket* ticket); - void CloseTicket(uint32 ticketId, ObjectGuid source); - void RemoveTicket(uint32 ticketId); - - bool GetStatus() const { return _status; } - void SetStatus(bool status) { _status = status; } - - uint64 GetLastChange() const { return _lastChange; } - void UpdateLastChange() { _lastChange = uint64(time(NULL)); } - - uint32 GenerateTicketId() { return ++_lastTicketId; } - uint32 GetOpenTicketCount() const { return _openTicketCount; } - uint32 GetNextSurveyID() { return ++_lastSurveyId; } - - void Initialize(); - void ResetTickets(); - - void ShowList(ChatHandler& handler, bool onlineOnly) const; - void ShowClosedList(ChatHandler& handler) const; - void ShowEscalatedList(ChatHandler& handler) const; - - void SendTicket(WorldSession* session, GmTicket* ticket) const; - -protected: - void _RemoveTicket(uint32 ticketId, int64 source = -1, bool permanently = false); - - GmTicketList _ticketList; - - bool _status; - uint32 _lastTicketId; - uint32 _lastSurveyId; - uint32 _openTicketCount; - uint64 _lastChange; -}; - -#define sTicketMgr TicketMgr::instance() - -#endif // _TICKETMGR_H diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index c51c9e3ecc4..561cd5bb3a0 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -57,7 +57,7 @@ #include "SkillExtraItems.h" #include "SmartAI.h" #include "SystemConfig.h" -#include "TicketMgr.h" +#include "SupportMgr.h" #include "TransportMgr.h" #include "Unit.h" #include "VMapFactory.h" @@ -427,12 +427,22 @@ void World::LoadConfigSettings(bool reload) SetPlayerAmountLimit(sConfigMgr->GetIntDefault("PlayerLimit", 100)); SetMotd(sConfigMgr->GetStringDefault("Motd", "Welcome to a Trinity Core Server.")); - ///- Read ticket system setting from the config file - m_bool_configs[CONFIG_TICKET_SYSTEM_STATUS] = sConfigMgr->GetBoolDefault("Ticket.SystemStatus", true); + ///- Read support system setting from the config file + m_bool_configs[CONFIG_SUPPORT_ENABLED] = sConfigMgr->GetBoolDefault("Support.Enabled", true); + m_bool_configs[CONFIG_SUPPORT_TICKETS_ENABLED] = sConfigMgr->GetBoolDefault("Support.TicketsEnabled", false); + m_bool_configs[CONFIG_SUPPORT_BUGS_ENABLED] = sConfigMgr->GetBoolDefault("Support.BugsEnabled", false); + m_bool_configs[CONFIG_SUPPORT_COMPLAINTS_ENABLED] = sConfigMgr->GetBoolDefault("Support.ComplaintsEnabled", false); + m_bool_configs[CONFIG_SUPPORT_SUGGESTIONS_ENABLED] = sConfigMgr->GetBoolDefault("Support.SuggestionsEnabled", false); if (reload) - sTicketMgr->SetStatus(m_bool_configs[CONFIG_TICKET_SYSTEM_STATUS]); - m_bool_configs[CONFIG_TICKET_SUBMIT_TICKET] = sConfigMgr->GetBoolDefault("Ticket.SubmitTicket", false); - m_bool_configs[CONFIG_TICKET_SUBMIT_BUG] = sConfigMgr->GetBoolDefault("Ticket.SubmitBug", false); + { + sSupportMgr->SetSupportSystemStatus(m_bool_configs[CONFIG_SUPPORT_ENABLED]); + sSupportMgr->SetTicketSystemStatus(m_bool_configs[CONFIG_SUPPORT_TICKETS_ENABLED]); + sSupportMgr->SetBugSystemStatus(m_bool_configs[CONFIG_SUPPORT_BUGS_ENABLED]); + sSupportMgr->SetComplaintSystemStatus(m_bool_configs[CONFIG_SUPPORT_COMPLAINTS_ENABLED]); + sSupportMgr->SetSuggestionSystemStatus(m_bool_configs[CONFIG_SUPPORT_SUGGESTIONS_ENABLED]); + } + m_float_configs[CONFIG_CHANCE_OF_GM_SURVEY] = sConfigMgr->GetFloatDefault("Support.ChanceOfGMSurvey", 50.0f); + ///- Get string for new logins (newly created characters) SetNewCharString(sConfigMgr->GetStringDefault("PlayerStart.String", "")); @@ -954,7 +964,6 @@ void World::LoadConfigSettings(bool reload) } m_bool_configs[CONFIG_ALLOW_GM_GROUP] = sConfigMgr->GetBoolDefault("GM.AllowInvite", false); m_bool_configs[CONFIG_GM_LOWER_SECURITY] = sConfigMgr->GetBoolDefault("GM.LowerSecurity", false); - m_float_configs[CONFIG_CHANCE_OF_GM_SURVEY] = sConfigMgr->GetFloatDefault("GM.TicketSystem.ChanceOfGMSurvey", 50.0f); m_int_configs[CONFIG_GROUP_VISIBILITY] = sConfigMgr->GetIntDefault("Visibility.GroupMode", 1); @@ -1814,10 +1823,19 @@ void World::SetInitialWorldSettings() sObjectMgr->LoadFactionChangeTitles(); TC_LOG_INFO("server.loading", "Loading GM tickets..."); - sTicketMgr->LoadTickets(); + sSupportMgr->LoadGmTickets(); + + TC_LOG_INFO("server.loading", "Loading GM bugs..."); + sSupportMgr->LoadBugTickets(); + + TC_LOG_INFO("server.loading", "Loading GM complaints..."); + sSupportMgr->LoadComplaintTickets(); + + TC_LOG_INFO("server.loading", "Loading GM suggestions..."); + sSupportMgr->LoadSuggestionTickets(); - TC_LOG_INFO("server.loading", "Loading GM surveys..."); - sTicketMgr->LoadSurveys(); + /*TC_LOG_INFO("server.loading", "Loading GM surveys..."); + sSupportMgr->LoadSurveys();*/ TC_LOG_INFO("server.loading", "Loading client addons..."); AddonMgr::LoadFromDB(); @@ -1916,7 +1934,7 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Starting Arena Season..."); sGameEventMgr->StartArenaSeason(); - sTicketMgr->Initialize(); + sSupportMgr->Initialize(); ///- Initialize Battlegrounds TC_LOG_INFO("server.loading", "Starting Battleground System"); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 847ca687112..f0cfcb37969 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -151,9 +151,11 @@ enum WorldBoolConfigs CONFIG_SHOW_MUTE_IN_WORLD, CONFIG_SHOW_BAN_IN_WORLD, CONFIG_AUTOBROADCAST, - CONFIG_TICKET_SYSTEM_STATUS, - CONFIG_TICKET_SUBMIT_TICKET, - CONFIG_TICKET_SUBMIT_BUG, + CONFIG_SUPPORT_ENABLED, + CONFIG_SUPPORT_TICKETS_ENABLED, + CONFIG_SUPPORT_BUGS_ENABLED, + CONFIG_SUPPORT_COMPLAINTS_ENABLED, + CONFIG_SUPPORT_SUGGESTIONS_ENABLED, CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES, CONFIG_PRESERVE_CUSTOM_CHANNELS, CONFIG_PDUMP_NO_PATHS, diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt index 1d441339df6..7df42e158de 100644 --- a/src/server/scripts/CMakeLists.txt +++ b/src/server/scripts/CMakeLists.txt @@ -131,12 +131,13 @@ include_directories( ${CMAKE_SOURCE_DIR}/src/server/game/Reputation ${CMAKE_SOURCE_DIR}/src/server/game/Scripting ${CMAKE_SOURCE_DIR}/src/server/game/Server + ${CMAKE_SOURCE_DIR}/src/server/game/Server/Packets ${CMAKE_SOURCE_DIR}/src/server/game/Server/Protocol ${CMAKE_SOURCE_DIR}/src/server/game/Skills ${CMAKE_SOURCE_DIR}/src/server/game/Spells ${CMAKE_SOURCE_DIR}/src/server/game/Spells/Auras + ${CMAKE_SOURCE_DIR}/src/server/game/Support ${CMAKE_SOURCE_DIR}/src/server/game/Texts - ${CMAKE_SOURCE_DIR}/src/server/game/Tickets ${CMAKE_SOURCE_DIR}/src/server/game/Tools ${CMAKE_SOURCE_DIR}/src/server/game/Warden ${CMAKE_SOURCE_DIR}/src/server/game/Warden/Modules diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index b2256ba962b..cae6765ad3e 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -25,7 +25,7 @@ EndScriptData */ #include "ScriptMgr.h" #include "ObjectMgr.h" #include "MapManager.h" -#include "TicketMgr.h" +#include "SupportMgr.h" #include "Chat.h" #include "Language.h" #include "Player.h" @@ -40,23 +40,26 @@ public: { static ChatCommand goCommandTable[] = { - { "creature", rbac::RBAC_PERM_COMMAND_GO_CREATURE, false, &HandleGoCreatureCommand, "", NULL }, - { "graveyard", rbac::RBAC_PERM_COMMAND_GO_GRAVEYARD, false, &HandleGoGraveyardCommand, "", NULL }, - { "grid", rbac::RBAC_PERM_COMMAND_GO_GRID, false, &HandleGoGridCommand, "", NULL }, - { "object", rbac::RBAC_PERM_COMMAND_GO_OBJECT, false, &HandleGoObjectCommand, "", NULL }, - { "taxinode", rbac::RBAC_PERM_COMMAND_GO_TAXINODE, false, &HandleGoTaxinodeCommand, "", NULL }, - { "trigger", rbac::RBAC_PERM_COMMAND_GO_TRIGGER, false, &HandleGoTriggerCommand, "", NULL }, - { "zonexy", rbac::RBAC_PERM_COMMAND_GO_ZONEXY, false, &HandleGoZoneXYCommand, "", NULL }, - { "xyz", rbac::RBAC_PERM_COMMAND_GO_XYZ, false, &HandleGoXYZCommand, "", NULL }, - { "ticket", rbac::RBAC_PERM_COMMAND_GO_TICKET, false, &HandleGoTicketCommand, "", NULL }, - { "", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoXYZCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } + { "creature", rbac::RBAC_PERM_COMMAND_GO_CREATURE, false, &HandleGoCreatureCommand, "", NULL }, + { "graveyard", rbac::RBAC_PERM_COMMAND_GO_GRAVEYARD, false, &HandleGoGraveyardCommand, "", NULL }, + { "grid", rbac::RBAC_PERM_COMMAND_GO_GRID, false, &HandleGoGridCommand, "", NULL }, + { "object", rbac::RBAC_PERM_COMMAND_GO_OBJECT, false, &HandleGoObjectCommand, "", NULL }, + { "taxinode", rbac::RBAC_PERM_COMMAND_GO_TAXINODE, false, &HandleGoTaxinodeCommand, "", NULL }, + { "trigger", rbac::RBAC_PERM_COMMAND_GO_TRIGGER, false, &HandleGoTriggerCommand, "", NULL }, + { "zonexy", rbac::RBAC_PERM_COMMAND_GO_ZONEXY, false, &HandleGoZoneXYCommand, "", NULL }, + { "xyz", rbac::RBAC_PERM_COMMAND_GO_XYZ, false, &HandleGoXYZCommand, "", NULL }, + { "ticket", rbac::RBAC_PERM_COMMAND_GO_TICKET, false, &HandleGoTicketCommand<GmTicket>, "", NULL }, + { "bugticket", rbac::RBAC_PERM_COMMAND_GO_BUG_TICKET, false, &HandleGoTicketCommand<BugTicket>, "", NULL }, + { "complaintticket", rbac::RBAC_PERM_COMMAND_GO_COMPLAINT_TICKET, false, &HandleGoTicketCommand<ComplaintTicket>, "", NULL }, + { "suggestionticket", rbac::RBAC_PERM_COMMAND_GO_SUGGESTION_TICKET, false, &HandleGoTicketCommand<SuggestionTicket>, "", NULL }, + { "", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoXYZCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } }; static ChatCommand commandTable[] = { { "go", rbac::RBAC_PERM_COMMAND_GO, false, NULL, "", goCommandTable }, - { NULL, 0, false, NULL, "", NULL } + { NULL, 0, false, NULL, "", NULL } }; return commandTable; } @@ -544,6 +547,7 @@ public: return true; } + template<typename T> static bool HandleGoTicketCommand(ChatHandler* handler, char const* args) { if (!*args) @@ -557,7 +561,7 @@ public: if (!ticketId) return false; - GmTicket* ticket = sTicketMgr->GetTicket(ticketId); + T* ticket = sSupportMgr->GetTicket<T>(ticketId); if (!ticket) { handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index df6010ac3ae..99cec7bed95 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -38,7 +38,7 @@ EndScriptData */ #include "SkillExtraItems.h" #include "SmartAI.h" #include "SpellMgr.h" -#include "TicketMgr.h" +#include "SupportMgr.h" #include "WardenCheckMgr.h" #include "WaypointManager.h" @@ -96,7 +96,6 @@ public: { "gameobject_questender", rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTENDER, true, &HandleReloadGOQuestEnderCommand, "", NULL }, { "gameobject_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUEST_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesGameobjectCommand, "", NULL }, { "gameobject_queststarter", rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTSTARTER, true, &HandleReloadGOQuestStarterCommand, "", NULL }, - { "gm_tickets", rbac::RBAC_PERM_COMMAND_RELOAD_GM_TICKETS, true, &HandleReloadGMTicketsCommand, "", NULL }, { "gossip_menu", rbac::RBAC_PERM_COMMAND_RELOAD_GOSSIP_MENU, true, &HandleReloadGossipMenuCommand, "", NULL }, { "gossip_menu_option", rbac::RBAC_PERM_COMMAND_RELOAD_GOSSIP_MENU_OPTION, true, &HandleReloadGossipMenuOptionCommand, "", NULL }, { "item_enchantment_template", rbac::RBAC_PERM_COMMAND_RELOAD_ITEM_ENCHANTMENT_TEMPLATE, true, &HandleReloadItemEnchantementsCommand, "", NULL }, @@ -146,6 +145,7 @@ public: { "spell_target_position", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION, true, &HandleReloadSpellTargetPositionCommand, "", NULL }, { "spell_threats", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_THREATS, true, &HandleReloadSpellThreatsCommand, "", NULL }, { "spell_group_stack_rules", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_GROUP_STACK_RULES, true, &HandleReloadSpellGroupStackRulesCommand, "", NULL }, + { "support", rbac::RBAC_PERM_COMMAND_RELOAD_SUPPORT_SYSTEM, true, &HandleReloadSupportSystemCommand, "", NULL }, { "trinity_string", rbac::RBAC_PERM_COMMAND_RELOAD_TRINITY_STRING, true, &HandleReloadTrinityStringCommand, "", NULL }, { "warden_action", rbac::RBAC_PERM_COMMAND_RELOAD_WARDEN_ACTION, true, &HandleReloadWardenactionCommand, "", NULL }, { "waypoint_scripts", rbac::RBAC_PERM_COMMAND_RELOAD_WAYPOINT_SCRIPTS, true, &HandleReloadWpScriptsCommand, "", NULL }, @@ -163,9 +163,14 @@ public: } //reload commands - static bool HandleReloadGMTicketsCommand(ChatHandler* /*handler*/, const char* /*args*/) - { - sTicketMgr->LoadTickets(); + static bool HandleReloadSupportSystemCommand(ChatHandler* handler, const char* /*args*/) + { + TC_LOG_INFO("misc", "Re-Loading Support System Tables..."); + sSupportMgr->LoadGmTickets(); + sSupportMgr->LoadBugTickets(); + sSupportMgr->LoadComplaintTickets(); + sSupportMgr->LoadSuggestionTickets(); + handler->SendGlobalGMSysMessage("DB tables `gm_*` reloaded."); return true; } diff --git a/src/server/scripts/Commands/cs_ticket.cpp b/src/server/scripts/Commands/cs_ticket.cpp index 98d1b2c7192..7af67902711 100644 --- a/src/server/scripts/Commands/cs_ticket.cpp +++ b/src/server/scripts/Commands/cs_ticket.cpp @@ -24,202 +24,45 @@ EndScriptData */ #include "AccountMgr.h" #include "Chat.h" +#include "Config.h" #include "Language.h" #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" -#include "TicketMgr.h" #include "ScriptMgr.h" +#include "SupportMgr.h" class ticket_commandscript : public CommandScript { public: ticket_commandscript() : CommandScript("ticket_commandscript") { } - ChatCommand* GetCommands() const override - { - static ChatCommand ticketResponseCommandTable[] = - { - { "append", rbac::RBAC_PERM_COMMAND_TICKET_RESPONSE_APPEND, true, &HandleGMTicketResponseAppendCommand, "", NULL }, - { "appendln", rbac::RBAC_PERM_COMMAND_TICKET_RESPONSE_APPENDLN, true, &HandleGMTicketResponseAppendLnCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - static ChatCommand ticketCommandTable[] = - { - { "assign", rbac::RBAC_PERM_COMMAND_TICKET_ASSIGN, true, &HandleGMTicketAssignToCommand, "", NULL }, - { "close", rbac::RBAC_PERM_COMMAND_TICKET_CLOSE, true, &HandleGMTicketCloseByIdCommand, "", NULL }, - { "closedlist", rbac::RBAC_PERM_COMMAND_TICKET_CLOSEDLIST, true, &HandleGMTicketListClosedCommand, "", NULL }, - { "comment", rbac::RBAC_PERM_COMMAND_TICKET_COMMENT, true, &HandleGMTicketCommentCommand, "", NULL }, - { "complete", rbac::RBAC_PERM_COMMAND_TICKET_COMPLETE, true, &HandleGMTicketCompleteCommand, "", NULL }, - { "delete", rbac::RBAC_PERM_COMMAND_TICKET_DELETE, true, &HandleGMTicketDeleteByIdCommand, "", NULL }, - { "escalate", rbac::RBAC_PERM_COMMAND_TICKET_ESCALATE, true, &HandleGMTicketEscalateCommand, "", NULL }, - { "escalatedlist", rbac::RBAC_PERM_COMMAND_TICKET_ESCALATEDLIST, true, &HandleGMTicketListEscalatedCommand, "", NULL }, - { "list", rbac::RBAC_PERM_COMMAND_TICKET_LIST, true, &HandleGMTicketListCommand, "", NULL }, - { "onlinelist", rbac::RBAC_PERM_COMMAND_TICKET_ONLINELIST, true, &HandleGMTicketListOnlineCommand, "", NULL }, - { "reset", rbac::RBAC_PERM_COMMAND_TICKET_RESET, true, &HandleGMTicketResetCommand, "", NULL }, - { "response", rbac::RBAC_PERM_COMMAND_TICKET_RESPONSE, true, NULL, "", ticketResponseCommandTable }, - { "togglesystem", rbac::RBAC_PERM_COMMAND_TICKET_TOGGLESYSTEM, true, &HandleToggleGMTicketSystem, "", NULL }, - { "unassign", rbac::RBAC_PERM_COMMAND_TICKET_UNASSIGN, true, &HandleGMTicketUnAssignCommand, "", NULL }, - { "viewid", rbac::RBAC_PERM_COMMAND_TICKET_VIEWID, true, &HandleGMTicketGetByIdCommand, "", NULL }, - { "viewname", rbac::RBAC_PERM_COMMAND_TICKET_VIEWNAME, true, &HandleGMTicketGetByNameCommand, "", NULL }, - { NULL, 0, false, NULL, "", NULL } - }; - static ChatCommand commandTable[] = - { - { "ticket", rbac::RBAC_PERM_COMMAND_TICKET, false, NULL, "", ticketCommandTable }, - { NULL, 0, false, NULL, "", NULL } - }; - return commandTable; - } - - static bool HandleGMTicketAssignToCommand(ChatHandler* handler, char const* args) - { - if (!*args) - return false; - - char* ticketIdStr = strtok((char*)args, " "); - uint32 ticketId = atoi(ticketIdStr); - - char* targetStr = strtok(NULL, " "); - if (!targetStr) - return false; - - std::string target(targetStr); - if (!normalizePlayerName(target)) - return false; - - GmTicket* ticket = sTicketMgr->GetTicket(ticketId); - if (!ticket || ticket->IsClosed()) - { - handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); - return true; - } - - ObjectGuid targetGuid = ObjectMgr::GetPlayerGUIDByName(target); - uint32 accountId = ObjectMgr::GetPlayerAccountIdByGUID(targetGuid); - // Target must exist and have administrative rights - if (!AccountMgr::HasPermission(accountId, rbac::RBAC_PERM_COMMANDS_BE_ASSIGNED_TICKET, realmHandle.Index)) - { - handler->SendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_A); - return true; - } - - // If already assigned, leave - if (ticket->IsAssignedTo(targetGuid)) - { - handler->PSendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_B, ticket->GetId()); - return true; - } - - // If assigned to different player other than current, leave - //! Console can override though - Player* player = handler->GetSession() ? handler->GetSession()->GetPlayer() : NULL; - if (player && ticket->IsAssignedNotTo(player->GetGUID())) - { - handler->PSendSysMessage(LANG_COMMAND_TICKETALREADYASSIGNED, ticket->GetId(), target.c_str()); - return true; - } - - // Assign ticket - SQLTransaction trans = SQLTransaction(NULL); - ticket->SetAssignedTo(targetGuid, AccountMgr::IsAdminAccount(AccountMgr::GetSecurity(accountId, realmHandle.Index))); - ticket->SaveToDB(trans); - sTicketMgr->UpdateLastChange(); - - std::string msg = ticket->FormatMessageString(*handler, NULL, target.c_str(), NULL, NULL, NULL); - handler->SendGlobalGMSysMessage(msg.c_str()); - return true; - } - - static bool HandleGMTicketCloseByIdCommand(ChatHandler* handler, char const* args) - { - if (!*args) - return false; - - uint32 ticketId = atoi(args); - GmTicket* ticket = sTicketMgr->GetTicket(ticketId); - if (!ticket || ticket->IsClosed() || ticket->IsCompleted()) - { - handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); - return true; - } - - // Ticket should be assigned to the player who tries to close it. - // Console can override though - Player* player = handler->GetSession() ? handler->GetSession()->GetPlayer() : NULL; - if (player && ticket->IsAssignedNotTo(player->GetGUID())) - { - handler->PSendSysMessage(LANG_COMMAND_TICKETCANNOTCLOSE, ticket->GetId()); - return true; - } + template<typename T> + static bool HandleTicketAssignToCommand(ChatHandler* handler, char const* args); - ObjectGuid closedByGuid; - if (player) - closedByGuid = player->GetGUID(); - else - closedByGuid.SetRawValue(0, uint64(-1)); - - sTicketMgr->CloseTicket(ticket->GetId(), closedByGuid); - sTicketMgr->UpdateLastChange(); - - std::string msg = ticket->FormatMessageString(*handler, player ? player->GetName().c_str() : "Console", NULL, NULL, NULL, NULL); - handler->SendGlobalGMSysMessage(msg.c_str()); - - // Inform player, who submitted this ticket, that it is closed - if (Player* submitter = ticket->GetPlayer()) - { - WorldPacket data(SMSG_GM_TICKET_UPDATE, 4); - data << uint32(GMTICKET_RESPONSE_TICKET_DELETED); - submitter->GetSession()->SendPacket(&data); - } - return true; - } - - static bool HandleGMTicketCommentCommand(ChatHandler* handler, char const* args) - { - if (!*args) - return false; + template<typename T> + static bool HandleTicketCloseByIdCommand(ChatHandler* handler, char const* args); - char* ticketIdStr = strtok((char*)args, " "); - uint32 ticketId = atoi(ticketIdStr); + template<typename T> + static bool HandleTicketCommentCommand(ChatHandler* handler, char const* args); - char* comment = strtok(NULL, "\n"); - if (!comment) - return false; + template<typename T> + static bool HandleTicketDeleteByIdCommand(ChatHandler* handler, char const* args); - GmTicket* ticket = sTicketMgr->GetTicket(ticketId); - if (!ticket || ticket->IsClosed()) - { - handler->PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST); - return true; - } + template<typename T> + static bool HandleTicketListCommand(ChatHandler* handler, char const* /*args*/); - // Cannot comment ticket assigned to someone else - //! Console excluded - Player* player = handler->GetSession() ? handler->GetSession()->GetPlayer() : NULL; - if (player && ticket->IsAssignedNotTo(player->GetGUID())) - { - handler->PSendSysMessage(LANG_COMMAND_TICKETALREADYASSIGNED, ticket->GetId()); - return true; - } + template<typename T> + static bool HandleTicketListClosedCommand(ChatHandler* handler, char const* /*args*/); - SQLTransaction trans = SQLTransaction(NULL); - ticket->SetComment(comment); - ticket->SaveToDB(trans); - sTicketMgr->UpdateLastChange(); + template<typename T> + static bool HandleTicketResetCommand(ChatHandler* handler, char const* /*args*/); - std::string msg = ticket->FormatMessageString(*handler, NULL, ticket->GetAssignedToName().c_str(), NULL, NULL, NULL); - msg += handler->PGetParseString(LANG_COMMAND_TICKETLISTADDCOMMENT, player ? player->GetName().c_str() : "Console", comment); - handler->SendGlobalGMSysMessage(msg.c_str()); + template<typename T> + static bool HandleTicketUnAssignCommand(ChatHandler* handler, char const* args); - return true; - } - - static bool HandleGMTicketListClosedCommand(ChatHandler* handler, char const* /*args*/) - { - sTicketMgr->ShowClosedList(*handler); - return true; - } + template<typename T> + static bool HandleTicketGetByIdCommand(ChatHandler* handler, char const* args); static bool HandleGMTicketCompleteCommand(ChatHandler* handler, char const* args) { @@ -227,7 +70,7 @@ public: return false; uint32 ticketId = atoi(args); - GmTicket* ticket = sTicketMgr->GetTicket(ticketId); + GmTicket* ticket = sSupportMgr->GetTicket<GmTicket>(ticketId); if (!ticket || ticket->IsClosed() || ticket->IsCompleted()) { handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); @@ -241,46 +84,9 @@ public: ticket->SetCompleted(); ticket->SaveToDB(trans); - std::string msg = ticket->FormatMessageString(*handler, NULL, NULL, - NULL, NULL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetName().c_str() : "Console"); - handler->SendGlobalGMSysMessage(msg.c_str()); - sTicketMgr->UpdateLastChange(); - return true; - } - - static bool HandleGMTicketDeleteByIdCommand(ChatHandler* handler, char const* args) - { - if (!*args) - return false; - - uint32 ticketId = atoi(args); - GmTicket* ticket = sTicketMgr->GetTicket(ticketId); - if (!ticket) - { - handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); - return true; - } - - if (!ticket->IsClosed()) - { - handler->SendSysMessage(LANG_COMMAND_TICKETCLOSEFIRST); - return true; - } - - std::string msg = ticket->FormatMessageString(*handler, NULL, NULL, NULL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetName().c_str() : "Console", NULL); + std::string msg = ticket->FormatViewMessageString(*handler, NULL, NULL, NULL, NULL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetName().c_str() : "Console"); handler->SendGlobalGMSysMessage(msg.c_str()); - - sTicketMgr->RemoveTicket(ticket->GetId()); - sTicketMgr->UpdateLastChange(); - - if (Player* player = ticket->GetPlayer()) - { - // Force abandon ticket - WorldPacket data(SMSG_GM_TICKET_UPDATE, 4); - data << uint32(GMTICKET_RESPONSE_TICKET_DELETED); - player->GetSession()->SendPacket(&data); - } - + sSupportMgr->UpdateLastChange(); return true; } @@ -290,7 +96,7 @@ public: return false; uint32 ticketId = atoi(args); - GmTicket* ticket = sTicketMgr->GetTicket(ticketId); + GmTicket* ticket = sSupportMgr->GetTicket<GmTicket>(ticketId); if (!ticket || ticket->IsClosed() || ticket->IsCompleted() || ticket->GetEscalatedStatus() != TICKET_UNASSIGNED) { handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); @@ -300,125 +106,54 @@ public: ticket->SetEscalatedStatus(TICKET_IN_ESCALATION_QUEUE); if (Player* player = ticket->GetPlayer()) - sTicketMgr->SendTicket(player->GetSession(), ticket); + sSupportMgr->SendGmTicket(player->GetSession(), ticket); - sTicketMgr->UpdateLastChange(); + sSupportMgr->UpdateLastChange(); return true; } static bool HandleGMTicketListEscalatedCommand(ChatHandler* handler, char const* /*args*/) { - sTicketMgr->ShowEscalatedList(*handler); - return true; - } - - static bool HandleGMTicketListCommand(ChatHandler* handler, char const* /*args*/) - { - sTicketMgr->ShowList(*handler, false); + sSupportMgr->ShowGmEscalatedList(*handler); return true; } static bool HandleGMTicketListOnlineCommand(ChatHandler* handler, char const* /*args*/) { - sTicketMgr->ShowList(*handler, true); + sSupportMgr->ShowList<GmTicket>(*handler, true); return true; } - static bool HandleGMTicketResetCommand(ChatHandler* handler, char const* /*args*/) + static bool HandleTicketResetAllCommand(ChatHandler* handler, char const* /*args*/) { - if (sTicketMgr->GetOpenTicketCount()) + if (sSupportMgr->GetOpenTicketCount<GmTicket>() || sSupportMgr->GetOpenTicketCount<BugTicket>() + || sSupportMgr->GetOpenTicketCount<ComplaintTicket>() || sSupportMgr->GetOpenTicketCount<SuggestionTicket>()) { handler->SendSysMessage(LANG_COMMAND_TICKETPENDING); return true; } else { - sTicketMgr->ResetTickets(); + sSupportMgr->ResetTickets<GmTicket>(); + sSupportMgr->ResetTickets<BugTicket>(); + sSupportMgr->ResetTickets<ComplaintTicket>(); + sSupportMgr->ResetTickets<SuggestionTicket>(); handler->SendSysMessage(LANG_COMMAND_TICKETRESET); } - return true; } static bool HandleToggleGMTicketSystem(ChatHandler* handler, char const* /*args*/) { - bool status = !sTicketMgr->GetStatus(); - sTicketMgr->SetStatus(status); - handler->PSendSysMessage(status ? LANG_ALLOW_TICKETS : LANG_DISALLOW_TICKETS); - return true; - } - - static bool HandleGMTicketUnAssignCommand(ChatHandler* handler, char const* args) - { - if (!*args) - return false; - - uint32 ticketId = atoi(args); - GmTicket* ticket = sTicketMgr->GetTicket(ticketId); - if (!ticket || ticket->IsClosed()) - { - handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); - return true; - } - // Ticket must be assigned - if (!ticket->IsAssigned()) + if (!sWorld->getBoolConfig(CONFIG_SUPPORT_TICKETS_ENABLED)) { - handler->PSendSysMessage(LANG_COMMAND_TICKETNOTASSIGNED, ticket->GetId()); + handler->SendSysMessage(LANG_DISALLOW_TICKETS_CONFIG); return true; } - // Get security level of player, whom this ticket is assigned to - uint32 security = SEC_PLAYER; - Player* assignedPlayer = ticket->GetAssignedPlayer(); - if (assignedPlayer) - security = assignedPlayer->GetSession()->GetSecurity(); - else - { - ObjectGuid guid = ticket->GetAssignedToGUID(); - uint32 accountId = ObjectMgr::GetPlayerAccountIdByGUID(guid); - security = AccountMgr::GetSecurity(accountId, realmHandle.Index); - } - - // Check security - //! If no m_session present it means we're issuing this command from the console - uint32 mySecurity = handler->GetSession() ? handler->GetSession()->GetSecurity() : SEC_CONSOLE; - if (security > mySecurity) - { - handler->SendSysMessage(LANG_COMMAND_TICKETUNASSIGNSECURITY); - return true; - } - - std::string assignedTo = ticket->GetAssignedToName(); // copy assignedto name because we need it after the ticket has been unnassigned - SQLTransaction trans = SQLTransaction(NULL); - ticket->SetUnassigned(); - ticket->SaveToDB(trans); - sTicketMgr->UpdateLastChange(); - - std::string msg = ticket->FormatMessageString(*handler, NULL, assignedTo.c_str(), - handler->GetSession() ? handler->GetSession()->GetPlayer()->GetName().c_str() : "Console", NULL, NULL); - handler->SendGlobalGMSysMessage(msg.c_str()); - - return true; - } - - static bool HandleGMTicketGetByIdCommand(ChatHandler* handler, char const* args) - { - if (!*args) - return false; - - uint32 ticketId = atoi(args); - GmTicket* ticket = sTicketMgr->GetTicket(ticketId); - if (!ticket || ticket->IsClosed() || ticket->IsCompleted()) - { - handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); - return true; - } - - SQLTransaction trans = SQLTransaction(NULL); - ticket->SetViewed(); - ticket->SaveToDB(trans); - - handler->SendSysMessage(ticket->FormatMessageString(*handler, true).c_str()); + bool status = !sSupportMgr->GetSupportSystemStatus(); + sSupportMgr->SetSupportSystemStatus(status); + handler->PSendSysMessage(status ? LANG_ALLOW_TICKETS : LANG_DISALLOW_TICKETS); return true; } @@ -446,7 +181,7 @@ public: } // Ticket must exist - GmTicket* ticket = sTicketMgr->GetTicketByPlayer(guid); + GmTicket* ticket = sSupportMgr->GetGmTicketByPlayerGuid(guid); if (!ticket) { handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); @@ -457,7 +192,7 @@ public: ticket->SetViewed(); ticket->SaveToDB(trans); - handler->SendSysMessage(ticket->FormatMessageString(*handler, true).c_str()); + handler->SendSysMessage(ticket->FormatViewMessageString(*handler, true).c_str()); return true; } @@ -473,7 +208,7 @@ public: if (!response) return false; - GmTicket* ticket = sTicketMgr->GetTicket(ticketId); + GmTicket* ticket = sSupportMgr->GetTicket<GmTicket>(ticketId); if (!ticket || ticket->IsClosed()) { handler->PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST); @@ -507,8 +242,571 @@ public: { return _HandleGMTicketResponseAppendCommand(args, true, handler); } + + ChatCommand* GetCommands() const override; }; +template<typename T> +bool ticket_commandscript::HandleTicketAssignToCommand(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + char* ticketIdStr = strtok((char*)args, " "); + uint32 ticketId = atoi(ticketIdStr); + + char* targetStr = strtok(NULL, " "); + if (!targetStr) + return false; + + std::string target(targetStr); + if (!normalizePlayerName(target)) + return false; + + T* ticket = sSupportMgr->GetTicket<T>(ticketId); + if (!ticket || ticket->IsClosed()) + { + handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + ObjectGuid targetGuid = ObjectMgr::GetPlayerGUIDByName(target); + uint32 accountId = ObjectMgr::GetPlayerAccountIdByGUID(targetGuid); + // Target must exist and have administrative rights + if (!AccountMgr::HasPermission(accountId, rbac::RBAC_PERM_COMMANDS_BE_ASSIGNED_TICKET, realmHandle.Index)) + { + handler->SendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_A); + return true; + } + + // If already assigned, leave + if (ticket->IsAssignedTo(targetGuid)) + { + handler->PSendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_B, ticket->GetId()); + return true; + } + + // If assigned to different player other than current, leave + //! Console can override though + Player* player = handler->GetSession() ? handler->GetSession()->GetPlayer() : nullptr; + if (player && ticket->IsAssignedNotTo(player->GetGUID())) + { + handler->PSendSysMessage(LANG_COMMAND_TICKETALREADYASSIGNED, ticket->GetId(), target.c_str()); + return true; + } + + // Assign ticket + SQLTransaction trans = SQLTransaction(nullptr); + ticket->SetAssignedTo(targetGuid); + + ticket->SaveToDB(trans); + + std::string msg = ticket->FormatViewMessageString(*handler, NULL, target.c_str(), NULL, NULL, NULL); + handler->SendGlobalGMSysMessage(msg.c_str()); + return true; +} + +template<> +bool ticket_commandscript::HandleTicketAssignToCommand<GmTicket>(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + char* ticketIdStr = strtok((char*)args, " "); + uint32 ticketId = atoi(ticketIdStr); + + char* targetStr = strtok(NULL, " "); + if (!targetStr) + return false; + + std::string target(targetStr); + if (!normalizePlayerName(target)) + return false; + + GmTicket* ticket = sSupportMgr->GetTicket<GmTicket>(ticketId); + if (!ticket || ticket->IsClosed()) + { + handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + ObjectGuid targetGuid = ObjectMgr::GetPlayerGUIDByName(target); + uint32 accountId = ObjectMgr::GetPlayerAccountIdByGUID(targetGuid); + // Target must exist and have administrative rights + if (!AccountMgr::HasPermission(accountId, rbac::RBAC_PERM_COMMANDS_BE_ASSIGNED_TICKET, realmHandle.Index)) + { + handler->SendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_A); + return true; + } + + // If already assigned, leave + if (ticket->IsAssignedTo(targetGuid)) + { + handler->PSendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_B, ticket->GetId()); + return true; + } + + // If assigned to different player other than current, leave + //! Console can override though + Player* player = handler->GetSession() ? handler->GetSession()->GetPlayer() : nullptr; + if (player && ticket->IsAssignedNotTo(player->GetGUID())) + { + handler->PSendSysMessage(LANG_COMMAND_TICKETALREADYASSIGNED, ticket->GetId(), target.c_str()); + return true; + } + + // Assign ticket + SQLTransaction trans = SQLTransaction(nullptr); + ticket->SetAssignedTo(targetGuid, AccountMgr::IsAdminAccount(AccountMgr::GetSecurity(accountId, realmHandle.Index))); + sSupportMgr->UpdateLastChange(); + + ticket->SaveToDB(trans); + + std::string msg = ticket->FormatViewMessageString(*handler, NULL, target.c_str(), NULL, NULL, NULL); + handler->SendGlobalGMSysMessage(msg.c_str()); + return true; +} + +template<typename T> +bool ticket_commandscript::HandleTicketCloseByIdCommand(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + uint32 ticketId = atoi(args); + T* ticket = sSupportMgr->GetTicket<T>(ticketId); + if (!ticket || ticket->IsClosed()) + { + handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + // Ticket should be assigned to the player who tries to close it. + // Console can override though + Player* player = handler->GetSession() ? handler->GetSession()->GetPlayer() : nullptr; + if (player && ticket->IsAssignedNotTo(player->GetGUID())) + { + handler->PSendSysMessage(LANG_COMMAND_TICKETCANNOTCLOSE, ticket->GetId()); + return true; + } + + ObjectGuid closedByGuid; + if (player) + closedByGuid = player->GetGUID(); + else + closedByGuid.SetRawValue(0, uint64(-1)); + + sSupportMgr->CloseTicket<T>(ticket->GetId(), closedByGuid); + + std::string msg = ticket->FormatViewMessageString(*handler, player ? player->GetName().c_str() : "Console", NULL, NULL, NULL, NULL); + handler->SendGlobalGMSysMessage(msg.c_str()); + + return true; +} + +template<> +bool ticket_commandscript::HandleTicketCloseByIdCommand<GmTicket>(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + uint32 ticketId = atoi(args); + GmTicket* ticket = sSupportMgr->GetTicket<GmTicket>(ticketId); + if (!ticket || ticket->IsClosed() || ticket->IsCompleted()) + { + handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + // Ticket should be assigned to the player who tries to close it. + // Console can override though + Player* player = handler->GetSession() ? handler->GetSession()->GetPlayer() : nullptr; + if (player && ticket->IsAssignedNotTo(player->GetGUID())) + { + handler->PSendSysMessage(LANG_COMMAND_TICKETCANNOTCLOSE, ticket->GetId()); + return true; + } + + ObjectGuid closedByGuid; + if (player) + closedByGuid = player->GetGUID(); + else + closedByGuid.SetRawValue(0, uint64(-1)); + + std::string msg = ticket->FormatViewMessageString(*handler, player ? player->GetName().c_str() : "Console", NULL, NULL, NULL, NULL); + handler->SendGlobalGMSysMessage(msg.c_str()); + + + sSupportMgr->CloseTicket<GmTicket>(ticket->GetId(), closedByGuid); + sSupportMgr->UpdateLastChange(); + + // Inform player, who submitted this ticket, that it is closed + if (Player* submitter = ticket->GetPlayer()) + sSupportMgr->SendGmTicketUpdate(submitter->GetSession(), GMTICKET_RESPONSE_TICKET_DELETED); + + return true; +} + +template<typename T> +bool ticket_commandscript::HandleTicketCommentCommand(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + char* ticketIdStr = strtok((char*)args, " "); + uint32 ticketId = atoi(ticketIdStr); + + char* comment = strtok(NULL, "\n"); + if (!comment) + return false; + + T* ticket = sSupportMgr->GetTicket<T>(ticketId); + if (!ticket || ticket->IsClosed()) + { + handler->PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + // Cannot comment ticket assigned to someone else + //! Console excluded + Player* player = handler->GetSession() ? handler->GetSession()->GetPlayer() : nullptr; + if (player && ticket->IsAssignedNotTo(player->GetGUID())) + { + handler->PSendSysMessage(LANG_COMMAND_TICKETALREADYASSIGNED, ticket->GetId()); + return true; + } + + SQLTransaction trans = SQLTransaction(nullptr); + ticket->SetComment(comment); + ticket->SaveToDB(trans); + sSupportMgr->UpdateLastChange(); + + std::string msg = ticket->FormatViewMessageString(*handler, NULL, ticket->GetAssignedToName().c_str(), NULL, NULL, NULL); + msg += handler->PGetParseString(LANG_COMMAND_TICKETLISTADDCOMMENT, player ? player->GetName().c_str() : "Console", comment); + handler->SendGlobalGMSysMessage(msg.c_str()); + + return true; +} + +template<typename T> +bool ticket_commandscript::HandleTicketListCommand(ChatHandler* handler, char const* /*args*/) +{ + sSupportMgr->ShowList<T>(*handler); + return true; +} + +template<typename T> +bool ticket_commandscript::HandleTicketListClosedCommand(ChatHandler* handler, char const* /*args*/) +{ + sSupportMgr->ShowClosedList<T>(*handler); + return true; +} + +template<typename T> +bool ticket_commandscript::HandleTicketDeleteByIdCommand(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + uint32 ticketId = atoi(args); + T* ticket = sSupportMgr->GetTicket<T>(ticketId); + if (!ticket) + { + handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + if (!ticket->IsClosed()) + { + handler->SendSysMessage(LANG_COMMAND_TICKETCLOSEFIRST); + return true; + } + + std::string msg = ticket->FormatViewMessageString(*handler, NULL, NULL, NULL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetName().c_str() : "Console", NULL); + handler->SendGlobalGMSysMessage(msg.c_str()); + + sSupportMgr->RemoveTicket<T>(ticket->GetId()); + + return true; +} + +template<> +bool ticket_commandscript::HandleTicketDeleteByIdCommand<GmTicket>(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + uint32 ticketId = atoi(args); + GmTicket* ticket = sSupportMgr->GetTicket<GmTicket>(ticketId); + if (!ticket) + { + handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + if (!ticket->IsClosed()) + { + handler->SendSysMessage(LANG_COMMAND_TICKETCLOSEFIRST); + return true; + } + + std::string msg = ticket->FormatViewMessageString(*handler, NULL, NULL, NULL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetName().c_str() : "Console", NULL); + handler->SendGlobalGMSysMessage(msg.c_str()); + + sSupportMgr->RemoveTicket<GmTicket>(ticket->GetId()); + sSupportMgr->UpdateLastChange(); + + if (Player* player = ticket->GetPlayer()) + sSupportMgr->SendGmTicketUpdate(player->GetSession(), GMTICKET_RESPONSE_TICKET_DELETED); + + return true; +} + +template<typename T> +bool ticket_commandscript::HandleTicketResetCommand(ChatHandler* handler, char const* /*args*/) +{ + if (sSupportMgr->GetOpenTicketCount<T>()) + { + handler->SendSysMessage(LANG_COMMAND_TICKETPENDING); + return true; + } + else + { + sSupportMgr->ResetTickets<T>(); + handler->SendSysMessage(LANG_COMMAND_TICKETRESET); + } + return true; +} + +template<typename T> +bool ticket_commandscript::HandleTicketUnAssignCommand(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + uint32 ticketId = atoi(args); + T* ticket = sSupportMgr->GetTicket<T>(ticketId); + if (!ticket || ticket->IsClosed()) + { + handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + // Ticket must be assigned + if (!ticket->IsAssigned()) + { + handler->PSendSysMessage(LANG_COMMAND_TICKETNOTASSIGNED, ticket->GetId()); + return true; + } + + // Get security level of player, whom this ticket is assigned to + uint32 security = SEC_PLAYER; + Player* assignedPlayer = ticket->GetAssignedPlayer(); + if (assignedPlayer) + security = assignedPlayer->GetSession()->GetSecurity(); + else + { + ObjectGuid guid = ticket->GetAssignedToGUID(); + uint32 accountId = ObjectMgr::GetPlayerAccountIdByGUID(guid); + security = AccountMgr::GetSecurity(accountId, realmHandle.Index); + } + + // Check security + //! If no m_session present it means we're issuing this command from the console + uint32 mySecurity = handler->GetSession() ? handler->GetSession()->GetSecurity() : SEC_CONSOLE; + if (security > mySecurity) + { + handler->SendSysMessage(LANG_COMMAND_TICKETUNASSIGNSECURITY); + return true; + } + + std::string assignedTo = ticket->GetAssignedToName(); // copy assignedto name because we need it after the ticket has been unnassigned + SQLTransaction trans = SQLTransaction(NULL); + ticket->SetUnassigned(); + ticket->SaveToDB(trans); + + std::string msg = ticket->FormatViewMessageString(*handler, NULL, assignedTo.c_str(), handler->GetSession() ? handler->GetSession()->GetPlayer()->GetName().c_str() : "Console", NULL, NULL); + handler->SendGlobalGMSysMessage(msg.c_str()); + + return true; +} + +template<> +bool ticket_commandscript::HandleTicketUnAssignCommand<GmTicket>(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + uint32 ticketId = atoi(args); + GmTicket* ticket = sSupportMgr->GetTicket<GmTicket>(ticketId); + if (!ticket || ticket->IsClosed()) + { + handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + // Ticket must be assigned + if (!ticket->IsAssigned()) + { + handler->PSendSysMessage(LANG_COMMAND_TICKETNOTASSIGNED, ticket->GetId()); + return true; + } + + // Get security level of player, whom this ticket is assigned to + uint32 security = SEC_PLAYER; + Player* assignedPlayer = ticket->GetAssignedPlayer(); + if (assignedPlayer) + security = assignedPlayer->GetSession()->GetSecurity(); + else + { + ObjectGuid guid = ticket->GetAssignedToGUID(); + uint32 accountId = ObjectMgr::GetPlayerAccountIdByGUID(guid); + security = AccountMgr::GetSecurity(accountId, realmHandle.Index); + } + + // Check security + //! If no m_session present it means we're issuing this command from the console + uint32 mySecurity = handler->GetSession() ? handler->GetSession()->GetSecurity() : SEC_CONSOLE; + if (security > mySecurity) + { + handler->SendSysMessage(LANG_COMMAND_TICKETUNASSIGNSECURITY); + return true; + } + + std::string assignedTo = ticket->GetAssignedToName(); // copy assignedto name because we need it after the ticket has been unnassigned + SQLTransaction trans = SQLTransaction(NULL); + ticket->SetUnassigned(); + ticket->SaveToDB(trans); + sSupportMgr->UpdateLastChange(); + + std::string msg = ticket->FormatViewMessageString(*handler, NULL, assignedTo.c_str(), handler->GetSession() ? handler->GetSession()->GetPlayer()->GetName().c_str() : "Console", NULL, NULL); + handler->SendGlobalGMSysMessage(msg.c_str()); + + return true; +} + +template<typename T> +bool ticket_commandscript::HandleTicketGetByIdCommand(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + uint32 ticketId = atoi(args); + T* ticket = sSupportMgr->GetTicket<T>(ticketId); + if (!ticket || ticket->IsClosed()) + { + handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + handler->SendSysMessage(ticket->FormatViewMessageString(*handler, true).c_str()); + return true; +} + +template<> +bool ticket_commandscript::HandleTicketGetByIdCommand<GmTicket>(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + uint32 ticketId = atoi(args); + GmTicket* ticket = sSupportMgr->GetTicket<GmTicket>(ticketId); + if (!ticket || ticket->IsClosed() || ticket->IsCompleted()) + { + handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); + return true; + } + + SQLTransaction trans = SQLTransaction(NULL); + ticket->SetViewed(); + ticket->SaveToDB(trans); + + handler->SendSysMessage(ticket->FormatViewMessageString(*handler, true).c_str()); + return true; +} + +ChatCommand* ticket_commandscript::GetCommands() const +{ + static ChatCommand ticketBugCommandTable[] = + { + { "assign", rbac::RBAC_PERM_COMMAND_TICKET_BUG_ASSIGN, true, &HandleTicketAssignToCommand<BugTicket>, "", NULL }, + { "close", rbac::RBAC_PERM_COMMAND_TICKET_BUG_CLOSE, true, &HandleTicketCloseByIdCommand<BugTicket>, "", NULL }, + { "closedlist", rbac::RBAC_PERM_COMMAND_TICKET_BUG_CLOSEDLIST, true, &HandleTicketListClosedCommand<BugTicket>, "", NULL }, + { "comment", rbac::RBAC_PERM_COMMAND_TICKET_BUG_COMMENT, true, &HandleTicketCommentCommand<BugTicket>, "", NULL }, + { "delete", rbac::RBAC_PERM_COMMAND_TICKET_BUG_DELETE, true, &HandleTicketDeleteByIdCommand<BugTicket>, "", NULL }, + { "list", rbac::RBAC_PERM_COMMAND_TICKET_BUG_LIST, true, &HandleTicketListCommand<BugTicket>, "", NULL }, + { "unassign", rbac::RBAC_PERM_COMMAND_TICKET_BUG_UNASSIGN, true, &HandleTicketUnAssignCommand<BugTicket>, "", NULL }, + { "view", rbac::RBAC_PERM_COMMAND_TICKET_BUG_VIEW, true, &HandleTicketGetByIdCommand<BugTicket>, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + static ChatCommand ticketComplaintCommandTable[] = + { + { "assign", rbac::RBAC_PERM_COMMAND_TICKET_COMPLAINT_ASSIGN, true, &HandleTicketAssignToCommand<ComplaintTicket>, "", NULL }, + { "close", rbac::RBAC_PERM_COMMAND_TICKET_COMPLAINT_CLOSE, true, &HandleTicketCloseByIdCommand<ComplaintTicket>, "", NULL }, + { "closedlist", rbac::RBAC_PERM_COMMAND_TICKET_COMPLAINT_CLOSEDLIST, true, &HandleTicketListClosedCommand<ComplaintTicket>, "", NULL }, + { "comment", rbac::RBAC_PERM_COMMAND_TICKET_COMPLAINT_COMMENT, true, &HandleTicketCommentCommand<ComplaintTicket>, "", NULL }, + { "delete", rbac::RBAC_PERM_COMMAND_TICKET_COMPLAINT_DELETE, true, &HandleTicketDeleteByIdCommand<ComplaintTicket>, "", NULL }, + { "list", rbac::RBAC_PERM_COMMAND_TICKET_COMPLAINT_LIST, true, &HandleTicketListCommand<ComplaintTicket>, "", NULL }, + { "unassign", rbac::RBAC_PERM_COMMAND_TICKET_COMPLAINT_UNASSIGN, true, &HandleTicketUnAssignCommand<ComplaintTicket>, "", NULL }, + { "view", rbac::RBAC_PERM_COMMAND_TICKET_COMPLAINT_VIEW, true, &HandleTicketGetByIdCommand<ComplaintTicket>, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + static ChatCommand ticketSuggestionCommandTable[] = + { + { "assign", rbac::RBAC_PERM_COMMAND_TICKET_SUGGESTION_ASSIGN, true, &HandleTicketAssignToCommand<SuggestionTicket>, "", NULL }, + { "close", rbac::RBAC_PERM_COMMAND_TICKET_SUGGESTION_CLOSE, true, &HandleTicketCloseByIdCommand<SuggestionTicket>, "", NULL }, + { "closedlist", rbac::RBAC_PERM_COMMAND_TICKET_SUGGESTION_CLOSEDLIST, true, &HandleTicketListClosedCommand<SuggestionTicket>, "", NULL }, + { "comment", rbac::RBAC_PERM_COMMAND_TICKET_SUGGESTION_COMMENT, true, &HandleTicketCommentCommand<SuggestionTicket>, "", NULL }, + { "delete", rbac::RBAC_PERM_COMMAND_TICKET_SUGGESTION_DELETE, true, &HandleTicketDeleteByIdCommand<SuggestionTicket>, "", NULL }, + { "list", rbac::RBAC_PERM_COMMAND_TICKET_SUGGESTION_LIST, true, &HandleTicketListCommand<SuggestionTicket>, "", NULL }, + { "unassign", rbac::RBAC_PERM_COMMAND_TICKET_SUGGESTION_UNASSIGN, true, &HandleTicketUnAssignCommand<SuggestionTicket>, "", NULL }, + { "view", rbac::RBAC_PERM_COMMAND_TICKET_SUGGESTION_VIEW, true, &HandleTicketGetByIdCommand<SuggestionTicket>, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + static ChatCommand ticketResetCommandTable[] = + { + { "all", rbac::RBAC_PERM_COMMAND_TICKET_RESET_ALL, true, &HandleTicketResetAllCommand, "", NULL }, + { "gm", rbac::RBAC_PERM_COMMAND_TICKET_RESET_GM, true, &HandleTicketResetCommand<GmTicket>, "", NULL }, + { "bug", rbac::RBAC_PERM_COMMAND_TICKET_RESET_BUG, true, &HandleTicketResetCommand<BugTicket>, "", NULL }, + { "complaint", rbac::RBAC_PERM_COMMAND_TICKET_RESET_COMPLAINT, true, &HandleTicketResetCommand<ComplaintTicket>, "", NULL }, + { "suggestion", rbac::RBAC_PERM_COMMAND_TICKET_RESET_SUGGESTION, true, &HandleTicketResetCommand<SuggestionTicket>, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + static ChatCommand ticketResponseCommandTable[] = + { + { "append", rbac::RBAC_PERM_COMMAND_TICKET_RESPONSE_APPEND, true, &HandleGMTicketResponseAppendCommand, "", NULL }, + { "appendln", rbac::RBAC_PERM_COMMAND_TICKET_RESPONSE_APPENDLN, true, &HandleGMTicketResponseAppendLnCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + static ChatCommand ticketCommandTable[] = + { + { "assign", rbac::RBAC_PERM_COMMAND_TICKET_ASSIGN, true, &HandleTicketAssignToCommand<GmTicket>, "", NULL }, + { "bug", rbac::RBAC_PERM_COMMAND_TICKET_BUG, true, NULL, "", ticketBugCommandTable }, + { "close", rbac::RBAC_PERM_COMMAND_TICKET_CLOSE, true, &HandleTicketCloseByIdCommand<GmTicket>, "", NULL }, + { "closedlist", rbac::RBAC_PERM_COMMAND_TICKET_CLOSEDLIST, true, &HandleTicketListClosedCommand<GmTicket>, "", NULL }, + { "comment", rbac::RBAC_PERM_COMMAND_TICKET_COMMENT, true, &HandleTicketCommentCommand<GmTicket>, "", NULL }, + { "complaint", rbac::RBAC_PERM_COMMAND_TICKET_COMPLAINT, true, NULL, "", ticketComplaintCommandTable }, + { "complete", rbac::RBAC_PERM_COMMAND_TICKET_COMPLETE, true, &HandleGMTicketCompleteCommand, "", NULL }, + { "delete", rbac::RBAC_PERM_COMMAND_TICKET_DELETE, true, &HandleTicketDeleteByIdCommand<GmTicket>, "", NULL }, + { "escalate", rbac::RBAC_PERM_COMMAND_TICKET_ESCALATE, true, &HandleGMTicketEscalateCommand, "", NULL }, + { "escalatedlist", rbac::RBAC_PERM_COMMAND_TICKET_ESCALATEDLIST, true, &HandleGMTicketListEscalatedCommand, "", NULL }, + { "list", rbac::RBAC_PERM_COMMAND_TICKET_LIST, true, &HandleTicketListCommand<GmTicket>, "", NULL }, + { "onlinelist", rbac::RBAC_PERM_COMMAND_TICKET_ONLINELIST, true, &HandleGMTicketListOnlineCommand, "", NULL }, + { "reset", rbac::RBAC_PERM_COMMAND_TICKET_RESET, true, NULL, "", ticketResetCommandTable }, + { "response", rbac::RBAC_PERM_COMMAND_TICKET_RESPONSE, true, NULL, "", ticketResponseCommandTable }, + { "suggestion", rbac::RBAC_PERM_COMMAND_TICKET_SUGGESTION, true, NULL, "", ticketSuggestionCommandTable }, + { "togglesystem", rbac::RBAC_PERM_COMMAND_TICKET_TOGGLESYSTEM, true, &HandleToggleGMTicketSystem, "", NULL }, + { "unassign", rbac::RBAC_PERM_COMMAND_TICKET_UNASSIGN, true, &HandleTicketUnAssignCommand<GmTicket>, "", NULL }, + { "viewid", rbac::RBAC_PERM_COMMAND_TICKET_VIEWID, true, &HandleTicketGetByIdCommand<GmTicket>, "", NULL }, + { "viewname", rbac::RBAC_PERM_COMMAND_TICKET_VIEWNAME, true, &HandleGMTicketGetByNameCommand, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + static ChatCommand commandTable[] = + { + { "ticket", rbac::RBAC_PERM_COMMAND_TICKET, false, NULL, "", ticketCommandTable }, + { NULL, 0, false, NULL, "", NULL } + }; + return commandTable; +} + void AddSC_ticket_commandscript() { new ticket_commandscript(); diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index 012d1cc3938..6369429236f 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -356,15 +356,37 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_GO_RESPAWN_BY_INSTANCE, "DELETE FROM gameobject_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC); // GM Tickets - PrepareStatement(CHAR_SEL_GM_TICKETS, "SELECT ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, haveTicket FROM gm_tickets", CONNECTION_SYNCH); - PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_tickets (ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, haveTicket) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_GM_TICKET, "DELETE FROM gm_tickets WHERE ticketId = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_PLAYER_GM_TICKETS, "DELETE FROM gm_tickets WHERE guid = ?", CONNECTION_ASYNC); - - // GM Survey/subsurvey/lag report - PrepareStatement(CHAR_INS_GM_SURVEY, "INSERT INTO gm_surveys (guid, surveyId, mainSurvey, overallComment, createTime) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(NOW()))", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_GM_SUBSURVEY, "INSERT INTO gm_subsurveys (surveyId, subsurveyId, rank, comment) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_LAG_REPORT, "INSERT INTO lag_reports (guid, lagType, mapId, posX, posY, posZ, latency, createTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_GM_TICKETS, "SELECT id, playerGuid, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp FROM gm_ticket", CONNECTION_SYNCH); + PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_ticket (id, playerGuid, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_GM_TICKET, "DELETE FROM gm_ticket WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PLAYER_GM_TICKETS, "DELETE FROM gm_ticket WHERE playerGuid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_ALL_GM_TICKETS, "TRUNCATE TABLE gm_ticket", CONNECTION_ASYNC); + + // GM Survey/subsurvey + PrepareStatement(CHAR_INS_GM_SURVEY, "INSERT INTO gm_survey (guid, surveyId, mainSurvey, comment, createTime) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(NOW()))", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_GM_SUBSURVEY, "INSERT INTO gm_subsurvey (surveyId, questionID, answer, answerComment) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); + + // GM Bug + PrepareStatement(CHAR_SEL_GM_BUGS, "SELECT id, playerGuid, note, createTime, mapId, posX, posY, posZ, facing, closedBy, assignedTo, comment FROM gm_bug", CONNECTION_SYNCH); + PrepareStatement(CHAR_REP_GM_BUG, "REPLACE INTO gm_bug (id, playerGuid, note, createTime, mapId, posX, posY, posZ, facing, closedBy, assignedTo, comment) VALUES (?, ?, ?, UNIX_TIMESTAMP(NOW()), ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_GM_BUG, "DELETE FROM gm_bug WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_ALL_GM_BUGS, "TRUNCATE TABLE gm_bug", CONNECTION_ASYNC); + + // GM Complaint + PrepareStatement(CHAR_SEL_GM_COMPLAINTS, "SELECT id, playerGuid, note, createTime, mapId, posX, posY, posZ, facing, targetCharacterGuid, complaintType, reportLineIndex, assignedTo, closedBy, comment FROM gm_complaint", CONNECTION_SYNCH); + PrepareStatement(CHAR_REP_GM_COMPLAINT, "REPLACE INTO gm_complaint (id, playerGuid, note, createTime, mapId, posX, posY, posZ, facing, targetCharacterGuid, complaintType, reportLineIndex, assignedTo, closedBy, comment) VALUES (?, ?, ?, UNIX_TIMESTAMP(NOW()), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_GM_COMPLAINT, "DELETE FROM gm_complaint WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_GM_COMPLAINT_CHATLINES, "SELECT timestamp, text FROM gm_complaint_chatlog WHERE complaintId = ? ORDER BY lineId ASC", CONNECTION_SYNCH); + PrepareStatement(CHAR_INS_GM_COMPLAINT_CHATLINE, "INSERT INTO gm_complaint_chatlog (complaintId, lineId, timestamp, text) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_GM_COMPLAINT_CHATLOG, "DELETE FROM gm_complaint_chatlog WHERE complaintId = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_ALL_GM_COMPLAINTS, "TRUNCATE TABLE gm_complaint", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_ALL_GM_COMPLAINT_CHATLOGS, "TRUNCATE TABLE gm_complaint_chatlog", CONNECTION_ASYNC); + + // GM Suggestion + PrepareStatement(CHAR_SEL_GM_SUGGESTIONS, "SELECT id, playerGuid, note, createTime, mapId, posX, posY, posZ, facing, closedBy, assignedTo, comment FROM gm_suggestion", CONNECTION_SYNCH); + PrepareStatement(CHAR_REP_GM_SUGGESTION, "REPLACE INTO gm_suggestion (id, playerGuid, note, createTime, mapId, posX, posY, posZ, facing, closedBy, assignedTo, comment) VALUES (?, ?, ?, UNIX_TIMESTAMP(NOW()), ?, ?, ?, ?, ?, ? ,? ,?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_GM_SUGGESTION, "DELETE FROM gm_suggestion WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_ALL_GM_SUGGESTIONS, "TRUNCATE TABLE gm_suggestion", CONNECTION_ASYNC); // LFG Data PrepareStatement(CHAR_INS_LFG_DATA, "INSERT INTO lfg_data (guid, dungeon, state) VALUES (?, ?, ?)", CONNECTION_ASYNC); @@ -405,7 +427,6 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_GROUP_DIFFICULTY, "UPDATE groups SET difficulty = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GROUP_RAID_DIFFICULTY, "UPDATE groups SET raidDifficulty = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GROUP_LEGACY_RAID_DIFFICULTY, "UPDATE groups SET legacyRaidDifficulty = ? WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_ALL_GM_TICKETS, "TRUNCATE TABLE gm_tickets", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_INVALID_SPELL_TALENTS, "DELETE FROM character_talent WHERE spell = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_INVALID_SPELL_SPELLS, "DELETE FROM character_spell WHERE spell = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_DELETE_INFO, "UPDATE characters SET deleteInfos_Name = name, deleteInfos_Account = account, deleteDate = UNIX_TIMESTAMP(), name = '', account = 0 WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index 6a9dc813386..b10591f0e5f 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -314,7 +314,25 @@ enum CharacterDatabaseStatements CHAR_INS_GM_SURVEY, CHAR_INS_GM_SUBSURVEY, - CHAR_INS_LAG_REPORT, + + CHAR_SEL_GM_BUGS, + CHAR_REP_GM_BUG, + CHAR_DEL_GM_BUG, + CHAR_DEL_ALL_GM_BUGS, + + CHAR_SEL_GM_COMPLAINTS, + CHAR_REP_GM_COMPLAINT, + CHAR_DEL_GM_COMPLAINT, + CHAR_SEL_GM_COMPLAINT_CHATLINES, + CHAR_INS_GM_COMPLAINT_CHATLINE, + CHAR_DEL_GM_COMPLAINT_CHATLOG, + CHAR_DEL_ALL_GM_COMPLAINTS, + CHAR_DEL_ALL_GM_COMPLAINT_CHATLOGS, + + CHAR_SEL_GM_SUGGESTIONS, + CHAR_REP_GM_SUGGESTION, + CHAR_DEL_GM_SUGGESTION, + CHAR_DEL_ALL_GM_SUGGESTIONS, CHAR_INS_CHARACTER, CHAR_UPD_CHARACTER, diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index b64393508fe..86d9dac2946 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -16,6 +16,7 @@ # CREATURE SETTINGS # CHAT SETTINGS # GAME MASTER SETTINGS +# SUPPORT SETTINGS # VISIBILITY AND DISTANCES # SERVER RATES # STATS LIMITS @@ -1060,33 +1061,6 @@ Server.LoginInfo = 0 Command.LookupMaxResults = 0 # -# Ticket.SystemStatus -# Description: Enable/disable the ticket system. This disables the whole customer -# support UI after trying to send a ticket in disabled state -# (MessageBox: "GM Help Tickets are currently unavailable."). -# UI remains disabled until the character relogs. -# Default: 1 - (Enabled) -# 0 - (Disabled) - -Ticket.SystemStatus = 1 - -# -# Ticket.SubmitTicket -# Description: Allow/disallow opening new tickets. -# Default: 0 - (Disabled) -# 1 - (Enabled, Experimental) - -Ticket.SubmitTicket = 0 - -# -# Ticket.SubmitBug -# Description: Allow/disallow opening new bug reports. -# Default: 0 - (Disabled) -# 1 - (Enabled, Experimental) - -Ticket.SubmitBug = 0 - -# # DungeonFinder.OptionsMask # Description: Dungeon and raid finder system. # Value is a bitmask consisting of: @@ -1662,12 +1636,60 @@ GM.AllowInvite = 0 GM.LowerSecurity = 0 # -# GM.TicketSystem.ChanceOfGMSurvey +################################################################################################### + +################################################################################################### +# SUPPORT SETTINGS +# +# Support.Enabled +# Description: Enable/disable the ticket system. This disables the whole customer +# support UI after trying to send a ticket in disabled state +# (MessageBox: "GM Help Tickets are currently unavailable."). +# UI remains disabled until the character relogs. +# Default: 1 - (Enabled) +# 0 - (Disabled) + +Support.Enabled = 1 + +# +# Support.TicketsEnabled +# Description: Allow/disallow opening new tickets. +# Default: 0 - (Disabled) +# 1 - (Enabled, Experimental) + +Support.TicketsEnabled = 0 + +# +# Support.BugsEnabled +# Description: Allow/disallow opening new bug reports. +# Default: 0 - (Disabled) +# 1 - (Enabled, Experimental) + +Support.BugsEnabled = 0 + +# +# Support.ComplaintsEnabled +# Description: Allow/disallow creating new player complaints. +# Default: 0 - (Disabled) +# 1 - (Enabled, Experimental) + +Support.ComplaintsEnabled = 0 + +# +# Support.SuggestionsEnabled +# Description: Allow/disallow opening new suggestion reports. +# Default: 0 - (Disabled) +# 1 - (Enabled, Experimental) + +Support.SuggestionsEnabled = 0 + +# +# Support.ChanceOfGMSurvey # Description: Chance of sending a GM survey after ticket completion. # Default: 50 - (Enabled) # 0 - (Disabled) -GM.TicketSystem.ChanceOfGMSurvey = 50 +Support.ChanceOfGMSurvey = 50 # ################################################################################################### |