From b8f5aa87a28673de19d07cf4b5a0da89ff8266b0 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 24 Jul 2022 12:32:41 +1000 Subject: [PATCH] implement basic my series functionality --- .../mhfpacket/msg_mhf_enumerate_house.go | 5 +- Erupe/network/mhfpacket/msg_mhf_load_house.go | 35 ++-- .../network/mhfpacket/msg_mhf_update_house.go | 18 +-- Erupe/server/channelserver/handlers_data.go | 2 + Erupe/server/channelserver/handlers_house.go | 150 +++++++++++++++++- Erupe/server/channelserver/sys_session.go | 7 + 6 files changed, 175 insertions(+), 42 deletions(-) diff --git a/Erupe/network/mhfpacket/msg_mhf_enumerate_house.go b/Erupe/network/mhfpacket/msg_mhf_enumerate_house.go index 3758a8c38..9bd1f30ef 100644 --- a/Erupe/network/mhfpacket/msg_mhf_enumerate_house.go +++ b/Erupe/network/mhfpacket/msg_mhf_enumerate_house.go @@ -2,6 +2,7 @@ package mhfpacket import ( "errors" + "erupe-ce/common/stringsupport" "erupe-ce/common/byteframe" "erupe-ce/network" @@ -14,7 +15,7 @@ type MsgMhfEnumerateHouse struct { CharID uint32 Method uint8 Unk uint16 - Name []byte + Name string } // Opcode returns the ID associated with this packet type. @@ -29,7 +30,7 @@ func (m *MsgMhfEnumerateHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli m.Method = bf.ReadUint8() m.Unk = bf.ReadUint16() _ = bf.ReadUint8() // len - m.Name = bf.ReadNullTerminatedBytes() + m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) return nil } diff --git a/Erupe/network/mhfpacket/msg_mhf_load_house.go b/Erupe/network/mhfpacket/msg_mhf_load_house.go index db0a04307..ebc7cb0ee 100644 --- a/Erupe/network/mhfpacket/msg_mhf_load_house.go +++ b/Erupe/network/mhfpacket/msg_mhf_load_house.go @@ -1,29 +1,22 @@ package mhfpacket import ( - "errors" + "errors" + "erupe-ce/common/stringsupport" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfLoadHouse represents the MSG_MHF_LOAD_HOUSE type MsgMhfLoadHouse struct { - AckHandle uint32 - CharID uint32 - // dest? - // 0x3 = house - // 0x4 = bookshelf - // 0x5 = gallery - // 0x8 = tore - // 0x9 = own house - // 0xA = garden - Unk1 uint8 - // bool inMezSquare? - Unk2 uint8 - Unk3 uint16 // Hardcoded 0 in binary - Password []byte + AckHandle uint32 + CharID uint32 + Destination uint8 + InMezeporta bool + Unk3 uint16 // Hardcoded 0 in binary + Password string } // Opcode returns the ID associated with this packet type. @@ -35,11 +28,11 @@ func (m *MsgMhfLoadHouse) Opcode() network.PacketID { func (m *MsgMhfLoadHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.CharID = bf.ReadUint32() - m.Unk1 = bf.ReadUint8() - m.Unk2 = bf.ReadUint8() + m.Destination = bf.ReadUint8() + m.InMezeporta = bf.ReadBool() _ = bf.ReadUint16() - _ = bf.ReadUint8() // Password length - m.Password = bf.ReadNullTerminatedBytes() + _ = bf.ReadUint8() // Password length + m.Password = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) return nil } diff --git a/Erupe/network/mhfpacket/msg_mhf_update_house.go b/Erupe/network/mhfpacket/msg_mhf_update_house.go index 47354a23f..320972673 100644 --- a/Erupe/network/mhfpacket/msg_mhf_update_house.go +++ b/Erupe/network/mhfpacket/msg_mhf_update_house.go @@ -2,6 +2,7 @@ package mhfpacket import ( "errors" + "erupe-ce/common/stringsupport" "erupe-ce/common/byteframe" "erupe-ce/network" @@ -11,15 +12,10 @@ import ( // MsgMhfUpdateHouse represents the MSG_MHF_UPDATE_HOUSE type MsgMhfUpdateHouse struct { AckHandle uint32 - // 01 = closed - // 02 = open anyone - // 03 = open friends - // 04 = open guild - // 05 = open friends guild - State uint8 - Unk1 uint8 // Always 0x01 - Unk2 uint16 // Always 0x0000 - Password string + State uint8 + Unk1 uint8 // Always 0x01 + Unk2 uint16 // Always 0x0000 + Password string } // Opcode returns the ID associated with this packet type. @@ -33,8 +29,8 @@ func (m *MsgMhfUpdateHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client m.State = bf.ReadUint8() m.Unk1 = bf.ReadUint8() m.Unk2 = bf.ReadUint16() - _ = bf.ReadUint8() - m.Password = string(bf.ReadNullTerminatedBytes()) + _ = bf.ReadUint8() // Password length + m.Password = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) return nil } diff --git a/Erupe/server/channelserver/handlers_data.go b/Erupe/server/channelserver/handlers_data.go index ea6724eec..88f634ba3 100644 --- a/Erupe/server/channelserver/handlers_data.go +++ b/Erupe/server/channelserver/handlers_data.go @@ -61,6 +61,8 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { s.logger.Fatal("Failed to character weapon type in db", zap.Error(err)) } + s.house.tier = decompressedData[129904] + isMale := uint8(decompressedData[80]) // 0x50 if isMale == 1 { _, err = s.server.db.Exec("UPDATE characters SET is_female=true WHERE id=$1", s.charID) diff --git a/Erupe/server/channelserver/handlers_house.go b/Erupe/server/channelserver/handlers_house.go index 94ce5e3b4..30a016abb 100644 --- a/Erupe/server/channelserver/handlers_house.go +++ b/Erupe/server/channelserver/handlers_house.go @@ -2,6 +2,8 @@ package channelserver import ( "erupe-ce/common/byteframe" + ps "erupe-ce/common/pascalstring" + "erupe-ce/common/stringsupport" "erupe-ce/network/mhfpacket" "go.uber.org/zap" ) @@ -15,28 +17,160 @@ func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } -func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfEnumerateHouse) - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) +type HouseData struct { + CharID uint32 `db:"id"` + HRP uint16 `db:"hrp"` + GR uint16 `db:"gr"` + Name string `db:"name"` } -func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateHouse) + bf := byteframe.NewByteFrame() + var houses []HouseData + switch pkt.Method { + case 1: + var friendsList string + s.server.db.QueryRow("SELECT friends FROM characters WHERE id=$1", s.charID).Scan(&friendsList) + cids := stringsupport.CSVElems(friendsList) + for _, cid := range cids { + house := HouseData{} + row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", cid) + err := row.StructScan(&house) + if err != nil { + panic(err) + } else { + houses = append(houses, house) + } + } + case 2: + guild, err := GetGuildInfoByCharacterId(s, s.charID) + if err != nil { + break + } + guildMembers, err := GetGuildMembers(s, guild.ID, false) + if err != nil { + break + } + for _, member := range guildMembers { + house := HouseData{} + row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", member.CharID) + err := row.StructScan(&house) + if err != nil { + panic(err) + } else { + houses = append(houses, house) + } + } + case 3: + house := HouseData{} + row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name=$1", pkt.Name) + err := row.StructScan(&house) + if err != nil { + panic(err) + } else { + houses = append(houses, house) + } + case 4: + house := HouseData{} + row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", pkt.CharID) + err := row.StructScan(&house) + if err != nil { + panic(err) + } else { + houses = append(houses, house) + } + case 5: // Recent visitors + break + } + var exists int + for _, house := range houses { + for _, session := range s.server.sessions { + if session.charID == house.CharID { + exists++ + bf.WriteUint32(house.CharID) + bf.WriteUint8(session.house.state) + if len(session.house.password) > 0 { + bf.WriteUint8(3) + } else { + bf.WriteUint8(0) + } + bf.WriteUint16(house.HRP) + bf.WriteUint16(house.GR) + ps.Uint8(bf, house.Name, true) + break + } + } + } + resp := byteframe.NewByteFrame() + resp.WriteUint16(uint16(exists)) + resp.WriteBytes(bf.Data()) + doAckBufSucceed(s, pkt.AckHandle, resp.Data()) +} + +func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfUpdateHouse) + // 01 = closed + // 02 = open anyone + // 03 = open friends + // 04 = open guild + // 05 = open friends+guild + s.house.state = pkt.State + s.house.password = pkt.Password + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfLoadHouse) bf := byteframe.NewByteFrame() var data []byte - err := s.server.db.QueryRow("SELECT house FROM characters WHERE id=$1", s.charID).Scan(&data) + err := s.server.db.QueryRow("SELECT house FROM characters WHERE id=$1", pkt.CharID).Scan(&data) if err != nil { panic(err) } if data == nil { data = make([]byte, 20) } - if pkt.CharID != s.charID { - bf.WriteBytes(make([]byte, 219)) + // TODO: Find where the missing data comes from, savefile offset? + switch pkt.Destination { + case 3: // Others house + houseTier := uint8(2) // Fallback if can't find + for _, session := range s.server.sessions { + if session.charID == pkt.CharID { + if pkt.Password != session.house.password { + // Not the correct error code but works + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + return + } + houseTier = session.house.tier + } + } + bf.WriteBytes(make([]byte, 4)) + bf.WriteUint8(houseTier) // House tier 0x1FB70 + // Item box style + // Rastae + // Partner + bf.WriteBytes(make([]byte, 214)) + bf.WriteBytes(data) + case 4: // Bookshelf + // Hunting log + // Street names/Aliases + bf.WriteBytes(make([]byte, 5576)) + case 5: // Gallery + // Furniture placement + bf.WriteBytes(make([]byte, 1748)) + case 8: // Tore + // Sister + // Cat shops + // Pugis + bf.WriteBytes(make([]byte, 240)) + case 9: // Own house + bf.WriteBytes(data) + case 10: // Garden + // Gardening upgrades + // Gooks + bf.WriteBytes(make([]byte, 72)) } - bf.WriteBytes(data) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } diff --git a/Erupe/server/channelserver/sys_session.go b/Erupe/server/channelserver/sys_session.go index a023381ff..d4de598a5 100644 --- a/Erupe/server/channelserver/sys_session.go +++ b/Erupe/server/channelserver/sys_session.go @@ -27,6 +27,7 @@ type Session struct { sendPackets chan []byte clientContext *clientctx.ClientContext + house House stageID string stage *Stage reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet. @@ -54,6 +55,12 @@ type Session struct { Name string } +type House struct { + tier uint8 + state uint8 + password string +} + // NewSession creates a new Session type. func NewSession(server *Server, conn net.Conn) *Session { s := &Session{