From 7194cdbc0740ffff3fde1a77b7fe4d1fad79d039 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 12 Oct 2023 23:54:35 +1100 Subject: [PATCH 1/4] optimise GuildTresure handlers --- config.json | 1 + config/config.go | 1 + .../channelserver/handlers_guild_tresure.go | 117 +++++++++--------- 3 files changed, 58 insertions(+), 61 deletions(-) diff --git a/config.json b/config.json index bd943d43b..31629b894 100644 --- a/config.json +++ b/config.json @@ -40,6 +40,7 @@ "MaximumNP": 100000, "MaximumRP": 50000, "MaximumFP": 120000, + "TreasureHuntExpiry": 604800, "DisableLoginBoost": false, "DisableBoostTime": false, "BoostTimeDuration": 120, diff --git a/config/config.go b/config/config.go index 713b08a90..25ac45313 100644 --- a/config/config.go +++ b/config/config.go @@ -125,6 +125,7 @@ type GameplayOptions struct { MaximumNP int // Maximum number of NP held by a player MaximumRP uint16 // Maximum number of RP held by a player MaximumFP uint32 // Maximum number of FP held by a player + TreasureHuntExpiry uint32 // Seconds until a Clan Treasure Hunt will expire DisableLoginBoost bool // Disables the Login Boost system DisableBoostTime bool // Disables the daily NetCafe Boost Time BoostTimeDuration int // The number of minutes NetCafe Boost Time lasts for diff --git a/server/channelserver/handlers_guild_tresure.go b/server/channelserver/handlers_guild_tresure.go index 981d8d258..b856293ab 100644 --- a/server/channelserver/handlers_guild_tresure.go +++ b/server/channelserver/handlers_guild_tresure.go @@ -22,54 +22,54 @@ type TreasureHunt struct { func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuildTresure) guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil { - panic(err) + if err != nil || guild == nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + return + } + var hunts []TreasureHunt + var hunt TreasureHunt + rows, err := 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+$3 + `, guild.ID, TimeAdjusted().Unix(), s.server.erupeConfig.GameplayOptions.TreasureHuntExpiry) + if err != nil { + rows.Close() + return } - 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, TimeAdjusted().Unix()) for rows.Next() { - hunt := &TreasureHunt{} err = rows.StructScan(&hunt) + if err != nil { + continue + } // Remove self from other hunter count hunt.Hunters = stringsupport.CSVRemove(hunt.Hunters, int(s.charID)) - if err != nil { - panic(err) - } if pkt.MaxHunts == 1 { if hunt.HostID != s.charID || hunt.Acquired { continue } - hunts++ - bf.WriteUint32(hunt.HuntID) - bf.WriteUint32(hunt.Destination) - bf.WriteUint32(hunt.Level) - bf.WriteUint32(uint32(stringsupport.CSVLength(hunt.Hunters))) - bf.WriteUint32(hunt.Return) - bf.WriteBool(false) - bf.WriteBool(false) - bf.WriteBytes(hunt.HuntData) + hunt.Claimed = false + hunt.Treasure = "" + hunts = append(hunts, hunt) break } else if pkt.MaxHunts == 30 && hunt.Acquired && hunt.Level == 2 { - if hunts == 30 { - break - } - hunts++ - bf.WriteUint32(hunt.HuntID) - bf.WriteUint32(hunt.Destination) - bf.WriteUint32(hunt.Level) - bf.WriteUint32(uint32(stringsupport.CSVLength(hunt.Hunters))) - bf.WriteUint32(hunt.Return) - bf.WriteBool(hunt.Claimed) - bf.WriteBool(stringsupport.CSVContains(hunt.Treasure, int(s.charID))) - bf.WriteBytes(hunt.HuntData) + hunts = append(hunts, hunt) } } - resp := byteframe.NewByteFrame() - resp.WriteUint16(uint16(hunts)) - resp.WriteUint16(uint16(hunts)) - resp.WriteBytes(bf.Data()) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + if len(hunts) > 30 { + hunts = hunts[:30] + } + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(len(hunts))) + bf.WriteUint16(uint16(len(hunts))) + for _, h := range hunts { + bf.WriteUint32(h.HuntID) + bf.WriteUint32(h.Destination) + bf.WriteUint32(h.Level) + bf.WriteUint32(uint32(stringsupport.CSVLength(h.Hunters))) + bf.WriteUint32(h.Return) + bf.WriteBool(h.Claimed) + bf.WriteBool(stringsupport.CSVContains(h.Treasure, int(s.charID))) + bf.WriteBytes(h.HuntData) + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { @@ -77,8 +77,9 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrameFromBytes(pkt.Data) huntData := byteframe.NewByteFrame() guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil { - panic(err) + if err != nil || guild == nil { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + return } guildCats := getGuildAirouList(s) destination := bf.ReadUint32() @@ -100,20 +101,14 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { huntData.WriteBytes(bf.ReadBytes(9)) } } - _, 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, TimeAdjusted().Unix(), huntData.Data(), catsUsed) - if err != nil { - panic(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, TimeAdjusted().Unix(), huntData.Data(), catsUsed) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfAcquireGuildTresure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireGuildTresure) - _, err := s.server.db.Exec("UPDATE guild_hunts SET acquired=true WHERE id=$1", pkt.HuntID) - if err != nil { - panic(err) - } + s.server.db.Exec("UPDATE guild_hunts SET acquired=true WHERE id=$1", pkt.HuntID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -126,6 +121,7 @@ func treasureHuntUnregister(s *Session) { var hunters string rows, err := s.server.db.Queryx("SELECT id, hunters FROM guild_hunts WHERE guild_id=$1", guild.ID) if err != nil { + rows.Close() return } for rows.Next() { @@ -138,41 +134,40 @@ func treasureHuntUnregister(s *Session) { func handleMsgMhfOperateGuildTresureReport(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfOperateGuildTresureReport) var csv string - if pkt.State == 0 { // Report registration + switch pkt.State { + case 0: // Report registration // Unregister from all other hunts treasureHuntUnregister(s) if pkt.HuntID != 0 { // Register to selected hunt err := s.server.db.QueryRow("SELECT hunters FROM guild_hunts WHERE id=$1", pkt.HuntID).Scan(&csv) if err != nil { - panic(err) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + return } csv = stringsupport.CSVAdd(csv, int(s.charID)) - _, err = s.server.db.Exec("UPDATE guild_hunts SET hunters=$1 WHERE id=$2", csv, pkt.HuntID) - if err != nil { - panic(err) - } + s.server.db.Exec("UPDATE guild_hunts SET hunters=$1 WHERE id=$2", csv, pkt.HuntID) } - } else if pkt.State == 1 { // Collected by hunter + case 1: // Collected by hunter s.server.db.Exec("UPDATE guild_hunts SET hunters='', claimed=true WHERE id=$1", pkt.HuntID) - } else if pkt.State == 2 { // Claim treasure + case 2: // Claim treasure err := s.server.db.QueryRow("SELECT treasure FROM guild_hunts WHERE id=$1", pkt.HuntID).Scan(&csv) if err != nil { - panic(err) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + return } csv = stringsupport.CSVAdd(csv, int(s.charID)) - _, err = s.server.db.Exec("UPDATE guild_hunts SET treasure=$1 WHERE id=$2", csv, pkt.HuntID) - if err != nil { - panic(err) - } + s.server.db.Exec("UPDATE guild_hunts SET treasure=$1 WHERE id=$2", csv, pkt.HuntID) } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfGetGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGuildTresureSouvenir) - - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 6)) + bf := byteframe.NewByteFrame() + bf.WriteUint32(0) + bf.WriteUint16(0) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfAcquireGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) { From bf44944884f2ad8b720121b8960d8627a913f0f2 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 14 Oct 2023 14:14:28 +1100 Subject: [PATCH 2/4] more GuildTresure optimisation --- patch-schema/09-fix-guild-treasure.sql | 8 ++++ server/channelserver/handlers.go | 2 +- .../channelserver/handlers_guild_tresure.go | 46 +++---------------- 3 files changed, 16 insertions(+), 40 deletions(-) create mode 100644 patch-schema/09-fix-guild-treasure.sql diff --git a/patch-schema/09-fix-guild-treasure.sql b/patch-schema/09-fix-guild-treasure.sql new file mode 100644 index 000000000..5abfe3271 --- /dev/null +++ b/patch-schema/09-fix-guild-treasure.sql @@ -0,0 +1,8 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.guild_hunts DROP COLUMN IF EXISTS hunters; + +ALTER TABLE IF EXISTS public.guild_characters + ADD COLUMN treasure_hunt integer; + +END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index e39942671..211fcaf79 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -233,7 +233,7 @@ func logoutPlayer(s *Session) { s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID) - treasureHuntUnregister(s) + s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE character_id=$1`, s.charID) if s.stage == nil { return diff --git a/server/channelserver/handlers_guild_tresure.go b/server/channelserver/handlers_guild_tresure.go index b856293ab..e34e36d23 100644 --- a/server/channelserver/handlers_guild_tresure.go +++ b/server/channelserver/handlers_guild_tresure.go @@ -108,56 +108,24 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireGuildTresure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireGuildTresure) - s.server.db.Exec("UPDATE guild_hunts SET acquired=true WHERE id=$1", pkt.HuntID) + s.server.db.Exec(`UPDATE guild_hunts SET acquired=true WHERE id=$1`, pkt.HuntID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } -func treasureHuntUnregister(s *Session) { - guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil || guild == nil { - return - } - var huntID int - var hunters string - rows, err := s.server.db.Queryx("SELECT id, hunters FROM guild_hunts WHERE guild_id=$1", guild.ID) - if err != nil { - rows.Close() - return - } - for rows.Next() { - rows.Scan(&huntID, &hunters) - hunters = stringsupport.CSVRemove(hunters, int(s.charID)) - s.server.db.Exec("UPDATE guild_hunts SET hunters=$1 WHERE id=$2", hunters, huntID) - } -} - func handleMsgMhfOperateGuildTresureReport(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfOperateGuildTresureReport) var csv string switch pkt.State { case 0: // Report registration - // Unregister from all other hunts - treasureHuntUnregister(s) - if pkt.HuntID != 0 { - // Register to selected hunt - err := s.server.db.QueryRow("SELECT hunters FROM guild_hunts WHERE id=$1", pkt.HuntID).Scan(&csv) - if err != nil { - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) - return - } - csv = stringsupport.CSVAdd(csv, int(s.charID)) - s.server.db.Exec("UPDATE guild_hunts SET hunters=$1 WHERE id=$2", csv, pkt.HuntID) - } + s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=$1 WHERE character_id=$2`, pkt.HuntID, s.charID) case 1: // Collected by hunter - s.server.db.Exec("UPDATE guild_hunts SET hunters='', claimed=true WHERE id=$1", pkt.HuntID) + s.server.db.Exec(`UPDATE guild_hunts SET claimed=true WHERE id=$1;UPDATE guild_characters SET treasure_hunt=NULL WHERE treasure_hunt=$1`, pkt.HuntID) case 2: // Claim treasure - err := s.server.db.QueryRow("SELECT treasure FROM guild_hunts WHERE id=$1", pkt.HuntID).Scan(&csv) - if err != nil { - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) - return + err := s.server.db.QueryRow(`SELECT treasure FROM guild_hunts WHERE id=$1`, pkt.HuntID).Scan(&csv) + if err == nil { + csv = stringsupport.CSVAdd(csv, int(s.charID)) + s.server.db.Exec(`UPDATE guild_hunts SET treasure=$1 WHERE id=$2`, csv, pkt.HuntID) } - csv = stringsupport.CSVAdd(csv, int(s.charID)) - s.server.db.Exec("UPDATE guild_hunts SET treasure=$1 WHERE id=$2", csv, pkt.HuntID) } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } From e688fdf3c5ad49a16cab6fb05f9ec0d1b9e9a3c7 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 17 Oct 2023 00:35:06 +1100 Subject: [PATCH 3/4] more GuildTresure optimisation --- network/mhfpacket/msg_mhf_enumerate_guild_tresure.go | 8 ++++---- server/channelserver/handlers_guild_tresure.go | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/network/mhfpacket/msg_mhf_enumerate_guild_tresure.go b/network/mhfpacket/msg_mhf_enumerate_guild_tresure.go index 61475d655..f03202bd4 100644 --- a/network/mhfpacket/msg_mhf_enumerate_guild_tresure.go +++ b/network/mhfpacket/msg_mhf_enumerate_guild_tresure.go @@ -12,7 +12,8 @@ import ( type MsgMhfEnumerateGuildTresure struct { AckHandle uint32 MaxHunts uint16 - Unk uint32 + Unk0 uint16 + Unk1 uint16 } // Opcode returns the ID associated with this packet type. @@ -24,9 +25,8 @@ func (m *MsgMhfEnumerateGuildTresure) Opcode() network.PacketID { func (m *MsgMhfEnumerateGuildTresure) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.MaxHunts = bf.ReadUint16() - // Changes with MaxHunts - // 0 if MaxHunts = 1, 1 if MaxHunts = 30 - m.Unk = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() return nil } diff --git a/server/channelserver/handlers_guild_tresure.go b/server/channelserver/handlers_guild_tresure.go index e34e36d23..516991595 100644 --- a/server/channelserver/handlers_guild_tresure.go +++ b/server/channelserver/handlers_guild_tresure.go @@ -14,7 +14,7 @@ type TreasureHunt struct { Return uint32 `db:"return"` Acquired bool `db:"acquired"` Claimed bool `db:"claimed"` - Hunters string `db:"hunters"` + Hunters uint32 `db:"hunters"` Treasure string `db:"treasure"` HuntData []byte `db:"hunt_data"` } @@ -28,8 +28,10 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { } var hunts []TreasureHunt var hunt TreasureHunt - rows, err := 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+$3 - `, guild.ID, TimeAdjusted().Unix(), s.server.erupeConfig.GameplayOptions.TreasureHuntExpiry) + rows, err := s.server.db.Queryx(`SELECT gh.id, gh.host_id, gh.destination, gh.level, gh.return, gh.acquired, gh.claimed, gh.treasure, gh.hunt_data, + (SELECT COUNT(*) FROM guild_characters gc WHERE gc.treasure_hunt = gh.id AND gc.character_id <> $1) AS hunters + FROM guild_hunts gh WHERE gh.guild_id=$2 AND $3 < gh.return+$4 + `, s.charID, guild.ID, TimeAdjusted().Unix(), s.server.erupeConfig.GameplayOptions.TreasureHuntExpiry) if err != nil { rows.Close() return @@ -39,8 +41,6 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { if err != nil { continue } - // Remove self from other hunter count - hunt.Hunters = stringsupport.CSVRemove(hunt.Hunters, int(s.charID)) if pkt.MaxHunts == 1 { if hunt.HostID != s.charID || hunt.Acquired { continue @@ -63,7 +63,7 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(h.HuntID) bf.WriteUint32(h.Destination) bf.WriteUint32(h.Level) - bf.WriteUint32(uint32(stringsupport.CSVLength(h.Hunters))) + bf.WriteUint32(h.Hunters) bf.WriteUint32(h.Return) bf.WriteBool(h.Claimed) bf.WriteBool(stringsupport.CSVContains(h.Treasure, int(s.charID))) From 2f8d09b09ee06dd0d03e39505ac4fd3329e0a2a9 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 24 Oct 2023 21:21:21 +1100 Subject: [PATCH 4/4] more GuildTresure optimisation --- config/config.go | 1 + main.go | 1 + patch-schema/09-fix-guild-treasure.sql | 18 +++ .../channelserver/handlers_guild_tresure.go | 105 ++++++++++-------- server/channelserver/handlers_mercenary.go | 46 ++++---- 5 files changed, 99 insertions(+), 72 deletions(-) diff --git a/config/config.go b/config/config.go index 25ac45313..65c663511 100644 --- a/config/config.go +++ b/config/config.go @@ -126,6 +126,7 @@ type GameplayOptions struct { MaximumRP uint16 // Maximum number of RP held by a player MaximumFP uint32 // Maximum number of FP held by a player TreasureHuntExpiry uint32 // Seconds until a Clan Treasure Hunt will expire + TreasureHuntPartnyaCooldown uint32 // Seconds until a Partnya can be assigned to another Clan Treasure Hunt DisableLoginBoost bool // Disables the Login Boost system DisableBoostTime bool // Disables the daily NetCafe Boost Time BoostTimeDuration int // The number of minutes NetCafe Boost Time lasts for diff --git a/main.go b/main.go index f549f4bc8..2a538b094 100644 --- a/main.go +++ b/main.go @@ -126,6 +126,7 @@ func main() { // Clear stale data _ = db.MustExec("DELETE FROM sign_sessions") _ = db.MustExec("DELETE FROM servers") + _ = db.MustExec(`UPDATE guild_characters SET treasure_hunt=NULL`) // Clean the DB if the option is on. if config.DevMode && config.DevModeOptions.CleanDB { diff --git a/patch-schema/09-fix-guild-treasure.sql b/patch-schema/09-fix-guild-treasure.sql index 5abfe3271..1c022292f 100644 --- a/patch-schema/09-fix-guild-treasure.sql +++ b/patch-schema/09-fix-guild-treasure.sql @@ -5,4 +5,22 @@ ALTER TABLE IF EXISTS public.guild_hunts DROP COLUMN IF EXISTS hunters; ALTER TABLE IF EXISTS public.guild_characters ADD COLUMN treasure_hunt integer; +ALTER TABLE IF EXISTS public.guild_hunts + ADD COLUMN start timestamp with time zone NOT NULL DEFAULT now(); + +UPDATE guild_hunts SET start=to_timestamp(return); + +ALTER TABLE IF EXISTS public.guild_hunts DROP COLUMN IF EXISTS "return"; + +ALTER TABLE IF EXISTS public.guild_hunts + RENAME claimed TO collected; + +CREATE TABLE public.guild_hunts_claimed +( + hunt_id integer NOT NULL, + character_id integer NOT NULL +); + +ALTER TABLE IF EXISTS public.guild_hunts DROP COLUMN IF EXISTS treasure; + END; \ No newline at end of file diff --git a/server/channelserver/handlers_guild_tresure.go b/server/channelserver/handlers_guild_tresure.go index 516991595..f3f4815e6 100644 --- a/server/channelserver/handlers_guild_tresure.go +++ b/server/channelserver/handlers_guild_tresure.go @@ -4,19 +4,20 @@ import ( "erupe-ce/common/byteframe" "erupe-ce/common/stringsupport" "erupe-ce/network/mhfpacket" + "time" ) type TreasureHunt struct { - HuntID uint32 `db:"id"` - HostID uint32 `db:"host_id"` - Destination uint32 `db:"destination"` - Level uint32 `db:"level"` - Return uint32 `db:"return"` - Acquired bool `db:"acquired"` - Claimed bool `db:"claimed"` - Hunters uint32 `db:"hunters"` - Treasure string `db:"treasure"` - HuntData []byte `db:"hunt_data"` + HuntID uint32 `db:"id"` + HostID uint32 `db:"host_id"` + Destination uint32 `db:"destination"` + Level uint32 `db:"level"` + Start time.Time `db:"start"` + Acquired bool `db:"acquired"` + Collected bool `db:"collected"` + HuntData []byte `db:"hunt_data"` + Hunters uint32 `db:"hunters"` + Claimed bool `db:"claimed"` } func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { @@ -28,33 +29,39 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { } var hunts []TreasureHunt var hunt TreasureHunt - rows, err := s.server.db.Queryx(`SELECT gh.id, gh.host_id, gh.destination, gh.level, gh.return, gh.acquired, gh.claimed, gh.treasure, gh.hunt_data, - (SELECT COUNT(*) FROM guild_characters gc WHERE gc.treasure_hunt = gh.id AND gc.character_id <> $1) AS hunters - FROM guild_hunts gh WHERE gh.guild_id=$2 AND $3 < gh.return+$4 - `, s.charID, guild.ID, TimeAdjusted().Unix(), s.server.erupeConfig.GameplayOptions.TreasureHuntExpiry) - if err != nil { - rows.Close() - return - } - for rows.Next() { - err = rows.StructScan(&hunt) + + switch pkt.MaxHunts { + case 1: + err = s.server.db.QueryRowx(`SELECT id, host_id, destination, level, start, hunt_data FROM guild_hunts WHERE host_id=$1 AND acquired=FALSE`, s.charID).StructScan(&hunt) + if err == nil { + hunts = append(hunts, hunt) + } + case 30: + rows, err := s.server.db.Queryx(`SELECT gh.id, gh.host_id, gh.destination, gh.level, gh.start, gh.collected, gh.hunt_data, + (SELECT COUNT(*) FROM guild_characters gc WHERE gc.treasure_hunt = gh.id AND gc.character_id <> $1) AS hunters, + CASE + WHEN ghc.character_id IS NOT NULL THEN true + ELSE false + END AS claimed + FROM guild_hunts gh + LEFT JOIN guild_hunts_claimed ghc ON gh.id = ghc.hunt_id AND ghc.character_id = $1 + WHERE gh.guild_id=$2 AND gh.level=2 AND gh.acquired=TRUE + `, s.charID, guild.ID) if err != nil { - continue - } - if pkt.MaxHunts == 1 { - if hunt.HostID != s.charID || hunt.Acquired { - continue + rows.Close() + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + return + } else { + for rows.Next() { + err = rows.StructScan(&hunt) + if err == nil && hunt.Start.Add(time.Second*time.Duration(s.server.erupeConfig.GameplayOptions.TreasureHuntExpiry)).After(TimeAdjusted()) { + hunts = append(hunts, hunt) + } } - hunt.Claimed = false - hunt.Treasure = "" - hunts = append(hunts, hunt) - break - } else if pkt.MaxHunts == 30 && hunt.Acquired && hunt.Level == 2 { - hunts = append(hunts, hunt) } - } - if len(hunts) > 30 { - hunts = hunts[:30] + if len(hunts) > 30 { + hunts = hunts[:30] + } } bf := byteframe.NewByteFrame() bf.WriteUint16(uint16(len(hunts))) @@ -64,9 +71,9 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(h.Destination) bf.WriteUint32(h.Level) bf.WriteUint32(h.Hunters) - bf.WriteUint32(h.Return) + bf.WriteUint32(uint32(h.Start.Unix())) + bf.WriteBool(h.Collected) bf.WriteBool(h.Claimed) - bf.WriteBool(stringsupport.CSVContains(h.Treasure, int(s.charID))) bf.WriteBytes(h.HuntData) } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) @@ -101,8 +108,8 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { huntData.WriteBytes(bf.ReadBytes(9)) } } - 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, TimeAdjusted().Unix(), huntData.Data(), catsUsed) + s.server.db.Exec(`INSERT INTO guild_hunts (guild_id, host_id, destination, level, hunt_data, cats_used) VALUES ($1, $2, $3, $4, $5, $6) + `, guild.ID, s.charID, destination, level, huntData.Data(), catsUsed) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -114,27 +121,33 @@ func handleMsgMhfAcquireGuildTresure(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfOperateGuildTresureReport(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfOperateGuildTresureReport) - var csv string switch pkt.State { case 0: // Report registration s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=$1 WHERE character_id=$2`, pkt.HuntID, s.charID) case 1: // Collected by hunter - s.server.db.Exec(`UPDATE guild_hunts SET claimed=true WHERE id=$1;UPDATE guild_characters SET treasure_hunt=NULL WHERE treasure_hunt=$1`, pkt.HuntID) + s.server.db.Exec(`UPDATE guild_hunts SET collected=true WHERE id=$1`, pkt.HuntID) + s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE treasure_hunt=$1`, pkt.HuntID) case 2: // Claim treasure - err := s.server.db.QueryRow(`SELECT treasure FROM guild_hunts WHERE id=$1`, pkt.HuntID).Scan(&csv) - if err == nil { - csv = stringsupport.CSVAdd(csv, int(s.charID)) - s.server.db.Exec(`UPDATE guild_hunts SET treasure=$1 WHERE id=$2`, csv, pkt.HuntID) - } + s.server.db.Exec(`INSERT INTO guild_hunts_claimed VALUES ($1, $2)`, pkt.HuntID, s.charID) } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } +type TreasureSouvenir struct { + Destination uint32 + Quantity uint32 +} + func handleMsgMhfGetGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGuildTresureSouvenir) bf := byteframe.NewByteFrame() bf.WriteUint32(0) - bf.WriteUint16(0) + souvenirs := []TreasureSouvenir{} + bf.WriteUint16(uint16(len(souvenirs))) + for _, souvenir := range souvenirs { + bf.WriteUint32(souvenir.Destination) + bf.WriteUint32(souvenir.Quantity) + } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index 25e4e3edc..19c358c52 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -325,44 +325,38 @@ type Airou struct { } func getGuildAirouList(s *Session) []Airou { - var guild *Guild - var err error var guildCats []Airou - - // returning 0 cats on any guild issues - // can probably optimise all of the guild queries pretty heavily - guild, err = GetGuildInfoByCharacterId(s, s.charID) + bannedCats := make(map[uint32]int) + guild, err := GetGuildInfoByCharacterId(s, s.charID) if err != nil { return guildCats } - - // Get cats used recently - // Retail reset at midday, 12 hours is a midpoint - tempBanDuration := 43200 - (1800) // Minus hunt time - bannedCats := make(map[uint32]int) - var csvTemp string - rows, err := s.server.db.Query(`SELECT cats_used - 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, TimeAdjusted().Unix()) + rows, err := s.server.db.Query(`SELECT cats_used FROM guild_hunts gh + INNER JOIN characters c ON gh.host_id = c.id WHERE c.id=$1 + `, s.charID) if err != nil { s.logger.Warn("Failed to get recently used airous", zap.Error(err)) + return guildCats } + + var csvTemp string + var startTemp time.Time for rows.Next() { - rows.Scan(&csvTemp) - for i, j := range stringsupport.CSVElems(csvTemp) { - bannedCats[uint32(j)] = i + err = rows.Scan(&csvTemp, &startTemp) + if err != nil { + continue + } + if startTemp.Add(time.Second * time.Duration(s.server.erupeConfig.GameplayOptions.TreasureHuntPartnyaCooldown)).Before(TimeAdjusted()) { + for i, j := range stringsupport.CSVElems(csvTemp) { + bannedCats[uint32(j)] = i + } } } - rows, err = s.server.db.Query(`SELECT c.otomoairou - FROM characters c - INNER JOIN guild_characters gc - ON gc.character_id = c.id + rows, err = s.server.db.Query(`SELECT c.otomoairou FROM characters c + INNER JOIN guild_characters gc ON gc.character_id = c.id WHERE gc.guild_id = $1 AND c.otomoairou IS NOT NULL - ORDER BY c.id ASC - LIMIT 60;`, guild.ID) + ORDER BY c.id LIMIT 60`, guild.ID) if err != nil { s.logger.Warn("Selecting otomoairou based on guild failed", zap.Error(err)) return guildCats