mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
Move all direct DB calls from handlers_festa.go (23 calls across 8 tables) and handlers_tower.go (16 calls across 4 tables) into dedicated repository structs following the established pattern. FestaRepository (14 methods): lifecycle cleanup, event management, team souls, trial stats/rankings, user state, voting, registration, soul submission, prize claiming/enumeration. TowerRepository (12 methods): personal tower data (skills, progress, gems), guild tenrouirai progress/scores/page advancement, tower RP. Also fix pre-existing nil pointer panics in integration tests by adding SetTestDB helper that initializes both the DB connection and all repositories, and wire the done channel in createTestServerWithDB to prevent Shutdown panics.
529 lines
16 KiB
Go
529 lines
16 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"sort"
|
|
"time"
|
|
|
|
"erupe-ce/common/byteframe"
|
|
ps "erupe-ce/common/pascalstring"
|
|
"erupe-ce/common/token"
|
|
_config "erupe-ce/config"
|
|
"erupe-ce/network/mhfpacket"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func handleMsgMhfSaveMezfesData(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfSaveMezfesData)
|
|
saveCharacterData(s, pkt.AckHandle, "mezfes", pkt.RawDataPayload, 4096)
|
|
}
|
|
|
|
func handleMsgMhfLoadMezfesData(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfLoadMezfesData)
|
|
loadCharacterData(s, pkt.AckHandle, "mezfes",
|
|
[]byte{0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
|
}
|
|
|
|
func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfEnumerateRanking)
|
|
bf := byteframe.NewByteFrame()
|
|
state := s.server.erupeConfig.DebugOptions.TournamentOverride
|
|
// Unk
|
|
// Unk
|
|
// Start?
|
|
// End?
|
|
midnight := TimeMidnight()
|
|
switch state {
|
|
case 1:
|
|
bf.WriteUint32(uint32(midnight.Unix()))
|
|
bf.WriteUint32(uint32(midnight.Add(3 * 24 * time.Hour).Unix()))
|
|
bf.WriteUint32(uint32(midnight.Add(13 * 24 * time.Hour).Unix()))
|
|
bf.WriteUint32(uint32(midnight.Add(20 * 24 * time.Hour).Unix()))
|
|
case 2:
|
|
bf.WriteUint32(uint32(midnight.Add(-3 * 24 * time.Hour).Unix()))
|
|
bf.WriteUint32(uint32(midnight.Unix()))
|
|
bf.WriteUint32(uint32(midnight.Add(10 * 24 * time.Hour).Unix()))
|
|
bf.WriteUint32(uint32(midnight.Add(17 * 24 * time.Hour).Unix()))
|
|
case 3:
|
|
bf.WriteUint32(uint32(midnight.Add(-13 * 24 * time.Hour).Unix()))
|
|
bf.WriteUint32(uint32(midnight.Add(-10 * 24 * time.Hour).Unix()))
|
|
bf.WriteUint32(uint32(midnight.Unix()))
|
|
bf.WriteUint32(uint32(midnight.Add(7 * 24 * time.Hour).Unix()))
|
|
default:
|
|
bf.WriteBytes(make([]byte, 16))
|
|
bf.WriteUint32(uint32(TimeAdjusted().Unix())) // TS Current Time
|
|
bf.WriteUint8(3)
|
|
bf.WriteBytes(make([]byte, 4))
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
return
|
|
}
|
|
bf.WriteUint32(uint32(TimeAdjusted().Unix())) // TS Current Time
|
|
bf.WriteUint8(3)
|
|
ps.Uint8(bf, "", false)
|
|
bf.WriteUint16(0) // numEvents
|
|
bf.WriteUint8(0) // numCups
|
|
|
|
/*
|
|
struct event
|
|
uint32 eventID
|
|
uint16 unk
|
|
uint16 unk
|
|
uint32 unk
|
|
psUint8 name
|
|
|
|
struct cup
|
|
uint32 cupID
|
|
uint16 unk
|
|
uint16 unk
|
|
uint16 unk
|
|
psUint8 name
|
|
psUint16 desc
|
|
*/
|
|
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
|
|
func cleanupFesta(s *Session) {
|
|
if err := s.server.festaRepo.CleanupAll(); err != nil {
|
|
s.logger.Error("Failed to cleanup festa", zap.Error(err))
|
|
}
|
|
}
|
|
|
|
// Festa timing constants (all values in seconds)
|
|
const (
|
|
festaVotingDuration = 9000 // 150 min voting window
|
|
festaRewardDuration = 1240200 // ~14.35 days reward period
|
|
festaEventLifespan = 2977200 // ~34.5 days total event window
|
|
)
|
|
|
|
func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
|
|
timestamps := make([]uint32, 5)
|
|
midnight := TimeMidnight()
|
|
if debug && start <= 3 {
|
|
midnight := uint32(midnight.Unix())
|
|
switch start {
|
|
case 1:
|
|
timestamps[0] = midnight
|
|
timestamps[1] = timestamps[0] + secsPerWeek
|
|
timestamps[2] = timestamps[1] + secsPerWeek
|
|
timestamps[3] = timestamps[2] + festaVotingDuration
|
|
timestamps[4] = timestamps[3] + festaRewardDuration
|
|
case 2:
|
|
timestamps[0] = midnight - secsPerWeek
|
|
timestamps[1] = midnight
|
|
timestamps[2] = timestamps[1] + secsPerWeek
|
|
timestamps[3] = timestamps[2] + festaVotingDuration
|
|
timestamps[4] = timestamps[3] + festaRewardDuration
|
|
case 3:
|
|
timestamps[0] = midnight - 2*secsPerWeek
|
|
timestamps[1] = midnight - secsPerWeek
|
|
timestamps[2] = midnight
|
|
timestamps[3] = timestamps[2] + festaVotingDuration
|
|
timestamps[4] = timestamps[3] + festaRewardDuration
|
|
}
|
|
return timestamps
|
|
}
|
|
if start == 0 || TimeAdjusted().Unix() > int64(start)+festaEventLifespan {
|
|
cleanupFesta(s)
|
|
// Generate a new festa, starting midnight tomorrow
|
|
start = uint32(midnight.Add(24 * time.Hour).Unix())
|
|
if err := s.server.festaRepo.InsertEvent(start); err != nil {
|
|
s.logger.Error("Failed to insert festa event", zap.Error(err))
|
|
}
|
|
}
|
|
timestamps[0] = start
|
|
timestamps[1] = timestamps[0] + secsPerWeek
|
|
timestamps[2] = timestamps[1] + secsPerWeek
|
|
timestamps[3] = timestamps[2] + festaVotingDuration
|
|
timestamps[4] = timestamps[3] + festaRewardDuration
|
|
return timestamps
|
|
}
|
|
|
|
// FestaTrial represents a festa trial/challenge entry.
|
|
type FestaTrial struct {
|
|
ID uint32 `db:"id"`
|
|
Objective uint16 `db:"objective"`
|
|
GoalID uint32 `db:"goal_id"`
|
|
TimesReq uint16 `db:"times_req"`
|
|
Locale uint16 `db:"locale_req"`
|
|
Reward uint16 `db:"reward"`
|
|
Monopoly FestivalColor `db:"monopoly"`
|
|
Unk uint16
|
|
}
|
|
|
|
// FestaReward represents a festa reward entry.
|
|
type FestaReward struct {
|
|
Unk0 uint8
|
|
Unk1 uint8
|
|
ItemType uint16
|
|
Quantity uint16
|
|
ItemID uint16
|
|
MinHR uint16 // Minimum Hunter Rank to receive this reward
|
|
MinSR uint16 // Minimum Skill Rank (max across weapon types) to receive this reward
|
|
MinGR uint8 // Minimum G Rank to receive this reward
|
|
}
|
|
|
|
func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfInfoFesta)
|
|
bf := byteframe.NewByteFrame()
|
|
|
|
const festaIDSentinel = uint32(0xDEADBEEF)
|
|
id, start := festaIDSentinel, uint32(0)
|
|
events, err := s.server.festaRepo.GetFestaEvents()
|
|
if err != nil {
|
|
s.logger.Error("Failed to query festa schedule", zap.Error(err))
|
|
} else {
|
|
for _, e := range events {
|
|
id = e.ID
|
|
start = e.StartTime
|
|
}
|
|
}
|
|
|
|
var timestamps []uint32
|
|
if s.server.erupeConfig.DebugOptions.FestaOverride >= 0 {
|
|
if s.server.erupeConfig.DebugOptions.FestaOverride == 0 {
|
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
timestamps = generateFestaTimestamps(s, uint32(s.server.erupeConfig.DebugOptions.FestaOverride), true)
|
|
} else {
|
|
timestamps = generateFestaTimestamps(s, start, false)
|
|
}
|
|
|
|
if timestamps[0] > uint32(TimeAdjusted().Unix()) {
|
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
blueSouls, err := s.server.festaRepo.GetTeamSouls("blue")
|
|
if err != nil {
|
|
s.logger.Error("Failed to get blue souls", zap.Error(err))
|
|
}
|
|
redSouls, err := s.server.festaRepo.GetTeamSouls("red")
|
|
if err != nil {
|
|
s.logger.Error("Failed to get red souls", zap.Error(err))
|
|
}
|
|
|
|
bf.WriteUint32(id)
|
|
for _, timestamp := range timestamps {
|
|
bf.WriteUint32(timestamp)
|
|
}
|
|
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
|
|
bf.WriteUint8(4)
|
|
ps.Uint8(bf, "", false)
|
|
bf.WriteUint32(0)
|
|
bf.WriteUint32(blueSouls)
|
|
bf.WriteUint32(redSouls)
|
|
|
|
trials, err := s.server.festaRepo.GetTrialsWithMonopoly()
|
|
if err != nil {
|
|
s.logger.Error("Failed to query festa trials", zap.Error(err))
|
|
}
|
|
bf.WriteUint16(uint16(len(trials)))
|
|
for _, trial := range trials {
|
|
bf.WriteUint32(trial.ID)
|
|
bf.WriteUint16(trial.Objective)
|
|
bf.WriteUint32(trial.GoalID)
|
|
bf.WriteUint16(trial.TimesReq)
|
|
bf.WriteUint16(trial.Locale)
|
|
bf.WriteUint16(trial.Reward)
|
|
bf.WriteInt16(FestivalColorCodes[trial.Monopoly])
|
|
if s.server.erupeConfig.RealClientMode >= _config.F4 { // Not in S6.0
|
|
bf.WriteUint16(trial.Unk)
|
|
}
|
|
}
|
|
|
|
// The Winner and Loser Armor IDs are missing
|
|
// Item 7011 may not exist in older versions, remove to prevent crashes
|
|
// Fields: {Unk0, Unk1, ItemType, Quantity, ItemID, MinHR, MinSR, MinGR}
|
|
rewards := []FestaReward{
|
|
{1, 0, 7, 350, 1520, 0, 0, 0},
|
|
{1, 0, 7, 1000, 7011, 0, 0, 1},
|
|
{1, 0, 12, 1000, 0, 0, 0, 0},
|
|
{1, 0, 13, 0, 0, 0, 0, 0},
|
|
//{1, 0, 1, 0, 0, 0, 0, 0},
|
|
{2, 0, 7, 350, 1520, 0, 0, 0},
|
|
{2, 0, 7, 1000, 7011, 0, 0, 1},
|
|
{2, 0, 12, 1000, 0, 0, 0, 0},
|
|
{2, 0, 13, 0, 0, 0, 0, 0},
|
|
//{2, 0, 4, 0, 0, 0, 0, 0},
|
|
{3, 0, 7, 350, 1520, 0, 0, 0},
|
|
{3, 0, 7, 1000, 7011, 0, 0, 1},
|
|
{3, 0, 12, 1000, 0, 0, 0, 0},
|
|
{3, 0, 13, 0, 0, 0, 0, 0},
|
|
//{3, 0, 1, 0, 0, 0, 0, 0},
|
|
{4, 0, 7, 350, 1520, 0, 0, 0},
|
|
{4, 0, 7, 1000, 7011, 0, 0, 1},
|
|
{4, 0, 12, 1000, 0, 0, 0, 0},
|
|
{4, 0, 13, 0, 0, 0, 0, 0},
|
|
//{4, 0, 4, 0, 0, 0, 0, 0},
|
|
{5, 0, 7, 350, 1520, 0, 0, 0},
|
|
{5, 0, 7, 1000, 7011, 0, 0, 1},
|
|
{5, 0, 12, 1000, 0, 0, 0, 0},
|
|
{5, 0, 13, 0, 0, 0, 0, 0},
|
|
//{5, 0, 1, 0, 0, 0, 0, 0},
|
|
}
|
|
|
|
bf.WriteUint16(uint16(len(rewards)))
|
|
for _, reward := range rewards {
|
|
bf.WriteUint8(reward.Unk0)
|
|
bf.WriteUint8(reward.Unk1)
|
|
bf.WriteUint16(reward.ItemType)
|
|
bf.WriteUint16(reward.Quantity)
|
|
bf.WriteUint16(reward.ItemID)
|
|
// Confirmed present in G3 via Wii U disassembly of import_festa_info
|
|
if s.server.erupeConfig.RealClientMode >= _config.G3 {
|
|
bf.WriteUint16(reward.MinHR)
|
|
bf.WriteUint16(reward.MinSR)
|
|
bf.WriteUint8(reward.MinGR)
|
|
}
|
|
}
|
|
if s.server.erupeConfig.RealClientMode <= _config.G61 {
|
|
if s.server.erupeConfig.GameplayOptions.MaximumFP > 0xFFFF {
|
|
s.server.erupeConfig.GameplayOptions.MaximumFP = 0xFFFF
|
|
}
|
|
bf.WriteUint16(uint16(s.server.erupeConfig.GameplayOptions.MaximumFP))
|
|
} else {
|
|
bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MaximumFP)
|
|
}
|
|
bf.WriteUint16(100) // Reward multiplier (%)
|
|
|
|
bf.WriteUint16(4)
|
|
for i := uint16(0); i < 4; i++ {
|
|
ranking, err := s.server.festaRepo.GetTopGuildForTrial(i + 1)
|
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
s.logger.Error("Failed to get festa trial ranking", zap.Error(err))
|
|
}
|
|
bf.WriteUint32(ranking.GuildID)
|
|
bf.WriteUint16(i + 1)
|
|
bf.WriteInt16(FestivalColorCodes[ranking.Team])
|
|
ps.Uint8(bf, ranking.GuildName, true)
|
|
}
|
|
bf.WriteUint16(7)
|
|
for i := uint16(0); i < 7; i++ {
|
|
offset := secsPerDay * uint32(i)
|
|
ranking, err := s.server.festaRepo.GetTopGuildInWindow(timestamps[1]+offset, timestamps[1]+offset+secsPerDay)
|
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
s.logger.Error("Failed to get festa daily ranking", zap.Error(err))
|
|
}
|
|
bf.WriteUint32(ranking.GuildID)
|
|
bf.WriteUint16(i + 1)
|
|
bf.WriteInt16(FestivalColorCodes[ranking.Team])
|
|
ps.Uint8(bf, ranking.GuildName, true)
|
|
}
|
|
|
|
bf.WriteUint32(0) // Clan goal
|
|
// Final bonus rates
|
|
bf.WriteUint32(5000) // 5000+ souls
|
|
bf.WriteUint32(2000) // 2000-4999 souls
|
|
bf.WriteUint32(1000) // 1000-1999 souls
|
|
bf.WriteUint32(100) // 100-999 souls
|
|
bf.WriteUint16(300) // 300% bonus
|
|
bf.WriteUint16(200) // 200% bonus
|
|
bf.WriteUint16(150) // 150% bonus
|
|
bf.WriteUint16(100) // Normal rate
|
|
bf.WriteUint16(50) // 50% penalty
|
|
|
|
if s.server.erupeConfig.RealClientMode >= _config.G52 {
|
|
ps.Uint16(bf, "", false)
|
|
}
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
|
|
// state festa (U)ser
|
|
func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfStateFestaU)
|
|
guild, err := s.server.guildRepo.GetByCharID(s.charID)
|
|
applicant := false
|
|
if guild != nil {
|
|
applicant, _ = s.server.guildRepo.HasApplication(guild.ID, s.charID)
|
|
}
|
|
if err != nil || guild == nil || applicant {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
souls, err := s.server.festaRepo.GetCharSouls(s.charID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get festa user souls", zap.Error(err))
|
|
}
|
|
claimed := s.server.festaRepo.HasClaimedMainPrize(s.charID)
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint32(souls)
|
|
if !claimed {
|
|
bf.WriteBool(true)
|
|
bf.WriteBool(false)
|
|
} else {
|
|
bf.WriteBool(false)
|
|
bf.WriteBool(true)
|
|
}
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
|
|
// state festa (G)uild
|
|
func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfStateFestaG)
|
|
guild, err := s.server.guildRepo.GetByCharID(s.charID)
|
|
applicant := false
|
|
if guild != nil {
|
|
applicant, _ = s.server.guildRepo.HasApplication(guild.ID, s.charID)
|
|
}
|
|
resp := byteframe.NewByteFrame()
|
|
if err != nil || guild == nil || applicant {
|
|
resp.WriteUint32(0)
|
|
resp.WriteInt32(0)
|
|
resp.WriteInt32(-1)
|
|
resp.WriteInt32(0)
|
|
resp.WriteInt32(0)
|
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
|
return
|
|
}
|
|
resp.WriteUint32(guild.Souls)
|
|
resp.WriteInt32(1) // unk
|
|
resp.WriteInt32(1) // unk, rank?
|
|
resp.WriteInt32(1) // unk
|
|
resp.WriteInt32(1) // unk
|
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
|
}
|
|
|
|
func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfEnumerateFestaMember)
|
|
guild, err := s.server.guildRepo.GetByCharID(s.charID)
|
|
if err != nil || guild == nil {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
members, err := s.server.guildRepo.GetMembers(guild.ID, false)
|
|
if err != nil {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
sort.Slice(members, func(i, j int) bool {
|
|
return members[i].Souls > members[j].Souls
|
|
})
|
|
var validMembers []*GuildMember
|
|
for _, member := range members {
|
|
if member.Souls > 0 {
|
|
validMembers = append(validMembers, member)
|
|
}
|
|
}
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint16(uint16(len(validMembers)))
|
|
bf.WriteUint16(0) // Unk
|
|
for _, member := range validMembers {
|
|
bf.WriteUint32(member.CharID)
|
|
if s.server.erupeConfig.RealClientMode <= _config.Z1 {
|
|
bf.WriteUint16(uint16(member.Souls))
|
|
bf.WriteUint16(0)
|
|
} else {
|
|
bf.WriteUint32(member.Souls)
|
|
}
|
|
}
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
|
|
func handleMsgMhfVoteFesta(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfVoteFesta)
|
|
if err := s.server.festaRepo.VoteTrial(s.charID, pkt.TrialID); err != nil {
|
|
s.logger.Error("Failed to update festa trial vote", zap.Error(err))
|
|
}
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
|
|
func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfEntryFesta)
|
|
guild, err := s.server.guildRepo.GetByCharID(s.charID)
|
|
if err != nil || guild == nil {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
team := uint32(token.RNG.Intn(2))
|
|
teamName := "blue"
|
|
if team == 1 {
|
|
teamName = "red"
|
|
}
|
|
if err := s.server.festaRepo.RegisterGuild(guild.ID, teamName); err != nil {
|
|
s.logger.Error("Failed to register guild for festa", zap.Error(err))
|
|
}
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint32(team)
|
|
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
|
|
func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfChargeFesta)
|
|
if err := s.server.festaRepo.SubmitSouls(s.charID, pkt.GuildID, pkt.Souls); err != nil {
|
|
s.logger.Error("Failed to submit festa souls", zap.Error(err))
|
|
}
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
|
|
func handleMsgMhfAcquireFesta(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfAcquireFesta)
|
|
if err := s.server.festaRepo.ClaimPrize(0, s.charID); err != nil {
|
|
s.logger.Error("Failed to accept festa prize", zap.Error(err))
|
|
}
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
|
|
func handleMsgMhfAcquireFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfAcquireFestaPersonalPrize)
|
|
if err := s.server.festaRepo.ClaimPrize(pkt.PrizeID, s.charID); err != nil {
|
|
s.logger.Error("Failed to accept festa personal prize", zap.Error(err))
|
|
}
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
|
|
func handleMsgMhfAcquireFestaIntermediatePrize(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfAcquireFestaIntermediatePrize)
|
|
if err := s.server.festaRepo.ClaimPrize(pkt.PrizeID, s.charID); err != nil {
|
|
s.logger.Error("Failed to accept festa intermediate prize", zap.Error(err))
|
|
}
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
|
|
// Prize represents a festa prize entry.
|
|
type Prize struct {
|
|
ID uint32 `db:"id"`
|
|
Tier uint32 `db:"tier"`
|
|
SoulsReq uint32 `db:"souls_req"`
|
|
ItemID uint32 `db:"item_id"`
|
|
NumItem uint32 `db:"num_item"`
|
|
Claimed int `db:"claimed"`
|
|
}
|
|
|
|
func writePrizeList(s *Session, pkt mhfpacket.MHFPacket, ackHandle uint32, prizeType string) {
|
|
prizes, err := s.server.festaRepo.ListPrizes(s.charID, prizeType)
|
|
var count uint32
|
|
prizeData := byteframe.NewByteFrame()
|
|
if err != nil {
|
|
s.logger.Error("Failed to query festa prizes", zap.Error(err), zap.String("type", prizeType))
|
|
} else {
|
|
for _, prize := range prizes {
|
|
count++
|
|
prizeData.WriteUint32(prize.ID)
|
|
prizeData.WriteUint32(prize.Tier)
|
|
prizeData.WriteUint32(prize.SoulsReq)
|
|
prizeData.WriteUint32(7) // Unk
|
|
prizeData.WriteUint32(prize.ItemID)
|
|
prizeData.WriteUint32(prize.NumItem)
|
|
prizeData.WriteBool(prize.Claimed > 0)
|
|
}
|
|
}
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint32(count)
|
|
bf.WriteBytes(prizeData.Data())
|
|
doAckBufSucceed(s, ackHandle, bf.Data())
|
|
}
|
|
|
|
func handleMsgMhfEnumerateFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfEnumerateFestaPersonalPrize)
|
|
writePrizeList(s, p, pkt.AckHandle, "personal")
|
|
}
|
|
|
|
func handleMsgMhfEnumerateFestaIntermediatePrize(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfEnumerateFestaIntermediatePrize)
|
|
writePrizeList(s, p, pkt.AckHandle, "guild")
|
|
}
|