mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-02-05 01:32:17 +01:00
Merge branch 'main' into fix/stage-concurrent-map
This commit is contained in:
@@ -79,7 +79,7 @@ func updateRights(s *Session) {
|
||||
rights := []mhfpacket.ClientRight{{1, 0, 0}}
|
||||
var netcafeBitSet bool
|
||||
for _, course := range s.courses {
|
||||
if (course.ID == 9 || course.ID == 26) && !netcafeBitSet {
|
||||
if (course.ID == 9 || course.ID == 25 || course.ID == 26) && !netcafeBitSet {
|
||||
netcafeBitSet = true
|
||||
rightsInt += 0x40000000 // set netcafe bit
|
||||
rights = append(rights, mhfpacket.ClientRight{ID: 30})
|
||||
@@ -144,7 +144,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
updateRights(s)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp
|
||||
bf.WriteUint32(uint32(TimeAdjusted().Unix())) // Unix timestamp
|
||||
|
||||
_, err := s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID)
|
||||
if err != nil {
|
||||
@@ -156,7 +156,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = s.server.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", Time_Current().Unix(), s.charID)
|
||||
_, err = s.server.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", TimeAdjusted().Unix(), s.charID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -216,7 +216,7 @@ func logoutPlayer(s *Session) {
|
||||
var timePlayed int
|
||||
var sessionTime int
|
||||
_ = s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed)
|
||||
sessionTime = int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)
|
||||
sessionTime = int(TimeAdjusted().Unix()) - int(s.sessionStart)
|
||||
timePlayed += sessionTime
|
||||
|
||||
var rpGained int
|
||||
@@ -276,7 +276,7 @@ func handleMsgSysTime(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
resp := &mhfpacket.MsgSysTime{
|
||||
GetRemoteTime: false,
|
||||
Timestamp: uint32(Time_Current_Adjusted().Unix()), // JP timezone
|
||||
Timestamp: uint32(TimeAdjusted().Unix()), // JP timezone
|
||||
}
|
||||
s.QueueSendMHF(resp)
|
||||
}
|
||||
@@ -528,8 +528,6 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
func handleMsgCaExchangeItem(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfServerCommand(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfAnnounce(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -1507,7 +1505,7 @@ func handleMsgMhfGetEtcPoints(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
var dailyTime time.Time
|
||||
_ = s.server.db.QueryRow("SELECT COALESCE(daily_time, $2) FROM characters WHERE id = $1", s.charID, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)).Scan(&dailyTime)
|
||||
if Time_Current_Adjusted().After(dailyTime) {
|
||||
if TimeAdjusted().After(dailyTime) {
|
||||
s.server.db.Exec("UPDATE characters SET bonus_quests = 0, daily_quests = 0 WHERE id=$1", s.charID)
|
||||
}
|
||||
|
||||
@@ -1706,14 +1704,6 @@ func handleMsgMhfPostNotice(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetRandFromTable(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetTinyBin(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetTinyBin)
|
||||
// requested after conquest quests
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{})
|
||||
}
|
||||
|
||||
func handleMsgMhfPostTinyBin(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetSenyuDailyCount(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetSeibattle(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -1723,45 +1713,6 @@ func handleMsgMhfGetSeibattle(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
func handleMsgMhfPostSeibattle(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetRyoudama(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetRyoudama)
|
||||
// likely guild related
|
||||
// REQ: 00 04 13 53 8F 18 00
|
||||
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 01 FE 4E
|
||||
// REQ: 00 06 13 53 8F 18 00
|
||||
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00
|
||||
// REQ: 00 05 13 53 8F 18 00
|
||||
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 0E 2A 15 9E CC 00 00 00 01 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 1E 55 B0 2F 00 00 00 01 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 2A 15 9E CC 00 00 00 02 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 03 D5 30 56 00 00 00 02 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 3F 57 76 9F 00 00 00 03 93 56 92 6E 96 B3 97 70 00 00 00 00 00 00 38 D9 0E C4 00 00 00 03 87 64 83 78 83 42 00 00 00 00 00 00 00 00 23 F3 B9 77 00 00 00 04 82 B3 82 CC 82 DC 82 E9 81 99 00 00 00 00 3F 1B 17 9C 00 00 00 04 82 B1 82 A4 82 BD 00 00 00 00 00 00 00 00 00 B9 F9 C0 00 00 00 05 82 CD 82 E9 82 A9 00 00 00 00 00 00 00 00 23 9F 9A EA 00 00 00 05 83 70 83 62 83 4C 83 83 83 49 00 00 00 00 38 D9 0E C4 00 00 00 06 87 64 83 78 83 42 00 00 00 00 00 00 00 00 1E 55 B0 2F 00 00 00 06 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 03 D5 30 56 00 00 00 07 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 02 D3 B8 77 00 00 00 07 6F 77 6C 32 35 32 35 00 00 00 00 00 00 00
|
||||
data, _ := hex.DecodeString("0A218EAD0000000000000000000000010000000000000000")
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfPostRyoudama(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
||||
// if the game gets bad responses for this it breaks the ability to save
|
||||
pkt := p.(*mhfpacket.MsgMhfGetTenrouirai)
|
||||
var data []byte
|
||||
var err error
|
||||
if pkt.Unk0 == 1 {
|
||||
data, err = hex.DecodeString("0A218EAD000000000000000000000001010000000000060010")
|
||||
} else if pkt.Unk2 == 4 {
|
||||
data, err = hex.DecodeString("0A218EAD0000000000000000000000210101005000000202010102020104001000000202010102020106003200000202010002020104000C003202020101020201030032000002020101020202059C4000000202010002020105C35000320202010102020201003C00000202010102020203003200000201010001020203002800320201010101020204000C00000201010101020206002800000201010001020101003C00320201020101020105C35000000301020101020106003200000301020001020104001000320301020101020105C350000003010201010202030028000003010200010201030032003203010201010202059C4000000301020101010206002800000301020001010201003C00320301020101010206003200000301020101010204000C000003010200010101010050003203010201010101059C40000003010201010101030032000003010200010101040010003203010001010101060032000003010001010102030028000003010001010101010050003203010000010102059C4000000301000001010206002800000301000001010010")
|
||||
} else {
|
||||
data = []byte{0x00, 0x00, 0x00, 0x00}
|
||||
s.logger.Info("GET_TENROUIRAI request for unknown type")
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostTenrouirai)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfGetDailyMissionMaster(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetDailyMissionPersonal(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
@@ -37,11 +37,8 @@ func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfCheckDailyCafepoint)
|
||||
|
||||
// get next midday
|
||||
var t = Time_static()
|
||||
year, month, day := t.Date()
|
||||
midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location())
|
||||
if t.After(midday) {
|
||||
midday := TimeMidnight().Add(12 * time.Hour)
|
||||
if TimeAdjusted().After(midday) {
|
||||
midday = midday.Add(24 * time.Hour)
|
||||
}
|
||||
|
||||
@@ -54,7 +51,7 @@ func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
var bondBonus, bonusQuests, dailyQuests uint32
|
||||
bf := byteframe.NewByteFrame()
|
||||
if t.After(dailyTime) {
|
||||
if midday.After(dailyTime) {
|
||||
addPointNetcafe(s, 5)
|
||||
bondBonus = 5 // Bond point bonus quests
|
||||
bonusQuests = 3 // HRP bonus quests?
|
||||
@@ -80,7 +77,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
|
||||
cafeReset = TimeWeekNext()
|
||||
s.server.db.Exec(`UPDATE characters SET cafe_reset=$1 WHERE id=$2`, cafeReset, s.charID)
|
||||
}
|
||||
if Time_Current_Adjusted().After(cafeReset) {
|
||||
if TimeAdjusted().After(cafeReset) {
|
||||
cafeReset = TimeWeekNext()
|
||||
s.server.db.Exec(`UPDATE characters SET cafe_time=0, cafe_reset=$1 WHERE id=$2`, cafeReset, s.charID)
|
||||
s.server.db.Exec(`DELETE FROM cafe_accepted WHERE character_id=$1`, s.charID)
|
||||
@@ -92,7 +89,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
|
||||
panic(err)
|
||||
}
|
||||
if s.FindCourse("NetCafe").ID != 0 || s.FindCourse("N").ID != 0 {
|
||||
cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime
|
||||
cafeTime = uint32(TimeAdjusted().Unix()) - uint32(s.sessionStart) + cafeTime
|
||||
}
|
||||
bf.WriteUint32(cafeTime) // Total cafe time
|
||||
bf.WriteUint16(0)
|
||||
@@ -142,7 +139,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(0)
|
||||
resp.WriteUint32(uint32(time.Now().Unix()))
|
||||
resp.WriteUint32(uint32(TimeAdjusted().Unix()))
|
||||
resp.WriteUint32(count)
|
||||
resp.WriteBytes(bf.Data())
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
@@ -165,7 +162,7 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {
|
||||
SELECT ch.cafe_time + $2
|
||||
FROM characters ch
|
||||
WHERE ch.id = $1
|
||||
) >= time_req`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart)
|
||||
) >= time_req`, s.charID, TimeAdjusted().Unix()-s.sessionStart)
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
} else {
|
||||
@@ -222,7 +219,7 @@ func addPointNetcafe(s *Session, p int) error {
|
||||
func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfStartBoostTime)
|
||||
bf := byteframe.NewByteFrame()
|
||||
boostLimit := Time_Current_Adjusted().Add(100 * time.Minute)
|
||||
boostLimit := TimeAdjusted().Add(120 * time.Minute)
|
||||
s.server.db.Exec("UPDATE characters SET boost_time=$1 WHERE id=$2", boostLimit, s.charID)
|
||||
bf.WriteUint32(uint32(boostLimit.Unix()))
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
@@ -255,7 +252,7 @@ func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
return
|
||||
}
|
||||
if boostLimit.Unix() > Time_Current_Adjusted().Unix() {
|
||||
if boostLimit.After(TimeAdjusted()) {
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
||||
} else {
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02})
|
||||
|
||||
@@ -1,9 +1,36 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
)
|
||||
|
||||
func handleMsgMhfGetRyoudama(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetRyoudama)
|
||||
// likely guild related
|
||||
// REQ: 00 04 13 53 8F 18 00
|
||||
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 01 FE 4E
|
||||
// REQ: 00 06 13 53 8F 18 00
|
||||
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00
|
||||
// REQ: 00 05 13 53 8F 18 00
|
||||
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 0E 2A 15 9E CC 00 00 00 01 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 1E 55 B0 2F 00 00 00 01 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 2A 15 9E CC 00 00 00 02 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 03 D5 30 56 00 00 00 02 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 3F 57 76 9F 00 00 00 03 93 56 92 6E 96 B3 97 70 00 00 00 00 00 00 38 D9 0E C4 00 00 00 03 87 64 83 78 83 42 00 00 00 00 00 00 00 00 23 F3 B9 77 00 00 00 04 82 B3 82 CC 82 DC 82 E9 81 99 00 00 00 00 3F 1B 17 9C 00 00 00 04 82 B1 82 A4 82 BD 00 00 00 00 00 00 00 00 00 B9 F9 C0 00 00 00 05 82 CD 82 E9 82 A9 00 00 00 00 00 00 00 00 23 9F 9A EA 00 00 00 05 83 70 83 62 83 4C 83 83 83 49 00 00 00 00 38 D9 0E C4 00 00 00 06 87 64 83 78 83 42 00 00 00 00 00 00 00 00 1E 55 B0 2F 00 00 00 06 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 03 D5 30 56 00 00 00 07 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 02 D3 B8 77 00 00 00 07 6F 77 6C 32 35 32 35 00 00 00 00 00 00 00
|
||||
data, _ := hex.DecodeString("0A218EAD0000000000000000000000010000000000000000")
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfPostRyoudama(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetTinyBin(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetTinyBin)
|
||||
// requested after conquest quests
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{})
|
||||
}
|
||||
|
||||
func handleMsgMhfPostTinyBin(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostTinyBin)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfCaravanMyScore(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfCaravanRanking(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
@@ -15,7 +15,7 @@ func cleanupDiva(s *Session) {
|
||||
|
||||
func generateDivaTimestamps(s *Session, start uint32, debug bool) []uint32 {
|
||||
timestamps := make([]uint32, 6)
|
||||
midnight := Time_Current_Midnight()
|
||||
midnight := TimeMidnight()
|
||||
if debug && start <= 3 {
|
||||
midnight := uint32(midnight.Unix())
|
||||
switch start {
|
||||
@@ -43,7 +43,7 @@ func generateDivaTimestamps(s *Session, start uint32, debug bool) []uint32 {
|
||||
}
|
||||
return timestamps
|
||||
}
|
||||
if start == 0 || Time_Current_Adjusted().Unix() > int64(start)+2977200 {
|
||||
if start == 0 || TimeAdjusted().Unix() > int64(start)+2977200 {
|
||||
cleanupDiva(s)
|
||||
// Generate a new diva defense, starting midnight tomorrow
|
||||
start = uint32(midnight.Add(24 * time.Hour).Unix())
|
||||
|
||||
@@ -61,7 +61,7 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetWeeklySchedule)
|
||||
|
||||
var features []activeFeature
|
||||
rows, _ := s.server.db.Queryx(`SELECT start_time, featured FROM feature_weapon WHERE start_time=$1 OR start_time=$2`, Time_Current_Midnight().Add(-24*time.Hour), Time_Current_Midnight())
|
||||
rows, _ := s.server.db.Queryx(`SELECT start_time, featured FROM feature_weapon WHERE start_time=$1 OR start_time=$2`, TimeMidnight(), TimeMidnight().Add(24*time.Hour))
|
||||
for rows.Next() {
|
||||
var feature activeFeature
|
||||
rows.StructScan(&feature)
|
||||
@@ -71,19 +71,19 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
|
||||
if len(features) < 2 {
|
||||
if len(features) == 0 {
|
||||
feature := generateFeatureWeapons(s.server.erupeConfig.FeaturedWeapons)
|
||||
feature.StartTime = Time_Current_Midnight().Add(-24 * time.Hour)
|
||||
feature.StartTime = TimeMidnight()
|
||||
features = append(features, feature)
|
||||
s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, feature.StartTime, feature.ActiveFeatures)
|
||||
}
|
||||
feature := generateFeatureWeapons(s.server.erupeConfig.FeaturedWeapons)
|
||||
feature.StartTime = Time_Current_Midnight()
|
||||
feature.StartTime = TimeMidnight().Add(24 * time.Hour)
|
||||
features = append(features, feature)
|
||||
s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, feature.StartTime, feature.ActiveFeatures)
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(2)
|
||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Add(-5 * time.Minute).Unix()))
|
||||
bf.WriteUint32(uint32(TimeAdjusted().Add(-5 * time.Minute).Unix()))
|
||||
for _, feature := range features {
|
||||
bf.WriteUint32(uint32(feature.StartTime.Unix()))
|
||||
bf.WriteUint32(feature.ActiveFeatures)
|
||||
@@ -119,129 +119,97 @@ func generateFeatureWeapons(count int) activeFeature {
|
||||
}
|
||||
|
||||
type loginBoost struct {
|
||||
WeekReq, WeekCount uint8
|
||||
Available bool
|
||||
Expiration uint32
|
||||
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)
|
||||
|
||||
var loginBoostStatus []loginBoost
|
||||
insert := false
|
||||
boostState, err := s.server.db.Query("SELECT week_req, week_count, available, end_time FROM login_boost_state WHERE char_id=$1 ORDER BY week_req ASC", s.charID)
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
var loginBoosts []loginBoost
|
||||
rows, err := s.server.db.Queryx("SELECT week_req, expiration, reset FROM login_boost WHERE char_id=$1 ORDER BY week_req", s.charID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 35))
|
||||
return
|
||||
}
|
||||
for boostState.Next() {
|
||||
var boost loginBoost
|
||||
err = boostState.Scan(&boost.WeekReq, &boost.WeekCount, &boost.Available, &boost.Expiration)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
for rows.Next() {
|
||||
var temp loginBoost
|
||||
rows.StructScan(&temp)
|
||||
loginBoosts = append(loginBoosts, temp)
|
||||
}
|
||||
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},
|
||||
}
|
||||
loginBoostStatus = append(loginBoostStatus, boost)
|
||||
}
|
||||
if len(loginBoostStatus) == 0 {
|
||||
// create default Entries (should only been week 1 with )
|
||||
insert = true
|
||||
loginBoostStatus = []loginBoost{
|
||||
{
|
||||
WeekReq: 1, // weeks needed
|
||||
WeekCount: 0, // weeks passed
|
||||
Available: true, // available
|
||||
Expiration: 0, //uint32(t.Add(120 * time.Minute).Unix()), // uncomment to enable permanently
|
||||
},
|
||||
{
|
||||
WeekReq: 2,
|
||||
WeekCount: 0,
|
||||
Available: true,
|
||||
Expiration: 0,
|
||||
},
|
||||
{
|
||||
WeekReq: 3,
|
||||
WeekCount: 0,
|
||||
Available: true,
|
||||
Expiration: 0,
|
||||
},
|
||||
{
|
||||
WeekReq: 4,
|
||||
WeekCount: 0,
|
||||
Available: true,
|
||||
Expiration: 0,
|
||||
},
|
||||
{
|
||||
WeekReq: 5,
|
||||
WeekCount: 0,
|
||||
Available: true,
|
||||
Expiration: 0,
|
||||
},
|
||||
for _, boost := range loginBoosts {
|
||||
s.server.db.Exec(`INSERT INTO login_boost VALUES ($1, $2, $3, $4)`, s.charID, boost.WeekReq, boost.Expiration, time.Time{})
|
||||
}
|
||||
}
|
||||
resp := byteframe.NewByteFrame()
|
||||
CurrentWeek := Time_Current_Week_uint8()
|
||||
for d := range loginBoostStatus {
|
||||
if CurrentWeek == 1 && loginBoostStatus[d].WeekCount <= 5 {
|
||||
loginBoostStatus[d].WeekCount = 0
|
||||
|
||||
for _, boost := range loginBoosts {
|
||||
// Reset if next week
|
||||
if !boost.Reset.IsZero() && boost.Reset.Before(TimeAdjusted()) {
|
||||
boost.Expiration = TimeWeekStart()
|
||||
boost.Reset = time.Time{}
|
||||
s.server.db.Exec(`UPDATE login_boost SET expiration=$1, reset=$2 WHERE char_id=$3 AND week_req=$4`, boost.Expiration, boost.Reset, s.charID, boost.WeekReq)
|
||||
}
|
||||
if loginBoostStatus[d].WeekReq == CurrentWeek || loginBoostStatus[d].WeekCount != 0 {
|
||||
loginBoostStatus[d].WeekCount = CurrentWeek
|
||||
|
||||
boost.WeekCount = uint8((TimeAdjusted().Unix()-boost.Expiration.Unix())/604800 + 1)
|
||||
|
||||
if boost.WeekCount >= boost.WeekReq {
|
||||
boost.Active = true
|
||||
boost.WeekCount = boost.WeekReq
|
||||
}
|
||||
if !loginBoostStatus[d].Available && loginBoostStatus[d].WeekCount >= loginBoostStatus[d].WeekReq && uint32(time.Now().In(time.FixedZone("UTC+1", 1*60*60)).Unix()) >= loginBoostStatus[d].Expiration {
|
||||
loginBoostStatus[d].Expiration = 1
|
||||
|
||||
// Show reset timer on expired boosts
|
||||
if boost.Reset.After(TimeAdjusted()) {
|
||||
boost.Active = true
|
||||
boost.WeekCount = 0
|
||||
}
|
||||
if !insert {
|
||||
_, err := s.server.db.Exec(`UPDATE login_boost_state SET week_count=$1, end_time=$2 WHERE char_id=$3 AND week_req=$4`, loginBoostStatus[d].WeekCount, loginBoostStatus[d].Expiration, s.charID, loginBoostStatus[d].WeekReq)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
for _, v := range loginBoostStatus {
|
||||
if insert {
|
||||
_, err := s.server.db.Exec(`INSERT INTO login_boost_state (char_id, week_req, week_count, available, end_time) VALUES ($1,$2,$3,$4,$5)`, s.charID, v.WeekReq, v.WeekCount, v.Available, v.Expiration)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
resp.WriteUint8(v.WeekReq)
|
||||
resp.WriteUint8(v.WeekCount)
|
||||
resp.WriteBool(v.Available)
|
||||
resp.WriteUint32(v.Expiration)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfUseKeepLoginBoost(s *Session, p mhfpacket.MHFPacket) {
|
||||
// Directly interacts with MhfGetKeepLoginBoostStatus
|
||||
// TODO: make these states persistent on a per character basis
|
||||
pkt := p.(*mhfpacket.MsgMhfUseKeepLoginBoost)
|
||||
var t = time.Now().In(time.FixedZone("UTC+1", 1*60*60))
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint8(0)
|
||||
|
||||
// response is end timestamp based on input
|
||||
var expiration time.Time
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(0)
|
||||
switch pkt.BoostWeekUsed {
|
||||
case 1:
|
||||
t = t.Add(120 * time.Minute)
|
||||
resp.WriteUint32(uint32(t.Unix()))
|
||||
case 2:
|
||||
t = t.Add(240 * time.Minute)
|
||||
resp.WriteUint32(uint32(t.Unix()))
|
||||
fallthrough
|
||||
case 3:
|
||||
t = t.Add(120 * time.Minute)
|
||||
resp.WriteUint32(uint32(t.Unix()))
|
||||
expiration = TimeAdjusted().Add(120 * time.Minute)
|
||||
case 4:
|
||||
t = t.Add(180 * time.Minute)
|
||||
resp.WriteUint32(uint32(t.Unix()))
|
||||
expiration = TimeAdjusted().Add(180 * time.Minute)
|
||||
case 2:
|
||||
fallthrough
|
||||
case 5:
|
||||
t = t.Add(240 * time.Minute)
|
||||
resp.WriteUint32(uint32(t.Unix()))
|
||||
expiration = TimeAdjusted().Add(240 * time.Minute)
|
||||
}
|
||||
_, err := s.server.db.Exec(`UPDATE login_boost_state SET available='false', end_time=$1 WHERE char_id=$2 AND week_req=$3`, uint32(t.Unix()), s.charID, pkt.BoostWeekUsed)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
bf.WriteUint32(uint32(expiration.Unix()))
|
||||
s.server.db.Exec(`UPDATE login_boost SET expiration=$1, reset=$2 WHERE char_id=$3 AND week_req=$4`, expiration, TimeWeekNext(), s.charID, pkt.BoostWeekUsed)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
@@ -12,22 +12,25 @@ import (
|
||||
|
||||
func handleMsgMhfSaveMezfesData(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfSaveMezfesData)
|
||||
s.server.db.Exec(`UPDATE characters SET mezfes=$1 WHERE id=$2`, pkt.RawDataPayload, s.charID)
|
||||
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())
|
||||
var data []byte
|
||||
s.server.db.QueryRow(`SELECT mezfes FROM characters WHERE id=$1`, s.charID).Scan(&data)
|
||||
bf := byteframe.NewByteFrame()
|
||||
if len(data) > 0 {
|
||||
bf.WriteBytes(data)
|
||||
} else {
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint8(2)
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint32(0)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -38,7 +41,7 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
// Unk
|
||||
// Start?
|
||||
// End?
|
||||
midnight := Time_Current_Midnight()
|
||||
midnight := TimeMidnight()
|
||||
switch state {
|
||||
case 1:
|
||||
bf.WriteUint32(uint32(midnight.Unix()))
|
||||
@@ -57,13 +60,13 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
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.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(Time_Current_Adjusted().Unix())) // TS Current Time
|
||||
bf.WriteUint32(uint32(TimeAdjusted().Unix())) // TS Current Time
|
||||
bf.WriteUint8(3)
|
||||
ps.Uint8(bf, "", false)
|
||||
bf.WriteUint16(0) // numEvents
|
||||
@@ -98,7 +101,7 @@ func cleanupFesta(s *Session) {
|
||||
|
||||
func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
|
||||
timestamps := make([]uint32, 5)
|
||||
midnight := Time_Current_Midnight()
|
||||
midnight := TimeMidnight()
|
||||
if debug && start <= 3 {
|
||||
midnight := uint32(midnight.Unix())
|
||||
switch start {
|
||||
@@ -123,7 +126,7 @@ func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
|
||||
}
|
||||
return timestamps
|
||||
}
|
||||
if start == 0 || Time_Current_Adjusted().Unix() > int64(start)+2977200 {
|
||||
if start == 0 || TimeAdjusted().Unix() > int64(start)+2977200 {
|
||||
cleanupFesta(s)
|
||||
// Generate a new festa, starting midnight tomorrow
|
||||
start = uint32(midnight.Add(24 * time.Hour).Unix())
|
||||
@@ -167,7 +170,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
||||
timestamps = generateFestaTimestamps(s, start, false)
|
||||
}
|
||||
|
||||
if timestamps[0] > uint32(Time_Current_Adjusted().Unix()) {
|
||||
if timestamps[0] > uint32(TimeAdjusted().Unix()) {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
@@ -180,7 +183,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
||||
for _, timestamp := range timestamps {
|
||||
bf.WriteUint32(timestamp)
|
||||
}
|
||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix()))
|
||||
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
|
||||
bf.WriteUint8(4)
|
||||
ps.Uint8(bf, "", false)
|
||||
bf.WriteUint32(0)
|
||||
|
||||
@@ -1732,57 +1732,59 @@ func handleMsgMhfCancelGuildMissionTarget(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
type GuildMeal struct {
|
||||
ID uint32 `db:"id"`
|
||||
MealID uint32 `db:"meal_id"`
|
||||
Level uint32 `db:"level"`
|
||||
Expires uint32 `db:"expires"`
|
||||
ID uint32 `db:"id"`
|
||||
MealID uint32 `db:"meal_id"`
|
||||
Level uint32 `db:"level"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
|
||||
func handleMsgMhfLoadGuildCooking(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfLoadGuildCooking)
|
||||
|
||||
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
|
||||
data, err := s.server.db.Queryx("SELECT id, meal_id, level, expires FROM guild_meals WHERE guild_id = $1", guild.ID)
|
||||
data, err := s.server.db.Queryx("SELECT id, meal_id, level, created_at FROM guild_meals WHERE guild_id = $1", guild.ID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get guild meals from db", zap.Error(err))
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2))
|
||||
return
|
||||
}
|
||||
temp := byteframe.NewByteFrame()
|
||||
count := 0
|
||||
var meals []GuildMeal
|
||||
var temp GuildMeal
|
||||
for data.Next() {
|
||||
mealData := &GuildMeal{}
|
||||
err = data.StructScan(&mealData)
|
||||
err = data.StructScan(&temp)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if mealData.Expires > uint32(Time_Current_Adjusted().Add(-60*time.Minute).Unix()) {
|
||||
count++
|
||||
temp.WriteUint32(mealData.ID)
|
||||
temp.WriteUint32(mealData.MealID)
|
||||
temp.WriteUint32(mealData.Level)
|
||||
temp.WriteUint32(mealData.Expires)
|
||||
if temp.CreatedAt.Add(60 * time.Minute).After(TimeAdjusted()) {
|
||||
meals = append(meals, temp)
|
||||
}
|
||||
}
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(uint16(count))
|
||||
bf.WriteBytes(temp.Data())
|
||||
bf.WriteUint16(uint16(len(meals)))
|
||||
for _, meal := range meals {
|
||||
bf.WriteUint32(meal.ID)
|
||||
bf.WriteUint32(meal.MealID)
|
||||
bf.WriteUint32(meal.Level)
|
||||
bf.WriteUint32(uint32(meal.CreatedAt.Unix()))
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfRegistGuildCooking(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfRegistGuildCooking)
|
||||
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
|
||||
currentTime := TimeAdjusted()
|
||||
if pkt.OverwriteID != 0 {
|
||||
_, err := s.server.db.Exec("DELETE FROM guild_meals WHERE id = $1", pkt.OverwriteID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to delete meal in db", zap.Error(err))
|
||||
}
|
||||
s.server.db.Exec("UPDATE guild_meals SET meal_id = $1, level = $2, created_at = $3 WHERE id = $4", pkt.MealID, pkt.Success, currentTime, pkt.OverwriteID)
|
||||
} else {
|
||||
s.server.db.QueryRow("INSERT INTO guild_meals (guild_id, meal_id, level, created_at) VALUES ($1, $2, $3, $4) RETURNING id", guild.ID, pkt.MealID, pkt.Success, currentTime).Scan(&pkt.OverwriteID)
|
||||
}
|
||||
_, err := s.server.db.Exec("INSERT INTO guild_meals (guild_id, meal_id, level, expires) VALUES ($1, $2, $3, $4)", guild.ID, pkt.MealID, pkt.Success, Time_Current_Adjusted().Add(30*time.Minute).Unix())
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to register meal in db", zap.Error(err))
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x01, 0x00})
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(1)
|
||||
bf.WriteUint32(pkt.OverwriteID)
|
||||
bf.WriteUint32(uint32(pkt.MealID))
|
||||
bf.WriteUint32(uint32(pkt.Success))
|
||||
bf.WriteUint32(uint32(currentTime.Unix()))
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfGetGuildWeeklyBonusMaster(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -1819,59 +1821,51 @@ func handleMsgMhfGuildHuntdata(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
type MessageBoardPost struct {
|
||||
Type uint32 `db:"post_type"`
|
||||
StampID uint32 `db:"stamp_id"`
|
||||
Title string `db:"title"`
|
||||
Body string `db:"body"`
|
||||
AuthorID uint32 `db:"author_id"`
|
||||
Timestamp uint64 `db:"created_at"`
|
||||
LikedBy string `db:"liked_by"`
|
||||
ID uint32 `db:"id"`
|
||||
StampID uint32 `db:"stamp_id"`
|
||||
Title string `db:"title"`
|
||||
Body string `db:"body"`
|
||||
AuthorID uint32 `db:"author_id"`
|
||||
Timestamp time.Time `db:"created_at"`
|
||||
LikedBy string `db:"liked_by"`
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateGuildMessageBoard)
|
||||
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
|
||||
|
||||
msgs, err := s.server.db.Queryx("SELECT post_type, stamp_id, title, body, author_id, (EXTRACT(epoch FROM created_at)::int) as created_at, liked_by FROM guild_posts WHERE guild_id = $1 AND post_type = $2 ORDER BY created_at DESC", guild.ID, int(pkt.BoardType))
|
||||
if pkt.BoardType == 1 {
|
||||
pkt.MaxPosts = 4
|
||||
}
|
||||
msgs, err := s.server.db.Queryx("SELECT id, stamp_id, title, body, author_id, created_at, liked_by FROM guild_posts WHERE guild_id = $1 AND post_type = $2 ORDER BY created_at DESC", guild.ID, int(pkt.BoardType))
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get guild messages from db", zap.Error(err))
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
|
||||
s.server.db.Exec("UPDATE characters SET guild_post_checked = now() WHERE id = $1", s.charID)
|
||||
bf := byteframe.NewByteFrame()
|
||||
noMsgs := true
|
||||
postCount := 0
|
||||
var postCount uint32
|
||||
for msgs.Next() {
|
||||
noMsgs = false
|
||||
postCount++
|
||||
postData := &MessageBoardPost{}
|
||||
msgs.StructScan(&postData)
|
||||
err = msgs.StructScan(&postData)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
bf.WriteUint32(postData.Type)
|
||||
postCount++
|
||||
bf.WriteUint32(postData.ID)
|
||||
bf.WriteUint32(postData.AuthorID)
|
||||
bf.WriteUint64(postData.Timestamp)
|
||||
likedBySlice := strings.Split(postData.LikedBy, ",")
|
||||
if likedBySlice[0] == "" {
|
||||
bf.WriteUint32(0)
|
||||
} else {
|
||||
bf.WriteUint32(uint32(len(likedBySlice)))
|
||||
}
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint32(uint32(postData.Timestamp.Unix()))
|
||||
bf.WriteUint32(uint32(stringsupport.CSVLength(postData.LikedBy)))
|
||||
bf.WriteBool(stringsupport.CSVContains(postData.LikedBy, int(s.charID)))
|
||||
bf.WriteUint32(postData.StampID)
|
||||
ps.Uint32(bf, postData.Title, true)
|
||||
ps.Uint32(bf, postData.Body, true)
|
||||
}
|
||||
if noMsgs {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
} else {
|
||||
data := byteframe.NewByteFrame()
|
||||
data.WriteUint32(uint32(postCount))
|
||||
data.WriteBytes(bf.Data())
|
||||
doAckBufSucceed(s, pkt.AckHandle, data.Data())
|
||||
}
|
||||
data := byteframe.NewByteFrame()
|
||||
data.WriteUint32(postCount)
|
||||
data.WriteBytes(bf.Data())
|
||||
doAckBufSucceed(s, pkt.AckHandle, data.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -1886,98 +1880,58 @@ func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
var titleConv, bodyConv string
|
||||
switch pkt.MessageOp {
|
||||
case 0: // Create message
|
||||
postType := bf.ReadUint32() // 0 = message, 1 = news
|
||||
stampId := bf.ReadUint32()
|
||||
stampID := bf.ReadUint32()
|
||||
titleLength := bf.ReadUint32()
|
||||
bodyLength := bf.ReadUint32()
|
||||
title := bf.ReadBytes(uint(titleLength))
|
||||
body := bf.ReadBytes(uint(bodyLength))
|
||||
titleConv = stringsupport.SJISToUTF8(title)
|
||||
bodyConv = stringsupport.SJISToUTF8(body)
|
||||
_, err := s.server.db.Exec("INSERT INTO guild_posts (guild_id, author_id, stamp_id, post_type, title, body) VALUES ($1, $2, $3, $4, $5, $6)", guild.ID, s.charID, int(stampId), int(postType), titleConv, bodyConv)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to add new guild message to db", zap.Error(err))
|
||||
}
|
||||
/* TODO: if there are too many messages, purge excess
|
||||
_, err = s.server.db.Exec("")
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to remove excess guild messages from db", zap.Error(err))
|
||||
}
|
||||
*/
|
||||
title := stringsupport.SJISToUTF8(bf.ReadBytes(uint(titleLength)))
|
||||
body := stringsupport.SJISToUTF8(bf.ReadBytes(uint(bodyLength)))
|
||||
s.server.db.Exec("INSERT INTO guild_posts (guild_id, author_id, stamp_id, post_type, title, body) VALUES ($1, $2, $3, $4, $5, $6)", guild.ID, s.charID, stampID, postType, title, body)
|
||||
// TODO: if there are too many messages, purge excess
|
||||
case 1: // Delete message
|
||||
postType := bf.ReadUint32()
|
||||
timestamp := bf.ReadUint64()
|
||||
_, err := s.server.db.Exec("DELETE FROM guild_posts WHERE post_type = $1 AND (EXTRACT(epoch FROM created_at)::int) = $2 AND guild_id = $3", int(postType), int(timestamp), guild.ID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to delete guild message from db", zap.Error(err))
|
||||
}
|
||||
postID := bf.ReadUint32()
|
||||
s.server.db.Exec("DELETE FROM guild_posts WHERE id = $1", postID)
|
||||
case 2: // Update message
|
||||
postType := bf.ReadUint32()
|
||||
timestamp := bf.ReadUint64()
|
||||
postID := bf.ReadUint32()
|
||||
bf.ReadBytes(8)
|
||||
titleLength := bf.ReadUint32()
|
||||
bodyLength := bf.ReadUint32()
|
||||
title := bf.ReadBytes(uint(titleLength))
|
||||
body := bf.ReadBytes(uint(bodyLength))
|
||||
titleConv = stringsupport.SJISToUTF8(title)
|
||||
bodyConv = stringsupport.SJISToUTF8(body)
|
||||
_, err := s.server.db.Exec("UPDATE guild_posts SET title = $1, body = $2 WHERE post_type = $3 AND (EXTRACT(epoch FROM created_at)::int) = $4 AND guild_id = $5", titleConv, bodyConv, int(postType), int(timestamp), guild.ID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to update guild message in db", zap.Error(err))
|
||||
}
|
||||
title := stringsupport.SJISToUTF8(bf.ReadBytes(uint(titleLength)))
|
||||
body := stringsupport.SJISToUTF8(bf.ReadBytes(uint(bodyLength)))
|
||||
s.server.db.Exec("UPDATE guild_posts SET title = $1, body = $2 WHERE id = $3", title, body, postID)
|
||||
case 3: // Update stamp
|
||||
postType := bf.ReadUint32()
|
||||
timestamp := bf.ReadUint64()
|
||||
stampId := bf.ReadUint32()
|
||||
_, err := s.server.db.Exec("UPDATE guild_posts SET stamp_id = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", int(stampId), int(postType), int(timestamp), guild.ID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to update guild message stamp in db", zap.Error(err))
|
||||
}
|
||||
postID := bf.ReadUint32()
|
||||
bf.ReadBytes(8)
|
||||
stampID := bf.ReadUint32()
|
||||
s.server.db.Exec("UPDATE guild_posts SET stamp_id = $1 WHERE id = $2", stampID, postID)
|
||||
case 4: // Like message
|
||||
postType := bf.ReadUint32()
|
||||
timestamp := bf.ReadUint64()
|
||||
postID := bf.ReadUint32()
|
||||
bf.ReadBytes(8)
|
||||
likeState := bf.ReadBool()
|
||||
var likedBy string
|
||||
err := s.server.db.QueryRow("SELECT liked_by FROM guild_posts WHERE post_type = $1 AND (EXTRACT(epoch FROM created_at)::int) = $2 AND guild_id = $3", int(postType), int(timestamp), guild.ID).Scan(&likedBy)
|
||||
err := s.server.db.QueryRow("SELECT liked_by FROM guild_posts WHERE id = $1", postID).Scan(&likedBy)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get guild message like data from db", zap.Error(err))
|
||||
} else {
|
||||
if likeState {
|
||||
likedBy = stringsupport.CSVAdd(likedBy, int(s.charID))
|
||||
_, err := s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", likedBy, int(postType), int(timestamp), guild.ID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to like guild message in db", zap.Error(err))
|
||||
}
|
||||
s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE id = $2", likedBy, postID)
|
||||
} else {
|
||||
likedBy = stringsupport.CSVRemove(likedBy, int(s.charID))
|
||||
_, err := s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", likedBy, int(postType), int(timestamp), guild.ID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to unlike guild message in db", zap.Error(err))
|
||||
}
|
||||
s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE id = $2", likedBy, postID)
|
||||
}
|
||||
}
|
||||
case 5: // Check for new messages
|
||||
var timeChecked int
|
||||
var timeChecked time.Time
|
||||
var newPosts int
|
||||
err := s.server.db.QueryRow("SELECT (EXTRACT(epoch FROM guild_post_checked)::int) FROM characters WHERE id = $1", s.charID).Scan(&timeChecked)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get last guild post check timestamp from db", zap.Error(err))
|
||||
} else {
|
||||
_, err = s.server.db.Exec("UPDATE characters SET guild_post_checked = $1 WHERE id = $2", time.Now(), s.charID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to update guild post check timestamp in db", zap.Error(err))
|
||||
} else {
|
||||
err = s.server.db.QueryRow("SELECT COUNT(*) FROM guild_posts WHERE guild_id = $1 AND (EXTRACT(epoch FROM created_at)::int) > $2 AND author_id != $3", guild.ID, timeChecked, s.charID).Scan(&newPosts)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to check for new guild posts in db", zap.Error(err))
|
||||
} else {
|
||||
if newPosts > 0 {
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
||||
return
|
||||
}
|
||||
}
|
||||
err := s.server.db.QueryRow("SELECT guild_post_checked FROM characters WHERE id = $1", s.charID).Scan(&timeChecked)
|
||||
if err == nil {
|
||||
s.server.db.QueryRow("SELECT COUNT(*) FROM guild_posts WHERE guild_id = $1 AND (EXTRACT(epoch FROM created_at)::int) > $2", guild.ID, timeChecked.Unix()).Scan(&newPosts)
|
||||
if newPosts > 0 {
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func handleMsgMhfLoadGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfRegistGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfRegistGuildAdventure)
|
||||
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
|
||||
_, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, depart, return) VALUES ($1, $2, $3, $4)", guild.ID, pkt.Destination, Time_Current_Adjusted().Unix(), Time_Current_Adjusted().Add(6*time.Hour).Unix())
|
||||
_, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, depart, return) VALUES ($1, $2, $3, $4)", guild.ID, pkt.Destination, TimeAdjusted().Unix(), TimeAdjusted().Add(6*time.Hour).Unix())
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to register guild adventure", zap.Error(err))
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func handleMsgMhfChargeGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfRegistGuildAdventureDiva(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfRegistGuildAdventureDiva)
|
||||
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
|
||||
_, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, charge, depart, return) VALUES ($1, $2, $3, $4, $5)", guild.ID, pkt.Destination, pkt.Charge, Time_Current_Adjusted().Unix(), Time_Current_Adjusted().Add(1*time.Hour).Unix())
|
||||
_, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, charge, depart, return) VALUES ($1, $2, $3, $4, $5)", guild.ID, pkt.Destination, pkt.Charge, TimeAdjusted().Unix(), TimeAdjusted().Add(1*time.Hour).Unix())
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to register guild adventure", zap.Error(err))
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
bf := byteframe.NewByteFrame()
|
||||
hunts := 0
|
||||
rows, _ := s.server.db.Queryx("SELECT id, host_id, destination, level, return, acquired, claimed, hunters, treasure, hunt_data FROM guild_hunts WHERE guild_id=$1 AND $2 < return+604800", guild.ID, Time_Current_Adjusted().Unix())
|
||||
rows, _ := s.server.db.Queryx("SELECT id, host_id, destination, level, return, acquired, claimed, hunters, treasure, hunt_data FROM guild_hunts WHERE guild_id=$1 AND $2 < return+604800", guild.ID, TimeAdjusted().Unix())
|
||||
for rows.Next() {
|
||||
hunt := &TreasureHunt{}
|
||||
err = rows.StructScan(&hunt)
|
||||
@@ -101,7 +101,7 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
_, err = s.server.db.Exec("INSERT INTO guild_hunts (guild_id, host_id, destination, level, return, hunt_data, cats_used) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
guild.ID, s.charID, destination, level, Time_Current_Adjusted().Unix(), huntData.Data(), catsUsed)
|
||||
guild.ID, s.charID, destination, level, TimeAdjusted().Unix(), huntData.Data(), catsUsed)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -124,7 +124,10 @@ func treasureHuntUnregister(s *Session) {
|
||||
}
|
||||
var huntID int
|
||||
var hunters string
|
||||
rows, _ := s.server.db.Queryx("SELECT id, hunters FROM guild_hunts WHERE guild_id=$1", guild.ID)
|
||||
rows, err := s.server.db.Queryx("SELECT id, hunters FROM guild_hunts WHERE guild_id=$1", guild.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for rows.Next() {
|
||||
rows.Scan(&huntID, &hunters)
|
||||
hunters = stringsupport.CSVRemove(hunters, int(s.charID))
|
||||
|
||||
@@ -43,9 +43,9 @@ func handleMsgMhfLoadLegendDispatch(s *Session, p mhfpacket.MHFPacket) {
|
||||
Unk uint32
|
||||
Timestamp uint32
|
||||
}{
|
||||
{0, uint32(Time_Current_Midnight().Add(-12 * time.Hour).Unix())},
|
||||
{0, uint32(Time_Current_Midnight().Add(12 * time.Hour).Unix())},
|
||||
{0, uint32(Time_Current_Midnight().Add(36 * time.Hour).Unix())},
|
||||
{0, uint32(TimeMidnight().Add(-12 * time.Hour).Unix())},
|
||||
{0, uint32(TimeMidnight().Add(12 * time.Hour).Unix())},
|
||||
{0, uint32(TimeMidnight().Add(36 * time.Hour).Unix())},
|
||||
}
|
||||
bf.WriteUint8(uint8(len(legendDispatch)))
|
||||
for _, dispatch := range legendDispatch {
|
||||
@@ -164,8 +164,8 @@ func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteUint32(pactID)
|
||||
bf.WriteUint32(cid)
|
||||
bf.WriteBool(false) // ?
|
||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -8).Unix()))
|
||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -1).Unix()))
|
||||
bf.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -8).Unix()))
|
||||
bf.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -1).Unix()))
|
||||
bf.WriteBytes(stringsupport.PaddedString(name, 18, true))
|
||||
} else {
|
||||
bf.WriteUint8(0)
|
||||
@@ -180,8 +180,8 @@ func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {
|
||||
rows.Scan(&name, &cid, &pactID)
|
||||
temp.WriteUint32(pactID)
|
||||
temp.WriteUint32(cid)
|
||||
temp.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -8).Unix()))
|
||||
temp.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -1).Unix()))
|
||||
temp.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -8).Unix()))
|
||||
temp.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -1).Unix()))
|
||||
temp.WriteBytes(stringsupport.PaddedString(name, 18, true))
|
||||
}
|
||||
bf.WriteUint8(loans)
|
||||
@@ -346,7 +346,7 @@ func getGuildAirouList(s *Session) []CatDefinition {
|
||||
FROM guild_hunts gh
|
||||
INNER JOIN characters c
|
||||
ON gh.host_id = c.id
|
||||
WHERE c.id=$1 AND gh.return+$2>$3`, s.charID, tempBanDuration, Time_Current_Adjusted().Unix())
|
||||
WHERE c.id=$1 AND gh.return+$2>$3`, s.charID, tempBanDuration, TimeAdjusted().Unix())
|
||||
if err != nil {
|
||||
s.logger.Warn("Failed to get recently used airous", zap.Error(err))
|
||||
}
|
||||
|
||||
@@ -43,7 +43,3 @@ func handleMsgMhfAcquireMonthlyReward(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
func handleMsgMhfAcceptReadReward(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetWeeklySeibatuRankingReward(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
@@ -2,14 +2,10 @@ package channelserver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"time"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"github.com/lib/pq"
|
||||
"github.com/sachaos/lottery"
|
||||
"go.uber.org/zap"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
type ShopItem struct {
|
||||
@@ -28,16 +24,36 @@ type ShopItem struct {
|
||||
}
|
||||
|
||||
type Gacha struct {
|
||||
ID uint32 `db:"id"`
|
||||
MinGR uint32 `db:"min_gr"`
|
||||
MinHR uint32 `db:"min_hr"`
|
||||
Name string `db:"name"`
|
||||
Link1 string `db:"link1"`
|
||||
Link2 string `db:"link2"`
|
||||
Link3 string `db:"link3"`
|
||||
Icon uint16 `db:"icon"`
|
||||
Type uint16 `db:"type"`
|
||||
Hide bool `db:"hide"`
|
||||
ID uint32 `db:"id"`
|
||||
MinGR uint32 `db:"min_gr"`
|
||||
MinHR uint32 `db:"min_hr"`
|
||||
Name string `db:"name"`
|
||||
URLBanner string `db:"url_banner"`
|
||||
URLFeature string `db:"url_feature"`
|
||||
URLThumbnail string `db:"url_thumbnail"`
|
||||
Wide bool `db:"wide"`
|
||||
Recommended bool `db:"recommended"`
|
||||
GachaType uint8 `db:"gacha_type"`
|
||||
Hidden bool `db:"hidden"`
|
||||
}
|
||||
|
||||
type GachaEntry struct {
|
||||
EntryType uint8 `db:"entry_type"`
|
||||
ID uint32 `db:"id"`
|
||||
ItemType uint8 `db:"item_type"`
|
||||
ItemNumber uint16 `db:"item_number"`
|
||||
ItemQuantity uint16 `db:"item_quantity"`
|
||||
Weight float64 `db:"weight"`
|
||||
Rarity uint8 `db:"rarity"`
|
||||
Rolls uint8 `db:"rolls"`
|
||||
FrontierPoints uint16 `db:"frontier_points"`
|
||||
DailyLimit uint8 `db:"daily_limit"`
|
||||
}
|
||||
|
||||
type GachaItem struct {
|
||||
ItemType uint8 `db:"item_type"`
|
||||
ItemID uint16 `db:"item_id"`
|
||||
Quantity uint16 `db:"quantity"`
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -55,7 +71,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
||||
switch pkt.ShopType {
|
||||
case 1: // Running gachas
|
||||
var count uint16
|
||||
shopEntries, err := s.server.db.Queryx("SELECT id, min_gr, min_hr, name, link1, link2, link3, icon, type, hide FROM gacha_shop")
|
||||
shopEntries, err := s.server.db.Queryx("SELECT id, min_gr, min_hr, name, url_banner, url_feature, url_thumbnail, wide, recommended, gacha_type, hidden FROM gacha_shop")
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
@@ -74,12 +90,18 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
||||
resp.WriteUint32(gacha.MinHR)
|
||||
resp.WriteUint32(0) // only 0 in known packet
|
||||
ps.Uint8(resp, gacha.Name, true)
|
||||
ps.Uint8(resp, gacha.Link1, false)
|
||||
ps.Uint8(resp, gacha.Link2, false)
|
||||
resp.WriteBool(gacha.Hide)
|
||||
ps.Uint8(resp, gacha.Link3, false)
|
||||
resp.WriteUint16(gacha.Icon)
|
||||
resp.WriteUint16(gacha.Type)
|
||||
ps.Uint8(resp, gacha.URLBanner, false)
|
||||
ps.Uint8(resp, gacha.URLFeature, false)
|
||||
resp.WriteBool(gacha.Wide)
|
||||
ps.Uint8(resp, gacha.URLThumbnail, false)
|
||||
resp.WriteUint8(0) // Unk
|
||||
if gacha.Recommended {
|
||||
resp.WriteUint8(2)
|
||||
} else {
|
||||
resp.WriteUint8(0)
|
||||
}
|
||||
resp.WriteUint8(gacha.GachaType)
|
||||
resp.WriteBool(gacha.Hidden)
|
||||
count++
|
||||
}
|
||||
resp.Seek(0, 0)
|
||||
@@ -87,47 +109,62 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
||||
resp.WriteUint16(count)
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
case 2: // Actual gacha
|
||||
shopEntries, err := s.server.db.Query("SELECT entryType, itemhash, currType, currNumber, currQuant, percentage, rarityIcon, rollsCount, itemCount, dailyLimit, itemType, itemId, quantity FROM gacha_shop_items WHERE shophash=$1", pkt.ShopID)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(pkt.ShopID)
|
||||
var gachaType int
|
||||
s.server.db.QueryRow(`SELECT gacha_type FROM gacha_shop WHERE id = $1`, pkt.ShopID).Scan(&gachaType)
|
||||
entries, err := s.server.db.Queryx(`SELECT entry_type, id, item_type, item_number, item_quantity, weight, rarity, rolls, daily_limit, frontier_points FROM gacha_entries WHERE gacha_id = $1 ORDER BY weight DESC`, pkt.ShopID)
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
var entryType, currType, rarityIcon, rollsCount, itemCount, dailyLimit uint8
|
||||
var currQuant, currNumber, percentage uint16
|
||||
var itemhash uint32
|
||||
var itemType, itemId, quantity pq.Int64Array
|
||||
var count uint16
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(pkt.ShopID)
|
||||
resp.WriteUint16(0) // total defs
|
||||
for shopEntries.Next() {
|
||||
err = shopEntries.Scan(&entryType, &itemhash, &currType, &currNumber, &currQuant, &percentage, &rarityIcon, &rollsCount, &itemCount, &dailyLimit, (*pq.Int64Array)(&itemType), (*pq.Int64Array)(&itemId), (*pq.Int64Array)(&quantity))
|
||||
var divisor float64
|
||||
s.server.db.QueryRow(`SELECT COALESCE(SUM(weight) / 100000.0, 0) AS chance FROM gacha_entries WHERE gacha_id = $1`, pkt.ShopID).Scan(&divisor)
|
||||
var entryCount uint16
|
||||
bf.WriteUint16(0)
|
||||
gachaEntry := GachaEntry{}
|
||||
gachaItem := GachaItem{}
|
||||
for entries.Next() {
|
||||
entryCount++
|
||||
entries.StructScan(&gachaEntry)
|
||||
bf.WriteUint8(gachaEntry.EntryType)
|
||||
bf.WriteUint32(gachaEntry.ID)
|
||||
bf.WriteUint8(gachaEntry.ItemType)
|
||||
bf.WriteUint16(0)
|
||||
bf.WriteUint16(gachaEntry.ItemNumber)
|
||||
bf.WriteUint16(gachaEntry.ItemQuantity)
|
||||
if gachaType >= 4 { // If box
|
||||
bf.WriteUint16(1)
|
||||
} else {
|
||||
bf.WriteUint16(uint16(gachaEntry.Weight / divisor))
|
||||
}
|
||||
bf.WriteUint8(gachaEntry.Rarity)
|
||||
bf.WriteUint8(gachaEntry.Rolls)
|
||||
|
||||
var itemCount uint8
|
||||
temp := byteframe.NewByteFrame()
|
||||
items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id=$1`, gachaEntry.ID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
bf.WriteUint8(0)
|
||||
} else {
|
||||
for items.Next() {
|
||||
itemCount++
|
||||
items.StructScan(&gachaItem)
|
||||
temp.WriteUint16(uint16(gachaItem.ItemType))
|
||||
temp.WriteUint16(gachaItem.ItemID)
|
||||
temp.WriteUint16(gachaItem.Quantity)
|
||||
}
|
||||
bf.WriteUint8(itemCount)
|
||||
}
|
||||
resp.WriteUint8(entryType)
|
||||
resp.WriteUint32(itemhash)
|
||||
resp.WriteUint8(currType)
|
||||
resp.WriteUint16(0) // unk, always 0 in existing packets
|
||||
resp.WriteUint16(currNumber) // it's either item ID or quantity for gacha coins
|
||||
resp.WriteUint16(currQuant) // only for item ID
|
||||
resp.WriteUint16(percentage)
|
||||
resp.WriteUint8(rarityIcon)
|
||||
resp.WriteUint8(rollsCount)
|
||||
resp.WriteUint8(itemCount)
|
||||
resp.WriteUint16(0) // unk, always 0 in existing packets
|
||||
resp.WriteUint8(dailyLimit)
|
||||
resp.WriteUint8(0) // unk, always 0 in existing packets
|
||||
for i := 0; i < int(itemCount); i++ {
|
||||
resp.WriteUint16(uint16(itemType[i])) // unk, always 0 in existing packets
|
||||
resp.WriteUint16(uint16(itemId[i])) // unk, always 0 in existing packets
|
||||
resp.WriteUint16(uint16(quantity[i])) // unk, always 0 in existing packets
|
||||
}
|
||||
count++
|
||||
|
||||
bf.WriteUint16(gachaEntry.FrontierPoints)
|
||||
bf.WriteUint8(gachaEntry.DailyLimit)
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteBytes(temp.Data())
|
||||
}
|
||||
resp.Seek(4, 0)
|
||||
resp.WriteUint16(count)
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
bf.Seek(4, 0)
|
||||
bf.WriteUint16(entryCount)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
case 4: // N Points, 0-6
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
case 5: // GCP->Item, 0-6
|
||||
@@ -206,124 +243,363 @@ func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfGetGachaPlayHistory(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetGachaPlayHistory)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteUint8(1)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfGetGachaPoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetGachaPoint)
|
||||
var fp, gp, gt uint32
|
||||
s.server.db.QueryRow("SELECT COALESCE(frontier_points, 0), COALESCE(gacha_prem, 0), COALESCE(gacha_trial,0) FROM characters WHERE id=$1", s.charID).Scan(&fp, &gp, >)
|
||||
s.server.db.QueryRow("SELECT COALESCE(frontier_points, 0), COALESCE(gacha_premium, 0), COALESCE(gacha_trial, 0) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)", s.charID).Scan(&fp, &gp, >)
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(gp) // Real Gacha Points?
|
||||
resp.WriteUint32(gt) // Trial Gacha Point?
|
||||
resp.WriteUint32(fp) // Frontier Points?
|
||||
resp.WriteUint32(gp)
|
||||
resp.WriteUint32(gt)
|
||||
resp.WriteUint32(fp)
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
type gachaItem struct {
|
||||
itemhash uint32
|
||||
percentage uint16
|
||||
rarityIcon byte
|
||||
itemCount byte
|
||||
itemType pq.Int64Array
|
||||
itemId pq.Int64Array
|
||||
quantity pq.Int64Array
|
||||
func handleMsgMhfUseGachaPoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfUseGachaPoint)
|
||||
if pkt.TrialCoins > 0 {
|
||||
s.server.db.Exec(`UPDATE users u SET gacha_trial=gacha_trial-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, pkt.TrialCoins, s.charID)
|
||||
}
|
||||
if pkt.PremiumCoins > 0 {
|
||||
s.server.db.Exec(`UPDATE users u SET gacha_premium=gacha_premium-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, pkt.PremiumCoins, s.charID)
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func (i gachaItem) Weight() int {
|
||||
return int(i.percentage)
|
||||
func spendGachaCoin(s *Session, quantity uint16) {
|
||||
var gt uint16
|
||||
s.server.db.QueryRow(`SELECT COALESCE(gacha_trial, 0) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(>)
|
||||
if quantity <= gt {
|
||||
s.server.db.Exec(`UPDATE users u SET gacha_trial=gacha_trial-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, quantity, s.charID)
|
||||
} else {
|
||||
s.server.db.Exec(`UPDATE users u SET gacha_premium=gacha_premium-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, quantity, s.charID)
|
||||
}
|
||||
}
|
||||
|
||||
func transactGacha(s *Session, gachaID uint32, rollID uint8) (error, int) {
|
||||
var itemType uint8
|
||||
var itemNumber uint16
|
||||
var rolls int
|
||||
err := s.server.db.QueryRowx(`SELECT item_type, item_number, rolls FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2`, gachaID, rollID).Scan(&itemType, &itemNumber, &rolls)
|
||||
if err != nil {
|
||||
return err, 0
|
||||
}
|
||||
switch itemType {
|
||||
/*
|
||||
valid types that need manual savedata manipulation:
|
||||
- Ryoudan Points
|
||||
- Bond Points
|
||||
- Image Change Points
|
||||
valid types that work (no additional code needed):
|
||||
- Tore Points
|
||||
- Festa Points
|
||||
*/
|
||||
case 17:
|
||||
_ = addPointNetcafe(s, int(itemNumber)*-1)
|
||||
case 19:
|
||||
fallthrough
|
||||
case 20:
|
||||
spendGachaCoin(s, itemNumber)
|
||||
case 21:
|
||||
s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", itemNumber, s.charID)
|
||||
}
|
||||
return nil, rolls
|
||||
}
|
||||
|
||||
func getGuaranteedItems(s *Session, gachaID uint32, rollID uint8) []GachaItem {
|
||||
var rewards []GachaItem
|
||||
var reward GachaItem
|
||||
items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = (SELECT id FROM gacha_entries WHERE entry_type = $1 AND gacha_id = $2)`, rollID, gachaID)
|
||||
if err == nil {
|
||||
for items.Next() {
|
||||
items.StructScan(&reward)
|
||||
rewards = append(rewards, reward)
|
||||
}
|
||||
}
|
||||
return rewards
|
||||
}
|
||||
|
||||
func addGachaItem(s *Session, items []GachaItem) {
|
||||
var data []byte
|
||||
s.server.db.QueryRow(`SELECT gacha_items FROM characters WHERE id = $1`, s.charID).Scan(&data)
|
||||
if len(data) > 0 {
|
||||
numItems := int(data[0])
|
||||
data = data[1:]
|
||||
oldItem := byteframe.NewByteFrameFromBytes(data)
|
||||
for i := 0; i < numItems; i++ {
|
||||
items = append(items, GachaItem{
|
||||
ItemType: oldItem.ReadUint8(),
|
||||
ItemID: oldItem.ReadUint16(),
|
||||
Quantity: oldItem.ReadUint16(),
|
||||
})
|
||||
}
|
||||
}
|
||||
newItem := byteframe.NewByteFrame()
|
||||
newItem.WriteUint8(uint8(len(items)))
|
||||
for i := range items {
|
||||
newItem.WriteUint8(items[i].ItemType)
|
||||
newItem.WriteUint16(items[i].ItemID)
|
||||
newItem.WriteUint16(items[i].Quantity)
|
||||
}
|
||||
s.server.db.Exec(`UPDATE characters SET gacha_items = $1 WHERE id = $2`, newItem.Data(), s.charID)
|
||||
}
|
||||
|
||||
func getRandomEntries(entries []GachaEntry, rolls int, isBox bool) ([]GachaEntry, error) {
|
||||
var chosen []GachaEntry
|
||||
var totalWeight float64
|
||||
for i := range entries {
|
||||
totalWeight += entries[i].Weight
|
||||
}
|
||||
for {
|
||||
if !isBox {
|
||||
result := rand.Float64() * totalWeight
|
||||
for _, entry := range entries {
|
||||
result -= entry.Weight
|
||||
if result < 0 {
|
||||
chosen = append(chosen, entry)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result := rand.Intn(len(entries))
|
||||
chosen = append(chosen, entries[result])
|
||||
entries[result] = entries[len(entries)-1]
|
||||
entries = entries[:len(entries)-1]
|
||||
}
|
||||
if rolls == len(chosen) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return chosen, nil
|
||||
}
|
||||
|
||||
func handleMsgMhfReceiveGachaItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfReceiveGachaItem)
|
||||
var data []byte
|
||||
err := s.server.db.QueryRow("SELECT COALESCE(gacha_items, $2) FROM characters WHERE id = $1", s.charID, []byte{0x00}).Scan(&data)
|
||||
if err != nil {
|
||||
data = []byte{0x00}
|
||||
}
|
||||
|
||||
// I think there are still some edge cases where rewards can be nulled via overflow
|
||||
if data[0] > 36 || len(data) > 181 {
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint8(36)
|
||||
resp.WriteBytes(data[1:181])
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
} else {
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
if !pkt.Freeze {
|
||||
if data[0] > 36 || len(data) > 181 {
|
||||
update := byteframe.NewByteFrame()
|
||||
update.WriteUint8(uint8(len(data[181:]) / 5))
|
||||
update.WriteBytes(data[181:])
|
||||
s.server.db.Exec("UPDATE characters SET gacha_items = $1 WHERE id = $2", update.Data(), s.charID)
|
||||
} else {
|
||||
s.server.db.Exec("UPDATE characters SET gacha_items = null WHERE id = $1", s.charID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfPlayNormalGacha(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPlayNormalGacha)
|
||||
// needs to query db for input gacha and return a result or number of results
|
||||
// uint8 number of results
|
||||
// uint8 item type
|
||||
// uint16 item id
|
||||
// uint16 quantity
|
||||
|
||||
var currType, rarityIcon, rollsCount, itemCount byte
|
||||
var currQuant, currNumber, percentage uint16
|
||||
var itemhash uint32
|
||||
var itemType, itemId, quantity pq.Int64Array
|
||||
var items []lottery.Weighter
|
||||
// get info for updating data and calculating costs
|
||||
err := s.server.db.QueryRow("SELECT currType, currNumber, currQuant, rollsCount FROM gacha_shop_items WHERE shophash=$1 AND entryType=$2", pkt.GachaHash, pkt.RollType).Scan(&currType, &currNumber, &currQuant, &rollsCount)
|
||||
bf := byteframe.NewByteFrame()
|
||||
var gachaEntries []GachaEntry
|
||||
var entry GachaEntry
|
||||
var rewards []GachaItem
|
||||
var reward GachaItem
|
||||
err, rolls := transactGacha(s, pkt.GachaID, pkt.RollType)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
|
||||
return
|
||||
}
|
||||
// get existing items in storage if any
|
||||
var data []byte
|
||||
_ = s.server.db.QueryRow("SELECT gacha_items FROM characters WHERE id = $1", s.charID).Scan(&data)
|
||||
if len(data) == 0 {
|
||||
data = []byte{0x00}
|
||||
}
|
||||
// get gacha items and iterate through them for gacha roll
|
||||
shopEntries, err := s.server.db.Query("SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity FROM gacha_shop_items WHERE shophash=$1 AND entryType=100", pkt.GachaHash)
|
||||
temp := byteframe.NewByteFrame()
|
||||
entries, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
|
||||
return
|
||||
}
|
||||
for shopEntries.Next() {
|
||||
err = shopEntries.Scan(&itemhash, &percentage, &rarityIcon, &itemCount, (*pq.Int64Array)(&itemType), (*pq.Int64Array)(&itemId), (*pq.Int64Array)(&quantity))
|
||||
for entries.Next() {
|
||||
entries.StructScan(&entry)
|
||||
gachaEntries = append(gachaEntries, entry)
|
||||
}
|
||||
rewardEntries, err := getRandomEntries(gachaEntries, rolls, false)
|
||||
for i := range rewardEntries {
|
||||
items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
continue
|
||||
}
|
||||
items = append(items, &gachaItem{itemhash: itemhash, percentage: percentage, rarityIcon: rarityIcon, itemCount: itemCount, itemType: itemType, itemId: itemId, quantity: quantity})
|
||||
}
|
||||
// execute rolls, build response and update database
|
||||
results := byte(0)
|
||||
resp := byteframe.NewByteFrame()
|
||||
dbUpdate := byteframe.NewByteFrame()
|
||||
resp.WriteUint8(0) // results go here later
|
||||
l := lottery.NewDefaultLottery()
|
||||
for x := 0; x < int(rollsCount); x++ {
|
||||
ind := l.Draw(items)
|
||||
results += items[ind].(*gachaItem).itemCount
|
||||
for y := 0; y < int(items[ind].(*gachaItem).itemCount); y++ {
|
||||
// items in storage don't get rarity
|
||||
dbUpdate.WriteUint8(uint8(items[ind].(*gachaItem).itemType[y]))
|
||||
dbUpdate.WriteUint16(uint16(items[ind].(*gachaItem).itemId[y]))
|
||||
dbUpdate.WriteUint16(uint16(items[ind].(*gachaItem).quantity[y]))
|
||||
data = append(data, dbUpdate.Data()...)
|
||||
dbUpdate.Seek(0, 0)
|
||||
// response needs all item info and the rarity
|
||||
resp.WriteBytes(dbUpdate.Data())
|
||||
resp.WriteUint8(items[ind].(*gachaItem).rarityIcon)
|
||||
for items.Next() {
|
||||
items.StructScan(&reward)
|
||||
rewards = append(rewards, reward)
|
||||
temp.WriteUint8(reward.ItemType)
|
||||
temp.WriteUint16(reward.ItemID)
|
||||
temp.WriteUint16(reward.Quantity)
|
||||
temp.WriteUint8(entry.Rarity)
|
||||
}
|
||||
}
|
||||
resp.Seek(0, 0)
|
||||
resp.WriteUint8(results)
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
|
||||
// add claimables to DB
|
||||
data[0] = data[0] + results
|
||||
_, err = s.server.db.Exec("UPDATE characters SET gacha_items = $1 WHERE id = $2", data, s.charID)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update minidata in db", zap.Error(err))
|
||||
}
|
||||
// deduct gacha coins if relevant, items are handled fine by the standard savedata packet immediately afterwards
|
||||
if currType == 19 {
|
||||
_, err = s.server.db.Exec("UPDATE characters SET gacha_trial = CASE WHEN (gacha_trial > $1) then gacha_trial - $1 else gacha_trial end, gacha_prem = CASE WHEN NOT (gacha_trial > $1) then gacha_prem - $1 else gacha_prem end WHERE id=$2", currNumber, s.charID)
|
||||
}
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err))
|
||||
}
|
||||
bf.WriteUint8(uint8(len(rewards)))
|
||||
bf.WriteBytes(temp.Data())
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
addGachaItem(s, rewards)
|
||||
}
|
||||
|
||||
func handleMsgMhfUseGachaPoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
// should write to database when that's set up
|
||||
pkt := p.(*mhfpacket.MsgMhfUseGachaPoint)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
func handleMsgMhfPlayStepupGacha(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPlayStepupGacha)
|
||||
bf := byteframe.NewByteFrame()
|
||||
var gachaEntries []GachaEntry
|
||||
var entry GachaEntry
|
||||
var rewards []GachaItem
|
||||
var reward GachaItem
|
||||
err, rolls := transactGacha(s, pkt.GachaID, pkt.RollType)
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
|
||||
return
|
||||
}
|
||||
s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points+(SELECT frontier_points FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2) WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$3)", pkt.GachaID, pkt.RollType, s.charID)
|
||||
s.server.db.Exec(`DELETE FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID)
|
||||
s.server.db.Exec(`INSERT INTO gacha_stepup (gacha_id, step, character_id) VALUES ($1, $2, $3)`, pkt.GachaID, pkt.RollType+1, s.charID)
|
||||
temp := byteframe.NewByteFrame()
|
||||
guaranteedItems := getGuaranteedItems(s, pkt.GachaID, pkt.RollType)
|
||||
for _, item := range guaranteedItems {
|
||||
temp.WriteUint8(item.ItemType)
|
||||
temp.WriteUint16(item.ItemID)
|
||||
temp.WriteUint16(item.Quantity)
|
||||
temp.WriteUint8(0)
|
||||
}
|
||||
entries, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID)
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
|
||||
return
|
||||
}
|
||||
for entries.Next() {
|
||||
entries.StructScan(&entry)
|
||||
gachaEntries = append(gachaEntries, entry)
|
||||
}
|
||||
rewardEntries, err := getRandomEntries(gachaEntries, rolls, false)
|
||||
for i := range rewardEntries {
|
||||
items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for items.Next() {
|
||||
items.StructScan(&reward)
|
||||
rewards = append(rewards, reward)
|
||||
temp.WriteUint8(reward.ItemType)
|
||||
temp.WriteUint16(reward.ItemID)
|
||||
temp.WriteUint16(reward.Quantity)
|
||||
temp.WriteUint8(entry.Rarity)
|
||||
}
|
||||
}
|
||||
bf.WriteUint8(uint8(len(rewards) + len(guaranteedItems)))
|
||||
bf.WriteUint8(uint8(len(rewards)))
|
||||
bf.WriteBytes(temp.Data())
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
addGachaItem(s, rewards)
|
||||
addGachaItem(s, guaranteedItems)
|
||||
}
|
||||
|
||||
func handleMsgMhfGetStepupStatus(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetStepupStatus)
|
||||
// TODO: Reset daily (noon)
|
||||
var step uint8
|
||||
s.server.db.QueryRow(`SELECT step FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID).Scan(&step)
|
||||
var stepCheck int
|
||||
s.server.db.QueryRow(`SELECT COUNT(1) FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2`, pkt.GachaID, step).Scan(&stepCheck)
|
||||
if stepCheck == 0 {
|
||||
s.server.db.Exec(`DELETE FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID)
|
||||
step = 0
|
||||
}
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(step)
|
||||
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfGetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetBoxGachaInfo)
|
||||
entries, err := s.server.db.Queryx(`SELECT entry_id FROM gacha_box WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID)
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
|
||||
return
|
||||
}
|
||||
var entryIDs []uint32
|
||||
for entries.Next() {
|
||||
var entryID uint32
|
||||
entries.Scan(&entryID)
|
||||
entryIDs = append(entryIDs, entryID)
|
||||
}
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(uint8(len(entryIDs)))
|
||||
for i := range entryIDs {
|
||||
bf.WriteUint32(entryIDs[i])
|
||||
bf.WriteBool(true)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPlayBoxGacha)
|
||||
bf := byteframe.NewByteFrame()
|
||||
var gachaEntries []GachaEntry
|
||||
var entry GachaEntry
|
||||
var rewards []GachaItem
|
||||
var reward GachaItem
|
||||
err, rolls := transactGacha(s, pkt.GachaID, pkt.RollType)
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
|
||||
return
|
||||
}
|
||||
temp := byteframe.NewByteFrame()
|
||||
entries, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID)
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
|
||||
return
|
||||
}
|
||||
for entries.Next() {
|
||||
entries.StructScan(&entry)
|
||||
gachaEntries = append(gachaEntries, entry)
|
||||
}
|
||||
rewardEntries, err := getRandomEntries(gachaEntries, rolls, true)
|
||||
for i := range rewardEntries {
|
||||
items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
s.server.db.Exec(`INSERT INTO gacha_box (gacha_id, entry_id, character_id) VALUES ($1, $2, $3)`, pkt.GachaID, rewardEntries[i].ID, s.charID)
|
||||
for items.Next() {
|
||||
items.StructScan(&reward)
|
||||
rewards = append(rewards, reward)
|
||||
temp.WriteUint8(reward.ItemType)
|
||||
temp.WriteUint16(reward.ItemID)
|
||||
temp.WriteUint16(reward.Quantity)
|
||||
temp.WriteUint8(0)
|
||||
}
|
||||
}
|
||||
bf.WriteUint8(uint8(len(rewards)))
|
||||
bf.WriteBytes(temp.Data())
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
addGachaItem(s, rewards)
|
||||
}
|
||||
|
||||
func handleMsgMhfResetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfResetBoxGachaInfo)
|
||||
s.server.db.Exec("DELETE FROM gacha_box WHERE gacha_id = $1 AND character_id = $2", pkt.GachaID, s.charID)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfExchangeFpoint2Item(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfExchangeFpoint2Item)
|
||||
var balance uint32
|
||||
var itemValue, quantity int
|
||||
_ = s.server.db.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue)
|
||||
s.server.db.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue)
|
||||
cost := (int(pkt.Quantity) * quantity) * itemValue
|
||||
s.server.db.QueryRow("UPDATE characters SET frontier_points=frontier_points::int - $1 WHERE id=$2 RETURNING frontier_points", cost, s.charID).Scan(&balance)
|
||||
s.server.db.QueryRow("UPDATE users u SET frontier_points=frontier_points::int - $1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2) RETURNING frontier_points", cost, s.charID).Scan(&balance)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(balance)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
||||
@@ -335,7 +611,7 @@ func handleMsgMhfExchangeItem2Fpoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
var itemValue, quantity int
|
||||
s.server.db.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue)
|
||||
cost := (int(pkt.Quantity) / quantity) * itemValue
|
||||
s.server.db.QueryRow("UPDATE characters SET frontier_points=COALESCE(frontier_points::int + $1, $1) WHERE id=$2 RETURNING frontier_points", cost, s.charID).Scan(&balance)
|
||||
s.server.db.QueryRow("UPDATE users u SET frontier_points=COALESCE(frontier_points::int + $1, $1) WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2) RETURNING frontier_points", cost, s.charID).Scan(&balance)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(balance)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
||||
@@ -394,291 +670,6 @@ func handleMsgMhfGetFpointExchangeList(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfPlayStepupGacha(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPlayStepupGacha)
|
||||
results := byte(0)
|
||||
stepResults := byte(0)
|
||||
resp := byteframe.NewByteFrame()
|
||||
rollFrame := byteframe.NewByteFrame()
|
||||
stepFrame := byteframe.NewByteFrame()
|
||||
stepData := []byte{}
|
||||
var currType, rarityIcon, rollsCount, itemCount byte
|
||||
var currQuant, currNumber, percentage uint16
|
||||
var itemhash uint32
|
||||
var itemType, itemId, quantity pq.Int64Array
|
||||
var items []lottery.Weighter
|
||||
// get info for updating data and calculating costs
|
||||
err := s.server.db.QueryRow("SELECT currType, currNumber, currQuant, rollsCount, itemCount, itemType, itemId, quantity FROM gacha_shop_items WHERE shophash=$1 AND entryType=$2", pkt.GachaHash, pkt.RollType).Scan(&currType, &currNumber, &currQuant, &rollsCount, &itemCount, (*pq.Int64Array)(&itemType), (*pq.Int64Array)(&itemId), (*pq.Int64Array)(&quantity))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// get existing items in storage if any
|
||||
var data []byte
|
||||
_ = s.server.db.QueryRow("SELECT gacha_items FROM characters WHERE id = $1", s.charID).Scan(&data)
|
||||
if len(data) == 0 {
|
||||
data = []byte{0x00}
|
||||
}
|
||||
// roll definition includes items with step up gachas that are appended last
|
||||
for x := 0; x < int(itemCount); x++ {
|
||||
stepFrame.WriteUint8(uint8(itemType[x]))
|
||||
stepFrame.WriteUint16(uint16(itemId[x]))
|
||||
stepFrame.WriteUint16(uint16(quantity[x]))
|
||||
stepData = append(stepData, stepFrame.Data()...)
|
||||
stepFrame.WriteUint8(0) // rarity still defined
|
||||
stepResults++
|
||||
}
|
||||
// get gacha items and iterate through them for gacha roll
|
||||
shopEntries, err := s.server.db.Query("SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity FROM gacha_shop_items WHERE shophash=$1 AND entryType=100", pkt.GachaHash)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for shopEntries.Next() {
|
||||
err = shopEntries.Scan(&itemhash, &percentage, &rarityIcon, &itemCount, (*pq.Int64Array)(&itemType), (*pq.Int64Array)(&itemId), (*pq.Int64Array)(&quantity))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
items = append(items, &gachaItem{itemhash: itemhash, percentage: percentage, rarityIcon: rarityIcon, itemCount: itemCount, itemType: itemType, itemId: itemId, quantity: quantity})
|
||||
}
|
||||
// execute rolls, build response and update database
|
||||
resp.WriteUint16(0) // results count goes here later
|
||||
l := lottery.NewDefaultLottery()
|
||||
for x := 0; x < int(rollsCount); x++ {
|
||||
ind := l.Draw(items)
|
||||
results += items[ind].(*gachaItem).itemCount
|
||||
for y := 0; y < int(items[ind].(*gachaItem).itemCount); y++ {
|
||||
// items in storage don't get rarity
|
||||
rollFrame.WriteUint8(uint8(items[ind].(*gachaItem).itemType[y]))
|
||||
rollFrame.WriteUint16(uint16(items[ind].(*gachaItem).itemId[y]))
|
||||
rollFrame.WriteUint16(uint16(items[ind].(*gachaItem).quantity[y]))
|
||||
data = append(data, rollFrame.Data()...)
|
||||
rollFrame.Seek(0, 0)
|
||||
// response needs all item info and the rarity
|
||||
resp.WriteBytes(rollFrame.Data())
|
||||
resp.WriteUint8(items[ind].(*gachaItem).rarityIcon)
|
||||
}
|
||||
}
|
||||
resp.WriteBytes(stepFrame.Data())
|
||||
resp.Seek(0, 0)
|
||||
resp.WriteUint8(results + stepResults)
|
||||
resp.WriteUint8(results)
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
|
||||
// add claimables to DB
|
||||
data = append(data, stepData...)
|
||||
data[0] = data[0] + results + stepResults
|
||||
_, err = s.server.db.Exec("UPDATE characters SET gacha_items = $1 WHERE id = $2", data, s.charID)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err))
|
||||
}
|
||||
// deduct gacha coins if relevant, items are handled fine by the standard savedata packet immediately afterwards
|
||||
// reduce real if trial don't cover cost
|
||||
if currType == 19 {
|
||||
_, err = s.server.db.Exec(`UPDATE characters
|
||||
SET gacha_trial = CASE WHEN (gacha_trial > $1) then gacha_trial - $1 else gacha_trial end,
|
||||
gacha_prem = CASE WHEN NOT (gacha_trial > $1) then gacha_prem - $1 else gacha_prem end
|
||||
WHERE id=$2`, currNumber, s.charID)
|
||||
}
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err))
|
||||
}
|
||||
// update step progression
|
||||
_, err = s.server.db.Exec("UPDATE stepup_state SET step_progression = $1 WHERE char_id = $2", pkt.RollType+1, s.charID)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update step_progression in db", zap.Error(err))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func handleMsgMhfReceiveGachaItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfReceiveGachaItem)
|
||||
// persistent for claimable items on cat
|
||||
var data []byte
|
||||
err := s.server.db.QueryRow("SELECT COALESCE(gacha_items, $2) FROM characters WHERE id = $1", s.charID, []byte{0x00}).Scan(&data)
|
||||
if err != nil {
|
||||
panic("Failed to get gacha_items")
|
||||
}
|
||||
// limit of 36 items are returned
|
||||
if data[0] > 36 {
|
||||
outData := make([]byte, 181)
|
||||
copy(outData, data[0:181])
|
||||
outData[0] = byte(36)
|
||||
saveData := append(data[:1], data[181:]...)
|
||||
saveData[0] = saveData[0] - 36
|
||||
doAckBufSucceed(s, pkt.AckHandle, outData)
|
||||
if pkt.Unk0 != 0x2401 {
|
||||
_, err := s.server.db.Exec("UPDATE characters SET gacha_items = $2 WHERE id = $1", s.charID, saveData)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
if pkt.Unk0 != 0x2401 {
|
||||
_, err := s.server.db.Exec("UPDATE characters SET gacha_items = null WHERE id = $1", s.charID)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfGetStepupStatus(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetStepupStatus)
|
||||
// get the reset time from db
|
||||
var step_progression int
|
||||
var step_time time.Time
|
||||
err := s.server.db.QueryRow(`SELECT COALESCE(step_progression, 0), COALESCE(step_time, $1) FROM stepup_state WHERE char_id = $2 AND shophash = $3`, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), s.charID, pkt.GachaHash).Scan(&step_progression, &step_time)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to Select coalesce in db", zap.Error(err))
|
||||
}
|
||||
|
||||
// calculate next midday
|
||||
var t = time.Now().In(time.FixedZone("UTC+9", 9*60*60))
|
||||
year, month, day := t.Date()
|
||||
midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location())
|
||||
if t.After(midday) {
|
||||
midday = midday.Add(24 * time.Hour)
|
||||
}
|
||||
// after midday or not set
|
||||
if t.After(step_time) {
|
||||
step_progression = 0
|
||||
}
|
||||
_, err = s.server.db.Exec(`INSERT INTO stepup_state (shophash, step_progression, step_time, char_id)
|
||||
VALUES ($1,$2,$3,$4) ON CONFLICT (shophash, char_id)
|
||||
DO UPDATE SET step_progression=$2, step_time=$3
|
||||
WHERE EXCLUDED.char_id=$4 AND EXCLUDED.shophash=$1`, pkt.GachaHash, step_progression, midday, s.charID)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update platedata savedata in db", zap.Error(err))
|
||||
}
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint8(uint8(step_progression))
|
||||
resp.WriteUint32(uint32(time.Now().In(time.FixedZone("UTC+9", 9*60*60)).Unix()))
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfPlayFreeGacha(s *Session, p mhfpacket.MHFPacket) {
|
||||
// not sure this is used anywhere, free gachas use the MSG_MHF_PLAY_NORMAL_GACHA method in captures
|
||||
}
|
||||
|
||||
func handleMsgMhfGetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetBoxGachaInfo)
|
||||
count := 0
|
||||
var used_itemhash pq.Int64Array
|
||||
// pull array of used values
|
||||
// single sized respone with 0x00 is a valid with no items present
|
||||
_ = s.server.db.QueryRow("SELECT used_itemhash FROM lucky_box_state WHERE shophash=$1 AND char_id=$2", pkt.GachaHash, s.charID).Scan((*pq.Int64Array)(&used_itemhash))
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint8(0)
|
||||
for ind := range used_itemhash {
|
||||
resp.WriteUint32(uint32(used_itemhash[ind]))
|
||||
resp.WriteUint8(1)
|
||||
count++
|
||||
}
|
||||
resp.Seek(0, 0)
|
||||
resp.WriteUint8(uint8(count))
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPlayBoxGacha)
|
||||
// needs to query db for input gacha and return a result or number of results
|
||||
// uint8 number of results
|
||||
// uint8 item type
|
||||
// uint16 item id
|
||||
// uint16 quantity
|
||||
|
||||
var currType, rarityIcon, rollsCount, itemCount byte
|
||||
var currQuant, currNumber, percentage uint16
|
||||
var itemhash uint32
|
||||
var itemType, itemId, quantity, usedItemHash pq.Int64Array
|
||||
var items []lottery.Weighter
|
||||
// get info for updating data and calculating costs
|
||||
err := s.server.db.QueryRow("SELECT currType, currNumber, currQuant, rollsCount FROM gacha_shop_items WHERE shophash=$1 AND entryType=$2", pkt.GachaHash, pkt.RollType).Scan(&currType, &currNumber, &currQuant, &rollsCount)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// get existing items in storage if any
|
||||
var data []byte
|
||||
_ = s.server.db.QueryRow("SELECT gacha_items FROM characters WHERE id = $1", s.charID).Scan(&data)
|
||||
if len(data) == 0 {
|
||||
data = []byte{0x00}
|
||||
}
|
||||
// get gacha items and iterate through them for gacha roll
|
||||
shopEntries, err := s.server.db.Query(`SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity
|
||||
FROM gacha_shop_items
|
||||
WHERE shophash=$1 AND entryType=100
|
||||
EXCEPT ALL SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity
|
||||
FROM gacha_shop_items gsi JOIN lucky_box_state lbs ON gsi.itemhash = ANY(lbs.used_itemhash)
|
||||
WHERE lbs.char_id=$2`, pkt.GachaHash, s.charID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for shopEntries.Next() {
|
||||
err = shopEntries.Scan(&itemhash, &percentage, &rarityIcon, &itemCount, (*pq.Int64Array)(&itemType), (*pq.Int64Array)(&itemId), (*pq.Int64Array)(&quantity))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
items = append(items, &gachaItem{itemhash: itemhash, percentage: percentage, rarityIcon: rarityIcon, itemCount: itemCount, itemType: itemType, itemId: itemId, quantity: quantity})
|
||||
}
|
||||
// execute rolls, build response and update database
|
||||
results := byte(0)
|
||||
resp := byteframe.NewByteFrame()
|
||||
dbUpdate := byteframe.NewByteFrame()
|
||||
resp.WriteUint8(0) // results go here later
|
||||
l := lottery.NewDefaultLottery()
|
||||
for x := 0; x < int(rollsCount); x++ {
|
||||
ind := l.Draw(items)
|
||||
results += items[ind].(*gachaItem).itemCount
|
||||
for y := 0; y < int(items[ind].(*gachaItem).itemCount); y++ {
|
||||
// items in storage don't get rarity
|
||||
dbUpdate.WriteUint8(uint8(items[ind].(*gachaItem).itemType[y]))
|
||||
dbUpdate.WriteUint16(uint16(items[ind].(*gachaItem).itemId[y]))
|
||||
dbUpdate.WriteUint16(uint16(items[ind].(*gachaItem).quantity[y]))
|
||||
data = append(data, dbUpdate.Data()...)
|
||||
dbUpdate.Seek(0, 0)
|
||||
// response needs all item info and the rarity
|
||||
resp.WriteBytes(dbUpdate.Data())
|
||||
resp.WriteUint8(items[ind].(*gachaItem).rarityIcon)
|
||||
|
||||
usedItemHash = append(usedItemHash, int64(items[ind].(*gachaItem).itemhash))
|
||||
}
|
||||
// remove rolled
|
||||
items = append(items[:ind], items[ind+1:]...)
|
||||
}
|
||||
resp.Seek(0, 0)
|
||||
resp.WriteUint8(results)
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
|
||||
// add claimables to DB
|
||||
data[0] = data[0] + results
|
||||
_, err = s.server.db.Exec("UPDATE characters SET gacha_items = $1 WHERE id = $2", data, s.charID)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err))
|
||||
}
|
||||
// update lucky_box_state
|
||||
_, err = s.server.db.Exec(`INSERT INTO lucky_box_state (char_id, shophash, used_itemhash)
|
||||
VALUES ($1,$2,$3) ON CONFLICT (char_id, shophash)
|
||||
DO UPDATE SET used_itemhash = COALESCE(lucky_box_state.used_itemhash::int[] || $3::int[], $3::int[])
|
||||
WHERE EXCLUDED.char_id=$1 AND EXCLUDED.shophash=$2`, s.charID, pkt.GachaHash, usedItemHash)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update lucky box state in db", zap.Error(err))
|
||||
}
|
||||
// deduct gacha coins if relevant, items are handled fine by the standard savedata packet immediately afterwards
|
||||
if currType == 19 {
|
||||
_, err = s.server.db.Exec(`UPDATE characters
|
||||
SET gacha_trial = CASE WHEN (gacha_trial > $1) then gacha_trial - $1 else gacha_trial end, gacha_prem = CASE WHEN NOT (gacha_trial > $1) then gacha_prem - $1 else gacha_prem end
|
||||
WHERE id=$2`, currNumber, s.charID)
|
||||
}
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update gacha_trial in db", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfResetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfResetBoxGachaInfo)
|
||||
_, err := s.server.db.Exec("DELETE FROM lucky_box_state WHERE shophash=$1 AND char_id=$2", pkt.GachaHash, s.charID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ func handleMsgMhfInfoTournament(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
switch pkt.Unk0 {
|
||||
case 0:
|
||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix()))
|
||||
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
|
||||
bf.WriteUint32(0) // Tied to schedule ID?
|
||||
case 1:
|
||||
|
||||
|
||||
@@ -58,6 +58,42 @@ func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
||||
// if the game gets bad responses for this it breaks the ability to save
|
||||
pkt := p.(*mhfpacket.MsgMhfGetTenrouirai)
|
||||
var data []byte
|
||||
var err error
|
||||
if pkt.Unk0 == 1 {
|
||||
data, err = hex.DecodeString("0A218EAD000000000000000000000001010000000000060010")
|
||||
} else if pkt.Unk2 == 4 {
|
||||
data, err = hex.DecodeString("0A218EAD0000000000000000000000210101005000000202010102020104001000000202010102020106003200000202010002020104000C003202020101020201030032000002020101020202059C4000000202010002020105C35000320202010102020201003C00000202010102020203003200000201010001020203002800320201010101020204000C00000201010101020206002800000201010001020101003C00320201020101020105C35000000301020101020106003200000301020001020104001000320301020101020105C350000003010201010202030028000003010200010201030032003203010201010202059C4000000301020101010206002800000301020001010201003C00320301020101010206003200000301020101010204000C000003010200010101010050003203010201010101059C40000003010201010101030032000003010200010101040010003203010001010101060032000003010001010102030028000003010001010101010050003203010000010102059C4000000301000001010206002800000301000001010010")
|
||||
} else {
|
||||
data = []byte{0x00, 0x00, 0x00, 0x00}
|
||||
s.logger.Info("GET_TENROUIRAI request for unknown type")
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostTenrouirai)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetWeeklySeibatuRankingReward(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetWeeklySeibatuRankingReward)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPresentBox)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetGemInfo)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
|
||||
@@ -73,7 +73,7 @@ func NewSession(server *Server, conn net.Conn) *Session {
|
||||
cryptConn: network.NewCryptConn(conn),
|
||||
sendPackets: make(chan packet, 20),
|
||||
clientContext: &clientctx.ClientContext{}, // Unused
|
||||
sessionStart: Time_Current_Adjusted().Unix(),
|
||||
sessionStart: TimeAdjusted().Unix(),
|
||||
stageMoveStack: stringstack.New(),
|
||||
}
|
||||
return s
|
||||
|
||||
25
server/channelserver/sys_time.go
Normal file
25
server/channelserver/sys_time.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func TimeAdjusted() time.Time {
|
||||
baseTime := time.Now().In(time.FixedZone("UTC+9", 9*60*60))
|
||||
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), baseTime.Hour(), baseTime.Minute(), baseTime.Second(), baseTime.Nanosecond(), baseTime.Location())
|
||||
}
|
||||
|
||||
func TimeMidnight() time.Time {
|
||||
baseTime := time.Now().In(time.FixedZone("UTC+9", 9*60*60))
|
||||
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location())
|
||||
}
|
||||
|
||||
func TimeWeekStart() time.Time {
|
||||
midnight := TimeMidnight()
|
||||
offset := (int(midnight.Weekday()) - 1) * -24
|
||||
return midnight.Add(time.Hour * time.Duration(offset))
|
||||
}
|
||||
|
||||
func TimeWeekNext() time.Time {
|
||||
return TimeWeekStart().Add(time.Hour * 24 * 7)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
Offset = 9
|
||||
YearAdjust = -7
|
||||
MonthAdjust = 0
|
||||
DayAdjust = 0
|
||||
)
|
||||
|
||||
var (
|
||||
TimeStatic = time.Time{}
|
||||
)
|
||||
|
||||
func Time_Current() time.Time {
|
||||
baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60))
|
||||
return baseTime
|
||||
}
|
||||
|
||||
func Time_Current_Adjusted() time.Time {
|
||||
baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust)
|
||||
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), baseTime.Hour(), baseTime.Minute(), baseTime.Second(), baseTime.Nanosecond(), baseTime.Location())
|
||||
}
|
||||
|
||||
func Time_Current_Midnight() time.Time {
|
||||
baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust)
|
||||
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location())
|
||||
}
|
||||
|
||||
func TimeWeekStart() time.Time {
|
||||
midnight := Time_Current_Midnight()
|
||||
offset := (int(midnight.Weekday()) - 1) * -24
|
||||
return midnight.Add(time.Hour * time.Duration(offset))
|
||||
}
|
||||
|
||||
func TimeWeekNext() time.Time {
|
||||
return TimeWeekStart().Add(time.Hour * 24 * 7)
|
||||
}
|
||||
|
||||
func Time_Current_Week_uint8() uint8 {
|
||||
baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust)
|
||||
|
||||
_, thisWeek := baseTime.ISOWeek()
|
||||
_, beginningOfTheMonth := time.Date(baseTime.Year(), baseTime.Month(), 1, 0, 0, 0, 0, baseTime.Location()).ISOWeek()
|
||||
|
||||
return uint8(1 + thisWeek - beginningOfTheMonth)
|
||||
}
|
||||
|
||||
func Time_static() time.Time {
|
||||
if TimeStatic.IsZero() {
|
||||
TimeStatic = Time_Current_Adjusted()
|
||||
}
|
||||
return TimeStatic
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"erupe-ce/config"
|
||||
@@ -112,7 +113,11 @@ func (s *Server) handleEntranceServerConnection(conn net.Conn) {
|
||||
|
||||
s.logger.Debug("Got entrance server command:\n", zap.String("raw", hex.Dump(pkt)))
|
||||
|
||||
data := makeSv2Resp(s.erupeConfig, s)
|
||||
local := false
|
||||
if strings.Split(conn.RemoteAddr().String(), ":")[0] == "127.0.0.1" {
|
||||
local = true
|
||||
}
|
||||
data := makeSv2Resp(s.erupeConfig, s, local)
|
||||
if len(pkt) > 5 {
|
||||
data = append(data, makeUsrResp(pkt, s)...)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ var season uint8
|
||||
// Server Channels
|
||||
var currentplayers uint16
|
||||
|
||||
func encodeServerInfo(config *config.Config, s *Server) []byte {
|
||||
func encodeServerInfo(config *config.Config, s *Server, local bool) []byte {
|
||||
serverInfos := config.Entrance.Entries
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
@@ -30,7 +30,11 @@ func encodeServerInfo(config *config.Config, s *Server) []byte {
|
||||
if si.IP == "" {
|
||||
si.IP = config.Host
|
||||
}
|
||||
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4()))
|
||||
if local {
|
||||
bf.WriteUint32(0x0100007F) // 127.0.0.1
|
||||
} else {
|
||||
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4()))
|
||||
}
|
||||
bf.WriteUint16(16 + uint16(serverIdx))
|
||||
bf.WriteUint16(0x0000)
|
||||
bf.WriteUint16(uint16(len(si.Channels)))
|
||||
@@ -62,7 +66,7 @@ func encodeServerInfo(config *config.Config, s *Server) []byte {
|
||||
bf.WriteUint16(0x3039)
|
||||
}
|
||||
}
|
||||
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Unix()))
|
||||
bf.WriteUint32(uint32(channelserver.TimeAdjusted().Unix()))
|
||||
bf.WriteUint32(0x0000003C)
|
||||
return bf.Data()
|
||||
}
|
||||
@@ -85,9 +89,9 @@ func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byt
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
func makeSv2Resp(config *config.Config, s *Server) []byte {
|
||||
func makeSv2Resp(config *config.Config, s *Server, local bool) []byte {
|
||||
serverInfos := config.Entrance.Entries
|
||||
rawServerData := encodeServerInfo(config, s)
|
||||
rawServerData := encodeServerInfo(config, s, local)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteBytes(makeHeader(rawServerData, "SV2", uint16(len(serverInfos)), 0x00))
|
||||
return bf.Data()
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"erupe-ce/server/channelserver"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
@@ -51,7 +52,11 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
||||
ps.Uint8(bf, s.server.erupeConfig.PatchServerFile, false)
|
||||
}
|
||||
}
|
||||
ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.Host, s.server.erupeConfig.Entrance.Port), false)
|
||||
if strings.Split(s.rawConn.RemoteAddr().String(), ":")[0] == "127.0.0.1" {
|
||||
ps.Uint8(bf, fmt.Sprintf("127.0.0.1:%d", s.server.erupeConfig.Entrance.Port), false)
|
||||
} else {
|
||||
ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.Host, s.server.erupeConfig.Entrance.Port), false)
|
||||
}
|
||||
|
||||
lastPlayed := uint32(0)
|
||||
for _, char := range chars {
|
||||
@@ -122,12 +127,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
||||
bf.WriteUint16(0x0001)
|
||||
bf.WriteUint16(0x4E20)
|
||||
ps.Uint16(bf, "", false) // unk ipv4
|
||||
if returnExpiry.Before(time.Now()) {
|
||||
// Hack to make Return work while having a non-adjusted expiry
|
||||
bf.WriteUint32(0)
|
||||
} else {
|
||||
bf.WriteUint32(uint32(returnExpiry.Unix()))
|
||||
}
|
||||
bf.WriteUint32(uint32(returnExpiry.Unix()))
|
||||
bf.WriteUint32(0x00000000)
|
||||
bf.WriteUint32(0x0A5197DF) // unk id
|
||||
|
||||
@@ -135,9 +135,9 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
||||
alt := s.server.erupeConfig.DevModeOptions.MezFesAlt
|
||||
if mezfes {
|
||||
// Start time
|
||||
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(-5 * time.Minute).Unix()))
|
||||
bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix()))
|
||||
// End time
|
||||
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(24 * time.Hour * 7).Unix()))
|
||||
bf.WriteUint32(uint32(channelserver.TimeWeekNext().Unix()))
|
||||
bf.WriteUint8(2) // Unk
|
||||
bf.WriteUint32(20) // Single tickets
|
||||
bf.WriteUint32(10) // Group tickets
|
||||
|
||||
Reference in New Issue
Block a user