mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
Several handler files discarded errors from repository and service calls, creating nil-dereference risks and silent data corruption: - guild_adventure: 3 GetByCharID calls could panic on nil guild - gacha: GetGachaPoints silently returned zero balances on DB error - house: HasApplication called before nil check on guild; GetHouseContents error discarded with 7 return values - distitem: 3 distRepo calls had no error logging - guild_ops: Disband/Leave service errors were invisible - shop: gacha type/weight/fpoint lookups had no error logging - discord: bcrypt error could result in nil password being set
267 lines
8.5 KiB
Go
267 lines
8.5 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"time"
|
|
|
|
"erupe-ce/common/byteframe"
|
|
"erupe-ce/common/stringsupport"
|
|
"erupe-ce/network/mhfpacket"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfOperateGuild)
|
|
|
|
guild, err := s.server.guildRepo.GetByID(pkt.GuildID)
|
|
if err != nil {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
characterGuildInfo, err := s.server.guildRepo.GetCharacterMembership(s.charID)
|
|
if err != nil {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
|
|
switch pkt.Action {
|
|
case mhfpacket.OperateGuildDisband:
|
|
result, err := s.server.guildService.Disband(s.charID, guild.ID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to disband guild", zap.Error(err))
|
|
}
|
|
response := 0
|
|
if result != nil && result.Success {
|
|
response = 1
|
|
}
|
|
bf.WriteUint32(uint32(response))
|
|
case mhfpacket.OperateGuildResign:
|
|
result, err := s.server.guildService.ResignLeadership(s.charID, guild.ID)
|
|
if err == nil && result.NewLeaderCharID != 0 {
|
|
bf.WriteUint32(result.NewLeaderCharID)
|
|
}
|
|
case mhfpacket.OperateGuildApply:
|
|
err = s.server.guildRepo.CreateApplication(guild.ID, s.charID, s.charID, GuildApplicationTypeApplied)
|
|
if err == nil {
|
|
bf.WriteUint32(guild.LeaderCharID)
|
|
} else {
|
|
bf.WriteUint32(0)
|
|
}
|
|
case mhfpacket.OperateGuildLeave:
|
|
result, err := s.server.guildService.Leave(s.charID, guild.ID, characterGuildInfo.IsApplicant, guild.Name)
|
|
if err != nil {
|
|
s.logger.Error("Failed to leave guild", zap.Error(err))
|
|
}
|
|
response := 0
|
|
if result != nil && result.Success {
|
|
response = 1
|
|
}
|
|
bf.WriteUint32(uint32(response))
|
|
case mhfpacket.OperateGuildDonateRank:
|
|
bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, 0))
|
|
case mhfpacket.OperateGuildSetApplicationDeny:
|
|
if err := s.server.guildRepo.SetRecruiting(guild.ID, false); err != nil {
|
|
s.logger.Error("Failed to deny guild applications", zap.Error(err))
|
|
}
|
|
case mhfpacket.OperateGuildSetApplicationAllow:
|
|
if err := s.server.guildRepo.SetRecruiting(guild.ID, true); err != nil {
|
|
s.logger.Error("Failed to allow guild applications", zap.Error(err))
|
|
}
|
|
case mhfpacket.OperateGuildSetAvoidLeadershipTrue:
|
|
handleAvoidLeadershipUpdate(s, pkt, true)
|
|
case mhfpacket.OperateGuildSetAvoidLeadershipFalse:
|
|
handleAvoidLeadershipUpdate(s, pkt, false)
|
|
case mhfpacket.OperateGuildUpdateComment:
|
|
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
guild.Comment = stringsupport.SJISToUTF8Lossy(pkt.Data2.ReadNullTerminatedBytes())
|
|
if err := s.server.guildRepo.Save(guild); err != nil {
|
|
s.logger.Error("Failed to save guild comment", zap.Error(err))
|
|
}
|
|
case mhfpacket.OperateGuildUpdateMotto:
|
|
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
_ = pkt.Data1.ReadUint16()
|
|
guild.SubMotto = pkt.Data1.ReadUint8()
|
|
guild.MainMotto = pkt.Data1.ReadUint8()
|
|
if err := s.server.guildRepo.Save(guild); err != nil {
|
|
s.logger.Error("Failed to save guild motto", zap.Error(err))
|
|
}
|
|
case mhfpacket.OperateGuildRenamePugi1:
|
|
handleRenamePugi(s, pkt.Data2, guild, 1)
|
|
case mhfpacket.OperateGuildRenamePugi2:
|
|
handleRenamePugi(s, pkt.Data2, guild, 2)
|
|
case mhfpacket.OperateGuildRenamePugi3:
|
|
handleRenamePugi(s, pkt.Data2, guild, 3)
|
|
case mhfpacket.OperateGuildChangePugi1:
|
|
handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 1)
|
|
case mhfpacket.OperateGuildChangePugi2:
|
|
handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 2)
|
|
case mhfpacket.OperateGuildChangePugi3:
|
|
handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 3)
|
|
case mhfpacket.OperateGuildUnlockOutfit:
|
|
if err := s.server.guildRepo.SetPugiOutfits(guild.ID, pkt.Data1.ReadUint32()); err != nil {
|
|
s.logger.Error("Failed to unlock guild pugi outfit", zap.Error(err))
|
|
}
|
|
case mhfpacket.OperateGuildDonateRoom:
|
|
quantity := uint16(pkt.Data1.ReadUint32())
|
|
bf.WriteBytes(handleDonateRP(s, quantity, guild, 2))
|
|
case mhfpacket.OperateGuildDonateEvent:
|
|
quantity := uint16(pkt.Data1.ReadUint32())
|
|
bf.WriteBytes(handleDonateRP(s, quantity, guild, 1))
|
|
if err := s.server.guildRepo.AddMemberDailyRP(s.charID, quantity); err != nil {
|
|
s.logger.Error("Failed to update guild character daily RP", zap.Error(err))
|
|
}
|
|
case mhfpacket.OperateGuildEventExchange:
|
|
rp := uint16(pkt.Data1.ReadUint32())
|
|
balance, err := s.server.guildRepo.ExchangeEventRP(guild.ID, rp)
|
|
if err != nil {
|
|
s.logger.Error("Failed to exchange guild event RP", zap.Error(err))
|
|
}
|
|
bf.WriteUint32(balance)
|
|
default:
|
|
s.logger.Error("unhandled operate guild action", zap.Uint8("action", uint8(pkt.Action)))
|
|
}
|
|
|
|
if len(bf.Data()) > 0 {
|
|
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
|
} else {
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
}
|
|
|
|
func handleRenamePugi(s *Session, bf *byteframe.ByteFrame, guild *Guild, num int) {
|
|
name := stringsupport.SJISToUTF8Lossy(bf.ReadNullTerminatedBytes())
|
|
switch num {
|
|
case 1:
|
|
guild.PugiName1 = name
|
|
case 2:
|
|
guild.PugiName2 = name
|
|
default:
|
|
guild.PugiName3 = name
|
|
}
|
|
if err := s.server.guildRepo.Save(guild); err != nil {
|
|
s.logger.Error("Failed to save guild pugi name", zap.Error(err))
|
|
}
|
|
}
|
|
|
|
func handleChangePugi(s *Session, outfit uint8, guild *Guild, num int) {
|
|
switch num {
|
|
case 1:
|
|
guild.PugiOutfit1 = outfit
|
|
case 2:
|
|
guild.PugiOutfit2 = outfit
|
|
case 3:
|
|
guild.PugiOutfit3 = outfit
|
|
}
|
|
if err := s.server.guildRepo.Save(guild); err != nil {
|
|
s.logger.Error("Failed to save guild pugi outfit", zap.Error(err))
|
|
}
|
|
}
|
|
|
|
func handleDonateRP(s *Session, amount uint16, guild *Guild, _type int) []byte {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint32(0)
|
|
saveData, err := GetCharacterSaveData(s, s.charID)
|
|
if err != nil {
|
|
return bf.Data()
|
|
}
|
|
var resetRoom bool
|
|
if _type == 2 {
|
|
currentRP, err := s.server.guildRepo.GetRoomRP(guild.ID)
|
|
if err != nil {
|
|
s.logger.Error("Failed to get guild room RP", zap.Error(err))
|
|
}
|
|
if currentRP+amount >= 30 {
|
|
amount = 30 - currentRP
|
|
resetRoom = true
|
|
}
|
|
}
|
|
saveData.RP -= amount
|
|
saveData.Save(s)
|
|
switch _type {
|
|
case 0:
|
|
if err := s.server.guildRepo.AddRankRP(guild.ID, amount); err != nil {
|
|
s.logger.Error("Failed to update guild rank RP", zap.Error(err))
|
|
}
|
|
case 1:
|
|
if err := s.server.guildRepo.AddEventRP(guild.ID, amount); err != nil {
|
|
s.logger.Error("Failed to update guild event RP", zap.Error(err))
|
|
}
|
|
case 2:
|
|
if resetRoom {
|
|
if err := s.server.guildRepo.SetRoomRP(guild.ID, 0); err != nil {
|
|
s.logger.Error("Failed to reset guild room RP", zap.Error(err))
|
|
}
|
|
if err := s.server.guildRepo.SetRoomExpiry(guild.ID, TimeAdjusted().Add(time.Hour*24*7)); err != nil {
|
|
s.logger.Error("Failed to update guild room expiry", zap.Error(err))
|
|
}
|
|
} else {
|
|
if err := s.server.guildRepo.AddRoomRP(guild.ID, amount); err != nil {
|
|
s.logger.Error("Failed to update guild room RP", zap.Error(err))
|
|
}
|
|
}
|
|
}
|
|
_, _ = bf.Seek(0, 0)
|
|
bf.WriteUint32(uint32(saveData.RP))
|
|
return bf.Data()
|
|
}
|
|
|
|
func handleAvoidLeadershipUpdate(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, avoidLeadership bool) {
|
|
characterGuildData, err := s.server.guildRepo.GetCharacterMembership(s.charID)
|
|
|
|
if err != nil {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
characterGuildData.AvoidLeadership = avoidLeadership
|
|
|
|
err = s.server.guildRepo.SaveMember(characterGuildData)
|
|
|
|
if err != nil {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
|
|
func handleMsgMhfOperateGuildMember(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgMhfOperateGuildMember)
|
|
|
|
action, ok := mapMemberAction(pkt.Action)
|
|
if !ok {
|
|
s.logger.Warn("Unhandled operateGuildMember action", zap.Uint8("action", pkt.Action))
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
result, err := s.server.guildService.OperateMember(s.charID, pkt.CharID, action)
|
|
if err != nil {
|
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
return
|
|
}
|
|
|
|
s.server.Registry.NotifyMailToCharID(result.MailRecipientID, s, &result.Mail)
|
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
}
|
|
|
|
func mapMemberAction(proto uint8) (GuildMemberAction, bool) {
|
|
switch proto {
|
|
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_ACCEPT:
|
|
return GuildMemberActionAccept, true
|
|
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_REJECT:
|
|
return GuildMemberActionReject, true
|
|
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_KICK:
|
|
return GuildMemberActionKick, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|