diff --git a/common/mhfitem/mhfitem.go b/common/mhfitem/mhfitem.go new file mode 100644 index 000000000..58fbf45e7 --- /dev/null +++ b/common/mhfitem/mhfitem.go @@ -0,0 +1,175 @@ +package mhfitem + +import ( + "erupe-ce/common/byteframe" + "erupe-ce/common/token" + _config "erupe-ce/config" +) + +type MHFItem struct { + ItemID uint16 +} + +type MHFSigilEffect struct { + ID uint16 + Level uint16 +} + +type MHFSigil struct { + Effects []MHFSigilEffect + Unk0 uint8 + Unk1 uint8 + Unk2 uint8 + Unk3 uint8 +} + +type MHFEquipment struct { + WarehouseID uint32 + ItemType uint8 + Unk0 uint8 + ItemID uint16 + Level uint16 + Decorations []MHFItem + Sigils []MHFSigil + Unk1 uint16 +} + +type MHFItemStack struct { + WarehouseID uint32 + Item MHFItem + Quantity uint16 + Unk0 uint32 +} + +func ReadWarehouseItem(bf *byteframe.ByteFrame) MHFItemStack { + var item MHFItemStack + item.WarehouseID = bf.ReadUint32() + if item.WarehouseID == 0 { + item.WarehouseID = token.RNG.Uint32() + } + item.Item.ItemID = bf.ReadUint16() + item.Quantity = bf.ReadUint16() + item.Unk0 = bf.ReadUint32() + return item +} + +func DiffItemStacks(o []MHFItemStack, u []MHFItemStack) []MHFItemStack { + // o = old, u = update, f = final + var f []MHFItemStack + for _, uItem := range u { + exists := false + for i := range o { + if o[i].WarehouseID == uItem.WarehouseID { + exists = true + o[i].Quantity = uItem.Quantity + } + } + if !exists { + uItem.WarehouseID = token.RNG.Uint32() + f = append(f, uItem) + } + } + for _, oItem := range o { + if oItem.Quantity > 0 { + f = append(f, oItem) + } + } + return f +} + +func (is MHFItemStack) ToBytes() []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint32(is.WarehouseID) + bf.WriteUint16(is.Item.ItemID) + bf.WriteUint16(is.Quantity) + bf.WriteUint32(is.Unk0) + return bf.Data() +} + +func SerializeWarehouseItems(i []MHFItemStack) []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(len(i))) + bf.WriteUint16(0) // Unused + for _, j := range i { + bf.WriteBytes(j.ToBytes()) + } + return bf.Data() +} + +func ReadWarehouseEquipment(bf *byteframe.ByteFrame) MHFEquipment { + var equipment MHFEquipment + equipment.Decorations = make([]MHFItem, 3) + equipment.Sigils = make([]MHFSigil, 3) + for i := 0; i < 3; i++ { + equipment.Sigils[i].Effects = make([]MHFSigilEffect, 3) + } + equipment.WarehouseID = bf.ReadUint32() + if equipment.WarehouseID == 0 { + equipment.WarehouseID = token.RNG.Uint32() + } + equipment.ItemType = bf.ReadUint8() + equipment.Unk0 = bf.ReadUint8() + equipment.ItemID = bf.ReadUint16() + equipment.Level = bf.ReadUint16() + for i := 0; i < 3; i++ { + equipment.Decorations[i].ItemID = bf.ReadUint16() + } + if _config.ErupeConfig.RealClientMode >= _config.G1 { + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + equipment.Sigils[i].Effects[j].ID = bf.ReadUint16() + } + for j := 0; j < 3; j++ { + equipment.Sigils[i].Effects[j].Level = bf.ReadUint16() + } + equipment.Sigils[i].Unk0 = bf.ReadUint8() + equipment.Sigils[i].Unk1 = bf.ReadUint8() + equipment.Sigils[i].Unk2 = bf.ReadUint8() + equipment.Sigils[i].Unk3 = bf.ReadUint8() + } + } + if _config.ErupeConfig.RealClientMode >= _config.Z1 { + equipment.Unk1 = bf.ReadUint16() + } + return equipment +} + +func (e MHFEquipment) ToBytes() []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint32(e.WarehouseID) + bf.WriteUint8(e.ItemType) + bf.WriteUint8(e.Unk0) + bf.WriteUint16(e.ItemID) + bf.WriteUint16(e.Level) + for i := 0; i < 3; i++ { + bf.WriteUint16(e.Decorations[i].ItemID) + } + if _config.ErupeConfig.RealClientMode >= _config.G1 { + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + bf.WriteUint16(e.Sigils[i].Effects[j].ID) + } + for j := 0; j < 3; j++ { + bf.WriteUint16(e.Sigils[i].Effects[j].Level) + } + bf.WriteUint8(e.Sigils[i].Unk0) + bf.WriteUint8(e.Sigils[i].Unk1) + bf.WriteUint8(e.Sigils[i].Unk2) + bf.WriteUint8(e.Sigils[i].Unk3) + } + } + if _config.ErupeConfig.RealClientMode >= _config.Z1 { + bf.WriteUint16(e.Unk1) + } + return bf.Data() +} + +func SerializeWarehouseEquipment(i []MHFEquipment) []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(len(i))) + bf.WriteUint16(0) // Unused + for _, j := range i { + bf.WriteBytes(j.ToBytes()) + } + return bf.Data() +} diff --git a/common/token/token.go b/common/token/token.go index c474fdaf5..decd16893 100644 --- a/common/token/token.go +++ b/common/token/token.go @@ -5,18 +5,19 @@ import ( "time" ) +var RNG = NewRNG() + // Generate returns an alphanumeric token of specified length func Generate(length int) string { - rng := RNG() var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") b := make([]rune, length) for i := range b { - b[i] = chars[rng.Intn(len(chars))] + b[i] = chars[RNG.Intn(len(chars))] } return string(b) } -// RNG returns a new RNG generator -func RNG() *rand.Rand { +// NewRNG returns a new NewRNG generator +func NewRNG() *rand.Rand { return rand.New(rand.NewSource(time.Now().UnixNano())) } diff --git a/network/mhfpacket/msg_mhf_enumerate_warehouse.go b/network/mhfpacket/msg_mhf_enumerate_warehouse.go index 3f1358045..dbc740f80 100644 --- a/network/mhfpacket/msg_mhf_enumerate_warehouse.go +++ b/network/mhfpacket/msg_mhf_enumerate_warehouse.go @@ -11,7 +11,7 @@ import ( // MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE type MsgMhfEnumerateWarehouse struct { AckHandle uint32 - BoxType string + BoxType uint8 BoxIndex uint8 } @@ -23,15 +23,10 @@ func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - boxType := bf.ReadUint8() - switch boxType { - case 0: - m.BoxType = "item" - case 1: - m.BoxType = "equip" - } + m.BoxType = bf.ReadUint8() m.BoxIndex = bf.ReadUint8() - _ = bf.ReadUint16() + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_operate_warehouse.go b/network/mhfpacket/msg_mhf_operate_warehouse.go index ba0ee7b78..0ea57e6c6 100644 --- a/network/mhfpacket/msg_mhf_operate_warehouse.go +++ b/network/mhfpacket/msg_mhf_operate_warehouse.go @@ -13,7 +13,7 @@ import ( type MsgMhfOperateWarehouse struct { AckHandle uint32 Operation uint8 - BoxType string + BoxType uint8 BoxIndex uint8 Name string } @@ -27,17 +27,13 @@ func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID { func (m *MsgMhfOperateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Operation = bf.ReadUint8() - boxType := bf.ReadUint8() - switch boxType { - case 0: - m.BoxType = "item" - case 1: - m.BoxType = "equip" - } + m.BoxType = bf.ReadUint8() m.BoxIndex = bf.ReadUint8() - _ = bf.ReadUint8() // lenName - _ = bf.ReadUint16() // Unk - m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + lenName := bf.ReadUint8() + bf.ReadUint16() // Zeroed + if lenName > 0 { + m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + } return nil } diff --git a/network/mhfpacket/msg_mhf_update_guild_item.go b/network/mhfpacket/msg_mhf_update_guild_item.go index ddd7ef6e5..164adfb20 100644 --- a/network/mhfpacket/msg_mhf_update_guild_item.go +++ b/network/mhfpacket/msg_mhf_update_guild_item.go @@ -2,24 +2,18 @@ package mhfpacket import ( "errors" + "erupe-ce/common/mhfitem" "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" ) -type Item struct { - Unk0 uint32 - ItemID uint16 - Amount uint16 - Unk1 uint32 -} - // MsgMhfUpdateGuildItem represents the MSG_MHF_UPDATE_GUILD_ITEM type MsgMhfUpdateGuildItem struct { - AckHandle uint32 - GuildID uint32 - Items []Item + AckHandle uint32 + GuildID uint32 + UpdatedItems []mhfitem.MHFItemStack } // Opcode returns the ID associated with this packet type. @@ -31,18 +25,12 @@ func (m *MsgMhfUpdateGuildItem) Opcode() network.PacketID { func (m *MsgMhfUpdateGuildItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.GuildID = bf.ReadUint32() - itemCount := int(bf.ReadUint16()) + changes := int(bf.ReadUint16()) bf.ReadUint8() // Zeroed bf.ReadUint8() // Zeroed - m.Items = make([]Item, itemCount) - - for i := 0; i < itemCount; i++ { - m.Items[i].Unk0 = bf.ReadUint32() - m.Items[i].ItemID = bf.ReadUint16() - m.Items[i].Amount = bf.ReadUint16() - m.Items[i].Unk1 = bf.ReadUint32() + for i := 0; i < changes; i++ { + m.UpdatedItems = append(m.UpdatedItems, mhfitem.ReadWarehouseItem(bf)) } - return nil } diff --git a/network/mhfpacket/msg_mhf_update_union_item.go b/network/mhfpacket/msg_mhf_update_union_item.go index 68e8de365..f40dedd5f 100644 --- a/network/mhfpacket/msg_mhf_update_union_item.go +++ b/network/mhfpacket/msg_mhf_update_union_item.go @@ -2,6 +2,7 @@ package mhfpacket import ( "errors" + "erupe-ce/common/mhfitem" "erupe-ce/common/byteframe" "erupe-ce/network" @@ -10,8 +11,8 @@ import ( // MsgMhfUpdateUnionItem represents the MSG_MHF_UPDATE_UNION_ITEM type MsgMhfUpdateUnionItem struct { - AckHandle uint32 - Items []Item + AckHandle uint32 + UpdatedItems []mhfitem.MHFItemStack } // Opcode returns the ID associated with this packet type. @@ -22,18 +23,12 @@ func (m *MsgMhfUpdateUnionItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateUnionItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - itemCount := int(bf.ReadUint16()) + changes := int(bf.ReadUint16()) bf.ReadUint8() // Zeroed bf.ReadUint8() // Zeroed - m.Items = make([]Item, itemCount) - - for i := 0; i < itemCount; i++ { - m.Items[i].Unk0 = bf.ReadUint32() - m.Items[i].ItemID = bf.ReadUint16() - m.Items[i].Amount = bf.ReadUint16() - m.Items[i].Unk1 = bf.ReadUint32() + for i := 0; i < changes; i++ { + m.UpdatedItems = append(m.UpdatedItems, mhfitem.ReadWarehouseItem(bf)) } - return nil } diff --git a/network/mhfpacket/msg_mhf_update_warehouse.go b/network/mhfpacket/msg_mhf_update_warehouse.go index 962906988..9d264cf89 100644 --- a/network/mhfpacket/msg_mhf_update_warehouse.go +++ b/network/mhfpacket/msg_mhf_update_warehouse.go @@ -3,25 +3,18 @@ package mhfpacket import ( "errors" "erupe-ce/common/byteframe" + "erupe-ce/common/mhfitem" "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 { - AckHandle uint32 - BoxType string - BoxIndex uint8 - Updates []WarehouseStack + AckHandle uint32 + BoxType uint8 + BoxIndex uint8 + UpdatedItems []mhfitem.MHFItemStack + UpdatedEquipment []mhfitem.MHFEquipment } // Opcode returns the ID associated with this packet type. @@ -32,35 +25,19 @@ func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - boxType := bf.ReadUint8() - switch boxType { - case 0: - m.BoxType = "item" - case 1: - m.BoxType = "equip" - } + m.BoxType = bf.ReadUint8() m.BoxIndex = bf.ReadUint8() changes := int(bf.ReadUint16()) - var stackUpdate WarehouseStack + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed for i := 0; i < changes; i++ { - switch boxType { + switch m.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) + m.UpdatedItems = append(m.UpdatedItems, mhfitem.ReadWarehouseItem(bf)) 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) + m.UpdatedEquipment = append(m.UpdatedEquipment, mhfitem.ReadWarehouseEquipment(bf)) } } - _ = bf.ReadUint16() return nil } diff --git a/schemas/patch-schema/20-reset-warehouses.sql b/schemas/patch-schema/20-reset-warehouses.sql new file mode 100644 index 000000000..efb42f8a9 --- /dev/null +++ b/schemas/patch-schema/20-reset-warehouses.sql @@ -0,0 +1,6 @@ +BEGIN; + +UPDATE guilds SET item_box=NULL; +UPDATE users SET item_box=NULL; + +END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index e2c15a4f8..fb91f096f 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -3,6 +3,7 @@ package channelserver import ( "encoding/binary" "erupe-ce/common/mhfcourse" + "erupe-ce/common/mhfitem" "erupe-ce/common/mhfmon" ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" @@ -817,93 +818,33 @@ func handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetExtraInfo(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfEnumerateUnionItem) - var boxContents []byte - bf := byteframe.NewByteFrame() - err := s.server.db.QueryRow("SELECT item_box FROM users, characters WHERE characters.id = $1 AND users.id = characters.user_id", int(s.charID)).Scan(&boxContents) - if err != nil { - s.logger.Error("Failed to get shared item box contents from db", zap.Error(err)) - bf.WriteBytes(make([]byte, 4)) - } else { - if len(boxContents) == 0 { - bf.WriteBytes(make([]byte, 4)) - } else { - amount := len(boxContents) / 4 - bf.WriteUint16(uint16(amount)) - bf.WriteUint32(0x00) - bf.WriteUint16(0x00) - for i := 0; i < amount; i++ { - bf.WriteUint32(binary.BigEndian.Uint32(boxContents[i*4 : i*4+4])) - if i+1 != amount { - bf.WriteUint64(0x00) - } - } +func userGetItems(s *Session) []mhfitem.MHFItemStack { + var data []byte + var items []mhfitem.MHFItemStack + s.server.db.QueryRow(`SELECT item_box FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&data) + if len(data) > 0 { + box := byteframe.NewByteFrameFromBytes(data) + numStacks := box.ReadUint16() + box.ReadUint16() // Unused + for i := 0; i < int(numStacks); i++ { + items = append(items, mhfitem.ReadWarehouseItem(box)) } } + return items +} + +func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateUnionItem) + items := userGetItems(s) + bf := byteframe.NewByteFrame() + bf.WriteBytes(mhfitem.SerializeWarehouseItems(items)) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateUnionItem) - // Get item cache from DB - var boxContents []byte - var oldItems []Item - - err := s.server.db.QueryRow("SELECT item_box FROM users, characters WHERE characters.id = $1 AND users.id = characters.user_id", int(s.charID)).Scan(&boxContents) - if err != nil { - s.logger.Error("Failed to get shared item box contents from db", zap.Error(err)) - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) - return - } else { - amount := len(boxContents) / 4 - oldItems = make([]Item, amount) - for i := 0; i < amount; i++ { - oldItems[i].ItemId = binary.BigEndian.Uint16(boxContents[i*4 : i*4+2]) - oldItems[i].Amount = binary.BigEndian.Uint16(boxContents[i*4+2 : i*4+4]) - } - } - - // Update item stacks - newItems := make([]Item, len(oldItems)) - copy(newItems, oldItems) - for i := 0; i < len(pkt.Items); i++ { - for j := 0; j <= len(oldItems); j++ { - if j == len(oldItems) { - var newItem Item - newItem.ItemId = pkt.Items[i].ItemID - newItem.Amount = pkt.Items[i].Amount - newItems = append(newItems, newItem) - break - } - if pkt.Items[i].ItemID == oldItems[j].ItemId { - newItems[j].Amount = pkt.Items[i].Amount - break - } - } - } - - // Delete empty item stacks - for i := len(newItems) - 1; i >= 0; i-- { - if int(newItems[i].Amount) == 0 { - copy(newItems[i:], newItems[i+1:]) - newItems[len(newItems)-1] = make([]Item, 1)[0] - newItems = newItems[:len(newItems)-1] - } - } - - // Create new item cache - bf := byteframe.NewByteFrame() - for i := 0; i < len(newItems); i++ { - bf.WriteUint16(newItems[i].ItemId) - bf.WriteUint16(newItems[i].Amount) - } - - // Upload new item cache - _, err = s.server.db.Exec("UPDATE users SET item_box = $1 FROM characters WHERE users.id = characters.user_id AND characters.id = $2", bf.Data(), int(s.charID)) - if err != nil { - s.logger.Error("Failed to update shared item box contents in db", zap.Error(err)) - } + newStacks := mhfitem.DiffItemStacks(userGetItems(s), pkt.UpdatedItems) + s.server.db.Exec(`UPDATE users u SET item_box=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, mhfitem.SerializeWarehouseItems(newStacks), s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -937,19 +878,19 @@ func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp) var total, redeemed uint16 - var tktStack mhfpacket.WarehouseStack + var tktStack mhfitem.MHFItemStack if pkt.Unk1 == 0xA { // Yearly Sub Ex s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed) - tktStack = mhfpacket.WarehouseStack{ItemID: 0x08A2, Quantity: 1} + tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 2210}, Quantity: 1} } else { s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) if pkt.StampType == "hl" { - tktStack = mhfpacket.WarehouseStack{ItemID: 0x065E, Quantity: 5} + tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 1630}, Quantity: 5} } else { - tktStack = mhfpacket.WarehouseStack{ItemID: 0x065F, Quantity: 5} + tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 1631}, Quantity: 5} } } - addWarehouseGift(s, "item", tktStack) + addWarehouseItem(s, tktStack) bf := byteframe.NewByteFrame() bf.WriteUint16(total) bf.WriteUint16(redeemed) @@ -1120,13 +1061,13 @@ func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(pkt.Reward2) bf.WriteUint16(pkt.Item2) bf.WriteUint16(pkt.Quantity2) - addWarehouseGift(s, "item", mhfpacket.WarehouseStack{ItemID: pkt.Item2, Quantity: pkt.Quantity2}) + addWarehouseItem(s, mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: pkt.Item2}, Quantity: pkt.Quantity2}) } else if stamps%15 == 0 { bf.WriteUint16(1) bf.WriteUint16(pkt.Reward1) bf.WriteUint16(pkt.Item1) bf.WriteUint16(pkt.Quantity1) - addWarehouseGift(s, "item", mhfpacket.WarehouseStack{ItemID: pkt.Item1, Quantity: pkt.Quantity1}) + addWarehouseItem(s, mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: pkt.Item1}, Quantity: pkt.Quantity1}) } else { bf.WriteBytes(make([]byte, 8)) } diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 67e6e3a3a..17815dc30 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -478,7 +478,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { m := binpacket.MsgBinChat{ Type: BinaryMessageTypeChat, Flags: 4, - Message: fmt.Sprintf(`%d`, token.RNG().Intn(100)+1), + Message: fmt.Sprintf(`%d`, token.RNG.Intn(100)+1), SenderName: author, } bf := byteframe.NewByteFrame() diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go index d39b629d9..7bc384226 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -101,8 +101,7 @@ func generateFeatureWeapons(count int) activeFeature { nums := make([]int, 0) var result int for len(nums) < count { - rng := token.RNG() - num := rng.Intn(_max) + num := token.RNG.Intn(_max) exist := false for _, v := range nums { if v == num { diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index f833a1f4e..eecd268e3 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -455,7 +455,7 @@ func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } - team := uint32(token.RNG().Intn(2)) + team := uint32(token.RNG.Intn(2)) switch team { case 0: s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'blue')", guild.ID) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 9df9dd2c6..be61f1241 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -3,9 +3,9 @@ package channelserver import ( "database/sql" "database/sql/driver" - "encoding/binary" "encoding/json" "errors" + "erupe-ce/common/mhfitem" _config "erupe-ce/config" "fmt" "math" @@ -1554,100 +1554,34 @@ func handleMsgMhfGetGuildTargetMemberNum(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } -func handleMsgMhfEnumerateGuildItem(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfEnumerateGuildItem) - var boxContents []byte - bf := byteframe.NewByteFrame() - err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", pkt.GuildID).Scan(&boxContents) - if err != nil { - s.logger.Error("Failed to get guild item box contents from db", zap.Error(err)) - bf.WriteBytes(make([]byte, 4)) - } else { - if len(boxContents) == 0 { - bf.WriteBytes(make([]byte, 4)) - } else { - amount := len(boxContents) / 4 - bf.WriteUint16(uint16(amount)) - bf.WriteUint32(0x00) - bf.WriteUint16(0x00) - for i := 0; i < amount; i++ { - bf.WriteUint32(binary.BigEndian.Uint32(boxContents[i*4 : i*4+4])) - if i+1 != amount { - bf.WriteUint64(0x00) - } - } +func guildGetItems(s *Session, guildID uint32) []mhfitem.MHFItemStack { + var data []byte + var items []mhfitem.MHFItemStack + s.server.db.QueryRow(`SELECT item_box FROM guilds WHERE id=$1`, guildID).Scan(&data) + if len(data) > 0 { + box := byteframe.NewByteFrameFromBytes(data) + numStacks := box.ReadUint16() + box.ReadUint16() // Unused + for i := 0; i < int(numStacks); i++ { + items = append(items, mhfitem.ReadWarehouseItem(box)) } } - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + return items } -type Item struct { - ItemId uint16 - Amount uint16 +func handleMsgMhfEnumerateGuildItem(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateGuildItem) + items := guildGetItems(s, pkt.GuildID) + bf := byteframe.NewByteFrame() + bf.WriteBytes(mhfitem.SerializeWarehouseItems(items)) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfUpdateGuildItem(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateGuildItem) - - // Get item cache from DB - var boxContents []byte - var oldItems []Item - err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", pkt.GuildID).Scan(&boxContents) - if err != nil { - s.logger.Error("Failed to get guild item box contents from db", zap.Error(err)) - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) - return - } else { - amount := len(boxContents) / 4 - oldItems = make([]Item, amount) - for i := 0; i < amount; i++ { - oldItems[i].ItemId = binary.BigEndian.Uint16(boxContents[i*4 : i*4+2]) - oldItems[i].Amount = binary.BigEndian.Uint16(boxContents[i*4+2 : i*4+4]) - } - } - - // Update item stacks - newItems := make([]Item, len(oldItems)) - copy(newItems, oldItems) - for i := 0; i < len(pkt.Items); i++ { - for j := 0; j <= len(oldItems); j++ { - if j == len(oldItems) { - var newItem Item - newItem.ItemId = pkt.Items[i].ItemID - newItem.Amount = pkt.Items[i].Amount - newItems = append(newItems, newItem) - break - } - if pkt.Items[i].ItemID == oldItems[j].ItemId { - newItems[j].Amount = pkt.Items[i].Amount - break - } - } - } - - // Delete empty item stacks - for i := len(newItems) - 1; i >= 0; i-- { - if int(newItems[i].Amount) == 0 { - copy(newItems[i:], newItems[i+1:]) - newItems[len(newItems)-1] = make([]Item, 1)[0] - newItems = newItems[:len(newItems)-1] - } - } - - // Create new item cache - bf := byteframe.NewByteFrame() - for i := 0; i < len(newItems); i++ { - bf.WriteUint16(newItems[i].ItemId) - bf.WriteUint16(newItems[i].Amount) - } - - // Upload new item cache - _, err = s.server.db.Exec("UPDATE guilds SET item_box = $1 WHERE id = $2", bf.Data(), pkt.GuildID) - if err != nil { - s.logger.Error("Failed to update guild item box contents in db", zap.Error(err)) - } - - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + newStacks := mhfitem.DiffItemStacks(guildGetItems(s, pkt.GuildID), pkt.UpdatedItems) + s.server.db.Exec(`UPDATE guilds SET item_box=$1 WHERE id=$2`, mhfitem.SerializeWarehouseItems(newStacks), pkt.GuildID) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 3f6188750..705c76393 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -2,8 +2,10 @@ package channelserver import ( "erupe-ce/common/byteframe" + "erupe-ce/common/mhfitem" ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" + "erupe-ce/common/token" _config "erupe-ce/config" "erupe-ce/network/mhfpacket" "fmt" @@ -406,7 +408,12 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { 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) + switch pkt.BoxType { + case 0: + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET item%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.charID) + case 1: + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET equip%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.charID) + } case 3: bf.WriteUint32(0) // Usage renewal time, >1 = disabled bf.WriteUint16(10000) // Usages @@ -424,81 +431,63 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } -func addWarehouseGift(s *Session, boxType string, giftStack mhfpacket.WarehouseStack) { - giftBox := getWarehouseBox(s, boxType, 10) - if boxType == "item" { - exists := false - for i, stack := range giftBox { - if stack.ItemID == giftStack.ItemID { - exists = true - giftBox[i].Quantity += giftStack.Quantity - break - } - } - if exists == false { - giftBox = append(giftBox, giftStack) - } - } else { - giftBox = append(giftBox, giftStack) - } - s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s10=$1 WHERE character_id=$2", boxType), boxToBytes(giftBox, boxType), s.charID) +func addWarehouseItem(s *Session, item mhfitem.MHFItemStack) { + giftBox := warehouseGetItems(s, 10) + item.WarehouseID = token.RNG.Uint32() + giftBox = append(giftBox, item) + s.server.db.Exec("UPDATE warehouse SET item10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseItems(giftBox), s.charID) } -func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.WarehouseStack { +func addWarehouseEquipment(s *Session, equipment mhfitem.MHFEquipment) { + giftBox := warehouseGetEquipment(s, 10) + equipment.WarehouseID = token.RNG.Uint32() + giftBox = append(giftBox, equipment) + s.server.db.Exec("UPDATE warehouse SET equip10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseEquipment(giftBox), s.charID) +} + +func warehouseGetItems(s *Session, index uint8) []mhfitem.MHFItemStack { var data []byte - s.server.db.QueryRow(fmt.Sprintf("SELECT %s%d FROM warehouse WHERE character_id=$1", boxType, boxIndex), s.charID).Scan(&data) + var items []mhfitem.MHFItemStack + s.server.db.QueryRow(fmt.Sprintf(`SELECT item%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data) if len(data) > 0 { box := byteframe.NewByteFrameFromBytes(data) numStacks := box.ReadUint16() - stacks := make([]mhfpacket.WarehouseStack, numStacks) + box.ReadUint16() // Unused 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) - } + items = append(items, mhfitem.ReadWarehouseItem(box)) } - return stacks - } else { - return make([]mhfpacket.WarehouseStack, 0) } + return items } -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) +func warehouseGetEquipment(s *Session, index uint8) []mhfitem.MHFEquipment { + var data []byte + var equipment []mhfitem.MHFEquipment + s.server.db.QueryRow(fmt.Sprintf(`SELECT equip%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data) + if len(data) > 0 { + box := byteframe.NewByteFrameFromBytes(data) + numStacks := box.ReadUint16() + box.ReadUint16() // Unused + for i := 0; i < int(numStacks); i++ { + equipment = append(equipment, mhfitem.ReadWarehouseEquipment(box)) } } - bf.WriteUint16(0) - return bf.Data() + return equipment } 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)) + bf := byteframe.NewByteFrame() + switch pkt.BoxType { + case 0: + items := warehouseGetItems(s, pkt.BoxIndex) + bf.WriteBytes(mhfitem.SerializeWarehouseItems(items)) + case 1: + equipment := warehouseGetEquipment(s, pkt.BoxIndex) + bf.WriteBytes(mhfitem.SerializeWarehouseEquipment(equipment)) + } + if bf.Index() > 0 { + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } else { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -506,49 +495,34 @@ func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) { 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 { + switch pkt.BoxType { + case 0: + newStacks := mhfitem.DiffItemStacks(warehouseGetItems(s, pkt.BoxIndex), pkt.UpdatedItems) + s.server.db.Exec(fmt.Sprintf(`UPDATE warehouse SET item%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseItems(newStacks), s.charID) + case 1: + var fEquip []mhfitem.MHFEquipment + oEquips := warehouseGetEquipment(s, pkt.BoxIndex) + for _, uEquip := range pkt.UpdatedEquipment { + exists := false + for i := range oEquips { + if oEquips[i].WarehouseID == uEquip.WarehouseID { exists = true - box[i].Quantity = update.Quantity + // Will set removed items to 0 + oEquips[i].ItemID = uEquip.ItemID break } } - } else { - for i, stack := range box { - if stack.Index == update.Index { - exists = true - box[i].ItemID = update.ItemID - break - } + if !exists { + uEquip.WarehouseID = token.RNG.Uint32() + fEquip = append(fEquip, uEquip) } } - 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) + for _, oEquip := range oEquips { + if oEquip.ItemID > 0 { + fEquip = append(fEquip, oEquip) } } + s.server.db.Exec(fmt.Sprintf(`UPDATE warehouse SET equip%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseEquipment(fEquip), s.charID) } - 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)) }