Merge pull request #97 from ZeruLight/fix/rework-distributions

fix/rework-distributions
This commit is contained in:
wish
2023-11-11 19:42:08 +11:00
committed by GitHub
4 changed files with 178 additions and 131 deletions

View File

@@ -0,0 +1,11 @@
BEGIN;
-- Adds a Distribution that can be accepted up to 20 times that gives one of Item Type 30 (Item Box extra page)
INSERT INTO distribution (type, event_name, description, times_acceptable) VALUES (1, 'Extra Item Storage', '~C05Adds one new page to your Item Box.', 20);
INSERT INTO distribution_items (distribution_id, item_type, quantity) VALUES ((SELECT id FROM distribution ORDER BY id DESC LIMIT 1), 30, 1);
-- Adds a Distribution that can be accepted up to 20 times that gives one of Item Type 31 (Equipment Box extra page)
INSERT INTO distribution (type, event_name, description, times_acceptable) VALUES (1, 'Extra Equipment Storage', '~C05Adds one new page to your Equipment Box.', 20);
INSERT INTO distribution_items (distribution_id, item_type, quantity) VALUES ((SELECT id FROM distribution ORDER BY id DESC LIMIT 1), 31, 1);
END;

View File

@@ -10,7 +10,7 @@ import (
// MsgMhfEnumerateDistItem represents the MSG_MHF_ENUMERATE_DIST_ITEM // MsgMhfEnumerateDistItem represents the MSG_MHF_ENUMERATE_DIST_ITEM
type MsgMhfEnumerateDistItem struct { type MsgMhfEnumerateDistItem struct {
AckHandle uint32 AckHandle uint32
Unk0 uint8 DistType uint8
Unk1 uint8 Unk1 uint8
Unk2 uint16 Unk2 uint16
Unk3 []byte Unk3 []byte
@@ -24,9 +24,9 @@ func (m *MsgMhfEnumerateDistItem) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfEnumerateDistItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfEnumerateDistItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() m.DistType = bf.ReadUint8()
m.Unk1 = bf.ReadUint8() m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint16() m.Unk2 = bf.ReadUint16() // Maximum? Hardcoded to 256
m.Unk3 = bf.ReadBytes(uint(bf.ReadUint8())) m.Unk3 = bf.ReadBytes(uint(bf.ReadUint8()))
return nil return nil
} }

View File

@@ -0,0 +1,36 @@
BEGIN;
-- This will delete all of your old distribution data!
--ALTER TABLE IF EXISTS public.distribution DROP COLUMN IF EXISTS data;
CREATE TABLE public.distribution_items
(
id serial PRIMARY KEY,
distribution_id integer NOT NULL,
item_type integer NOT NULL,
item_id integer,
quantity integer
);
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_hr DROP DEFAULT;
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_hr DROP DEFAULT;
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_sr DROP DEFAULT;
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_sr DROP DEFAULT;
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_gr DROP DEFAULT;
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_gr DROP DEFAULT;
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_hr DROP NOT NULL;
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_hr DROP NOT NULL;
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_sr DROP NOT NULL;
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_sr DROP NOT NULL;
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_gr DROP NOT NULL;
ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_gr DROP NOT NULL;
UPDATE distribution SET min_hr=NULL WHERE min_hr=65535;
UPDATE distribution SET max_hr=NULL WHERE max_hr=65535;
UPDATE distribution SET min_sr=NULL WHERE min_sr=65535;
UPDATE distribution SET max_sr=NULL WHERE max_sr=65535;
UPDATE distribution SET min_gr=NULL WHERE min_gr=65535;
UPDATE distribution SET max_gr=NULL WHERE max_gr=65535;
END;

View File

@@ -4,161 +4,161 @@ import (
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
ps "erupe-ce/common/pascalstring" ps "erupe-ce/common/pascalstring"
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
"time"
"go.uber.org/zap" "go.uber.org/zap"
) )
type ItemDist struct { type Distribution struct {
ID uint32 `db:"id"` ID uint32 `db:"id"`
Deadline uint32 `db:"deadline"` Deadline time.Time `db:"deadline"`
TimesAcceptable uint16 `db:"times_acceptable"` TimesAcceptable uint16 `db:"times_acceptable"`
TimesAccepted uint16 `db:"times_accepted"` TimesAccepted uint16 `db:"times_accepted"`
MinHR uint16 `db:"min_hr"` MinHR int16 `db:"min_hr"`
MaxHR uint16 `db:"max_hr"` MaxHR int16 `db:"max_hr"`
MinSR uint16 `db:"min_sr"` MinSR int16 `db:"min_sr"`
MaxSR uint16 `db:"max_sr"` MaxSR int16 `db:"max_sr"`
MinGR uint16 `db:"min_gr"` MinGR int16 `db:"min_gr"`
MaxGR uint16 `db:"max_gr"` MaxGR int16 `db:"max_gr"`
EventName string `db:"event_name"` EventName string `db:"event_name"`
Description string `db:"description"` Description string `db:"description"`
Data []byte `db:"data"` Data []byte `db:"data"`
} }
func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateDistItem) pkt := p.(*mhfpacket.MsgMhfEnumerateDistItem)
var itemDists []Distribution
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
distCount := 0 rows, err := s.server.db.Queryx(`
dists, err := s.server.db.Queryx(`
SELECT d.id, event_name, description, times_acceptable, SELECT d.id, event_name, description, times_acceptable,
min_hr, max_hr, min_sr, max_sr, min_gr, max_gr, COALESCE(min_hr, -1) AS min_hr, COALESCE(max_hr, -1) AS max_hr,
COALESCE(min_sr, -1) AS min_sr, COALESCE(max_sr, -1) AS max_sr,
COALESCE(min_gr, -1) AS min_gr, COALESCE(max_gr, -1) AS max_gr,
( (
SELECT count(*) SELECT count(*) FROM distributions_accepted da
FROM distributions_accepted da WHERE d.id = da.distribution_id AND da.character_id = $1
WHERE d.id = da.distribution_id
AND da.character_id = $1
) AS times_accepted, ) AS times_accepted,
CASE COALESCE(deadline, TO_TIMESTAMP(0)) AS deadline
WHEN (EXTRACT(epoch FROM deadline)::int) IS NULL THEN 0
ELSE (EXTRACT(epoch FROM deadline)::int)
END deadline
FROM distribution d FROM distribution d
WHERE character_id = $1 AND type = $2 OR character_id IS NULL AND type = $2 ORDER BY id DESC; WHERE character_id = $1 AND type = $2 OR character_id IS NULL AND type = $2 ORDER BY id DESC
`, s.charID, pkt.Unk0) `, s.charID, pkt.DistType)
if err != nil {
s.logger.Error("Error getting distribution data from db", zap.Error(err)) if err == nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) var itemDist Distribution
} else { for rows.Next() {
for dists.Next() { err = rows.StructScan(&itemDist)
distCount++
distData := &ItemDist{}
err = dists.StructScan(&distData)
if err != nil { if err != nil {
s.logger.Error("Error parsing item distribution data", zap.Error(err)) continue
} }
bf.WriteUint32(distData.ID) itemDists = append(itemDists, itemDist)
bf.WriteUint32(distData.Deadline) }
bf.WriteUint32(0) // Unk }
bf.WriteUint16(distData.TimesAcceptable)
bf.WriteUint16(distData.TimesAccepted) bf.WriteUint16(uint16(len(itemDists)))
bf.WriteUint16(0) // Unk for _, dist := range itemDists {
bf.WriteUint16(distData.MinHR) bf.WriteUint32(dist.ID)
bf.WriteUint16(distData.MaxHR) bf.WriteUint32(uint32(dist.Deadline.Unix()))
bf.WriteUint16(distData.MinSR) bf.WriteUint32(0) // Unk
bf.WriteUint16(distData.MaxSR) bf.WriteUint16(dist.TimesAcceptable)
bf.WriteUint16(distData.MinGR) bf.WriteUint16(dist.TimesAccepted)
bf.WriteUint16(distData.MaxGR) bf.WriteUint16(0) // Unk
bf.WriteUint8(0) bf.WriteInt16(dist.MinHR)
bf.WriteUint16(0) bf.WriteInt16(dist.MaxHR)
bf.WriteUint8(0) bf.WriteInt16(dist.MinSR)
bf.WriteUint16(0) bf.WriteInt16(dist.MaxSR)
bf.WriteUint16(0) bf.WriteInt16(dist.MinGR)
bf.WriteUint8(0) bf.WriteInt16(dist.MaxGR)
ps.Uint8(bf, distData.EventName, true) bf.WriteUint8(0)
for i := 0; i < 6; i++ { bf.WriteUint16(0)
for j := 0; j < 13; j++ { bf.WriteUint8(0)
bf.WriteUint8(0) bf.WriteUint16(0)
bf.WriteUint32(0) bf.WriteUint16(0)
} bf.WriteUint8(0)
} ps.Uint8(bf, dist.EventName, true)
i := uint8(0) for i := 0; i < 6; i++ {
bf.WriteUint8(i) for j := 0; j < 13; j++ {
if i <= 10 { bf.WriteUint8(0)
for j := uint8(0); j < i; j++ { bf.WriteUint32(0)
bf.WriteUint32(0) }
bf.WriteUint32(0) }
bf.WriteUint32(0) i := uint8(0)
} bf.WriteUint8(i)
if i <= 10 {
for j := uint8(0); j < i; j++ {
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint32(0)
} }
} }
resp := byteframe.NewByteFrame()
resp.WriteUint16(uint16(distCount))
resp.WriteBytes(bf.Data())
resp.WriteUint8(0)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
} }
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
type DistributionItem struct {
ItemType uint8 `db:"item_type"`
ID uint32 `db:"id"`
ItemID uint32 `db:"item_id"`
Quantity uint32 `db:"quantity"`
}
func getDistributionItems(s *Session, i uint32) []DistributionItem {
var distItems []DistributionItem
rows, err := s.server.db.Queryx(`SELECT id, item_type, COALESCE(item_id, 0) AS item_id, COALESCE(quantity, 0) AS quantity FROM distribution_items WHERE distribution_id=$1`, i)
if err == nil {
var distItem DistributionItem
for rows.Next() {
err = rows.StructScan(&distItem)
if err != nil {
continue
}
distItems = append(distItems, distItem)
}
}
return distItems
} }
func handleMsgMhfApplyDistItem(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfApplyDistItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfApplyDistItem) pkt := p.(*mhfpacket.MsgMhfApplyDistItem)
bf := byteframe.NewByteFrame()
if pkt.DistributionID == 0 { bf.WriteUint32(pkt.DistributionID)
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 6)) distItems := getDistributionItems(s, pkt.DistributionID)
} else { bf.WriteUint16(uint16(len(distItems)))
row := s.server.db.QueryRowx("SELECT data FROM distribution WHERE id = $1", pkt.DistributionID) for _, item := range distItems {
dist := &ItemDist{} bf.WriteUint8(item.ItemType)
err := row.StructScan(dist) bf.WriteUint32(item.ItemID)
if err != nil { bf.WriteUint32(item.Quantity)
s.logger.Error("Error parsing item distribution data", zap.Error(err)) bf.WriteUint32(item.ID)
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 6))
return
}
if len(dist.Data) >= 2 {
distData := byteframe.NewByteFrameFromBytes(dist.Data)
distItems := int(distData.ReadUint16())
for i := 0; i < distItems; i++ {
if len(dist.Data) >= 2+(i*13) {
itemType := distData.ReadUint8()
_ = distData.ReadBytes(6)
quantity := int(distData.ReadUint16())
_ = distData.ReadBytes(4)
switch itemType {
case 17:
_ = addPointNetcafe(s, quantity)
case 19:
s.server.db.Exec("UPDATE users u SET gacha_premium=gacha_premium+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", quantity, s.charID)
case 20:
s.server.db.Exec("UPDATE users u SET gacha_trial=gacha_trial+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", quantity, s.charID)
case 21:
s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", quantity, s.charID)
case 23:
saveData, err := GetCharacterSaveData(s, s.charID)
if err == nil {
saveData.RP += uint16(quantity)
saveData.Save(s)
}
}
}
}
}
bf := byteframe.NewByteFrame()
bf.WriteUint32(pkt.DistributionID)
bf.WriteBytes(dist.Data)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
_, err = s.server.db.Exec(`
INSERT INTO public.distributions_accepted
VALUES ($1, $2)
`, pkt.DistributionID, s.charID)
if err != nil {
s.logger.Error("Error updating accepted dist count", zap.Error(err))
}
} }
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleMsgMhfAcquireDistItem(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireDistItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireDistItem) pkt := p.(*mhfpacket.MsgMhfAcquireDistItem)
if pkt.DistributionID > 0 {
_, err := s.server.db.Exec(`INSERT INTO public.distributions_accepted VALUES ($1, $2)`, pkt.DistributionID, s.charID)
if err == nil {
distItems := getDistributionItems(s, pkt.DistributionID)
for _, item := range distItems {
switch item.ItemType {
case 17:
_ = addPointNetcafe(s, int(item.Quantity))
case 19:
s.server.db.Exec("UPDATE users u SET gacha_premium=gacha_premium+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.charID)
case 20:
s.server.db.Exec("UPDATE users u SET gacha_trial=gacha_trial+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.charID)
case 21:
s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.charID)
case 23:
saveData, err := GetCharacterSaveData(s, s.charID)
if err == nil {
saveData.RP += uint16(item.Quantity)
saveData.Save(s)
}
}
}
}
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }