From 40d5c6790232625ab6dadcf4d1afa27cd2462c1f Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 12 Mar 2023 22:05:42 +1100 Subject: [PATCH] merge shop enumeration fixes from feature/diva --- patch-schema/shop-db.sql | 44 +++++-- server/channelserver/handlers_shop_gacha.go | 138 ++++++++++---------- 2 files changed, 103 insertions(+), 79 deletions(-) diff --git a/patch-schema/shop-db.sql b/patch-schema/shop-db.sql index b95b26ca1..75f086f05 100644 --- a/patch-schema/shop-db.sql +++ b/patch-schema/shop-db.sql @@ -1,39 +1,59 @@ BEGIN; ALTER TABLE IF EXISTS public.normal_shop_items + RENAME TO shop_items; + +ALTER TABLE IF EXISTS public.shop_items + RENAME COLUMN shoptype TO shop_type; + +ALTER TABLE IF EXISTS public.shop_items + RENAME COLUMN shopid TO shop_id; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN itemhash TO id; -ALTER TABLE IF EXISTS public.normal_shop_items +ALTER TABLE IF EXISTS public.shop_items + RENAME COLUMN itemid TO item_id; + +ALTER TABLE IF EXISTS public.shop_items ALTER COLUMN points TYPE integer; -ALTER TABLE IF EXISTS public.normal_shop_items +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN points TO cost; -ALTER TABLE IF EXISTS public.normal_shop_items +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN tradequantity TO quantity; -ALTER TABLE IF EXISTS public.normal_shop_items +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN rankreqlow TO min_hr; -ALTER TABLE IF EXISTS public.normal_shop_items +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN rankreqhigh TO min_sr; -ALTER TABLE IF EXISTS public.normal_shop_items +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN rankreqg TO min_gr; -ALTER TABLE IF EXISTS public.normal_shop_items +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN storelevelreq TO req_store_level; -ALTER TABLE IF EXISTS public.normal_shop_items +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN maximumquantity TO max_quantity; -ALTER TABLE IF EXISTS public.normal_shop_items - DROP COLUMN boughtquantity; +ALTER TABLE IF EXISTS public.shop_items + DROP COLUMN IF EXISTS boughtquantity; -ALTER TABLE IF EXISTS public.normal_shop_items +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN roadfloorsrequired TO road_floors; -ALTER TABLE IF EXISTS public.normal_shop_items +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN weeklyfataliskills TO road_fatalis; +DROP TABLE IF EXISTS public.shop_item_state; + +CREATE TABLE IF NOT EXISTS public.shop_items_bought ( + character_id INTEGER, + shop_item_id INTEGER, + bought INTEGER +); + END; \ No newline at end of file diff --git a/server/channelserver/handlers_shop_gacha.go b/server/channelserver/handlers_shop_gacha.go index 2b07b3168..2ebf209ea 100644 --- a/server/channelserver/handlers_shop_gacha.go +++ b/server/channelserver/handlers_shop_gacha.go @@ -1,7 +1,6 @@ package channelserver import ( - "encoding/hex" "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" "erupe-ce/network/mhfpacket" @@ -9,18 +8,18 @@ import ( ) type ShopItem struct { - ID uint32 `db:"id"` - ItemID uint16 `db:"itemid"` - Cost uint32 `db:"cost"` - Quantity uint16 `db:"quantity"` - MinHR uint16 `db:"min_hr"` - MinSR uint16 `db:"min_sr"` - MinGR uint16 `db:"min_gr"` - ReqStoreLevel uint16 `db:"req_store_level"` - MaxQuantity uint16 `db:"max_quantity"` - CharQuantity uint16 `db:"char_quantity"` - RoadFloors uint16 `db:"road_floors"` - RoadFatalis uint16 `db:"road_fatalis"` + ID uint32 `db:"id"` + ItemID uint16 `db:"item_id"` + Cost uint32 `db:"cost"` + Quantity uint16 `db:"quantity"` + MinHR uint16 `db:"min_hr"` + MinSR uint16 `db:"min_sr"` + MinGR uint16 `db:"min_gr"` + StoreLevel uint16 `db:"store_level"` + MaxQuantity uint16 `db:"max_quantity"` + UsedQuantity uint16 `db:"used_quantity"` + RoadFloors uint16 `db:"road_floors"` + RoadFatalis uint16 `db:"road_fatalis"` } type Gacha struct { @@ -56,6 +55,45 @@ type GachaItem struct { Quantity uint16 `db:"quantity"` } +func writeShopItems(bf *byteframe.ByteFrame, items []ShopItem) { + bf.WriteUint16(uint16(len(items))) + bf.WriteUint16(uint16(len(items))) + for _, item := range items { + bf.WriteUint32(item.ID) + bf.WriteUint16(0) + bf.WriteUint16(item.ItemID) + bf.WriteUint32(item.Cost) + bf.WriteUint16(item.Quantity) + bf.WriteUint16(item.MinHR) + bf.WriteUint16(item.MinSR) + bf.WriteUint16(item.MinGR) + bf.WriteUint16(item.StoreLevel) + bf.WriteUint16(item.MaxQuantity) + bf.WriteUint16(item.UsedQuantity) + bf.WriteUint16(item.RoadFloors) + bf.WriteUint16(item.RoadFatalis) + } +} + +func getShopItems(s *Session, shopType uint8, shopID uint32) []ShopItem { + var items []ShopItem + var temp ShopItem + rows, err := s.server.db.Queryx(`SELECT id, item_id, cost, quantity, min_hr, min_sr, min_gr, store_level, max_quantity, + COALESCE((SELECT bought FROM shop_items_bought WHERE shop_item_id=si.id AND character_id=$3), 0) as used_quantity, + road_floors, road_fatalis FROM shop_items si WHERE shop_type=$1 AND shop_id=$2 + `, shopType, shopID, s.charID) + if err == nil { + for rows.Next() { + err = rows.StructScan(&temp) + if err != nil { + continue + } + items = append(items, temp) + } + } + return items +} + func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateShop) // Generic Shop IDs @@ -165,62 +203,25 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { bf.Seek(4, 0) bf.WriteUint16(entryCount) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + case 3: // Hunting Festival Exchange + fallthrough case 4: // N Points, 0-6 - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + fallthrough case 5: // GCP->Item, 0-6 - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + fallthrough case 6: // Gacha coin->Item - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + fallthrough case 7: // Item->GCP - data, _ := hex.DecodeString("000300033a9186fb000033860000000a000100000000000000000000000000000000097fdb1c0000067e0000000a0001000000000000000000000000000000001374db29000027c300000064000100000000000000000000000000000000") - doAckBufSucceed(s, pkt.AckHandle, data) + fallthrough case 8: // Diva - switch pkt.ShopID { - case 0: // Normal exchange - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) - case 5: // GCP skills - data, _ := hex.DecodeString("001f001f2c9365c1000000010000001e000a0000000000000000000a0000000000001979f1c2000000020000003c000a0000000000000000000a0000000000003e5197df000000030000003c000a0000000000000000000a000000000000219337c0000000040000001e000a0000000000000000000a00000000000009b24c9d000000140000001e000a0000000000000000000a0000000000001f1d496e000000150000001e000a0000000000000000000a0000000000003b918fcb000000160000003c000a0000000000000000000a0000000000000b7fd81c000000170000003c000a0000000000000000000a0000000000001374f239000000180000003c000a0000000000000000000a00000000000026950cba0000001c0000003c000a0000000000000000000a0000000000003797eae70000001d0000003c000a012b000000000000000a00000000000015758ad8000000050000003c00000000000000000000000a0000000000003c7035050000000600000050000a0000000000000001000a00000000000024f3b5560000000700000050000a0000000000000001000a00000000000000b600330000000800000050000a0000000000000001000a0000000000002efdce840000001900000050000a0000000000000001000a0000000000002d9365f10000001a00000050000a0000000000000001000a0000000000001979f3420000001f00000050000a012b000000000001000a0000000000003f5397cf0000002000000050000a012b000000000001000a000000000000319337c00000002100000050000a012b000000000001000a00000000000008b04cbd0000000900000064000a0000000000000002000a0000000000000b1d4b6e0000000a00000064000a0000000000000002000a0000000000003b918feb0000000b00000064000a0000000000000002000a0000000000001b7fd81c0000000c00000064000a0000000000000002000a0000000000001276f2290000000d00000064000a0000000000000002000a00000000000022950cba0000000e000000c8000a0000000000000002000a0000000000003697ead70000000f000001f4000a0000000000000003000a00000000000005758a5800000010000003e8000a0000000000000003000a0000000000003c7035250000001b000001f4000a0000000000010003000a00000000000034f3b5d60000001e00000064000a012b000000000003000a00000000000000b600030000002200000064000a0000000000010003000a000000000000") - doAckBufSucceed(s, pkt.AckHandle, data) - case 7: // Note exchange - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) - } + fallthrough + case 9: // Diva song shop + fallthrough case 10: // Item shop, 0-8 - shopEntries, err := s.server.db.Queryx(`SELECT id, itemid, cost, quantity, min_hr, min_sr, min_gr, req_store_level, max_quantity, - COALESCE((SELECT usedquantity FROM shop_item_state WHERE itemhash=nsi.id AND char_id=$3), 0) as char_quantity, - road_floors, road_fatalis FROM normal_shop_items nsi WHERE shoptype=$1 AND shopid=$2 - `, pkt.ShopType, pkt.ShopID, s.charID) - if err != nil { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) - return - } - var count uint16 - resp := byteframe.NewByteFrame() - resp.WriteBytes(make([]byte, 4)) - var shopItem ShopItem - for shopEntries.Next() { - err = shopEntries.StructScan(&shopItem) - if err != nil { - continue - } - resp.WriteUint32(shopItem.ID) - resp.WriteUint16(0) - resp.WriteUint16(shopItem.ItemID) - resp.WriteUint32(shopItem.Cost) - resp.WriteUint16(shopItem.Quantity) - resp.WriteUint16(shopItem.MinHR) - resp.WriteUint16(shopItem.MinSR) - resp.WriteUint16(shopItem.MinGR) - resp.WriteUint16(shopItem.ReqStoreLevel) - resp.WriteUint16(shopItem.MaxQuantity) - resp.WriteUint16(shopItem.CharQuantity) - resp.WriteUint16(shopItem.RoadFloors) - resp.WriteUint16(shopItem.RoadFatalis) - count++ - } - resp.Seek(0, 0) - resp.WriteUint16(count) - resp.WriteUint16(count) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + bf := byteframe.NewByteFrame() + items := getShopItems(s, pkt.ShopType, pkt.ShopID) + writeShopItems(bf, items) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } } @@ -230,11 +231,14 @@ func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) { exchanges := int(bf.ReadUint16()) for i := 0; i < exchanges; i++ { itemHash := bf.ReadUint32() + if itemHash == 0 { + continue + } buyCount := bf.ReadUint32() - s.server.db.Exec(`INSERT INTO shop_item_state (char_id, itemhash, usedquantity) - VALUES ($1,$2,$3) ON CONFLICT (char_id, itemhash) - DO UPDATE SET usedquantity = shop_item_state.usedquantity + $3 - WHERE EXCLUDED.char_id=$1 AND EXCLUDED.itemhash=$2 + s.server.db.Exec(`INSERT INTO shop_items_bought (character_id, shop_item_id, bought) + VALUES ($1,$2,$3) ON CONFLICT (character_id, shop_item_id) + DO UPDATE SET bought = bought + $3 + WHERE EXCLUDED.character_id=$1 AND EXCLUDED.shop_item_id=$2 `, s.charID, itemHash, buyCount) } doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})