mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-13 07:25:03 +01:00
road leaderboard initial implementation
This commit is contained in:
@@ -1,19 +1,19 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfEnumerateRengokuRanking represents the MSG_MHF_ENUMERATE_RENGOKU_RANKING
|
// MsgMhfEnumerateRengokuRanking represents the MSG_MHF_ENUMERATE_RENGOKU_RANKING
|
||||||
type MsgMhfEnumerateRengokuRanking struct {
|
type MsgMhfEnumerateRengokuRanking struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Unk0 uint32
|
Leaderboard uint32
|
||||||
Unk1 uint16 // Hardcoded 0 in the binary
|
Unk1 uint16 // Hardcoded 0 in the binary
|
||||||
Unk2 uint16 // Hardcoded 00 01 in the binary
|
Unk2 uint16 // Hardcoded 00 01 in the binary
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// 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
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfEnumerateRengokuRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfEnumerateRengokuRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Unk0 = bf.ReadUint32()
|
m.Leaderboard = bf.ReadUint32()
|
||||||
m.Unk1 = bf.ReadUint16()
|
m.Unk1 = bf.ReadUint16()
|
||||||
m.Unk2 = bf.ReadUint16()
|
m.Unk2 = bf.ReadUint16()
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
11
patch-schema/road-leaderboard.sql
Normal file
11
patch-schema/road-leaderboard.sql
Normal file
@@ -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;
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package channelserver
|
package channelserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
ps "erupe-ce/common/pascalstring"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
@@ -17,7 +19,19 @@ func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to update rengokudata savedata in db", zap.Error(err))
|
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})
|
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)
|
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) {
|
func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateRengokuRanking)
|
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) {
|
func handleMsgMhfGetRengokuRankingRank(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetRengokuRankingRank)
|
pkt := p.(*mhfpacket.MsgMhfGetRengokuRankingRank)
|
||||||
|
// What is this for?
|
||||||
resp := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
bf.WriteUint32(0) // Max stage overall MP rank
|
||||||
|
bf.WriteUint32(0) // Max RdP overall MP rank
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user