Merge pull request #56 from ZeruLight/fix/shop-enum

fix/shop-enum
This commit is contained in:
wish
2022-12-23 23:55:36 +11:00
committed by GitHub
7 changed files with 702 additions and 268 deletions

View File

@@ -0,0 +1,387 @@
INSERT INTO fpoint_items (item_type, item_id, quantity, fpoints, trade_type) VALUES
(7,8895,1,500,0),
(7,8891,1,300,0),
(7,8892,1,300,0),
(7,8893,1,300,0),
(7,8894,1,300,0),
(7,8890,1,10,0),
(7,10354,1,500,0),
(7,11983,1,300,0),
(7,11984,1,300,0),
(7,11985,1,300,0),
(7,11986,1,300,0),
(7,12524,1,500,0),
(7,12470,1,300,0),
(7,12471,1,300,0),
(7,12472,1,300,0),
(7,12473,1,300,0),
(7,2158,2,1,0),
(7,14548,1,500,0),
(7,9509,1,1,0),
(7,9510,1,1,0),
(7,9511,1,1,0),
(7,9512,1,1,0),
(7,9513,1,1,0),
(7,9514,1,1,0),
(7,9515,1,1,0),
(7,10753,1,1,0),
(7,10754,1,1,0),
(7,10755,1,1,0),
(7,10756,1,1,0),
(7,10757,1,1,0),
(7,10758,1,1,0),
(7,10759,1,1,0),
(7,11296,1,1,0),
(7,11297,1,1,0),
(7,11298,1,1,0),
(7,11299,1,1,0),
(7,11300,1,1,0),
(7,12386,1,1,0),
(7,12387,1,1,0),
(7,12388,1,1,0),
(7,12389,1,1,0),
(7,12390,1,1,0),
(7,13034,1,1,0),
(7,13035,1,1,0),
(7,13036,1,1,0),
(7,13037,1,1,0),
(7,13038,1,1,0),
(7,14179,1,1,0),
(7,14180,1,1,0),
(7,14181,1,1,0),
(7,14182,1,1,0),
(7,14183,1,1,0),
(7,13422,1,1,0),
(7,13423,1,1,0),
(7,13424,1,1,0),
(7,13425,1,1,0),
(7,13426,1,1,0),
(7,13427,1,1,0),
(7,9796,1,3,0),
(7,9700,1,3,0),
(7,10380,1,3,0),
(7,10810,1,3,0),
(7,10811,1,3,0),
(7,11436,1,3,0),
(7,9509,1,1,0),
(7,9510,1,1,0),
(7,9511,1,1,0),
(7,9512,1,1,0),
(7,9513,1,1,0),
(7,9514,1,1,0),
(7,9515,1,1,0),
(7,10753,1,1,0),
(7,10754,1,1,0),
(7,10755,1,1,0),
(7,10756,1,1,0),
(7,10757,1,1,0),
(7,10758,1,1,0),
(7,10759,1,1,0),
(7,11296,1,1,0),
(7,11297,1,1,0),
(7,11298,1,1,0),
(7,11299,1,1,0),
(7,11300,1,1,0),
(7,12509,1,3,0),
(7,12386,1,1,0),
(7,12387,1,1,0),
(7,12388,1,1,0),
(7,12389,1,1,0),
(7,12390,1,1,0),
(7,12872,1,3,0),
(7,12873,1,3,0),
(7,12840,1,1,0),
(7,12841,1,1,0),
(7,12874,1,1,0),
(7,12875,1,1,0),
(7,13191,1,3,0),
(7,13177,1,3,0),
(7,13326,1,3,0),
(7,13034,1,1,0),
(7,13035,1,1,0),
(7,13036,1,1,0),
(7,13037,1,1,0),
(7,13038,1,1,0),
(7,13178,1,3,0),
(7,13453,1,3,0),
(7,13449,1,3,0),
(7,13450,1,3,0),
(7,13404,1,3,0),
(7,13422,1,1,0),
(7,13423,1,1,0),
(7,13424,1,1,0),
(7,13425,1,1,0),
(7,13426,1,1,0),
(7,13427,1,1,0),
(7,13791,1,3,0),
(7,14006,1,3,0),
(7,14031,1,3,0),
(7,14032,1,3,0),
(7,13960,1,3,0),
(7,14029,1,3,0),
(7,13956,1,1,0),
(7,13958,1,1,0),
(7,13957,1,1,0),
(7,13959,1,1,0),
(7,13790,1,3,0),
(7,14005,1,3,0),
(7,14010,1,3,0),
(7,14009,1,3,0),
(7,14008,1,3,0),
(7,13965,1,3,0),
(7,14028,1,3,0),
(7,13963,1,3,0),
(7,14026,1,3,0),
(7,13964,1,3,0),
(7,14027,1,3,0),
(7,14069,1,3,0),
(7,14124,1,3,0),
(7,14065,1,1,0),
(7,14066,1,1,0),
(7,14067,1,1,0),
(7,14068,1,1,0),
(7,13962,1,3,0),
(7,14125,1,3,0),
(7,14089,1,3,0),
(7,14090,1,3,0),
(7,14091,1,3,0),
(7,14092,1,3,0),
(7,14194,1,3,0),
(7,14191,1,3,0),
(7,14198,1,3,0),
(7,14197,1,3,0),
(7,14179,1,1,0),
(7,14180,1,1,0),
(7,14181,1,1,0),
(7,14182,1,1,0),
(7,14183,1,1,0),
(7,14196,1,3,0),
(7,14195,1,3,0),
(7,14193,1,3,0),
(7,14192,1,3,0),
(7,14407,1,3,0),
(7,14414,1,3,0),
(7,14406,1,3,0),
(7,14413,1,3,0),
(7,14416,1,3,0),
(7,14549,1,3,0),
(7,14550,1,3,0),
(7,14502,1,3,0),
(7,14507,1,3,0),
(7,14501,1,3,0),
(7,14506,1,3,0),
(7,14500,1,3,0),
(7,14505,1,3,0),
(7,14498,1,3,0),
(7,14659,1,3,0),
(7,14660,1,3,0),
(7,14657,1,1,0),
(7,14658,1,1,0),
(7,11420,1,3,0),
(7,14704,1,3,0),
(7,11288,1,1,0),
(7,11289,1,1,0),
(7,11290,1,1,0),
(7,11291,1,1,0),
(7,10750,1,3,0),
(7,14705,1,3,0),
(7,10633,1,1,0),
(7,10634,1,1,0),
(7,10635,1,1,0),
(7,10636,1,1,0),
(7,14662,1,3,0),
(7,14663,1,3,0),
(7,14665,1,3,0),
(7,14666,1,3,0),
(7,14667,1,3,0),
(7,14668,1,3,0),
(7,14669,1,3,0),
(7,14670,1,3,0),
(7,14671,1,3,0),
(7,14672,1,3,0),
(7,14673,1,3,0),
(7,14674,1,3,0),
(7,14675,1,3,0),
(7,14676,1,3,0),
(7,14677,1,3,0),
(7,14678,1,3,0),
(7,14679,1,3,0),
(7,14680,1,3,0),
(7,14681,1,3,0),
(7,14682,1,3,0),
(7,14683,1,3,0),
(7,14684,1,3,0),
(7,14685,1,3,0),
(7,14686,1,3,0),
(7,14687,1,3,0),
(7,14688,1,3,0),
(7,14689,1,3,0),
(7,14690,1,3,0),
(7,14691,1,3,0),
(7,14692,1,3,0),
(7,14693,1,3,0),
(7,14694,1,3,0),
(7,14695,1,3,0),
(7,14696,1,3,0),
(7,14697,1,3,0),
(7,14698,1,3,0),
(7,14699,1,3,0),
(7,14700,1,3,0),
(7,14314,1,3,0),
(7,14503,1,3,0),
(7,14510,1,3,0),
(7,14904,1,3,0),
(7,14906,1,3,0),
(7,14910,1,1,0),
(7,14912,1,1,0),
(7,14905,1,3,0),
(7,14907,1,3,0),
(7,14911,1,1,0),
(7,14909,1,1,0),
(7,14855,1,3,0),
(7,14894,1,3,0),
(7,14913,1,3,0),
(7,14914,1,3,0),
(7,14891,1,3,0),
(7,14895,1,3,0),
(7,15027,1,3,0),
(7,15028,1,3,0),
(7,15026,1,1,0),
(7,15025,1,1,0),
(7,15024,1,1,0),
(7,15023,1,1,0),
(7,15064,1,3,0),
(7,15065,1,3,0),
(7,15030,1,3,0),
(7,15031,1,3,0),
(7,15062,1,3,0),
(7,15063,1,3,0),
(7,15066,1,3,0),
(7,15067,1,3,0),
(7,15061,1,3,0),
(7,15060,1,3,0),
(7,1227,1,2,0),
(7,13176,1,2,0),
(7,4360,1,2,0),
(7,4358,1,1,0),
(7,15118,1,3,0),
(7,15119,1,3,0),
(7,15113,1,3,0),
(7,15114,1,3,0),
(7,15115,1,3,0),
(7,15116,1,3,0),
(7,15220,1,3,0),
(7,15221,1,3,0),
(7,14126,1,3,0),
(7,15222,1,3,0),
(7,15223,1,3,0),
(7,15224,1,3,0),
(7,15225,1,3,0),
(7,15524,1,3,0),
(7,15525,1,3,0),
(7,15507,1,3,0),
(7,15508,1,3,0),
(7,15285,1,3,0),
(7,15286,1,3,0),
(7,15281,1,1,0),
(7,15282,1,1,0),
(7,15283,1,1,0),
(7,15284,1,1,0),
(7,15776,1,3,0),
(7,15777,1,3,0),
(7,15774,1,3,0),
(7,15775,1,3,0),
(7,15823,1,3,0),
(7,15824,1,3,0),
(7,15343,1,3,0),
(7,15342,1,3,0),
(7,15341,1,3,0),
(7,15340,1,3,0),
(7,15339,1,3,0),
(7,15338,1,3,0),
(7,15337,1,3,0),
(7,15336,1,3,0),
(7,15335,1,3,0),
(7,15334,1,3,0),
(7,15333,1,3,0),
(7,15332,1,3,0),
(7,15331,1,3,0),
(7,15330,1,3,0),
(7,15329,1,3,0),
(7,15328,1,3,0),
(7,15327,1,3,0),
(7,15326,1,3,0),
(7,15325,1,3,0),
(7,15324,1,3,0),
(7,15323,1,3,0),
(7,15322,1,3,0),
(7,15321,1,3,0),
(7,15314,1,3,0),
(7,15312,1,3,0),
(7,15311,1,3,0),
(7,15306,1,3,0),
(7,15307,1,3,0),
(7,15308,1,3,0),
(7,15309,1,3,0),
(7,15310,1,3,0),
(7,15305,1,3,0),
(7,15304,1,3,0),
(7,15303,1,3,0),
(7,15302,1,3,0),
(7,15301,1,3,0),
(7,15300,1,3,0),
(7,15299,1,3,0),
(7,15298,1,3,0),
(7,15297,1,3,0),
(7,15296,1,3,0),
(7,15295,1,3,0),
(7,15293,1,3,0),
(7,15294,1,3,0),
(7,15292,1,3,0),
(7,15291,1,3,0),
(7,15290,1,3,0),
(7,15289,1,3,0),
(7,15315,1,3,0),
(7,15316,1,3,0),
(7,15317,1,3,0),
(7,15318,1,3,0),
(7,15319,1,3,0),
(7,15320,1,3,0),
(7,15819,1,3,0),
(7,15820,1,3,0),
(7,15821,1,3,0),
(7,15822,1,3,0),
(7,16450,1,3,0),
(7,16451,1,3,0),
(7,16459,1,1,0),
(7,16460,1,1,0),
(7,16461,1,1,0),
(7,16462,1,1,0),
(7,16463,1,1,0),
(7,16464,1,1,0),
(7,16465,1,1,0),
(7,16466,1,1,0),
(7,16467,1,1,0),
(7,16468,1,1,0),
(7,16469,1,1,0),
(7,16470,1,1,0),
(7,16471,1,1,0),
(7,16472,1,1,0),
(7,16454,1,3,0),
(7,16455,1,3,0),
(7,16442,1,3,0),
(7,16443,1,3,0),
(7,16342,1,3,0),
(7,16343,1,3,0),
(7,16444,1,3,0),
(7,16445,1,3,0),
(7,16344,1,3,0),
(7,16345,1,3,0),
(7,16352,1,3,0),
(7,16353,1,3,0),
(7,16446,1,3,0),
(7,16447,1,3,0),
(7,16448,1,3,0),
(7,16449,1,3,0),
(7,16348,1,3,0),
(7,16349,1,3,0)

View File

@@ -1,20 +1,20 @@
package mhfpacket package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfExchangeFpoint2Item represents the MSG_MHF_EXCHANGE_FPOINT_2_ITEM // MsgMhfExchangeFpoint2Item represents the MSG_MHF_EXCHANGE_FPOINT_2_ITEM
type MsgMhfExchangeFpoint2Item struct{ type MsgMhfExchangeFpoint2Item struct {
AckHandle uint32 AckHandle uint32
ItemHash uint32 TradeID uint32
ItemType uint16 ItemType uint16
ItemId uint16 ItemId uint16
Quantity byte Quantity byte
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -25,7 +25,7 @@ func (m *MsgMhfExchangeFpoint2Item) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfExchangeFpoint2Item) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfExchangeFpoint2Item) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.ItemHash = bf.ReadUint32() m.TradeID = bf.ReadUint32()
m.ItemType = bf.ReadUint16() m.ItemType = bf.ReadUint16()
m.ItemId = bf.ReadUint16() m.ItemId = bf.ReadUint16()
m.Quantity = bf.ReadUint8() m.Quantity = bf.ReadUint8()

View File

@@ -1,20 +1,20 @@
package mhfpacket package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfExchangeItem2Fpoint represents the MSG_MHF_EXCHANGE_ITEM_2_FPOINT // MsgMhfExchangeItem2Fpoint represents the MSG_MHF_EXCHANGE_ITEM_2_FPOINT
type MsgMhfExchangeItem2Fpoint struct{ type MsgMhfExchangeItem2Fpoint struct {
AckHandle uint32 AckHandle uint32
ItemHash uint32 TradeID uint32
ItemType uint16 ItemType uint16
ItemId uint16 ItemId uint16
Quantity byte Quantity byte
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -25,7 +25,7 @@ func (m *MsgMhfExchangeItem2Fpoint) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfExchangeItem2Fpoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfExchangeItem2Fpoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.ItemHash = bf.ReadUint32() m.TradeID = bf.ReadUint32()
m.ItemType = bf.ReadUint16() m.ItemType = bf.ReadUint16()
m.ItemId = bf.ReadUint16() m.ItemId = bf.ReadUint16()
m.Quantity = bf.ReadUint8() m.Quantity = bf.ReadUint8()

View File

@@ -1,17 +1,17 @@
package mhfpacket package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfGetGachaPlayHistory represents the MSG_MHF_GET_GACHA_PLAY_HISTORY // MsgMhfGetGachaPlayHistory represents the MSG_MHF_GET_GACHA_PLAY_HISTORY
type MsgMhfGetGachaPlayHistory struct{ type MsgMhfGetGachaPlayHistory struct {
AckHandle uint32 AckHandle uint32
GachaHash uint32 GachaID uint32
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -22,7 +22,7 @@ func (m *MsgMhfGetGachaPlayHistory) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfGetGachaPlayHistory) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetGachaPlayHistory) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.GachaHash = bf.ReadUint32() m.GachaID = bf.ReadUint32()
return nil return nil
} }

29
patch-schema/gacha-db.sql Normal file
View File

@@ -0,0 +1,29 @@
BEGIN;
DROP TABLE IF EXISTS public.gacha_shop;
CREATE TABLE IF NOT EXISTS public.gacha_shop (
id serial PRIMARY KEY,
min_gr integer,
min_hr integer,
name text,
link1 text,
link2 text,
link3 text,
icon integer,
type integer,
hide boolean
);
DROP TABLE IF EXISTS public.fpoint_items;
CREATE TABLE IF NOT EXISTS public.fpoint_items (
id serial PRIMARY KEY,
item_type integer,
item_id integer,
quantity integer,
fpoints integer,
trade_type integer
);
END;

39
patch-schema/shop-db.sql Normal file
View File

@@ -0,0 +1,39 @@
BEGIN;
ALTER TABLE IF EXISTS public.normal_shop_items
RENAME COLUMN itemhash TO id;
ALTER TABLE IF EXISTS public.normal_shop_items
ALTER COLUMN points TYPE integer;
ALTER TABLE IF EXISTS public.normal_shop_items
RENAME COLUMN points TO cost;
ALTER TABLE IF EXISTS public.normal_shop_items
RENAME COLUMN tradequantity TO quantity;
ALTER TABLE IF EXISTS public.normal_shop_items
RENAME COLUMN rankreqlow TO min_hr;
ALTER TABLE IF EXISTS public.normal_shop_items
RENAME COLUMN rankreqhigh TO min_sr;
ALTER TABLE IF EXISTS public.normal_shop_items
RENAME COLUMN rankreqg TO min_gr;
ALTER TABLE IF EXISTS public.normal_shop_items
RENAME COLUMN storelevelreq TO req_store_level;
ALTER TABLE IF EXISTS public.normal_shop_items
RENAME COLUMN maximumquantity TO max_quantity;
ALTER TABLE IF EXISTS public.normal_shop_items
DROP COLUMN boughtquantity;
ALTER TABLE IF EXISTS public.normal_shop_items
RENAME COLUMN roadfloorsrequired TO road_floors;
ALTER TABLE IF EXISTS public.normal_shop_items
RENAME COLUMN weeklyfataliskills TO road_fatalis;
END;

View File

@@ -2,6 +2,7 @@ package channelserver
import ( import (
"encoding/hex" "encoding/hex"
ps "erupe-ce/common/pascalstring"
"time" "time"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
@@ -11,43 +12,91 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
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"`
}
type Gacha struct {
ID uint32 `db:"id"`
MinGR uint32 `db:"min_gr"`
MinHR uint32 `db:"min_hr"`
Name string `db:"name"`
Link1 string `db:"link1"`
Link2 string `db:"link2"`
Link3 string `db:"link3"`
Icon uint16 `db:"icon"`
Type uint16 `db:"type"`
Hide bool `db:"hide"`
}
func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateShop) pkt := p.(*mhfpacket.MsgMhfEnumerateShop)
// SHOP TYPES: // Generic Shop IDs
// 01 = Running Gachas, 02 = actual gacha, 04 = N Points, 05 = GCP, 07 = Item to GCP, 08 = Diva Defense, 10 = Hunter's Road // 0: basic item
// 1: gatherables
// GACHA FORMAT: // 2: hr1-4 materials
// int32: gacha id // 3: hr5-7 materials
// 4: decos
// STORE FORMAT: // 5: other item
// Int16: total item count // 6: g mats
// Int16: total item count // 7: limited item
// 8: special item
// ITEM FORMAT: switch pkt.ShopType {
// int32: Unique item hash for tracking purchases case 1: // Running gachas
// int16: padding? var count uint16
// int16: Item ID shopEntries, err := s.server.db.Queryx("SELECT id, min_gr, min_hr, name, link1, link2, link3, icon, type, hide FROM gacha_shop")
// int16: padding? if err != nil {
// int16: GCP returns doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
// int16: Number traded at once return
// int16: HR or SR Requirement }
// int16: Whichever of the above it isn't resp := byteframe.NewByteFrame()
// int16: GR Requirement resp.WriteUint32(0)
// int16: Store level requirement var gacha Gacha
// int16: Maximum quantity purchasable for shopEntries.Next() {
// int16: Unk err = shopEntries.StructScan(&gacha)
// int16: Road floors cleared requirement if err != nil {
// int16: Road White Fatalis weekly kills continue
if pkt.ShopType == 2 { }
resp.WriteUint32(gacha.ID)
resp.WriteBytes(make([]byte, 16)) // Rank restriction
resp.WriteUint32(gacha.MinGR)
resp.WriteUint32(gacha.MinHR)
resp.WriteUint32(0) // only 0 in known packet
ps.Uint8(resp, gacha.Name, true)
ps.Uint8(resp, gacha.Link1, false)
ps.Uint8(resp, gacha.Link2, false)
resp.WriteBool(gacha.Hide)
ps.Uint8(resp, gacha.Link3, false)
resp.WriteUint16(gacha.Icon)
resp.WriteUint16(gacha.Type)
count++
}
resp.Seek(0, 0)
resp.WriteUint16(count)
resp.WriteUint16(count)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
case 2: // Actual gacha
shopEntries, err := s.server.db.Query("SELECT entryType, itemhash, currType, currNumber, currQuant, percentage, rarityIcon, rollsCount, itemCount, dailyLimit, itemType, itemId, quantity FROM gacha_shop_items WHERE shophash=$1", pkt.ShopID) shopEntries, err := s.server.db.Query("SELECT entryType, itemhash, currType, currNumber, currQuant, percentage, rarityIcon, rollsCount, itemCount, dailyLimit, itemType, itemId, quantity FROM gacha_shop_items WHERE shophash=$1", pkt.ShopID)
if err != nil { if err != nil {
panic(err) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
} }
var entryType, currType, rarityIcon, rollsCount, itemCount, dailyLimit byte var entryType, currType, rarityIcon, rollsCount, itemCount, dailyLimit uint8
var currQuant, currNumber, percentage uint16 var currQuant, currNumber, percentage uint16
var itemhash uint32 var itemhash uint32
var itemType, itemId, quantity pq.Int64Array var itemType, itemId, quantity pq.Int64Array
var entryCount int var count uint16
resp := byteframe.NewByteFrame() resp := byteframe.NewByteFrame()
resp.WriteUint32(pkt.ShopID) resp.WriteUint32(pkt.ShopID)
resp.WriteUint16(0) // total defs resp.WriteUint16(0) // total defs
@@ -74,164 +123,101 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint16(uint16(itemId[i])) // unk, always 0 in existing packets resp.WriteUint16(uint16(itemId[i])) // unk, always 0 in existing packets
resp.WriteUint16(uint16(quantity[i])) // unk, always 0 in existing packets resp.WriteUint16(uint16(quantity[i])) // unk, always 0 in existing packets
} }
entryCount++ count++
}
if entryCount == 0 {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
} }
resp.Seek(4, 0) resp.Seek(4, 0)
resp.WriteUint16(uint16(entryCount)) resp.WriteUint16(count)
doAckBufSucceed(s, pkt.AckHandle, resp.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data())
} else if pkt.ShopType == 1 { case 4: // N Points, 0-6
gachaCount := 0 doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
shopEntries, err := s.server.db.Query("SELECT hash, reqGR, reqHR, gachaName, gachaLink0, gachaLink1, COALESCE(gachaLink2, ''), extraIcon, gachaType, hideFlag FROM gacha_shop") case 5: // GCP->Item, 0-6
if err != nil { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
panic(err) case 6: // Gacha coin->Item
} doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
resp := byteframe.NewByteFrame() case 7: // Item->GCP
resp.WriteUint32(0) data, _ := hex.DecodeString("000300033a9186fb000033860000000a000100000000000000000000000000000000097fdb1c0000067e0000000a0001000000000000000000000000000000001374db29000027c300000064000100000000000000000000000000000000")
var gachaName, gachaLink0, gachaLink1, gachaLink2 string doAckBufSucceed(s, pkt.AckHandle, data)
var hash, reqGR, reqHR, extraIcon, gachaType int case 8: // Diva
var hideFlag bool switch pkt.ShopID {
for shopEntries.Next() { case 0: // Normal exchange
err = shopEntries.Scan(&hash, &reqGR, &reqHR, &gachaName, &gachaLink0, &gachaLink1, &gachaLink2, &extraIcon, &gachaType, &hideFlag) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
if err != nil { case 5: // GCP skills
panic(err)
}
resp.WriteUint32(uint32(hash))
resp.WriteUint32(0) // only 0 in known packets
resp.WriteUint32(0) // all of these seem to trigger the 'rank restriction'
resp.WriteUint32(0) // message so they are presumably placeholders for a
resp.WriteUint32(0) // Z Rank or similar that never turned up?
resp.WriteUint32(uint32(reqGR))
resp.WriteUint32(uint32(reqHR))
resp.WriteUint32(0) // only 0 in known packet
stringBytes := append([]byte(gachaName), 0x00)
resp.WriteUint8(byte(len(stringBytes)))
resp.WriteBytes(stringBytes)
stringBytes = append([]byte(gachaLink0), 0x00)
resp.WriteUint8(byte(len(stringBytes)))
resp.WriteBytes(stringBytes)
stringBytes = append([]byte(gachaLink1), 0x00)
resp.WriteUint8(byte(len(stringBytes)))
resp.WriteBytes(stringBytes)
stringBytes = append([]byte(gachaLink2), 0x00)
resp.WriteBool(hideFlag)
resp.WriteUint8(uint8(len(stringBytes)))
resp.WriteBytes(stringBytes)
resp.WriteUint16(uint16(extraIcon))
resp.WriteUint16(uint16(gachaType))
gachaCount++
}
resp.Seek(0, 0)
resp.WriteUint16(uint16(gachaCount))
resp.WriteUint16(uint16(gachaCount))
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
} else if pkt.ShopType == 7 {
// GCP conversion store
if pkt.ShopID == 0 {
// Items to GCP exchange. Gou Tickets, Shiten Tickets, GP Tickets
data, _ := hex.DecodeString("000300033a9186fb000033860000000a000100000000000000000000000000000000097fdb1c0000067e0000000a0001000000000000000000000000000000001374db29000027c300000064000100000000000000000000000000000000")
doAckBufSucceed(s, pkt.AckHandle, data)
} else {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
} else if pkt.ShopType == 8 {
// Dive Defense sections
// 00 = normal level limited exchange store, 05 = GCP skill store, 07 = limited quantity exchange
if pkt.ShopID == 5 {
// diva defense skill level limited store
data, _ := hex.DecodeString("001f001f2c9365c1000000010000001e000a0000000000000000000a0000000000001979f1c2000000020000003c000a0000000000000000000a0000000000003e5197df000000030000003c000a0000000000000000000a000000000000219337c0000000040000001e000a0000000000000000000a00000000000009b24c9d000000140000001e000a0000000000000000000a0000000000001f1d496e000000150000001e000a0000000000000000000a0000000000003b918fcb000000160000003c000a0000000000000000000a0000000000000b7fd81c000000170000003c000a0000000000000000000a0000000000001374f239000000180000003c000a0000000000000000000a00000000000026950cba0000001c0000003c000a0000000000000000000a0000000000003797eae70000001d0000003c000a012b000000000000000a00000000000015758ad8000000050000003c00000000000000000000000a0000000000003c7035050000000600000050000a0000000000000001000a00000000000024f3b5560000000700000050000a0000000000000001000a00000000000000b600330000000800000050000a0000000000000001000a0000000000002efdce840000001900000050000a0000000000000001000a0000000000002d9365f10000001a00000050000a0000000000000001000a0000000000001979f3420000001f00000050000a012b000000000001000a0000000000003f5397cf0000002000000050000a012b000000000001000a000000000000319337c00000002100000050000a012b000000000001000a00000000000008b04cbd0000000900000064000a0000000000000002000a0000000000000b1d4b6e0000000a00000064000a0000000000000002000a0000000000003b918feb0000000b00000064000a0000000000000002000a0000000000001b7fd81c0000000c00000064000a0000000000000002000a0000000000001276f2290000000d00000064000a0000000000000002000a00000000000022950cba0000000e000000c8000a0000000000000002000a0000000000003697ead70000000f000001f4000a0000000000000003000a00000000000005758a5800000010000003e8000a0000000000000003000a0000000000003c7035250000001b000001f4000a0000000000010003000a00000000000034f3b5d60000001e00000064000a012b000000000003000a00000000000000b600030000002200000064000a0000000000010003000a000000000000") data, _ := hex.DecodeString("001f001f2c9365c1000000010000001e000a0000000000000000000a0000000000001979f1c2000000020000003c000a0000000000000000000a0000000000003e5197df000000030000003c000a0000000000000000000a000000000000219337c0000000040000001e000a0000000000000000000a00000000000009b24c9d000000140000001e000a0000000000000000000a0000000000001f1d496e000000150000001e000a0000000000000000000a0000000000003b918fcb000000160000003c000a0000000000000000000a0000000000000b7fd81c000000170000003c000a0000000000000000000a0000000000001374f239000000180000003c000a0000000000000000000a00000000000026950cba0000001c0000003c000a0000000000000000000a0000000000003797eae70000001d0000003c000a012b000000000000000a00000000000015758ad8000000050000003c00000000000000000000000a0000000000003c7035050000000600000050000a0000000000000001000a00000000000024f3b5560000000700000050000a0000000000000001000a00000000000000b600330000000800000050000a0000000000000001000a0000000000002efdce840000001900000050000a0000000000000001000a0000000000002d9365f10000001a00000050000a0000000000000001000a0000000000001979f3420000001f00000050000a012b000000000001000a0000000000003f5397cf0000002000000050000a012b000000000001000a000000000000319337c00000002100000050000a012b000000000001000a00000000000008b04cbd0000000900000064000a0000000000000002000a0000000000000b1d4b6e0000000a00000064000a0000000000000002000a0000000000003b918feb0000000b00000064000a0000000000000002000a0000000000001b7fd81c0000000c00000064000a0000000000000002000a0000000000001276f2290000000d00000064000a0000000000000002000a00000000000022950cba0000000e000000c8000a0000000000000002000a0000000000003697ead70000000f000001f4000a0000000000000003000a00000000000005758a5800000010000003e8000a0000000000000003000a0000000000003c7035250000001b000001f4000a0000000000010003000a00000000000034f3b5d60000001e00000064000a012b000000000003000a00000000000000b600030000002200000064000a0000000000010003000a000000000000")
doAckBufSucceed(s, pkt.AckHandle, data) doAckBufSucceed(s, pkt.AckHandle, data)
} else { case 7: // Note exchange
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
} }
} else { case 10: // Item shop, 0-8
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) 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 { if err != nil {
panic(err) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
}
var ItemHash, entryCount int
var itemID, Points, TradeQuantity, rankReqLow, rankReqHigh, rankReqG, storeLevelReq, maximumQuantity, boughtQuantity, roadFloorsRequired, weeklyFatalisKills, charQuantity uint16
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)
if err != nil {
panic(err)
}
resp.WriteUint32(uint32(ItemHash))
resp.WriteUint16(0) // unk, always 0 in existing packets
resp.WriteUint16(itemID)
resp.WriteUint16(0) // unk, always 0 in existing packets
resp.WriteUint16(Points) // it's either item ID or quantity for gacha coins
resp.WriteUint16(TradeQuantity) // only for item ID
resp.WriteUint16(rankReqLow)
resp.WriteUint16(rankReqHigh)
resp.WriteUint16(rankReqG)
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)
if err != nil {
resp.WriteUint16(0)
} else {
resp.WriteUint16(charQuantity)
}
} else {
resp.WriteUint16(boughtQuantity)
}
resp.WriteUint16(roadFloorsRequired)
resp.WriteUint16(weeklyFatalisKills)
entryCount++
}
if entryCount == 0 {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return 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.Seek(0, 0)
resp.WriteUint16(uint16(entryCount)) resp.WriteUint16(count)
resp.WriteUint16(uint16(entryCount)) resp.WriteUint16(count)
doAckBufSucceed(s, pkt.AckHandle, resp.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data())
} }
} }
func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {
// writing out to an editable shop enumeration
pkt := p.(*mhfpacket.MsgMhfAcquireExchangeShop) pkt := p.(*mhfpacket.MsgMhfAcquireExchangeShop)
if pkt.DataSize == 10 { bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) exchanges := int(bf.ReadUint16())
_ = bf.ReadUint16() // unk, always 1 in examples for i := 0; i < exchanges; i++ {
itemHash := bf.ReadUint32() itemHash := bf.ReadUint32()
buyCount := bf.ReadUint32() buyCount := bf.ReadUint32()
_, err := s.server.db.Exec(`INSERT INTO shop_item_state (char_id, itemhash, usedquantity) s.server.db.Exec(`INSERT INTO shop_item_state (char_id, itemhash, usedquantity)
VALUES ($1,$2,$3) ON CONFLICT (char_id, itemhash) VALUES ($1,$2,$3) ON CONFLICT (char_id, itemhash)
DO UPDATE SET usedquantity = shop_item_state.usedquantity + $3 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
if err != nil { `, s.charID, itemHash, buyCount)
s.logger.Fatal("Failed to update shop_item_state in db", zap.Error(err))
}
} }
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} }
func handleMsgMhfGetGachaPlayHistory(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetGachaPlayHistory(s *Session, p mhfpacket.MHFPacket) {
// returns number of times the gacha was played, will need persistent db stuff
pkt := p.(*mhfpacket.MsgMhfGetGachaPlayHistory) pkt := p.(*mhfpacket.MsgMhfGetGachaPlayHistory)
doAckBufSucceed(s, pkt.AckHandle, []byte{0x0A}) bf := byteframe.NewByteFrame()
bf.WriteUint8(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleMsgMhfGetGachaPoint(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetGachaPoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGachaPoint) pkt := p.(*mhfpacket.MsgMhfGetGachaPoint)
var fp, gp, gt uint32 var fp, gp, gt uint32
_ = s.server.db.QueryRow("SELECT COALESCE(frontier_points, 0), COALESCE(gacha_prem, 0), COALESCE(gacha_trial,0) FROM characters WHERE id=$1", s.charID).Scan(&fp, &gp, &gt) s.server.db.QueryRow("SELECT COALESCE(frontier_points, 0), COALESCE(gacha_prem, 0), COALESCE(gacha_trial,0) FROM characters WHERE id=$1", s.charID).Scan(&fp, &gp, &gt)
resp := byteframe.NewByteFrame() resp := byteframe.NewByteFrame()
resp.WriteUint32(gp) // Real Gacha Points? resp.WriteUint32(gp) // Real Gacha Points?
resp.WriteUint32(gt) // Trial Gacha Point? resp.WriteUint32(gt) // Trial Gacha Point?
resp.WriteUint32(fp) // Frontier Points? resp.WriteUint32(fp) // Frontier Points?
doAckBufSucceed(s, pkt.AckHandle, resp.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data())
} }
@@ -303,11 +289,11 @@ func handleMsgMhfPlayNormalGacha(s *Session, p mhfpacket.MHFPacket) {
dbUpdate.Seek(0, 0) dbUpdate.Seek(0, 0)
// response needs all item info and the rarity // response needs all item info and the rarity
resp.WriteBytes(dbUpdate.Data()) resp.WriteBytes(dbUpdate.Data())
resp.WriteUint8(uint8(items[ind].(*gachaItem).rarityIcon)) resp.WriteUint8(items[ind].(*gachaItem).rarityIcon)
} }
} }
resp.Seek(0, 0) resp.Seek(0, 0)
resp.WriteUint8(uint8(results)) resp.WriteUint8(results)
doAckBufSucceed(s, pkt.AckHandle, resp.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data())
// add claimables to DB // add claimables to DB
@@ -333,83 +319,77 @@ func handleMsgMhfUseGachaPoint(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfExchangeFpoint2Item(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfExchangeFpoint2Item(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfExchangeFpoint2Item) pkt := p.(*mhfpacket.MsgMhfExchangeFpoint2Item)
var balance uint32
var itemValue, quant int var itemValue, quantity int
_ = s.server.db.QueryRow("SELECT quant, itemValue FROM fpoint_items WHERE hash=$1", pkt.ItemHash).Scan(&quant, &itemValue) _ = s.server.db.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue)
itemCost := (int(pkt.Quantity) * quant) * itemValue cost := (int(pkt.Quantity) * quantity) * itemValue
s.server.db.QueryRow("UPDATE characters SET frontier_points=frontier_points::int - $1 WHERE id=$2 RETURNING frontier_points", cost, s.charID).Scan(&balance)
// also update frontierpoints entry in database bf := byteframe.NewByteFrame()
_, err := s.server.db.Exec("UPDATE characters SET frontier_points=frontier_points::int - $1 WHERE id=$2", itemCost, s.charID) bf.WriteUint32(balance)
if err != nil { doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
s.logger.Fatal("Failed to update minidata in db", zap.Error(err))
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
} }
func handleMsgMhfExchangeItem2Fpoint(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfExchangeItem2Fpoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfExchangeItem2Fpoint) pkt := p.(*mhfpacket.MsgMhfExchangeItem2Fpoint)
var balance uint32
var itemValue, quant int var itemValue, quantity int
_ = s.server.db.QueryRow("SELECT quant, itemValue FROM fpoint_items WHERE hash=$1", pkt.ItemHash).Scan(&quant, &itemValue) s.server.db.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue)
itemCost := (int(pkt.Quantity) / quant) * itemValue cost := (int(pkt.Quantity) / quantity) * itemValue
// also update frontierpoints entry in database s.server.db.QueryRow("UPDATE characters SET frontier_points=COALESCE(frontier_points::int + $1, $1) WHERE id=$2 RETURNING frontier_points", cost, s.charID).Scan(&balance)
_, err := s.server.db.Exec("UPDATE characters SET frontier_points=COALESCE(frontier_points::int + $1, $1) WHERE id=$2", itemCost, s.charID) bf := byteframe.NewByteFrame()
if err != nil { bf.WriteUint32(balance)
s.logger.Fatal("Failed to update minidata in db", zap.Error(err)) doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
} }
func handleMsgMhfGetFpointExchangeList(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetFpointExchangeList(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetFpointExchangeList) pkt := p.(*mhfpacket.MsgMhfGetFpointExchangeList)
//absurd, probably lists every single item to trade to FP?
var buyables int
var sellables int
buyRows, err := s.server.db.Query("SELECT hash,itemType,itemID,quant,itemValue FROM fpoint_items WHERE tradeType=0")
if err != nil {
panic(err)
}
resp := byteframe.NewByteFrame() resp := byteframe.NewByteFrame()
resp.WriteUint32(0) resp.WriteUint32(0)
var hash, itemType, itemID, quant, itemValue int var buyables, sellables uint16
for buyRows.Next() { var id uint32
err = buyRows.Scan(&hash, &itemType, &itemID, &quant, &itemValue) var itemType uint8
if err != nil { var itemID, quantity, fPoints uint16
panic("Error in fpoint_items")
buyRows, err := s.server.db.Query("SELECT id,item_type,item_id,quantity,fpoints FROM fpoint_items WHERE trade_type=0")
if err == nil {
for buyRows.Next() {
err = buyRows.Scan(&id, &itemType, &itemID, &quantity, &fPoints)
if err != nil {
continue
}
resp.WriteUint32(id)
resp.WriteUint16(0)
resp.WriteUint16(0)
resp.WriteUint16(0)
resp.WriteUint8(itemType)
resp.WriteUint16(itemID)
resp.WriteUint16(quantity)
resp.WriteUint16(fPoints)
buyables++
} }
resp.WriteUint32(uint32(hash))
resp.WriteUint32(0) // this and following only 0 in known packets
resp.WriteUint16(0)
resp.WriteUint8(byte(itemType))
resp.WriteUint16(uint16(itemID))
resp.WriteUint16(uint16(quant))
resp.WriteUint16(uint16(itemValue))
buyables++
} }
sellRows, err := s.server.db.Query("SELECT hash,itemType,itemID,quant,itemValue FROM fpoint_items WHERE tradeType=1") sellRows, err := s.server.db.Query("SELECT id,item_type,item_id,quantity,fpoints FROM fpoint_items WHERE trade_type=1")
if err != nil { if err == nil {
panic(err) for sellRows.Next() {
} err = sellRows.Scan(&id, &itemType, &itemID, &quantity, &fPoints)
for sellRows.Next() { if err != nil {
err = sellRows.Scan(&hash, &itemType, &itemID, &quant, &itemValue) continue
if err != nil { }
panic("Error in fpoint_items") resp.WriteUint32(id)
resp.WriteUint16(0)
resp.WriteUint16(0)
resp.WriteUint16(0)
resp.WriteUint8(itemType)
resp.WriteUint16(itemID)
resp.WriteUint16(quantity)
resp.WriteUint16(fPoints)
sellables++
} }
resp.WriteUint32(uint32(hash))
resp.WriteUint32(0) // this and following only 0 in known packets
resp.WriteUint16(0)
resp.WriteUint8(byte(itemType))
resp.WriteUint16(uint16(itemID))
resp.WriteUint16(uint16(quant))
resp.WriteUint16(uint16(itemValue))
sellables++
} }
resp.Seek(0, 0) resp.Seek(0, 0)
resp.WriteUint16(uint16(sellables)) resp.WriteUint16(buyables)
resp.WriteUint16(uint16(buyables)) resp.WriteUint16(sellables)
doAckBufSucceed(s, pkt.AckHandle, resp.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data())
} }
@@ -474,13 +454,13 @@ func handleMsgMhfPlayStepupGacha(s *Session, p mhfpacket.MHFPacket) {
rollFrame.Seek(0, 0) rollFrame.Seek(0, 0)
// response needs all item info and the rarity // response needs all item info and the rarity
resp.WriteBytes(rollFrame.Data()) resp.WriteBytes(rollFrame.Data())
resp.WriteUint8(uint8(items[ind].(*gachaItem).rarityIcon)) resp.WriteUint8(items[ind].(*gachaItem).rarityIcon)
} }
} }
resp.WriteBytes(stepFrame.Data()) resp.WriteBytes(stepFrame.Data())
resp.Seek(0, 0) resp.Seek(0, 0)
resp.WriteUint8(uint8(results + stepResults)) resp.WriteUint8(results + stepResults)
resp.WriteUint8(uint8(results)) resp.WriteUint8(results)
doAckBufSucceed(s, pkt.AckHandle, resp.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data())
// add claimables to DB // add claimables to DB
@@ -494,10 +474,9 @@ func handleMsgMhfPlayStepupGacha(s *Session, p mhfpacket.MHFPacket) {
// reduce real if trial don't cover cost // reduce real if trial don't cover cost
if currType == 19 { if currType == 19 {
_, err = s.server.db.Exec(`UPDATE characters _, err = s.server.db.Exec(`UPDATE characters
SET gacha_trial = CASE WHEN (gacha_trial > $1) then gacha_trial - $1 else gacha_trial end, SET gacha_trial = CASE WHEN (gacha_trial > $1) then gacha_trial - $1 else gacha_trial end,
gacha_prem = CASE WHEN NOT (gacha_trial > $1) then gacha_prem - $1 else gacha_prem end gacha_prem = CASE WHEN NOT (gacha_trial > $1) then gacha_prem - $1 else gacha_prem end
WHERE id=$2`, currNumber, s.charID) WHERE id=$2`, currNumber, s.charID)
} }
if err != nil { if err != nil {
s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err)) s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err))
@@ -565,9 +544,9 @@ func handleMsgMhfGetStepupStatus(s *Session, p mhfpacket.MHFPacket) {
step_progression = 0 step_progression = 0
} }
_, err = s.server.db.Exec(`INSERT INTO stepup_state (shophash, step_progression, step_time, char_id) _, err = s.server.db.Exec(`INSERT INTO stepup_state (shophash, step_progression, step_time, char_id)
VALUES ($1,$2,$3,$4) ON CONFLICT (shophash, char_id) VALUES ($1,$2,$3,$4) ON CONFLICT (shophash, char_id)
DO UPDATE SET step_progression=$2, step_time=$3 DO UPDATE SET step_progression=$2, step_time=$3
WHERE EXCLUDED.char_id=$4 AND EXCLUDED.shophash=$1`, pkt.GachaHash, step_progression, midday, s.charID) WHERE EXCLUDED.char_id=$4 AND EXCLUDED.shophash=$1`, pkt.GachaHash, step_progression, midday, s.charID)
if err != nil { if err != nil {
s.logger.Fatal("Failed to update platedata savedata in db", zap.Error(err)) s.logger.Fatal("Failed to update platedata savedata in db", zap.Error(err))
} }
@@ -626,11 +605,11 @@ func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) {
} }
// get gacha items and iterate through them for gacha roll // get gacha items and iterate through them for gacha roll
shopEntries, err := s.server.db.Query(`SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity shopEntries, err := s.server.db.Query(`SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity
FROM gacha_shop_items FROM gacha_shop_items
WHERE shophash=$1 AND entryType=100 WHERE shophash=$1 AND entryType=100
EXCEPT ALL SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity EXCEPT ALL SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity
FROM gacha_shop_items gsi JOIN lucky_box_state lbs ON gsi.itemhash = ANY(lbs.used_itemhash) FROM gacha_shop_items gsi JOIN lucky_box_state lbs ON gsi.itemhash = ANY(lbs.used_itemhash)
WHERE lbs.char_id=$2`, pkt.GachaHash, s.charID) WHERE lbs.char_id=$2`, pkt.GachaHash, s.charID)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -659,7 +638,7 @@ func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) {
dbUpdate.Seek(0, 0) dbUpdate.Seek(0, 0)
// response needs all item info and the rarity // response needs all item info and the rarity
resp.WriteBytes(dbUpdate.Data()) resp.WriteBytes(dbUpdate.Data())
resp.WriteUint8(uint8(items[ind].(*gachaItem).rarityIcon)) resp.WriteUint8(items[ind].(*gachaItem).rarityIcon)
usedItemHash = append(usedItemHash, int64(items[ind].(*gachaItem).itemhash)) usedItemHash = append(usedItemHash, int64(items[ind].(*gachaItem).itemhash))
} }
@@ -667,7 +646,7 @@ func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) {
items = append(items[:ind], items[ind+1:]...) items = append(items[:ind], items[ind+1:]...)
} }
resp.Seek(0, 0) resp.Seek(0, 0)
resp.WriteUint8(uint8(results)) resp.WriteUint8(results)
doAckBufSucceed(s, pkt.AckHandle, resp.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data())
// add claimables to DB // add claimables to DB
@@ -677,18 +656,18 @@ func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) {
s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err)) s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err))
} }
// update lucky_box_state // update lucky_box_state
_, err = s.server.db.Exec(` INSERT INTO lucky_box_state (char_id, shophash, used_itemhash) _, err = s.server.db.Exec(`INSERT INTO lucky_box_state (char_id, shophash, used_itemhash)
VALUES ($1,$2,$3) ON CONFLICT (char_id, shophash) VALUES ($1,$2,$3) ON CONFLICT (char_id, shophash)
DO UPDATE SET used_itemhash = COALESCE(lucky_box_state.used_itemhash::int[] || $3::int[], $3::int[]) DO UPDATE SET used_itemhash = COALESCE(lucky_box_state.used_itemhash::int[] || $3::int[], $3::int[])
WHERE EXCLUDED.char_id=$1 AND EXCLUDED.shophash=$2`, s.charID, pkt.GachaHash, usedItemHash) WHERE EXCLUDED.char_id=$1 AND EXCLUDED.shophash=$2`, s.charID, pkt.GachaHash, usedItemHash)
if err != nil { if err != nil {
s.logger.Fatal("Failed to update lucky box state in db", zap.Error(err)) s.logger.Fatal("Failed to update lucky box state in db", zap.Error(err))
} }
// deduct gacha coins if relevant, items are handled fine by the standard savedata packet immediately afterwards // deduct gacha coins if relevant, items are handled fine by the standard savedata packet immediately afterwards
if currType == 19 { if currType == 19 {
_, err = s.server.db.Exec(` UPDATE characters _, err = s.server.db.Exec(`UPDATE characters
SET gacha_trial = CASE WHEN (gacha_trial > $1) then gacha_trial - $1 else gacha_trial end, gacha_prem = CASE WHEN NOT (gacha_trial > $1) then gacha_prem - $1 else gacha_prem end SET gacha_trial = CASE WHEN (gacha_trial > $1) then gacha_trial - $1 else gacha_trial end, gacha_prem = CASE WHEN NOT (gacha_trial > $1) then gacha_prem - $1 else gacha_prem end
WHERE id=$2`, currNumber, s.charID) WHERE id=$2`, currNumber, s.charID)
} }
if err != nil { if err != nil {
s.logger.Fatal("Failed to update gacha_trial in db", zap.Error(err)) s.logger.Fatal("Failed to update gacha_trial in db", zap.Error(err))