mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
138 bare db.Exec calls across 22 handler files silently dropped write errors. Each is now wrapped with error check and zap logging. 4 QueryRow sites that legitimately return sql.ErrNoRows during normal operation (new player mezfes, festa rankings, empty guild item box) now filter it out to reduce log noise.
247 lines
7.8 KiB
Go
247 lines
7.8 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"erupe-ce/common/byteframe"
|
|
ps "erupe-ce/common/pascalstring"
|
|
"fmt"
|
|
"time"
|
|
|
|
"erupe-ce/network/mhfpacket"
|
|
"github.com/jmoiron/sqlx"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
const allianceInfoSelectQuery = `
|
|
SELECT
|
|
ga.id,
|
|
ga.name,
|
|
created_at,
|
|
parent_id,
|
|
CASE
|
|
WHEN sub1_id IS NULL THEN 0
|
|
ELSE sub1_id
|
|
END,
|
|
CASE
|
|
WHEN sub2_id IS NULL THEN 0
|
|
ELSE sub2_id
|
|
END
|
|
FROM guild_alliances ga
|
|
`
|
|
|
|
type GuildAlliance struct {
|
|
ID uint32 `db:"id"`
|
|
Name string `db:"name"`
|
|
CreatedAt time.Time `db:"created_at"`
|
|
TotalMembers uint16
|
|
|
|
ParentGuildID uint32 `db:"parent_id"`
|
|
SubGuild1ID uint32 `db:"sub1_id"`
|
|
SubGuild2ID uint32 `db:"sub2_id"`
|
|
|
|
ParentGuild Guild
|
|
SubGuild1 Guild
|
|
SubGuild2 Guild
|
|
}
|
|
|
|
func GetAllianceData(s *Session, AllianceID uint32) (*GuildAlliance, error) {
|
|
rows, err := s.server.db.Queryx(fmt.Sprintf(`
|
|
%s
|
|
WHERE ga.id = $1
|
|
`, allianceInfoSelectQuery), AllianceID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to retrieve alliance data from database", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
hasRow := rows.Next()
|
|
if !hasRow {
|
|
return nil, nil
|
|
}
|
|
|
|
return buildAllianceObjectFromDbResult(rows, err, s)
|
|
}
|
|
|
|
func buildAllianceObjectFromDbResult(result *sqlx.Rows, _ error, s *Session) (*GuildAlliance, error) {
|
|
alliance := &GuildAlliance{}
|
|
|
|
err := result.StructScan(alliance)
|
|
|
|
if err != nil {
|
|
s.logger.Error("failed to retrieve alliance from database", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
parentGuild, err := GetGuildInfoByID(s, alliance.ParentGuildID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get parent guild info", zap.Error(err))
|
|
return nil, err
|
|
} else {
|
|
alliance.ParentGuild = *parentGuild
|
|
alliance.TotalMembers += parentGuild.MemberCount
|
|
}
|
|
|
|
if alliance.SubGuild1ID > 0 {
|
|
subGuild1, err := GetGuildInfoByID(s, alliance.SubGuild1ID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get sub guild 1 info", zap.Error(err))
|
|
return nil, err
|
|
} else {
|
|
alliance.SubGuild1 = *subGuild1
|
|
alliance.TotalMembers += subGuild1.MemberCount
|
|
}
|
|
}
|
|
|
|
if alliance.SubGuild2ID > 0 {
|
|
subGuild2, err := GetGuildInfoByID(s, alliance.SubGuild2ID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get sub guild 2 info", zap.Error(err))
|
|
return nil, err
|
|
} else {
|
|
alliance.SubGuild2 = *subGuild2
|
|
alliance.TotalMembers += subGuild2.MemberCount
|
|
}
|
|
}
|
|
|
|
return alliance, nil
|
|
}
|
|
|
|
func handleMsgMhfCreateJoint(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfCreateJoint)
|
|
_, err := s.server.db.Exec("INSERT INTO guild_alliances (name, parent_id) VALUES ($1, $2)", pkt.Name, pkt.GuildID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to create guild alliance in db", zap.Error(err))
|
|
}
|
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x01, 0x01, 0x01, 0x01})
|
|
}
|
|
|
|
func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfOperateJoint)
|
|
|
|
guild, err := GetGuildInfoByID(s, pkt.GuildID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get guild info", zap.Error(err))
|
|
}
|
|
alliance, err := GetAllianceData(s, pkt.AllianceID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get alliance info", zap.Error(err))
|
|
}
|
|
|
|
switch pkt.Action {
|
|
case mhfpacket.OPERATE_JOINT_DISBAND:
|
|
if guild.LeaderCharID == s.charID && alliance.ParentGuildID == guild.ID {
|
|
_, err = s.server.db.Exec("DELETE FROM guild_alliances WHERE id=$1", alliance.ID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to disband alliance", zap.Error(err))
|
|
}
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
} else {
|
|
s.logger.Warn(
|
|
"Non-owner of alliance attempted disband",
|
|
zap.Uint32("CharID", s.charID),
|
|
zap.Uint32("AllyID", alliance.ID),
|
|
)
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
case mhfpacket.OPERATE_JOINT_LEAVE:
|
|
if guild.LeaderCharID == s.charID {
|
|
if guild.ID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 {
|
|
if _, err := s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID); err != nil {
|
|
s.logger.Error("Failed to update alliance on guild leave", zap.Error(err))
|
|
}
|
|
} else if guild.ID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 {
|
|
if _, err := s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID); err != nil {
|
|
s.logger.Error("Failed to remove sub guild 1 from alliance", zap.Error(err))
|
|
}
|
|
} else {
|
|
if _, err := s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID); err != nil {
|
|
s.logger.Error("Failed to remove sub guild 2 from alliance", zap.Error(err))
|
|
}
|
|
}
|
|
// NOTE: Alliance join requests are not yet implemented (no DB table exists),
|
|
// so there are no pending applications to clean up on leave.
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
} else {
|
|
s.logger.Warn(
|
|
"Non-owner of guild attempted alliance leave",
|
|
zap.Uint32("CharID", s.charID),
|
|
)
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
case mhfpacket.OPERATE_JOINT_KICK:
|
|
if alliance.ParentGuild.LeaderCharID == s.charID {
|
|
kickedGuildID := pkt.Data1.ReadUint32()
|
|
if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 {
|
|
if _, err := s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID); err != nil {
|
|
s.logger.Error("Failed to update alliance on guild kick", zap.Error(err))
|
|
}
|
|
} else if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 {
|
|
if _, err := s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID); err != nil {
|
|
s.logger.Error("Failed to remove kicked sub guild 1 from alliance", zap.Error(err))
|
|
}
|
|
} else {
|
|
if _, err := s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID); err != nil {
|
|
s.logger.Error("Failed to remove kicked sub guild 2 from alliance", zap.Error(err))
|
|
}
|
|
}
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
} else {
|
|
s.logger.Warn(
|
|
"Non-owner of alliance attempted kick",
|
|
zap.Uint32("CharID", s.charID),
|
|
zap.Uint32("AllyID", alliance.ID),
|
|
)
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
default:
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
panic(fmt.Sprintf("Unhandled operate joint action '%d'", pkt.Action))
|
|
}
|
|
}
|
|
|
|
func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfInfoJoint)
|
|
bf := byteframe.NewByteFrame()
|
|
alliance, err := GetAllianceData(s, pkt.AllianceID)
|
|
if err != nil {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
} else {
|
|
bf.WriteUint32(alliance.ID)
|
|
bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
|
|
bf.WriteUint16(alliance.TotalMembers)
|
|
bf.WriteUint16(0x0000) // Unk
|
|
ps.Uint16(bf, alliance.Name, true)
|
|
if alliance.SubGuild1ID > 0 {
|
|
if alliance.SubGuild2ID > 0 {
|
|
bf.WriteUint8(3)
|
|
} else {
|
|
bf.WriteUint8(2)
|
|
}
|
|
} else {
|
|
bf.WriteUint8(1)
|
|
}
|
|
bf.WriteUint32(alliance.ParentGuildID)
|
|
bf.WriteUint32(alliance.ParentGuild.LeaderCharID)
|
|
bf.WriteUint16(alliance.ParentGuild.Rank())
|
|
bf.WriteUint16(alliance.ParentGuild.MemberCount)
|
|
ps.Uint16(bf, alliance.ParentGuild.Name, true)
|
|
ps.Uint16(bf, alliance.ParentGuild.LeaderName, true)
|
|
if alliance.SubGuild1ID > 0 {
|
|
bf.WriteUint32(alliance.SubGuild1ID)
|
|
bf.WriteUint32(alliance.SubGuild1.LeaderCharID)
|
|
bf.WriteUint16(alliance.SubGuild1.Rank())
|
|
bf.WriteUint16(alliance.SubGuild1.MemberCount)
|
|
ps.Uint16(bf, alliance.SubGuild1.Name, true)
|
|
ps.Uint16(bf, alliance.SubGuild1.LeaderName, true)
|
|
}
|
|
if alliance.SubGuild2ID > 0 {
|
|
bf.WriteUint32(alliance.SubGuild2ID)
|
|
bf.WriteUint32(alliance.SubGuild2.LeaderCharID)
|
|
bf.WriteUint16(alliance.SubGuild2.Rank())
|
|
bf.WriteUint16(alliance.SubGuild2.MemberCount)
|
|
ps.Uint16(bf, alliance.SubGuild2.Name, true)
|
|
ps.Uint16(bf, alliance.SubGuild2.LeaderName, true)
|
|
}
|
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
|
}
|
|
}
|