diff --git a/sql/330/3_character_ticket.sql b/sql/330/3_character_ticket.sql new file mode 100644 index 000000000..74445dc03 --- /dev/null +++ b/sql/330/3_character_ticket.sql @@ -0,0 +1,2 @@ +alter table `character_ticket` + add column `response_text` text CHARSET utf8 COLLATE utf8_general_ci NULL after `ticket_text`; diff --git a/src/game/GMTicketHandler.cpp b/src/game/GMTicketHandler.cpp index 4807baa78..73d3dc034 100644 --- a/src/game/GMTicketHandler.cpp +++ b/src/game/GMTicketHandler.cpp @@ -43,15 +43,34 @@ void WorldSession::SendGMTicketGetTicket(uint32 status, char const* text) SendPacket( &data ); } +void WorldSession::SendGMResponse(GMTicket *ticket) +{ + int len = strlen(ticket->GetText())+1+strlen(ticket->GetResponse())+1; + WorldPacket data(SMSG_GMRESPONSE_RECEIVED, 4+4+len+1+1+1); + data << uint32(123); + data << uint32(456); + data << ticket->GetText(); // issue text + data << ticket->GetResponse(); // response text 1 + data << uint8(0); // response text 2 + data << uint8(0); // response text 3 + data << uint8(0); // response text 4 + SendPacket(&data); +} + void WorldSession::HandleGMTicketGetTicketOpcode( WorldPacket & /*recv_data*/ ) { SendQueryTimeResponse(); GMTicket* ticket = sTicketMgr.GetGMTicket(GetPlayer()->GetGUIDLow()); if(ticket) - SendGMTicketGetTicket(0x06,ticket->GetText()); + { + if(ticket->HasResponse()) + SendGMResponse(ticket); + else + SendGMTicketGetTicket(0x06, ticket->GetText()); + } else - SendGMTicketGetTicket(0x0A,0); + SendGMTicketGetTicket(0x0A, 0); } void WorldSession::HandleGMTicketUpdateTextOpcode( WorldPacket & recv_data ) @@ -81,18 +100,19 @@ void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data ) uint32 map; float x, y, z; std::string ticketText = ""; + uint8 isFollowup; recv_data >> map >> x >> y >> z; // last check 2.4.3 recv_data >> ticketText; - recv_data.read_skip(); // unk1, 0 - recv_data.read_skip(); // unk2, 1 + recv_data.read_skip(); // unk1, 11 - talk to gm, 1 - report problem + recv_data >> isFollowup; // unk2, 1 - followup ticket recv_data.read_skip(); // unk3, 0 recv_data.read_skip(); // unk4, 0 sLog.outDebug("TicketCreate: map %u, x %f, y %f, z %f, text %s", map, x, y, z, ticketText.c_str()); - if(sTicketMgr.GetGMTicket(GetPlayer()->GetGUIDLow())) + if(sTicketMgr.GetGMTicket(GetPlayer()->GetGUIDLow()) && !isFollowup) { WorldPacket data( SMSG_GMTICKET_CREATE, 4 ); data << uint32(1); // 1 - You already have GM ticket @@ -100,6 +120,9 @@ void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data ) return; } + if(isFollowup) + sTicketMgr.Delete(_player->GetGUIDLow()); + sTicketMgr.Create(_player->GetGUIDLow(), ticketText.c_str()); SendQueryTimeResponse(); @@ -107,7 +130,6 @@ void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data ) WorldPacket data( SMSG_GMTICKET_CREATE, 4 ); data << uint32(2); // 2 - nothing appears (3-error creating, 5-error updating) SendPacket( &data ); - DEBUG_LOG("update the ticket"); //TODO: Guard player map HashMapHolder::MapType &m = sObjectAccessor.GetPlayers(); @@ -157,3 +179,15 @@ void WorldSession::HandleGMSurveySubmit( WorldPacket & recv_data) // TODO: chart this data in some way } + +void WorldSession::HandleGMResponseResolve(WorldPacket & recv_data) +{ + // empty opcode + sLog.outDebug("WORLD: %s", LookupOpcodeName(recv_data.GetOpcode())); + + sTicketMgr.Delete(GetPlayer()->GetGUIDLow()); + + WorldPacket data(SMSG_GMRESPONSE_STATUS_UPDATE, 1); + data << uint8(0); // ask to fill out gm survey = 1 + SendPacket(&data); +} diff --git a/src/game/GMTicketMgr.cpp b/src/game/GMTicketMgr.cpp index 50e88ebf6..5f1044887 100644 --- a/src/game/GMTicketMgr.cpp +++ b/src/game/GMTicketMgr.cpp @@ -34,8 +34,8 @@ void GMTicketMgr::LoadGMTickets() m_GMTicketMap.clear(); // For reload case QueryResult *result = CharacterDatabase.Query( - // 0 1 2 - "SELECT guid, ticket_text,UNIX_TIMESTAMP(ticket_lastchange) FROM character_ticket"); + // 0 1 2 3 + "SELECT guid, ticket_text, response_text, UNIX_TIMESTAMP(ticket_lastchange) FROM character_ticket"); if( !result ) { @@ -59,7 +59,7 @@ void GMTicketMgr::LoadGMTickets() Field* fields = result->Fetch(); uint32 guid = fields[0].GetUInt32(); - m_GMTicketMap[guid] = GMTicket(guid, fields[1].GetCppString(), time_t(fields[2].GetUInt64())); + m_GMTicketMap[guid] = GMTicket(guid, fields[1].GetCppString(), fields[2].GetCppString(), time_t(fields[3].GetUInt64())); ++count; } while (result->NextRow()); @@ -73,8 +73,8 @@ void GMTicketMgr::DeleteAll() { for(GMTicketMap::const_iterator itr = m_GMTicketMap.begin(); itr != m_GMTicketMap.end(); ++itr) { - if(Player* owner = sObjectMgr.GetPlayer(MAKE_NEW_GUID(itr->first,0,HIGHGUID_PLAYER))) - owner->GetSession()->SendGMTicketGetTicket(0x0A,0); + if(Player* owner = sObjectMgr.GetPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER))) + owner->GetSession()->SendGMTicketGetTicket(0x0A, 0); } CharacterDatabase.PExecute("DELETE FROM character_ticket"); m_GMTicketMap.clear(); diff --git a/src/game/GMTicketMgr.h b/src/game/GMTicketMgr.h index 54e4df428..0a472a464 100644 --- a/src/game/GMTicketMgr.h +++ b/src/game/GMTicketMgr.h @@ -31,7 +31,7 @@ class GMTicket { } - GMTicket(uint32 guid, const std::string& text, time_t update) : m_guid(guid), m_text(text), m_lastUpdate(update) + GMTicket(uint32 guid, const std::string& text, const std::string& responsetext, time_t update) : m_guid(guid), m_text(text), m_responseText(responsetext), m_lastUpdate(update) { } @@ -41,6 +41,11 @@ class GMTicket return m_text.c_str(); } + const char* GetResponse() const + { + return m_responseText.c_str(); + } + uint64 GetLastUpdate() const { return m_lastUpdate; @@ -56,6 +61,18 @@ class GMTicket CharacterDatabase.PExecute("UPDATE character_ticket SET ticket_text = '%s' WHERE guid = '%u'", escapedString.c_str(), m_guid); } + void SetResponseText(const char* text) + { + m_responseText = text ? text : ""; + m_lastUpdate = time(NULL); + + std::string escapedString = m_responseText; + CharacterDatabase.escape_string(escapedString); + CharacterDatabase.PExecute("UPDATE character_ticket SET response_text = '%s' WHERE guid = '%u'", escapedString.c_str(), m_guid); + } + + bool HasResponse() { return !m_responseText.empty(); } + void DeleteFromDB() const { CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u' LIMIT 1", m_guid); @@ -69,12 +86,16 @@ class GMTicket std::string escapedString = m_text; CharacterDatabase.escape_string(escapedString); - CharacterDatabase.PExecute("INSERT INTO character_ticket (guid, ticket_text) VALUES ('%u', '%s')", m_guid, escapedString.c_str()); + std::string escapedString2 = m_responseText; + CharacterDatabase.escape_string(escapedString2); + + CharacterDatabase.PExecute("INSERT INTO character_ticket (guid, ticket_text, response_text) VALUES ('%u', '%s', '%s')", m_guid, escapedString.c_str(), escapedString2.c_str()); CharacterDatabase.CommitTransaction(); } private: uint32 m_guid; std::string m_text; + std::string m_responseText; time_t m_lastUpdate; }; typedef std::map GMTicketMap; @@ -113,7 +134,7 @@ class GMTicketMgr void Create(uint32 guid, const char* text) { - GMTicket t = GMTicket(guid, text, time(NULL)); + GMTicket t = GMTicket(guid, text, "", time(NULL)); t.SaveToDB(); m_GMTicketMap[guid] = t; } diff --git a/src/game/Language.h b/src/game/Language.h index 986f28872..6b16d6aaf 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -277,7 +277,7 @@ enum MangosStrings LANG_COMMAND_TICKETVIEW = 290, LANG_COMMAND_TICKETON = 291, LANG_COMMAND_TICKETOFF = 292, - LANG_COMMAND_TICKENOTEXIST = 293, + LANG_COMMAND_TICKETNOTEXIST = 293, LANG_COMMAND_ALLTICKETDELETED = 294, LANG_COMMAND_TICKETPLAYERDEL = 295, LANG_COMMAND_TICKETDEL = 296, diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index 3e28e8c85..2f38131ec 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -2261,6 +2261,54 @@ bool ChatHandler::HandleTicketCommand(const char* args) return true; } + // ticket respond + if(strncmp(px,"respond",8) == 0) + { + char *name = strtok(NULL, " "); + + if(!name) + { + SendSysMessage(LANG_CMD_SYNTAX); + SetSentErrorMessage(true); + return false; + } + + std::string plName = name; + uint64 guid = sObjectMgr.GetPlayerGUIDByName(plName); + + if(!guid) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + GMTicket* ticket = sTicketMgr.GetGMTicket(GUID_LOPART(guid)); + + if(!ticket) + { + PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST, GUID_LOPART(guid)); + SetSentErrorMessage(true); + return false; + } + + char* response = strtok(NULL, ""); + + if(!response) + { + SendSysMessage(LANG_CMD_SYNTAX); + SetSentErrorMessage(true); + return false; + } + + ticket->SetResponseText(response); + + if(Player* pl = sObjectMgr.GetPlayer(guid)) + pl->GetSession()->SendGMResponse(ticket); + + return true; + } + // ticket #num int num = atoi(px); if(num > 0) @@ -2269,7 +2317,7 @@ bool ChatHandler::HandleTicketCommand(const char* args) if(!result) { - PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num); + PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST, num); SetSentErrorMessage(true); return false; } @@ -2324,7 +2372,7 @@ bool ChatHandler::HandleDelTicketCommand(const char *args) QueryResult* result = CharacterDatabase.PQuery("SELECT guid FROM character_ticket ORDER BY ticket_id ASC "_OFFSET_,num-1); if(!result) { - PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num); + PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST, num); SetSentErrorMessage(true); return false; } diff --git a/src/game/Opcodes.cpp b/src/game/Opcodes.cpp index 4a394dd2a..9983f7859 100644 --- a/src/game/Opcodes.cpp +++ b/src/game/Opcodes.cpp @@ -1290,7 +1290,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x4ED*/ { "SMSG_TOGGLE_XP_GAIN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x4EE*/ { "SMSG_GMRESPONSE_DB_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x4EF*/ { "SMSG_GMRESPONSE_RECEIVED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, - /*0x4F0*/ { "CMSG_GMRESPONSE_RESOLVE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x4F0*/ { "CMSG_GMRESPONSE_RESOLVE", STATUS_LOGGEDIN, &WorldSession::HandleGMResponseResolve }, /*0x4F1*/ { "SMSG_GMRESPONSE_STATUS_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x4F2*/ { "UMSG_UNKNOWN_1266", STATUS_NEVER, &WorldSession::Handle_NULL }, /*0x4F3*/ { "UMSG_UNKNOWN_1267", STATUS_NEVER, &WorldSession::Handle_NULL }, diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index 194f3b3cf..5a936ee8a 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -41,6 +41,7 @@ class WorldSocket; class QueryResult; class LoginQueryHolder; class CharacterHandler; +class GMTicket; enum AccountDataType { @@ -184,6 +185,7 @@ class MANGOS_DLL_SPEC WorldSession void SendSpiritResurrect(); void SendBindPoint(Creature* npc); void SendGMTicketGetTicket(uint32 status, char const* text); + void SendGMResponse(GMTicket *ticket); void SendAttackStop(Unit const* enemy); @@ -340,6 +342,7 @@ class MANGOS_DLL_SPEC WorldSession void HandleGMTicketUpdateTextOpcode(WorldPacket& recvPacket); void HandleGMSurveySubmit(WorldPacket& recvPacket); + void HandleGMResponseResolve(WorldPacket& recv_data); void HandleTogglePvP(WorldPacket& recvPacket);