package channelserver import ( "encoding/hex" "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" "erupe-ce/network/mhfpacket" "math/rand" "time" ) func handleMsgMhfSaveMezfesData(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveMezfesData) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } func handleMsgMhfLoadMezfesData(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfLoadMezfesData) resp := byteframe.NewByteFrame() resp.WriteUint32(0) // Unk resp.WriteUint8(2) // Count of the next 2 uint32s resp.WriteUint32(0) resp.WriteUint32(0) resp.WriteUint32(0) // Unk doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateRanking) bf := byteframe.NewByteFrame() state := s.server.erupeConfig.DevModeOptions.TournamentEvent // Unk // Unk // Start? // End? midnight := Time_Current_Midnight() 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(Time_Current_Adjusted().Unix())) // TS Current Time bf.WriteUint8(3) bf.WriteBytes(make([]byte, 4)) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) return } bf.WriteUint32(uint32(Time_Current_Adjusted().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) { s.server.db.Exec("DELETE FROM events WHERE event_type='festa'") s.server.db.Exec("DELETE FROM festa_registrations") s.server.db.Exec("DELETE FROM festa_prizes_accepted") s.server.db.Exec("UPDATE guild_characters SET souls=0") } func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 { timestamps := make([]uint32, 5) midnight := Time_Current_Midnight() if debug && start <= 3 { midnight := uint32(midnight.Unix()) switch start { case 1: timestamps[0] = midnight timestamps[1] = timestamps[0] + 604800 timestamps[2] = timestamps[1] + 604800 timestamps[3] = timestamps[2] + 9000 timestamps[4] = timestamps[3] + 1240200 case 2: timestamps[0] = midnight - 604800 timestamps[1] = midnight timestamps[2] = timestamps[1] + 604800 timestamps[3] = timestamps[2] + 9000 timestamps[4] = timestamps[3] + 1240200 case 3: timestamps[0] = midnight - 1209600 timestamps[1] = midnight - 604800 timestamps[2] = midnight timestamps[3] = timestamps[2] + 9000 timestamps[4] = timestamps[3] + 1240200 } return timestamps } if start == 0 || Time_Current_Adjusted().Unix() > int64(start)+2977200 { cleanupFesta(s) // Generate a new festa, starting midnight tomorrow start = uint32(midnight.Add(24 * time.Hour).Unix()) s.server.db.Exec("INSERT INTO events (event_type, start_time) VALUES ('festa', to_timestamp($1)::timestamp without time zone)", start) } timestamps[0] = start timestamps[1] = timestamps[0] + 604800 timestamps[2] = timestamps[1] + 604800 timestamps[3] = timestamps[2] + 9000 timestamps[4] = timestamps[3] + 1240200 return timestamps } type Trial struct { ID uint32 `db:"id"` Objective uint8 `db:"objective"` GoalID uint32 `db:"goal_id"` TimesReq uint16 `db:"times_req"` Locale uint16 `db:"locale_req"` Reward uint16 `db:"reward"` } func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfInfoFesta) bf := byteframe.NewByteFrame() id, start := uint32(0xDEADBEEF), uint32(0) rows, _ := s.server.db.Queryx("SELECT id, (EXTRACT(epoch FROM start_time)::int) as start_time FROM events WHERE event_type='festa'") for rows.Next() { rows.Scan(&id, &start) } var timestamps []uint32 if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.FestaEvent >= 0 { if s.server.erupeConfig.DevModeOptions.FestaEvent == 0 { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } timestamps = generateFestaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.FestaEvent), true) } else { timestamps = generateFestaTimestamps(s, start, false) } if timestamps[0] > uint32(Time_Current_Adjusted().Unix()) { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } var blueSouls, redSouls uint32 s.server.db.QueryRow("SELECT SUM(gc.souls) FROM guild_characters gc INNER JOIN festa_registrations fr ON fr.guild_id = gc.guild_id WHERE fr.team = 'blue'").Scan(&blueSouls) s.server.db.QueryRow("SELECT SUM(gc.souls) FROM guild_characters gc INNER JOIN festa_registrations fr ON fr.guild_id = gc.guild_id WHERE fr.team = 'red'").Scan(&redSouls) bf.WriteUint32(id) for _, timestamp := range timestamps { bf.WriteUint32(timestamp) } bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) bf.WriteUint8(4) ps.Uint8(bf, "", false) bf.WriteUint32(0) bf.WriteUint32(blueSouls) bf.WriteUint32(redSouls) rows, _ = s.server.db.Queryx("SELECT * FROM festa_trials") trialData := byteframe.NewByteFrame() var count uint16 for rows.Next() { trial := &Trial{} err := rows.StructScan(&trial) if err != nil { continue } count++ trialData.WriteUint32(trial.ID) trialData.WriteUint8(0) // Unk trialData.WriteUint8(trial.Objective) trialData.WriteUint32(trial.GoalID) trialData.WriteUint16(trial.TimesReq) trialData.WriteUint16(trial.Locale) trialData.WriteUint16(trial.Reward) trialData.WriteUint8(0xFF) // Unk trialData.WriteUint8(0xFF) // MonopolyState trialData.WriteUint16(0) // Unk } bf.WriteUint16(count) bf.WriteBytes(trialData.Data()) // Static bonus rewards rewards, _ := hex.DecodeString("001901000007015E05F000000000000100000703E81B6300000000010100000C03E8000000000000000100000D0000000000000000000100000100000000000000000002000007015E05F000000000000200000703E81B6300000000010200000C03E8000000000000000200000D0000000000000000000200000400000000000000000003000007015E05F000000000000300000703E81B6300000000010300000C03E8000000000000000300000D0000000000000000000300000100000000000000000004000007015E05F000000000000400000703E81B6300000000010400000C03E8000000000000000400000D0000000000000000000400000400000000000000000005000007015E05F000000000000500000703E81B6300000000010500000C03E8000000000000000500000D00000000000000000005000001000000000000000000") bf.WriteBytes(rewards) bf.WriteUint16(0x0001) bf.WriteUint32(0xD4C001F4) categoryWinners := uint16(0) // NYI bf.WriteUint16(categoryWinners) for i := uint16(0); i < categoryWinners; i++ { bf.WriteUint32(0) // Guild ID bf.WriteUint16(i + 1) // Category ID bf.WriteUint16(0) // Festa Team ps.Uint8(bf, "", true) // Guild Name } dailyWinners := uint16(0) // NYI bf.WriteUint16(dailyWinners) for i := uint16(0); i < dailyWinners; i++ { bf.WriteUint32(0) // Guild ID bf.WriteUint16(i + 1) // Category ID bf.WriteUint16(0) // Festa Team ps.Uint8(bf, "", true) // Guild Name } d, _ := hex.DecodeString("000000000000000100001388000007D0000003E800000064012C00C8009600640032") bf.WriteBytes(d) 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 := GetGuildInfoByCharacterId(s, s.charID) applicant := false if guild != nil { applicant, _ = guild.HasApplicationForCharID(s, s.charID) } if err != nil || guild == nil || applicant { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } var souls uint32 s.server.db.QueryRow("SELECT souls FROM guild_characters WHERE character_id=$1", s.charID).Scan(&souls) bf := byteframe.NewByteFrame() bf.WriteUint32(souls) bf.WriteUint32(0) // unk doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } // state festa (G)uild func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStateFestaG) guild, err := GetGuildInfoByCharacterId(s, s.charID) applicant := false if guild != nil { applicant, _ = guild.HasApplicationForCharID(s, s.charID) } resp := byteframe.NewByteFrame() if err != nil || guild == nil || applicant { resp.WriteUint32(0) resp.WriteUint32(0) resp.WriteUint32(0xFFFFFFFF) resp.WriteUint32(0) resp.WriteUint32(0) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) return } resp.WriteUint32(guild.Souls) resp.WriteUint32(0) // unk resp.WriteUint32(0) // unk, rank? resp.WriteUint32(0) // unk resp.WriteUint32(0) // unk doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateFestaMember) guild, err := GetGuildInfoByCharacterId(s, s.charID) if err != nil || guild == nil { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } members, err := GetGuildMembers(s, guild.ID, false) if err != nil { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } bf := byteframe.NewByteFrame() bf.WriteUint16(uint16(len(members))) bf.WriteUint16(0) // Unk for _, member := range members { bf.WriteUint32(member.CharID) bf.WriteUint32(member.Souls) } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfVoteFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfVoteFesta) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEntryFesta) guild, err := GetGuildInfoByCharacterId(s, s.charID) if err != nil || guild == nil { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } rand.Seed(time.Now().UnixNano()) team := uint32(rand.Intn(2)) switch team { case 0: s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'blue')", guild.ID) case 1: s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'red')", guild.ID) } bf := byteframe.NewByteFrame() bf.WriteUint32(team) doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfChargeFesta) s.server.db.Exec("UPDATE guild_characters SET souls=souls+$1 WHERE character_id=$2", pkt.Souls, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfAcquireFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireFesta) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfAcquireFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireFestaPersonalPrize) s.server.db.Exec("INSERT INTO public.festa_prizes_accepted VALUES ($1, $2)", pkt.PrizeID, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfAcquireFestaIntermediatePrize(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireFestaIntermediatePrize) s.server.db.Exec("INSERT INTO public.festa_prizes_accepted VALUES ($1, $2)", pkt.PrizeID, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } 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 handleMsgMhfEnumerateFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateFestaPersonalPrize) rows, _ := s.server.db.Queryx("SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = 4) AS claimed FROM festa_prizes fp WHERE type='personal'") var count uint32 prizeData := byteframe.NewByteFrame() for rows.Next() { prize := &Prize{} err := rows.StructScan(&prize) if err != nil { continue } 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, pkt.AckHandle, bf.Data()) } func handleMsgMhfEnumerateFestaIntermediatePrize(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateFestaIntermediatePrize) rows, _ := s.server.db.Queryx("SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = 4) AS claimed FROM festa_prizes fp WHERE type='guild'") var count uint32 prizeData := byteframe.NewByteFrame() for rows.Next() { prize := &Prize{} err := rows.StructScan(&prize) if err != nil { continue } 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, pkt.AckHandle, bf.Data()) }