mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-15 08:25:09 +01:00
Merge branch 'main' into feature/diva
# Conflicts: # server/channelserver/handlers_quest.go
This commit is contained in:
@@ -6,7 +6,6 @@ import (
|
||||
"erupe-ce/common/stringsupport"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -74,26 +73,16 @@ func doAckSimpleFail(s *Session, ackHandle uint32, data []byte) {
|
||||
}
|
||||
|
||||
func updateRights(s *Session) {
|
||||
s.rights = uint32(0x0E)
|
||||
s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.charID).Scan(&s.rights)
|
||||
|
||||
rights := make([]mhfpacket.ClientRight, 0)
|
||||
tempRights := s.rights
|
||||
for i := 30; i > 0; i-- {
|
||||
right := uint32(math.Pow(2, float64(i)))
|
||||
if tempRights-right < 0x80000000 {
|
||||
if i == 1 {
|
||||
continue
|
||||
}
|
||||
rights = append(rights, mhfpacket.ClientRight{ID: uint16(i), Timestamp: 0x70DB59F0})
|
||||
tempRights -= right
|
||||
}
|
||||
rightsInt := uint32(0x0E)
|
||||
s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.charID).Scan(&rightsInt)
|
||||
s.courses = mhfpacket.GetCourseStruct(rightsInt)
|
||||
rights := []mhfpacket.ClientRight{{1, 0, 0}}
|
||||
for _, course := range s.courses {
|
||||
rights = append(rights, mhfpacket.ClientRight{ID: course.ID, Timestamp: 0x70DB59F0})
|
||||
}
|
||||
rights = append(rights, mhfpacket.ClientRight{ID: 1, Timestamp: 0})
|
||||
|
||||
update := &mhfpacket.MsgSysUpdateRight{
|
||||
ClientRespAckHandle: 0,
|
||||
Bitfield: s.rights,
|
||||
Bitfield: rightsInt,
|
||||
Rights: rights,
|
||||
UnkSize: 0,
|
||||
}
|
||||
@@ -190,6 +179,16 @@ func logoutPlayer(s *Session) {
|
||||
s.server.Unlock()
|
||||
|
||||
for _, stage := range s.server.stages {
|
||||
// Tell sessions registered to disconnecting players quest to unregister
|
||||
if stage.host != nil && stage.host.charID == s.charID {
|
||||
for _, sess := range s.server.sessions {
|
||||
for rSlot := range stage.reservedClientSlots {
|
||||
if sess.charID == rSlot && sess.stage != nil && sess.stage.id[3:5] != "Qs" {
|
||||
sess.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for session := range stage.clients {
|
||||
if session.charID == s.charID {
|
||||
delete(stage.clients, session)
|
||||
@@ -214,7 +213,7 @@ func logoutPlayer(s *Session) {
|
||||
timePlayed += sessionTime
|
||||
|
||||
var rpGained int
|
||||
if s.rights >= 0x40000000 { // N Course
|
||||
if s.FindCourse("Netcafe").ID != 0 {
|
||||
rpGained = timePlayed / 900
|
||||
timePlayed = timePlayed % 900
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"erupe-ce/common/byteframe"
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"io"
|
||||
"time"
|
||||
@@ -71,15 +72,22 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetCafeDuration)
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
var cafeReset time.Time
|
||||
err := s.server.db.QueryRow(`SELECT cafe_reset FROM characters WHERE id=$1`, s.charID).Scan(&cafeReset)
|
||||
if Time_Current_Adjusted().After(cafeReset) {
|
||||
cafeReset = TimeWeekNext()
|
||||
s.server.db.Exec(`UPDATE characters SET cafe_time=0, cafe_reset=$1 WHERE id=$2; DELETE FROM cafe_accepted WHERE character_id=$2`, cafeReset, s.charID)
|
||||
}
|
||||
|
||||
var cafeTime uint32
|
||||
err := s.server.db.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.charID).Scan(&cafeTime)
|
||||
err = s.server.db.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.charID).Scan(&cafeTime)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime
|
||||
bf.WriteUint32(cafeTime) // Total cafe time
|
||||
bf.WriteUint16(0)
|
||||
ps.Uint16(bf, "Resets at next maintenance", true)
|
||||
ps.Uint16(bf, fmt.Sprintf("Resets on %s %d", cafeReset.Month().String(), cafeReset.Day()), true)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"erupe-ce/network/binpacket"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"fmt"
|
||||
"golang.org/x/exp/slices"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strings"
|
||||
@@ -19,6 +20,7 @@ import (
|
||||
const (
|
||||
BinaryMessageTypeState = 0
|
||||
BinaryMessageTypeChat = 1
|
||||
BinaryMessageTypeQuest = 2
|
||||
BinaryMessageTypeData = 3
|
||||
BinaryMessageTypeMailNotify = 4
|
||||
BinaryMessageTypeEmote = 6
|
||||
@@ -89,6 +91,18 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
|
||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools == true && s.server.erupeConfig.DevMode {
|
||||
if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x02 && len(pkt.RawDataPayload) > 32 {
|
||||
// This is only correct most of the time
|
||||
tmp.ReadBytes(20)
|
||||
tmp.SetLE()
|
||||
x := tmp.ReadFloat32()
|
||||
y := tmp.ReadFloat32()
|
||||
z := tmp.ReadFloat32()
|
||||
s.logger.Debug("Coord", zap.Float32s("XYZ", []float32{x, y, z}))
|
||||
}
|
||||
}
|
||||
|
||||
// Parse out the real casted binary payload
|
||||
var msgBinTargeted *binpacket.MsgBinTargeted
|
||||
var authorLen, msgLen uint16
|
||||
@@ -298,6 +312,53 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(chatMessage.Message, commands["Course"].Prefix) {
|
||||
if commands["Course"].Enabled {
|
||||
var name string
|
||||
n, err := fmt.Sscanf(chatMessage.Message, "!course %s", &name)
|
||||
if err != nil || n != 1 {
|
||||
sendServerChatMessage(s, "Error in command. Format: !course <name>")
|
||||
} else {
|
||||
name = strings.ToLower(name)
|
||||
for _, course := range mhfpacket.Courses() {
|
||||
for _, alias := range course.Aliases {
|
||||
if strings.ToLower(name) == strings.ToLower(alias) {
|
||||
if slices.Contains(s.server.erupeConfig.Courses, config.Course{Name: course.Aliases[0], Enabled: true}) {
|
||||
if s.FindCourse(name).Value != 0 {
|
||||
ei := slices.IndexFunc(s.courses, func(c mhfpacket.Course) bool {
|
||||
for _, alias := range c.Aliases {
|
||||
if strings.ToLower(name) == strings.ToLower(alias) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
if ei != -1 {
|
||||
s.courses = append(s.courses[:ei], s.courses[ei+1:]...)
|
||||
sendServerChatMessage(s, fmt.Sprintf(`%s Course disabled.`, course.Aliases[0]))
|
||||
}
|
||||
} else {
|
||||
s.courses = append(s.courses, course)
|
||||
sendServerChatMessage(s, fmt.Sprintf(`%s Course enabled.`, course.Aliases[0]))
|
||||
}
|
||||
var newInt uint32
|
||||
for _, course := range s.courses {
|
||||
newInt += course.Value
|
||||
}
|
||||
s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", newInt, s.charID)
|
||||
updateRights(s)
|
||||
} else {
|
||||
sendServerChatMessage(s, fmt.Sprintf(`%s Course is locked.`, course.Aliases[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sendDisabledCommandMessage(s, commands["Course"])
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(chatMessage.Message, commands["Raviente"].Prefix) {
|
||||
if commands["Raviente"].Enabled {
|
||||
if getRaviSemaphore(s) != "" {
|
||||
|
||||
@@ -2,6 +2,7 @@ package channelserver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"erupe-ce/common/bfutil"
|
||||
"erupe-ce/common/stringsupport"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -9,7 +10,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"erupe-ce/common/bfutil"
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/server/channelserver/compression/deltacomp"
|
||||
@@ -49,7 +49,7 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
|
||||
characterSaveData.Save(s)
|
||||
s.logger.Info("Wrote recompressed savedata back to DB.")
|
||||
|
||||
characterSaveData.Name = s.clientContext.StrConv.MustDecode(bfutil.UpToNull(characterSaveData.decompSave[88:100]))
|
||||
characterSaveData.Name = stringsupport.SJISToUTF8(bfutil.UpToNull(characterSaveData.decompSave[88:100]))
|
||||
_, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterSaveData.Name, s.charID)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update character name in db", zap.Error(err))
|
||||
|
||||
@@ -53,38 +53,46 @@ func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
type activeFeature struct {
|
||||
StartTime time.Time
|
||||
ActiveFeatures uint32
|
||||
Unk1 uint16
|
||||
StartTime time.Time `db:"start_time"`
|
||||
ActiveFeatures uint32 `db:"featured"`
|
||||
}
|
||||
|
||||
func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetWeeklySchedule)
|
||||
persistentEventSchedule := make([]activeFeature, 8) // generate day after weekly restart
|
||||
for x := -1; x < 7; x++ {
|
||||
feat := generateActiveWeapons(14) // number of active weapons
|
||||
// TODO: only generate this once per restart (server should be restarted weekly)
|
||||
// then load data from db instead of regenerating
|
||||
persistentEventSchedule[x+1] = activeFeature{
|
||||
StartTime: Time_Current_Midnight().Add(time.Duration(24*x) * time.Hour),
|
||||
ActiveFeatures: uint32(feat),
|
||||
Unk1: 0,
|
||||
|
||||
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())
|
||||
for rows.Next() {
|
||||
var feature activeFeature
|
||||
rows.StructScan(&feature)
|
||||
features = append(features, feature)
|
||||
}
|
||||
|
||||
if len(features) < 2 {
|
||||
if len(features) == 0 {
|
||||
feature := generateFeatureWeapons(s.server.erupeConfig.FeaturedWeapons)
|
||||
feature.StartTime = Time_Current_Midnight().Add(-24 * time.Hour)
|
||||
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()
|
||||
features = append(features, feature)
|
||||
s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, feature.StartTime, feature.ActiveFeatures)
|
||||
}
|
||||
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint8(uint8(len(persistentEventSchedule))) // Entry count, client only parses the first 7 or 8.
|
||||
resp.WriteUint32(uint32(Time_Current_Adjusted().Add(-5 * time.Minute).Unix())) // 5 minutes ago server time
|
||||
|
||||
for _, es := range persistentEventSchedule {
|
||||
resp.WriteUint32(uint32(es.StartTime.Unix()))
|
||||
resp.WriteUint32(es.ActiveFeatures)
|
||||
resp.WriteUint16(es.Unk1)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(2)
|
||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Add(-5 * time.Minute).Unix()))
|
||||
for _, feature := range features {
|
||||
bf.WriteUint32(uint32(feature.StartTime.Unix()))
|
||||
bf.WriteUint32(feature.ActiveFeatures)
|
||||
bf.WriteUint16(0)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func generateActiveWeapons(count int) int {
|
||||
func generateFeatureWeapons(count int) activeFeature {
|
||||
nums := make([]int, 0)
|
||||
var result int
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
@@ -104,7 +112,7 @@ func generateActiveWeapons(count int) int {
|
||||
for _, num := range nums {
|
||||
result += int(math.Pow(2, float64(num)))
|
||||
}
|
||||
return result
|
||||
return activeFeature{ActiveFeatures: uint32(result)}
|
||||
}
|
||||
|
||||
type loginBoost struct {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -253,11 +254,19 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
var souls uint32
|
||||
var souls, exists uint32
|
||||
s.server.db.QueryRow("SELECT souls FROM guild_characters WHERE character_id=$1", s.charID).Scan(&souls)
|
||||
err = s.server.db.QueryRow("SELECT prize_id FROM festa_prizes_accepted WHERE prize_id=0 AND character_id=$1", s.charID).Scan(&exists)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(souls)
|
||||
bf.WriteUint32(0) // unk
|
||||
if err != nil {
|
||||
bf.WriteBool(true)
|
||||
bf.WriteBool(false)
|
||||
} else {
|
||||
bf.WriteBool(false)
|
||||
bf.WriteBool(true)
|
||||
}
|
||||
bf.WriteUint16(0) // Unk
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
@@ -280,10 +289,10 @@ func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) {
|
||||
return
|
||||
}
|
||||
resp.WriteUint32(guild.Souls)
|
||||
resp.WriteUint32(0) // unk
|
||||
resp.WriteUint32(0) // unk, rank?
|
||||
resp.WriteUint32(0) // unk
|
||||
resp.WriteUint32(0) // unk
|
||||
resp.WriteUint32(1) // unk
|
||||
resp.WriteUint32(1) // unk
|
||||
resp.WriteUint32(1) // unk, rank?
|
||||
resp.WriteUint32(1) // unk
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
@@ -302,6 +311,9 @@ func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(uint16(len(members)))
|
||||
bf.WriteUint16(0) // Unk
|
||||
sort.Slice(members, func(i, j int) bool {
|
||||
return members[i].Souls > members[j].Souls
|
||||
})
|
||||
for _, member := range members {
|
||||
bf.WriteUint32(member.CharID)
|
||||
bf.WriteUint32(member.Souls)
|
||||
@@ -342,6 +354,7 @@ func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
func handleMsgMhfAcquireFesta(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfAcquireFesta)
|
||||
s.server.db.Exec("INSERT INTO public.festa_prizes_accepted VALUES (0, $1)", s.charID)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
@@ -368,7 +381,7 @@ type Prize struct {
|
||||
|
||||
func handleMsgMhfEnumerateFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateFestaPersonalPrize)
|
||||
rows, _ := s.server.db.Queryx("SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = 4) AS claimed FROM festa_prizes fp WHERE type='personal'")
|
||||
rows, _ := s.server.db.Queryx(`SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = $1) AS claimed FROM festa_prizes fp WHERE type='personal'`, s.charID)
|
||||
var count uint32
|
||||
prizeData := byteframe.NewByteFrame()
|
||||
for rows.Next() {
|
||||
@@ -394,7 +407,7 @@ func handleMsgMhfEnumerateFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket)
|
||||
|
||||
func handleMsgMhfEnumerateFestaIntermediatePrize(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateFestaIntermediatePrize)
|
||||
rows, _ := s.server.db.Queryx("SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = 4) AS claimed FROM festa_prizes fp WHERE type='guild'")
|
||||
rows, _ := s.server.db.Queryx(`SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = $1) AS claimed FROM festa_prizes fp WHERE type='guild'`, s.charID)
|
||||
var count uint32
|
||||
prizeData := byteframe.NewByteFrame()
|
||||
for rows.Next() {
|
||||
|
||||
@@ -1034,7 +1034,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
applicants, err := GetGuildMembers(s, guild.ID, true)
|
||||
if err != nil {
|
||||
if err != nil || (characterGuildData != nil && !characterGuildData.CanRecruit()) {
|
||||
bf.WriteUint16(0)
|
||||
} else {
|
||||
bf.WriteUint16(uint16(len(applicants)))
|
||||
@@ -1101,9 +1101,9 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
switch pkt.Type {
|
||||
case mhfpacket.ENUMERATE_GUILD_TYPE_GUILD_NAME:
|
||||
bf.ReadBytes(10)
|
||||
bf.ReadBytes(8)
|
||||
searchTerm := fmt.Sprintf(`%%%s%%`, stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()))
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE g.name ILIKE $1 OFFSET $2`, guildInfoSelectQuery), searchTerm, pkt.Page*10)
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE g.name ILIKE $1 OFFSET $2 LIMIT 11`, guildInfoSelectQuery), searchTerm, pkt.Page*10)
|
||||
if err == nil {
|
||||
for rows.Next() {
|
||||
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||
@@ -1111,9 +1111,9 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_NAME:
|
||||
bf.ReadBytes(10)
|
||||
bf.ReadBytes(8)
|
||||
searchTerm := fmt.Sprintf(`%%%s%%`, stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()))
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE lc.name ILIKE $1 OFFSET $2`, guildInfoSelectQuery), searchTerm, pkt.Page*10)
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE lc.name ILIKE $1 OFFSET $2 LIMIT 11`, guildInfoSelectQuery), searchTerm, pkt.Page*10)
|
||||
if err == nil {
|
||||
for rows.Next() {
|
||||
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||
@@ -1121,7 +1121,6 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_ID:
|
||||
bf.ReadBytes(2)
|
||||
ID := bf.ReadUint32()
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE leader_id = $1`, guildInfoSelectQuery), ID)
|
||||
if err == nil {
|
||||
@@ -1131,11 +1130,10 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_MEMBERS:
|
||||
sorting := bf.ReadUint8()
|
||||
if sorting == 1 {
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||
if pkt.Sorting {
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
|
||||
} else {
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count ASC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
|
||||
}
|
||||
if err == nil {
|
||||
for rows.Next() {
|
||||
@@ -1144,11 +1142,10 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_REGISTRATION:
|
||||
sorting := bf.ReadUint8()
|
||||
if sorting == 1 {
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||
if pkt.Sorting {
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id ASC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
|
||||
} else {
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
|
||||
}
|
||||
if err == nil {
|
||||
for rows.Next() {
|
||||
@@ -1157,11 +1154,10 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_RANK:
|
||||
sorting := bf.ReadUint8()
|
||||
if sorting == 1 {
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||
if pkt.Sorting {
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
|
||||
} else {
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp ASC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
|
||||
}
|
||||
if err == nil {
|
||||
for rows.Next() {
|
||||
@@ -1170,10 +1166,9 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
case mhfpacket.ENUMERATE_GUILD_TYPE_MOTTO:
|
||||
bf.ReadBytes(2)
|
||||
mainMotto := bf.ReadUint16()
|
||||
subMotto := bf.ReadUint16()
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE main_motto = $1 AND sub_motto = $2 OFFSET $3`, guildInfoSelectQuery), mainMotto, subMotto, pkt.Page*10)
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE main_motto = $1 AND sub_motto = $2 OFFSET $3 LIMIT 11`, guildInfoSelectQuery), mainMotto, subMotto, pkt.Page*10)
|
||||
if err == nil {
|
||||
for rows.Next() {
|
||||
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||
@@ -1182,7 +1177,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
case mhfpacket.ENUMERATE_GUILD_TYPE_RECRUITING:
|
||||
// Assume the player wants the newest guilds with open recruitment
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE recruiting=true ORDER BY id DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE recruiting=true ORDER BY id DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
|
||||
if err == nil {
|
||||
for rows.Next() {
|
||||
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||
@@ -1202,7 +1197,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
switch pkt.Type {
|
||||
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME:
|
||||
bf.ReadBytes(10)
|
||||
bf.ReadBytes(8)
|
||||
searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
|
||||
for _, alliance := range tempAlliances {
|
||||
if strings.Contains(alliance.Name, searchTerm) {
|
||||
@@ -1210,7 +1205,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME:
|
||||
bf.ReadBytes(10)
|
||||
bf.ReadBytes(8)
|
||||
searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
|
||||
for _, alliance := range tempAlliances {
|
||||
if strings.Contains(alliance.ParentGuild.LeaderName, searchTerm) {
|
||||
@@ -1218,7 +1213,6 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_ID:
|
||||
bf.ReadBytes(2)
|
||||
ID := bf.ReadUint32()
|
||||
for _, alliance := range tempAlliances {
|
||||
if alliance.ParentGuild.LeaderCharID == ID {
|
||||
@@ -1226,8 +1220,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS:
|
||||
sorting := bf.ReadBool()
|
||||
if sorting {
|
||||
if pkt.Sorting {
|
||||
sort.Slice(tempAlliances, func(i, j int) bool {
|
||||
return tempAlliances[i].TotalMembers > tempAlliances[j].TotalMembers
|
||||
})
|
||||
@@ -1238,8 +1231,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
alliances = tempAlliances
|
||||
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION:
|
||||
sorting := bf.ReadBool()
|
||||
if sorting {
|
||||
if pkt.Sorting {
|
||||
sort.Slice(tempAlliances, func(i, j int) bool {
|
||||
return tempAlliances[i].CreatedAt.Unix() > tempAlliances[j].CreatedAt.Unix()
|
||||
})
|
||||
@@ -1260,16 +1252,14 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf = byteframe.NewByteFrame()
|
||||
|
||||
if pkt.Type > 8 {
|
||||
if len(guilds) > 10 {
|
||||
bf.WriteUint16(10)
|
||||
} else {
|
||||
bf.WriteUint16(uint16(len(alliances)))
|
||||
hasNextPage := false
|
||||
if len(alliances) > 10 {
|
||||
hasNextPage = true
|
||||
alliances = alliances[:10]
|
||||
}
|
||||
bf.WriteUint8(0x00) // Unk
|
||||
for i, alliance := range alliances {
|
||||
if i == 10 {
|
||||
break
|
||||
}
|
||||
bf.WriteUint16(uint16(len(alliances)))
|
||||
bf.WriteBool(hasNextPage)
|
||||
for _, alliance := range alliances {
|
||||
bf.WriteUint32(alliance.ID)
|
||||
bf.WriteUint32(alliance.ParentGuild.LeaderCharID)
|
||||
bf.WriteUint16(alliance.TotalMembers)
|
||||
@@ -1288,16 +1278,14 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteBool(true) // TODO: Enable GuildAlliance applications
|
||||
}
|
||||
} else {
|
||||
hasNextPage := false
|
||||
if len(guilds) > 10 {
|
||||
bf.WriteUint16(10)
|
||||
} else {
|
||||
bf.WriteUint16(uint16(len(guilds)))
|
||||
hasNextPage = true
|
||||
guilds = guilds[:10]
|
||||
}
|
||||
bf.WriteUint8(0x01) // Unk
|
||||
for i, guild := range guilds {
|
||||
if i == 10 {
|
||||
break
|
||||
}
|
||||
bf.WriteUint16(uint16(len(guilds)))
|
||||
bf.WriteBool(hasNextPage)
|
||||
for _, guild := range guilds {
|
||||
bf.WriteUint32(guild.ID)
|
||||
bf.WriteUint32(guild.LeaderCharID)
|
||||
bf.WriteUint16(guild.MemberCount)
|
||||
|
||||
@@ -2,7 +2,7 @@ package channelserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"go.uber.org/zap"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -14,14 +14,24 @@ import (
|
||||
func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysGetFile)
|
||||
|
||||
// Debug print the request.
|
||||
if pkt.IsScenario {
|
||||
fmt.Printf("%+v\n", pkt.ScenarioIdentifer)
|
||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools && s.server.erupeConfig.DevMode {
|
||||
s.logger.Debug(
|
||||
"Scenario",
|
||||
zap.Uint8("CategoryID", pkt.ScenarioIdentifer.CategoryID),
|
||||
zap.Uint32("MainID", pkt.ScenarioIdentifer.MainID),
|
||||
zap.Uint8("ChapterID", pkt.ScenarioIdentifer.ChapterID),
|
||||
zap.Uint8("Flags", pkt.ScenarioIdentifer.Flags),
|
||||
)
|
||||
}
|
||||
filename := fmt.Sprintf("%d_0_0_0_S%d_T%d_C%d", pkt.ScenarioIdentifer.CategoryID, pkt.ScenarioIdentifer.MainID, pkt.ScenarioIdentifer.Flags, pkt.ScenarioIdentifer.ChapterID)
|
||||
// Read the scenario file.
|
||||
data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("scenarios/%s.bin", filename)))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
s.logger.Error(fmt.Sprintf("Failed to open file: %s/scenarios/%s.bin", s.server.erupeConfig.BinPath, filename))
|
||||
// This will crash the game.
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
return
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
} else {
|
||||
@@ -32,10 +42,19 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
} else {
|
||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools && s.server.erupeConfig.DevMode {
|
||||
s.logger.Debug(
|
||||
"Quest",
|
||||
zap.String("Filename", pkt.Filename),
|
||||
)
|
||||
}
|
||||
// Get quest file.
|
||||
data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", pkt.Filename)))
|
||||
if err != nil {
|
||||
s.logger.Fatal(fmt.Sprintf("Failed to open quest file: quests/%s.bin", pkt.Filename))
|
||||
s.logger.Error(fmt.Sprintf("Failed to open file: %s/quests/%s.bin", s.server.erupeConfig.BinPath, pkt.Filename))
|
||||
// This will crash the game.
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
return
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package channelserver
|
||||
import (
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"fmt"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
@@ -96,20 +97,13 @@ 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
|
||||
const rengokuScoreQuery = `, c.name 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
|
||||
`
|
||||
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"`
|
||||
Name string `db:"name"`
|
||||
Score uint32 `db:"score"`
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -121,170 +115,64 @@ func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
guild = nil
|
||||
}
|
||||
|
||||
if pkt.Leaderboard == 2 || pkt.Leaderboard == 3 || pkt.Leaderboard == 6 || pkt.Leaderboard == 7 {
|
||||
if guild == nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 11))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var score RengokuScore
|
||||
var selfExist bool
|
||||
i := uint32(1)
|
||||
bf := byteframe.NewByteFrame()
|
||||
scoreData := byteframe.NewByteFrame()
|
||||
|
||||
var rows *sqlx.Rows
|
||||
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))
|
||||
}
|
||||
case 0:
|
||||
rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_mp AS score %s ORDER BY max_stages_mp DESC", rengokuScoreQuery))
|
||||
case 1:
|
||||
rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_mp AS score %s ORDER BY max_points_mp DESC", rengokuScoreQuery))
|
||||
case 2:
|
||||
rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_mp AS score %s WHERE guild_id=$1 ORDER BY max_stages_mp DESC", rengokuScoreQuery), guild.ID)
|
||||
case 3:
|
||||
rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_mp AS score %s WHERE guild_id=$1 ORDER BY max_points_mp DESC", rengokuScoreQuery), guild.ID)
|
||||
case 4:
|
||||
rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_sp AS score %s ORDER BY max_stages_sp DESC", rengokuScoreQuery))
|
||||
case 5:
|
||||
rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_sp AS score %s ORDER BY max_points_sp DESC", rengokuScoreQuery))
|
||||
case 6:
|
||||
rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_sp AS score %s WHERE guild_id=$1 ORDER BY max_stages_sp DESC", rengokuScoreQuery), guild.ID)
|
||||
case 7:
|
||||
rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_sp AS score %s WHERE guild_id=$1 ORDER BY max_points_sp DESC", rengokuScoreQuery), guild.ID)
|
||||
}
|
||||
if i == 1 {
|
||||
bf.WriteUint32(1)
|
||||
bf.WriteUint32(0)
|
||||
ps.Uint8(bf, s.Name, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
bf.WriteUint8(1)
|
||||
bf.WriteUint32(1)
|
||||
bf.WriteUint32(0)
|
||||
ps.Uint8(bf, s.Name, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
} else {
|
||||
bf.WriteUint8(uint8(i) - 1)
|
||||
bf.WriteBytes(scoreData.Data())
|
||||
|
||||
for rows.Next() {
|
||||
rows.StructScan(&score)
|
||||
if score.Name == s.Name {
|
||||
bf.WriteUint32(i)
|
||||
bf.WriteUint32(score.Score)
|
||||
ps.Uint8(bf, s.Name, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
selfExist = true
|
||||
}
|
||||
if i > 100 {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
scoreData.WriteUint32(i)
|
||||
scoreData.WriteUint32(score.Score)
|
||||
ps.Uint8(scoreData, score.Name, true)
|
||||
ps.Uint8(scoreData, "", false)
|
||||
i++
|
||||
}
|
||||
|
||||
if !selfExist {
|
||||
bf.WriteBytes(make([]byte, 10))
|
||||
}
|
||||
bf.WriteUint8(uint8(i) - 1)
|
||||
bf.WriteBytes(scoreData.Data())
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
} else {
|
||||
stage := NewStage(pkt.StageID)
|
||||
stage.host = s
|
||||
stage.maxPlayers = uint16(pkt.PlayerCount)
|
||||
s.server.stages[stage.id] = stage
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
@@ -42,6 +43,7 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
|
||||
stage = s.server.stages[stageID]
|
||||
s.server.Unlock()
|
||||
stage.Lock()
|
||||
stage.host = s
|
||||
stage.clients[s] = s.charID
|
||||
stage.Unlock()
|
||||
}
|
||||
|
||||
@@ -6,17 +6,16 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/common/stringstack"
|
||||
"erupe-ce/common/stringsupport"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
)
|
||||
|
||||
type packet struct {
|
||||
@@ -43,7 +42,7 @@ type Session struct {
|
||||
charID uint32
|
||||
logKey []byte
|
||||
sessionStart int64
|
||||
rights uint32
|
||||
courses []mhfpacket.Course
|
||||
token string
|
||||
kqf []byte
|
||||
kqfOverride bool
|
||||
@@ -68,16 +67,12 @@ type Session struct {
|
||||
// NewSession creates a new Session type.
|
||||
func NewSession(server *Server, conn net.Conn) *Session {
|
||||
s := &Session{
|
||||
logger: server.logger.Named(conn.RemoteAddr().String()),
|
||||
server: server,
|
||||
rawConn: conn,
|
||||
cryptConn: network.NewCryptConn(conn),
|
||||
sendPackets: make(chan packet, 20),
|
||||
clientContext: &clientctx.ClientContext{
|
||||
StrConv: &stringsupport.StringConverter{
|
||||
Encoding: japanese.ShiftJIS,
|
||||
},
|
||||
},
|
||||
logger: server.logger.Named(conn.RemoteAddr().String()),
|
||||
server: server,
|
||||
rawConn: conn,
|
||||
cryptConn: network.NewCryptConn(conn),
|
||||
sendPackets: make(chan packet, 20),
|
||||
clientContext: &clientctx.ClientContext{}, // Unused
|
||||
sessionStart: Time_Current_Adjusted().Unix(),
|
||||
stageMoveStack: stringstack.New(),
|
||||
}
|
||||
@@ -268,3 +263,14 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien
|
||||
fmt.Printf("Data [%d bytes]:\n(Too long!)\n\n", len(data))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) FindCourse(name string) mhfpacket.Course {
|
||||
for _, course := range s.courses {
|
||||
for _, alias := range course.Aliases {
|
||||
if strings.ToLower(name) == strings.ToLower(alias) {
|
||||
return course
|
||||
}
|
||||
}
|
||||
}
|
||||
return mhfpacket.Course{}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ type Stage struct {
|
||||
// other clients expect the server to echo them back in the exact same format.
|
||||
rawBinaryData map[stageBinaryKey][]byte
|
||||
|
||||
host *Session
|
||||
maxPlayers uint16
|
||||
password string
|
||||
createdAt string
|
||||
|
||||
@@ -90,6 +90,7 @@ func (s *Server) acceptClients() {
|
||||
}
|
||||
|
||||
func (s *Server) handleEntranceServerConnection(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
|
||||
nullInit := make([]byte, 8)
|
||||
n, err := io.ReadFull(conn, nullInit)
|
||||
@@ -118,5 +119,4 @@ func (s *Server) handleEntranceServerConnection(conn net.Conn) {
|
||||
cc.SendPacket(data)
|
||||
// Close because we only need to send the response once.
|
||||
// Any further requests from the client will come from a new connection.
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ func (s *Server) getFriendsForCharacters(chars []character) []members {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for i, _ := range charFriends {
|
||||
for i := range charFriends {
|
||||
charFriends[i].CID = char.ID
|
||||
}
|
||||
friends = append(friends, charFriends...)
|
||||
|
||||
@@ -42,13 +42,23 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
bf.WriteUint8(1) // resp_code
|
||||
bf.WriteUint8(0) // file/patch server count
|
||||
bf.WriteUint8(1) // resp_code
|
||||
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.PatchServerManifest != "" && s.server.erupeConfig.DevModeOptions.PatchServerFile != "" {
|
||||
bf.WriteUint8(2)
|
||||
} else {
|
||||
bf.WriteUint8(0)
|
||||
}
|
||||
bf.WriteUint8(1) // entrance server count
|
||||
bf.WriteUint8(uint8(len(chars))) // character count
|
||||
bf.WriteUint32(0xFFFFFFFF) // login_token_number
|
||||
bf.WriteBytes([]byte(token)) // login_token
|
||||
bf.WriteUint32(uint32(time.Now().Unix())) // current time
|
||||
if s.server.erupeConfig.DevMode {
|
||||
if s.server.erupeConfig.DevModeOptions.PatchServerManifest != "" && s.server.erupeConfig.DevModeOptions.PatchServerFile != "" {
|
||||
ps.Uint8(bf, s.server.erupeConfig.DevModeOptions.PatchServerManifest, false)
|
||||
ps.Uint8(bf, s.server.erupeConfig.DevModeOptions.PatchServerFile, false)
|
||||
}
|
||||
}
|
||||
ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.Host, s.server.erupeConfig.Entrance.Port), false)
|
||||
|
||||
lastPlayed := uint32(0)
|
||||
|
||||
@@ -16,32 +16,19 @@ import (
|
||||
type Session struct {
|
||||
sync.Mutex
|
||||
logger *zap.Logger
|
||||
sid int
|
||||
server *Server
|
||||
rawConn *net.Conn
|
||||
rawConn net.Conn
|
||||
cryptConn *network.CryptConn
|
||||
}
|
||||
|
||||
func (s *Session) fail() {
|
||||
s.server.Lock()
|
||||
delete(s.server.sessions, s.sid)
|
||||
s.server.Unlock()
|
||||
|
||||
}
|
||||
|
||||
func (s *Session) work() {
|
||||
for {
|
||||
pkt, err := s.cryptConn.ReadPacket()
|
||||
if err != nil {
|
||||
s.fail()
|
||||
return
|
||||
}
|
||||
|
||||
err = s.handlePacket(pkt)
|
||||
if err != nil {
|
||||
s.fail()
|
||||
return
|
||||
}
|
||||
pkt, err := s.cryptConn.ReadPacket()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = s.handlePacket(pkt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +48,7 @@ func (s *Session) handlePacket(pkt []byte) error {
|
||||
case "DELETE:100":
|
||||
loginTokenString := string(bf.ReadNullTerminatedBytes())
|
||||
characterID := int(bf.ReadUint32())
|
||||
_ = int(bf.ReadUint32()) // login_token_number
|
||||
s.server.deleteCharacter(characterID, loginTokenString)
|
||||
sugar.Infof("Deleted character ID: %v\n", characterID)
|
||||
err := s.cryptConn.SendPacket([]byte{0x01}) // DEL_SUCCESS
|
||||
@@ -78,13 +66,13 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
|
||||
|
||||
reqUsername := string(bf.ReadNullTerminatedBytes())
|
||||
reqPassword := string(bf.ReadNullTerminatedBytes())
|
||||
reqUnk := string(bf.ReadNullTerminatedBytes())
|
||||
reqSkey := string(bf.ReadNullTerminatedBytes())
|
||||
|
||||
s.server.logger.Info(
|
||||
"Got sign in request",
|
||||
zap.String("reqUsername", reqUsername),
|
||||
zap.String("reqPassword", reqPassword),
|
||||
zap.String("reqUnk", reqUnk),
|
||||
zap.String("reqSkey", reqSkey),
|
||||
)
|
||||
|
||||
newCharaReq := false
|
||||
@@ -105,12 +93,15 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
|
||||
s.logger.Info("Account not found", zap.String("reqUsername", reqUsername))
|
||||
serverRespBytes = makeSignInFailureResp(SIGN_EAUTH)
|
||||
|
||||
// HACK(Andoryuuta): Create a new account if it doesn't exit.
|
||||
s.logger.Info("Creating account", zap.String("reqUsername", reqUsername), zap.String("reqPassword", reqPassword))
|
||||
err = s.server.registerDBAccount(reqUsername, reqPassword)
|
||||
if err != nil {
|
||||
s.logger.Info("Error on creating new account", zap.Error(err))
|
||||
serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
|
||||
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.AutoCreateAccount {
|
||||
s.logger.Info("Creating account", zap.String("reqUsername", reqUsername), zap.String("reqPassword", reqPassword))
|
||||
err = s.server.registerDBAccount(reqUsername, reqPassword)
|
||||
if err != nil {
|
||||
s.logger.Info("Error on creating new account", zap.Error(err))
|
||||
serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ type Server struct {
|
||||
sync.Mutex
|
||||
logger *zap.Logger
|
||||
erupeConfig *config.Config
|
||||
sid int
|
||||
sessions map[int]*Session
|
||||
db *sqlx.DB
|
||||
listener net.Listener
|
||||
@@ -36,8 +35,6 @@ func NewServer(config *Config) *Server {
|
||||
s := &Server{
|
||||
logger: config.Logger,
|
||||
erupeConfig: config.ErupeConfig,
|
||||
sid: 0,
|
||||
sessions: make(map[int]*Session),
|
||||
db: config.DB,
|
||||
}
|
||||
return s
|
||||
@@ -84,20 +81,19 @@ func (s *Server) acceptClients() {
|
||||
}
|
||||
}
|
||||
|
||||
go s.handleConnection(s.sid, conn)
|
||||
s.sid++
|
||||
go s.handleConnection(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleConnection(sid int, conn net.Conn) {
|
||||
func (s *Server) handleConnection(conn net.Conn) {
|
||||
s.logger.Info("Got connection to sign server", zap.String("remoteaddr", conn.RemoteAddr().String()))
|
||||
defer conn.Close()
|
||||
|
||||
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
|
||||
nullInit := make([]byte, 8)
|
||||
_, err := io.ReadFull(conn, nullInit)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
conn.Close()
|
||||
s.logger.Error("Error initialising sign server connection", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -105,15 +101,10 @@ func (s *Server) handleConnection(sid int, conn net.Conn) {
|
||||
session := &Session{
|
||||
logger: s.logger,
|
||||
server: s,
|
||||
rawConn: &conn,
|
||||
rawConn: conn,
|
||||
cryptConn: network.NewCryptConn(conn),
|
||||
}
|
||||
|
||||
// Add the session to the server's sessions map.
|
||||
s.Lock()
|
||||
s.sessions[sid] = session
|
||||
s.Unlock()
|
||||
|
||||
// Do the session's work.
|
||||
session.work()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user