diff options
| author | Carbenium <keresztesschmidt@gmail.com> | 2015-02-08 01:07:00 +0100 |
|---|---|---|
| committer | Carbenium <keresztesschmidt@gmail.com> | 2015-03-10 21:09:21 +0100 |
| commit | 37a14bc2d5066bdcdc24331c6718ccf54fb1afa5 (patch) | |
| tree | a8514c64ae0e5184f20a810d36043d03ab4feac1 /src/server/game | |
| parent | c8944d73b32697456b8baeaa72f98378044d3800 (diff) | |
Core/Support: Update the support/ticket system to 6.x
Core/Mail: fix a logic error introduced in 829e11b6623f967672143d79cd8d31772f54866d
Diffstat (limited to 'src/server/game')
21 files changed, 2350 insertions, 897 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, |
