diff --git a/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go b/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go index c89159c0c..be8947f2c 100644 --- a/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go +++ b/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go @@ -1,15 +1,19 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfPostCafeDurationBonusReceived represents the MSG_MHF_POST_CAFE_DURATION_BONUS_RECEIVED -type MsgMhfPostCafeDurationBonusReceived struct{} +type MsgMhfPostCafeDurationBonusReceived struct { + AckHandle uint32 + Unk0 uint32 + CafeBonusID uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfPostCafeDurationBonusReceived) Opcode() network.PacketID { @@ -18,7 +22,10 @@ func (m *MsgMhfPostCafeDurationBonusReceived) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPostCafeDurationBonusReceived) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.CafeBonusID = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_start_boost_time.go b/network/mhfpacket/msg_mhf_start_boost_time.go index ff424aca9..583ab3ab0 100644 --- a/network/mhfpacket/msg_mhf_start_boost_time.go +++ b/network/mhfpacket/msg_mhf_start_boost_time.go @@ -1,15 +1,18 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfStartBoostTime represents the MSG_MHF_START_BOOST_TIME -type MsgMhfStartBoostTime struct{} +type MsgMhfStartBoostTime struct { + AckHandle uint32 + Unk0 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfStartBoostTime) Opcode() network.PacketID { @@ -18,7 +21,9 @@ func (m *MsgMhfStartBoostTime) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfStartBoostTime) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql new file mode 100644 index 000000000..6bef530cc --- /dev/null +++ b/patch-schema/netcafe.sql @@ -0,0 +1,21 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.characters + ADD COLUMN cafe_time integer DEFAULT 0; + +CREATE TABLE IF NOT EXISTS public.cafebonus +( + id integer NOT NULL PRIMARY KEY, + line integer NOT NULL, + itemclass integer NOT NULL, + itemid integer NOT NULL, + tradequantity integer NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.cafe_accepted +( + cafe_id integer NOT NULL, + character_id integer NOT NULL +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index d3497eae9..d409fdde8 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -10,16 +10,14 @@ import ( "net" "strings" - "io/ioutil" - "math/bits" - "math/rand" - "time" - "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" "go.uber.org/zap" "golang.org/x/text/encoding/japanese" "golang.org/x/text/transform" + "io/ioutil" + "math/bits" + "math/rand" ) // Temporary function to just return no results for a MSG_MHF_ENUMERATE* packet @@ -641,65 +639,6 @@ func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } -func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem) - var netcafe_points int - err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafe_points) - if err != nil { - s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) - } - resp := byteframe.NewByteFrame() - resp.WriteUint32(uint32(netcafe_points)) - doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) -} - -func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfUpdateCafepoint) - var netcafe_points int - err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafe_points) - if err != nil { - s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) - } - resp := byteframe.NewByteFrame() - resp.WriteUint32(0) - resp.WriteUint32(uint32(netcafe_points)) - doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) -} - -func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfCheckDailyCafepoint) - - // I am not sure exactly what this does, but all responses I have seen include this exact sequence of bytes - // 1 daily, 5 daily halk pots, 3 point boosted quests, also adds 5 netcafe points but not sent to client - // available once after midday every day - - // get next midday - var t = Time_static() - year, month, day := t.Date() - midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location()) - if t.After(midday) { - midday = midday.Add(24 * time.Hour) - } - - // get time after which daily claiming would be valid from db - var dailyTime time.Time - err := s.server.db.QueryRow("SELECT COALESCE(daily_time, $2) FROM characters WHERE id = $1", s.charID, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)).Scan(&dailyTime) - if err != nil { - s.logger.Fatal("Failed to get daily_time savedata from db", zap.Error(err)) - } - - if t.After(dailyTime) { - // +5 netcafe points and setting next valid window - _, err := s.server.db.Exec("UPDATE characters SET daily_time=$1, netcafe_points=netcafe_points::int + 5 WHERE id=$2", midday, s.charID) - if err != nil { - s.logger.Fatal("Failed to update daily_time and netcafe_points savedata in db", zap.Error(err)) - } - doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01}) - } else { - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - } -} - func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go new file mode 100644 index 000000000..8692864cf --- /dev/null +++ b/server/channelserver/handlers_cafe.go @@ -0,0 +1,178 @@ +package channelserver + +import ( + "erupe-ce/common/byteframe" + ps "erupe-ce/common/pascalstring" + "erupe-ce/network/mhfpacket" + "go.uber.org/zap" + "time" +) + +func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem) + var netcafe_points int + err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafe_points) + if err != nil { + s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) + } + resp := byteframe.NewByteFrame() + resp.WriteUint32(uint32(netcafe_points)) + doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) +} + +func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfUpdateCafepoint) + var netcafe_points int + err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafe_points) + if err != nil { + s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) + } + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) + resp.WriteUint32(uint32(netcafe_points)) + doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) +} + +func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfCheckDailyCafepoint) + + // I am not sure exactly what this does, but all responses I have seen include this exact sequence of bytes + // 1 daily, 5 daily halk pots, 3 point boosted quests, also adds 5 netcafe points but not sent to client + // available once after midday every day + + // get next midday + var t = Time_static() + year, month, day := t.Date() + midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location()) + if t.After(midday) { + midday = midday.Add(24 * time.Hour) + } + + // get time after which daily claiming would be valid from db + var dailyTime time.Time + err := s.server.db.QueryRow("SELECT COALESCE(daily_time, $2) FROM characters WHERE id = $1", s.charID, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)).Scan(&dailyTime) + if err != nil { + s.logger.Fatal("Failed to get daily_time savedata from db", zap.Error(err)) + } + + if t.After(dailyTime) { + // +5 netcafe points and setting next valid window + _, err := s.server.db.Exec("UPDATE characters SET daily_time=$1, netcafe_points=netcafe_points::int + 5 WHERE id=$2", midday, s.charID) + if err != nil { + s.logger.Fatal("Failed to update daily_time and netcafe_points savedata in db", zap.Error(err)) + } + doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01}) + } else { + doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + } +} + +func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetCafeDuration) + bf := byteframe.NewByteFrame() + + var cafeTime uint32 + err := s.server.db.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.charID).Scan(&cafeTime) + if err != nil { + panic(err) + } + cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime + bf.WriteUint32(cafeTime) // Total hours + bf.WriteUint16(0) + ps.Uint16(bf, "Resets at next maintenance", true) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} + +type CafeBonus struct { + ID uint32 `db:"id"` + Line uint32 `db:"line"` + ItemClass uint32 `db:"itemclass"` + ItemID uint32 `db:"itemid"` + TradeQuantity uint32 `db:"tradequantity"` + FlagCount uint8 `db:"flagcount"` +} + +func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetCafeDurationBonusInfo) + bf := byteframe.NewByteFrame() + + var count uint32 + rows, err := s.server.db.Queryx(` + SELECT cb.id, line, itemclass, itemid, tradequantity, + ( + SELECT count(*) + FROM cafe_accepted ca + WHERE cb.id = ca.cafe_id AND ca.character_id = $1 + ) AS flagcount + FROM cafebonus cb ORDER BY id ASC;`, s.charID) + if err != nil { + s.logger.Error("Error getting cafebonus", zap.Error(err)) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + } else { + for rows.Next() { + count++ + Cafes := &CafeBonus{} + err = rows.StructScan(&Cafes) + if err != nil { + s.logger.Error("Error scanning cafebonus", zap.Error(err)) + } + bf.WriteUint32(Cafes.Line) // Time + bf.WriteUint32(0) // Unk + bf.WriteUint32(Cafes.ItemID) + bf.WriteUint32(Cafes.TradeQuantity) + bf.WriteUint8(Cafes.FlagCount) + } + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) + resp.WriteUint32(uint32(time.Now().Unix())) //timestamp + resp.WriteUint32(count) + resp.WriteBytes(bf.Data()) + doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + } +} + +func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfReceiveCafeDurationBonus) + bf := byteframe.NewByteFrame() + + row := s.server.db.QueryRowx(` + SELECT c.id, c.line, itemclass, itemid, tradequantity + FROM cafebonus c + WHERE ( + SELECT count(*) + FROM cafe_accepted ca + WHERE c.id = ca.cafe_id AND ca.character_id = $1 + ) < 1 AND ( + SELECT ch.cafe_time + $2 + FROM characters ch + WHERE ch.id = $1 + ) >= c.line LIMIT 1;`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) + Cafe := &CafeBonus{} + err := row.StructScan(Cafe) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 8)) + return + } + bf.WriteUint32(1) + bf.WriteUint32(Cafe.ID) + bf.WriteUint32(Cafe.ItemClass) + bf.WriteUint32(Cafe.ItemID) + bf.WriteUint32(Cafe.TradeQuantity) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} + +func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfPostCafeDurationBonusReceived) + + _, err := s.server.db.Exec("INSERT INTO public.cafe_accepted VALUES ($1, $2)", pkt.CafeBonusID, s.charID) + if err != nil { + s.logger.Error("Error updating accepted CafeBonus count", zap.Error(err)) + } + + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} + +func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfStartBoostTime) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go index af16799c7..87ae246af 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -325,8 +325,6 @@ func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } -func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {} - func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_tactics.go b/server/channelserver/handlers_tactics.go index 585c0ef79..4991ce846 100644 --- a/server/channelserver/handlers_tactics.go +++ b/server/channelserver/handlers_tactics.go @@ -2,7 +2,6 @@ package channelserver import ( "encoding/hex" - "erupe-ce/network/mhfpacket" ) @@ -58,11 +57,3 @@ func handleMsgMhfGetUdTacticsRanking(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdTacticsLog(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) {}