From d33ec97b5079f75c4937d1ff268d9913f96192d9 Mon Sep 17 00:00:00 2001 From: Evan Martine Date: Wed, 5 Aug 2026 20:31:57 +0000 Subject: [PATCH] handlers: V2 summon ticket inventory Adds UnitSelectorGachaTicket so V2 typed summon tickets can be redeemed, and 25042026-era migration 13052026_CreateUserSummonTicketsV2 for the per-user ticket counts. The archive-driven summoning path itself is unchanged: GachaAction and GachaList already live on the helper layer upstream, and this only adds the typed-ticket entry point alongside them. --- .../gme/handlers/GmeControllerHandlers.cpp | 4 ++ gimuserver/gme/handlers/Handlers.hpp | 1 + .../gme/handlers/UnitSelectorGachaTicket.cpp | 37 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 gimuserver/gme/handlers/UnitSelectorGachaTicket.cpp diff --git a/gimuserver/gme/handlers/GmeControllerHandlers.cpp b/gimuserver/gme/handlers/GmeControllerHandlers.cpp index 1271f8f..1dd538a 100644 --- a/gimuserver/gme/handlers/GmeControllerHandlers.cpp +++ b/gimuserver/gme/handlers/GmeControllerHandlers.cpp @@ -77,6 +77,7 @@ static GmeHandler getHandler(std::string_view cmd) REGISTER("2o4axPIC", FriendGet, "EoYuZ2nbImhCU1c0"); REGISTER("F7JvPk5H", GachaAction, "bL9fipzaSy7xN2w1"); REGISTER("Uo86DcRh", GachaList, "8JbxFvuSaB2CK7Ln"); + REGISTER("k57TdKDj", UnitSelectorGachaTicket, "1IJ8SaNk"); REGISTER("NiYWKdzs", HomeInfo, "f6uOewOD"); REGISTER("9TvyNR5H", MissionEnd, "oINq0rfUFPx5MgmT"); REGISTER("jE6Sp0q4", MissionStart, "csiVLDKkxEwBfR70"); @@ -87,6 +88,7 @@ static GmeHandler getHandler(std::string_view cmd) REGISTER("ynB7X5P9", UpdateInfoLight, "7kH9NXwC"); REGISTER("cTZ3W2JG", UserInfo, "ScJx6ywWEb0A3njT"); + } } @@ -150,6 +152,7 @@ drogon::Task GmeController::Handle(drogon::SessionPtr session, const catch (const drogon::orm::DrogonDbException& ex) { LOG_ERROR << "Handler error " << header.id << " (" << handler.name << ") database exception: " << ex.base().what(); + logReq << "EXCEPTION (db): " << ex.base().what() << "\n"; GmeError err{}; err.cmd = GmeErrorCommand::Close; err.flag = GmeErrorFlags::IsInError; @@ -159,6 +162,7 @@ drogon::Task GmeController::Handle(drogon::SessionPtr session, const catch (const std::exception& ex) { LOG_ERROR << "Handler error " << header.id << " (" << handler.name << ") exception: " << ex.what(); + logReq << "EXCEPTION (std): " << ex.what() << "\n"; GmeError err{}; err.cmd = GmeErrorCommand::Close; err.flag = GmeErrorFlags::IsInError; diff --git a/gimuserver/gme/handlers/Handlers.hpp b/gimuserver/gme/handlers/Handlers.hpp index e8eb93e..b0c84da 100644 --- a/gimuserver/gme/handlers/Handlers.hpp +++ b/gimuserver/gme/handlers/Handlers.hpp @@ -87,6 +87,7 @@ namespace GmeHandlers HANDLE(FriendGet); HANDLE(GachaAction); HANDLE(GachaList); + HANDLE(UnitSelectorGachaTicket); HANDLE(HomeInfo); HANDLE(MissionEnd); HANDLE(MissionStart); diff --git a/gimuserver/gme/handlers/UnitSelectorGachaTicket.cpp b/gimuserver/gme/handlers/UnitSelectorGachaTicket.cpp new file mode 100644 index 0000000..626391c --- /dev/null +++ b/gimuserver/gme/handlers/UnitSelectorGachaTicket.cpp @@ -0,0 +1,37 @@ +#include "App.hpp" +#include "Handlers.hpp" + +// UnitSelectorGachaTicket — handler stub for the "use a Unit Selector Gacha +// Ticket" client request. Triggered by the user tapping a per-door ticket +// button on a selector gacha (e.g. the Burst Heroes Selector banner under +// the Veteran summon category). +// +// GroupId = "k57TdKDj" +// AES key = "1IJ8SaNk" +// (per BraveFrontier IDA readParam and Handler exports.../UnitSelectorGachaTicketRequestHandler.hpp) +// +// Request shape (from bfdata/createbody/UnitSelectorGachaTicketRequest.txt): +// "CGHaOZda": [{ +// "7Ffmi96v": "", target gacha door +// "XIvaD6Jp": "", which selector entry within that gacha +// "pn16CNah": "", the unit the user picked from the pool +// "H6k1LIxC": "", unknown — possibly count or ticket-id +// "C5QbG2DM": "", unknown — possibly cost +// }] +// +// Response: empty `{}` for now. The full flow needs: +// 1. Verify the user owns at least one matching V2 ticket +// 2. INSERT the selected unit into user_units +// 3. Decrement user_summon_tickets_v2 for the ticket type +// 4. Return UserUnitInfo + UnitSelectorGachaUserInfoResponse (CGHaOZda) +// +// Even an empty `{}` is useful as a registration test — the BF client may +// hide UI elements whose target endpoint isn't registered on the server +// (since tapping would error out). See handbook §7.11.6 for the broader +// "use ticket" button-visibility investigation. +HANDLEF(UnitSelectorGachaTicket) +{ + (void)session; + LOG_INFO << "UnitSelectorGachaTicket (stub): " << json; + co_return HandleResult::success("{}"); +}