diff --git a/network/mhfpacket/msg_mhf_enumerate_guild.go b/network/mhfpacket/msg_mhf_enumerate_guild.go index 61ead7870..6251505ab 100644 --- a/network/mhfpacket/msg_mhf_enumerate_guild.go +++ b/network/mhfpacket/msg_mhf_enumerate_guild.go @@ -10,20 +10,19 @@ import ( type EnumerateGuildType uint8 const ( - ENUMERATE_GUILD_UNKNOWN = iota - ENUMERATE_GUILD_TYPE_GUILD_NAME - ENUMERATE_GUILD_TYPE_LEADER_NAME - ENUMERATE_GUILD_TYPE_LEADER_ID - ENUMERATE_GUILD_TYPE_ORDER_MEMBERS - ENUMERATE_GUILD_TYPE_ORDER_REGISTRATION - ENUMERATE_GUILD_TYPE_ORDER_RANK - ENUMERATE_GUILD_TYPE_MOTTO - ENUMERATE_GUILD_TYPE_RECRUITING - ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME - ENUMERATE_ALLIANCE_TYPE_LEADER_NAME - ENUMERATE_ALLIANCE_TYPE_LEADER_ID - ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS - ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION + EnumerateGuildTypeGuildName = iota + 1 + EnumerateGuildTypeLeaderName + EnumerateGuildTypeLeaderId + EnumerateGuildTypeOrderMembers + EnumerateGuildTypeOrderRegistration + EnumerateGuildTypeOrderRank + EnumerateGuildTypeMotto + EnumerateGuildTypeRecruiting + EnumerateAllianceTypeAllianceName + EnumerateAllianceTypeLeaderName + EnumerateAllianceTypeLeaderId + EnumerateAllianceTypeOrderMembers + EnumerateAllianceTypeOrderRegistration ) // MsgMhfEnumerateGuild represents the MSG_MHF_ENUMERATE_GUILD diff --git a/network/mhfpacket/msg_mhf_enumerate_inv_guild.go b/network/mhfpacket/msg_mhf_enumerate_inv_guild.go index 994c374d8..50ef22f80 100644 --- a/network/mhfpacket/msg_mhf_enumerate_inv_guild.go +++ b/network/mhfpacket/msg_mhf_enumerate_inv_guild.go @@ -12,8 +12,8 @@ import ( type MsgMhfEnumerateInvGuild struct { AckHandle uint32 Unk uint32 - Operation uint8 - ActiveHours uint8 + ActiveHours1 uint8 + ActiveHours2 uint8 DaysActive uint8 PlayStyle uint8 GuildRequest uint8 @@ -28,8 +28,8 @@ func (m *MsgMhfEnumerateInvGuild) Opcode() network.PacketID { func (m *MsgMhfEnumerateInvGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Unk = bf.ReadUint32() - m.Operation = bf.ReadUint8() - m.ActiveHours = bf.ReadUint8() + m.ActiveHours1 = bf.ReadUint8() + m.ActiveHours2 = bf.ReadUint8() m.DaysActive = bf.ReadUint8() m.PlayStyle = bf.ReadUint8() m.GuildRequest = bf.ReadUint8() diff --git a/network/mhfpacket/msg_mhf_operate_guild_member.go b/network/mhfpacket/msg_mhf_operate_guild_member.go index 8daf82dc5..45ce097c2 100644 --- a/network/mhfpacket/msg_mhf_operate_guild_member.go +++ b/network/mhfpacket/msg_mhf_operate_guild_member.go @@ -11,10 +11,9 @@ import ( type OperateGuildMemberAction uint8 const ( - _ = iota - OPERATE_GUILD_MEMBER_ACTION_ACCEPT - OPERATE_GUILD_MEMBER_ACTION_REJECT - OPERATE_GUILD_MEMBER_ACTION_KICK + OperateGuildMemberAccept = iota + 1 + OperateGuildMemberReject + OperateGuildMemberKick ) // MsgMhfOperateGuildMember represents the MSG_MHF_OPERATE_GUILD_MEMBER diff --git a/network/mhfpacket/msg_mhf_operate_joint.go b/network/mhfpacket/msg_mhf_operate_joint.go index eccb3139d..0f0a77a07 100644 --- a/network/mhfpacket/msg_mhf_operate_joint.go +++ b/network/mhfpacket/msg_mhf_operate_joint.go @@ -11,9 +11,9 @@ import ( type OperateJointAction uint8 const ( - OPERATE_JOINT_DISBAND = 0x01 - OPERATE_JOINT_LEAVE = 0x03 - OPERATE_JOINT_KICK = 0x09 + OperateJointDisband = 1 + OperateJointLeave = 3 + OperateJointKick = 9 ) // MsgMhfOperateJoint represents the MSG_MHF_OPERATE_JOINT diff --git a/network/mhfpacket/msg_mhf_operation_inv_guild.go b/network/mhfpacket/msg_mhf_operation_inv_guild.go index bcd0b45b8..c6da08ea1 100644 --- a/network/mhfpacket/msg_mhf_operation_inv_guild.go +++ b/network/mhfpacket/msg_mhf_operation_inv_guild.go @@ -8,10 +8,17 @@ import ( "erupe-ce/network/clientctx" ) +type OperateInvGuildAction uint8 + +const ( + OperateInvGuildSend = iota + 1 + OperateInvGuildCancel +) + // MsgMhfOperationInvGuild represents the MSG_MHF_OPERATION_INV_GUILD type MsgMhfOperationInvGuild struct { AckHandle uint32 - Operation uint8 + Action uint8 ActiveHours uint8 DaysActive uint8 PlayStyle uint8 @@ -26,7 +33,7 @@ func (m *MsgMhfOperationInvGuild) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfOperationInvGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Operation = bf.ReadUint8() + m.Action = bf.ReadUint8() m.ActiveHours = bf.ReadUint8() m.DaysActive = bf.ReadUint8() m.PlayStyle = bf.ReadUint8() diff --git a/schemas/patch-schema/clan-applications-rework.sql b/schemas/patch-schema/clan-applications-rework.sql new file mode 100644 index 000000000..c4bed0720 --- /dev/null +++ b/schemas/patch-schema/clan-applications-rework.sql @@ -0,0 +1,26 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.guild_applications DROP COLUMN IF EXISTS actor_id; + +ALTER TABLE IF EXISTS public.guild_applications DROP COLUMN IF EXISTS application_type; + +ALTER TABLE IF EXISTS public.guild_applications + ALTER COLUMN created_at DROP NOT NULL; +ALTER TABLE IF EXISTS public.guild_applications DROP CONSTRAINT IF EXISTS guild_applications_actor_id_fkey; + +create table public.guild_invites ( + id serial primary key, + guild_id integer, + character_id integer, + actor_id integer, + created_at timestamp with time zone not null default now(), + foreign key (guild_id) references guilds (id), + foreign key (character_id) references characters (id), + foreign key (actor_id) references characters (id) +); + +drop type if exists guild_application_type; + +ALTER TABLE IF EXISTS public.mail DROP CONSTRAINT IF EXISTS mail_sender_id_fkey; + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index eecd268e3..6396d2f6e 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -356,18 +356,14 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { // state festa (U)ser func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStateFestaU) - guild, err := GetGuildInfoByCharacterId(s, s.charID) - applicant := false - if guild != nil { - applicant, _ = guild.HasApplicationForCharID(s, s.charID) - } - if err != nil || guild == nil || applicant { + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 || IsGuildApplicant(s, s.charID) { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } var souls, exists uint32 s.server.db.QueryRow(`SELECT COALESCE((SELECT SUM(souls) FROM festa_submissions WHERE character_id=$1), 0)`, 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) + 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) if err != nil { @@ -383,13 +379,9 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { // state festa (G)uild func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStateFestaG) - guild, err := GetGuildInfoByCharacterId(s, s.charID) - applicant := false - if guild != nil { - applicant, _ = guild.HasApplicationForCharID(s, s.charID) - } + guild := GetGuildInfoByCharacterId(s, s.charID) resp := byteframe.NewByteFrame() - if err != nil || guild == nil || applicant { + if guild.ID == 0 || IsGuildApplicant(s, s.charID) { resp.WriteUint32(0) resp.WriteInt32(0) resp.WriteInt32(-1) @@ -408,20 +400,20 @@ func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateFestaMember) - guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil || guild == nil { + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } - members, err := GetGuildMembers(s, guild.ID, false) - if err != nil { + members := GetGuildMembers(s, guild.ID) + if len(members) == 0 { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } sort.Slice(members, func(i, j int) bool { return members[i].Souls > members[j].Souls }) - var validMembers []*GuildMember + var validMembers []GuildMember for _, member := range members { if member.Souls > 0 { validMembers = append(validMembers, member) @@ -450,8 +442,8 @@ func handleMsgMhfVoteFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEntryFesta) - guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil || guild == nil { + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 096419137..44f43d6bf 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -66,13 +66,9 @@ type Guild struct { Souls uint32 `db:"souls"` AllianceID uint32 `db:"alliance_id"` Icon *GuildIcon `db:"icon"` - - GuildLeader -} - -type GuildLeader struct { - LeaderCharID uint32 `db:"leader_id"` - LeaderName string `db:"leader_name"` + LeaderCharID uint32 `db:"leader_id"` + LeaderName string `db:"leader_name"` + Members []*GuildMember } type GuildIconPart struct { @@ -89,12 +85,23 @@ type GuildIconPart struct { } type GuildApplication struct { - ID int `db:"id"` - GuildID uint32 `db:"guild_id"` - CharID uint32 `db:"character_id"` - ActorID uint32 `db:"actor_id"` - ApplicationType GuildApplicationType `db:"application_type"` - CreatedAt time.Time `db:"created_at"` + GuildID uint32 `db:"guild_id"` + CharID uint32 `db:"character_id"` + AppliedAt time.Time `db:"created_at"` + HR uint16 `db:"hr"` + GR uint16 `db:"gr"` + Name string `db:"name"` +} + +type GuildInvite struct { + ID uint32 `db:"id"` + GuildID uint32 `db:"guild_id"` + CharID uint32 `db:"character_id"` + ActorID uint32 `db:"actor_id"` + InvitedAt time.Time `db:"created_at"` + HR uint16 `db:"hr"` + GR uint16 `db:"gr"` + Name string `db:"name"` } type GuildIcon struct { @@ -191,7 +198,7 @@ func (guild *Guild) Save(s *Session) error { UPDATE guilds SET main_motto=$2, sub_motto=$3, comment=$4, pugi_name_1=$5, pugi_name_2=$6, pugi_name_3=$7, pugi_outfit_1=$8, pugi_outfit_2=$9, pugi_outfit_3=$10, pugi_outfits=$11, icon=$12, leader_id=$13 WHERE id=$1 `, guild.ID, guild.MainMotto, guild.SubMotto, guild.Comment, guild.PugiName1, guild.PugiName2, guild.PugiName3, - guild.PugiOutfit1, guild.PugiOutfit2, guild.PugiOutfit3, guild.PugiOutfits, guild.Icon, guild.GuildLeader.LeaderCharID) + guild.PugiOutfit1, guild.PugiOutfit2, guild.PugiOutfit3, guild.PugiOutfits, guild.Icon, guild.LeaderCharID) if err != nil { s.logger.Error("failed to update guild data", zap.Error(err), zap.Uint32("guildID", guild.ID)) @@ -349,13 +356,8 @@ func (guild *Guild) AcceptApplication(s *Session, charID uint32) error { return nil } -// This is relying on the fact that invitation ID is also character ID right now -// if invitation ID changes, this will break. func (guild *Guild) CancelInvitation(s *Session, charID uint32) error { - _, err := s.server.db.Exec( - `DELETE FROM guild_applications WHERE character_id = $1 AND guild_id = $2 AND application_type = 'invited'`, - charID, guild.ID, - ) + _, err := s.server.db.Exec(`DELETE FROM guild_invites WHERE character_id = $1 AND guild_id = $2`, charID, guild.ID) if err != nil { s.logger.Error( @@ -447,6 +449,17 @@ func (guild *Guild) GetApplicationForCharID(s *Session, charID uint32, applicati return application, nil } +func IsGuildApplicant(s *Session, charID uint32) bool { + var exists bool + err := s.server.db.QueryRowx(` + SELECT 1 from guild_applications WHERE character_id = $1 + `, charID).Scan(&exists) + if err == nil && exists { + return true + } + return false +} + func (guild *Guild) HasApplicationForCharID(s *Session, charID uint32) (bool, error) { row := s.server.db.QueryRowx(` SELECT 1 from guild_applications WHERE character_id = $1 AND guild_id = $2 @@ -545,62 +558,43 @@ func rollbackTransaction(s *Session, transaction *sql.Tx) { } } -func GetGuildInfoByID(s *Session, guildID uint32) (*Guild, error) { - rows, err := s.server.db.Queryx(fmt.Sprintf(` +func GetGuildInfoByID(s *Session, guildID uint32) Guild { + var guild Guild + err := s.server.db.QueryRowx(fmt.Sprintf(` %s WHERE g.id = $1 - LIMIT 1 - `, guildInfoSelectQuery), guildID) + `, guildInfoSelectQuery), guildID).StructScan(&guild) if err != nil { s.logger.Error("failed to retrieve guild", zap.Error(err), zap.Uint32("guildID", guildID)) - return nil, err } - defer rows.Close() - - hasRow := rows.Next() - - if !hasRow { - return nil, nil - } - - return buildGuildObjectFromDbResult(rows, err, s) + return guild } -func GetGuildInfoByCharacterId(s *Session, charID uint32) (*Guild, error) { - rows, err := s.server.db.Queryx(fmt.Sprintf(` +func GetGuildInfoByCharacterId(s *Session, charID uint32) Guild { + var guild Guild + err := s.server.db.QueryRowx(fmt.Sprintf(` %s - WHERE EXISTS( - SELECT 1 - FROM guild_characters gc1 - WHERE gc1.character_id = $1 - AND gc1.guild_id = g.id - ) - OR EXISTS( - SELECT 1 - FROM guild_applications ga - WHERE ga.character_id = $1 - AND ga.guild_id = g.id - AND ga.application_type = 'applied' - ) + WHERE EXISTS ( + SELECT 1 + FROM guild_characters gc1 + WHERE gc1.character_id = $1 + AND gc1.guild_id = g.id + ) OR EXISTS ( + SELECT 1 + FROM guild_applications ga + WHERE ga.character_id = $1 + AND ga.guild_id = g.id + ) LIMIT 1 - `, guildInfoSelectQuery), charID) + `, guildInfoSelectQuery), charID).StructScan(&guild) if err != nil { s.logger.Error("failed to retrieve guild for character", zap.Error(err), zap.Uint32("charID", charID)) - return nil, err } - defer rows.Close() - - hasRow := rows.Next() - - if !hasRow { - return nil, nil - } - - return buildGuildObjectFromDbResult(rows, err, s) + return guild } func buildGuildObjectFromDbResult(result *sqlx.Rows, err error, s *Session) (*Guild, error) { @@ -642,12 +636,10 @@ func handleMsgMhfCreateGuild(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfOperateGuild) - guild, err := GetGuildInfoByID(s, pkt.GuildID) - characterGuildInfo, err := GetCharacterGuildData(s, s.charID) - if err != nil { - doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) - return - } + var err error + + guild := GetGuildInfoByID(s, pkt.GuildID) + characterGuildInfo := GetCharacterGuildData(s, s.charID) bf := byteframe.NewByteFrame() @@ -665,8 +657,8 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { } bf.WriteUint32(uint32(response)) case mhfpacket.OperateGuildResign: - guildMembers, err := GetGuildMembers(s, guild.ID, false) - if err == nil { + guildMembers := GetGuildMembers(s, guild.ID) + if len(guildMembers) > 0 { sort.Slice(guildMembers[:], func(i, j int) bool { return guildMembers[i].OrderIndex < guildMembers[j].OrderIndex }) @@ -682,71 +674,61 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { } } guild.Save(s) + } else { + bf.WriteUint32(0) } case mhfpacket.OperateGuildApply: - err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil) + _, err = s.server.db.Exec(`INSERT INTO guild_applications (guild_id, character_id) VALUES ($1, $2)`, guild.ID, s.charID) if err == nil { bf.WriteUint32(guild.LeaderCharID) } else { bf.WriteUint32(0) } case mhfpacket.OperateGuildLeave: - if characterGuildInfo.IsApplicant { - err = guild.RejectApplication(s, s.charID) + if characterGuildInfo.CharID == 0 { + s.server.db.Exec(`DELETE FROM guild_applications WHERE character_id=$1 AND guild_id=$2`, s.charID, guild.ID) + bf.WriteUint32(1) } else { - err = guild.RemoveCharacter(s, s.charID) - } - response := 1 - if err != nil { - response = 0 - } else { - mail := Mail{ - RecipientID: s.charID, - Subject: "Withdrawal", - Body: fmt.Sprintf("You have withdrawn from 「%s」.", guild.Name), - IsSystemMessage: true, + if guild.RemoveCharacter(s, s.charID) != nil { + bf.WriteUint32(0) + } else { + bf.WriteUint32(1) } - mail.Send(s, nil) } - bf.WriteUint32(uint32(response)) + mail := Mail{ + RecipientID: s.charID, + Subject: "Withdrawal", + Body: fmt.Sprintf("You have withdrawn from 「%s」.", guild.Name), + IsSystemMessage: true, + } + mail.Send(s) case mhfpacket.OperateGuildDonateRank: bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, 0)) case mhfpacket.OperateGuildSetApplicationDeny: - s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID) + s.server.db.Exec(`UPDATE guilds SET recruiting=false WHERE id=$1`, guild.ID) case mhfpacket.OperateGuildSetApplicationAllow: - s.server.db.Exec("UPDATE guilds SET recruiting=true WHERE id=$1", guild.ID) + s.server.db.Exec(`UPDATE guilds SET recruiting=true WHERE id=$1`, guild.ID) case mhfpacket.OperateGuildSetAvoidLeadershipTrue: - handleAvoidLeadershipUpdate(s, pkt, true) + s.server.db.Exec(`UPDATE guild_characters SET avoid_leadership=true WHERE character_id=$1`, s.charID) case mhfpacket.OperateGuildSetAvoidLeadershipFalse: - handleAvoidLeadershipUpdate(s, pkt, false) + s.server.db.Exec(`UPDATE guild_characters SET avoid_leadership=false WHERE character_id=$1`, s.charID) case mhfpacket.OperateGuildUpdateComment: - if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() { - doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) - return - } - guild.Comment = stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes()) - guild.Save(s) + s.server.db.Exec(`UPDATE guilds SET comment=$1 WHERE id=$2`, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes()), guild.ID) 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() - guild.Save(s) + s.server.db.Exec(`UPDATE guilds SET sub_motto=$1, main_motto=$2 WHERE id=$3`, pkt.Data1.ReadUint8(), pkt.Data1.ReadUint8(), guild.ID) case mhfpacket.OperateGuildRenamePugi1: - handleRenamePugi(s, pkt.Data2, guild, 1) + guild.RenamePoogie(s, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes()), 1) case mhfpacket.OperateGuildRenamePugi2: - handleRenamePugi(s, pkt.Data2, guild, 2) + guild.RenamePoogie(s, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes()), 2) case mhfpacket.OperateGuildRenamePugi3: - handleRenamePugi(s, pkt.Data2, guild, 3) + guild.RenamePoogie(s, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes()), 3) case mhfpacket.OperateGuildChangePugi1: - handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 1) + guild.ChangePoogie(s, uint8(pkt.Data1.ReadUint32()), 1) case mhfpacket.OperateGuildChangePugi2: - handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 2) + guild.ChangePoogie(s, uint8(pkt.Data1.ReadUint32()), 2) case mhfpacket.OperateGuildChangePugi3: - handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 3) + guild.ChangePoogie(s, uint8(pkt.Data1.ReadUint32()), 3) case mhfpacket.OperateGuildUnlockOutfit: // TODO: This doesn't implement blocking, if someone unlocked the same outfit at the same time s.server.db.Exec(`UPDATE guilds SET pugi_outfits=pugi_outfits+$1 WHERE id=$2`, int(math.Pow(float64(pkt.Data1.ReadUint32()), 2)), guild.ID) @@ -774,32 +756,31 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { } } -func handleRenamePugi(s *Session, bf *byteframe.ByteFrame, guild *Guild, num int) { - name := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) +func (g *Guild) RenamePoogie(s *Session, name string, num int) { switch num { case 1: - guild.PugiName1 = name + g.PugiName1 = name case 2: - guild.PugiName2 = name + g.PugiName2 = name default: - guild.PugiName3 = name + g.PugiName3 = name } - guild.Save(s) + g.Save(s) } -func handleChangePugi(s *Session, outfit uint8, guild *Guild, num int) { +func (g *Guild) ChangePoogie(s *Session, outfit uint8, num int) { switch num { case 1: - guild.PugiOutfit1 = outfit + g.PugiOutfit1 = outfit case 2: - guild.PugiOutfit2 = outfit + g.PugiOutfit2 = outfit case 3: - guild.PugiOutfit3 = outfit + g.PugiOutfit3 = outfit } - guild.Save(s) + g.Save(s) } -func handleDonateRP(s *Session, amount uint16, guild *Guild, _type int) []byte { +func handleDonateRP(s *Session, amount uint16, guild Guild, _type int) []byte { bf := byteframe.NewByteFrame() bf.WriteUint32(0) saveData, err := GetCharacterSaveData(s, s.charID) @@ -835,63 +816,42 @@ func handleDonateRP(s *Session, amount uint16, guild *Guild, _type int) []byte { return bf.Data() } -func handleAvoidLeadershipUpdate(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, avoidLeadership bool) { - characterGuildData, err := GetCharacterGuildData(s, s.charID) - - if err != nil { - doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) - return - } - - characterGuildData.AvoidLeadership = avoidLeadership - - err = characterGuildData.Save(s) - - 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) - guild, err := GetGuildInfoByCharacterId(s, pkt.CharID) - - if err != nil || guild == nil { + guild := GetGuildInfoByCharacterId(s, pkt.CharID) + if guild.ID == 0 { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } - actorCharacter, err := GetCharacterGuildData(s, s.charID) - - if err != nil || (!actorCharacter.IsSubLeader() && guild.LeaderCharID != s.charID) { + actor := GetCharacterGuildData(s, s.charID) + if !actor.IsSubLeader() { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } var mail Mail switch pkt.Action { - case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_ACCEPT: - err = guild.AcceptApplication(s, pkt.CharID) + case mhfpacket.OperateGuildMemberAccept: + s.server.db.Exec(`DELETE FROM guild_applications WHERE character_id=$1`, pkt.CharID) + s.server.db.Exec(`INSERT INTO guild_characters (guild_id, character_id, order_index) VALUES ($1, $2, (SELECT MAX(order_index) + 1 FROM guild_characters WHERE guild_id = $1))`, guild.ID, pkt.CharID) mail = Mail{ RecipientID: pkt.CharID, Subject: "Accepted!", Body: fmt.Sprintf("Your application to join 「%s」 was accepted.", guild.Name), IsSystemMessage: true, } - case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_REJECT: - err = guild.RejectApplication(s, pkt.CharID) + case mhfpacket.OperateGuildMemberReject: + s.server.db.Exec(`DELETE FROM guild_applications WHERE character_id=$1 AND guild_id=$2`, pkt.CharID, guild.ID) mail = Mail{ RecipientID: pkt.CharID, Subject: "Rejected", Body: fmt.Sprintf("Your application to join 「%s」 was rejected.", guild.Name), IsSystemMessage: true, } - case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_KICK: - err = guild.RemoveCharacter(s, pkt.CharID) + case mhfpacket.OperateGuildMemberKick: + _ = guild.RemoveCharacter(s, pkt.CharID) mail = Mail{ RecipientID: pkt.CharID, Subject: "Kicked", @@ -903,10 +863,8 @@ func handleMsgMhfOperateGuildMember(s *Session, p mhfpacket.MHFPacket) { s.logger.Warn(fmt.Sprintf("unhandled operateGuildMember action '%d'", pkt.Action)) } - if err != nil { - doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) - } else { - mail.Send(s, nil) + if mail.RecipientID != 0 { + mail.Send(s) for _, channel := range s.server.Channels { for _, session := range channel.sessions { if session.charID == pkt.CharID { @@ -914,44 +872,28 @@ func handleMsgMhfOperateGuildMember(s *Session, p mhfpacket.MHFPacket) { } } } - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfInfoGuild) - var guild *Guild - var err error - + var guild Guild if pkt.GuildID > 0 { - guild, err = GetGuildInfoByID(s, pkt.GuildID) + guild = GetGuildInfoByID(s, pkt.GuildID) } else { - guild, err = GetGuildInfoByCharacterId(s, s.charID) + guild = GetGuildInfoByCharacterId(s, s.charID) } - if err == nil && guild != nil { + if guild.ID > 0 { s.prevGuildID = guild.ID guildName := stringsupport.UTF8ToSJIS(guild.Name) guildComment := stringsupport.UTF8ToSJIS(guild.Comment) guildLeaderName := stringsupport.UTF8ToSJIS(guild.LeaderName) - characterGuildData, err := GetCharacterGuildData(s, s.charID) - characterJoinedAt := uint32(0xFFFFFFFF) - - if characterGuildData != nil && characterGuildData.JoinedAt != nil { - characterJoinedAt = uint32(characterGuildData.JoinedAt.Unix()) - } - - if err != nil { - resp := byteframe.NewByteFrame() - resp.WriteUint32(0) // Count - resp.WriteUint8(0) // Unk, read if count == 0. - - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - return - } + characterGuildData := GetCharacterGuildData(s, s.charID) bf := byteframe.NewByteFrame() @@ -973,16 +915,20 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteBool(!guild.Recruiting) - if characterGuildData == nil || characterGuildData.IsApplicant { - bf.WriteUint16(0x00) + if characterGuildData.CharID == 0 { + bf.WriteUint16(0) } else if guild.LeaderCharID == s.charID { - bf.WriteUint16(0x01) + bf.WriteUint16(1) } else { - bf.WriteUint16(0x02) + bf.WriteUint16(2) } bf.WriteUint32(uint32(guild.CreatedAt.Unix())) - bf.WriteUint32(characterJoinedAt) + if characterGuildData.JoinedAt.IsZero() { + bf.WriteInt32(-1) + } else { + bf.WriteInt32(int32(characterGuildData.JoinedAt.Unix())) + } bf.WriteUint8(uint8(len(guildName))) bf.WriteUint8(uint8(len(guildComment))) bf.WriteUint8(uint8(5)) // Length of unknown string below @@ -1028,8 +974,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(0) // Ignored if guild.AllianceID > 0 { - alliance, err := GetAllianceData(s, guild.AllianceID) - if err != nil { + alliance := GetAllianceData(s, guild.AllianceID) + if alliance.ID == 0 { bf.WriteUint32(0) // Error, no alliance } else { bf.WriteUint32(alliance.ID) @@ -1089,10 +1035,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(0) // No alliance } - applicants, err := GetGuildMembers(s, guild.ID, true) - if err != nil || (characterGuildData != nil && !characterGuildData.CanRecruit()) { - bf.WriteUint16(0) - } else { + applicants := GetGuildApplications(s, guild.ID) + if characterGuildData.CanRecruit() { bf.WriteUint16(uint16(len(applicants))) for _, applicant := range applicants { bf.WriteUint32(applicant.CharID) @@ -1101,6 +1045,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(applicant.GR) ps.Uint8(bf, applicant.Name, true) } + } else { + bf.WriteUint16(0) } type UnkGuildInfo struct { @@ -1166,17 +1112,18 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuild) - var guilds []*Guild - var alliances []*GuildAlliance + var guilds []Guild + var alliances []GuildAlliance var rows *sqlx.Rows var err error if pkt.Type <= 8 { - var tempGuilds []*Guild + var tempGuilds []Guild rows, err = s.server.db.Queryx(guildInfoSelectQuery) if err == nil { for rows.Next() { - guild, err := buildGuildObjectFromDbResult(rows, err, s) + var guild Guild + err = rows.StructScan(&guild) if err != nil { continue } @@ -1184,26 +1131,26 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } } switch pkt.Type { - case mhfpacket.ENUMERATE_GUILD_TYPE_GUILD_NAME: + case mhfpacket.EnumerateGuildTypeGuildName: for _, guild := range tempGuilds { if strings.Contains(guild.Name, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())) { guilds = append(guilds, guild) } } - case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_NAME: + case mhfpacket.EnumerateGuildTypeLeaderName: for _, guild := range tempGuilds { if strings.Contains(guild.LeaderName, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())) { guilds = append(guilds, guild) } } - case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_ID: + case mhfpacket.EnumerateGuildTypeLeaderId: CID := pkt.Data1.ReadUint32() for _, guild := range tempGuilds { if guild.LeaderCharID == CID { guilds = append(guilds, guild) } } - case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_MEMBERS: + case mhfpacket.EnumerateGuildTypeOrderMembers: if pkt.Sorting { sort.Slice(tempGuilds, func(i, j int) bool { return tempGuilds[i].MemberCount > tempGuilds[j].MemberCount @@ -1214,7 +1161,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { }) } guilds = tempGuilds - case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_REGISTRATION: + case mhfpacket.EnumerateGuildTypeOrderRegistration: if pkt.Sorting { sort.Slice(tempGuilds, func(i, j int) bool { return tempGuilds[i].CreatedAt.Unix() > tempGuilds[j].CreatedAt.Unix() @@ -1225,7 +1172,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { }) } guilds = tempGuilds - case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_RANK: + case mhfpacket.EnumerateGuildTypeOrderRank: if pkt.Sorting { sort.Slice(tempGuilds, func(i, j int) bool { return tempGuilds[i].RankRP > tempGuilds[j].RankRP @@ -1236,7 +1183,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { }) } guilds = tempGuilds - case mhfpacket.ENUMERATE_GUILD_TYPE_MOTTO: + case mhfpacket.EnumerateGuildTypeMotto: mainMotto := uint8(pkt.Data1.ReadUint16()) subMotto := uint8(pkt.Data1.ReadUint16()) for _, guild := range tempGuilds { @@ -1244,7 +1191,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { guilds = append(guilds, guild) } } - case mhfpacket.ENUMERATE_GUILD_TYPE_RECRUITING: + case mhfpacket.EnumerateGuildTypeRecruiting: recruitingMotto := uint8(pkt.Data1.ReadUint16()) for _, guild := range tempGuilds { if guild.MainMotto == recruitingMotto { @@ -1255,35 +1202,39 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } if pkt.Type > 8 { - var tempAlliances []*GuildAlliance + var tempAlliances []GuildAlliance rows, err = s.server.db.Queryx(allianceInfoSelectQuery) if err == nil { for rows.Next() { - alliance, _ := buildAllianceObjectFromDbResult(rows, err, s) + var alliance GuildAlliance + err = rows.StructScan(&alliance) + if err != nil { + continue + } tempAlliances = append(tempAlliances, alliance) } } switch pkt.Type { - case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME: + case mhfpacket.EnumerateAllianceTypeAllianceName: for _, alliance := range tempAlliances { if strings.Contains(alliance.Name, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())) { alliances = append(alliances, alliance) } } - case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME: + case mhfpacket.EnumerateAllianceTypeLeaderName: for _, alliance := range tempAlliances { if strings.Contains(alliance.ParentGuild.LeaderName, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())) { alliances = append(alliances, alliance) } } - case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_ID: + case mhfpacket.EnumerateAllianceTypeLeaderId: CID := pkt.Data1.ReadUint32() for _, alliance := range tempAlliances { if alliance.ParentGuild.LeaderCharID == CID { alliances = append(alliances, alliance) } } - case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS: + case mhfpacket.EnumerateAllianceTypeOrderMembers: if pkt.Sorting { sort.Slice(tempAlliances, func(i, j int) bool { return tempAlliances[i].TotalMembers > tempAlliances[j].TotalMembers @@ -1294,7 +1245,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { }) } alliances = tempAlliances - case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION: + case mhfpacket.EnumerateAllianceTypeOrderRegistration: if pkt.Sorting { sort.Slice(tempAlliances, func(i, j int) bool { return tempAlliances[i].CreatedAt.Unix() > tempAlliances[j].CreatedAt.Unix() @@ -1369,26 +1320,13 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfArrangeGuildMember(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfArrangeGuildMember) - guild, err := GetGuildInfoByID(s, pkt.GuildID) - - if err != nil { - s.logger.Error( - "failed to respond to ArrangeGuildMember message", - zap.Uint32("charID", s.charID), - ) + guild := GetGuildInfoByID(s, pkt.GuildID) + if guild.ID == 0 { + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) return } - if guild.LeaderCharID != s.charID { - s.logger.Error("non leader attempting to rearrange guild members!", - zap.Uint32("charID", s.charID), - zap.Uint32("guildID", guild.ID), - ) - return - } - - err = guild.ArrangeCharacters(s, pkt.CharIDs) - + err := guild.ArrangeCharacters(s, pkt.CharIDs) if err != nil { s.logger.Error( "failed to respond to ArrangeGuildMember message", @@ -1404,47 +1342,29 @@ func handleMsgMhfArrangeGuildMember(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuildMember) - var guild *Guild - var err error - + var guild Guild if pkt.GuildID > 0 { - guild, err = GetGuildInfoByID(s, pkt.GuildID) + guild = GetGuildInfoByID(s, pkt.GuildID) } else { - guild, err = GetGuildInfoByCharacterId(s, s.charID) + guild = GetGuildInfoByCharacterId(s, s.charID) } - - if guild != nil { - isApplicant, _ := guild.HasApplicationForCharID(s, s.charID) - if isApplicant { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2)) - return - } + if guild.ID == 0 && s.prevGuildID > 0 { + guild = GetGuildInfoByID(s, s.prevGuildID) } - - if guild == nil && s.prevGuildID > 0 { - guild, err = GetGuildInfoByID(s, s.prevGuildID) - } - - if err != nil { - s.logger.Warn("failed to retrieve guild sending no result message") - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2)) - return - } else if guild == nil { + if guild.ID == 0 { + s.logger.Warn("failed to get guild") doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2)) return } - guildMembers, err := GetGuildMembers(s, guild.ID, false) - - if err != nil { - s.logger.Error("failed to retrieve guild") - return + guildMembers := GetGuildMembers(s, guild.ID) + if len(guildMembers) == 0 { + s.logger.Error("failed to get guild members") } - alliance, err := GetAllianceData(s, guild.AllianceID) - if err != nil { - s.logger.Error("Failed to get alliance data") - return + alliance := GetAllianceData(s, guild.AllianceID) + if alliance.ID == 0 { + s.logger.Error("failed to get alliance data") } bf := byteframe.NewByteFrame() @@ -1481,37 +1401,30 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(member.LastLogin) } + var allianceMembers []GuildMember if guild.AllianceID > 0 { - bf.WriteUint16(alliance.TotalMembers - uint16(len(guildMembers))) if guild.ID != alliance.ParentGuildID { - mems, err := GetGuildMembers(s, alliance.ParentGuildID, false) - if err != nil { - panic(err) - } - for _, m := range mems { - bf.WriteUint32(m.CharID) + members := GetGuildMembers(s, alliance.ParentGuildID) + for i := range members { + allianceMembers = append(allianceMembers, members[i]) } } if guild.ID != alliance.SubGuild1ID { - mems, err := GetGuildMembers(s, alliance.SubGuild1ID, false) - if err != nil { - panic(err) - } - for _, m := range mems { - bf.WriteUint32(m.CharID) + members := GetGuildMembers(s, alliance.SubGuild1ID) + for i := range members { + allianceMembers = append(allianceMembers, members[i]) } } if guild.ID != alliance.SubGuild2ID { - mems, err := GetGuildMembers(s, alliance.SubGuild2ID, false) - if err != nil { - panic(err) - } - for _, m := range mems { - bf.WriteUint32(m.CharID) + members := GetGuildMembers(s, alliance.SubGuild2ID) + for i := range members { + allianceMembers = append(allianceMembers, members[i]) } } - } else { - bf.WriteUint16(0) + } + bf.WriteUint16(uint16(len(allianceMembers))) + for i := range allianceMembers { + bf.WriteUint32(allianceMembers[i].CharID) } for _, member := range guildMembers { @@ -1525,23 +1438,27 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetGuildManageRight(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGuildManageRight) - guild, err := GetGuildInfoByCharacterId(s, s.charID) - if guild == nil && s.prevGuildID != 0 { - guild, err = GetGuildInfoByID(s, s.prevGuildID) + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 && s.prevGuildID != 0 { + guild = GetGuildInfoByID(s, s.prevGuildID) s.prevGuildID = 0 - if guild == nil || err != nil { + if guild.ID == 0 { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } } + members := GetGuildMembers(s, guild.ID) bf := byteframe.NewByteFrame() - bf.WriteUint32(uint32(guild.MemberCount)) - members, _ := GetGuildMembers(s, guild.ID, false) - for _, member := range members { - bf.WriteUint32(member.CharID) - bf.WriteBool(member.Recruiter) - bf.WriteBytes(make([]byte, 3)) + if members != nil { + bf.WriteUint32(uint32(len(members))) + for _, member := range members { + bf.WriteUint32(member.CharID) + bf.WriteBool(member.Recruiter) + bf.WriteBytes(make([]byte, 3)) + } + } else { + bf.WriteUint32(0) } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -1554,25 +1471,23 @@ func handleMsgMhfGetUdGuildMapInfo(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetGuildTargetMemberNum(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGuildTargetMemberNum) - var guild *Guild - var err error - - if pkt.GuildID == 0x0 { - guild, err = GetGuildInfoByCharacterId(s, s.charID) + var guild Guild + if pkt.GuildID == 0 { + guild = GetGuildInfoByCharacterId(s, s.charID) } else { - guild, err = GetGuildInfoByID(s, pkt.GuildID) - } - - if err != nil || guild == nil { - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02}) - return + guild = GetGuildInfoByID(s, pkt.GuildID) } bf := byteframe.NewByteFrame() - bf.WriteUint16(0x0) - bf.WriteUint16(guild.MemberCount - 1) + if guild.ID == 0 { + bf.WriteUint32(2) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + return + } + bf.WriteUint16(0) + bf.WriteUint16(guild.MemberCount - 1) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -1609,32 +1524,14 @@ func handleMsgMhfUpdateGuildItem(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateGuildIcon) - guild, err := GetGuildInfoByID(s, pkt.GuildID) - - if err != nil { - panic(err) - } - - characterInfo, err := GetCharacterGuildData(s, s.charID) - - if err != nil { - panic(err) - } - - if !characterInfo.IsSubLeader() && !characterInfo.IsLeader { - s.logger.Warn( - "character without leadership attempting to update guild icon", - zap.Uint32("guildID", guild.ID), - zap.Uint32("charID", s.charID), - ) + guild := GetGuildInfoByID(s, pkt.GuildID) + if guild.ID == 0 { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } icon := &GuildIcon{} - icon.Parts = make([]GuildIconPart, len(pkt.IconParts)) - for i, p := range pkt.IconParts { icon.Parts[i] = GuildIconPart{ Index: p.Index, @@ -1652,7 +1549,7 @@ func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) { guild.Icon = icon - err = guild.Save(s) + err := guild.Save(s) if err != nil { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) @@ -1756,7 +1653,7 @@ type GuildMeal struct { func handleMsgMhfLoadGuildCooking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfLoadGuildCooking) - guild, _ := GetGuildInfoByCharacterId(s, s.charID) + guild := GetGuildInfoByCharacterId(s, s.charID) data, err := s.server.db.Queryx("SELECT id, meal_id, level, created_at FROM guild_meals WHERE guild_id = $1", guild.ID) if err != nil { s.logger.Error("Failed to get guild meals from db", zap.Error(err)) @@ -1787,7 +1684,7 @@ func handleMsgMhfLoadGuildCooking(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfRegistGuildCooking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfRegistGuildCooking) - guild, _ := GetGuildInfoByCharacterId(s, s.charID) + guild := GetGuildInfoByCharacterId(s, s.charID) startTime := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.ClanMealDuration-3600) * time.Second) if pkt.OverwriteID != 0 { s.server.db.Exec("UPDATE guild_meals SET meal_id = $1, level = $2, created_at = $3 WHERE id = $4", pkt.MealID, pkt.Success, startTime, pkt.OverwriteID) @@ -1852,10 +1749,10 @@ func handleMsgMhfGuildHuntdata(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint8(count) } case 2: // Check - guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err == nil { + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID > 0 { var count uint8 - err = s.server.db.QueryRow(`SELECT COUNT(*) FROM kill_logs kl + err := s.server.db.QueryRow(`SELECT COUNT(*) FROM kill_logs kl INNER JOIN guild_characters gc ON kl.character_id = gc.character_id WHERE gc.guild_id=$1 AND kl.timestamp >= (SELECT box_claimed FROM guild_characters WHERE character_id=$2) @@ -1884,13 +1781,13 @@ type MessageBoardPost struct { func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuildMessageBoard) - guild, _ := GetGuildInfoByCharacterId(s, s.charID) + guild := GetGuildInfoByCharacterId(s, s.charID) if pkt.BoardType == 1 { pkt.MaxPosts = 4 } msgs, err := s.server.db.Queryx("SELECT id, stamp_id, title, body, author_id, created_at, liked_by FROM guild_posts WHERE guild_id = $1 AND post_type = $2 ORDER BY created_at DESC", guild.ID, int(pkt.BoardType)) if err != nil { - s.logger.Error("Failed to get guild messages from db", zap.Error(err)) + s.logger.Error("failed to get guild messages from db", zap.Error(err)) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } @@ -1922,12 +1819,8 @@ func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateGuildMessageBoard) - guild, err := GetGuildInfoByCharacterId(s, s.charID) - applicant := false - if guild != nil { - applicant, _ = guild.HasApplicationForCharID(s, s.charID) - } - if err != nil || guild == nil || applicant { + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 || IsGuildApplicant(s, s.charID) { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) return } @@ -2011,12 +1904,13 @@ func handleMsgMhfAcquireMonthlyItem(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateInvGuild) - stubEnumerateNoResults(s, pkt.AckHandle) + bf := byteframe.NewByteFrame() + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfOperationInvGuild) - doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfUpdateGuildcard(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_guild_adventure.go b/server/channelserver/handlers_guild_adventure.go index 8e953bf24..99e9ad925 100644 --- a/server/channelserver/handlers_guild_adventure.go +++ b/server/channelserver/handlers_guild_adventure.go @@ -20,7 +20,7 @@ type GuildAdventure struct { func handleMsgMhfLoadGuildAdventure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfLoadGuildAdventure) - guild, _ := GetGuildInfoByCharacterId(s, s.charID) + guild := GetGuildInfoByCharacterId(s, s.charID) data, err := s.server.db.Queryx("SELECT id, destination, charge, depart, return, collected_by FROM guild_adventures WHERE guild_id = $1", guild.ID) if err != nil { s.logger.Error("Failed to get guild adventures from db", zap.Error(err)) @@ -51,7 +51,7 @@ func handleMsgMhfLoadGuildAdventure(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfRegistGuildAdventure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfRegistGuildAdventure) - guild, _ := GetGuildInfoByCharacterId(s, s.charID) + guild := GetGuildInfoByCharacterId(s, s.charID) _, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, depart, return) VALUES ($1, $2, $3, $4)", guild.ID, pkt.Destination, TimeAdjusted().Unix(), TimeAdjusted().Add(6*time.Hour).Unix()) if err != nil { s.logger.Error("Failed to register guild adventure", zap.Error(err)) @@ -86,7 +86,7 @@ func handleMsgMhfChargeGuildAdventure(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfRegistGuildAdventureDiva(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfRegistGuildAdventureDiva) - guild, _ := GetGuildInfoByCharacterId(s, s.charID) + guild := GetGuildInfoByCharacterId(s, s.charID) _, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, charge, depart, return) VALUES ($1, $2, $3, $4, $5)", guild.ID, pkt.Destination, pkt.Charge, TimeAdjusted().Unix(), TimeAdjusted().Add(1*time.Hour).Unix()) if err != nil { s.logger.Error("Failed to register guild adventure", zap.Error(err)) diff --git a/server/channelserver/handlers_guild_alliance.go b/server/channelserver/handlers_guild_alliance.go index 39dbe13f6..30a9b8e6b 100644 --- a/server/channelserver/handlers_guild_alliance.go +++ b/server/channelserver/handlers_guild_alliance.go @@ -7,7 +7,6 @@ import ( "time" "erupe-ce/network/mhfpacket" - "github.com/jmoiron/sqlx" "go.uber.org/zap" ) @@ -43,66 +42,36 @@ type GuildAlliance struct { SubGuild2 Guild } -func GetAllianceData(s *Session, AllianceID uint32) (*GuildAlliance, error) { - rows, err := s.server.db.Queryx(fmt.Sprintf(` +func GetAllianceData(s *Session, AllianceID uint32) GuildAlliance { + var alliance GuildAlliance + err := s.server.db.QueryRowx(fmt.Sprintf(` %s WHERE ga.id = $1 - `, allianceInfoSelectQuery), AllianceID) + `, allianceInfoSelectQuery), AllianceID).StructScan(&alliance) 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, err 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 + s.logger.Error("failed to retrieve alliance data", zap.Error(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 + 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 + } } } - - 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 + return alliance } func handleMsgMhfCreateJoint(s *Session, p mhfpacket.MHFPacket) { @@ -117,19 +86,19 @@ func handleMsgMhfCreateJoint(s *Session, p mhfpacket.MHFPacket) { 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)) + guild := GetGuildInfoByID(s, pkt.GuildID) + if guild.ID == 0 { + s.logger.Error("failed to get guild info") } - alliance, err := GetAllianceData(s, pkt.AllianceID) - if err != nil { - s.logger.Error("Failed to get alliance info", zap.Error(err)) + alliance := GetAllianceData(s, pkt.AllianceID) + if alliance.ID == 0 { + s.logger.Error("failed to get alliance info") } switch pkt.Action { - case mhfpacket.OPERATE_JOINT_DISBAND: + 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) + _, 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)) } @@ -142,7 +111,7 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) { ) doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) } - case mhfpacket.OPERATE_JOINT_LEAVE: + 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) @@ -160,7 +129,7 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) { ) doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) } - case mhfpacket.OPERATE_JOINT_KICK: + case mhfpacket.OperateJointKick: if alliance.ParentGuild.LeaderCharID == s.charID { kickedGuildID := pkt.Data1.ReadUint32() if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 { @@ -188,8 +157,8 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfInfoJoint) bf := byteframe.NewByteFrame() - alliance, err := GetAllianceData(s, pkt.AllianceID) - if err != nil { + alliance := GetAllianceData(s, pkt.AllianceID) + if alliance.ID == 0 { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) } else { bf.WriteUint32(alliance.ID) diff --git a/server/channelserver/handlers_guild_member.go b/server/channelserver/handlers_guild_member.go index f4cb04175..37f40a8d4 100644 --- a/server/channelserver/handlers_guild_member.go +++ b/server/channelserver/handlers_guild_member.go @@ -4,49 +4,44 @@ import ( "fmt" "time" - "github.com/jmoiron/sqlx" "go.uber.org/zap" ) type GuildMember struct { - GuildID uint32 `db:"guild_id"` - CharID uint32 `db:"character_id"` - JoinedAt *time.Time `db:"joined_at"` - Souls uint32 `db:"souls"` - RPToday uint16 `db:"rp_today"` - RPYesterday uint16 `db:"rp_yesterday"` - Name string `db:"name"` - IsApplicant bool `db:"is_applicant"` - OrderIndex uint16 `db:"order_index"` - LastLogin uint32 `db:"last_login"` - Recruiter bool `db:"recruiter"` - AvoidLeadership bool `db:"avoid_leadership"` - IsLeader bool `db:"is_leader"` - HR uint16 `db:"hr"` - GR uint16 `db:"gr"` - WeaponID uint16 `db:"weapon_id"` - WeaponType uint8 `db:"weapon_type"` + GuildID uint32 `db:"guild_id"` + CharID uint32 `db:"character_id"` + JoinedAt time.Time `db:"joined_at"` + Souls uint32 `db:"souls"` + RPToday uint16 `db:"rp_today"` + RPYesterday uint16 `db:"rp_yesterday"` + Name string `db:"name"` + OrderIndex uint16 `db:"order_index"` + LastLogin uint32 `db:"last_login"` + Recruiter bool `db:"recruiter"` + AvoidLeadership bool `db:"avoid_leadership"` + HR uint16 `db:"hr"` + GR uint16 `db:"gr"` + WeaponID uint16 `db:"weapon_id"` + WeaponType uint8 `db:"weapon_type"` } -func (gm *GuildMember) CanRecruit() bool { +func (gm GuildMember) CanRecruit() bool { if gm.Recruiter { return true } - if gm.OrderIndex <= 3 { - return true - } - if gm.IsLeader { - return true - } - return false + return gm.IsSubLeader() } -func (gm *GuildMember) IsSubLeader() bool { +func (gm GuildMember) IsSubLeader() bool { return gm.OrderIndex <= 3 } -func (gm *GuildMember) Save(s *Session) error { - _, err := s.server.db.Exec("UPDATE guild_characters SET avoid_leadership=$1, order_index=$2 WHERE character_id=$3", gm.AvoidLeadership, gm.OrderIndex, gm.CharID) +func (gm GuildMember) IsLeader() bool { + return gm.OrderIndex == 1 +} + +func (gm GuildMember) Save(s *Session) error { + _, err := s.server.db.Exec("UPDATE guild_characters SET order_index=$1 WHERE character_id=$2", gm.OrderIndex, gm.CharID) if err != nil { s.logger.Error( @@ -61,7 +56,6 @@ func (gm *GuildMember) Save(s *Session) error { } const guildMembersSelectSQL = ` -SELECT * FROM ( SELECT g.id AS guild_id, joined_at, @@ -77,71 +71,71 @@ SELECT * FROM ( c.hr, c.gr, c.weapon_id, - c.weapon_type, - EXISTS(SELECT 1 FROM guild_applications ga WHERE ga.character_id=c.id AND application_type='applied') AS is_applicant, - CASE WHEN g.leader_id = c.id THEN true ELSE false END AS is_leader + c.weapon_type FROM guild_characters gc LEFT JOIN characters c ON c.id = gc.character_id LEFT JOIN guilds g ON g.id = gc.guild_id -) AS subquery ` -func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMember, error) { +func GetGuildApplications(s *Session, guildID uint32) []GuildApplication { + var applications []GuildApplication + var application GuildApplication + rows, err := s.server.db.Queryx(`SELECT ga.character_id, ga.created_at, c.hr, c.gr, c.name FROM guild_applications ga LEFT JOIN characters c ON c.id = ga.character_id WHERE ga.guild_id=$1`, guildID) + if err == nil { + for rows.Next() { + err = rows.StructScan(&application) + if err != nil { + continue + } + applications = append(applications, application) + } + } + return applications +} + +func GetGuildInvites(s *Session, guildID uint32) []GuildInvite { + var invites []GuildInvite + var invite GuildInvite + rows, err := s.server.db.Queryx(`SELECT gi.character_id, gi.id, gi.created_at, gi.actor_id, c.hr, c.gr, c.name FROM guild_invites gi LEFT JOIN characters c ON c.id = gi.character_id WHERE gi.guild_id=$1`, guildID) + if err == nil { + for rows.Next() { + err = rows.StructScan(&invite) + if err != nil { + continue + } + invites = append(invites, invite) + } + } + return invites +} + +func GetGuildMembers(s *Session, guildID uint32) []GuildMember { + var members []GuildMember + var member GuildMember rows, err := s.server.db.Queryx(fmt.Sprintf(` %s - WHERE guild_id = $1 AND is_applicant = $2 - `, guildMembersSelectSQL), guildID, applicants) - + WHERE guild_id = $1 + `, guildMembersSelectSQL), guildID) if err != nil { - s.logger.Error("failed to retrieve membership data for guild", zap.Error(err), zap.Uint32("guildID", guildID)) - return nil, err + s.logger.Error("Failed to retrieve membership data for guild", zap.Error(err), zap.Uint32("guildID", guildID)) + return members } - defer rows.Close() - - members := make([]*GuildMember, 0) - for rows.Next() { - member, err := buildGuildMemberObjectFromDBResult(rows, err, s) - + err = rows.StructScan(&member) if err != nil { - return nil, err + continue } - members = append(members, member) } - - return members, nil + return members } -func GetCharacterGuildData(s *Session, charID uint32) (*GuildMember, error) { - rows, err := s.server.db.Queryx(fmt.Sprintf("%s WHERE character_id=$1", guildMembersSelectSQL), charID) - +func GetCharacterGuildData(s *Session, charID uint32) GuildMember { + var member GuildMember + err := s.server.db.QueryRowx(fmt.Sprintf("%s WHERE character_id=$1", guildMembersSelectSQL), charID).StructScan(&member) if err != nil { - s.logger.Error(fmt.Sprintf("failed to retrieve membership data for character '%d'", charID)) - return nil, err + s.logger.Error("Failed to retrieve membership data for character", zap.Error(err), zap.Uint32("charID", charID)) } - - defer rows.Close() - - hasRow := rows.Next() - - if !hasRow { - return nil, nil - } - - return buildGuildMemberObjectFromDBResult(rows, err, s) -} - -func buildGuildMemberObjectFromDBResult(rows *sqlx.Rows, err error, s *Session) (*GuildMember, error) { - memberData := &GuildMember{} - - err = rows.StructScan(&memberData) - - if err != nil { - s.logger.Error("failed to retrieve guild data from database", zap.Error(err)) - return nil, err - } - - return memberData, nil + return member } diff --git a/server/channelserver/handlers_guild_scout.go b/server/channelserver/handlers_guild_scout.go index a599ec301..7ce8ff554 100644 --- a/server/channelserver/handlers_guild_scout.go +++ b/server/channelserver/handlers_guild_scout.go @@ -5,57 +5,18 @@ import ( "erupe-ce/common/stringsupport" "erupe-ce/network/mhfpacket" "fmt" - "go.uber.org/zap" - "io" ) func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfPostGuildScout) - actorCharGuildData, err := GetCharacterGuildData(s, s.charID) - - if err != nil { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - panic(err) - } - - if actorCharGuildData == nil || !actorCharGuildData.CanRecruit() { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } - guildInfo, err := GetGuildInfoByID(s, actorCharGuildData.GuildID) - - if err != nil { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - panic(err) - } - - hasApplication, err := guildInfo.HasApplicationForCharID(s, pkt.CharID) - - if err != nil { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - panic(err) - } - - if hasApplication { - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x04}) - return - } - - transaction, err := s.server.db.Begin() - - if err != nil { - panic(err) - } - - err = guildInfo.CreateApplication(s, pkt.CharID, GuildApplicationTypeInvited, transaction) - - if err != nil { - rollbackTransaction(s, transaction) - doAckBufFail(s, pkt.AckHandle, nil) - panic(err) - } + s.server.db.Exec(`INSERT INTO guild_invites (guild_id, character_id, actor_id) VALUES ($1, $2, $3)`, s.charID, pkt.CharID, guild.ID) mail := &Mail{ SenderID: s.charID, @@ -63,85 +24,33 @@ func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) { Subject: s.server.i18n.guild.invite.title, Body: fmt.Sprintf( s.server.i18n.guild.invite.body, - guildInfo.Name, + guild.Name, ), IsGuildInvite: true, } - err = mail.Send(s, transaction) - - if err != nil { - rollbackTransaction(s, transaction) - doAckBufFail(s, pkt.AckHandle, nil) - return - } - - err = transaction.Commit() - - if err != nil { - doAckBufFail(s, pkt.AckHandle, nil) - panic(err) - } - - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + mail.Send(s) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfCancelGuildScout(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfCancelGuildScout) - - guildCharData, err := GetCharacterGuildData(s, s.charID) - - if err != nil { - panic(err) - } - - if guildCharData == nil || !guildCharData.CanRecruit() { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - return - } - - guild, err := GetGuildInfoByID(s, guildCharData.GuildID) - - if err != nil { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - return - } - - err = guild.CancelInvitation(s, pkt.InvitationID) - - if err != nil { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - return - } - + s.server.db.Exec(`DELETE FROM guild_invites WHERE id=$1`, pkt.InvitationID) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfAnswerGuildScout(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAnswerGuildScout) - bf := byteframe.NewByteFrame() - guild, err := GetGuildInfoByCharacterId(s, pkt.LeaderID) - if err != nil { - panic(err) - } - - app, err := guild.GetApplicationForCharID(s, s.charID, GuildApplicationTypeInvited) - - if app == nil || err != nil { - s.logger.Warn( - "Guild invite missing, deleted?", - zap.Error(err), - zap.Uint32("guildID", guild.ID), - zap.Uint32("charID", s.charID), - ) - bf.WriteUint32(7) - bf.WriteUint32(guild.ID) - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + guild := GetGuildInfoByCharacterId(s, pkt.LeaderID) + if guild.ID == 0 { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } + bf := byteframe.NewByteFrame() var mail []Mail + var err error if pkt.Answer { err = guild.AcceptApplication(s, s.charID) mail = append(mail, Mail{ @@ -182,7 +91,7 @@ func handleMsgMhfAnswerGuildScout(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(guild.ID) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) for _, m := range mail { - m.Send(s, nil) + m.Send(s) } } } @@ -190,121 +99,46 @@ func handleMsgMhfAnswerGuildScout(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGuildScoutList) - guildInfo, err := GetGuildInfoByCharacterId(s, s.charID) - - if guildInfo == nil && s.prevGuildID == 0 { - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 && s.prevGuildID == 0 { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } else { - guildInfo, err = GetGuildInfoByID(s, s.prevGuildID) - if guildInfo == nil || err != nil { - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + guild = GetGuildInfoByID(s, s.prevGuildID) + if guild.ID == 0 { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } } - rows, err := s.server.db.Queryx(` - SELECT c.id, c.name, c.hr, c.gr, ga.actor_id - FROM guild_applications ga - JOIN characters c ON c.id = ga.character_id - WHERE ga.guild_id = $1 AND ga.application_type = 'invited' - `, guildInfo.ID) - - if err != nil { - s.logger.Error("failed to retrieve scouted characters", zap.Error(err)) - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) - return - } - - defer rows.Close() - + invites := GetGuildInvites(s, guild.ID) bf := byteframe.NewByteFrame() - - bf.SetBE() - - // Result count, we will overwrite this later - bf.WriteUint32(0x00) - - count := uint32(0) - - for rows.Next() { - var charName string - var charID, actorID uint32 - var HR, GR uint16 - - err = rows.Scan(&charID, &charName, &HR, &GR, &actorID) - - if err != nil { - doAckSimpleFail(s, pkt.AckHandle, nil) - continue - } - - // This seems to be used as a unique ID for the invitation sent - // we can just use the charID and then filter on guild_id+charID when performing operations - // this might be a problem later with mails sent referencing IDs but we'll see. - bf.WriteUint32(charID) - bf.WriteUint32(actorID) - bf.WriteUint32(charID) - bf.WriteUint32(uint32(TimeAdjusted().Unix())) - bf.WriteUint16(HR) // HR? - bf.WriteUint16(GR) // GR? - bf.WriteBytes(stringsupport.PaddedString(charName, 32, true)) - count++ + bf.WriteUint32(uint32(len(invites))) + for _, invite := range invites { + bf.WriteUint32(invite.ID) + bf.WriteUint32(invite.ActorID) + bf.WriteUint32(invite.CharID) + bf.WriteUint32(uint32(invite.InvitedAt.Unix())) + bf.WriteUint16(invite.HR) + bf.WriteUint16(invite.GR) + bf.WriteBytes(stringsupport.PaddedString(invite.Name, 32, true)) } - - _, err = bf.Seek(0, io.SeekStart) - - if err != nil { - panic(err) - } - - bf.WriteUint32(count) - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfGetRejectGuildScout(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetRejectGuildScout) - - row := s.server.db.QueryRow("SELECT restrict_guild_scout FROM characters WHERE id=$1", s.charID) - var currentStatus bool - - err := row.Scan(¤tStatus) - - if err != nil { - s.logger.Error( - "failed to retrieve character guild scout status", - zap.Error(err), - zap.Uint32("charID", s.charID), - ) - doAckSimpleFail(s, pkt.AckHandle, nil) - return - } - - response := uint8(0x00) - + s.server.db.QueryRow(`SELECT restrict_guild_scout FROM characters WHERE id=$1`, s.charID).Scan(¤tStatus) if currentStatus { - response = 0x01 + doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) + } else { + doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } - - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, response}) } func handleMsgMhfSetRejectGuildScout(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSetRejectGuildScout) - - _, err := s.server.db.Exec("UPDATE characters SET restrict_guild_scout=$1 WHERE id=$2", pkt.Reject, s.charID) - - if err != nil { - s.logger.Error( - "failed to update character guild scout status", - zap.Error(err), - zap.Uint32("charID", s.charID), - ) - doAckSimpleFail(s, pkt.AckHandle, nil) - return - } - + s.server.db.Exec(`UPDATE characters SET restrict_guild_scout=$1 WHERE id=$2`, pkt.Reject, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, nil) } diff --git a/server/channelserver/handlers_guild_tresure.go b/server/channelserver/handlers_guild_tresure.go index f3f4815e6..6898eae24 100644 --- a/server/channelserver/handlers_guild_tresure.go +++ b/server/channelserver/handlers_guild_tresure.go @@ -22,8 +22,8 @@ type TreasureHunt struct { func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuildTresure) - guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil || guild == nil { + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } @@ -32,7 +32,7 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { switch pkt.MaxHunts { case 1: - err = s.server.db.QueryRowx(`SELECT id, host_id, destination, level, start, hunt_data FROM guild_hunts WHERE host_id=$1 AND acquired=FALSE`, s.charID).StructScan(&hunt) + err := s.server.db.QueryRowx(`SELECT id, host_id, destination, level, start, hunt_data FROM guild_hunts WHERE host_id=$1 AND acquired=FALSE`, s.charID).StructScan(&hunt) if err == nil { hunts = append(hunts, hunt) } @@ -83,8 +83,8 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfRegistGuildTresure) bf := byteframe.NewByteFrameFromBytes(pkt.Data) huntData := byteframe.NewByteFrame() - guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil || guild == nil { + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index c91660b54..48946e89e 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -75,18 +75,18 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) { } } case 2: - guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil || guild == nil { + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 { break } - guildMembers, err := GetGuildMembers(s, guild.ID, false) - if err != nil { + guildMembers := GetGuildMembers(s, guild.ID) + if len(guildMembers) == 0 { break } for _, member := range guildMembers { house := HouseData{} row := s.server.db.QueryRowx(houseQuery, member.CharID) - err = row.StructScan(&house) + err := row.StructScan(&house) if err == nil { houses = append(houses, house) } @@ -176,12 +176,11 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) { // Guild verification if state > 3 { - ownGuild, err := GetGuildInfoByCharacterId(s, s.charID) - isApplicant, _ := ownGuild.HasApplicationForCharID(s, s.charID) - if err == nil && ownGuild != nil { - othersGuild, err := GetGuildInfoByCharacterId(s, pkt.CharID) - if err == nil && othersGuild != nil { - if othersGuild.ID == ownGuild.ID && !isApplicant { + ownGuild := GetGuildInfoByCharacterId(s, s.charID) + if ownGuild.ID != 0 && !IsGuildApplicant(s, s.charID) { + othersGuild := GetGuildInfoByCharacterId(s, pkt.CharID) + if othersGuild.ID != 0 && !IsGuildApplicant(s, pkt.CharID) { + if othersGuild.ID == ownGuild.ID { allowed = true } } diff --git a/server/channelserver/handlers_mail.go b/server/channelserver/handlers_mail.go index a596d927b..afcbacb68 100644 --- a/server/channelserver/handlers_mail.go +++ b/server/channelserver/handlers_mail.go @@ -1,7 +1,6 @@ package channelserver import ( - "database/sql" "erupe-ce/common/stringsupport" "time" @@ -29,36 +28,16 @@ type Mail struct { SenderName string `db:"sender_name"` } -func (m *Mail) Send(s *Session, transaction *sql.Tx) error { +func (m *Mail) Send(s *Session) error { query := ` INSERT INTO mail (sender_id, recipient_id, subject, body, attached_item, attached_item_amount, is_guild_invite, is_sys_message) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ` - - var err error - - if transaction == nil { - _, err = s.server.db.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage) - } else { - _, err = transaction.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage) - } - + _, err := s.server.db.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage) if err != nil { - s.logger.Error( - "failed to send mail", - zap.Error(err), - zap.Uint32("senderID", m.SenderID), - zap.Uint32("recipientID", m.RecipientID), - zap.String("subject", m.Subject), - zap.String("body", m.Body), - zap.Uint16("itemID", m.AttachedItemID), - zap.Uint16("itemAmount", m.AttachedItemAmount), - zap.Bool("isGuildInvite", m.IsGuildInvite), - zap.Bool("isSystemMessage", m.IsSystemMessage), - ) + s.logger.Error("failed to send mail", zap.Error(err)) return err } - return nil } @@ -189,16 +168,9 @@ func SendMailNotification(s *Session, m *Mail, recipient *Session) { } func getCharacterName(s *Session, charID uint32) string { - row := s.server.db.QueryRow("SELECT name FROM characters WHERE id = $1", charID) - - charName := "" - - err := row.Scan(&charName) - - if err != nil { - return "" - } - return charName + var name string + s.server.db.QueryRow("SELECT name FROM characters WHERE id = $1", charID).Scan(&name) + return name } func handleMsgMhfReadMail(s *Session, p mhfpacket.MHFPacket) { @@ -255,26 +227,21 @@ func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) { msg.WriteUint8(accIndex) msg.WriteUint8(uint8(i)) - flags := uint8(0x00) - + var flags uint8 if m.Read { - flags |= 0x01 + flags |= 1 << 0 } - if m.Locked { - flags |= 0x02 + flags |= 1 << 1 } - if m.IsSystemMessage { - flags |= 0x04 + flags |= 1 << 2 } - if m.AttachedItemReceived { - flags |= 0x08 + flags |= 1 << 3 } - if m.IsGuildInvite { - flags |= 0x10 + flags |= 1 << 4 } msg.WriteUint8(flags) @@ -322,31 +289,20 @@ func handleMsgMhfSendMail(s *Session, p mhfpacket.MHFPacket) { ` if pkt.RecipientID == 0 { // Guild mail - g, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil { - s.logger.Error("Failed to get guild info for mail") + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 { + s.logger.Error("failed to get guild info for mail") doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) return } - gm, err := GetGuildMembers(s, g.ID, false) - if err != nil { - s.logger.Error("Failed to get guild members for mail") - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) - return - } - for i := 0; i < len(gm); i++ { - _, err := s.server.db.Exec(query, s.charID, gm[i].CharID, pkt.Subject, pkt.Body, 0, 0, false) - if err != nil { - s.logger.Error("Failed to send mail") - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) - return + guildMembers := GetGuildMembers(s, guild.ID) + if len(guildMembers) > 0 { + for _, member := range guildMembers { + s.server.db.Exec(query, s.charID, member.CharID, pkt.Subject, pkt.Body, 0, 0, false) } } } else { - _, err := s.server.db.Exec(query, s.charID, pkt.RecipientID, pkt.Subject, pkt.Body, pkt.ItemID, pkt.Quantity, false) - if err != nil { - s.logger.Error("Failed to send mail") - } + s.server.db.Exec(query, s.charID, pkt.RecipientID, pkt.Subject, pkt.Body, pkt.ItemID, pkt.Quantity, false) } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index 8f1f4f018..914084ad6 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -327,8 +327,8 @@ type Airou struct { func getGuildAirouList(s *Session) []Airou { var guildCats []Airou bannedCats := make(map[uint32]int) - guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil { + guild := GetGuildInfoByCharacterId(s, s.charID) + if guild.ID == 0 { return guildCats } rows, err := s.server.db.Query(`SELECT cats_used FROM guild_hunts gh diff --git a/server/channelserver/handlers_rengoku.go b/server/channelserver/handlers_rengoku.go index 63a591fd2..73541b6a0 100644 --- a/server/channelserver/handlers_rengoku.go +++ b/server/channelserver/handlers_rengoku.go @@ -111,14 +111,9 @@ type RengokuScore struct { func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateRengokuRanking) - guild, _ := GetGuildInfoByCharacterId(s, s.charID) - isApplicant, _ := guild.HasApplicationForCharID(s, s.charID) - if isApplicant { - guild = nil - } - + guild := GetGuildInfoByCharacterId(s, s.charID) if pkt.Leaderboard == 2 || pkt.Leaderboard == 3 || pkt.Leaderboard == 6 || pkt.Leaderboard == 7 { - if guild == nil { + if guild.ID == 0 || IsGuildApplicant(s, s.charID) { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 11)) return }