mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
fix(guild): add nil guards for alliance guild lookups (#171)
scanAllianceWithGuilds dereferences guild pointers returned by GetByID without checking for nil. Since GetByID returns (nil, nil) when a guild is missing, alliances referencing deleted guilds cause nil-pointer panics. The panic is caught by session recovery but no ACK is sent, softlocking the client. Add nil checks in scanAllianceWithGuilds, handleMsgMhfOperateJoint, handleMsgMhfInfoJoint, and handleMsgMhfInfoGuild so that missing guilds or alliances produce proper error responses instead of panics.
This commit is contained in:
@@ -41,10 +41,20 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get guild info", zap.Error(err))
|
s.logger.Error("Failed to get guild info", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
if guild == nil {
|
||||||
|
s.logger.Error("Guild not found for alliance operation", zap.Uint32("GuildID", pkt.GuildID))
|
||||||
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
return
|
||||||
|
}
|
||||||
alliance, err := s.server.guildRepo.GetAllianceByID(pkt.AllianceID)
|
alliance, err := s.server.guildRepo.GetAllianceByID(pkt.AllianceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get alliance info", zap.Error(err))
|
s.logger.Error("Failed to get alliance info", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
if alliance == nil {
|
||||||
|
s.logger.Error("Alliance not found for operation", zap.Uint32("AllianceID", pkt.AllianceID))
|
||||||
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch pkt.Action {
|
switch pkt.Action {
|
||||||
case mhfpacket.OPERATE_JOINT_DISBAND:
|
case mhfpacket.OPERATE_JOINT_DISBAND:
|
||||||
@@ -119,9 +129,10 @@ func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
pkt := p.(*mhfpacket.MsgMhfInfoJoint)
|
pkt := p.(*mhfpacket.MsgMhfInfoJoint)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
alliance, err := s.server.guildRepo.GetAllianceByID(pkt.AllianceID)
|
alliance, err := s.server.guildRepo.GetAllianceByID(pkt.AllianceID)
|
||||||
if err != nil {
|
if err != nil || alliance == nil {
|
||||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
} else {
|
return
|
||||||
|
}
|
||||||
bf.WriteUint32(alliance.ID)
|
bf.WriteUint32(alliance.ID)
|
||||||
bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
|
bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
|
||||||
bf.WriteUint16(alliance.TotalMembers)
|
bf.WriteUint16(alliance.TotalMembers)
|
||||||
@@ -160,4 +171,3 @@ func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -438,3 +438,70 @@ func TestInfoJoint_NotFound(t *testing.T) {
|
|||||||
t.Error("No response packet queued")
|
t.Error("No response packet queued")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInfoJoint_NilAlliance(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
// alliance is nil, no error — simulates deleted alliance
|
||||||
|
guildMock := &mockGuildRepo{}
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfInfoJoint{AckHandle: 100, AllianceID: 999}
|
||||||
|
|
||||||
|
handleMsgMhfInfoJoint(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued — would softlock the client")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOperateJoint_NilGuild(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
// guild is nil — simulates deleted guild
|
||||||
|
guildMock := &mockGuildRepo{
|
||||||
|
alliance: &GuildAlliance{ID: 5, ParentGuildID: 10},
|
||||||
|
}
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfOperateJoint{
|
||||||
|
AckHandle: 100,
|
||||||
|
AllianceID: 5,
|
||||||
|
GuildID: 10,
|
||||||
|
Action: mhfpacket.OPERATE_JOINT_DISBAND,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfOperateJoint(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued — would softlock the client")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOperateJoint_NilAlliance(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
guildMock := &mockGuildRepo{}
|
||||||
|
guildMock.guild = &Guild{ID: 10}
|
||||||
|
guildMock.guild.LeaderCharID = 1
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfOperateJoint{
|
||||||
|
AckHandle: 100,
|
||||||
|
AllianceID: 999,
|
||||||
|
GuildID: 10,
|
||||||
|
Action: mhfpacket.OPERATE_JOINT_DISBAND,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfOperateJoint(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued — would softlock the client")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -135,8 +135,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
if guild.AllianceID > 0 {
|
if guild.AllianceID > 0 {
|
||||||
alliance, err := s.server.guildRepo.GetAllianceByID(guild.AllianceID)
|
alliance, err := s.server.guildRepo.GetAllianceByID(guild.AllianceID)
|
||||||
if err != nil {
|
if err != nil || alliance == nil {
|
||||||
bf.WriteUint32(0) // Error, no alliance
|
bf.WriteUint32(0) // Error or missing alliance
|
||||||
} else {
|
} else {
|
||||||
bf.WriteUint32(alliance.ID)
|
bf.WriteUint32(alliance.ID)
|
||||||
bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
|
bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
|
||||||
|
|||||||
@@ -97,6 +97,9 @@ func (r *GuildRepository) scanAllianceWithGuilds(rows *sqlx.Rows) (*GuildAllianc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if parentGuild == nil {
|
||||||
|
return nil, fmt.Errorf("alliance %d references non-existent parent guild %d", alliance.ID, alliance.ParentGuildID)
|
||||||
|
}
|
||||||
alliance.ParentGuild = *parentGuild
|
alliance.ParentGuild = *parentGuild
|
||||||
alliance.TotalMembers += parentGuild.MemberCount
|
alliance.TotalMembers += parentGuild.MemberCount
|
||||||
|
|
||||||
@@ -105,6 +108,9 @@ func (r *GuildRepository) scanAllianceWithGuilds(rows *sqlx.Rows) (*GuildAllianc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if subGuild1 == nil {
|
||||||
|
return nil, fmt.Errorf("alliance %d references non-existent sub guild 1 %d", alliance.ID, alliance.SubGuild1ID)
|
||||||
|
}
|
||||||
alliance.SubGuild1 = *subGuild1
|
alliance.SubGuild1 = *subGuild1
|
||||||
alliance.TotalMembers += subGuild1.MemberCount
|
alliance.TotalMembers += subGuild1.MemberCount
|
||||||
}
|
}
|
||||||
@@ -114,6 +120,9 @@ func (r *GuildRepository) scanAllianceWithGuilds(rows *sqlx.Rows) (*GuildAllianc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if subGuild2 == nil {
|
||||||
|
return nil, fmt.Errorf("alliance %d references non-existent sub guild 2 %d", alliance.ID, alliance.SubGuild2ID)
|
||||||
|
}
|
||||||
alliance.SubGuild2 = *subGuild2
|
alliance.SubGuild2 = *subGuild2
|
||||||
alliance.TotalMembers += subGuild2.MemberCount
|
alliance.TotalMembers += subGuild2.MemberCount
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user