From 1ba3d5556669679461ef03a4f9980ba336b65dcb Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 03:53:28 +1000 Subject: [PATCH] road leaderboard initial implementation --- .../msg_mhf_enumerate_rengoku_ranking.go | 18 +- patch-schema/road-leaderboard.sql | 11 + server/channelserver/handlers_rengoku.go | 203 +++++++++++++++++- 3 files changed, 216 insertions(+), 16 deletions(-) create mode 100644 patch-schema/road-leaderboard.sql diff --git a/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go b/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go index d35bc7227..21817237d 100644 --- a/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go +++ b/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go @@ -1,19 +1,19 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfEnumerateRengokuRanking represents the MSG_MHF_ENUMERATE_RENGOKU_RANKING type MsgMhfEnumerateRengokuRanking struct { - AckHandle uint32 - Unk0 uint32 - Unk1 uint16 // Hardcoded 0 in the binary - Unk2 uint16 // Hardcoded 00 01 in the binary + AckHandle uint32 + Leaderboard uint32 + Unk1 uint16 // Hardcoded 0 in the binary + Unk2 uint16 // Hardcoded 00 01 in the binary } // Opcode returns the ID associated with this packet type. @@ -24,7 +24,7 @@ func (m *MsgMhfEnumerateRengokuRanking) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateRengokuRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint32() + m.Leaderboard = bf.ReadUint32() m.Unk1 = bf.ReadUint16() m.Unk2 = bf.ReadUint16() return nil diff --git a/patch-schema/road-leaderboard.sql b/patch-schema/road-leaderboard.sql new file mode 100644 index 000000000..8dfea2875 --- /dev/null +++ b/patch-schema/road-leaderboard.sql @@ -0,0 +1,11 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS rengoku_score ( + character_id integer PRIMARY KEY, + max_stages_mp integer, + max_points_mp integer, + max_stages_sp integer, + max_points_sp integer +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_rengoku.go b/server/channelserver/handlers_rengoku.go index d7b60686b..6fdce7c22 100644 --- a/server/channelserver/handlers_rengoku.go +++ b/server/channelserver/handlers_rengoku.go @@ -1,6 +1,8 @@ package channelserver import ( + ps "erupe-ce/common/pascalstring" + "fmt" "io/ioutil" "path/filepath" @@ -17,7 +19,19 @@ func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) { if err != nil { s.logger.Fatal("Failed to update rengokudata savedata in db", zap.Error(err)) } - + bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) + bf.Seek(71, 0) + maxStageMp := bf.ReadUint32() + maxScoreMp := bf.ReadUint32() + bf.Seek(4, 1) + maxStageSp := bf.ReadUint32() + maxScoreSp := bf.ReadUint32() + var t int + err = s.server.db.QueryRow("SELECT character_id FROM rengoku_score WHERE character_id=$1", s.charID).Scan(&t) + if err != nil { + s.server.db.Exec("INSERT INTO rengoku_score (character_id) VALUES ($1)", s.charID) + } + s.server.db.Exec("UPDATE rengoku_score SET max_stages_mp=$1, max_points_mp=$2, max_stages_sp=$3, max_points_sp=$4 WHERE character_id=$5", maxStageMp, maxScoreMp, maxStageSp, maxScoreSp, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } @@ -81,16 +95,191 @@ func handleMsgMhfGetRengokuBinary(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, data) } +const rengokuScoreQuery = ` +SELECT max_stages_mp, max_points_mp, max_stages_sp, max_points_sp, c.name, gc.guild_id +FROM rengoku_score rs +LEFT JOIN characters c ON c.id = rs.character_id +LEFT JOIN guild_characters gc ON gc.character_id = rs.character_id +` + +type RengokuScore struct { + Name string `db:"name"` + GuildID int `db:"guild_id"` + MaxStagesMP uint32 `db:"max_stages_mp"` + MaxPointsMP uint32 `db:"max_points_mp"` + MaxStagesSP uint32 `db:"max_stages_sp"` + MaxPointsSP uint32 `db:"max_points_sp"` +} + func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateRengokuRanking) - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + + guild, _ := GetGuildInfoByCharacterId(s, s.charID) + isApplicant, _ := guild.HasApplicationForCharID(s, s.charID) + if isApplicant { + guild = nil + } + + var score RengokuScore + i := uint32(1) + bf := byteframe.NewByteFrame() + scoreData := byteframe.NewByteFrame() + switch pkt.Leaderboard { + case 0: // Max stage overall MP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_mp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 1: // Max RdP overall MP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_mp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 2: // Max stage guild MP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_mp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + case 3: // Max RdP guild MP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_mp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + case 4: // Max stage overall SP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_sp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 5: // Max RdP overall SP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_sp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 6: // Max stage guild SP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_sp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + case 7: // Max RdP guild SP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_sp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + } + bf.WriteUint8(uint8(i) - 1) + bf.WriteBytes(scoreData.Data()) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfGetRengokuRankingRank(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetRengokuRankingRank) - - resp := byteframe.NewByteFrame() - resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + // What is this for? + bf := byteframe.NewByteFrame() + bf.WriteUint32(0) // Max stage overall MP rank + bf.WriteUint32(0) // Max RdP overall MP rank + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) }