Core/Ticket:

- Fully parse CMSG_GMTICKET_CREATE, use GmTicket::GetChatLog() to access the reporters chat log from this session (unused atm, possible useful to detect chat harassment)
- Simplify SMSG_GMTICKET_GETTICKET and fix "category" field (renamed it same as in blizz LUA files)
- Store response in DB
This commit is contained in:
kaelima
2012-10-17 00:53:55 +02:00
parent 4ec23cdba5
commit 98588aa192
5 changed files with 90 additions and 47 deletions

View File

@@ -0,0 +1,3 @@
ALTER TABLE `gm_tickets`
ADD COLUMN `response` TEXT NOT NULL AFTER `comment`,
ADD COLUMN `haveTicket` TINYINT(3) UNSIGNED NOT NULL AFTER `viewed`;

View File

@@ -16,6 +16,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "zlib.h"
#include "Language.h"
#include "WorldPacket.h"
#include "Common.h"
@@ -26,7 +27,7 @@
#include "WorldSession.h"
#include "Util.h"
void WorldSession::HandleGMTicketCreateOpcode(WorldPacket & recv_data)
void WorldSession::HandleGMTicketCreateOpcode(WorldPacket& recvData)
{
// 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)
@@ -42,7 +43,46 @@ void WorldSession::HandleGMTicketCreateOpcode(WorldPacket & recv_data)
// Player must not have ticket
if (!sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
{
GmTicket* ticket = new GmTicket(GetPlayer(), recv_data);
GmTicket* ticket = new GmTicket(GetPlayer(), recvData);
uint32 count;
std::list<uint32> times;
uint32 decompressedSize;
std::string chatLog;
recvData >> count;
for (uint32 i = 0; i < count; i++)
{
uint32 time;
recvData >> time;
times.push_back(time);
}
recvData >> decompressedSize;
if (count && decompressedSize && decompressedSize < 0xFFFF)
{
uint32 pos = recvData.rpos();
ByteBuffer dest;
dest.resize(decompressedSize);
uLongf realSize = decompressedSize;
if (uncompress(const_cast<uint8*>(dest.contents()), &realSize, const_cast<uint8*>(recvData.contents() + pos), recvData.size() - pos) == Z_OK)
{
dest >> chatLog;
ticket->SetChatLog(times, chatLog);
}
else
{
sLog->outError(LOG_FILTER_NETWORKIO, "CMSG_GMTICKET_CREATE possibly corrupt. Uncompression failed.");
recvData.rfinish();
return;
}
recvData.rfinish(); // Will still have compressed data in buffer.
}
sTicketMgr->AddTicket(ticket);
sTicketMgr->UpdateLastChange();

View File

@@ -31,45 +31,32 @@ 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)
GmTicket::GmTicket(Player* player, WorldPacket& recvData) : _createTime(time(NULL)), _lastModifiedTime(time(NULL)), _closedBy(0), _assignedTo(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _haveTicket(false)
{
_id = sTicketMgr->GenerateTicketId();
_playerName = player->GetName();
_playerGuid = player->GetGUID();
uint32 mapId;
recv_data >> mapId; // Map is sent as UInt32!
recvData >> mapId; // Map is sent as UInt32!
_mapId = mapId;
recv_data >> _posX;
recv_data >> _posY;
recv_data >> _posZ;
recv_data >> _message;
recvData >> _posX;
recvData >> _posY;
recvData >> _posZ;
recvData >> _message;
uint32 needResponse;
recv_data >> needResponse;
_needResponse = (needResponse == 17); // Requires GM response. 17 = true, 1 = false (17 is default)
uint8 unk1;
recv_data >> unk1; // Requests further GM interaction on a ticket to which a GM has already responded
recv_data.rfinish();
/*
recv_data >> uint32(count); // text lines
for (int i = 0; i < count; i++)
recv_data >> uint32();
if (something)
recv_data >> uint32();
else
compressed uint32 + string;
*/
recvData >> needResponse;
_needResponse = (needResponse == 17); // Requires GM response. 17 = true, 1 = false (17 is default)
recvData >> _haveTicket; // Requests further GM interaction on a ticket to which a GM has already responded. Basically means "has a new ticket"
}
GmTicket::~GmTicket() { }
bool GmTicket::LoadFromDB(Field* fields)
{
// 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
// 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 = MAKE_NEW_GUID(fields[++index].GetUInt32(), 0, HIGHGUID_PLAYER);
@@ -84,9 +71,11 @@ bool GmTicket::LoadFromDB(Field* fields)
_closedBy = fields[++index].GetInt32();
_assignedTo = MAKE_NEW_GUID(fields[++index].GetUInt32(), 0, HIGHGUID_PLAYER);
_comment = fields[++index].GetString();
_response = fields[++index].GetString();
_completed = fields[++index].GetBool();
_escalatedStatus = GMTicketEscalationStatus(fields[++index].GetUInt8());
_viewed = fields[++index].GetBool();
_haveTicket = fields[++index].GetBool();
return true;
}
@@ -109,9 +98,11 @@ void GmTicket::SaveToDB(SQLTransaction& trans) const
stmt->setInt32 (++index, GUID_LOPART(_closedBy));
stmt->setUInt32(++index, GUID_LOPART(_assignedTo));
stmt->setString(++index, _comment);
stmt->setString(++index, _response);
stmt->setBool (++index, _completed);
stmt->setUInt8 (++index, uint8(_escalatedStatus));
stmt->setBool (++index, _viewed);
stmt->setBool (++index, _haveTicket);
CharacterDatabase.ExecuteOrAppend(trans, stmt);
}
@@ -125,6 +116,10 @@ void GmTicket::DeleteFromDB()
void GmTicket::WritePacket(WorldPacket& data) const
{
data << uint32(GMTICKET_STATUS_HASTEXT);
data << uint32(_id);
data << _message;
data << uint8(_haveTicket);
data << GetAge(_lastModifiedTime);
if (GmTicket* ticket = sTicketMgr->GetOldestOpenTicket())
data << GetAge(ticket->GetLastModifiedTime());
@@ -223,6 +218,20 @@ 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))
{
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)) { }
@@ -365,26 +374,12 @@ void TicketMgr::ShowEscalatedList(ChatHandler& handler) const
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(ticket ? ticket->GetId() : 0); // ticketID
WorldPacket data(SMSG_GMTICKET_GETTICKET, (ticket ? (4 + 4 + 1 + 4 + 4 + 4 + 1 + 1) : 4));
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
ticket->WritePacket(data);
}
else
data << uint32(GMTICKET_STATUS_DEFAULT);
session->SendPacket(&data);
}

View File

@@ -82,7 +82,7 @@ class GmTicket
{
public:
GmTicket();
explicit GmTicket(Player* player, WorldPacket& recv_data);
explicit GmTicket(Player* player, WorldPacket& recvData);
~GmTicket();
bool IsClosed() const { return _closedBy; }
@@ -142,6 +142,9 @@ public:
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;
void SetChatLog(std::list<uint32> time, std::string const& log);
std::string GetChatLog() const { return _chatLog; }
private:
uint32 _id;
uint64 _playerGuid;
@@ -160,7 +163,9 @@ private:
GMTicketEscalationStatus _escalatedStatus;
bool _viewed;
bool _needResponse; // TODO: find out the use of this, and then store it in DB
bool _haveTicket;
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;

View File

@@ -327,8 +327,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PREPARE_STATEMENT(CHAR_DEL_GO_RESPAWN_BY_INSTANCE, "DELETE FROM gameobject_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC)
// GM Tickets
PREPARE_STATEMENT(CHAR_SEL_GM_TICKETS, "SELECT ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, completed, escalated, viewed FROM gm_tickets", CONNECTION_SYNCH)
PREPARE_STATEMENT(CHAR_REP_GM_TICKET, "REPLACE INTO gm_tickets (ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, completed, escalated, viewed) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC)
PREPARE_STATEMENT(CHAR_SEL_GM_TICKETS, "SELECT ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, haveTicket FROM gm_tickets", CONNECTION_SYNCH)
PREPARE_STATEMENT(CHAR_REP_GM_TICKET, "REPLACE INTO gm_tickets (ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, haveTicket) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC)
PREPARE_STATEMENT(CHAR_DEL_GM_TICKET, "DELETE FROM gm_tickets WHERE ticketId = ?", CONNECTION_ASYNC)
PREPARE_STATEMENT(CHAR_DEL_PLAYER_GM_TICKETS, "DELETE FROM gm_tickets WHERE guid = ?", CONNECTION_ASYNC)