From d49c2a5e4d66b1f4bd4bdff3d6ed92902e8565f8 Mon Sep 17 00:00:00 2001 From: tibbi Date: Sun, 7 Oct 2012 13:55:04 +0100 Subject: correct reputation converting at faction change --- src/server/shared/Database/Implementation/CharacterDatabase.cpp | 3 ++- src/server/shared/Database/Implementation/CharacterDatabase.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/server/shared/Database/Implementation') diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index add782cf517..4396f67244a 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -497,8 +497,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() PREPARE_STATEMENT(CHAR_UPD_CHAR_INVENTORY_FACTION_CHANGE, "UPDATE item_instance ii, character_inventory ci SET ii.itemEntry = ? WHERE ii.itemEntry = ? AND ci.guid = ? AND ci.item = ii.guid", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_DEL_CHAR_SPELL_BY_SPELL, "DELETE FROM character_spell WHERE spell = ? AND guid = ?", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_UPD_CHAR_SPELL_FACTION_CHANGE, "UPDATE character_spell SET spell = ? where spell = ? AND guid = ?", CONNECTION_ASYNC); + PREPARE_STATEMENT(CHAR_SEL_CHAR_REP_BY_FACTION, "SELECT standing FROM character_reputation WHERE faction = ? AND guid = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_DEL_CHAR_REP_BY_FACTION, "DELETE FROM character_reputation WHERE faction = ? AND guid = ?", CONNECTION_ASYNC); - PREPARE_STATEMENT(CHAR_UPD_CHAR_REP_FACTION_CHANGE, "UPDATE character_reputation SET faction = ? where faction = ? AND guid = ?", CONNECTION_ASYNC); + PREPARE_STATEMENT(CHAR_UPD_CHAR_REP_FACTION_CHANGE, "UPDATE character_reputation SET faction = ?, standing = ? WHERE faction = ? AND guid = ?", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_UPD_CHAR_TITLES_FACTION_CHANGE, "UPDATE characters SET knownTitles = ? WHERE guid = ?", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_RES_CHAR_TITLES_FACTION_CHANGE, "UPDATE characters SET chosenTitle = 0 WHERE guid = ?", CONNECTION_ASYNC); PREPARE_STATEMENT(CHAR_DEL_CHAR_SPELL_COOLDOWN, "DELETE FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index fedf0022fcc..95b059de95c 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -460,6 +460,7 @@ enum CharacterDatabaseStatements CHAR_UPD_CHAR_INVENTORY_FACTION_CHANGE, CHAR_DEL_CHAR_SPELL_BY_SPELL, CHAR_UPD_CHAR_SPELL_FACTION_CHANGE, + CHAR_SEL_CHAR_REP_BY_FACTION, CHAR_DEL_CHAR_REP_BY_FACTION, CHAR_UPD_CHAR_REP_FACTION_CHANGE, CHAR_UPD_CHAR_TITLES_FACTION_CHANGE, -- cgit v1.2.3 From 405c3b09aa9fc9ea5750f0221d7f4974dd65b67f Mon Sep 17 00:00:00 2001 From: tibbi Date: Mon, 8 Oct 2012 17:29:02 +0100 Subject: adding a new prepared statement for selecting char at_login and titles --- src/server/game/Handlers/CharacterHandler.cpp | 6 +++--- src/server/shared/Database/Implementation/CharacterDatabase.cpp | 3 ++- src/server/shared/Database/Implementation/CharacterDatabase.h | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src/server/shared/Database/Implementation') diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 91388d9bf11..853e71fd732 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1631,7 +1631,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recv_data) uint8 playerClass = nameData->m_class; uint8 level = nameData->m_level; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_AT_LOGIN_TITLES); stmt->setUInt32(0, lowGuid); PreparedQueryResult result = CharacterDatabase.Query(stmt); @@ -1644,9 +1644,9 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recv_data) } Field* fields = result->Fetch(); - uint32 at_loginFlags = fields[2].GetUInt16(); + uint32 at_loginFlags = fields[0].GetUInt16(); + char const* knownTitlesStr = fields[1].GetCString(); uint32 used_loginFlag = ((recv_data.GetOpcode() == CMSG_CHAR_RACE_CHANGE) ? AT_LOGIN_CHANGE_RACE : AT_LOGIN_CHANGE_FACTION); - char const* knownTitlesStr = fields[3].GetCString(); if (!sObjectMgr->GetPlayerInfo(race, playerClass)) { diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index 4396f67244a..c29d8313c20 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -49,7 +49,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PREPARE_STATEMENT(CHAR_SEL_CHAR_RACE, "SELECT race FROM characters WHERE guid = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_SEL_CHAR_LEVEL, "SELECT level FROM characters WHERE guid = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH); - PREPARE_STATEMENT(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender FROM characters WHERE guid = ?", CONNECTION_SYNCH); + PREPARE_STATEMENT(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_SEL_CHAR_POSITION_XYZ, "SELECT map, position_x, position_y, position_z FROM characters WHERE guid = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_SEL_CHAR_POSITION, "SELECT position_x, position_y, position_z, orientation, map, taxi_path FROM characters WHERE guid = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_DEL_QUEST_STATUS_DAILY, "DELETE FROM character_queststatus_daily", CONNECTION_ASYNC); @@ -421,6 +421,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PREPARE_STATEMENT(CHAR_SEL_POOL_QUEST_SAVE, "SELECT quest_id FROM pool_quest_save WHERE pool_id = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_SEL_CHARACTER_AT_LOGIN, "SELECT at_login FROM characters WHERE guid = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, "SELECT class, level, at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_SYNCH); + PREPARE_STATEMENT(CHAR_SEL_CHAR_AT_LOGIN_TITLES, "SELECT at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_SEL_INSTANCE, "SELECT data, completedEncounters FROM instance WHERE map = ? AND id = ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_SEL_PET_SPELL_LIST, "SELECT DISTINCT pet_spell.spell FROM pet_spell, character_pet WHERE character_pet.owner = ? AND character_pet.id = pet_spell.guid AND character_pet.id <> ?", CONNECTION_SYNCH); PREPARE_STATEMENT(CHAR_SEL_CHAR_PET, "SELECT id FROM character_pet WHERE owner = ? AND id <> ?", CONNECTION_SYNCH); diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index 95b059de95c..88018997e4b 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -384,6 +384,7 @@ enum CharacterDatabaseStatements CHAR_SEL_POOL_QUEST_SAVE, CHAR_SEL_CHARACTER_AT_LOGIN, CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, + CHAR_SEL_CHAR_AT_LOGIN_TITLES, CHAR_SEL_INSTANCE, CHAR_SEL_PET_SPELL_LIST, CHAR_SEL_CHAR_PET, -- cgit v1.2.3 From 98588aa19202c2d259fba0717f9c4a4feac32a0a Mon Sep 17 00:00:00 2001 From: kaelima Date: Wed, 17 Oct 2012 00:53:55 +0200 Subject: 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 --- .../2012_10_17_00_character_gm_tickets.sql | 3 + src/server/game/Handlers/TicketHandler.cpp | 44 +++++++++++- src/server/game/Tickets/TicketMgr.cpp | 79 ++++++++++------------ src/server/game/Tickets/TicketMgr.h | 7 +- .../Database/Implementation/CharacterDatabase.cpp | 4 +- 5 files changed, 90 insertions(+), 47 deletions(-) create mode 100644 sql/updates/characters/2012_10_17_00_character_gm_tickets.sql (limited to 'src/server/shared/Database/Implementation') diff --git a/sql/updates/characters/2012_10_17_00_character_gm_tickets.sql b/sql/updates/characters/2012_10_17_00_character_gm_tickets.sql new file mode 100644 index 00000000000..c41df0c115d --- /dev/null +++ b/sql/updates/characters/2012_10_17_00_character_gm_tickets.sql @@ -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`; diff --git a/src/server/game/Handlers/TicketHandler.cpp b/src/server/game/Handlers/TicketHandler.cpp index d6675188f6e..7fcfbbbc23e 100755 --- a/src/server/game/Handlers/TicketHandler.cpp +++ b/src/server/game/Handlers/TicketHandler.cpp @@ -16,6 +16,7 @@ * with this program. If not, see . */ +#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 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(dest.contents()), &realSize, const_cast(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(); diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp index 0a4682db759..51e17cb9076 100755 --- a/src/server/game/Tickets/TicketMgr.cpp +++ b/src/server/game/Tickets/TicketMgr.cpp @@ -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 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); } diff --git a/src/server/game/Tickets/TicketMgr.h b/src/server/game/Tickets/TicketMgr.h index ecf315c059c..63837e39452 100755 --- a/src/server/game/Tickets/TicketMgr.h +++ b/src/server/game/Tickets/TicketMgr.h @@ -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 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 GmTicketList; diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index c29d8313c20..d9b517c098f 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -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) -- cgit v1.2.3