diff --git a/network/mhfpacket/msg_mhf_enumerate_warehouse.go b/network/mhfpacket/msg_mhf_enumerate_warehouse.go index f567e8bcb..3f1358045 100644 --- a/network/mhfpacket/msg_mhf_enumerate_warehouse.go +++ b/network/mhfpacket/msg_mhf_enumerate_warehouse.go @@ -1,15 +1,19 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE -type MsgMhfEnumerateWarehouse struct{} +type MsgMhfEnumerateWarehouse struct { + AckHandle uint32 + BoxType string + BoxIndex uint8 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID { @@ -18,7 +22,17 @@ func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + boxType := bf.ReadUint8() + switch boxType { + case 0: + m.BoxType = "item" + case 1: + m.BoxType = "equip" + } + m.BoxIndex = bf.ReadUint8() + _ = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_operate_warehouse.go b/network/mhfpacket/msg_mhf_operate_warehouse.go index 9df4e3770..ba0ee7b78 100644 --- a/network/mhfpacket/msg_mhf_operate_warehouse.go +++ b/network/mhfpacket/msg_mhf_operate_warehouse.go @@ -1,15 +1,22 @@ package mhfpacket -import ( - "errors" +import ( + "errors" + "erupe-ce/common/stringsupport" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfOperateWarehouse represents the MSG_MHF_OPERATE_WAREHOUSE -type MsgMhfOperateWarehouse struct{} +type MsgMhfOperateWarehouse struct { + AckHandle uint32 + Operation uint8 + BoxType string + BoxIndex uint8 + Name string +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID { @@ -18,7 +25,20 @@ func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfOperateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Operation = bf.ReadUint8() + boxType := bf.ReadUint8() + switch boxType { + case 0: + m.BoxType = "item" + case 1: + m.BoxType = "equip" + } + m.BoxIndex = bf.ReadUint8() + _ = bf.ReadUint8() // lenName + _ = bf.ReadUint16() // Unk + m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_update_warehouse.go b/network/mhfpacket/msg_mhf_update_warehouse.go index ba0321910..962906988 100644 --- a/network/mhfpacket/msg_mhf_update_warehouse.go +++ b/network/mhfpacket/msg_mhf_update_warehouse.go @@ -1,15 +1,28 @@ package mhfpacket -import ( - "errors" - - "erupe-ce/network/clientctx" - "erupe-ce/network" +import ( + "errors" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) +type WarehouseStack struct { + ID uint32 + Index uint16 + EquipType uint16 + ItemID uint16 + Quantity uint16 + Data []byte +} + // MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE -type MsgMhfUpdateWarehouse struct{} +type MsgMhfUpdateWarehouse struct { + AckHandle uint32 + BoxType string + BoxIndex uint8 + Updates []WarehouseStack +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID { @@ -18,7 +31,37 @@ func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + boxType := bf.ReadUint8() + switch boxType { + case 0: + m.BoxType = "item" + case 1: + m.BoxType = "equip" + } + m.BoxIndex = bf.ReadUint8() + changes := int(bf.ReadUint16()) + var stackUpdate WarehouseStack + for i := 0; i < changes; i++ { + switch boxType { + case 0: + stackUpdate.ID = bf.ReadUint32() + stackUpdate.Index = bf.ReadUint16() + stackUpdate.ItemID = bf.ReadUint16() + stackUpdate.Quantity = bf.ReadUint16() + _ = bf.ReadUint16() // Unk + m.Updates = append(m.Updates, stackUpdate) + case 1: + stackUpdate.ID = bf.ReadUint32() + stackUpdate.Index = bf.ReadUint16() + stackUpdate.EquipType = bf.ReadUint16() + stackUpdate.ItemID = bf.ReadUint16() + stackUpdate.Data = bf.ReadBytes(56) + m.Updates = append(m.Updates, stackUpdate) + } + } + _ = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/patch-schema/warehouse.sql b/patch-schema/warehouse.sql new file mode 100644 index 000000000..2f2a5adde --- /dev/null +++ b/patch-schema/warehouse.sql @@ -0,0 +1,49 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS public.warehouse ( + character_id integer PRIMARY KEY, + item0 bytea, + item1 bytea, + item2 bytea, + item3 bytea, + item4 bytea, + item5 bytea, + item6 bytea, + item7 bytea, + item8 bytea, + item9 bytea, + item10 bytea, + item0name text, + item1name text, + item2name text, + item3name text, + item4name text, + item5name text, + item6name text, + item7name text, + item8name text, + item9name text, + equip0 bytea, + equip1 bytea, + equip2 bytea, + equip3 bytea, + equip4 bytea, + equip5 bytea, + equip6 bytea, + equip7 bytea, + equip8 bytea, + equip9 bytea, + equip10 bytea, + equip0name text, + equip1name text, + equip2name text, + equip3name text, + equip4name text, + equip5name text, + equip6name text, + equip7name text, + equip8name text, + equip9name text +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 401347b76..b4b97e3fa 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -5,11 +5,37 @@ import ( ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" "erupe-ce/network/mhfpacket" + "fmt" "go.uber.org/zap" "io" "time" ) +const warehouseNamesQuery = ` +SELECT +COALESCE(item0name, ''), +COALESCE(item1name, ''), +COALESCE(item2name, ''), +COALESCE(item3name, ''), +COALESCE(item4name, ''), +COALESCE(item5name, ''), +COALESCE(item6name, ''), +COALESCE(item7name, ''), +COALESCE(item8name, ''), +COALESCE(item9name, ''), +COALESCE(equip0name, ''), +COALESCE(equip1name, ''), +COALESCE(equip2name, ''), +COALESCE(equip3name, ''), +COALESCE(equip4name, ''), +COALESCE(equip5name, ''), +COALESCE(equip6name, ''), +COALESCE(equip7name, ''), +COALESCE(equip8name, ''), +COALESCE(equip9name, '') +FROM warehouse +` + func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateInterior) _, err := s.server.db.Exec("UPDATE characters SET house=$1 WHERE id=$2", pkt.InteriorData, s.charID) @@ -341,8 +367,170 @@ func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfOperateWarehouse) + var t int + err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t) + if err != nil { + s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID) + } + bf := byteframe.NewByteFrame() + bf.WriteUint8(pkt.Operation) + switch pkt.Operation { + case 0: + var count uint8 + itemNames := make([]string, 10) + equipNames := make([]string, 10) + s.server.db.QueryRow(fmt.Sprintf("%s WHERE character_id=$1", warehouseNamesQuery), s.charID).Scan(&itemNames[0], + &itemNames[1], &itemNames[2], &itemNames[3], &itemNames[4], &itemNames[5], &itemNames[6], &itemNames[7], &itemNames[8], &itemNames[9], &equipNames[0], + &equipNames[1], &equipNames[2], &equipNames[3], &equipNames[4], &equipNames[5], &equipNames[6], &equipNames[7], &equipNames[8], &equipNames[9]) + bf.WriteUint32(0) + bf.WriteUint16(10000) // Usages + temp := byteframe.NewByteFrame() + for i, name := range itemNames { + if len(name) > 0 { + count++ + temp.WriteUint8(0) + temp.WriteUint8(uint8(i)) + ps.Uint8(temp, name, true) + } + } + for i, name := range equipNames { + if len(name) > 0 { + count++ + temp.WriteUint8(1) + temp.WriteUint8(uint8(i)) + ps.Uint8(temp, name, true) + } + } + bf.WriteUint8(count) + bf.WriteBytes(temp.Data()) + case 1: + bf.WriteUint8(0) + case 2: + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%dname=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), pkt.Name, s.charID) + case 3: + bf.WriteUint32(0) // Usage renewal time, >1 = disabled + bf.WriteUint16(10000) // Usages + case 4: + bf.WriteUint32(0) + bf.WriteUint16(10000) // Usages + bf.WriteUint8(0) + } + // Opcodes + // 0 = Get box names + // 1 = Commit usage + // 2 = Rename + // 3 = Get usage limit + // 4 = Get gift box names (doesn't do anything?) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} -func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {} +func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.WarehouseStack { + var data []byte + s.server.db.QueryRow(fmt.Sprintf("SELECT %s%d FROM warehouse WHERE character_id=$1", boxType, boxIndex), s.charID).Scan(&data) + if len(data) > 0 { + box := byteframe.NewByteFrameFromBytes(data) + numStacks := box.ReadUint16() + stacks := make([]mhfpacket.WarehouseStack, numStacks) + for i := 0; i < int(numStacks); i++ { + if boxType == "item" { + stacks[i].ID = box.ReadUint32() + stacks[i].Index = box.ReadUint16() + stacks[i].ItemID = box.ReadUint16() + stacks[i].Quantity = box.ReadUint16() + box.ReadUint16() + } else { + stacks[i].ID = box.ReadUint32() + stacks[i].Index = box.ReadUint16() + stacks[i].EquipType = box.ReadUint16() + stacks[i].ItemID = box.ReadUint16() + stacks[i].Data = box.ReadBytes(56) + } + } + return stacks + } else { + return make([]mhfpacket.WarehouseStack, 0) + } +} -func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {} +func boxToBytes(stacks []mhfpacket.WarehouseStack, boxType string) []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(len(stacks))) + for i, stack := range stacks { + if boxType == "item" { + bf.WriteUint32(stack.ID) + bf.WriteUint16(uint16(i + 1)) + bf.WriteUint16(stack.ItemID) + bf.WriteUint16(stack.Quantity) + bf.WriteUint16(0) + } else { + bf.WriteUint32(stack.ID) + bf.WriteUint16(uint16(i + 1)) + bf.WriteUint16(stack.EquipType) + bf.WriteUint16(stack.ItemID) + bf.WriteBytes(stack.Data) + } + } + bf.WriteUint16(0) + return bf.Data() +} + +func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse) + box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex) + if len(box) > 0 { + doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box, pkt.BoxType)) + } else { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + } +} + +func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse) + box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex) + // Update existing stacks + var newStacks []mhfpacket.WarehouseStack + for _, update := range pkt.Updates { + exists := false + if pkt.BoxType == "item" { + for i, stack := range box { + if stack.Index == update.Index { + exists = true + box[i].Quantity = update.Quantity + break + } + } + } else { + for i, stack := range box { + if stack.Index == update.Index { + exists = true + box[i].ItemID = update.ItemID + break + } + } + } + if exists == false { + newStacks = append(newStacks, update) + } + } + // Append new stacks + for _, stack := range newStacks { + box = append(box, stack) + } + // Slice empty stacks + var cleanedBox []mhfpacket.WarehouseStack + for _, stack := range box { + if pkt.BoxType == "item" { + if stack.Quantity > 0 { + cleanedBox = append(cleanedBox, stack) + } + } else { + if stack.ItemID != 0 { + cleanedBox = append(cleanedBox, stack) + } + } + } + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox, pkt.BoxType), s.charID) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +}