diff --git a/Erupe/common/stringsupport/string_convert.go b/Erupe/common/stringsupport/string_convert.go index bc0039ba6..35a131d1a 100644 --- a/Erupe/common/stringsupport/string_convert.go +++ b/Erupe/common/stringsupport/string_convert.go @@ -145,6 +145,14 @@ func CSVContains(csv string, v int) bool { return false } +func CSVLength(csv string) int { + if csv == "" { + return 0 + } + s := strings.Split(csv, ",") + return len(s) +} + // ConvertUTF8ToShiftJIS converts a UTF8 string to a Shift-JIS []byte. func ConvertUTF8ToShiftJIS(text string) ([]byte, error) { r := bytes.NewBuffer([]byte(text)) diff --git a/Erupe/guild-additions.sql b/Erupe/guild-additions.sql index edc058276..2c9ddde46 100644 --- a/Erupe/guild-additions.sql +++ b/Erupe/guild-additions.sql @@ -37,4 +37,19 @@ CREATE TABLE IF NOT EXISTS public.guild_meals expires int NOT NULL ); +CREATE TABLE IF NOT EXISTS public.guild_hunts +( + id serial NOT NULL PRIMARY KEY, + guild_id int NOT NULL, + host_id int NOT NULL, + destination int NOT NULL, + level int NOT NULL, + return int NOT NULL, + acquired bool NOT NULL DEFAULT false, + claimed bool NOT NULL DEFAULT false, + hunters text NOT NULL DEFAULT '', + treasure text NOT NULL DEFAULT '', + hunt_data bytea NOT NULL +); + END; \ No newline at end of file diff --git a/Erupe/network/mhfpacket/msg_mhf_acquire_guild_tresure.go b/Erupe/network/mhfpacket/msg_mhf_acquire_guild_tresure.go index df9f0cf52..775e98bf5 100644 --- a/Erupe/network/mhfpacket/msg_mhf_acquire_guild_tresure.go +++ b/Erupe/network/mhfpacket/msg_mhf_acquire_guild_tresure.go @@ -1,18 +1,18 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfAcquireGuildTresure represents the MSG_MHF_ACQUIRE_GUILD_TRESURE type MsgMhfAcquireGuildTresure struct { - AckHandle uint32 - Unk0 uint32 - Unk1 uint8 + AckHandle uint32 + HuntID uint32 + Unk uint8 } // Opcode returns the ID associated with this packet type. @@ -22,10 +22,10 @@ func (m *MsgMhfAcquireGuildTresure) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfAcquireGuildTresure) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint32() - m.Unk1 = bf.ReadUint8() - return nil + m.AckHandle = bf.ReadUint32() + m.HuntID = bf.ReadUint32() + m.Unk = bf.ReadUint8() + return nil } // Build builds a binary packet from the current data. diff --git a/Erupe/network/mhfpacket/msg_mhf_operate_guild_tresure_report.go b/Erupe/network/mhfpacket/msg_mhf_operate_guild_tresure_report.go index 7952f7551..d2564fb00 100644 --- a/Erupe/network/mhfpacket/msg_mhf_operate_guild_tresure_report.go +++ b/Erupe/network/mhfpacket/msg_mhf_operate_guild_tresure_report.go @@ -1,18 +1,18 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfOperateGuildTresureReport represents the MSG_MHF_OPERATE_GUILD_TRESURE_REPORT -type MsgMhfOperateGuildTresureReport struct{ - AckHandle uint32 - HuntID uint32 - Unk uint16 +type MsgMhfOperateGuildTresureReport struct { + AckHandle uint32 + HuntID uint32 + State uint16 } // Opcode returns the ID associated with this packet type. @@ -22,10 +22,10 @@ func (m *MsgMhfOperateGuildTresureReport) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfOperateGuildTresureReport) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.HuntID = bf.ReadUint32() - m.Unk = bf.ReadUint16() - return nil + m.AckHandle = bf.ReadUint32() + m.HuntID = bf.ReadUint32() + m.State = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/Erupe/network/mhfpacket/msg_mhf_regist_guild_tresure.go b/Erupe/network/mhfpacket/msg_mhf_regist_guild_tresure.go index 438f342b7..b8c24ff15 100644 --- a/Erupe/network/mhfpacket/msg_mhf_regist_guild_tresure.go +++ b/Erupe/network/mhfpacket/msg_mhf_regist_guild_tresure.go @@ -1,17 +1,17 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfRegistGuildTresure represents the MSG_MHF_REGIST_GUILD_TRESURE type MsgMhfRegistGuildTresure struct { - AckHandle uint32 - Data []byte + AckHandle uint32 + Data []byte } // Opcode returns the ID associated with this packet type. @@ -21,10 +21,10 @@ func (m *MsgMhfRegistGuildTresure) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfRegistGuildTresure) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.Data = bf.ReadBytes(uint(bf.ReadUint16())) - _ = bf.ReadUint32() - return nil + m.AckHandle = bf.ReadUint32() + m.Data = bf.ReadBytes(uint(bf.ReadUint16())) + _ = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/Erupe/server/channelserver/handlers.go b/Erupe/server/channelserver/handlers.go index c52bf9623..1a0f12ae1 100644 --- a/Erupe/server/channelserver/handlers.go +++ b/Erupe/server/channelserver/handlers.go @@ -239,6 +239,7 @@ func logoutPlayer(s *Session) { removeSessionFromSemaphore(s) removeSessionFromStage(s) + treasureHuntUnregister(s) saveData, err := GetCharacterSaveData(s, s.charID) if err != nil { diff --git a/Erupe/server/channelserver/handlers_guild_tresure.go b/Erupe/server/channelserver/handlers_guild_tresure.go index a1c574418..c187f1407 100644 --- a/Erupe/server/channelserver/handlers_guild_tresure.go +++ b/Erupe/server/channelserver/handlers_guild_tresure.go @@ -1,25 +1,166 @@ package channelserver -import "erupe-ce/network/mhfpacket" +import ( + "erupe-ce/common/byteframe" + "erupe-ce/common/stringsupport" + "erupe-ce/network/mhfpacket" +) + +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 string `db:"hunters"` + Treasure string `db:"treasure"` + HuntData []byte `db:"hunt_data"` +} func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuildTresure) - - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + guild, err := GetGuildInfoByCharacterId(s, s.charID) + if err != nil { + panic(err) + } + 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", guild.ID) + for rows.Next() { + hunt := &TreasureHunt{} + err = rows.StructScan(&hunt) + // 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) + break + } else if pkt.MaxHunts == 30 && hunt.Acquired && hunt.Level == 2 { + 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) + } + } + resp := byteframe.NewByteFrame() + resp.WriteUint16(uint16(hunts)) + resp.WriteUint16(uint16(hunts)) + resp.WriteBytes(bf.Data()) + doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfRegistGuildTresure) + bf := byteframe.NewByteFrameFromBytes(pkt.Data) + huntData := byteframe.NewByteFrame() + guild, err := GetGuildInfoByCharacterId(s, s.charID) + if err != nil { + panic(err) + } + guildCats := getGuildAirouList(s) + destination := bf.ReadUint32() + level := bf.ReadUint32() + huntData.WriteUint32(s.charID) + huntData.WriteBytes(stringsupport.PaddedString(s.Name, 18, true)) + for i := 0; i < 5; i++ { + catID := bf.ReadUint32() + huntData.WriteUint32(catID) + if catID > 0 { + for _, cat := range guildCats { + if cat.CatID == catID { + huntData.WriteBytes(cat.CatName) + break + } + } + huntData.WriteBytes(bf.ReadBytes(9)) + } + } + _, err = s.server.db.Exec("INSERT INTO guild_hunts (guild_id, host_id, destination, level, return, hunt_data) VALUES ($1, $2, $3, $4, $5, $6)", + guild.ID, s.charID, destination, level, Time_Current_Adjusted().Unix(), huntData.Data()) + if err != nil { + panic(err) + } 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) + } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } +func treasureHuntUnregister(s *Session) { + guild, err := GetGuildInfoByCharacterId(s, s.charID) + if err != nil { + panic(err) + } + var huntID int + var hunters string + rows, _ := s.server.db.Queryx("SELECT id, hunters FROM guild_hunts WHERE guild_id=$1", guild.ID) + for rows.Next() { + rows.Scan(&huntID, &hunters) + hunters = stringsupport.CSVRemove(hunters, int(s.charID)) + _, err = s.server.db.Exec("UPDATE guild_hunts SET hunters=$1 WHERE id=$2", hunters, huntID) + if err != nil { + panic(err) + } + } +} + func handleMsgMhfOperateGuildTresureReport(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfOperateGuildTresureReport) + var csv string + if pkt.State == 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) + } + 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) + } + } + } else if pkt.State == 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 + err := s.server.db.QueryRow("SELECT treasure FROM guild_hunts WHERE id=$1", pkt.HuntID).Scan(&csv) + if err != nil { + panic(err) + } + 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) + } + } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) }