aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Tickets/TicketMgr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/Tickets/TicketMgr.cpp')
-rwxr-xr-xsrc/server/game/Tickets/TicketMgr.cpp402
1 files changed, 280 insertions, 122 deletions
diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp
index 9452c920c3e..ecd9f7c6caf 100755
--- a/src/server/game/Tickets/TicketMgr.cpp
+++ b/src/server/game/Tickets/TicketMgr.cpp
@@ -24,35 +24,194 @@
#include "WorldPacket.h"
#include "WorldSession.h"
-TicketMgr::TicketMgr()
+inline float GetAge(uint64 t) { return float(time(NULL) - t) / DAY; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// GM ticket
+GmTicket::GmTicket() { }
+
+GmTicket::GmTicket(Player* player, WorldPacket& recv_data) : _createTime(time(NULL)), _lastModifiedTime(time(NULL)), _closedBy(0), _assignedTo(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED)
{
- m_GMticketid = 0; // this is initialized in LoadGMTickets() but it's best to be safe
- m_GMSurveyID = 0;
- m_openTickets = 0;
- lastChange = time(NULL);
- status = true;
+ _id = sTicketMgr->GenerateTicketId();
+ _playerName = player->GetName();
+ _playerGuid = player->GetGUID();
+
+ uint32 mapId;
+ recv_data >> mapId;
+ _mapId = mapId;
+
+ recv_data >> _posX;
+ recv_data >> _posY;
+ recv_data >> _posZ;
+ recv_data >> _message;
+
+ uint32 unk1;
+ recv_data >> unk1; // not sure what this is... replyTo?
+ uint8 needResponse;
+ recv_data >> needResponse; // always 1/0 -- not sure what retail does with this
}
-uint64 TicketMgr::GenerateGMTicketId()
+GmTicket::~GmTicket() { }
+
+bool GmTicket::LoadFromDB(Field* fields)
{
- return ++m_GMticketid;
+ uint8 index = 0;
+ _id = fields[ index].GetUInt32();
+ _playerGuid = MAKE_NEW_GUID(fields[++index].GetUInt32(), 0, HIGHGUID_PLAYER);
+ _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();
+ _closedBy = fields[++index].GetInt32();
+ _assignedTo = MAKE_NEW_GUID(fields[++index].GetUInt32(), 0, HIGHGUID_PLAYER);
+ _comment = fields[++index].GetString();
+ _completed = fields[++index].GetBool();
+ _escalatedStatus = GMTicketEscalationStatus(fields[++index].GetUInt8());
+ _viewed = fields[++index].GetBool();
+ return true;
}
-void TicketMgr::LoadGMTickets()
+void GmTicket::SaveToDB(SQLTransaction& trans) const
{
- uint32 oldMSTime = getMSTime();
+ uint8 index = 0;
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_ADD_GM_TICKET);
+ stmt->setUInt32( index, _id);
+ stmt->setUInt32(++index, GUID_LOPART(_playerGuid));
+ 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->setInt32 (++index, GUID_LOPART(_closedBy));
+ stmt->setUInt32(++index, GUID_LOPART(_assignedTo));
+ stmt->setString(++index, _comment);
+ stmt->setBool (++index, _completed);
+ stmt->setUInt8 (++index, uint8(_escalatedStatus));
+ stmt->setBool (++index, _viewed);
+
+ CharacterDatabase.ExecuteOrAppend(trans, stmt);
+}
+
+void GmTicket::DeleteFromDB(SQLTransaction& trans)
+{
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_TICKET);
+ stmt->setUInt32(0, _id);
+ CharacterDatabase.Execute(stmt);
+}
+
+void GmTicket::WritePacket(WorldPacket& data) const
+{
+ data << GetAge(_lastModifiedTime);
+ if (GmTicket* ticket = sTicketMgr->GetOldestOpenTicket())
+ data << GetAge(ticket->GetLastModifiedTime());
+ else
+ data << float(0);
+
+ // I am not sure how blizzlike this is, and we don't really have a way to find out
+ data << GetAge(sTicketMgr->GetLastChange());
+
+ data << uint8(std::min(_escalatedStatus, TICKET_IN_ESCALATION_QUEUE)); // escalated data
+ data << uint8(_viewed ? GMTICKET_OPENEDBYGM_STATUS_OPENED : GMTICKET_OPENEDBYGM_STATUS_NOT_OPENED); // whether or not it has been viewed
+}
+
+void GmTicket::SendResponse(WorldSession* session) const
+{
+ WorldPacket data(SMSG_GMRESPONSE_RECEIVED);
+ data << uint32(1); // unk? Zor says "hasActiveTicket"
+ data << uint32(0); // can-edit - always 1 or 0, not flags
+ data << _message.c_str();
+ data << _response.c_str();
+ // 3 null strings
+ data << uint8(0);
+ data << uint8(0);
+ 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 (sObjectMgr->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());
+ }
+ return ss.str();
+}
+
+std::string GmTicket::FormatMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName) 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);
+ return ss.str();
+}
+
+void GmTicket::SetUnassigned()
+{
+ _assignedTo = 0;
+ 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;
+ }
+}
- if (!m_GMTicketList.empty())
- for (GmTicketList::const_iterator itr = m_GMTicketList.begin(); itr != m_GMTicketList.end(); ++itr)
- delete *itr;
+void GmTicket::TeleportTo(Player* player) const
+{
+ player->TeleportTo(_mapId, _posX, _posY, _posZ, 1, 0);
+}
- m_GMTicketList.clear();
- m_GMticketid = 0;
- m_openTickets = 0;
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Ticket manager
+TicketMgr::TicketMgr() : _lastTicketId(0), _lastSurveyId(0), _openTicketCount(0), _lastChange(time(NULL)), _status(true) { }
- QueryResult result = CharacterDatabase.Query("SELECT guid, playerGuid, name, message, createtime, map, posX, posY, posZ, timestamp, closed,"
- "assignedto, comment, completed, escalated, viewed FROM gm_tickets");
+void TicketMgr::LoadTickets()
+{
+ uint32 oldMSTime = getMSTime();
+
+ if (!_ticketList.empty())
+ for (GmTicketList::const_iterator itr = _ticketList.begin(); itr != _ticketList.end(); ++itr)
+ if (itr->second)
+ delete itr->second;
+ _ticketList.clear();
+ _lastTicketId = 0;
+ _openTicketCount = 0;
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_LOAD_GM_TICKETS);
+ PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
{
sLog->outString(">> Loaded 0 GM tickets. DB table `gm_tickets` is empty!");
@@ -61,131 +220,130 @@ void TicketMgr::LoadGMTickets()
}
uint32 count = 0;
-
do
{
- Field *fields = result->Fetch();
- GM_Ticket *ticket = new GM_Ticket;
- ticket->guid = fields[0].GetUInt32();
- ticket->playerGuid = fields[1].GetUInt32();
- ticket->name = fields[2].GetString();
- ticket->message = fields[3].GetString();
- ticket->createtime = fields[4].GetUInt32();
- ticket->map = fields[5].GetUInt32();
- ticket->pos_x = fields[6].GetFloat();
- ticket->pos_y = fields[7].GetFloat();
- ticket->pos_z = fields[8].GetFloat();
- ticket->timestamp = fields[9].GetUInt32();
- ticket->closed = fields[10].GetInt32();
- if (ticket->closed == 0)
- m_openTickets++;
-
- ticket->assignedToGM = fields[11].GetUInt64();
- ticket->comment = fields[12].GetString();
- ticket->completed = fields[13].GetBool();
- ticket->escalated = fields[14].GetUInt8();
- ticket->viewed = fields[15].GetBool();
- ++count;
-
- m_GMTicketList.push_back(ticket);
- } while (result->NextRow());
+ Field* fields = result->Fetch();
+ GmTicket* ticket = new GmTicket();
+ if (!ticket->LoadFromDB(fields))
+ {
+ delete ticket;
+ continue;
+ }
+ if (!ticket->IsClosed())
+ ++_openTicketCount;
- result = CharacterDatabase.Query("SELECT MAX(guid) from gm_tickets");
+ // Update max ticket id if necessary
+ uint32 id = ticket->GetId();
+ if (_lastTicketId < id)
+ _lastTicketId = id;
- if (result)
- {
- Field *fields = result->Fetch();
- m_GMticketid = fields[0].GetUInt64();
- }
+ _ticketList[id] = ticket;
+ ++count;
+ } while (result->NextRow());
sLog->outString(">> Loaded %u GM tickets in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
-void TicketMgr::LoadGMSurveys()
+void TicketMgr::LoadSurveys()
{
- uint32 oldMSTime = getMSTime();
-
// we don't actually load anything into memory here as there's no reason to
- QueryResult result = CharacterDatabase.Query("SELECT MAX(surveyid) FROM gm_surveys");
- if (result)
- {
- Field *fields = result->Fetch();
- m_GMSurveyID = fields[0].GetUInt32();
- }
- else
- m_GMSurveyID = 0;
+ _lastSurveyId = 0;
+
+ uint32 oldMSTime = getMSTime();
+ if (QueryResult result = CharacterDatabase.Query("SELECT MAX(surveyId) FROM gm_surveys"))
+ _lastSurveyId = (*result)[0].GetUInt32();
sLog->outString(">> Loaded GM Survey count from database in %u ms", GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
-void TicketMgr::AddOrUpdateGMTicket(GM_Ticket &ticket, bool create)
+void TicketMgr::AddTicket(GmTicket* ticket)
{
- if (create)
+ _ticketList[ticket->GetId()] = ticket;
+ if (!ticket->IsClosed())
+ ++_openTicketCount;
+}
+
+void TicketMgr::CloseTicket(uint32 ticketId, int64 source)
+{
+ if (GmTicket* ticket = GetTicket(ticketId))
{
- m_GMTicketList.push_back(&ticket);
- if (ticket.closed == 0)
- m_openTickets++;
+ SQLTransaction trans = SQLTransaction(NULL);
+ ticket->SetClosedBy(source);
+ if (source)
+ --_openTicketCount;
+ ticket->SaveToDB(trans);
}
+}
- _AddOrUpdateGMTicket(ticket);
-}
-
-void TicketMgr::_AddOrUpdateGMTicket(GM_Ticket &ticket)
-{
- std::string msg(ticket.message), name(ticket.name), comment(ticket.comment);
- CharacterDatabase.escape_string(msg);
- CharacterDatabase.escape_string(name);
- CharacterDatabase.escape_string(comment);
- std::ostringstream ss;
- ss << "REPLACE INTO gm_tickets (guid, playerGuid, name, message, createtime, map, posX, posY, posZ, timestamp, closed, assignedto, comment, completed, escalated, viewed) VALUES (";
- ss << ticket.guid << ", ";
- ss << ticket.playerGuid << ", '";
- ss << name << "', '";
- ss << msg << "', " ;
- ss << ticket.createtime << ", ";
- ss << ticket.map << ", ";
- ss << ticket.pos_x << ", ";
- ss << ticket.pos_y << ", ";
- ss << ticket.pos_z << ", ";
- ss << ticket.timestamp << ", ";
- ss << ticket.closed << ", ";
- ss << ticket.assignedToGM << ", '";
- ss << comment << "', ";
- ss << (ticket.completed ? 1 : 0) << ", ";
- ss << uint32(ticket.escalated) << ", ";
- ss << (ticket.viewed ? 1 : 0) << ");";
-
- SQLTransaction trans = CharacterDatabase.BeginTransaction();
- trans->Append(ss.str().c_str());
- CharacterDatabase.CommitTransaction(trans);
-}
-
-void TicketMgr::RemoveGMTicket(GM_Ticket *ticket, int64 source, bool permanently)
-{
- for (GmTicketList::iterator i = m_GMTicketList.begin(); i != m_GMTicketList.end(); ++i)
- if ((*i)->guid == ticket->guid)
- {
- if (permanently)
- {
- CharacterDatabase.PExecute("DELETE FROM gm_tickets WHERE guid = '%u'", ticket->guid);
- i = m_GMTicketList.erase(i);
- ticket = NULL;
- return;
- }
- (*i)->closed = source;
-
- if (source != 0)
- m_openTickets--;
-
- _AddOrUpdateGMTicket(*(*i));
- }
+void TicketMgr::RemoveTicket(uint32 ticketId)
+{
+ if (GmTicket* ticket = GetTicket(ticketId))
+ {
+ SQLTransaction trans = SQLTransaction(NULL);
+ ticket->DeleteFromDB(trans);
+ _ticketList.erase(ticketId);
+ }
+}
+
+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::RemoveGMTicket(uint64 ticketGuid, int64 source, bool permanently)
+void TicketMgr::ShowClosedList(ChatHandler& handler) const
{
- GM_Ticket *ticket = GetGMTicket(ticketGuid);
- ASSERT(ticket); // hmm
- RemoveGMTicket(ticket, source, permanently);
+ 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
+{
+ uint32 status = GMTICKET_STATUS_DEFAULT;
+ std::string message;
+ if (ticket)
+ {
+ message = ticket->GetMessage();
+ status = GMTICKET_STATUS_HASTEXT;
+ }
+
+ WorldPacket data(SMSG_GMTICKET_GETTICKET, (4 + 4 + (ticket ? message.length() + 1 + 4 + 4 + 4 + 1 + 1 : 0)));
+ data << uint32(status); // standard 0x0A, 0x06 if text present
+ data << uint32(1); // g_HasActiveGMTicket -- not a flag
+
+ if (ticket)
+ {
+ data << message.c_str(); // ticket text
+ data << uint8(0x7); // ticket category; why is this hardcoded? does it make a diff re: client?
+
+ // we've got the easy stuff done by now.
+ // Now we need to go through the client logic for displaying various levels of ticket load
+ if (ticket)
+ ticket->WritePacket(data);
+ else
+ {
+ // we can't actually get any numbers here...
+ data << float(0);
+ data << float(0);
+ data << float(1);
+ data << uint8(0);
+ data << uint8(0);
+ }
+ }
+ session->SendPacket(&data);
}