package channelserver import ( "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" "fmt" "time" "erupe-ce/network/mhfpacket" "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 { var alliance GuildAlliance err := s.server.db.QueryRowx(fmt.Sprintf(` %s WHERE ga.id = $1 `, allianceInfoSelectQuery), AllianceID).StructScan(&alliance) if err != nil { s.logger.Error("failed to retrieve alliance data", zap.Error(err)) } else { parentGuild := GetGuildInfoByID(s, alliance.ParentGuildID) if parentGuild.ID > 0 { alliance.ParentGuild = parentGuild alliance.TotalMembers += parentGuild.MemberCount } if alliance.SubGuild1ID > 0 { subGuild1 := GetGuildInfoByID(s, alliance.SubGuild1ID) if subGuild1.ID > 0 { alliance.SubGuild1 = subGuild1 alliance.TotalMembers += subGuild1.MemberCount } } if alliance.SubGuild2ID > 0 { subGuild2 := GetGuildInfoByID(s, alliance.SubGuild2ID) if subGuild2.ID > 0 { alliance.SubGuild2 = subGuild2 alliance.TotalMembers += subGuild2.MemberCount } } } return alliance } 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 := GetGuildInfoByID(s, pkt.GuildID) if guild.ID == 0 { s.logger.Error("failed to get guild info") } alliance := GetAllianceData(s, pkt.AllianceID) if alliance.ID == 0 { s.logger.Error("failed to get alliance info") } switch pkt.Action { case mhfpacket.OperateJointDisband: 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.OperateJointLeave: if guild.LeaderCharID == s.charID { if guild.ID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 { s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID) } else if guild.ID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 { s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID) } else { s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID) } // TODO: Handle deleting Alliance applications 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.OperateJointKick: if alliance.ParentGuild.LeaderCharID == s.charID { kickedGuildID := pkt.Data1.ReadUint32() if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 { s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID) } else if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 { s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID) } else { s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID) } 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 := GetAllianceData(s, pkt.AllianceID) if alliance.ID == 0 { 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()) } }