diff --git a/Erupe/guild-additions.sql b/Erupe/guild-additions.sql new file mode 100644 index 000000000..d3299119e --- /dev/null +++ b/Erupe/guild-additions.sql @@ -0,0 +1,20 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.guilds +( + ADD COLUMN pugi_name_1 character varying(12), + ADD COLUMN pugi_name_2 character varying(12), + ADD COLUMN pugi_name_3 character varying(12) +); + +CREATE TABLE IF NOT EXISTS public.guild_alliances +( + id serial NOT NULL PRIMARY KEY, + name character varying(24) NOT NULL, + created_at timestamp without time zone NOT NULL, + parent_id int NOT NULL, + sub1_id int, + sub2_id int +); + +END; \ No newline at end of file diff --git a/Erupe/network/mhfpacket/msg_mhf_create_joint.go b/Erupe/network/mhfpacket/msg_mhf_create_joint.go index 2436c8895..c273a95c2 100644 --- a/Erupe/network/mhfpacket/msg_mhf_create_joint.go +++ b/Erupe/network/mhfpacket/msg_mhf_create_joint.go @@ -1,15 +1,20 @@ package mhfpacket -import ( - "errors" +import ( + "errors" + "github.com/Solenataris/Erupe/common/bfutil" "github.com/Solenataris/Erupe/network/clientctx" "github.com/Solenataris/Erupe/network" "github.com/Andoryuuta/byteframe" ) // MsgMhfCreateJoint represents the MSG_MHF_CREATE_JOINT -type MsgMhfCreateJoint struct{} +type MsgMhfCreateJoint struct { + AckHandle uint32 + GuildID uint32 + Name string +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfCreateJoint) Opcode() network.PacketID { @@ -18,7 +23,12 @@ func (m *MsgMhfCreateJoint) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfCreateJoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.GuildID = bf.ReadUint32() + nameLength := bf.ReadUint32() + nameBytes := bfutil.UpToNull(bf.ReadBytes(uint(nameLength))) + m.Name = ctx.StrConv.MustDecode(nameBytes) + return nil } // Build builds a binary packet from the current data. diff --git a/Erupe/server/channelserver/handlers_guild.go b/Erupe/server/channelserver/handlers_guild.go index 04dd23642..507e78456 100644 --- a/Erupe/server/channelserver/handlers_guild.go +++ b/Erupe/server/channelserver/handlers_guild.go @@ -1,7 +1,6 @@ package channelserver import ( - "bytes" "database/sql" "database/sql/driver" "encoding/binary" @@ -9,9 +8,6 @@ import ( "encoding/json" "errors" "fmt" - "io" - "io/ioutil" - "log" "sort" "strconv" "strings" @@ -23,8 +19,6 @@ import ( "github.com/Solenataris/Erupe/network/mhfpacket" "github.com/jmoiron/sqlx" "go.uber.org/zap" - "golang.org/x/text/encoding/japanese" - "golang.org/x/text/transform" ) type FestivalColour string @@ -764,7 +758,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { } func handleRenamePugi(s *Session, data []byte, guild *Guild, num int) { - bf := NewByteFrameFromBytes(data) + bf := byteframe.NewByteFrameFromBytes(data) _ = bf.ReadUint8() // len _ = bf.ReadUint32() // unk name, _ := stringsupport.ConvertSJISBytesToString(bf.ReadNullTerminatedBytes()) @@ -776,7 +770,7 @@ func handleRenamePugi(s *Session, data []byte, guild *Guild, num int) { default: guild.PugiName3 = name } - guild.Save() + guild.Save(s) } func handleDonateRP(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, bf *byteframe.ByteFrame, guild *Guild, isEvent bool) error { @@ -1918,9 +1912,3 @@ func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUpdateGuildcard(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfCreateJoint(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {} diff --git a/Erupe/server/channelserver/handlers_guild_alliance.go b/Erupe/server/channelserver/handlers_guild_alliance.go new file mode 100644 index 000000000..6d29e9c61 --- /dev/null +++ b/Erupe/server/channelserver/handlers_guild_alliance.go @@ -0,0 +1,157 @@ +package channelserver + +import ( + "fmt" + "time" + + "github.com/Solenataris/Erupe/network/mhfpacket" + "github.com/jmoiron/sqlx" + "go.uber.org/zap" +) + +const allianceInfoSelectQuery = ` +SELECT +ga.id, +ga.name, +created_at, +parent_id, +CASE + WHEN sub1_id IS NULL THEN 0 + ELSE sub1_id +END, +CASE + WHEN sub2_id IS NULL THEN 0 + ELSE sub2_id +END +FROM guild_alliances ga +` + +type GuildAlliance struct { + ID uint32 `db:"id"` + Name string `db:"name"` + CreatedAt time.Time `db:"created_at"` + TotalMembers uint16 + + ParentGuildID uint32 `db:"parent_id"` + SubGuild1ID uint32 `db:"sub1_id"` + SubGuild2ID uint32 `db:"sub2_id"` + + ParentGuild Guild + SubGuild1 Guild + SubGuild2 Guild +} + +func GetAllianceData(s *Session, AllianceID uint32) (*GuildAlliance, error) { + rows, err := s.server.db.Queryx(fmt.Sprintf(` + %s + WHERE ga.id = $1 + `, allianceInfoSelectQuery), AllianceID) + if err != nil { + s.logger.Error("Failed to retrieve alliance data from database", zap.Error(err)) + return nil, err + } + defer rows.Close() + hasRow := rows.Next() + if !hasRow { + return nil, nil + } + + return buildAllianceObjectFromDbResult(rows, err, s) +} + +func buildAllianceObjectFromDbResult(result *sqlx.Rows, 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.Fatal("Failed to get parent guild info", 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.Fatal("Failed to get sub guild 1 info", zap.Error(err)) + } else { + alliance.SubGuild1 = *subGuild1 + alliance.TotalMembers += subGuild1.MemberCount + } + } + + if alliance.SubGuild2ID > 0 { + subGuild2, err := GetGuildInfoByID(s, alliance.SubGuild2ID) + if err != nil { + s.logger.Fatal("Failed to get sub guild 2 info", zap.Error(err)) + } else { + alliance.SubGuild2 = *subGuild2 + alliance.TotalMembers += subGuild2.MemberCount + } + } + + return alliance, nil +} + +func handleMsgMhfCreateJoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfCreateJoint) + _, err := s.server.db.Exec("INSERT INTO guild_alliances (name, parent_id) VALUES ($1, $2)", pkt.Name, pkt.GuildID) + if err != nil { + s.logger.Fatal("Failed to create guild alliance in db", zap.Error(err)) + } + doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x01, 0x01, 0x01, 0x01}) +} + +func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfOperateJoint) + + guild, err := GetGuildInfoByID(s, pkt.GuildID) + if err != nil { + s.logger.Fatal("Failed to get guild info", zap.Error(err)) + } + alliance, err := GetAllianceData(s, pkt.AllianceID) + if err != nil { + s.logger.Fatal("Failed to get alliance info", zap.Error(err)) + } + + switch pkt.Action { + case mhfpacket.OPERATE_JOINT_DISBAND: + if guild.LeaderCharID == s.charID && alliance.ParentGuildID == guild.ID { + _, err = s.server.db.Exec("DELETE FROM guild_alliances WHERE id=$1", alliance.ID) + if err != nil { + s.logger.Fatal("Failed to disband alliance", zap.Error(err)) + } + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + } else { + s.logger.Warn( + "Non-owner of alliance attempted disband", + zap.Uint32("CharID", s.charID), + zap.Uint32("AllyID", alliance.ID), + ) + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + } + case mhfpacket.OPERATE_JOINT_LEAVE: + if guild.LeaderCharID == s.charID { + // delete alliance application + // or leave alliance + } else { + s.logger.Warn( + "Non-owner of guild attempted alliance leave", + zap.Uint32("CharID", s.charID), + ) + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + } + default: + panic(fmt.Sprintf("Unhandled operate joint action '%d'", pkt.Action)) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + } +} + +func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {}