diff --git a/Erupe/road-shop-rotation.sql b/Erupe/road-shop-rotation.sql new file mode 100644 index 000000000..034d6bfce --- /dev/null +++ b/Erupe/road-shop-rotation.sql @@ -0,0 +1,39 @@ +BEGIN; +CREATE TABLE IF NOT EXISTS public.normal_shop_items +( + shoptype integer, + shopid integer, + itemhash integer not null, + itemid integer, + points integer, + tradequantity integer, + rankreqlow integer, + rankreqhigh integer, + rankreqg integer, + storelevelreq integer, + maximumquantity integer, + boughtquantity integer, + roadfloorsrequired integer, + weeklyfataliskills integer, + enable_weeks character varying(8) +); + +ALTER TABLE IF EXISTS public.normal_shop_items +( + ADD COLUMN enable_weeks character varying(8) +); + +CREATE TABLE IF NOT EXISTS public.shop_item_state +( + char_id bigint REFERENCES characters (id), + itemhash int UNIQUE NOT NULL, + usedquantity int, + week int +); + +ALTER TABLE IF EXISTS public.shop_item_state +( + ADD COLUMN week int +); + +END; \ No newline at end of file diff --git a/Erupe/server/channelserver/handlers_shop_gacha.go b/Erupe/server/channelserver/handlers_shop_gacha.go index be0ec9e04..ac786cccf 100644 --- a/Erupe/server/channelserver/handlers_shop_gacha.go +++ b/Erupe/server/channelserver/handlers_shop_gacha.go @@ -2,16 +2,28 @@ package channelserver import ( "encoding/hex" + "fmt" + "strings" "time" //"github.com/Solenataris/Erupe/common/stringsupport" - "github.com/Solenataris/Erupe/network/mhfpacket" "github.com/Andoryuuta/byteframe" + "github.com/Solenataris/Erupe/network/mhfpacket" "github.com/lib/pq" "github.com/sachaos/lottery" "go.uber.org/zap" ) +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} + func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateShop) // SHOP TYPES: @@ -149,20 +161,28 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { } else { doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } - } else { - shopEntries, err := s.server.db.Query("SELECT itemhash,itemID,Points,TradeQuantity,rankReqLow,rankReqHigh,rankReqG,storeLevelReq,maximumQuantity,boughtQuantity,roadFloorsRequired,weeklyFatalisKills FROM normal_shop_items WHERE shoptype=$1 AND shopid=$2", pkt.ShopType, pkt.ShopID) + } else { + _, week := time.Now().ISOWeek() + season := fmt.Sprintf("%d", week%4) + shopEntries, err := s.server.db.Query("SELECT itemhash,itemID,Points,TradeQuantity,rankReqLow,rankReqHigh,rankReqG,storeLevelReq,maximumQuantity,boughtQuantity,roadFloorsRequired,weeklyFatalisKills, COALESCE(enable_weeks, '') FROM normal_shop_items WHERE shoptype=$1 AND shopid=$2", pkt.ShopType, pkt.ShopID) if err != nil { panic(err) } var ItemHash, entryCount int var itemID, Points, TradeQuantity, rankReqLow, rankReqHigh, rankReqG, storeLevelReq, maximumQuantity, boughtQuantity, roadFloorsRequired, weeklyFatalisKills, charQuantity uint16 + var itemWeeks string resp := byteframe.NewByteFrame() resp.WriteUint32(0) // total defs for shopEntries.Next() { - err = shopEntries.Scan(&ItemHash, &itemID, &Points, &TradeQuantity, &rankReqLow, &rankReqHigh, &rankReqG, &storeLevelReq, &maximumQuantity, &boughtQuantity, &roadFloorsRequired, &weeklyFatalisKills) + err = shopEntries.Scan(&ItemHash, &itemID, &Points, &TradeQuantity, &rankReqLow, &rankReqHigh, &rankReqG, &storeLevelReq, &maximumQuantity, &boughtQuantity, &roadFloorsRequired, &weeklyFatalisKills, &itemWeeks) if err != nil { panic(err) } + + if len(itemWeeks) > 0 && !contains(strings.Split(itemWeeks, ","), season) { + continue + } + resp.WriteUint32(uint32(ItemHash)) resp.WriteUint16(0) // unk, always 0 in existing packets resp.WriteUint16(itemID) @@ -175,9 +195,13 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { resp.WriteUint16(storeLevelReq) resp.WriteUint16(maximumQuantity) if maximumQuantity > 0 { - err = s.server.db.QueryRow("SELECT COALESCE(usedquantity,0) FROM shop_item_state WHERE itemhash=$1 AND char_id=$2", ItemHash, s.charID).Scan(&charQuantity) + var itemWeek int + err = s.server.db.QueryRow("SELECT COALESCE(usedquantity,0), COALESCE(week,-1) FROM shop_item_state WHERE itemhash=$1 AND char_id=$2", ItemHash, s.charID).Scan(&charQuantity, &itemWeek) if err != nil { resp.WriteUint16(0) + } else if pkt.ShopID == 7 && itemWeek >= 0 && itemWeek != week { + clearShopItemState(s, s.charID, uint32(ItemHash)) + resp.WriteUint16(0) } else { resp.WriteUint16(charQuantity) } @@ -200,6 +224,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { } func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) { + _, week := time.Now().ISOWeek() // writing out to an editable shop enumeration pkt := p.(*mhfpacket.MsgMhfAcquireExchangeShop) if pkt.DataSize == 10 { @@ -207,10 +232,10 @@ func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) { _ = bf.ReadUint16() // unk, always 1 in examples itemHash := bf.ReadUint32() buyCount := bf.ReadUint32() - _, err := s.server.db.Exec(`INSERT INTO shop_item_state (char_id, itemhash, usedquantity) - VALUES ($1,$2,$3) ON CONFLICT (char_id, itemhash) + _, err := s.server.db.Exec(`INSERT INTO shop_item_state (char_id, itemhash, usedquantity, week) + VALUES ($1,$2,$3,$4) ON CONFLICT (char_id, itemhash) DO UPDATE SET usedquantity = shop_item_state.usedquantity + $3 - WHERE EXCLUDED.char_id=$1 AND EXCLUDED.itemhash=$2`, s.charID, itemHash, buyCount) + WHERE EXCLUDED.char_id=$1 AND EXCLUDED.itemhash=$2`, s.charID, itemHash, buyCount, week) if err != nil { s.logger.Fatal("Failed to update shop_item_state in db", zap.Error(err)) } @@ -218,6 +243,13 @@ func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } +func clearShopItemState(s *Session, charId uint32, itemHash uint32) { + _, err := s.server.db.Exec(`DELETE FROM shop_item_state WHERE char_id=$1 AND itemhash=$2`, charId, itemHash) + if err != nil { + s.logger.Fatal("Failed to delete shop_item_state in db", zap.Error(err)) + } +} + func handleMsgMhfGetGachaPlayHistory(s *Session, p mhfpacket.MHFPacket) { // returns number of times the gacha was played, will need persistent db stuff pkt := p.(*mhfpacket.MsgMhfGetGachaPlayHistory) @@ -703,4 +735,4 @@ func handleMsgMhfResetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) { panic(err) } doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) -} +} \ No newline at end of file