mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 23:54:33 +01:00
Move scan loops from handlers into repository methods so that interfaces return typed slices instead of leaking database cursors. This fixes resource leaks (7 of 12 call sites never closed rows) and makes all 12 methods mockable for unit tests. Affected repos: CafeRepo, ShopRepo, EventRepo, RengokuRepo, DivaRepo, ScenarioRepo, MiscRepo, MercenaryRepo. New structs: DivaEvent, MercenaryLoan, GuildHuntCatUsage. EventRepo.GetEventQuests left as-is (requires broader Server refactor).
222 lines
5.9 KiB
Go
222 lines
5.9 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"erupe-ce/common/token"
|
|
cfg "erupe-ce/config"
|
|
"math"
|
|
"time"
|
|
|
|
"erupe-ce/common/byteframe"
|
|
"erupe-ce/network/mhfpacket"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// Event represents an in-game event entry.
|
|
type Event struct {
|
|
EventType uint16
|
|
Unk1 uint16
|
|
Unk2 uint16
|
|
Unk3 uint16
|
|
Unk4 uint16
|
|
Unk5 uint32
|
|
Unk6 uint32
|
|
QuestFileIDs []uint16
|
|
}
|
|
|
|
func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfEnumerateEvent)
|
|
bf := byteframe.NewByteFrame()
|
|
|
|
events := []Event{}
|
|
|
|
bf.WriteUint8(uint8(len(events)))
|
|
for _, event := range events {
|
|
bf.WriteUint16(event.EventType)
|
|
bf.WriteUint16(event.Unk1)
|
|
bf.WriteUint16(event.Unk2)
|
|
bf.WriteUint16(event.Unk3)
|
|
bf.WriteUint16(event.Unk4)
|
|
bf.WriteUint32(event.Unk5)
|
|
bf.WriteUint32(event.Unk6)
|
|
if event.EventType == 2 {
|
|
bf.WriteUint8(uint8(len(event.QuestFileIDs)))
|
|
for _, qf := range event.QuestFileIDs {
|
|
bf.WriteUint16(qf)
|
|
}
|
|
}
|
|
}
|
|
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
|
|
type activeFeature struct {
|
|
StartTime time.Time `db:"start_time"`
|
|
ActiveFeatures uint32 `db:"featured"`
|
|
}
|
|
|
|
func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfGetWeeklySchedule)
|
|
|
|
var features []activeFeature
|
|
times := []time.Time{
|
|
TimeMidnight().Add(-24 * time.Hour),
|
|
TimeMidnight(),
|
|
TimeMidnight().Add(24 * time.Hour),
|
|
}
|
|
|
|
for _, t := range times {
|
|
temp, err := s.server.eventRepo.GetFeatureWeapon(t)
|
|
if err != nil || temp.StartTime.IsZero() {
|
|
weapons := token.RNG.Intn(s.server.erupeConfig.GameplayOptions.MaxFeatureWeapons-s.server.erupeConfig.GameplayOptions.MinFeatureWeapons+1) + s.server.erupeConfig.GameplayOptions.MinFeatureWeapons
|
|
temp = generateFeatureWeapons(weapons, s.server.erupeConfig.RealClientMode)
|
|
temp.StartTime = t
|
|
if err := s.server.eventRepo.InsertFeatureWeapon(temp.StartTime, temp.ActiveFeatures); err != nil {
|
|
s.logger.Error("Failed to insert feature weapon", zap.Error(err))
|
|
}
|
|
}
|
|
features = append(features, temp)
|
|
}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint8(uint8(len(features)))
|
|
bf.WriteUint32(uint32(TimeAdjusted().Add(-5 * time.Minute).Unix()))
|
|
for _, feature := range features {
|
|
bf.WriteUint32(uint32(feature.StartTime.Unix()))
|
|
bf.WriteUint32(feature.ActiveFeatures)
|
|
bf.WriteUint16(0)
|
|
}
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
|
|
func generateFeatureWeapons(count int, mode cfg.Mode) activeFeature {
|
|
_max := 14
|
|
if mode < cfg.ZZ {
|
|
_max = 13
|
|
}
|
|
if mode < cfg.G10 {
|
|
_max = 12
|
|
}
|
|
if mode < cfg.GG {
|
|
_max = 11
|
|
}
|
|
if count > _max {
|
|
count = _max
|
|
}
|
|
nums := make([]int, 0)
|
|
var result int
|
|
for len(nums) < count {
|
|
num := token.RNG.Intn(_max)
|
|
exist := false
|
|
for _, v := range nums {
|
|
if v == num {
|
|
exist = true
|
|
break
|
|
}
|
|
}
|
|
if !exist {
|
|
nums = append(nums, num)
|
|
}
|
|
}
|
|
for _, num := range nums {
|
|
result += int(math.Pow(2, float64(num)))
|
|
}
|
|
return activeFeature{ActiveFeatures: uint32(result)}
|
|
}
|
|
|
|
type loginBoost struct {
|
|
WeekReq uint8 `db:"week_req"`
|
|
WeekCount uint8
|
|
Active bool
|
|
Expiration time.Time `db:"expiration"`
|
|
Reset time.Time `db:"reset"`
|
|
}
|
|
|
|
func handleMsgMhfGetKeepLoginBoostStatus(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfGetKeepLoginBoostStatus)
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
|
|
loginBoosts, err := s.server.eventRepo.GetLoginBoosts(s.charID)
|
|
if err != nil || s.server.erupeConfig.GameplayOptions.DisableLoginBoost {
|
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 35))
|
|
return
|
|
}
|
|
if len(loginBoosts) == 0 {
|
|
temp := TimeWeekStart()
|
|
loginBoosts = []loginBoost{
|
|
{WeekReq: 1, Expiration: temp},
|
|
{WeekReq: 2, Expiration: temp},
|
|
{WeekReq: 3, Expiration: temp},
|
|
{WeekReq: 4, Expiration: temp},
|
|
{WeekReq: 5, Expiration: temp},
|
|
}
|
|
for _, boost := range loginBoosts {
|
|
if err := s.server.eventRepo.InsertLoginBoost(s.charID, boost.WeekReq, boost.Expiration, time.Time{}); err != nil {
|
|
s.logger.Error("Failed to insert login boost", zap.Error(err))
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, boost := range loginBoosts {
|
|
// Reset if next week
|
|
if !boost.Reset.IsZero() && boost.Reset.Before(TimeAdjusted()) {
|
|
boost.Expiration = TimeWeekStart()
|
|
boost.Reset = time.Time{}
|
|
if err := s.server.eventRepo.UpdateLoginBoost(s.charID, boost.WeekReq, boost.Expiration, boost.Reset); err != nil {
|
|
s.logger.Error("Failed to reset login boost", zap.Error(err))
|
|
}
|
|
}
|
|
|
|
boost.WeekCount = uint8((TimeAdjusted().Unix()-boost.Expiration.Unix())/secsPerWeek + 1)
|
|
|
|
if boost.WeekCount >= boost.WeekReq {
|
|
boost.Active = true
|
|
boost.WeekCount = boost.WeekReq
|
|
}
|
|
|
|
// Show reset timer on expired boosts
|
|
if boost.Reset.After(TimeAdjusted()) {
|
|
boost.Active = true
|
|
boost.WeekCount = 0
|
|
}
|
|
|
|
bf.WriteUint8(boost.WeekReq)
|
|
bf.WriteBool(boost.Active)
|
|
bf.WriteUint8(boost.WeekCount)
|
|
if !boost.Reset.IsZero() {
|
|
bf.WriteUint32(uint32(boost.Expiration.Unix()))
|
|
} else {
|
|
bf.WriteUint32(0)
|
|
}
|
|
}
|
|
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
|
|
func handleMsgMhfUseKeepLoginBoost(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfUseKeepLoginBoost)
|
|
var expiration time.Time
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint8(0)
|
|
switch pkt.BoostWeekUsed {
|
|
case 1, 3:
|
|
expiration = TimeAdjusted().Add(120 * time.Minute)
|
|
case 4:
|
|
expiration = TimeAdjusted().Add(180 * time.Minute)
|
|
case 2, 5:
|
|
expiration = TimeAdjusted().Add(240 * time.Minute)
|
|
}
|
|
bf.WriteUint32(uint32(expiration.Unix()))
|
|
if err := s.server.eventRepo.UpdateLoginBoost(s.charID, pkt.BoostWeekUsed, expiration, TimeWeekNext()); err != nil {
|
|
s.logger.Error("Failed to use login boost", zap.Error(err))
|
|
}
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
|
|
func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {}
|
|
|
|
func handleMsgMhfSetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfSetRestrictionEvent)
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|