GM Tickets

GM ticket system

Persistence, assignment, application layer, and 4.3.4 / build 15595 networking for WoW-style help tickets. Complements GM Commands (. commands and gossip desk UI).

Goals

  • Players open tickets from the client help UI; server persists and responds with SMSG_GM_TICKET_*
  • Staff claim, reply, and close; players see replies and can resolve (CMSG_GM_TICKET_RESPONSE_RESOLVE)
  • Queue lives in firelands_characters (same DB as characters)

Persistence — gm_ticket table

Defined in sql/18_gm_ticket.sql.

ColumnPurpose
idStable ticket id
account_idPlayer account
character_guidOwner character (FK characters.guid)
statusSee GmTicketStatus in domain/models/GmTicket.h
categoryClient category byte
messagePlayer text
gm_responseLast staff reply
map_id, pos_*Snapshot at creation
assigned_account_idAssigned GM or NULL in open queue
created_at, updated_at, assigned_at, closed_atAudit / FIFO

GmTicketStatus

StatusMeaning
OpenIn queue, unassigned
Assignedassigned_account_id set
GmAnsweredStaff reply stored
ClosedResolvedPlayer resolved after reply
ClosedAbandonedPlayer deleted ticket
ClosedStaffStaff closed without player resolve

Repository

  • Port: domain/repositories/IGmTicketRepository.h
  • Adapter: MySqlGmTicketRepository
  • TryAssign: atomic UPDATE … WHERE assigned_account_id IS NULL AND status = 0 — requires affected_rows == 1

Business rules

  1. One active ticket per character (recommended) — reject duplicate open tickets
  2. Queue: ListUnassignedOpen(N) ordered by created_at ASC
  3. Claim: GmTicketService::Assign(ticketId, staffAccountId) validates permissions, calls TryAssign
  4. Reply: update gm_response, set GmAnswered, send SMSG_GMRESPONSE_RECEIVED
  5. Close: staff or player resolve paths update status and timestamps

Application layer

ComponentPath
Serviceapplication/services/GmTicketService.*
Handlersinfrastructure/network/sessions/worldsession/WorldSessionGmTicketHandlers.cpp
PacketsGmTicketPackets.cpp
Gossip deskWorldSessionGmTicketGossip.cpp, GmTicketGossipUi.h

Wired in world/main.cpp: MySqlGmTicketRepositoryGmTicketServiceWorldSession factory.

Permission: ManageGmTickets (default on Game Master).

Network opcodes (15595)

Constants in shared/network/WorldOpcodes.h (WowPacketParser V4_3_4_15595/Opcodes.cs).

Client → server

OpcodeValueRole
CMSG_GM_TICKET_CREATE0x0137Create ticket
CMSG_GM_TICKET_UPDATE_TEXT0x0636Update message
CMSG_GM_TICKET_DELETE_TICKET0x6B14Delete / abandon
CMSG_GM_TICKET_GET_TICKET0x0326Query current ticket
CMSG_GM_TICKET_GET_SYSTEM_STATUS0x4205Queue enabled/disabled
CMSG_GM_TICKET_RESPONSE_RESOLVE0x6506Player marks resolved
CMSG_GM_SURVEY_SUBMIT0x2724Post-resolution survey

Server → client

OpcodeValueRole
SMSG_GM_TICKET_CREATE0x2107Create result
SMSG_GM_TICKET_UPDATE_TEXT0x6535Update result
SMSG_GM_TICKET_DELETE_TICKET0x6D17Delete ack
SMSG_GM_TICKET_GET_TICKET0x2C15Ticket payload or none
SMSG_GM_TICKET_GET_SYSTEM_STATUS0x0D35Enable/disable UI
SMSG_GM_TICKET_STATUS_UPDATE0x2C25Queue notification
SMSG_GMRESPONSE_RECEIVED0x2E34GM reply to player
SMSG_GMRESPONSE_STATUS_UPDATE0x0A04After resolve
SMSG_GMRESPONSE_DB_ERROR0x0006Backend error

Important: Do not copy 3.3.5-era layouts. Use WowPacketParser 15595 structs or sniff captures; unit-test fixed hex blobs under tests/data/.

Staff UI

.ticket ui opens a synthetic gossip desk (no NPC). Reserved menu/text ids avoid colliding with world DB gossip. Full UX documented in GM Commands.

Implementation status

  • MySqlGmTicketRepository, GmTicketService, core CMSG/SMSG path
  • In-game .ticket commands and gossip desk UI
  • Reference hyperlink sanitization (optional hardening)