From 9c772a09fcc347e92ea3b01995417159eb9b801b Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 27 Jul 2022 21:05:57 +1000 Subject: [PATCH 01/86] initial festa build --- Erupe/config.json | 2 +- Erupe/festa.sql | 164 +++++++++++++ Erupe/server/channelserver/handlers_festa.go | 220 +++++++++++++----- Erupe/server/channelserver/handlers_guild.go | 20 +- .../channelserver/handlers_guild_member.go | 51 ++-- 5 files changed, 364 insertions(+), 93 deletions(-) create mode 100644 Erupe/festa.sql diff --git a/Erupe/config.json b/Erupe/config.json index eb59db613..b3be9512f 100644 --- a/Erupe/config.json +++ b/Erupe/config.json @@ -11,7 +11,7 @@ "MaxHexdumpLength": 256, "Event": 0, "DivaEvent": 0, - "FestaEvent": 0, + "FestaEvent": -1, "TournamentEvent": 0, "MezFesEvent": true, "SaveDumps": { diff --git a/Erupe/festa.sql b/Erupe/festa.sql new file mode 100644 index 000000000..da56e8c89 --- /dev/null +++ b/Erupe/festa.sql @@ -0,0 +1,164 @@ +BEGIN; + +CREATE TYPE event_type AS ENUM ('festa', 'diva', 'vs', 'mezfes'); + +DROP TABLE IF EXISTS public.event_week; + +ALTER TABLE IF EXISTS public.guild_characters + ADD COLUMN IF NOT EXISTS souls int DEFAULT 0; + +ALTER TABLE IF EXISTS public.guilds + DROP COLUMN IF EXISTS festival_colour; + +CREATE TABLE IF NOT EXISTS public.events +( + id serial NOT NULL PRIMARY KEY, + event_type event_type NOT NULL, + start_time timestamp without time zone NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.festa_registrations +( + guild_id int NOT NULL, + team festival_colour NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.festa_trials +( + id serial NOT NULL PRIMARY KEY, + objective int NOT NULL, + goal_id int NOT NULL, + times_req int NOT NULL, + locale_req int NOT NULL DEFAULT 0, + reward int NOT NULL +); + +-- Ripped trials +INSERT INTO public.festa_trials + (objective, goal_id, times_req, locale_req, reward) +VALUES + (1,27,1,0,1), + (5,53034,0,0,400), + (5,22042,0,0,89), + (5,23397,0,0,89), + (1,28,1,0,1), + (1,68,1,0,1), + (1,6,1,0,2), + (1,38,1,0,2), + (1,20,1,0,3), + (1,39,1,0,4), + (1,48,1,0,4), + (1,67,1,0,4), + (1,93,1,0,4), + (1,22,1,0,5), + (1,52,1,0,5), + (1,101,1,0,5), + (1,1,1,0,5), + (1,37,1,0,5), + (1,15,1,0,5), + (1,45,1,0,5), + (1,74,1,0,5), + (1,78,1,0,5), + (1,103,1,0,5), + (1,51,1,0,6), + (1,17,1,0,6), + (1,21,1,0,6), + (1,92,1,0,6), + (1,47,1,0,7), + (1,46,1,0,7), + (1,26,1,0,7), + (1,14,1,0,7), + (1,11,1,0,7), + (1,44,1,0,8), + (1,43,1,0,8), + (1,49,1,0,8), + (1,40,1,0,8), + (1,76,1,0,8), + (1,89,1,0,8), + (1,94,1,0,8), + (1,96,1,0,8), + (1,75,1,0,8), + (1,91,1,0,8), + (1,53,1,0,9), + (1,80,1,0,9), + (1,42,1,0,9), + (1,79,1,0,9), + (1,81,1,0,10), + (1,41,1,0,10), + (1,82,1,0,10), + (1,90,1,0,10), + (1,149,1,0,10), + (1,85,1,0,11), + (1,95,1,0,11), + (1,121,1,0,11), + (1,142,1,0,11), + (1,141,1,0,11), + (1,146,1,0,12), + (1,147,1,0,12), + (1,148,1,0,12), + (1,151,1,0,12), + (1,152,1,0,12), + (1,159,1,0,12), + (1,153,1,0,12), + (1,162,1,0,12), + (1,111,1,0,13), + (1,110,1,0,13), + (1,112,1,0,13), + (1,109,1,0,14), + (1,169,1,0,15), + (2,33,1,0,6), + (2,104,1,0,8), + (2,119,1,0,8), + (2,120,1,0,8), + (2,54,1,0,8), + (2,59,1,0,8), + (2,64,1,0,8), + (2,65,1,0,8), + (2,99,1,0,9), + (2,83,1,0,9), + (2,84,1,0,10), + (2,77,1,0,10), + (2,106,1,0,10), + (2,55,1,0,10), + (2,58,1,0,10), + (2,7,1,0,10), + (2,50,1,0,11), + (2,131,1,0,11), + (2,129,1,0,11), + (2,140,1,0,11), + (2,122,1,0,11), + (2,126,1,0,11), + (2,127,1,0,11), + (2,128,1,0,11), + (2,130,1,0,11), + (2,139,1,0,11), + (2,144,1,0,11), + (2,150,1,0,11), + (2,158,1,0,11), + (2,164,1,0,15), + (2,165,1,0,15), + (2,2,1,7,15), + (2,36,1,0,15), + (2,71,1,0,15), + (2,108,1,0,15), + (2,116,1,0,15), + (2,107,1,0,15), + (2,154,1,0,17), + (2,166,1,0,17), + (2,170,1,0,18), + (3,31,1,0,1), + (3,8,1,0,3), + (3,123,1,0,8), + (3,105,1,0,9), + (3,125,1,0,11), + (3,115,1,0,12), + (3,114,1,0,12), + (3,161,1,0,12), + (4,670,1,0,1), + (4,671,1,0,1), + (4,672,1,0,1), + (4,675,1,0,1), + (4,673,1,0,1), + (4,674,1,0,1); + +END; \ No newline at end of file diff --git a/Erupe/server/channelserver/handlers_festa.go b/Erupe/server/channelserver/handlers_festa.go index c1f3cecfb..3c39f9999 100644 --- a/Erupe/server/channelserver/handlers_festa.go +++ b/Erupe/server/channelserver/handlers_festa.go @@ -2,12 +2,11 @@ package channelserver import ( "encoding/hex" - "math/rand" - "time" - "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" "erupe-ce/network/mhfpacket" + "math/rand" + "time" ) func handleMsgMhfSaveMezfesData(s *Session, p mhfpacket.MHFPacket) { @@ -69,65 +68,129 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +func cleanupFesta(s *Session) { + s.server.db.Exec("DELETE FROM events WHERE event_type='festa'") + s.server.db.Exec("DELETE FROM festa_registrations") + s.server.db.Exec("UPDATE guild_characters SET souls=0") +} + +func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 { + timestamps := make([]uint32, 5) + midnight := Time_Current_Midnight() + if debug && start <= 3 { + switch start { + case 1: + timestamps[0] = uint32(midnight.Unix()) + timestamps[1] = uint32(midnight.Add(24 * 7 * time.Hour).Unix()) + timestamps[2] = uint32(midnight.Add(24 * 14 * time.Hour).Unix()) + timestamps[3] = uint32(midnight.Add(24*14*time.Hour + 150*time.Minute).Unix()) + timestamps[4] = uint32(midnight.Add(24*28*time.Hour + 11*time.Hour).Unix()) + case 2: + timestamps[0] = uint32(midnight.Add(-24 * 7 * time.Hour).Unix()) + timestamps[1] = uint32(midnight.Unix()) + timestamps[2] = uint32(midnight.Add(24 * 7 * time.Hour).Unix()) + timestamps[3] = uint32(midnight.Add(24*7*time.Hour + 150*time.Minute).Unix()) + timestamps[4] = uint32(midnight.Add(24 * 21 * time.Hour).Add(11 * time.Hour).Unix()) + case 3: + timestamps[0] = uint32(midnight.Add(-24 * 14 * time.Hour).Unix()) + timestamps[1] = uint32(midnight.Add(-24*7*time.Hour + 11*time.Hour).Unix()) + timestamps[2] = uint32(midnight.Unix()) + timestamps[3] = uint32(midnight.Add(150 * time.Minute).Unix()) + timestamps[4] = uint32(midnight.Add(24*14*time.Hour + 11*time.Hour).Unix()) + } + return timestamps + } + if start == 0 || Time_Current_Adjusted().Unix() > int64(start)+2977200 { + cleanupFesta(s) + // Generate a new festa, starting midnight tomorrow + start = uint32(midnight.Add(24 * time.Hour).Unix()) + s.server.db.Exec("INSERT INTO events (event_type, start_time) VALUES ('festa', to_timestamp($1)::timestamp without time zone)", start) + } + timestamps[0] = start + timestamps[1] = timestamps[0] + 604800 + timestamps[2] = timestamps[1] + 604800 + timestamps[3] = timestamps[2] + 9000 + timestamps[4] = timestamps[3] + 1240200 + return timestamps +} + +type Trial struct { + ID uint32 `db:"id"` + Objective uint8 `db:"objective"` + GoalID uint32 `db:"goal_id"` + TimesReq uint16 `db:"times_req"` + Locale uint16 `db:"locale_req"` + Reward uint16 `db:"reward"` +} + func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfInfoFesta) bf := byteframe.NewByteFrame() - state := s.server.erupeConfig.DevModeOptions.FestaEvent - bf.WriteUint32(0xdeadbeef) // festaID - // Registration Week Start - // Introductory Week Start - // Totalling Time - // Reward Festival Start (2.5hrs after totalling) - // 2 weeks after RewardFes (next fes?) - midnight := Time_Current_Midnight() - switch state { - case 1: - bf.WriteUint32(uint32(midnight.Unix())) - bf.WriteUint32(uint32(midnight.Add(24 * 7 * time.Hour).Unix())) - bf.WriteUint32(uint32(midnight.Add(24 * 14 * time.Hour).Unix())) - bf.WriteUint32(uint32(midnight.Add(24*14*time.Hour + 150*time.Minute).Unix())) - bf.WriteUint32(uint32(midnight.Add(24*28*time.Hour + 11*time.Hour).Unix())) - case 2: - bf.WriteUint32(uint32(midnight.Add(-24 * 7 * time.Hour).Unix())) - bf.WriteUint32(uint32(midnight.Unix())) - bf.WriteUint32(uint32(midnight.Add(24 * 7 * time.Hour).Unix())) - bf.WriteUint32(uint32(midnight.Add(24*7*time.Hour + 150*time.Minute).Unix())) - bf.WriteUint32(uint32(midnight.Add(24 * 21 * time.Hour).Add(11 * time.Hour).Unix())) - case 3: - bf.WriteUint32(uint32(midnight.Add(-24 * 14 * time.Hour).Unix())) - bf.WriteUint32(uint32(midnight.Add(-24*7*time.Hour + 11*time.Hour).Unix())) - bf.WriteUint32(uint32(midnight.Unix())) - bf.WriteUint32(uint32(midnight.Add(150 * time.Minute).Unix())) - bf.WriteUint32(uint32(midnight.Add(24*14*time.Hour + 11*time.Hour).Unix())) - default: + + id, start := uint32(0xDEADBEEF), uint32(0) + rows, _ := s.server.db.Queryx("SELECT id, (EXTRACT(epoch FROM start_time)::int) as start_time FROM events WHERE event_type='festa'") + for rows.Next() { + rows.Scan(&id, &start) + } + + var timestamps []uint32 + if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.FestaEvent >= 0 { + if s.server.erupeConfig.DevModeOptions.FestaEvent == 0 { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + return + } + timestamps = generateFestaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.FestaEvent), true) + } else { + timestamps = generateFestaTimestamps(s, start, false) + } + + if timestamps[0] > uint32(Time_Current_Adjusted().Unix()) { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } - bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // TS Current Time + + var blueSouls, redSouls uint32 + s.server.db.QueryRow("SELECT SUM(gc.souls) FROM guild_characters gc INNER JOIN festa_registrations fr ON fr.guild_id = gc.guild_id WHERE fr.team = 'blue'").Scan(&blueSouls) + s.server.db.QueryRow("SELECT SUM(gc.souls) FROM guild_characters gc INNER JOIN festa_registrations fr ON fr.guild_id = gc.guild_id WHERE fr.team = 'red'").Scan(&redSouls) + + bf.WriteUint32(id) + for _, timestamp := range timestamps { + bf.WriteUint32(timestamp) + } + bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) bf.WriteUint8(4) ps.Uint8(bf, "", false) bf.WriteUint32(0) - bf.WriteUint32(0) // Blue souls - bf.WriteUint32(0) // Red souls + bf.WriteUint32(blueSouls) + bf.WriteUint32(redSouls) - trials := 0 - bf.WriteUint16(uint16(trials)) - for i := 0; i < trials; i++ { - bf.WriteUint32(uint32(i + 1)) // trialID - bf.WriteUint8(0xFF) // unk - bf.WriteUint8(uint8(i)) // objective - bf.WriteUint32(0x1B) // monID, itemID if deliver - bf.WriteUint16(1) // huntsRemain? - bf.WriteUint16(0) // location - bf.WriteUint16(1) // numSoulsReward - bf.WriteUint8(0xFF) // unk - bf.WriteUint8(0xFF) // monopolised - bf.WriteUint16(0) // unk + rows, _ = s.server.db.Queryx("SELECT * FROM festa_trials") + trialData := byteframe.NewByteFrame() + var count uint16 + for rows.Next() { + trial := &Trial{} + err := rows.StructScan(&trial) + if err != nil { + continue + } + count++ + trialData.WriteUint32(trial.ID) + trialData.WriteUint8(0) // Unk + trialData.WriteUint8(trial.Objective) + trialData.WriteUint32(trial.GoalID) + trialData.WriteUint16(trial.TimesReq) + trialData.WriteUint16(trial.Locale) + trialData.WriteUint16(trial.Reward) + trialData.WriteUint8(0xFF) // Unk + trialData.WriteUint8(0xFF) // MonopolyState + trialData.WriteUint16(0) // Unk } + bf.WriteUint16(count) + bf.WriteBytes(trialData.Data()) - unk := 0 // static rewards? - bf.WriteUint16(uint16(unk)) - for i := 0; i < unk; i++ { + unk := uint16(0) // static rewards? + bf.WriteUint16(unk) + for i := uint16(0); i < unk; i++ { bf.WriteUint32(0) bf.WriteUint16(0) bf.WriteUint16(0) @@ -144,8 +207,15 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { // state festa (U)ser func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStateFestaU) + guild, err := GetGuildInfoByCharacterId(s, s.charID) + if err != nil || guild == nil { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + return + } + var souls uint32 + s.server.db.QueryRow("SELECT souls FROM guild_characters WHERE character_id=$1", s.charID).Scan(&souls) bf := byteframe.NewByteFrame() - bf.WriteUint32(0) // souls + bf.WriteUint32(souls) bf.WriteUint32(0) // unk doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -153,8 +223,13 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { // state festa (G)uild func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStateFestaG) + guild, err := GetGuildInfoByCharacterId(s, s.charID) + if err != nil || guild == nil { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + return + } resp := byteframe.NewByteFrame() - resp.WriteUint32(0) // souls + resp.WriteUint32(guild.Souls) resp.WriteUint32(1) // unk resp.WriteUint32(1) // unk resp.WriteUint32(1) // unk, rank? @@ -164,31 +239,54 @@ func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateFestaMember) + guild, err := GetGuildInfoByCharacterId(s, s.charID) + if err != nil || guild == nil { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + return + } + members, err := GetGuildMembers(s, guild.ID, false) + if err != nil { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + return + } bf := byteframe.NewByteFrame() - bf.WriteUint16(0) // numMembers - // uint16 unk - // uint32 charID - // uint32 souls + bf.WriteUint16(uint16(len(members))) + for _, member := range members { + bf.WriteUint16(0) + bf.WriteUint32(member.CharID) + bf.WriteUint32(member.Souls) + } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfVoteFesta(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfEntryFesta) + pkt := p.(*mhfpacket.MsgMhfVoteFesta) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEntryFesta) - bf := byteframe.NewByteFrame() + guild, err := GetGuildInfoByCharacterId(s, s.charID) + if err != nil || guild == nil { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + return + } rand.Seed(time.Now().UnixNano()) - bf.WriteUint32(uint32(rand.Intn(2))) - // Update guild table + team := uint32(rand.Intn(2)) + switch team { + case 0: + s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'blue')", guild.ID) + case 1: + s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'red')", guild.ID) + } + bf := byteframe.NewByteFrame() + bf.WriteUint32(team) doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfChargeFesta) - // Update festa state table + s.server.db.Exec("UPDATE guild_characters SET souls=souls+$1 WHERE character_id=$2", pkt.Souls, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } diff --git a/Erupe/server/channelserver/handlers_guild.go b/Erupe/server/channelserver/handlers_guild.go index 9051e8098..2277e7561 100644 --- a/Erupe/server/channelserver/handlers_guild.go +++ b/Erupe/server/channelserver/handlers_guild.go @@ -12,10 +12,10 @@ import ( "strings" "time" - "erupe-ce/common/byteframe" "erupe-ce/common/bfutil" - "erupe-ce/common/stringsupport" + "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" + "erupe-ce/common/stringsupport" "erupe-ce/network/mhfpacket" "github.com/jmoiron/sqlx" "go.uber.org/zap" @@ -56,6 +56,7 @@ type Guild struct { PugiName2 string `db:"pugi_name_2"` PugiName3 string `db:"pugi_name_3"` FestivalColour FestivalColour `db:"festival_colour"` + Souls uint32 `db:"souls"` Rank uint16 `db:"rank"` AllianceID uint32 `db:"alliance_id"` Icon *GuildIcon `db:"icon"` @@ -124,7 +125,14 @@ SELECT pugi_name_1, pugi_name_2, pugi_name_3, - festival_colour, + CASE WHEN ( + SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id + ) IS NULL THEN 'none' ELSE ( + SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id + ) END festival_colour, + ( + SELECT SUM(souls) FROM guild_characters gc WHERE gc.guild_id = g.id + ) AS souls, CASE WHEN rank_rp <= 48 THEN rank_rp/24 WHEN rank_rp <= 288 THEN rank_rp/48+1 @@ -138,14 +146,12 @@ SELECT ga.parent_id = g.id OR ga.sub1_id = g.id OR ga.sub2_id = g.id - ) IS NULL THEN 0 - ELSE ( + ) IS NULL THEN 0 ELSE ( SELECT id FROM guild_alliances ga WHERE ga.parent_id = g.id OR ga.sub1_id = g.id OR ga.sub2_id = g.id - ) - END alliance_id, + ) END alliance_id, icon, ( SELECT count(1) FROM guild_characters gc WHERE gc.guild_id = g.id diff --git a/Erupe/server/channelserver/handlers_guild_member.go b/Erupe/server/channelserver/handlers_guild_member.go index db9623b4a..2077f7fb4 100644 --- a/Erupe/server/channelserver/handlers_guild_member.go +++ b/Erupe/server/channelserver/handlers_guild_member.go @@ -12,6 +12,7 @@ type GuildMember struct { GuildID uint32 `db:"guild_id"` CharID uint32 `db:"character_id"` JoinedAt *time.Time `db:"joined_at"` + Souls uint32 `db:"souls"` Name string `db:"name"` IsApplicant bool `db:"is_applicant"` OrderIndex uint8 `db:"order_index"` @@ -49,30 +50,32 @@ func (gm *GuildMember) IsRecruiter() bool { } const guildMembersSelectSQL = ` -SELECT g.id as guild_id, - joined_at, - c.name, - character.character_id, - coalesce(gc.order_index, 0) as order_index, - c.last_login, - coalesce(gc.avoid_leadership, false) as avoid_leadership, - c.hrp, - c.gr, - c.weapon_id, - c.weapon_type, - character.is_applicant, - CASE WHEN g.leader_id = c.id THEN 1 ELSE 0 END as is_leader -FROM ( - SELECT character_id, true as is_applicant, guild_id - FROM guild_applications ga - WHERE ga.application_type = 'applied' - UNION - SELECT character_id, false as is_applicant, guild_id - FROM guild_characters gc - ) character - JOIN characters c on character.character_id = c.id - LEFT JOIN guild_characters gc ON gc.character_id = character.character_id - JOIN guilds g ON g.id = character.guild_id +SELECT + g.id as guild_id, + joined_at, + souls, + c.name, + character.character_id, + coalesce(gc.order_index, 0) as order_index, + c.last_login, + coalesce(gc.avoid_leadership, false) as avoid_leadership, + c.hrp, + c.gr, + c.weapon_id, + c.weapon_type, + character.is_applicant, + CASE WHEN g.leader_id = c.id THEN 1 ELSE 0 END as is_leader + FROM ( + SELECT character_id, true as is_applicant, guild_id + FROM guild_applications ga + WHERE ga.application_type = 'applied' + UNION + SELECT character_id, false as is_applicant, guild_id + FROM guild_characters gc + ) character + JOIN characters c on character.character_id = c.id + LEFT JOIN guild_characters gc ON gc.character_id = character.character_id + JOIN guilds g ON g.id = character.guild_id ` func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMember, error) { From 3638d8aabf92a24e82e87bfe255bead43ed9cdc3 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 27 Jul 2022 21:07:15 +1000 Subject: [PATCH 02/86] further decoding and guild fix --- Erupe/server/channelserver/handlers_festa.go | 23 +++++++++++++++++++- Erupe/server/channelserver/handlers_guild.go | 4 ++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Erupe/server/channelserver/handlers_festa.go b/Erupe/server/channelserver/handlers_festa.go index 3c39f9999..8db270a4f 100644 --- a/Erupe/server/channelserver/handlers_festa.go +++ b/Erupe/server/channelserver/handlers_festa.go @@ -198,7 +198,28 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { bf.WriteBool(false) } - d, _ := hex.DecodeString("0001D4C001F4000411B6648100010001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100020001152A81F589A497A793C196B18B528E6D926381F52A000C952CE10003000109E54BE54E89B38F970029FDCE04000400001381818D84836C8352819993A294B091D1818100000811B6648100010001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100020001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100030001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100040001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100050001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100060001152A81F589A497A793C196B18B528E6D926381F52A000C952CE10007000109E54BE54E89B38F9700000000000008000001000000000100001388000007D0000003E800000064012C00C8009600640032") + bf.WriteUint16(0x0001) + bf.WriteUint32(0xD4C001F4) + + categoryWinners := uint16(0) // NYI + bf.WriteUint16(categoryWinners) + for i := uint16(0); i < categoryWinners; i++ { + bf.WriteUint32(0) // Guild ID + bf.WriteUint16(i + 1) // Category ID + bf.WriteUint16(0) // Festa Team + ps.Uint8(bf, "", true) // Guild Name + } + + dailyWinners := uint16(0) // NYI + bf.WriteUint16(dailyWinners) + for i := uint16(0); i < dailyWinners; i++ { + bf.WriteUint32(0) // Guild ID + bf.WriteUint16(i + 1) // Category ID + bf.WriteUint16(0) // Festa Team + ps.Uint8(bf, "", true) // Guild Name + } + + d, _ := hex.DecodeString("000000000000000100001388000007D0000003E800000064012C00C8009600640032") bf.WriteBytes(d) ps.Uint16(bf, "", false) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) diff --git a/Erupe/server/channelserver/handlers_guild.go b/Erupe/server/channelserver/handlers_guild.go index 2277e7561..8ea45e891 100644 --- a/Erupe/server/channelserver/handlers_guild.go +++ b/Erupe/server/channelserver/handlers_guild.go @@ -163,8 +163,8 @@ SELECT func (guild *Guild) Save(s *Session) error { _, err := s.server.db.Exec(` - UPDATE guilds SET main_motto=$2, sub_motto=$3, comment=$4, pugi_name_1=$5, pugi_name_2=$6, pugi_name_3=$7, festival_colour=$8, icon=$9 WHERE id=$1 - `, guild.ID, guild.MainMotto, guild.SubMotto, guild.Comment, guild.PugiName1, guild.PugiName2, guild.PugiName3, guild.FestivalColour, guild.Icon) + UPDATE guilds SET main_motto=$2, sub_motto=$3, comment=$4, pugi_name_1=$5, pugi_name_2=$6, pugi_name_3=$7, icon=$8 WHERE id=$1 + `, guild.ID, guild.MainMotto, guild.SubMotto, guild.Comment, guild.PugiName1, guild.PugiName2, guild.PugiName3, guild.Icon) if err != nil { s.logger.Error("failed to update guild data", zap.Error(err), zap.Uint32("guildID", guild.ID)) From 69eb7e1cba3955eb2fdde6e1cf77765d43a4adf1 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 28 Jul 2022 01:51:17 +1000 Subject: [PATCH 03/86] implement canned festa prizes --- Erupe/festa.sql | 147 +++++++++++++++++++ Erupe/server/channelserver/handlers_festa.go | 68 +++++++-- 2 files changed, 202 insertions(+), 13 deletions(-) diff --git a/Erupe/festa.sql b/Erupe/festa.sql index da56e8c89..ce7d03594 100644 --- a/Erupe/festa.sql +++ b/Erupe/festa.sql @@ -33,6 +33,153 @@ CREATE TABLE IF NOT EXISTS public.festa_trials reward int NOT NULL ); +CREATE TYPE prize_type AS ENUM ('personal', 'guild'); + +CREATE TABLE IF NOT EXISTS public.festa_prizes +( + id serial NOT NULL PRIMARY KEY, + type prize_type NOT NULL, + tier int NOT NULL, + souls_req int NOT NULL, + item_id int NOT NULL, + num_item int NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.festa_prizes_accepted +( + prize_id int NOT NULL, + character_id int NOT NULL +); + +-- Ripped prizes +INSERT INTO public.festa_prizes + (type, tier, souls_req, item_id, num_item) +VALUES + ('personal', 1, 1, 9647, 7), + ('personal', 2, 1, 9647, 7), + ('personal', 3, 1, 9647, 7), + ('personal', 1, 200, 11284, 4), + ('personal', 2, 200, 11284, 4), + ('personal', 3, 200, 11284, 4), + ('personal', 1, 400, 11381, 3), + ('personal', 2, 400, 11381, 3), + ('personal', 3, 400, 11381, 3), + ('personal', 1, 600, 11284, 8), + ('personal', 2, 600, 11284, 8), + ('personal', 3, 600, 11284, 8), + ('personal', 1, 800, 11384, 3), + ('personal', 2, 800, 11384, 3), + ('personal', 3, 800, 11384, 3), + ('personal', 1, 1000, 11284, 12), + ('personal', 2, 1000, 11284, 12), + ('personal', 3, 1000, 11284, 12), + ('personal', 1, 1200, 11381, 5), + ('personal', 2, 1200, 11381, 5), + ('personal', 3, 1200, 11381, 5), + ('personal', 1, 1400, 11284, 16), + ('personal', 2, 1400, 11284, 16), + ('personal', 3, 1400, 11284, 16), + ('personal', 1, 1700, 11384, 5), + ('personal', 2, 1700, 11384, 5), + ('personal', 3, 1700, 11384, 5), + ('personal', 1, 2000, 11284, 16), + ('personal', 2, 2000, 11284, 16), + ('personal', 3, 2000, 11284, 16), + ('personal', 1, 2500, 11382, 4), + ('personal', 2, 2500, 11382, 4), + ('personal', 3, 2500, 11382, 4), + ('personal', 1, 3000, 11284, 24), + ('personal', 2, 3000, 11284, 24), + ('personal', 3, 3000, 11284, 24), + ('personal', 1, 4000, 11385, 4), + ('personal', 2, 4000, 11385, 4), + ('personal', 3, 4000, 11385, 4), + ('personal', 1, 5000, 11381, 11), + ('personal', 2, 5000, 11381, 11), + ('personal', 3, 5000, 11381, 11), + ('personal', 1, 6000, 5177, 5), + ('personal', 2, 6000, 5177, 5), + ('personal', 3, 6000, 5177, 5), + ('personal', 1, 7000, 11384, 11), + ('personal', 2, 7000, 11384, 11), + ('personal', 3, 7000, 11384, 11), + ('personal', 1, 10000, 11382, 8), + ('personal', 2, 10000, 11382, 8), + ('personal', 3, 10000, 11382, 8), + ('personal', 1, 15000, 11385, 4), + ('personal', 2, 15000, 11385, 4), + ('personal', 3, 15000, 11385, 4), + ('personal', 1, 20000, 11381, 13), + ('personal', 2, 20000, 11381, 13), + ('personal', 3, 20000, 11381, 13), + ('personal', 1, 25000, 11385, 4), + ('personal', 2, 25000, 11385, 4), + ('personal', 3, 25000, 11385, 4), + ('personal', 1, 30000, 11383, 1), + ('personal', 2, 30000, 11383, 1), + ('personal', 3, 30000, 11383, 1); + +INSERT INTO public.festa_prizes +(type, tier, souls_req, item_id, num_item) +VALUES + ('guild', 1, 100, 7468, 5), + ('guild', 2, 100, 7468, 5), + ('guild', 3, 100, 7465, 5), + ('guild', 1, 300, 7469, 5), + ('guild', 2, 300, 7469, 5), + ('guild', 3, 300, 7466, 5), + ('guild', 1, 700, 7470, 5), + ('guild', 2, 700, 7470, 5), + ('guild', 3, 700, 7467, 5), + ('guild', 1, 1500, 13405, 14), + ('guild', 1, 1500, 1520, 3), + ('guild', 2, 1500, 13405, 14), + ('guild', 2, 1500, 1520, 3), + ('guild', 3, 1500, 7011, 3), + ('guild', 3, 1500, 13405, 14), + ('guild', 1, 3000, 10201, 10), + ('guild', 2, 3000, 10201, 10), + ('guild', 3, 3000, 10201, 10), + ('guild', 1, 6000, 13895, 14), + ('guild', 1, 6000, 1520, 6), + ('guild', 2, 6000, 13895, 14), + ('guild', 2, 6000, 1520, 6), + ('guild', 3, 6000, 13895, 14), + ('guild', 3, 6000, 7011, 4), + ('guild', 1, 12000, 13406, 14), + ('guild', 1, 12000, 1520, 9), + ('guild', 2, 12000, 13406, 14), + ('guild', 2, 12000, 1520, 9), + ('guild', 3, 12000, 13406, 14), + ('guild', 3, 12000, 7011, 5), + ('guild', 1, 25000, 10207, 10), + ('guild', 2, 25000, 10207, 10), + ('guild', 3, 25000, 10207, 10), + ('guild', 1, 50000, 1520, 12), + ('guild', 1, 50000, 13896, 14), + ('guild', 2, 50000, 1520, 12), + ('guild', 2, 50000, 13896, 14), + ('guild', 3, 50000, 7011, 6), + ('guild', 3, 50000, 13896, 14), + ('guild', 1, 100000, 10201, 10), + ('guild', 2, 100000, 10201, 10), + ('guild', 3, 100000, 10201, 10), + ('guild', 1, 200000, 13406, 16), + ('guild', 2, 200000, 13406, 16), + ('guild', 3, 200000, 13406, 16), + ('guild', 1, 300000, 13896, 16), + ('guild', 2, 300000, 13896, 16), + ('guild', 3, 300000, 13896, 16), + ('guild', 1, 400000, 10207, 10), + ('guild', 2, 400000, 10207, 10), + ('guild', 3, 400000, 10207, 10), + ('guild', 1, 500000, 13407, 6), + ('guild', 1, 500000, 13897, 6), + ('guild', 2, 500000, 13407, 6), + ('guild', 2, 500000, 13897, 6), + ('guild', 3, 500000, 13407, 6), + ('guild', 3, 500000, 13897, 6); + -- Ripped trials INSERT INTO public.festa_trials (objective, goal_id, times_req, locale_req, reward) diff --git a/Erupe/server/channelserver/handlers_festa.go b/Erupe/server/channelserver/handlers_festa.go index 8db270a4f..d05e97239 100644 --- a/Erupe/server/channelserver/handlers_festa.go +++ b/Erupe/server/channelserver/handlers_festa.go @@ -71,6 +71,7 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { func cleanupFesta(s *Session) { s.server.db.Exec("DELETE FROM events WHERE event_type='festa'") s.server.db.Exec("DELETE FROM festa_registrations") + s.server.db.Exec("DELETE FROM festa_prizes_accepted") s.server.db.Exec("UPDATE guild_characters SET souls=0") } @@ -320,32 +321,73 @@ func handleMsgMhfAcquireFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireFestaPersonalPrize) - // Set prize as claimed + s.server.db.Exec("INSERT INTO public.festa_prizes_accepted VALUES ($1, $2)", pkt.PrizeID, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfAcquireFestaIntermediatePrize(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireFestaIntermediatePrize) - // Set prize as claimed + s.server.db.Exec("INSERT INTO public.festa_prizes_accepted VALUES ($1, $2)", pkt.PrizeID, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } -// uint32 numPrizes -// struct festaPrize -// uint32 prizeID -// uint32 prizeTier (1/2/3, 3 = GR) -// uint32 soulsReq -// uint32 unk (00 00 00 07) -// uint32 itemID -// uint32 numItem -// bool claimed +type Prize struct { + ID uint32 `db:"id"` + Tier uint32 `db:"tier"` + SoulsReq uint32 `db:"souls_req"` + ItemID uint32 `db:"item_id"` + NumItem uint32 `db:"num_item"` + Claimed int `db:"claimed"` +} func handleMsgMhfEnumerateFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateFestaPersonalPrize) - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + rows, _ := s.server.db.Queryx("SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = 4) AS claimed FROM festa_prizes fp WHERE type='personal'") + var count uint32 + prizeData := byteframe.NewByteFrame() + for rows.Next() { + prize := &Prize{} + err := rows.StructScan(&prize) + if err != nil { + continue + } + count++ + prizeData.WriteUint32(prize.ID) + prizeData.WriteUint32(prize.Tier) + prizeData.WriteUint32(prize.SoulsReq) + prizeData.WriteUint32(7) // Unk + prizeData.WriteUint32(prize.ItemID) + prizeData.WriteUint32(prize.NumItem) + prizeData.WriteBool(prize.Claimed > 0) + } + bf := byteframe.NewByteFrame() + bf.WriteUint32(count) + bf.WriteBytes(prizeData.Data()) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfEnumerateFestaIntermediatePrize(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateFestaIntermediatePrize) - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + rows, _ := s.server.db.Queryx("SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = 4) AS claimed FROM festa_prizes fp WHERE type='guild'") + var count uint32 + prizeData := byteframe.NewByteFrame() + for rows.Next() { + prize := &Prize{} + err := rows.StructScan(&prize) + if err != nil { + continue + } + count++ + prizeData.WriteUint32(prize.ID) + prizeData.WriteUint32(prize.Tier) + prizeData.WriteUint32(prize.SoulsReq) + prizeData.WriteUint32(7) // Unk + prizeData.WriteUint32(prize.ItemID) + prizeData.WriteUint32(prize.NumItem) + prizeData.WriteBool(prize.Claimed > 0) + } + bf := byteframe.NewByteFrame() + bf.WriteUint32(count) + bf.WriteBytes(prizeData.Data()) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } From 732199358ad07831aa4d3dcd7decfa2022d5d5a6 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 28 Jul 2022 02:36:50 +1000 Subject: [PATCH 04/86] handle acquiring festa finale --- Erupe/server/channelserver/handlers_festa.go | 57 ++++++++++---------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/Erupe/server/channelserver/handlers_festa.go b/Erupe/server/channelserver/handlers_festa.go index d05e97239..76b6dedad 100644 --- a/Erupe/server/channelserver/handlers_festa.go +++ b/Erupe/server/channelserver/handlers_festa.go @@ -79,25 +79,26 @@ func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 { timestamps := make([]uint32, 5) midnight := Time_Current_Midnight() if debug && start <= 3 { + midnight := uint32(midnight.Unix()) switch start { case 1: - timestamps[0] = uint32(midnight.Unix()) - timestamps[1] = uint32(midnight.Add(24 * 7 * time.Hour).Unix()) - timestamps[2] = uint32(midnight.Add(24 * 14 * time.Hour).Unix()) - timestamps[3] = uint32(midnight.Add(24*14*time.Hour + 150*time.Minute).Unix()) - timestamps[4] = uint32(midnight.Add(24*28*time.Hour + 11*time.Hour).Unix()) + timestamps[0] = midnight + timestamps[1] = timestamps[0] + 604800 + timestamps[2] = timestamps[1] + 604800 + timestamps[3] = timestamps[2] + 9000 + timestamps[4] = timestamps[3] + 1240200 case 2: - timestamps[0] = uint32(midnight.Add(-24 * 7 * time.Hour).Unix()) - timestamps[1] = uint32(midnight.Unix()) - timestamps[2] = uint32(midnight.Add(24 * 7 * time.Hour).Unix()) - timestamps[3] = uint32(midnight.Add(24*7*time.Hour + 150*time.Minute).Unix()) - timestamps[4] = uint32(midnight.Add(24 * 21 * time.Hour).Add(11 * time.Hour).Unix()) + timestamps[0] = midnight - 604800 + timestamps[1] = midnight + timestamps[2] = timestamps[1] + 604800 + timestamps[3] = timestamps[2] + 9000 + timestamps[4] = timestamps[3] + 1240200 case 3: - timestamps[0] = uint32(midnight.Add(-24 * 14 * time.Hour).Unix()) - timestamps[1] = uint32(midnight.Add(-24*7*time.Hour + 11*time.Hour).Unix()) - timestamps[2] = uint32(midnight.Unix()) - timestamps[3] = uint32(midnight.Add(150 * time.Minute).Unix()) - timestamps[4] = uint32(midnight.Add(24*14*time.Hour + 11*time.Hour).Unix()) + timestamps[0] = midnight - 1209600 + timestamps[1] = midnight - 604800 + timestamps[2] = midnight + timestamps[3] = timestamps[2] + 9000 + timestamps[4] = timestamps[3] + 1240200 } return timestamps } @@ -189,15 +190,9 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(count) bf.WriteBytes(trialData.Data()) - unk := uint16(0) // static rewards? - bf.WriteUint16(unk) - for i := uint16(0); i < unk; i++ { - bf.WriteUint32(0) - bf.WriteUint16(0) - bf.WriteUint16(0) - bf.WriteUint32(0) - bf.WriteBool(false) - } + // Static bonus rewards + rewards, _ := hex.DecodeString("001901000007015E05F000000000000100000703E81B6300000000010100000C03E8000000000000000100000D0000000000000000000100000100000000000000000002000007015E05F000000000000200000703E81B6300000000010200000C03E8000000000000000200000D0000000000000000000200000400000000000000000003000007015E05F000000000000300000703E81B6300000000010300000C03E8000000000000000300000D0000000000000000000300000100000000000000000004000007015E05F000000000000400000703E81B6300000000010400000C03E8000000000000000400000D0000000000000000000400000400000000000000000005000007015E05F000000000000500000703E81B6300000000010500000C03E8000000000000000500000D00000000000000000005000001000000000000000000") + bf.WriteBytes(rewards) bf.WriteUint16(0x0001) bf.WriteUint32(0xD4C001F4) @@ -238,7 +233,16 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { s.server.db.QueryRow("SELECT souls FROM guild_characters WHERE character_id=$1", s.charID).Scan(&souls) bf := byteframe.NewByteFrame() bf.WriteUint32(souls) - bf.WriteUint32(0) // unk + + // This definitely isn't right, but it does stop you from claiming the festa infinitely. + var claimed uint32 + s.server.db.QueryRow("SELECT count(*) FROM festa_prizes_accepted fpa WHERE fpa.prize_id=0 AND fpa.character_id=$1", s.charID).Scan(&claimed) + if claimed > 0 { + bf.WriteUint32(0) // unk + } else { + bf.WriteUint32(0x01000000) // unk + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -314,8 +318,7 @@ func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireFesta) - // Mark festa as claimed - // Update guild table? + s.server.db.Exec("INSERT INTO public.festa_prizes_accepted VALUES (0, $1)", s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } From 1e62e8bf96f65e954c9f58260ece71c495e8a6de Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 28 Jul 2022 15:34:50 +1000 Subject: [PATCH 05/86] achievements concept --- Erupe/achievements.sql | 41 ++++++ .../channelserver/handlers_achievement.go | 135 ++++++++---------- 2 files changed, 104 insertions(+), 72 deletions(-) create mode 100644 Erupe/achievements.sql diff --git a/Erupe/achievements.sql b/Erupe/achievements.sql new file mode 100644 index 000000000..5c24f9e89 --- /dev/null +++ b/Erupe/achievements.sql @@ -0,0 +1,41 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS public.achievements +( + id int NOT NULL, + ach0 int DEFAULT 0, + ach1 int DEFAULT 0, + ach2 int DEFAULT 0, + ach3 int DEFAULT 0, + ach4 int DEFAULT 0, + ach5 int DEFAULT 0, + ach6 int DEFAULT 0, + ach7 int DEFAULT 0, + ach8 int DEFAULT 0, + ach9 int DEFAULT 0, + ach10 int DEFAULT 0, + ach11 int DEFAULT 0, + ach12 int DEFAULT 0, + ach13 int DEFAULT 0, + ach14 int DEFAULT 0, + ach15 int DEFAULT 0, + ach16 int DEFAULT 0, + ach17 int DEFAULT 0, + ach18 int DEFAULT 0, + ach19 int DEFAULT 0, + ach20 int DEFAULT 0, + ach21 int DEFAULT 0, + ach22 int DEFAULT 0, + ach23 int DEFAULT 0, + ach24 int DEFAULT 0, + ach25 int DEFAULT 0, + ach26 int DEFAULT 0, + ach27 int DEFAULT 0, + ach28 int DEFAULT 0, + ach29 int DEFAULT 0, + ach30 int DEFAULT 0, + ach31 int DEFAULT 0, + ach32 int DEFAULT 0 +); + +END; \ No newline at end of file diff --git a/Erupe/server/channelserver/handlers_achievement.go b/Erupe/server/channelserver/handlers_achievement.go index 7887ab71d..b9197f6ea 100644 --- a/Erupe/server/channelserver/handlers_achievement.go +++ b/Erupe/server/channelserver/handlers_achievement.go @@ -5,83 +5,74 @@ import ( "erupe-ce/network/mhfpacket" ) +var achievementCurves = [][]uint32{ + // 0: HR weapon use, Class use, Tore dailies + {5, 15, 30, 50, 100, 150, 200, 250, 300}, + // 1: Weapon collector, G wep enhances + {1, 3, 5, 15, 30, 50, 75, 100, 150}, + // 2: Festa wins + {1, 2, 3, 4, 5, 6, 7, 8, 9}, + // 3: GR weapon use + {10, 50, 100, 200, 350, 500, 750, 1000, 1500}, + // 4: Armor refinement + {0, 5, 5, 5, 5, 5, 5, 5, 5}, + // 5: Sigil crafts + {0, 50, 50, 50, 50, 50, 50, 50, 50}, +} + +var achievementCurveMap = map[uint8][]uint32{ + 0: achievementCurves[0], 1: achievementCurves[0], 2: achievementCurves[0], 3: achievementCurves[0], + 4: achievementCurves[0], 5: achievementCurves[0], 6: achievementCurves[0], 7: achievementCurves[1], + 8: achievementCurves[2], 9: achievementCurves[0], 10: achievementCurves[0], 11: achievementCurves[0], + 12: achievementCurves[0], 13: achievementCurves[0], 14: achievementCurves[0], 15: achievementCurves[0], + 16: achievementCurves[3], 17: achievementCurves[3], 18: achievementCurves[3], 19: achievementCurves[3], + 20: achievementCurves[3], 21: achievementCurves[3], 22: achievementCurves[3], 23: achievementCurves[3], + 24: achievementCurves[3], 25: achievementCurves[3], 26: achievementCurves[3], 27: achievementCurves[1], + 28: achievementCurves[4], 29: achievementCurves[5], 30: achievementCurves[3], 31: achievementCurves[3], + 32: achievementCurves[3], +} + func handleMsgMhfGetAchievement(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetAchievement) - achievementStruct := []struct { - ID uint8 // Main ID - Unk0 uint8 // always FF - Unk1 uint16 // 0x05 0x00 - Unk2 uint32 // 0x01 0x0A 0x05 0x00 - }{ - {ID: 0, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 1, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 2, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 3, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 4, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 5, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 6, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 7, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 8, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 9, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 10, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 11, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 12, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 13, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 14, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 15, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 16, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 17, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 18, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 19, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 20, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 21, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 22, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 23, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 24, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 25, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 26, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 27, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 28, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 29, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 30, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 31, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 32, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 33, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 34, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 35, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 36, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 37, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 38, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 39, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 40, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 41, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 42, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 43, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 44, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 45, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 46, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 47, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 48, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 49, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 50, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 51, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 52, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 53, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 54, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 55, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 56, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 57, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 58, Unk0: 0xFF, Unk1: 0, Unk2: 0}, - {ID: 59, Unk0: 0xFF, Unk1: 0, Unk2: 0}, + err := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", s.charID) + if err != nil { + s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", s.charID) } + + scores := make([]int, 33) + s.server.db.QueryRow("SELECT * FROM achievements WHERE id=$1", s.charID).Scan(&scores[0], &scores[0], + &scores[1], &scores[2], &scores[3], &scores[4], &scores[5], &scores[6], &scores[7], &scores[8], &scores[9], + &scores[10], &scores[11], &scores[12], &scores[13], &scores[14], &scores[15], &scores[16], &scores[17], + &scores[18], &scores[19], &scores[20], &scores[21], &scores[22], &scores[23], &scores[24], &scores[25], + &scores[26], &scores[27], &scores[28], &scores[29], &scores[30], &scores[31], &scores[32]) + resp := byteframe.NewByteFrame() - resp.WriteUint8(uint8(len(achievementStruct))) // Entry count - for _, entry := range achievementStruct { - resp.WriteUint8(entry.ID) - resp.WriteUint8(entry.Unk0) - resp.WriteUint16(entry.Unk1) - resp.WriteUint32(entry.Unk2) + points := uint32(69) + resp.WriteUint32(points) + resp.WriteUint32(points) + resp.WriteUint32(points) + resp.WriteUint32(points) + resp.WriteBytes([]byte{0x02, 0x00, 0x00}) + + entries := 34 + resp.WriteUint8(uint8(entries)) // Entry count + for i := 0; i < entries; i++ { + resp.WriteUint8(uint8(i)) // achievement id + resp.WriteUint8(uint8(i)) // level + resp.WriteUint16(20) // point value + resp.WriteUint32(100) // required + resp.WriteUint8(0) + if i < 10 { + resp.WriteUint16(0x7FFF) + } else if i < 20 { + resp.WriteUint16(0x3FFF) + } else { + resp.WriteUint16(0x1FFF) + } + //resp.WriteUint16(0x7F7F) // unk + resp.WriteUint8(0) + resp.WriteUint32(100) // progress } doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } From f8ed2ef40dac284a6d557451c7ab8b58c175ea99 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 7 Aug 2022 17:16:54 +1000 Subject: [PATCH 06/86] use sequential seasons --- main.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/main.go b/main.go index c5ac726b3..037a6cbef 100644 --- a/main.go +++ b/main.go @@ -168,9 +168,6 @@ func main() { ci := 0 count := 1 for _, ee := range erupeConfig.Entrance.Entries { - rand.Seed(time.Now().UnixNano()) - // Randomly generate a season for the World - season := rand.Intn(3) + 1 for _, ce := range ee.Channels { sid := (4096 + si*256) + (16 + ci) c := *channelserver.NewServer(&channelserver.Config{ @@ -190,7 +187,7 @@ func main() { if err != nil { preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error())) } else { - channelQuery += fmt.Sprintf("INSERT INTO servers (server_id, season, current_players) VALUES (%d, %d, 0);", sid, season) + channelQuery += fmt.Sprintf("INSERT INTO servers (server_id, season, current_players) VALUES (%d, %d, 0);", sid, si%3) channels = append(channels, &c) logger.Info(fmt.Sprintf("Started channel server %d on port %d", count, ce.Port)) ci++ From d10dcbc63071374c56cbe9b85242844fddb323dd Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 7 Aug 2022 20:16:21 +1000 Subject: [PATCH 07/86] remove unused import --- main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/main.go b/main.go index 037a6cbef..d0b13d649 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "math/rand" "net" "os" "os/signal" From 503e944c2d88686b712c3c59ed811177ff78cc5d Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 8 Aug 2022 04:53:55 +1000 Subject: [PATCH 08/86] enumerate achievement data --- network/mhfpacket/msg_mhf_get_achievement.go | 18 +-- patch-schema/achievements.sql | 2 +- server/channelserver/handlers_achievement.go | 161 +++++++++++++------ 3 files changed, 125 insertions(+), 56 deletions(-) diff --git a/network/mhfpacket/msg_mhf_get_achievement.go b/network/mhfpacket/msg_mhf_get_achievement.go index 4b41ce72b..afa49d0d4 100644 --- a/network/mhfpacket/msg_mhf_get_achievement.go +++ b/network/mhfpacket/msg_mhf_get_achievement.go @@ -1,18 +1,18 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfGetAchievement represents the MSG_MHF_GET_ACHIEVEMENT -type MsgMhfGetAchievement struct{ - AckHandle uint32 - Unk0 uint32 // id? - Unk1 uint32 // char? +type MsgMhfGetAchievement struct { + AckHandle uint32 + CharID uint32 + Unk1 uint32 // char? } // Opcode returns the ID associated with this packet type. @@ -22,8 +22,8 @@ func (m *MsgMhfGetAchievement) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetAchievement) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint32() + m.AckHandle = bf.ReadUint32() + m.CharID = bf.ReadUint32() m.Unk1 = bf.ReadUint32() return nil } diff --git a/patch-schema/achievements.sql b/patch-schema/achievements.sql index 5c24f9e89..333ae5ed6 100644 --- a/patch-schema/achievements.sql +++ b/patch-schema/achievements.sql @@ -2,7 +2,7 @@ BEGIN; CREATE TABLE IF NOT EXISTS public.achievements ( - id int NOT NULL, + id int NOT NULL PRIMARY KEY , ach0 int DEFAULT 0, ach1 int DEFAULT 0, ach2 int DEFAULT 0, diff --git a/server/channelserver/handlers_achievement.go b/server/channelserver/handlers_achievement.go index b9197f6ea..a7628ed8f 100644 --- a/server/channelserver/handlers_achievement.go +++ b/server/channelserver/handlers_achievement.go @@ -3,24 +3,23 @@ package channelserver import ( "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" + "fmt" + "go.uber.org/zap" + "io" ) -var achievementCurves = [][]uint32{ +var achievementCurves = [][]int32{ // 0: HR weapon use, Class use, Tore dailies - {5, 15, 30, 50, 100, 150, 200, 250, 300}, + {5, 15, 30, 50, 100, 150, 200, 300}, // 1: Weapon collector, G wep enhances - {1, 3, 5, 15, 30, 50, 75, 100, 150}, + {1, 3, 5, 15, 30, 50, 75, 100}, // 2: Festa wins - {1, 2, 3, 4, 5, 6, 7, 8, 9}, - // 3: GR weapon use - {10, 50, 100, 200, 350, 500, 750, 1000, 1500}, - // 4: Armor refinement - {0, 5, 5, 5, 5, 5, 5, 5, 5}, - // 5: Sigil crafts - {0, 50, 50, 50, 50, 50, 50, 50, 50}, + {1, 2, 3, 4, 5, 6, 7, 8}, + // 3: GR weapon use, Sigil crafts + {10, 50, 100, 200, 350, 500, 750, 999}, } -var achievementCurveMap = map[uint8][]uint32{ +var achievementCurveMap = map[uint8][]int32{ 0: achievementCurves[0], 1: achievementCurves[0], 2: achievementCurves[0], 3: achievementCurves[0], 4: achievementCurves[0], 5: achievementCurves[0], 6: achievementCurves[0], 7: achievementCurves[1], 8: achievementCurves[2], 9: achievementCurves[0], 10: achievementCurves[0], 11: achievementCurves[0], @@ -28,52 +27,119 @@ var achievementCurveMap = map[uint8][]uint32{ 16: achievementCurves[3], 17: achievementCurves[3], 18: achievementCurves[3], 19: achievementCurves[3], 20: achievementCurves[3], 21: achievementCurves[3], 22: achievementCurves[3], 23: achievementCurves[3], 24: achievementCurves[3], 25: achievementCurves[3], 26: achievementCurves[3], 27: achievementCurves[1], - 28: achievementCurves[4], 29: achievementCurves[5], 30: achievementCurves[3], 31: achievementCurves[3], + 28: achievementCurves[1], 29: achievementCurves[3], 30: achievementCurves[3], 31: achievementCurves[3], 32: achievementCurves[3], } +type Achievement struct { + Level uint8 + Value uint32 + NextValue uint16 + Required uint32 + Updated bool + Progress uint32 + Trophy uint8 +} + +func GetAchData(id uint8, score int32) Achievement { + curve := achievementCurveMap[id] + var ach Achievement + for i, v := range curve { + temp := score - v + if temp < 0 { + ach.Progress = uint32(score) + ach.Required = uint32(curve[i]) + switch ach.Level { + case 0: + ach.NextValue = 5 + case 1, 2, 3: + ach.NextValue = 10 + case 4, 5: + ach.NextValue = 15 + case 6: + ach.NextValue = 15 + ach.Trophy = 0x40 + case 7: + ach.NextValue = 20 + ach.Trophy = 0x60 + } + return ach + } else { + score = temp + ach.Level++ + switch ach.Level { + case 1: + ach.Value += 5 + case 2, 3, 4: + ach.Value += 10 + case 5, 6, 7: + ach.Value += 15 + case 8: + ach.Value += 20 + } + } + } + ach.Required = uint32(curve[7]) + ach.Trophy = 0x7F + ach.Progress = ach.Required + return ach +} + func handleMsgMhfGetAchievement(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetAchievement) - err := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", s.charID) - if err != nil { - s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", s.charID) + row := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", pkt.CharID) + if row != nil { + s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", pkt.CharID) } - scores := make([]int, 33) - s.server.db.QueryRow("SELECT * FROM achievements WHERE id=$1", s.charID).Scan(&scores[0], &scores[0], - &scores[1], &scores[2], &scores[3], &scores[4], &scores[5], &scores[6], &scores[7], &scores[8], &scores[9], - &scores[10], &scores[11], &scores[12], &scores[13], &scores[14], &scores[15], &scores[16], &scores[17], - &scores[18], &scores[19], &scores[20], &scores[21], &scores[22], &scores[23], &scores[24], &scores[25], - &scores[26], &scores[27], &scores[28], &scores[29], &scores[30], &scores[31], &scores[32]) + var scores [33]int32 + row = s.server.db.QueryRow("SELECT * FROM achievements WHERE id=$1", pkt.CharID) + if row != nil { + err := row.Scan(&scores[0], &scores[0], + &scores[1], &scores[2], &scores[3], &scores[4], &scores[5], &scores[6], &scores[7], &scores[8], &scores[9], + &scores[10], &scores[11], &scores[12], &scores[13], &scores[14], &scores[15], &scores[16], &scores[17], + &scores[18], &scores[19], &scores[20], &scores[21], &scores[22], &scores[23], &scores[24], &scores[25], + &scores[26], &scores[27], &scores[28], &scores[29], &scores[30], &scores[31], &scores[32]) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 20)) + s.logger.Error("ERR@", zap.Error(err)) + return + } + } resp := byteframe.NewByteFrame() - points := uint32(69) - resp.WriteUint32(points) - resp.WriteUint32(points) - resp.WriteUint32(points) - resp.WriteUint32(points) - resp.WriteBytes([]byte{0x02, 0x00, 0x00}) + var points uint32 + resp.WriteBytes(make([]byte, 16)) + resp.WriteBytes([]byte{0x02, 0x00, 0x00}) // Unk - entries := 34 - resp.WriteUint8(uint8(entries)) // Entry count - for i := 0; i < entries; i++ { - resp.WriteUint8(uint8(i)) // achievement id - resp.WriteUint8(uint8(i)) // level - resp.WriteUint16(20) // point value - resp.WriteUint32(100) // required - resp.WriteUint8(0) - if i < 10 { - resp.WriteUint16(0x7FFF) - } else if i < 20 { - resp.WriteUint16(0x3FFF) - } else { - resp.WriteUint16(0x1FFF) - } - //resp.WriteUint16(0x7F7F) // unk - resp.WriteUint8(0) - resp.WriteUint32(100) // progress + var id uint8 + entries := uint8(33) + resp.WriteUint8(entries) // Entry count + for id = 0; id < entries; id++ { + achData := GetAchData(id, scores[id]) + points += achData.Value + resp.WriteUint8(id) + resp.WriteUint8(achData.Level) + resp.WriteUint16(achData.NextValue) + resp.WriteUint32(achData.Required) + resp.WriteBool(false) // level increased notification + resp.WriteUint8(achData.Trophy) + /* Trophy bitfield + 0000 0000 + abcd efgh + B - Bronze (0x40) + B-C - Silver (0x60) + B-H - Gold (0x7F) + */ + resp.WriteUint16(0) // Unk + resp.WriteUint32(achData.Progress) } + resp.Seek(0, io.SeekStart) + resp.WriteUint32(points) + resp.WriteUint32(points) + resp.WriteUint32(points) + resp.WriteUint32(points) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } @@ -84,7 +150,10 @@ func handleMsgMhfSetCaAchievementHist(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfResetAchievement(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfAddAchievement(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfAddAchievement(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfAddAchievement) + s.server.db.Exec(fmt.Sprintf("UPDATE achievements SET ach%d=ach%d+1 WHERE id=$1", pkt.AchievementID, pkt.AchievementID), s.charID) +} func handleMsgMhfPaymentAchievement(s *Session, p mhfpacket.MHFPacket) {} From 781827c6bfe79605581d965d16bc232e1f873f42 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 8 Aug 2022 05:01:18 +1000 Subject: [PATCH 09/86] update curve map --- server/channelserver/handlers_achievement.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_achievement.go b/server/channelserver/handlers_achievement.go index a7628ed8f..ab1f94097 100644 --- a/server/channelserver/handlers_achievement.go +++ b/server/channelserver/handlers_achievement.go @@ -12,7 +12,7 @@ var achievementCurves = [][]int32{ // 0: HR weapon use, Class use, Tore dailies {5, 15, 30, 50, 100, 150, 200, 300}, // 1: Weapon collector, G wep enhances - {1, 3, 5, 15, 30, 50, 75, 100}, + {1, 5, 10, 15, 30, 50, 75, 100}, // 2: Festa wins {1, 2, 3, 4, 5, 6, 7, 8}, // 3: GR weapon use, Sigil crafts From c0bc7c24396c80a66106726a1c285d4767f4c796 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 8 Aug 2022 05:04:06 +1000 Subject: [PATCH 10/86] add comments --- server/channelserver/handlers_achievement.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers_achievement.go b/server/channelserver/handlers_achievement.go index ab1f94097..b552101f8 100644 --- a/server/channelserver/handlers_achievement.go +++ b/server/channelserver/handlers_achievement.go @@ -123,7 +123,7 @@ func handleMsgMhfGetAchievement(s *Session, p mhfpacket.MHFPacket) { resp.WriteUint8(achData.Level) resp.WriteUint16(achData.NextValue) resp.WriteUint32(achData.Required) - resp.WriteBool(false) // level increased notification + resp.WriteBool(false) // TODO: Notify on rank increase since last checked, see MhfDisplayedAchievement resp.WriteUint8(achData.Trophy) /* Trophy bitfield 0000 0000 @@ -157,7 +157,9 @@ func handleMsgMhfAddAchievement(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPaymentAchievement(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfDisplayedAchievement(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfDisplayedAchievement(s *Session, p mhfpacket.MHFPacket) { + // This is how you would figure out if the rank-up notification needs to occur +} func handleMsgMhfGetCaAchievementHist(s *Session, p mhfpacket.MHFPacket) {} From 49682524917415dc3f0ca960d5f095be7c5f8737 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 8 Aug 2022 18:21:54 +1000 Subject: [PATCH 11/86] initial mercenaries build --- patch-schema/mercenary.sql | 5 ++ server/channelserver/handlers_mercenary.go | 55 ++++++++++------------ 2 files changed, 30 insertions(+), 30 deletions(-) create mode 100644 patch-schema/mercenary.sql diff --git a/patch-schema/mercenary.sql b/patch-schema/mercenary.sql new file mode 100644 index 000000000..f67e4fe4e --- /dev/null +++ b/patch-schema/mercenary.sql @@ -0,0 +1,5 @@ +BEGIN; + +CREATE SEQUENCE IF NOT EXISTS public.rasta_id_seq; + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index 606579fe5..bbcf5dceb 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -9,7 +9,6 @@ import ( "go.uber.org/zap" "io" "io/ioutil" - "math/rand" "os" "path/filepath" ) @@ -132,8 +131,11 @@ func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() - bf.WriteUint32(0x00) // Unk - bf.WriteUint32(rand.Uint32()) // Partner ID? + var nextID uint32 + s.server.db.QueryRow("SELECT nextval('rasta_id_seq')").Scan(&nextID) + + bf.WriteUint32(nextID) // New MercID + bf.WriteUint32(0xDEADBEEF) // Unk doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) } @@ -141,24 +143,16 @@ func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveMercenary) bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) - GCPValue := bf.ReadUint32() + GCP := bf.ReadUint32() _ = bf.ReadUint32() // unk MercDataSize := bf.ReadUint32() MercData := bf.ReadBytes(uint(MercDataSize)) _ = bf.ReadUint32() // unk if MercDataSize > 0 { - // the save packet has an extra null byte after its size - _, err := s.server.db.Exec("UPDATE characters SET savemercenary=$1 WHERE id=$2", MercData[:MercDataSize], s.charID) - if err != nil { - s.logger.Fatal("Failed to update savemercenary and gcp in db", zap.Error(err)) - } - } - // gcp value is always present regardless - _, err := s.server.db.Exec("UPDATE characters SET gcp=$1 WHERE id=$2", GCPValue, s.charID) - if err != nil { - s.logger.Fatal("Failed to update savemercenary and gcp in db", zap.Error(err)) + s.server.db.Exec("UPDATE characters SET savemercenary=$1 WHERE id=$2", MercData[:MercDataSize], s.charID) } + s.server.db.Exec("UPDATE characters SET gcp=$1 WHERE id=$2", GCP, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } @@ -166,31 +160,32 @@ func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfReadMercenaryW) var data []byte var gcp uint32 - // still has issues - err := s.server.db.QueryRow("SELECT savemercenary FROM characters WHERE id = $1", s.charID).Scan(&data) - if err != nil { - s.logger.Fatal("Failed to get savemercenary data from db", zap.Error(err)) - } - - err = s.server.db.QueryRow("SELECT COALESCE(gcp, 0) FROM characters WHERE id = $1", s.charID).Scan(&gcp) - if err != nil { - panic(err) - } - if len(data) == 0 { - data = []byte{0x00} - } + s.server.db.QueryRow("SELECT savemercenary FROM characters WHERE id = $1", s.charID).Scan(&data) + s.server.db.QueryRow("SELECT COALESCE(gcp, 0) FROM characters WHERE id = $1", s.charID).Scan(&gcp) resp := byteframe.NewByteFrame() - resp.WriteBytes(data) resp.WriteUint16(0) + if len(data) == 0 { + resp.WriteBool(false) + } else { + resp.WriteBool(true) + resp.WriteBytes(data) + } resp.WriteUint32(gcp) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } func handleMsgMhfReadMercenaryM(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfReadMercenaryM) - // accessing actual rasta data of someone else still unsure of the formatting of this - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + var data []byte + s.server.db.QueryRow("SELECT savemercenary FROM characters WHERE id = $1", pkt.CharID).Scan(&data) + resp := byteframe.NewByteFrame() + if len(data) == 0 { + resp.WriteBool(false) + } else { + resp.WriteBytes(data[4:]) + } + doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } func handleMsgMhfContractMercenary(s *Session, p mhfpacket.MHFPacket) {} From 265dc258005d2a9fe06ee1692b0b082f93f23817 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 8 Aug 2022 21:02:07 +1000 Subject: [PATCH 12/86] fix merc saving/loading --- network/mhfpacket/msg_mhf_read_mercenary_w.go | 12 ++++----- network/mhfpacket/msg_mhf_save_mercenary.go | 25 +++++++++++-------- server/channelserver/handlers_mercenary.go | 24 ++++++++---------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/network/mhfpacket/msg_mhf_read_mercenary_w.go b/network/mhfpacket/msg_mhf_read_mercenary_w.go index 7cb0117e7..c80afee14 100644 --- a/network/mhfpacket/msg_mhf_read_mercenary_w.go +++ b/network/mhfpacket/msg_mhf_read_mercenary_w.go @@ -1,17 +1,17 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfReadMercenaryW represents the MSG_MHF_READ_MERCENARY_W type MsgMhfReadMercenaryW struct { AckHandle uint32 - Unk0 uint8 + Unk0 bool Unk1 uint8 Unk2 uint16 // Hardcoded 0 in the binary } @@ -24,7 +24,7 @@ func (m *MsgMhfReadMercenaryW) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfReadMercenaryW) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint8() + m.Unk0 = bf.ReadBool() m.Unk1 = bf.ReadUint8() m.Unk2 = bf.ReadUint16() return nil diff --git a/network/mhfpacket/msg_mhf_save_mercenary.go b/network/mhfpacket/msg_mhf_save_mercenary.go index a52c973ab..3aa2b0311 100644 --- a/network/mhfpacket/msg_mhf_save_mercenary.go +++ b/network/mhfpacket/msg_mhf_save_mercenary.go @@ -1,18 +1,20 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfSaveMercenary represents the MSG_MHF_SAVE_MERCENARY -type MsgMhfSaveMercenary struct{ - AckHandle uint32 - DataSize uint32 - RawDataPayload []byte +type MsgMhfSaveMercenary struct { + AckHandle uint32 + GCP uint32 + Unk0 uint32 + MercData []byte + Unk1 uint32 } // Opcode returns the ID associated with this packet type. @@ -23,8 +25,11 @@ func (m *MsgMhfSaveMercenary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfSaveMercenary) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.DataSize = bf.ReadUint32() - m.RawDataPayload = bf.ReadBytes(uint(m.DataSize)) + bf.ReadUint32() // lenData + m.GCP = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.MercData = bf.ReadBytes(uint(bf.ReadUint32())) + m.Unk1 = bf.ReadUint32() return nil } diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index bbcf5dceb..37ce9e628 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -142,34 +142,30 @@ func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveMercenary) - bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) - GCP := bf.ReadUint32() - _ = bf.ReadUint32() // unk - MercDataSize := bf.ReadUint32() - MercData := bf.ReadBytes(uint(MercDataSize)) - _ = bf.ReadUint32() // unk - - if MercDataSize > 0 { - s.server.db.Exec("UPDATE characters SET savemercenary=$1 WHERE id=$2", MercData[:MercDataSize], s.charID) + if len(pkt.MercData) > 0 { + s.server.db.Exec("UPDATE characters SET savemercenary=$1 WHERE id=$2", pkt.MercData, s.charID) } - s.server.db.Exec("UPDATE characters SET gcp=$1 WHERE id=$2", GCP, s.charID) + s.server.db.Exec("UPDATE characters SET gcp=$1 WHERE id=$2", pkt.GCP, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfReadMercenaryW) + if pkt.Unk0 { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2)) + return + } var data []byte var gcp uint32 s.server.db.QueryRow("SELECT savemercenary FROM characters WHERE id = $1", s.charID).Scan(&data) s.server.db.QueryRow("SELECT COALESCE(gcp, 0) FROM characters WHERE id = $1", s.charID).Scan(&gcp) resp := byteframe.NewByteFrame() - resp.WriteUint16(0) if len(data) == 0 { - resp.WriteBool(false) + resp.WriteBytes(make([]byte, 3)) } else { - resp.WriteBool(true) - resp.WriteBytes(data) + resp.WriteBytes(data[1:]) + resp.WriteUint32(0) // Unk } resp.WriteUint32(gcp) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) From 483490bbd572921f845d4dd91a8712c3530b3dd7 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 8 Aug 2022 22:17:14 +1000 Subject: [PATCH 13/86] use simplesucceed for existing semaphores --- server/channelserver/handlers_semaphore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_semaphore.go b/server/channelserver/handlers_semaphore.go index 6659bd2da..95af79314 100644 --- a/server/channelserver/handlers_semaphore.go +++ b/server/channelserver/handlers_semaphore.go @@ -119,7 +119,7 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(newSemaphore.id) doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) } else { - doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } } From 032ee1eac73e4ba2c9c07198df3bf2497016636f Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 8 Aug 2022 22:17:44 +1000 Subject: [PATCH 14/86] add missing castbinary messagetype --- server/channelserver/handlers_cast_binary.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 304369295..fbdaf6987 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -16,6 +16,7 @@ import ( const ( BinaryMessageTypeState = 0 BinaryMessageTypeChat = 1 + BinaryMessageTypeData = 3 BinaryMessageTypeMailNotify = 4 BinaryMessageTypeEmote = 6 ) @@ -164,6 +165,17 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { fmt.Printf("Got chat message: %+v\n", chatMessage) + if strings.HasPrefix(chatMessage.Message, "!test ") { + var x uint32 + n, err := fmt.Sscanf(chatMessage.Message, "!test %d", &x) + if err != nil || n != 1 { + sendServerChatMessage(s, "Invalid command. Usage:\"!test X\"") + } else { + s.test = x + sendServerChatMessage(s, fmt.Sprintf("Set value to %d", x)) + } + } + // Set account rights if strings.HasPrefix(chatMessage.Message, "!rights") { var v uint32 From af48d75522bb1f47839260845683e4c170302775 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 9 Aug 2022 13:45:04 +1000 Subject: [PATCH 15/86] remove debug command --- server/channelserver/handlers_cast_binary.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index fbdaf6987..bf0f2f105 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -165,17 +165,6 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { fmt.Printf("Got chat message: %+v\n", chatMessage) - if strings.HasPrefix(chatMessage.Message, "!test ") { - var x uint32 - n, err := fmt.Sscanf(chatMessage.Message, "!test %d", &x) - if err != nil || n != 1 { - sendServerChatMessage(s, "Invalid command. Usage:\"!test X\"") - } else { - s.test = x - sendServerChatMessage(s, fmt.Sprintf("Set value to %d", x)) - } - } - // Set account rights if strings.HasPrefix(chatMessage.Message, "!rights") { var v uint32 From 37e4b42b2370e06f606fa9d51cc0b4505626a16a Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 10 Aug 2022 01:17:00 +1000 Subject: [PATCH 16/86] fix character gender offset --- server/channelserver/handlers_data.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index 1f18fe262..e98578da7 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -71,8 +71,8 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { s.myseries.toreData = decompressedData[130228:130468] // 0x1FCB4 + 240 s.myseries.gardenData = decompressedData[142424:142492] // 0x22C58 + 68 - isMale := uint8(decompressedData[80]) // 0x50 - if isMale == 1 { + isFemale := decompressedData[81] // 0x51 + if isFemale == 1 { _, err = s.server.db.Exec("UPDATE characters SET is_female=true WHERE id=$1", s.charID) } else { _, err = s.server.db.Exec("UPDATE characters SET is_female=false WHERE id=$1", s.charID) From 9fe1b1d04ae3bdd332c7f60b7442ac4887224e88 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 11 Aug 2022 02:50:59 +1000 Subject: [PATCH 17/86] initial titles implementation --- network/mhfpacket/msg_mhf_acquire_title.go | 21 ++++++--- patch-schema/titles.sql | 11 +++++ server/channelserver/handlers.go | 4 -- server/channelserver/handlers_house.go | 54 +++++++++++++++++----- 4 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 patch-schema/titles.sql diff --git a/network/mhfpacket/msg_mhf_acquire_title.go b/network/mhfpacket/msg_mhf_acquire_title.go index e521734f8..fe3a5ca95 100644 --- a/network/mhfpacket/msg_mhf_acquire_title.go +++ b/network/mhfpacket/msg_mhf_acquire_title.go @@ -1,15 +1,20 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfAcquireTitle represents the MSG_MHF_ACQUIRE_TITLE -type MsgMhfAcquireTitle struct{} +type MsgMhfAcquireTitle struct { + AckHandle uint32 + Unk0 uint16 + Unk1 uint16 + TitleID uint16 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfAcquireTitle) Opcode() network.PacketID { @@ -18,7 +23,11 @@ func (m *MsgMhfAcquireTitle) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfAcquireTitle) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() + m.TitleID = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/patch-schema/titles.sql b/patch-schema/titles.sql new file mode 100644 index 000000000..e4a87dc86 --- /dev/null +++ b/patch-schema/titles.sql @@ -0,0 +1,11 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS public.titles +( + id int NOT NULL, + char_id int NOT NULL, + unlocked_at timestamp without time zone, + updated_at timestamp without time zone +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 1cfe9e3ff..58ea6b401 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -500,8 +500,6 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { func handleMsgCaExchangeItem(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {} - func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfServerCommand(s *Session, p mhfpacket.MHFPacket) {} @@ -556,8 +554,6 @@ func handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetExtraInfo(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) {} - func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateUnionItem) var boxContents []byte diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 92d7c853e..7b28582d7 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -6,6 +6,8 @@ import ( "erupe-ce/common/stringsupport" "erupe-ce/network/mhfpacket" "go.uber.org/zap" + "io" + "time" ) func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) { @@ -291,25 +293,53 @@ func handleMsgMhfSaveDecoMyset(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } +type Title struct { + ID uint16 `db:"id"` + Acquired time.Time `db:"unlocked_at"` + Updated time.Time `db:"updated_at"` +} + func handleMsgMhfEnumerateTitle(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateTitle) + var count uint16 bf := byteframe.NewByteFrame() - if pkt.CharID == 0 { - titleCount := 114 // all titles unlocked - bf.WriteUint16(uint16(titleCount)) // title count - bf.WriteUint16(0) // unk - for i := 0; i < titleCount; i++ { - bf.WriteUint16(uint16(i)) - bf.WriteUint16(0) // unk - bf.WriteUint32(0) // timestamp acquired - bf.WriteUint32(0) // timestamp updated - } - } else { - bf.WriteUint16(0) + bf.WriteUint16(0) + bf.WriteUint16(0) // Unk + rows, err := s.server.db.Queryx("SELECT id, unlocked_at, updated_at FROM titles WHERE char_id=$1", s.charID) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + return } + for rows.Next() { + title := &Title{} + err = rows.StructScan(&title) + if err != nil { + continue + } + count++ + bf.WriteUint16(title.ID) + bf.WriteUint16(0) // Unk + bf.WriteUint32(uint32(title.Acquired.Unix())) + bf.WriteUint32(uint32(title.Updated.Unix())) + } + bf.Seek(0, io.SeekStart) + bf.WriteUint16(count) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfAcquireTitle) + err := s.server.db.QueryRow("SELECT count(*) FROM titles WHERE id=$1 AND char_id=$2", pkt.TitleID, s.charID) + if err != nil { + s.server.db.Exec("INSERT INTO titles VALUES ($1, $2, now(), now())", pkt.TitleID, s.charID) + } else { + s.server.db.Exec("UPDATE titles SET updated_at=now()") + } + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} + +func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {} + func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {} From 2a65977a44ad172c6460c708cf7b79639f21d851 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 11 Aug 2022 02:53:51 +1000 Subject: [PATCH 18/86] prevent panic on invalid string transform --- common/pascalstring/pascalstring.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/common/pascalstring/pascalstring.go b/common/pascalstring/pascalstring.go index 3640db22d..8ad332018 100644 --- a/common/pascalstring/pascalstring.go +++ b/common/pascalstring/pascalstring.go @@ -11,7 +11,8 @@ func Uint8(bf *byteframe.ByteFrame, x string, t bool) { e := japanese.ShiftJIS.NewEncoder() xt, _, err := transform.String(e, x) if err != nil { - panic(err) + bf.WriteUint8(0) + return } x = xt } @@ -24,7 +25,8 @@ func Uint16(bf *byteframe.ByteFrame, x string, t bool) { e := japanese.ShiftJIS.NewEncoder() xt, _, err := transform.String(e, x) if err != nil { - panic(err) + bf.WriteUint16(0) + return } x = xt } @@ -37,7 +39,8 @@ func Uint32(bf *byteframe.ByteFrame, x string, t bool) { e := japanese.ShiftJIS.NewEncoder() xt, _, err := transform.String(e, x) if err != nil { - panic(err) + bf.WriteUint32(0) + return } x = xt } From e89c6f50ae89d950b2d1ca39ee6a7cbcd1d27f71 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 11 Aug 2022 14:58:58 +1000 Subject: [PATCH 19/86] clean up user binary querying --- server/channelserver/handlers_users.go | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/server/channelserver/handlers_users.go b/server/channelserver/handlers_users.go index 6db6613c0..186cf686f 100644 --- a/server/channelserver/handlers_users.go +++ b/server/channelserver/handlers_users.go @@ -39,25 +39,18 @@ func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) { s.server.userBinaryPartsLock.RLock() defer s.server.userBinaryPartsLock.RUnlock() data, ok := s.server.userBinaryParts[userBinaryPartID{charID: pkt.CharID, index: pkt.BinaryType}] - resp := byteframe.NewByteFrame() // If we can't get the real data, try to get it from the database. if !ok { - var data []byte - rows, _ := s.server.db.Queryx(fmt.Sprintf("SELECT type%d FROM user_binaries WHERE id=$1", pkt.BinaryType), pkt.CharID) - for rows.Next() { - rows.Scan(&data) - resp.WriteBytes(data) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - return + err := s.server.db.QueryRow(fmt.Sprintf("SELECT type%d FROM user_binaries WHERE id=$1", pkt.BinaryType), pkt.CharID).Scan(&data) + if err != nil { + doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) + } else { + doAckBufSucceed(s, pkt.AckHandle, data) } - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - return } else { - resp.WriteBytes(data) + doAckBufSucceed(s, pkt.AckHandle, data) } - - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } func handleMsgSysNotifyUserBinary(s *Session, p mhfpacket.MHFPacket) {} From 33c3865da6d322333d294f563093e980cc0cbfa3 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 11 Aug 2022 15:00:15 +1000 Subject: [PATCH 20/86] remove unused import --- server/channelserver/handlers_users.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/channelserver/handlers_users.go b/server/channelserver/handlers_users.go index 186cf686f..dcab8bf84 100644 --- a/server/channelserver/handlers_users.go +++ b/server/channelserver/handlers_users.go @@ -3,7 +3,6 @@ package channelserver import ( "fmt" - "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" ) From 29d86177791e4255fe408c485974c26d72b21709 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 11 Aug 2022 15:17:50 +1000 Subject: [PATCH 21/86] achievement fallback --- server/channelserver/handlers_achievement.go | 33 +++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/server/channelserver/handlers_achievement.go b/server/channelserver/handlers_achievement.go index b552101f8..5db3853e1 100644 --- a/server/channelserver/handlers_achievement.go +++ b/server/channelserver/handlers_achievement.go @@ -4,7 +4,6 @@ import ( "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" "fmt" - "go.uber.org/zap" "io" ) @@ -88,24 +87,21 @@ func GetAchData(id uint8, score int32) Achievement { func handleMsgMhfGetAchievement(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetAchievement) - row := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", pkt.CharID) - if row != nil { + var exists int + err := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", pkt.CharID).Scan(&exists) + if err != nil { s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", pkt.CharID) } var scores [33]int32 - row = s.server.db.QueryRow("SELECT * FROM achievements WHERE id=$1", pkt.CharID) - if row != nil { - err := row.Scan(&scores[0], &scores[0], - &scores[1], &scores[2], &scores[3], &scores[4], &scores[5], &scores[6], &scores[7], &scores[8], &scores[9], - &scores[10], &scores[11], &scores[12], &scores[13], &scores[14], &scores[15], &scores[16], &scores[17], - &scores[18], &scores[19], &scores[20], &scores[21], &scores[22], &scores[23], &scores[24], &scores[25], - &scores[26], &scores[27], &scores[28], &scores[29], &scores[30], &scores[31], &scores[32]) - if err != nil { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 20)) - s.logger.Error("ERR@", zap.Error(err)) - return - } + err = s.server.db.QueryRow("SELECT * FROM achievements WHERE id=$1", pkt.CharID).Scan(&scores[0], + &scores[0], &scores[1], &scores[2], &scores[3], &scores[4], &scores[5], &scores[6], &scores[7], &scores[8], + &scores[9], &scores[10], &scores[11], &scores[12], &scores[13], &scores[14], &scores[15], &scores[16], + &scores[17], &scores[18], &scores[19], &scores[20], &scores[21], &scores[22], &scores[23], &scores[24], + &scores[25], &scores[26], &scores[27], &scores[28], &scores[29], &scores[30], &scores[31], &scores[32]) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 20)) + return } resp := byteframe.NewByteFrame() @@ -152,6 +148,13 @@ func handleMsgMhfResetAchievement(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfAddAchievement(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAddAchievement) + + var exists int + err := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", s.charID).Scan(&exists) + if err != nil { + s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", s.charID) + } + s.server.db.Exec(fmt.Sprintf("UPDATE achievements SET ach%d=ach%d+1 WHERE id=$1", pkt.AchievementID, pkt.AchievementID), s.charID) } From 7dab9e3ae17cc4cb3d60fcf4481cbc2c11101204 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 11 Aug 2022 15:21:04 +1000 Subject: [PATCH 22/86] achievement fallback --- server/channelserver/handlers_achievement.go | 33 +++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/server/channelserver/handlers_achievement.go b/server/channelserver/handlers_achievement.go index b552101f8..5db3853e1 100644 --- a/server/channelserver/handlers_achievement.go +++ b/server/channelserver/handlers_achievement.go @@ -4,7 +4,6 @@ import ( "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" "fmt" - "go.uber.org/zap" "io" ) @@ -88,24 +87,21 @@ func GetAchData(id uint8, score int32) Achievement { func handleMsgMhfGetAchievement(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetAchievement) - row := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", pkt.CharID) - if row != nil { + var exists int + err := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", pkt.CharID).Scan(&exists) + if err != nil { s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", pkt.CharID) } var scores [33]int32 - row = s.server.db.QueryRow("SELECT * FROM achievements WHERE id=$1", pkt.CharID) - if row != nil { - err := row.Scan(&scores[0], &scores[0], - &scores[1], &scores[2], &scores[3], &scores[4], &scores[5], &scores[6], &scores[7], &scores[8], &scores[9], - &scores[10], &scores[11], &scores[12], &scores[13], &scores[14], &scores[15], &scores[16], &scores[17], - &scores[18], &scores[19], &scores[20], &scores[21], &scores[22], &scores[23], &scores[24], &scores[25], - &scores[26], &scores[27], &scores[28], &scores[29], &scores[30], &scores[31], &scores[32]) - if err != nil { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 20)) - s.logger.Error("ERR@", zap.Error(err)) - return - } + err = s.server.db.QueryRow("SELECT * FROM achievements WHERE id=$1", pkt.CharID).Scan(&scores[0], + &scores[0], &scores[1], &scores[2], &scores[3], &scores[4], &scores[5], &scores[6], &scores[7], &scores[8], + &scores[9], &scores[10], &scores[11], &scores[12], &scores[13], &scores[14], &scores[15], &scores[16], + &scores[17], &scores[18], &scores[19], &scores[20], &scores[21], &scores[22], &scores[23], &scores[24], + &scores[25], &scores[26], &scores[27], &scores[28], &scores[29], &scores[30], &scores[31], &scores[32]) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 20)) + return } resp := byteframe.NewByteFrame() @@ -152,6 +148,13 @@ func handleMsgMhfResetAchievement(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfAddAchievement(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAddAchievement) + + var exists int + err := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", s.charID).Scan(&exists) + if err != nil { + s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", s.charID) + } + s.server.db.Exec(fmt.Sprintf("UPDATE achievements SET ach%d=ach%d+1 WHERE id=$1", pkt.AchievementID, pkt.AchievementID), s.charID) } From 1b129ac630a68c1ca55095e9df2e333cd9b7f9fc Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 11 Aug 2022 16:03:15 +1000 Subject: [PATCH 23/86] actually prevent leak --- server/channelserver/handlers_users.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/channelserver/handlers_users.go b/server/channelserver/handlers_users.go index dcab8bf84..3bebcc97a 100644 --- a/server/channelserver/handlers_users.go +++ b/server/channelserver/handlers_users.go @@ -16,7 +16,8 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) { s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: pkt.BinaryType}] = pkt.RawDataPayload s.server.userBinaryPartsLock.Unlock() - err := s.server.db.QueryRow("SELECT type2 FROM user_binaries WHERE id=$1", s.charID) + var exists []byte + err := s.server.db.QueryRow("SELECT type2 FROM user_binaries WHERE id=$1", s.charID).Scan(&exists) if err != nil { s.server.db.Exec("INSERT INTO user_binaries (id) VALUES ($1)", s.charID) } From 315cc391ff2ef31c3ffd420dd978558603c8f67c Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 11 Aug 2022 18:21:10 +1000 Subject: [PATCH 24/86] fix management right acknowledgement --- server/channelserver/handlers_guild.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index fcaddbaf9..f7aa9cdc5 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1883,8 +1883,7 @@ func handleMsgMhfUpdateGuild(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfSetGuildManageRight(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSetGuildManageRight) s.server.db.Exec("UPDATE guild_characters SET recruiter=$1 WHERE character_id=$2", pkt.Allowed, pkt.CharID) - // TODO: What is this supposed to return? This works for now - doAckBufSucceed(s, pkt.AckHandle, []byte{0x01}) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) { From a0dbe9d8c6043c0e496bc800ba7fd774c2bed4f1 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 11 Aug 2022 18:47:50 +1000 Subject: [PATCH 25/86] change title db syntax --- server/channelserver/handlers_house.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 7b28582d7..401347b76 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -329,8 +329,9 @@ func handleMsgMhfEnumerateTitle(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireTitle) - err := s.server.db.QueryRow("SELECT count(*) FROM titles WHERE id=$1 AND char_id=$2", pkt.TitleID, s.charID) - if err != nil { + var exists int + err := s.server.db.QueryRow("SELECT count(*) FROM titles WHERE id=$1 AND char_id=$2", pkt.TitleID, s.charID).Scan(&exists) + if err != nil || exists == 0 { s.server.db.Exec("INSERT INTO titles VALUES ($1, $2, now(), now())", pkt.TitleID, s.charID) } else { s.server.db.Exec("UPDATE titles SET updated_at=now()") From ce31b248eb4150092a94828db18f137224dd61b4 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 12 Aug 2022 15:13:31 +1000 Subject: [PATCH 26/86] loaddata fallback --- server/channelserver/handlers_data.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index e98578da7..d9cdcca17 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -297,10 +297,11 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) { } var data []byte - err := s.server.db.QueryRow("SELECT savedata FROM characters WHERE id = $1", s.charID).Scan(&data) - if err != nil { - s.logger.Fatal("Failed to get savedata from db", zap.Error(err)) + if err != nil || len(data) == 0 { + s.logger.Warn(fmt.Sprintf("Failed to load savedata (CID: %d)", s.charID), zap.Error(err)) + s.rawConn.Close() // Terminate the connection + return } doAckBufSucceed(s, pkt.AckHandle, data) @@ -310,11 +311,11 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) { } bf := byteframe.NewByteFrameFromBytes(decompSaveData) bf.Seek(88, io.SeekStart) - binary1 := bf.ReadNullTerminatedBytes() + name := bf.ReadNullTerminatedBytes() s.server.userBinaryPartsLock.Lock() - s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: 1}] = append(binary1, []byte{0x00}...) + s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: 1}] = append(name, []byte{0x00}...) s.server.userBinaryPartsLock.Unlock() - s.Name = stringsupport.SJISToUTF8(binary1) + s.Name = stringsupport.SJISToUTF8(name) } func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) { From d58f765898df54c51c8b776d02b36e25a1c24968 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 12 Aug 2022 16:36:22 +1000 Subject: [PATCH 27/86] prevent null souls on applicant members --- server/channelserver/handlers_guild_member.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_guild_member.go b/server/channelserver/handlers_guild_member.go index 24a3341ab..0bc528fc7 100644 --- a/server/channelserver/handlers_guild_member.go +++ b/server/channelserver/handlers_guild_member.go @@ -49,7 +49,7 @@ const guildMembersSelectSQL = ` SELECT g.id as guild_id, joined_at, - souls, + coalesce(souls, 0) as souls, c.name, character.character_id, coalesce(gc.order_index, 0) as order_index, From 0bf19c8ac8358f941dec51fc58f901341774afcc Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 12 Aug 2022 16:38:00 +1000 Subject: [PATCH 28/86] temp handle monthly guild allowance --- .../mhfpacket/msg_mhf_acquire_monthly_item.go | 23 ++++++++++++++----- .../mhfpacket/msg_mhf_check_monthly_item.go | 17 +++++++++----- server/channelserver/handlers.go | 4 ---- server/channelserver/handlers_guild.go | 11 +++++++++ 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/network/mhfpacket/msg_mhf_acquire_monthly_item.go b/network/mhfpacket/msg_mhf_acquire_monthly_item.go index a8d8e2f04..acc10b42a 100644 --- a/network/mhfpacket/msg_mhf_acquire_monthly_item.go +++ b/network/mhfpacket/msg_mhf_acquire_monthly_item.go @@ -1,15 +1,21 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfAcquireMonthlyItem represents the MSG_MHF_ACQUIRE_MONTHLY_ITEM -type MsgMhfAcquireMonthlyItem struct{} +type MsgMhfAcquireMonthlyItem struct { + AckHandle uint32 + Unk0 uint16 + Unk1 uint16 + Unk2 uint32 + Unk3 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfAcquireMonthlyItem) Opcode() network.PacketID { @@ -18,7 +24,12 @@ func (m *MsgMhfAcquireMonthlyItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfAcquireMonthlyItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() + m.Unk2 = bf.ReadUint32() + m.Unk3 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_check_monthly_item.go b/network/mhfpacket/msg_mhf_check_monthly_item.go index 2e04c3226..d79c65240 100644 --- a/network/mhfpacket/msg_mhf_check_monthly_item.go +++ b/network/mhfpacket/msg_mhf_check_monthly_item.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" ) // MsgMhfCheckMonthlyItem represents the MSG_MHF_CHECK_MONTHLY_ITEM -type MsgMhfCheckMonthlyItem struct{} +type MsgMhfCheckMonthlyItem struct { + AckHandle uint32 + Unk uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfCheckMonthlyItem) Opcode() network.PacketID { @@ -18,7 +21,9 @@ func (m *MsgMhfCheckMonthlyItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfCheckMonthlyItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 58ea6b401..d3497eae9 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -702,10 +702,6 @@ func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfCheckMonthlyItem(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfAcquireMonthlyItem(s *Session, p mhfpacket.MHFPacket) {} - func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index f7aa9cdc5..2dd84f659 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1886,6 +1886,17 @@ func handleMsgMhfSetGuildManageRight(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) } +func handleMsgMhfCheckMonthlyItem(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfCheckMonthlyItem) + doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) + // TODO: Implement month-by-month tracker, 0 = Not claimed, 1 = Claimed +} + +func handleMsgMhfAcquireMonthlyItem(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfAcquireMonthlyItem) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} + func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateInvGuild) stubEnumerateNoResults(s, pkt.AckHandle) From b093d98d01daca8dbf670dc73663ec3b55921d7e Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 02:08:26 +1000 Subject: [PATCH 29/86] decode tournament info --- server/channelserver/handlers_festa.go | 40 +++++++++++++++++++------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index 76b6dedad..4db9b97a2 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -42,29 +42,49 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { case 1: bf.WriteUint32(uint32(midnight.Unix())) bf.WriteUint32(uint32(midnight.Add(3 * 24 * time.Hour).Unix())) - bf.WriteUint32(uint32(midnight.Add(12 * 24 * time.Hour).Unix())) - bf.WriteUint32(uint32(midnight.Add(21 * 24 * time.Hour).Unix())) + bf.WriteUint32(uint32(midnight.Add(13 * 24 * time.Hour).Unix())) + bf.WriteUint32(uint32(midnight.Add(20 * 24 * time.Hour).Unix())) case 2: bf.WriteUint32(uint32(midnight.Add(-3 * 24 * time.Hour).Unix())) bf.WriteUint32(uint32(midnight.Unix())) - bf.WriteUint32(uint32(midnight.Add(9 * 24 * time.Hour).Unix())) - bf.WriteUint32(uint32(midnight.Add(16 * 24 * time.Hour).Unix())) + bf.WriteUint32(uint32(midnight.Add(10 * 24 * time.Hour).Unix())) + bf.WriteUint32(uint32(midnight.Add(17 * 24 * time.Hour).Unix())) case 3: - bf.WriteUint32(uint32(midnight.Add(-12 * 24 * time.Hour).Unix())) - bf.WriteUint32(uint32(midnight.Add(-9 * 24 * time.Hour).Unix())) + bf.WriteUint32(uint32(midnight.Add(-13 * 24 * time.Hour).Unix())) + bf.WriteUint32(uint32(midnight.Add(-10 * 24 * time.Hour).Unix())) bf.WriteUint32(uint32(midnight.Unix())) bf.WriteUint32(uint32(midnight.Add(7 * 24 * time.Hour).Unix())) default: bf.WriteBytes(make([]byte, 16)) bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // TS Current Time - bf.WriteUint16(1) - bf.WriteUint32(0) + bf.WriteUint8(3) + bf.WriteBytes(make([]byte, 4)) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) return } bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // TS Current Time - d, _ := hex.DecodeString("031491E631353089F18CF68EAE8EEB97C291E589EF00001200000A54001000000000ED130D949A96B697B393A294B081490000000A55001000010000ED130D949A96B697B393A294B081490000000A56001000020000ED130D949A96B697B393A294B081490000000A57001000030000ED130D949A96B697B393A294B081490000000A58001000040000ED130D949A96B697B393A294B081490000000A59001000050000ED130D949A96B697B393A294B081490000000A5A001000060000ED130D949A96B697B393A294B081490000000A5B001000070000ED130D949A96B697B393A294B081490000000A5C001000080000ED130D949A96B697B393A294B081490000000A5D001000090000ED130D949A96B697B393A294B081490000000A5E0010000A0000ED130D949A96B697B393A294B081490000000A5F0010000B0000ED130D949A96B697B393A294B081490000000A600010000C0000ED130D949A96B697B393A294B081490000000A610010000D0000ED130D949A96B697B393A294B081490000000A620011FFFF0000ED121582DD82F182C882C5949A96B697B393A294B081490000000A63000600EA0000000009834C838C834183570000000A64000600ED000000000B836E838A837D834F838D0000000A65000600EF0000000011834A834E8354839383668381834C83930003000002390006000600000E8CC2906C208B9091E58B9B94740001617E43303581798BA38B5A93E09765817A0A7E433030834E83478358836782C592DE82C182BD8B9B82CC83548343835982F08BA382A40A7E433034817991CE8FDB8B9B817A0A7E433030834C838C8341835781410A836E838A837D834F838D8141834A834E8354839383668381834C83930A7E433037817993FC8FDC8FDC9569817A0A7E4330308B9B947482CC82B582E982B58141835E838B836C835290B68E598C9481410A834F815B834E90B68E598C948141834F815B834E91AB90B68E598C9481410A834F815B834E89F095FA8C94283181603388CA290A2F97C29263837C8343839383672831816031303088CA290A2F8FA08360835083628367817B836E815B8374836083508362836794920A2831816035303088CA290A7E43303381798A4A8DC38AFA8AD4817A0A7E43303032303139944E31318C8E323293FA2031343A303082A982E70A32303139944E31318C8E323593FA2031343A303082DC82C5000000023A0011000700001297C292632082668B89E8E891CA935694740000ED7E43303581798BA38B5A93E09765817A0A7E43303081E182DD82F182C882C5949A96B697B393A294B0814981E282F00A93AF82B697C2926382C98F8A91AE82B782E934906C82DC82C582CC0A97C2926388F582C582A282A982C9918182AD834E838A834182B782E982A90A82F08BA382A40A0A7E433037817993FC8FDC8FDC9569817A0A7E43303091E631343789F18EEB906C8DD582CC8DB02831816032303088CA290A0A7E43303381798A4A8DC38AFA8AD4817A0A7E43303032303139944E31318C8E323293FA2031343A303082A982E70A32303139944E31318C8E323593FA2031343A303082DC82C50A000000023B001000070000128CC2906C2082668B89E8E891CA935694740001497E43303581798BA38B5A93E09765817A0A7E43303081E1949A96B697B393A294B0814981E282F00A82A282A982C9918182AD834E838A834182B782E982A982F08BA382A40A0A7E433037817993FC8FDC8FDC9569817A0A7E43303089A48ED282CC8381835F838B283188CA290A2F8CF68EAE82CC82B582E982B58141835E838B836C835290B68E598C9481410A834F815B834E90B68E598C948141834F815B834E91AB90B68E598C9481410A834F815B834E89F095FA8C94283181603388CA290A2F97C29263837C8343839383672831816031303088CA290A2F8FA08360835083628367817B836E815B8374836083508362836794920A2831816035303088CA290A7E43303381798A4A8DC38AFA8AD4817A0A7E43303032303139944E31318C8E323293FA2031343A303082A982E70A32303139944E31318C8E323593FA2031343A303082DC82C500") - bf.WriteBytes(d) + bf.WriteUint8(3) + ps.Uint8(bf, "", false) + bf.WriteUint16(0) // numEvents + bf.WriteUint8(0) // numCups + + /* + struct event + uint32 eventID + uint16 unk + uint16 unk + uint32 unk + psUint8 name + + struct cup + uint32 cupID + uint16 unk + uint16 unk + uint16 unk + psUint8 name + psUint16 desc + */ + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } From 18989e9435d70a481279166b3e77f07890f94d9e Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 16:25:32 +1000 Subject: [PATCH 30/86] initial netcafe implementation --- ...g_mhf_post_cafe_duration_bonus_received.go | 19 +- network/mhfpacket/msg_mhf_start_boost_time.go | 17 +- patch-schema/netcafe.sql | 21 +++ server/channelserver/handlers.go | 67 +------ server/channelserver/handlers_cafe.go | 178 ++++++++++++++++++ server/channelserver/handlers_event.go | 2 - server/channelserver/handlers_tactics.go | 9 - 7 files changed, 226 insertions(+), 87 deletions(-) create mode 100644 patch-schema/netcafe.sql create mode 100644 server/channelserver/handlers_cafe.go 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) {} From 3bb0ab62955ad30358f233baba04d36dd10285ef Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 17:21:24 +1000 Subject: [PATCH 31/86] cleanup logic and update cafe time --- patch-schema/netcafe.sql | 8 ++-- server/channelserver/handlers.go | 25 +++++----- server/channelserver/handlers_cafe.go | 66 +++++++++++++-------------- 3 files changed, 51 insertions(+), 48 deletions(-) diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql index 6bef530cc..cfd85e5f8 100644 --- a/patch-schema/netcafe.sql +++ b/patch-schema/netcafe.sql @@ -6,10 +6,10 @@ ALTER TABLE IF EXISTS public.characters 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 + minutes_req integer NOT NULL, + item_type integer NOT NULL, + item_id integer NOT NULL, + quantity integer NOT NULL ); CREATE TABLE IF NOT EXISTS public.cafe_accepted diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index d409fdde8..26447a2fb 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -190,8 +190,12 @@ func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) { } func logoutPlayer(s *Session) { - delete(s.server.sessions, s.rawConn) - s.rawConn.Close() + if _, exists := s.server.sessions[s.rawConn]; exists { + delete(s.server.sessions, s.rawConn) + s.rawConn.Close() + } else { + return // Prevent re-running logout logic on real logouts + } _, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL, char_id=NULL WHERE token=$1", s.token) if err != nil { @@ -204,13 +208,13 @@ func logoutPlayer(s *Session) { } var timePlayed int + var sessionTime int _ = s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed) - - timePlayed = (int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)) + timePlayed + sessionTime = int(Time_Current_Adjusted().Unix()) - int(s.sessionStart) + timePlayed += sessionTime var rpGained int - - if s.rights > 0x40000000 { // N Course + if s.rights >= 0x40000000 { // N Course rpGained = timePlayed / 900 timePlayed = timePlayed % 900 } else { @@ -218,10 +222,10 @@ func logoutPlayer(s *Session) { timePlayed = timePlayed % 1800 } - _, err = s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID) - if err != nil { - panic(err) - } + s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID) + s.server.db.Exec("UPDATE characters SET cafe_time=cafe_time+$1 WHERE id=$2", sessionTime, s.charID) + + treasureHuntUnregister(s) if s.stage == nil { return @@ -241,7 +245,6 @@ func logoutPlayer(s *Session) { removeSessionFromSemaphore(s) removeSessionFromStage(s) - treasureHuntUnregister(s) saveData, err := GetCharacterSaveData(s, s.charID) if err != nil { diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 8692864cf..76529ce4f 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -10,26 +10,26 @@ import ( 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) + var netcafePoints uint32 + err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafePoints) if err != nil { - s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) + s.logger.Fatal("Failed to get netcafe points from db", zap.Error(err)) } resp := byteframe.NewByteFrame() - resp.WriteUint32(uint32(netcafe_points)) + resp.WriteUint32(netcafePoints) 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) + var netcafePoints uint32 + err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafePoints) if err != nil { - s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err)) + s.logger.Fatal("Failed to get netcate points from db", zap.Error(err)) } resp := byteframe.NewByteFrame() resp.WriteUint32(0) - resp.WriteUint32(uint32(netcafe_points)) + resp.WriteUint32(netcafePoints) doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) } @@ -77,19 +77,19 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { panic(err) } cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime - bf.WriteUint32(cafeTime) // Total hours + bf.WriteUint32(cafeTime) // Total cafe time 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"` + ID uint32 `db:"id"` + Minutes uint32 `db:"minutes_req"` + ItemType uint32 `db:"item_type"` + ItemID uint32 `db:"item_id"` + Quantity uint32 `db:"quantity"` + Claimed bool `db:"claimed"` } func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { @@ -98,12 +98,12 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { var count uint32 rows, err := s.server.db.Queryx(` - SELECT cb.id, line, itemclass, itemid, tradequantity, + SELECT cb.id, minutes_req, item_type, item_id, quantity, ( SELECT count(*) FROM cafe_accepted ca WHERE cb.id = ca.cafe_id AND ca.character_id = $1 - ) AS flagcount + )::int::bool AS claimed FROM cafebonus cb ORDER BY id ASC;`, s.charID) if err != nil { s.logger.Error("Error getting cafebonus", zap.Error(err)) @@ -111,20 +111,20 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { } else { for rows.Next() { count++ - Cafes := &CafeBonus{} - err = rows.StructScan(&Cafes) + cafeBonus := &CafeBonus{} + err = rows.StructScan(&cafeBonus) 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) + bf.WriteUint32(cafeBonus.Minutes) + bf.WriteUint32(0) // Unk + bf.WriteUint32(cafeBonus.ItemID) + bf.WriteUint32(cafeBonus.Quantity) + bf.WriteBool(cafeBonus.Claimed) } resp := byteframe.NewByteFrame() resp.WriteUint32(0) - resp.WriteUint32(uint32(time.Now().Unix())) //timestamp + resp.WriteUint32(uint32(time.Now().Unix())) resp.WriteUint32(count) resp.WriteBytes(bf.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) @@ -136,7 +136,7 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() row := s.server.db.QueryRowx(` - SELECT c.id, c.line, itemclass, itemid, tradequantity + SELECT c.id, minutes_req, item_type, item_id, quantity FROM cafebonus c WHERE ( SELECT count(*) @@ -146,18 +146,18 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { 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) + ) >= minutes_req LIMIT 1;`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) + cafeBonus := &CafeBonus{} + err := row.StructScan(cafeBonus) 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) + bf.WriteUint32(cafeBonus.ID) + bf.WriteUint32(cafeBonus.ItemType) + bf.WriteUint32(cafeBonus.ItemID) + bf.WriteUint32(cafeBonus.Quantity) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } From ac90c2dd09aae0103aa91b57ddcab35536cd69f8 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 17:23:33 +1000 Subject: [PATCH 32/86] correct minutes->seconds --- patch-schema/netcafe.sql | 2 +- server/channelserver/handlers_cafe.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql index cfd85e5f8..e56633c46 100644 --- a/patch-schema/netcafe.sql +++ b/patch-schema/netcafe.sql @@ -6,7 +6,7 @@ ALTER TABLE IF EXISTS public.characters CREATE TABLE IF NOT EXISTS public.cafebonus ( id integer NOT NULL PRIMARY KEY, - minutes_req integer NOT NULL, + seconds_req integer NOT NULL, item_type integer NOT NULL, item_id integer NOT NULL, quantity integer NOT NULL diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 76529ce4f..8e7deaf5e 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -85,7 +85,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { type CafeBonus struct { ID uint32 `db:"id"` - Minutes uint32 `db:"minutes_req"` + Seconds uint32 `db:"time_req"` ItemType uint32 `db:"item_type"` ItemID uint32 `db:"item_id"` Quantity uint32 `db:"quantity"` @@ -98,7 +98,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { var count uint32 rows, err := s.server.db.Queryx(` - SELECT cb.id, minutes_req, item_type, item_id, quantity, + SELECT cb.id, seconds_req, item_type, item_id, quantity, ( SELECT count(*) FROM cafe_accepted ca @@ -116,7 +116,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { if err != nil { s.logger.Error("Error scanning cafebonus", zap.Error(err)) } - bf.WriteUint32(cafeBonus.Minutes) + bf.WriteUint32(cafeBonus.Seconds) bf.WriteUint32(0) // Unk bf.WriteUint32(cafeBonus.ItemID) bf.WriteUint32(cafeBonus.Quantity) @@ -136,7 +136,7 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() row := s.server.db.QueryRowx(` - SELECT c.id, minutes_req, item_type, item_id, quantity + SELECT c.id, seconds_req, item_type, item_id, quantity FROM cafebonus c WHERE ( SELECT count(*) @@ -146,7 +146,7 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { SELECT ch.cafe_time + $2 FROM characters ch WHERE ch.id = $1 - ) >= minutes_req LIMIT 1;`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) + ) >= seconds_req LIMIT 1;`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) cafeBonus := &CafeBonus{} err := row.StructScan(cafeBonus) if err != nil { From 0ba9adcc88534dba575a27e8bfe946ad52303ff6 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 17:46:17 +1000 Subject: [PATCH 33/86] handle receiving multiple items --- patch-schema/netcafe.sql | 2 +- server/channelserver/handlers_cafe.go | 43 ++++++++++++++++----------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql index e56633c46..c7aeb9460 100644 --- a/patch-schema/netcafe.sql +++ b/patch-schema/netcafe.sql @@ -6,7 +6,7 @@ ALTER TABLE IF EXISTS public.characters CREATE TABLE IF NOT EXISTS public.cafebonus ( id integer NOT NULL PRIMARY KEY, - seconds_req integer NOT NULL, + time_req integer NOT NULL, item_type integer NOT NULL, item_id integer NOT NULL, quantity integer NOT NULL diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 8e7deaf5e..cfac96cc7 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -5,6 +5,7 @@ import ( ps "erupe-ce/common/pascalstring" "erupe-ce/network/mhfpacket" "go.uber.org/zap" + "io" "time" ) @@ -85,7 +86,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { type CafeBonus struct { ID uint32 `db:"id"` - Seconds uint32 `db:"time_req"` + TimeReq uint32 `db:"time_req"` ItemType uint32 `db:"item_type"` ItemID uint32 `db:"item_id"` Quantity uint32 `db:"quantity"` @@ -98,7 +99,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { var count uint32 rows, err := s.server.db.Queryx(` - SELECT cb.id, seconds_req, item_type, item_id, quantity, + SELECT cb.id, time_req, item_type, item_id, quantity, ( SELECT count(*) FROM cafe_accepted ca @@ -116,7 +117,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { if err != nil { s.logger.Error("Error scanning cafebonus", zap.Error(err)) } - bf.WriteUint32(cafeBonus.Seconds) + bf.WriteUint32(cafeBonus.TimeReq) bf.WriteUint32(0) // Unk bf.WriteUint32(cafeBonus.ItemID) bf.WriteUint32(cafeBonus.Quantity) @@ -134,9 +135,10 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfReceiveCafeDurationBonus) bf := byteframe.NewByteFrame() - - row := s.server.db.QueryRowx(` - SELECT c.id, seconds_req, item_type, item_id, quantity + var count uint32 + bf.WriteUint32(0) + rows, err := s.server.db.Queryx(` + SELECT c.id, time_req, item_type, item_id, quantity FROM cafebonus c WHERE ( SELECT count(*) @@ -146,19 +148,26 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { SELECT ch.cafe_time + $2 FROM characters ch WHERE ch.id = $1 - ) >= seconds_req LIMIT 1;`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) - cafeBonus := &CafeBonus{} - err := row.StructScan(cafeBonus) + ) >= time_req`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) if err != nil { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 8)) - return + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + } else { + for rows.Next() { + cafeBonus := &CafeBonus{} + err = rows.StructScan(cafeBonus) + if err != nil { + continue + } + count++ + bf.WriteUint32(cafeBonus.ID) + bf.WriteUint32(cafeBonus.ItemType) + bf.WriteUint32(cafeBonus.ItemID) + bf.WriteUint32(cafeBonus.Quantity) + } + bf.Seek(0, io.SeekStart) + bf.WriteUint32(count) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } - bf.WriteUint32(1) - bf.WriteUint32(cafeBonus.ID) - bf.WriteUint32(cafeBonus.ItemType) - bf.WriteUint32(cafeBonus.ItemID) - bf.WriteUint32(cafeBonus.Quantity) - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) { From d8072ee06e70ff8abf405e055fee6c4fd1e8d9a2 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 18:22:56 +1000 Subject: [PATCH 34/86] default netcafe rewards and maintenance --- main.go | 4 +++- patch-schema/netcafe.sql | 12 +++++++++++- server/channelserver/handlers_cafe.go | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index d0b13d649..c3c0162e0 100644 --- a/main.go +++ b/main.go @@ -108,9 +108,11 @@ func main() { } logger.Info("Connected to database") - // Clear existing tokens + // Clear stale data _ = db.MustExec("DELETE FROM sign_sessions") _ = db.MustExec("DELETE FROM servers") + _ = db.MustExec("DELETE FROM cafe_accepted") + _ = db.MustExec("UPDATE characters SET cafe_time=0") // Clean the DB if the option is on. if erupeConfig.DevMode && erupeConfig.DevModeOptions.CleanDB { diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql index c7aeb9460..077adc7bd 100644 --- a/patch-schema/netcafe.sql +++ b/patch-schema/netcafe.sql @@ -5,7 +5,7 @@ ALTER TABLE IF EXISTS public.characters CREATE TABLE IF NOT EXISTS public.cafebonus ( - id integer NOT NULL PRIMARY KEY, + id serial NOT NULL PRIMARY KEY, time_req integer NOT NULL, item_type integer NOT NULL, item_id integer NOT NULL, @@ -18,4 +18,14 @@ CREATE TABLE IF NOT EXISTS public.cafe_accepted character_id integer NOT NULL ); +INSERT INTO public.cafebonus (time_req, item_type, item_id, quantity) +VALUES + (1800, 17, 0, 250), + (3600, 17, 0, 500), + (7200, 17, 0, 1000), + (10800, 17, 0, 1500), + (18000, 17, 0, 1750), + (28800, 17, 0, 3000), + (43200, 17, 0, 4000); + END; \ No newline at end of file diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index cfac96cc7..a020d38a0 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -118,7 +118,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { s.logger.Error("Error scanning cafebonus", zap.Error(err)) } bf.WriteUint32(cafeBonus.TimeReq) - bf.WriteUint32(0) // Unk + bf.WriteUint32(cafeBonus.ItemType) bf.WriteUint32(cafeBonus.ItemID) bf.WriteUint32(cafeBonus.Quantity) bf.WriteBool(cafeBonus.Claimed) From 440b73f69658fdae79430fa3162d258ba01b66f1 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 21:23:02 +1000 Subject: [PATCH 35/86] implement netcafe points and boost time --- ...g_mhf_post_cafe_duration_bonus_received.go | 9 +-- patch-schema/netcafe.sql | 11 +++- server/channelserver/handlers_cafe.go | 66 +++++++++++++++++-- server/channelserver/handlers_event.go | 26 -------- 4 files changed, 74 insertions(+), 38 deletions(-) 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 be8947f2c..baa102f61 100644 --- a/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go +++ b/network/mhfpacket/msg_mhf_post_cafe_duration_bonus_received.go @@ -11,8 +11,7 @@ import ( // MsgMhfPostCafeDurationBonusReceived represents the MSG_MHF_POST_CAFE_DURATION_BONUS_RECEIVED type MsgMhfPostCafeDurationBonusReceived struct { AckHandle uint32 - Unk0 uint32 - CafeBonusID uint32 + CafeBonusID []uint32 } // Opcode returns the ID associated with this packet type. @@ -23,8 +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 { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint32() - m.CafeBonusID = bf.ReadUint32() + ids := int(bf.ReadUint32()) + for i := 0; i < ids; i++ { + m.CafeBonusID = append(m.CafeBonusID, bf.ReadUint32()) + } return nil } diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql index 077adc7bd..e284742ce 100644 --- a/patch-schema/netcafe.sql +++ b/patch-schema/netcafe.sql @@ -1,7 +1,16 @@ BEGIN; ALTER TABLE IF EXISTS public.characters - ADD COLUMN cafe_time integer DEFAULT 0; + ADD COLUMN IF NOT EXISTS cafe_time integer DEFAULT 0; + +ALTER TABLE IF EXISTS public.characters + DROP COLUMN IF EXISTS netcafe_points; + +ALTER TABLE IF EXISTS public.characters + ADD COLUMN IF NOT EXISTS netcafe_points int DEFAULT 0; + +ALTER TABLE IF EXISTS public.characters + ADD COLUMN IF NOT EXISTS boost_time timestamp without time zone; CREATE TABLE IF NOT EXISTS public.cafebonus ( diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index a020d38a0..041304ed7 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -29,7 +29,6 @@ func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) { s.logger.Fatal("Failed to get netcate points from db", zap.Error(err)) } resp := byteframe.NewByteFrame() - resp.WriteUint32(0) resp.WriteUint32(netcafePoints) doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) } @@ -58,7 +57,7 @@ func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) { 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) + _, err := s.server.db.Exec("UPDATE characters SET daily_time=$1, netcafe_points=netcafe_points+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)) } @@ -172,16 +171,69 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { 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)) + var cafeBonus CafeBonus + for _, cbID := range pkt.CafeBonusID { + err := s.server.db.QueryRow(` + SELECT cb.id, item_type, quantity FROM cafebonus cb WHERE cb.id=$1 + `, cbID).Scan(&cafeBonus.ID, &cafeBonus.ItemType, &cafeBonus.Quantity) + if err == nil { + if cafeBonus.ItemType == 17 { + s.server.db.Exec("UPDATE characters SET netcafe_points=netcafe_points+$1 WHERE id=$2", cafeBonus.Quantity, s.charID) + } + } + s.server.db.Exec("INSERT INTO public.cafe_accepted VALUES ($1, $2)", cbID, s.charID) } - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStartBoostTime) + bf := byteframe.NewByteFrame() + boostLimit := Time_Current_Adjusted().Add(100 * time.Minute) + s.server.db.Exec("UPDATE characters SET boost_time=$1 WHERE id=$2", boostLimit, s.charID) + bf.WriteUint32(uint32(boostLimit.Unix())) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} + +func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetBoostTime) + doAckBufSucceed(s, pkt.AckHandle, []byte{}) +} + +func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit) + bf := byteframe.NewByteFrame() + var boostLimit time.Time + err := s.server.db.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.charID).Scan(&boostLimit) + if err != nil { + bf.WriteUint32(0) + } else { + bf.WriteUint32(uint32(boostLimit.Unix())) + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } + +func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetBoostRight) + var boostLimit time.Time + err := s.server.db.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.charID).Scan(&boostLimit) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + return + } + if boostLimit.Unix() < Time_Current_Adjusted().Unix() { + doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02}) + } else { + doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) + } +} + +func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfPostBoostTimeQuestReturn) + doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) +} + +func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {} + +func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go index 87ae246af..7ec7c30e7 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -303,32 +303,6 @@ func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } -func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetBoostTime) - - doAckBufSucceed(s, pkt.AckHandle, []byte{}) - updateRights(s) -} - -func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit) - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) -} - -func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetBoostRight) - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) -} - -func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfPostBoostTimeQuestReturn) - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) -} - -func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {} - func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfSetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) { From 0edfd255fdd1d10ad22b374a04d2cc36d44ecebc Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 13 Aug 2022 21:46:05 +1000 Subject: [PATCH 36/86] correct backwards comparison --- server/channelserver/handlers_cafe.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 041304ed7..c7d826429 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -222,10 +222,10 @@ func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) return } - if boostLimit.Unix() < Time_Current_Adjusted().Unix() { - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02}) - } else { + if boostLimit.Unix() > Time_Current_Adjusted().Unix() { doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) + } else { + doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02}) } } From 12ac12ff3f7375c5001c8760d27e0420747a7415 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 09:49:35 +1000 Subject: [PATCH 37/86] stub mercenary data --- server/channelserver/handlers_mercenary.go | 24 ++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index 37ce9e628..ca3dd2a71 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -121,10 +121,30 @@ func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfMercenaryHuntdata(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfMercenaryHuntdata) - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x0A)) + if pkt.Unk0 == 1 { + // Format: + // uint8 Hunts + // struct Hunt + // uint32 HuntID + // uint32 MonID + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) + } else { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0)) + } } -func handleMsgMhfEnumerateMercenaryLog(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumerateMercenaryLog(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateMercenaryLog) + bf := byteframe.NewByteFrame() + bf.WriteUint32(0) + // Format: + // struct Log + // uint32 Timestamp + // []byte Name (len 18) + // uint8 Unk + // uint8 Unk + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfCreateMercenary) From 6ab961fa90625addb4ad3b1539bb6dc0d9ced46c Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 17:38:25 +1000 Subject: [PATCH 38/86] initial stamp implementation --- .../mhfpacket/msg_mhf_check_weekly_stamp.go | 19 ++++---- .../msg_mhf_exchange_weekly_stamp.go | 27 +++++++++--- patch-schema/stamps.sql | 13 ++++++ server/channelserver/handlers.go | 44 ++++++++++++++----- server/channelserver/sys_timefix.go | 10 +++++ 5 files changed, 88 insertions(+), 25 deletions(-) create mode 100644 patch-schema/stamps.sql diff --git a/network/mhfpacket/msg_mhf_check_weekly_stamp.go b/network/mhfpacket/msg_mhf_check_weekly_stamp.go index 83f6e5ce8..f03b1d1e7 100644 --- a/network/mhfpacket/msg_mhf_check_weekly_stamp.go +++ b/network/mhfpacket/msg_mhf_check_weekly_stamp.go @@ -1,15 +1,16 @@ package mhfpacket import ( + "errors" + "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgMhfCheckWeeklyStamp represents the MSG_MHF_CHECK_WEEKLY_STAMP type MsgMhfCheckWeeklyStamp struct { AckHandle uint32 - Unk0 uint8 + StampType string Unk1 bool Unk2 uint16 // Hardcoded 0 in the binary } @@ -22,7 +23,13 @@ func (m *MsgMhfCheckWeeklyStamp) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfCheckWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint8() + stampType := bf.ReadUint8() + switch stampType { + case 1: + m.StampType = "hl" + case 2: + m.StampType = "ex" + } m.Unk1 = bf.ReadBool() m.Unk2 = bf.ReadUint16() return nil @@ -30,9 +37,5 @@ func (m *MsgMhfCheckWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.C // Build builds a binary packet from the current data. func (m *MsgMhfCheckWeeklyStamp) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - bf.WriteUint32(m.AckHandle) - bf.WriteUint8(m.Unk0) - bf.WriteBool(m.Unk1) - bf.WriteUint16(m.Unk2) - return nil + return errors.New("NOT IMPLEMENTED") } diff --git a/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go b/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go index 6cce19147..758925936 100644 --- a/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go +++ b/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go @@ -1,15 +1,20 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfExchangeWeeklyStamp represents the MSG_MHF_EXCHANGE_WEEKLY_STAMP -type MsgMhfExchangeWeeklyStamp struct{} +type MsgMhfExchangeWeeklyStamp struct { + AckHandle uint32 + StampType string + Unk1 bool + Unk2 uint16 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfExchangeWeeklyStamp) Opcode() network.PacketID { @@ -18,7 +23,17 @@ func (m *MsgMhfExchangeWeeklyStamp) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfExchangeWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + stampType := bf.ReadUint8() + switch stampType { + case 1: + m.StampType = "hl" + case 2: + m.StampType = "ex" + } + m.Unk1 = bf.ReadBool() + m.Unk2 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/patch-schema/stamps.sql b/patch-schema/stamps.sql new file mode 100644 index 000000000..2b940fa8c --- /dev/null +++ b/patch-schema/stamps.sql @@ -0,0 +1,13 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS public.stamps ( + character_id integer PRIMARY KEY, + hl_total integer DEFAULT 0, + hl_redeemed integer DEFAULT 0, + hl_next timestamp without time zone, + ex_total integer DEFAULT 0, + ex_redeemed integer DEFAULT 0, + ex_next timestamp without time zone +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 26447a2fb..fa2bbf2f4 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -646,19 +646,41 @@ func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp) - - resp := byteframe.NewByteFrame() - resp.WriteUint16(0x000E) - resp.WriteUint16(0x0001) - resp.WriteUint16(0x0000) - resp.WriteUint16(0x0000) // 0x0000 stops the vaguely annoying log in pop up - resp.WriteUint32(0) - resp.WriteUint32(0x5dddcbb3) // Timestamp - - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + weekCurrentStart := TimeWeekStart() + weekNextStart := TimeWeekNext() + var total, redeemed, updated uint16 + var nextClaim time.Time + err := s.server.db.QueryRow(fmt.Sprintf("SELECT %s_next FROM stamps WHERE character_id=$1", pkt.StampType), s.charID).Scan(&nextClaim) + if err != nil { + s.server.db.Exec("INSERT INTO stamps (character_id, hl_next, ex_next) VALUES ($1, $2, $2)", s.charID, weekNextStart) + nextClaim = weekNextStart + } + if nextClaim.Before(weekCurrentStart) { + s.server.db.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1, %s_next=$1 WHERE character_id=$2", pkt.StampType, pkt.StampType, pkt.StampType), weekNextStart, s.charID) + updated = 1 + } + s.server.db.QueryRow(fmt.Sprintf("SELECT %s_total, %s_redeemed FROM stamps WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) + bf := byteframe.NewByteFrame() + bf.WriteUint16(total) + bf.WriteUint16(redeemed) + bf.WriteUint16(updated) + bf.WriteUint32(0) // Unk + bf.WriteUint32(uint32(weekCurrentStart.Unix())) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } -func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp) + var total, redeemed uint16 + s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) + bf := byteframe.NewByteFrame() + bf.WriteUint16(total) + bf.WriteUint16(redeemed) + bf.WriteUint16(0) + bf.WriteUint32(0) // Unk, but has possible values + bf.WriteUint32(uint32(TimeWeekStart().Unix())) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} func getGookData(s *Session, cid uint32) (uint16, []byte) { var data []byte diff --git a/server/channelserver/sys_timefix.go b/server/channelserver/sys_timefix.go index 839382053..4cde6a319 100644 --- a/server/channelserver/sys_timefix.go +++ b/server/channelserver/sys_timefix.go @@ -31,6 +31,16 @@ func Time_Current_Midnight() time.Time { return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location()) } +func TimeWeekStart() time.Time { + midnight := Time_Current_Midnight() + offset := (int(midnight.Weekday()) - 1) * -24 + return midnight.Add(time.Hour * time.Duration(offset)) +} + +func TimeWeekNext() time.Time { + return TimeWeekStart().Add(time.Hour * 24 * 7) +} + func Time_Current_Week_uint8() uint8 { baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust) From cecfb1348dd17ec52945a113924a27a5a57ac525 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 17:39:25 +1000 Subject: [PATCH 39/86] add missing import --- server/channelserver/handlers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index fa2bbf2f4..1e5d77e33 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -9,6 +9,7 @@ import ( "io" "net" "strings" + "time" "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" From d48a64e20828e1b1c5cbe94ee421502917a5d578 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 18:09:06 +1000 Subject: [PATCH 40/86] rename rights variable --- network/mhfpacket/msg_sys_update_right.go | 4 ++-- server/channelserver/handlers.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/network/mhfpacket/msg_sys_update_right.go b/network/mhfpacket/msg_sys_update_right.go index 3a7c95f4e..b343dd0c4 100644 --- a/network/mhfpacket/msg_sys_update_right.go +++ b/network/mhfpacket/msg_sys_update_right.go @@ -37,7 +37,7 @@ type ClientRight struct { // MsgSysUpdateRight represents the MSG_SYS_UPDATE_RIGHT type MsgSysUpdateRight struct { ClientRespAckHandle uint32 // If non-0, requests the client to send back a MSG_SYS_ACK packet with this value. - Unk1 uint32 + Bitfield uint32 Rights []ClientRight UnkSize uint16 // Count of some buf up to 0x800 bytes following it. } @@ -55,7 +55,7 @@ func (m *MsgSysUpdateRight) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client // Build builds a binary packet from the current data. func (m *MsgSysUpdateRight) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { bf.WriteUint32(m.ClientRespAckHandle) - bf.WriteUint32(m.Unk1) + bf.WriteUint32(m.Bitfield) bf.WriteUint16(uint16(len(m.Rights))) bf.WriteUint16(0) for _, v := range m.Rights { diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 1e5d77e33..5af8abb20 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -79,7 +79,7 @@ func doAckSimpleFail(s *Session, ackHandle uint32, data []byte) { func updateRights(s *Session) { update := &mhfpacket.MsgSysUpdateRight{ ClientRespAckHandle: 0, - Unk1: s.rights, + Bitfield: s.rights, Rights: []mhfpacket.ClientRight{ { ID: 1, From d7cc7ab5e34943ba0fa8268d095149b9398abe44 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 18:52:53 +1000 Subject: [PATCH 41/86] fix invalid semaphore id for raviente support --- server/channelserver/handlers_register.go | 14 +++++++------- server/channelserver/handlers_semaphore.go | 12 +++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/server/channelserver/handlers_register.go b/server/channelserver/handlers_register.go index 3dd65cf6a..2c44cc6db 100644 --- a/server/channelserver/handlers_register.go +++ b/server/channelserver/handlers_register.go @@ -11,7 +11,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) s.server.raviente.Lock() switch pkt.SemaphoreID { - case 3: + case 4: resp := byteframe.NewByteFrame() size := 6 for i := 0; i < len(bf.Data())-1; i += size { @@ -49,7 +49,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) { } resp.WriteUint8(0) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - case 4: + case 5: resp := byteframe.NewByteFrame() size := 6 for i := 0; i < len(bf.Data())-1; i += size { @@ -74,7 +74,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) { } resp.WriteUint8(0) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - case 5: + case 6: resp := byteframe.NewByteFrame() size := 6 for i := 0; i < len(bf.Data())-1; i += size { @@ -242,15 +242,15 @@ func handleMsgSysLoadRegister(s *Session, p mhfpacket.MHFPacket) { func (s *Session) notifyRavi() { var temp mhfpacket.MHFPacket raviNotif := byteframe.NewByteFrame() - temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 3} - raviNotif.WriteUint16(uint16(temp.Opcode())) - temp.Build(raviNotif, s.clientContext) temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 4} raviNotif.WriteUint16(uint16(temp.Opcode())) temp.Build(raviNotif, s.clientContext) temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 5} raviNotif.WriteUint16(uint16(temp.Opcode())) temp.Build(raviNotif, s.clientContext) + temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 6} + raviNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(raviNotif, s.clientContext) raviNotif.WriteUint16(0x0010) // End it. sema := getRaviSemaphore(s) if sema != "" { @@ -262,7 +262,7 @@ func (s *Session) notifyRavi() { func getRaviSemaphore(s *Session) string { for _, semaphore := range s.server.semaphore { - if strings.HasPrefix(semaphore.id_semaphore, "hs_l0u3B5") && strings.HasSuffix(semaphore.id_semaphore, "3") { + if strings.HasPrefix(semaphore.id_semaphore, "hs_l0u3B5") && strings.HasSuffix(semaphore.id_semaphore, "4") { return semaphore.id_semaphore } } diff --git a/server/channelserver/handlers_semaphore.go b/server/channelserver/handlers_semaphore.go index 95af79314..cbf1a0a7f 100644 --- a/server/channelserver/handlers_semaphore.go +++ b/server/channelserver/handlers_semaphore.go @@ -45,12 +45,10 @@ func destructEmptySemaphores(s *Session) { } func releaseRaviSemaphore(s *Session, sema *Semaphore) { - if !strings.HasSuffix(sema.id_semaphore, "5") { - delete(sema.reservedClientSlots, s.charID) - delete(sema.clients, s) - } - if len(sema.reservedClientSlots) == 0 && len(sema.clients) == 0 { - s.logger.Debug("Raviente semaphore is empty, resetting") + delete(sema.reservedClientSlots, s.charID) + delete(sema.clients, s) + if strings.HasSuffix(sema.id_semaphore, "2") && len(sema.clients) == 0 { + s.logger.Debug("Main raviente semaphore is empty, resetting") resetRavi(s) } } @@ -91,7 +89,7 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { suffix, _ := strconv.ParseUint(pkt.SemaphoreID[len(pkt.SemaphoreID)-1:], 10, 32) s.server.semaphore[SemaphoreID] = &Semaphore{ id_semaphore: pkt.SemaphoreID, - id: uint32(suffix), + id: uint32(suffix + 1), clients: make(map[*Session]uint32), reservedClientSlots: make(map[uint32]interface{}), maxPlayers: 32, From 7a303d6e31d2a0f7d407b9641ea8f201d7a0d449 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 14 Aug 2022 19:03:11 +1000 Subject: [PATCH 42/86] prevent use of reserved semaphore indexes --- server/channelserver/sys_channel_server.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index a23ec2834..ca9063baf 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -154,7 +154,7 @@ func NewServer(config *Config) *Server { stages: make(map[string]*Stage), userBinaryParts: make(map[userBinaryPartID][]byte), semaphore: make(map[string]*Semaphore), - semaphoreIndex: 5, + semaphoreIndex: 7, discordBot: config.DiscordBot, name: config.Name, enable: config.Enable, @@ -413,6 +413,9 @@ func (s *Server) NextSemaphoreID() uint32 { for { exists := false s.semaphoreIndex = s.semaphoreIndex + 1 + if s.semaphoreIndex == 0 { + s.semaphoreIndex = 7 // Skip reserved indexes + } for _, semaphore := range s.semaphore { if semaphore.id == s.semaphoreIndex { exists = true From 86cb254d1a5fe5205909783bef7f6157a94b07a5 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 15 Aug 2022 15:12:50 +1000 Subject: [PATCH 43/86] initial warehouse concept --- .../mhfpacket/msg_mhf_enumerate_warehouse.go | 20 ++-- .../mhfpacket/msg_mhf_operate_warehouse.go | 32 ++++-- network/mhfpacket/msg_mhf_update_warehouse.go | 37 +++++-- patch-schema/warehouse.sql | 49 ++++++++++ server/channelserver/handlers_house.go | 98 ++++++++++++++++++- 5 files changed, 215 insertions(+), 21 deletions(-) create mode 100644 patch-schema/warehouse.sql diff --git a/network/mhfpacket/msg_mhf_enumerate_warehouse.go b/network/mhfpacket/msg_mhf_enumerate_warehouse.go index f567e8bcb..7a5e76a7b 100644 --- a/network/mhfpacket/msg_mhf_enumerate_warehouse.go +++ b/network/mhfpacket/msg_mhf_enumerate_warehouse.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" ) // MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE -type MsgMhfEnumerateWarehouse struct{} +type MsgMhfEnumerateWarehouse struct { + AckHandle uint32 + BoxType uint8 + BoxIndex uint8 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID { @@ -18,7 +22,11 @@ func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.BoxType = bf.ReadUint8() + m.BoxIndex = bf.ReadUint8() + _ = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_operate_warehouse.go b/network/mhfpacket/msg_mhf_operate_warehouse.go index 9df4e3770..ba0ee7b78 100644 --- a/network/mhfpacket/msg_mhf_operate_warehouse.go +++ b/network/mhfpacket/msg_mhf_operate_warehouse.go @@ -1,15 +1,22 @@ package mhfpacket -import ( - "errors" +import ( + "errors" + "erupe-ce/common/stringsupport" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfOperateWarehouse represents the MSG_MHF_OPERATE_WAREHOUSE -type MsgMhfOperateWarehouse struct{} +type MsgMhfOperateWarehouse struct { + AckHandle uint32 + Operation uint8 + BoxType string + BoxIndex uint8 + Name string +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID { @@ -18,7 +25,20 @@ func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfOperateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Operation = bf.ReadUint8() + boxType := bf.ReadUint8() + switch boxType { + case 0: + m.BoxType = "item" + case 1: + m.BoxType = "equip" + } + m.BoxIndex = bf.ReadUint8() + _ = bf.ReadUint8() // lenName + _ = bf.ReadUint16() // Unk + m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_update_warehouse.go b/network/mhfpacket/msg_mhf_update_warehouse.go index ba0321910..884ed4119 100644 --- a/network/mhfpacket/msg_mhf_update_warehouse.go +++ b/network/mhfpacket/msg_mhf_update_warehouse.go @@ -1,15 +1,27 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) +type UpdatedStack struct { + ID uint32 + Index uint16 + ItemID uint16 + Quantity uint16 + Unk uint16 +} + // MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE -type MsgMhfUpdateWarehouse struct{} +type MsgMhfUpdateWarehouse struct { + AckHandle uint32 + BoxID uint16 + Items []UpdatedStack +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID { @@ -18,7 +30,20 @@ func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.BoxID = bf.ReadUint16() + changes := int(bf.ReadUint16()) + var stackUpdate UpdatedStack + for i := 0; i < changes; i++ { + stackUpdate.ID = bf.ReadUint32() + stackUpdate.Index = bf.ReadUint16() + stackUpdate.ItemID = bf.ReadUint16() + stackUpdate.Quantity = bf.ReadUint16() + stackUpdate.Unk = bf.ReadUint16() + m.Items = append(m.Items, stackUpdate) + } + _ = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/patch-schema/warehouse.sql b/patch-schema/warehouse.sql new file mode 100644 index 000000000..2f2a5adde --- /dev/null +++ b/patch-schema/warehouse.sql @@ -0,0 +1,49 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS public.warehouse ( + character_id integer PRIMARY KEY, + item0 bytea, + item1 bytea, + item2 bytea, + item3 bytea, + item4 bytea, + item5 bytea, + item6 bytea, + item7 bytea, + item8 bytea, + item9 bytea, + item10 bytea, + item0name text, + item1name text, + item2name text, + item3name text, + item4name text, + item5name text, + item6name text, + item7name text, + item8name text, + item9name text, + equip0 bytea, + equip1 bytea, + equip2 bytea, + equip3 bytea, + equip4 bytea, + equip5 bytea, + equip6 bytea, + equip7 bytea, + equip8 bytea, + equip9 bytea, + equip10 bytea, + equip0name text, + equip1name text, + equip2name text, + equip3name text, + equip4name text, + equip5name text, + equip6name text, + equip7name text, + equip8name text, + equip9name text +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 401347b76..b7bdd78bf 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -5,11 +5,37 @@ import ( ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" "erupe-ce/network/mhfpacket" + "fmt" "go.uber.org/zap" "io" "time" ) +const warehouseNamesQuery = ` +SELECT +COALESCE(item0name, ''), +COALESCE(item1name, ''), +COALESCE(item2name, ''), +COALESCE(item3name, ''), +COALESCE(item4name, ''), +COALESCE(item5name, ''), +COALESCE(item6name, ''), +COALESCE(item7name, ''), +COALESCE(item8name, ''), +COALESCE(item9name, ''), +COALESCE(equip0name, ''), +COALESCE(equip1name, ''), +COALESCE(equip2name, ''), +COALESCE(equip3name, ''), +COALESCE(equip4name, ''), +COALESCE(equip5name, ''), +COALESCE(equip6name, ''), +COALESCE(equip7name, ''), +COALESCE(equip8name, ''), +COALESCE(equip9name, '') +FROM warehouse +` + func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateInterior) _, err := s.server.db.Exec("UPDATE characters SET house=$1 WHERE id=$2", pkt.InteriorData, s.charID) @@ -341,8 +367,74 @@ func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfOperateWarehouse) + bf := byteframe.NewByteFrame() + bf.WriteUint8(pkt.Operation) + switch pkt.Operation { + case 0: + var count uint8 + itemNames := make([]string, 10) + equipNames := make([]string, 10) + s.server.db.QueryRow(fmt.Sprintf("%s WHERE character_id=$1", warehouseNamesQuery), s.charID).Scan(&itemNames[0], + &itemNames[1], &itemNames[2], &itemNames[3], &itemNames[4], &itemNames[5], &itemNames[6], &itemNames[7], &itemNames[8], &itemNames[9], &equipNames[0], + &equipNames[1], &equipNames[2], &equipNames[3], &equipNames[4], &equipNames[5], &equipNames[6], &equipNames[7], &equipNames[8], &equipNames[9]) + bf.WriteUint32(0) + bf.WriteUint16(1000) // Usages + temp := byteframe.NewByteFrame() + for i, name := range itemNames { + if len(name) > 0 { + count++ + temp.WriteUint8(0) + temp.WriteUint8(uint8(i)) + ps.Uint8(temp, name, true) + } + } + for i, name := range equipNames { + if len(name) > 0 { + count++ + temp.WriteUint8(1) + temp.WriteUint8(uint8(i)) + ps.Uint8(temp, name, true) + } + } + bf.WriteUint8(count) + bf.WriteBytes(temp.Data()) + case 1: + bf.WriteUint8(0) + case 2: + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%dname=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), pkt.Name, s.charID) + case 3: + var t int + err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t) + if err != nil { + s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID) + } + bf.WriteUint32(0) + bf.WriteUint16(1000) // Usages + case 4: + bf.WriteUint32(0) + bf.WriteUint16(1000) // Usages + bf.WriteUint8(0) + } + // Opcodes + // 0 = Get box names + // 1 = Commit usage + // 2 = Rename + // 3 = Get usage limit + // 4 = Get gift box names (doesn't do anything?) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} -func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse) + bf := byteframe.NewByteFrame() + bf.WriteUint16(0) // numStacks + bf.WriteUint16(0) // Unk + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} -func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} From b7d41c1c7f0e829095095861148aa48134c476e0 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 15 Aug 2022 20:09:19 +1000 Subject: [PATCH 44/86] warehouse item updates --- .../mhfpacket/msg_mhf_enumerate_warehouse.go | 10 ++- network/mhfpacket/msg_mhf_update_warehouse.go | 24 ++++--- server/channelserver/handlers_house.go | 72 +++++++++++++++++-- 3 files changed, 91 insertions(+), 15 deletions(-) diff --git a/network/mhfpacket/msg_mhf_enumerate_warehouse.go b/network/mhfpacket/msg_mhf_enumerate_warehouse.go index 7a5e76a7b..3f1358045 100644 --- a/network/mhfpacket/msg_mhf_enumerate_warehouse.go +++ b/network/mhfpacket/msg_mhf_enumerate_warehouse.go @@ -11,7 +11,7 @@ import ( // MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE type MsgMhfEnumerateWarehouse struct { AckHandle uint32 - BoxType uint8 + BoxType string BoxIndex uint8 } @@ -23,7 +23,13 @@ func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.BoxType = bf.ReadUint8() + boxType := bf.ReadUint8() + switch boxType { + case 0: + m.BoxType = "item" + case 1: + m.BoxType = "equip" + } m.BoxIndex = bf.ReadUint8() _ = bf.ReadUint16() return nil diff --git a/network/mhfpacket/msg_mhf_update_warehouse.go b/network/mhfpacket/msg_mhf_update_warehouse.go index 884ed4119..b6777b8d6 100644 --- a/network/mhfpacket/msg_mhf_update_warehouse.go +++ b/network/mhfpacket/msg_mhf_update_warehouse.go @@ -2,25 +2,24 @@ package mhfpacket import ( "errors" - "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" ) -type UpdatedStack struct { +type WarehouseStack struct { ID uint32 Index uint16 ItemID uint16 Quantity uint16 - Unk uint16 } // MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE type MsgMhfUpdateWarehouse struct { AckHandle uint32 - BoxID uint16 - Items []UpdatedStack + BoxType string + BoxIndex uint8 + Updates []WarehouseStack } // Opcode returns the ID associated with this packet type. @@ -31,16 +30,23 @@ func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.BoxID = bf.ReadUint16() + boxType := bf.ReadUint8() + switch boxType { + case 0: + m.BoxType = "item" + case 1: + m.BoxType = "equip" + } + m.BoxIndex = bf.ReadUint8() changes := int(bf.ReadUint16()) - var stackUpdate UpdatedStack + var stackUpdate WarehouseStack for i := 0; i < changes; i++ { stackUpdate.ID = bf.ReadUint32() stackUpdate.Index = bf.ReadUint16() stackUpdate.ItemID = bf.ReadUint16() stackUpdate.Quantity = bf.ReadUint16() - stackUpdate.Unk = bf.ReadUint16() - m.Items = append(m.Items, stackUpdate) + _ = bf.ReadUint16() // Unk + m.Updates = append(m.Updates, stackUpdate) } _ = bf.ReadUint16() return nil diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index b7bdd78bf..1059b7a72 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -426,15 +426,79 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.WarehouseStack { + var data []byte + s.server.db.QueryRow(fmt.Sprintf("SELECT %s%d FROM warehouse WHERE character_id=$1", boxType, boxIndex), s.charID).Scan(&data) + if len(data) > 0 { + box := byteframe.NewByteFrameFromBytes(data) + numStacks := box.ReadUint16() + stacks := make([]mhfpacket.WarehouseStack, numStacks) + for i := 0; i < int(numStacks); i++ { + stacks[i].ID = box.ReadUint32() + stacks[i].Index = box.ReadUint16() + stacks[i].ItemID = box.ReadUint16() + stacks[i].Quantity = box.ReadUint16() + box.ReadUint16() + } + return stacks + } else { + return make([]mhfpacket.WarehouseStack, 0) + } +} + +func boxToBytes(stacks []mhfpacket.WarehouseStack) []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(len(stacks))) + for _, stack := range stacks { + bf.WriteUint32(stack.ID) + bf.WriteUint16(stack.Index) + bf.WriteUint16(stack.ItemID) + bf.WriteUint16(stack.Quantity) + bf.WriteUint16(0) + } + bf.WriteUint16(0) + return bf.Data() +} + func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse) - bf := byteframe.NewByteFrame() - bf.WriteUint16(0) // numStacks - bf.WriteUint16(0) // Unk - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex) + if len(box) > 0 { + doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box)) + } else { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + } } func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse) + box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex) + // Update existing stacks + var newStacks []mhfpacket.WarehouseStack + for _, update := range pkt.Updates { + exists := false + for i, stack := range box { + if stack.ItemID == update.ItemID { + box[i].Quantity = update.Quantity + exists = true + break + } + } + if exists == false { + newStacks = append(newStacks, update) + } + } + // Append new stacks + for _, stack := range newStacks { + box = append(box, stack) + } + // Slice empty stacks + var cleanedBox []mhfpacket.WarehouseStack + for _, stack := range box { + if stack.Quantity > 0 { + cleanedBox = append(cleanedBox, stack) + } + } + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox), s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } From 81e40b988d38d80b53db3bfe9f38fc5e5e9c4de6 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 15 Aug 2022 21:53:00 +1000 Subject: [PATCH 45/86] warehouse equipment updates --- network/mhfpacket/msg_mhf_update_warehouse.go | 32 +++++--- server/channelserver/handlers_house.go | 74 +++++++++++++------ 2 files changed, 75 insertions(+), 31 deletions(-) diff --git a/network/mhfpacket/msg_mhf_update_warehouse.go b/network/mhfpacket/msg_mhf_update_warehouse.go index b6777b8d6..962906988 100644 --- a/network/mhfpacket/msg_mhf_update_warehouse.go +++ b/network/mhfpacket/msg_mhf_update_warehouse.go @@ -8,10 +8,12 @@ import ( ) type WarehouseStack struct { - ID uint32 - Index uint16 - ItemID uint16 - Quantity uint16 + ID uint32 + Index uint16 + EquipType uint16 + ItemID uint16 + Quantity uint16 + Data []byte } // MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE @@ -41,12 +43,22 @@ func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cl changes := int(bf.ReadUint16()) var stackUpdate WarehouseStack for i := 0; i < changes; i++ { - stackUpdate.ID = bf.ReadUint32() - stackUpdate.Index = bf.ReadUint16() - stackUpdate.ItemID = bf.ReadUint16() - stackUpdate.Quantity = bf.ReadUint16() - _ = bf.ReadUint16() // Unk - m.Updates = append(m.Updates, stackUpdate) + switch boxType { + case 0: + stackUpdate.ID = bf.ReadUint32() + stackUpdate.Index = bf.ReadUint16() + stackUpdate.ItemID = bf.ReadUint16() + stackUpdate.Quantity = bf.ReadUint16() + _ = bf.ReadUint16() // Unk + m.Updates = append(m.Updates, stackUpdate) + case 1: + stackUpdate.ID = bf.ReadUint32() + stackUpdate.Index = bf.ReadUint16() + stackUpdate.EquipType = bf.ReadUint16() + stackUpdate.ItemID = bf.ReadUint16() + stackUpdate.Data = bf.ReadBytes(56) + m.Updates = append(m.Updates, stackUpdate) + } } _ = bf.ReadUint16() return nil diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 1059b7a72..4e6af6947 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -434,11 +434,19 @@ func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.War numStacks := box.ReadUint16() stacks := make([]mhfpacket.WarehouseStack, numStacks) for i := 0; i < int(numStacks); i++ { - stacks[i].ID = box.ReadUint32() - stacks[i].Index = box.ReadUint16() - stacks[i].ItemID = box.ReadUint16() - stacks[i].Quantity = box.ReadUint16() - box.ReadUint16() + if boxType == "item" { + stacks[i].ID = box.ReadUint32() + stacks[i].Index = box.ReadUint16() + stacks[i].ItemID = box.ReadUint16() + stacks[i].Quantity = box.ReadUint16() + box.ReadUint16() + } else { + stacks[i].ID = box.ReadUint32() + stacks[i].Index = box.ReadUint16() + stacks[i].EquipType = box.ReadUint16() + stacks[i].ItemID = box.ReadUint16() + stacks[i].Data = box.ReadBytes(56) + } } return stacks } else { @@ -446,15 +454,23 @@ func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.War } } -func boxToBytes(stacks []mhfpacket.WarehouseStack) []byte { +func boxToBytes(stacks []mhfpacket.WarehouseStack, boxType string) []byte { bf := byteframe.NewByteFrame() bf.WriteUint16(uint16(len(stacks))) - for _, stack := range stacks { - bf.WriteUint32(stack.ID) - bf.WriteUint16(stack.Index) - bf.WriteUint16(stack.ItemID) - bf.WriteUint16(stack.Quantity) - bf.WriteUint16(0) + for i, stack := range stacks { + if boxType == "item" { + bf.WriteUint32(stack.ID) + bf.WriteUint16(uint16(i + 1)) + bf.WriteUint16(stack.ItemID) + bf.WriteUint16(stack.Quantity) + bf.WriteUint16(0) + } else { + bf.WriteUint32(stack.ID) + bf.WriteUint16(uint16(i + 1)) + bf.WriteUint16(stack.EquipType) + bf.WriteUint16(stack.ItemID) + bf.WriteBytes(stack.Data) + } } bf.WriteUint16(0) return bf.Data() @@ -464,7 +480,7 @@ func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse) box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex) if len(box) > 0 { - doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box)) + doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box, pkt.BoxType)) } else { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -477,11 +493,21 @@ func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) { var newStacks []mhfpacket.WarehouseStack for _, update := range pkt.Updates { exists := false - for i, stack := range box { - if stack.ItemID == update.ItemID { - box[i].Quantity = update.Quantity - exists = true - break + if pkt.BoxType == "item" { + for i, stack := range box { + if stack.Index == update.Index { + exists = true + box[i].Quantity = update.Quantity + break + } + } + } else { + for i, stack := range box { + if stack.Index == update.Index { + exists = true + box[i].ItemID = update.ItemID + break + } } } if exists == false { @@ -495,10 +521,16 @@ func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) { // Slice empty stacks var cleanedBox []mhfpacket.WarehouseStack for _, stack := range box { - if stack.Quantity > 0 { - cleanedBox = append(cleanedBox, stack) + if pkt.BoxType == "item" { + if stack.Quantity > 0 { + cleanedBox = append(cleanedBox, stack) + } + } else { + if stack.ItemID != 0 { + cleanedBox = append(cleanedBox, stack) + } } } - s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox), s.charID) + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox, pkt.BoxType), s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } From bc75902da126b3b3e7172ee2f4daa1bd82fc1990 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 16 Aug 2022 01:39:53 +1000 Subject: [PATCH 46/86] prevent nil warehouse error --- server/channelserver/handlers_house.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 4e6af6947..b4b97e3fa 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -369,6 +369,11 @@ func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfOperateWarehouse) + var t int + err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t) + if err != nil { + s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID) + } bf := byteframe.NewByteFrame() bf.WriteUint8(pkt.Operation) switch pkt.Operation { @@ -380,7 +385,7 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { &itemNames[1], &itemNames[2], &itemNames[3], &itemNames[4], &itemNames[5], &itemNames[6], &itemNames[7], &itemNames[8], &itemNames[9], &equipNames[0], &equipNames[1], &equipNames[2], &equipNames[3], &equipNames[4], &equipNames[5], &equipNames[6], &equipNames[7], &equipNames[8], &equipNames[9]) bf.WriteUint32(0) - bf.WriteUint16(1000) // Usages + bf.WriteUint16(10000) // Usages temp := byteframe.NewByteFrame() for i, name := range itemNames { if len(name) > 0 { @@ -405,16 +410,11 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { case 2: s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%dname=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), pkt.Name, s.charID) case 3: - var t int - err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t) - if err != nil { - s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID) - } - bf.WriteUint32(0) - bf.WriteUint16(1000) // Usages + bf.WriteUint32(0) // Usage renewal time, >1 = disabled + bf.WriteUint16(10000) // Usages case 4: bf.WriteUint32(0) - bf.WriteUint16(1000) // Usages + bf.WriteUint16(10000) // Usages bf.WriteUint8(0) } // Opcodes From 5aa386a92c8d85e3ef40131e92c1e7449aca4df7 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 16 Aug 2022 13:06:16 +1000 Subject: [PATCH 47/86] add player reload command --- server/channelserver/handlers_cast_binary.go | 60 ++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index bf0f2f105..0d8a5495b 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -165,6 +165,66 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { fmt.Printf("Got chat message: %+v\n", chatMessage) + // Flush all objects and users and reload + if strings.HasPrefix(chatMessage.Message, "!reload") { + sendServerChatMessage(s, "Reloading players...") + var temp mhfpacket.MHFPacket + deleteNotif := byteframe.NewByteFrame() + for _, object := range s.stage.objects { + if object.ownerCharID == s.charID { + continue + } + temp = &mhfpacket.MsgSysDeleteObject{ObjID: object.id} + deleteNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(deleteNotif, s.clientContext) + } + for _, session := range s.server.sessions { + if s == session { + continue + } + temp = &mhfpacket.MsgSysDeleteUser{CharID: session.charID} + deleteNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(deleteNotif, s.clientContext) + } + deleteNotif.WriteUint16(0x0010) + s.QueueSend(deleteNotif.Data()) + time.Sleep(500 * time.Millisecond) + reloadNotif := byteframe.NewByteFrame() + for _, session := range s.server.sessions { + if s == session { + continue + } + temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID} + reloadNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(reloadNotif, s.clientContext) + for i := 0; i < 3; i++ { + temp = &mhfpacket.MsgSysNotifyUserBinary{ + CharID: session.charID, + BinaryType: uint8(i + 1), + } + reloadNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(reloadNotif, s.clientContext) + } + } + for _, obj := range s.stage.objects { + if obj.ownerCharID == s.charID { + continue + } + temp = &mhfpacket.MsgSysDuplicateObject{ + ObjID: obj.id, + X: obj.x, + Y: obj.y, + Z: obj.z, + Unk0: 0, + OwnerCharID: obj.ownerCharID, + } + reloadNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(reloadNotif, s.clientContext) + } + reloadNotif.WriteUint16(0x0010) + s.QueueSend(reloadNotif.Data()) + } + // Set account rights if strings.HasPrefix(chatMessage.Message, "!rights") { var v uint32 From ed3687548c1130cdfe39ded0e02445664d7785b6 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 16 Aug 2022 21:03:30 +1000 Subject: [PATCH 48/86] prevent CSVs from adding existing values --- common/stringsupport/string_convert.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/stringsupport/string_convert.go b/common/stringsupport/string_convert.go index 84574375b..ab91311ac 100644 --- a/common/stringsupport/string_convert.go +++ b/common/stringsupport/string_convert.go @@ -120,7 +120,11 @@ func CSVAdd(csv string, v int) string { if len(csv) == 0 { return strconv.Itoa(v) } - return csv + "," + strconv.Itoa(v) + if CSVContains(csv, v) { + return csv + } else { + return csv + "," + strconv.Itoa(v) + } } func CSVRemove(csv string, v int) string { From 602cf31ed5cfc083f69c58b6d61f1c5ac71502fb Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 00:41:22 +1000 Subject: [PATCH 49/86] stub OperationInvGuild packets --- .../mhfpacket/msg_mhf_operation_inv_guild.go | 25 ++++++++++++++----- server/channelserver/handlers_guild.go | 5 +++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/network/mhfpacket/msg_mhf_operation_inv_guild.go b/network/mhfpacket/msg_mhf_operation_inv_guild.go index b07311c9d..bcd0b45b8 100644 --- a/network/mhfpacket/msg_mhf_operation_inv_guild.go +++ b/network/mhfpacket/msg_mhf_operation_inv_guild.go @@ -1,15 +1,22 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfOperationInvGuild represents the MSG_MHF_OPERATION_INV_GUILD -type MsgMhfOperationInvGuild struct{} +type MsgMhfOperationInvGuild struct { + AckHandle uint32 + Operation uint8 + ActiveHours uint8 + DaysActive uint8 + PlayStyle uint8 + GuildRequest uint8 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfOperationInvGuild) Opcode() network.PacketID { @@ -18,7 +25,13 @@ func (m *MsgMhfOperationInvGuild) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfOperationInvGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Operation = bf.ReadUint8() + m.ActiveHours = bf.ReadUint8() + m.DaysActive = bf.ReadUint8() + m.PlayStyle = bf.ReadUint8() + m.GuildRequest = bf.ReadUint8() + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 2dd84f659..0bcfdfcbd 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1902,6 +1902,9 @@ func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) { stubEnumerateNoResults(s, pkt.AckHandle) } -func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfOperationInvGuild) + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) +} func handleMsgMhfUpdateGuildcard(s *Session, p mhfpacket.MHFPacket) {} From 89683e6063b02c472a9b40e4ba2561be4d8fcd1a Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 04:55:42 +1000 Subject: [PATCH 50/86] further TransitMessage decoding --- server/channelserver/handlers.go | 60 ++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 5af8abb20..28164b103 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -423,6 +423,9 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { } for session := range stage.clients { count++ + hrp := uint16(1) + gr := uint16(0) + s.server.db.QueryRow("SELECT hrp, gr FROM characters WHERE id=$1", session.charID).Scan(&hrp, &gr) sessionStage := stringsupport.UTF8ToSJIS(session.stageID) sessionName := stringsupport.UTF8ToSJIS(session.Name) resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) @@ -435,8 +438,8 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { resp.WriteBytes(make([]byte, 48)) resp.WriteNullTerminatedBytes(sessionStage) resp.WriteNullTerminatedBytes(sessionName) - resp.WriteUint16(999) // HR - resp.WriteUint16(999) // GR + resp.WriteUint16(hrp) + resp.WriteUint16(gr) resp.WriteBytes([]byte{0x06, 0x10, 0x00}) // Unk } } @@ -445,12 +448,23 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { } case 4: // Find Party bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) - bf.ReadUint8() + setting := bf.ReadUint8() maxResults := bf.ReadUint16() - bf.ReadUint8() - bf.ReadUint8() + bf.Seek(2, 1) partyType := bf.ReadUint16() - _ = bf.DataFromCurrent() // Restrictions + rankRestriction := uint16(0) + if setting >= 2 { + bf.Seek(2, 1) + rankRestriction = bf.ReadUint16() + } + targets := make([]uint16, 4) + if setting >= 3 { + bf.Seek(1, 1) + lenTargets := int(bf.ReadUint8()) + for i := 0; i < lenTargets; i++ { + targets[i] = bf.ReadUint16() + } + } var stagePrefix string switch partyType { case 0: // Public Bar @@ -470,31 +484,41 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { break } if strings.HasPrefix(stage.id, stagePrefix) { + sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}]) + sb3.Seek(4, 0) + stageRankRestriction := sb3.ReadUint16() + stageTarget := sb3.ReadUint16() + if rankRestriction != 0xFFFF && stageRankRestriction < rankRestriction { + continue + } count++ sessionStage := stringsupport.UTF8ToSJIS(stage.id) resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) resp.WriteUint16(c.Port) - - // TODO: This is half right, could be trimmed - resp.WriteUint16(0) - resp.WriteUint16(uint16(len(stage.clients))) + resp.WriteUint16(0) // Static? + resp.WriteUint16(0) // Unk resp.WriteUint16(uint16(len(stage.clients))) resp.WriteUint16(stage.maxPlayers) - resp.WriteUint16(0) - resp.WriteUint16(uint16(len(stage.clients))) - // - - resp.WriteUint16(uint16(len(sessionStage) + 1)) - resp.WriteUint8(1) + resp.WriteUint16(0) // Num clients entered from stage + resp.WriteUint16(stage.maxPlayers) + resp.WriteUint8(1) // Static? + resp.WriteUint8(uint8(len(sessionStage) + 1)) + resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 0}]))) resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}]))) - resp.WriteBytes(make([]byte, 16)) + resp.WriteUint16(stageRankRestriction) + resp.WriteUint16(stageTarget) + resp.WriteBytes(make([]byte, 12)) resp.WriteNullTerminatedBytes(sessionStage) - resp.WriteBytes([]byte{0x00}) + resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 0}]) resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}]) } } } } + if (pkt.SearchType == 1 || pkt.SearchType == 3) && count == 0 { + doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) + return + } resp.Seek(0, io.SeekStart) resp.WriteUint16(count) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) From 3c099acec405a316179320827779fe7a0191bf52 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 05:07:37 +1000 Subject: [PATCH 51/86] update launcher links --- www/erupe/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/erupe/index.html b/www/erupe/index.html index 57aa1c568..0b130c684 100644 --- a/www/erupe/index.html +++ b/www/erupe/index.html @@ -118,11 +118,11 @@
-
+
From 97c57762f03bd53d43ff6cad591c5745909f2752 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 17:04:13 +1000 Subject: [PATCH 52/86] add query to reset savemercenary --- patch-schema/mercenary.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/patch-schema/mercenary.sql b/patch-schema/mercenary.sql index f67e4fe4e..9000db9f3 100644 --- a/patch-schema/mercenary.sql +++ b/patch-schema/mercenary.sql @@ -2,4 +2,6 @@ BEGIN; CREATE SEQUENCE IF NOT EXISTS public.rasta_id_seq; +UPDATE characters SET savemercenary=NULL; + END; \ No newline at end of file From 14d3b3743516dfd230e4efb9cca1ae76aef7e306 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 23:29:16 +1000 Subject: [PATCH 53/86] discord bot cleanup --- config.json | 5 +- main.go | 3 +- server/channelserver/handlers_discord.go | 346 +++------------------ server/channelserver/sys_channel_server.go | 21 +- server/channelserver/sys_stage.go | 29 -- server/discordbot/discord_bot.go | 38 +-- 6 files changed, 54 insertions(+), 388 deletions(-) diff --git a/config.json b/config.json index 29af1fb8d..67087d28c 100644 --- a/config.json +++ b/config.json @@ -28,10 +28,7 @@ "discord": { "enabled": false, "bottoken": "", - "realtimeChannelID": "", - "serverId": "", - "devRoles": [], - "devMode": false + "realtimeChannelID": "" }, "database": { "host": "localhost", diff --git a/main.go b/main.go index c3c0162e0..2bc598a06 100644 --- a/main.go +++ b/main.go @@ -65,7 +65,7 @@ func main() { var discordBot *discordbot.DiscordBot = nil if erupeConfig.Discord.Enabled { - bot, err := discordbot.NewDiscordBot(discordbot.DiscordBotOptions{ + bot, err := discordbot.NewDiscordBot(discordbot.Options{ Logger: logger, Config: erupeConfig, }) @@ -82,6 +82,7 @@ func main() { } discordBot = bot + logger.Info("Discord bot is enabled") } else { logger.Info("Discord bot is disabled") } diff --git a/server/channelserver/handlers_discord.go b/server/channelserver/handlers_discord.go index 38619e079..f9290908b 100644 --- a/server/channelserver/handlers_discord.go +++ b/server/channelserver/handlers_discord.go @@ -2,87 +2,45 @@ package channelserver import ( "fmt" + "github.com/bwmarrin/discordgo" "sort" "strings" - - "github.com/bwmarrin/discordgo" + "unicode" ) -type Character struct { - ID uint32 `db:"id"` - IsFemale bool `db:"is_female"` - IsNewCharacter bool `db:"is_new_character"` - Name string `db:"name"` - UnkDescString string `db:"unk_desc_string"` - HRP uint16 `db:"hrp"` - GR uint16 `db:"gr"` - WeaponType uint16 `db:"weapon_type"` - LastLogin uint32 `db:"last_login"` +type Player struct { + CharName string + QuestID int } -var weapons = []string{ - "<:gs:970861408227049477>", - "<:hbg:970861408281563206>", - "<:hm:970861408239628308>", - "<:lc:970861408298352660>", - "<:sns:970861408319315988>", - "<:lbg:970861408327725137>", - "<:ds:970861408277368883>", - "<:ls:970861408319311872>", - "<:hh:970861408222863400>", - "<:gl:970861408327720980>", - "<:bw:970861408294174780>", - "<:tf:970861408424177744>", - "<:sw:970861408340283472>", - "<:ms:970861408411594842>", -} +func getPlayerSlice(s *Server) []Player { + var p []Player + var questIndex int -func (s *Server) getCharacterForUser(uid int) (*Character, error) { - character := Character{} - err := s.db.Get(&character, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE id = $1", uid) - if err != nil { - return nil, err + for _, channel := range s.Channels { + for _, stage := range channel.stages { + if len(stage.clients) == 0 { + continue + } + questID := 0 + if stage.isQuest() { + questIndex++ + questID = questIndex + } + for client := range stage.clients { + p = append(p, Player{ + CharName: client.Name, + QuestID: questID, + }) + } + } } - - return &character, nil + return p } -func CountChars(s *Server) string { - count := 0 - for _, stage := range s.stages { - count += len(stage.clients) - } - - message := fmt.Sprintf("Server [%s]: %d players;", s.name, count) - - return message -} - -type ListPlayer struct { - CharName string - InQuest bool - WeaponEmoji string - QuestEmoji string - StageName string -} - -func (p *ListPlayer) toString(length int) string { - status := "" - if p.InQuest { - status = "(in quest " + p.QuestEmoji + ")" - } else { - status = p.StageName - } - - missingSpace := length - len(p.CharName) - whitespaces := strings.Repeat(" ", missingSpace+5) - - return fmt.Sprintf("%s %s %s %s", p.WeaponEmoji, p.CharName, whitespaces, status) -} - -func getPlayerList(s *Server) ([]ListPlayer, int) { - list := []ListPlayer{} +func getCharacterList(s *Server) string { questEmojis := []string{ + ":person_in_lotus_position:", ":white_circle:", ":red_circle:", ":blue_circle:", @@ -91,255 +49,41 @@ func getPlayerList(s *Server) ([]ListPlayer, int) { ":purple_circle:", ":yellow_circle:", ":orange_circle:", + ":black_circle:", } - bigNameLen := 0 + playerSlice := getPlayerSlice(s) - for _, stage := range s.stages { - if len(stage.clients) == 0 { - continue - } - - questEmoji := ":black_circle:" - - if len(questEmojis) > 0 { - questEmoji = questEmojis[len(questEmojis)-1] - questEmojis = questEmojis[:len(questEmojis)-1] - } - - isQuest := stage.isQuest() - for client := range stage.clients { - char, err := s.getCharacterForUser(int(client.charID)) - if err == nil { - if len(char.Name) > bigNameLen { - bigNameLen = len(char.Name) - } - - list = append(list, ListPlayer{ - CharName: char.Name, - InQuest: isQuest, - QuestEmoji: questEmoji, - WeaponEmoji: weapons[char.WeaponType], - StageName: stage.GetName(), - }) - - } - } - } - - return list, bigNameLen -} - -func PlayerList(s *Server) string { - list := "" - count := 0 - listPlayers, bigNameLen := getPlayerList(s) - - sort.SliceStable(listPlayers, func(i, j int) bool { - return listPlayers[i].CharName < listPlayers[j].CharName + sort.SliceStable(playerSlice, func(i, j int) bool { + return playerSlice[i].QuestID < playerSlice[j].QuestID }) - for _, lp := range listPlayers { - list += lp.toString(bigNameLen) + "\n" - count++ + message := fmt.Sprintf("===== Online: %d =====\n", len(playerSlice)) + for _, player := range playerSlice { + message += fmt.Sprintf("%s %s", questEmojis[player.QuestID], player.CharName) } - message := fmt.Sprintf("<:SnS:822963937360347148> Frontier Hunters Online: [%s ] <:switcha:822963906401533992> \n============== Total %d =============\n", s.name, count) - message += list - return message } -func debug(s *Server) string { - list := "" - - for _, stage := range s.stages { - if !stage.isQuest() && len(stage.objects) == 0 { - continue - } - - list += fmt.Sprintf(" -> Stage: %s StageId: %s\n", stage.GetName(), stage.id) - isQuest := "false" - hasDeparted := "false" - - if stage.isQuest() { - isQuest = "true" - } - - list += fmt.Sprintf(" '-> isQuest: %s\n", isQuest) - - if stage.isQuest() { - if len(stage.clients) > 0 { - hasDeparted = "true" - } - - list += fmt.Sprintf(" '-> isDeparted: %s\n", hasDeparted) - list += fmt.Sprintf(" '-> reserveSlots (%d/%d)\n", len(stage.reservedClientSlots), stage.maxPlayers) - - for charid, _ := range stage.reservedClientSlots { - char, err := s.getCharacterForUser(int(charid)) - if err == nil { - list += fmt.Sprintf(" '-> %s\n", char.Name) - } - } - } - - list += " '-> objects: \n" - for _, obj := range stage.objects { - objInfo := fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z) - list += fmt.Sprintf(" '-> ObjectId: %d - %s\n", obj.id, objInfo) - } - } - - message := fmt.Sprintf("Objects in Server: [%s ]\n", s.name) - message += list - - return message -} - -func questlist(s *Server) string { - list := "" - - for _, stage := range s.stages { - if !stage.isQuest() { - continue - } - - hasDeparted := "" - if len(stage.clients) > 0 { - hasDeparted = " - departed" - } - list += fmt.Sprintf(" '-> StageId: %s (%d/%d) %s - %s\n", stage.id, len(stage.reservedClientSlots), stage.maxPlayers, hasDeparted, stage.createdAt) - - for charid, _ := range stage.reservedClientSlots { - char, err := s.getCharacterForUser(int(charid)) - if err == nil { - list += fmt.Sprintf(" '-> %s\n", char.Name) - } - } - } - - message := fmt.Sprintf("Quests in Server: [%s ]\n", s.name) - message += list - - return message -} - -func removeStageById(s *Server, stageId string) string { - if s.stages[stageId] != nil { - delete(s.stages, stageId) - return "Stage deleted!" - } - - return "Stage not found!" -} - -func cleanStr(str string) string { - return strings.ToLower(strings.Trim(str, " ")) -} - -func getCharInfo(server *Server, charName string) string { - var s *Stage - var c *Session - - for _, stage := range server.stages { - for client := range stage.clients { - - if client.Name == "" { - continue - } - - if cleanStr(client.Name) == cleanStr(charName) { - s = stage - c = client - } - - } - } - - if s == nil { - return "Character not found" - } - - objInfo := "" - - obj := server.FindObjectByChar(c.charID) - // server.logger.Info("Found object: %+v", zap.Object("obj", obj)) - - if obj != nil { - objInfo = fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z) - } - - return fmt.Sprintf("Character: %s\nStage: %s\nStageId: %s\n%s", c.Name, s.GetName(), s.id, objInfo) -} - -func (s *Server) isDiscordAdmin(ds *discordgo.Session, m *discordgo.MessageCreate) bool { - for _, role := range m.Member.Roles { - for _, id := range s.erupeConfig.Discord.DevRoles { - if id == role { - return true - } - } - } - - return false -} - // onDiscordMessage handles receiving messages from discord and forwarding them ingame. func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) { // Ignore messages from our bot, or ones that are not in the correct channel. - if m.Author.ID == ds.State.User.ID || !s.enable { + if m.Author.Bot || m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID { return } - // Ignore other channels in devMode - if s.erupeConfig.Discord.DevMode && m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID { - return - } - - args := strings.Split(m.Content, " ") - commandName := args[0] - - // Move to slash commadns - if commandName == "!players" { - ds.ChannelMessageSend(m.ChannelID, PlayerList(s)) - return - } - - if commandName == "-char" { - if len(args) < 2 { - ds.ChannelMessageSend(m.ChannelID, "Usage: !char ") - return + paddedName := strings.TrimSpace(strings.Map(func(r rune) rune { + if r > unicode.MaxASCII { + return -1 } + return r + }, m.Author.Username)) - charName := strings.Join(args[1:], " ") - ds.ChannelMessageSend(m.ChannelID, getCharInfo(s, charName)) - return + for i := 0; i < 10-len(m.Author.Username); i++ { + paddedName += " " } - if commandName == "!debug" && s.isDiscordAdmin(ds, m) { - ds.ChannelMessageSend(m.ChannelID, debug(s)) - return - } - - if commandName == "!questlist" && s.isDiscordAdmin(ds, m) { - ds.ChannelMessageSend(m.ChannelID, questlist(s)) - return - } - - if commandName == "!remove-stage" && s.isDiscordAdmin(ds, m) { - if len(args) < 2 { - ds.ChannelMessageSend(m.ChannelID, "Usage: !remove-stage ") - return - } - - stageId := strings.Join(args[1:], " ") - ds.ChannelMessageSend(m.ChannelID, removeStageById(s, stageId)) - return - } - - if m.ChannelID == s.erupeConfig.Discord.RealtimeChannelID { - message := fmt.Sprintf("[DISCORD] %s: %s", m.Author.Username, m.Content) - s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message)) - } + message := fmt.Sprintf("[D] %s > %s", paddedName, m.Content) + s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message)) } diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index ca9063baf..8702d056a 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -17,21 +17,6 @@ import ( type StageIdType = string -const ( - // GlobalStage is the stage that is used for all users. - MezeportaStageId StageIdType = "sl1Ns200p0a0u0" - GuildHallLv1StageId StageIdType = "sl1Ns202p0a0u0" - GuildHallLv2StageId StageIdType = "sl1Ns203p0a0u0" - GuildHallLv3StageId StageIdType = "sl1Ns204p0a0u0" - PugiFarmStageId StageIdType = "sl1Ns205p0a0u0" - RastaBarStageId StageIdType = "sl1Ns211p0a0u0" - PalloneCaravanStageId StageIdType = "sl1Ns260p0a0u0" - GookFarmStageId StageIdType = "sl1Ns265p0a0u0" - DivaFountainStageId StageIdType = "sl2Ns379p0a0u0" - DivaHallStageId StageIdType = "sl1Ns445p0a0u0" - MezFesStageId StageIdType = "sl1Ns462p0a0u0" -) - // Config struct allows configuring the server. type Config struct { ID uint16 @@ -80,8 +65,7 @@ type Server struct { // Discord chat integration discordBot *discordbot.DiscordBot - name string - enable bool + name string raviente *Raviente } @@ -157,7 +141,6 @@ func NewServer(config *Config) *Server { semaphoreIndex: 7, discordBot: config.DiscordBot, name: config.Name, - enable: config.Enable, raviente: NewRaviente(), } @@ -367,7 +350,7 @@ func (s *Server) BroadcastRaviente(ip uint32, port uint16, stage []byte, _type u func (s *Server) DiscordChannelSend(charName string, content string) { if s.erupeConfig.Discord.Enabled && s.discordBot != nil { - message := fmt.Sprintf("**%s** : %s", charName, content) + message := fmt.Sprintf("**%s**: %s", charName, content) s.discordBot.RealtimeChannelSend(message) } } diff --git a/server/channelserver/sys_stage.go b/server/channelserver/sys_stage.go index 9a968bf3f..827b2d6be 100644 --- a/server/channelserver/sys_stage.go +++ b/server/channelserver/sys_stage.go @@ -98,35 +98,6 @@ func (s *Stage) isQuest() bool { return len(s.reservedClientSlots) > 0 } -func (s *Stage) GetName() string { - switch s.id { - case MezeportaStageId: - return "Mezeporta" - case GuildHallLv1StageId: - return "Guild Hall Lv1" - case GuildHallLv2StageId: - return "Guild Hall Lv2" - case GuildHallLv3StageId: - return "Guild Hall Lv3" - case PugiFarmStageId: - return "Pugi Farm" - case RastaBarStageId: - return "Rasta Bar" - case PalloneCaravanStageId: - return "Pallone Caravan" - case GookFarmStageId: - return "Gook Farm" - case DivaFountainStageId: - return "Diva Fountain" - case DivaHallStageId: - return "Diva Hall" - case MezFesStageId: - return "Mez Fes" - default: - return "" - } -} - func (s *Stage) NextObjectID() uint32 { s.objectIndex = s.objectIndex + 1 // Objects beyond 127 do not duplicate correctly diff --git a/server/discordbot/discord_bot.go b/server/discordbot/discord_bot.go index df817d50a..fc5c41ce8 100644 --- a/server/discordbot/discord_bot.go +++ b/server/discordbot/discord_bot.go @@ -1,11 +1,10 @@ package discordbot import ( - "regexp" - "erupe-ce/config" "github.com/bwmarrin/discordgo" "go.uber.org/zap" + "regexp" ) type DiscordBot struct { @@ -16,12 +15,12 @@ type DiscordBot struct { RealtimeChannel *discordgo.Channel } -type DiscordBotOptions struct { +type Options struct { Config *config.Config Logger *zap.Logger } -func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error) { +func NewDiscordBot(options Options) (discordBot *DiscordBot, err error) { session, err := discordgo.New("Bot " + options.Config.Discord.BotToken) if err != nil { @@ -29,13 +28,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error return nil, err } - mainGuild, err := session.Guild(options.Config.Discord.ServerID) - - if err != nil { - options.Logger.Fatal("Discord failed to get main guild", zap.Error(err)) - return nil, err - } - realtimeChannel, err := session.Channel(options.Config.Discord.RealtimeChannelID) if err != nil { @@ -47,7 +39,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error config: options.Config, logger: options.Logger, Session: session, - MainGuild: mainGuild, RealtimeChannel: realtimeChannel, } @@ -60,21 +51,10 @@ func (bot *DiscordBot) Start() (err error) { return } -func (bot *DiscordBot) FindRoleByID(id string) *discordgo.Role { - for _, role := range bot.MainGuild.Roles { - if role.ID == id { - return role - } - } - - return nil -} - // Replace all mentions to real name from the message. func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { userRegex := regexp.MustCompile(`<@!?(\d{17,19})>`) emojiRegex := regexp.MustCompile(`(?:)?`) - roleRegex := regexp.MustCompile(`<@&(\d{17,19})>`) result := ReplaceTextAll(message, userRegex, func(userId string) string { user, err := bot.Session.User(userId) @@ -90,17 +70,7 @@ func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { return ":" + emojiName + ":" }) - result = ReplaceTextAll(result, roleRegex, func(roleId string) string { - role := bot.FindRoleByID(roleId) - - if role != nil { - return "@!" + role.Name - } - - return "@!unknown" - }) - - return string(result) + return result } func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) { From e0c50bb5d76e7e6200ba6ed564879bea71ece819 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 17 Aug 2022 23:38:09 +1000 Subject: [PATCH 54/86] correct padding on chat broadcast --- server/channelserver/handlers_discord.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_discord.go b/server/channelserver/handlers_discord.go index f9290908b..1f3398d18 100644 --- a/server/channelserver/handlers_discord.go +++ b/server/channelserver/handlers_discord.go @@ -80,7 +80,7 @@ func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCre return r }, m.Author.Username)) - for i := 0; i < 10-len(m.Author.Username); i++ { + for i := 0; i < 8-len(m.Author.Username); i++ { paddedName += " " } From a79c09d205ad0df39c3368c78311e5b1a3a3eeb6 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 18 Aug 2022 00:11:54 +1000 Subject: [PATCH 55/86] restrict local chat, enable world chat --- server/channelserver/handlers_cast_binary.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 0d8a5495b..35538e0fb 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -240,7 +240,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { } // Discord integration - if chatMessage.Type == binpacket.ChatTypeLocal || chatMessage.Type == binpacket.ChatTypeParty { + if (pkt.BroadcastType == BroadcastTypeStage && s.stage.id == "sl1Ns200p0a0u0") || pkt.BroadcastType == BroadcastTypeWorld { s.server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message) } From 56ae30745ded1a6cae935f654bb9c06536654340 Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 18 Aug 2022 00:23:48 +1000 Subject: [PATCH 56/86] remove unused static stages --- server/channelserver/sys_channel_server.go | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 8702d056a..635706d1a 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -15,8 +15,6 @@ import ( "go.uber.org/zap" ) -type StageIdType = string - // Config struct allows configuring the server. type Config struct { ID uint16 @@ -147,33 +145,15 @@ func NewServer(config *Config) *Server { // Mezeporta s.stages["sl1Ns200p0a0u0"] = NewStage("sl1Ns200p0a0u0") - // Guild Hall LV1 - s.stages["sl1Ns202p0a0u0"] = NewStage("sl1Ns202p0a0u0") - - // Guild Hall LV2 - s.stages["sl1Ns203p0a0u0"] = NewStage("sl1Ns203p0a0u0") - - // Guild Hall LV3 - s.stages["sl1Ns204p0a0u0"] = NewStage("sl1Ns204p0a0u0") - - // Pugi Farm - s.stages["sl1Ns205p0a0u0"] = NewStage("sl1Ns205p0a0u0") - // Rasta bar stage s.stages["sl1Ns211p0a0u0"] = NewStage("sl1Ns211p0a0u0") // Pallone Carvan s.stages["sl1Ns260p0a0u0"] = NewStage("sl1Ns260p0a0u0") - // Gook Farm - s.stages["sl1Ns265p0a0u0"] = NewStage("sl1Ns265p0a0u0") - // Diva fountain / prayer fountain. s.stages["sl2Ns379p0a0u0"] = NewStage("sl2Ns379p0a0u0") - // Diva Hall - s.stages["sl1Ns445p0a0u0"] = NewStage("sl1Ns445p0a0u0") - // MezFes s.stages["sl1Ns462p0a0u0"] = NewStage("sl1Ns462p0a0u0") From 1ba3d5556669679461ef03a4f9980ba336b65dcb Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 03:53:28 +1000 Subject: [PATCH 57/86] road leaderboard initial implementation --- .../msg_mhf_enumerate_rengoku_ranking.go | 18 +- patch-schema/road-leaderboard.sql | 11 + server/channelserver/handlers_rengoku.go | 203 +++++++++++++++++- 3 files changed, 216 insertions(+), 16 deletions(-) create mode 100644 patch-schema/road-leaderboard.sql diff --git a/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go b/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go index d35bc7227..21817237d 100644 --- a/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go +++ b/network/mhfpacket/msg_mhf_enumerate_rengoku_ranking.go @@ -1,19 +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" ) // MsgMhfEnumerateRengokuRanking represents the MSG_MHF_ENUMERATE_RENGOKU_RANKING type MsgMhfEnumerateRengokuRanking struct { - AckHandle uint32 - Unk0 uint32 - Unk1 uint16 // Hardcoded 0 in the binary - Unk2 uint16 // Hardcoded 00 01 in the binary + AckHandle uint32 + Leaderboard uint32 + Unk1 uint16 // Hardcoded 0 in the binary + Unk2 uint16 // Hardcoded 00 01 in the binary } // Opcode returns the ID associated with this packet type. @@ -24,7 +24,7 @@ func (m *MsgMhfEnumerateRengokuRanking) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateRengokuRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint32() + m.Leaderboard = bf.ReadUint32() m.Unk1 = bf.ReadUint16() m.Unk2 = bf.ReadUint16() return nil diff --git a/patch-schema/road-leaderboard.sql b/patch-schema/road-leaderboard.sql new file mode 100644 index 000000000..8dfea2875 --- /dev/null +++ b/patch-schema/road-leaderboard.sql @@ -0,0 +1,11 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS rengoku_score ( + character_id integer PRIMARY KEY, + max_stages_mp integer, + max_points_mp integer, + max_stages_sp integer, + max_points_sp integer +); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_rengoku.go b/server/channelserver/handlers_rengoku.go index d7b60686b..6fdce7c22 100644 --- a/server/channelserver/handlers_rengoku.go +++ b/server/channelserver/handlers_rengoku.go @@ -1,6 +1,8 @@ package channelserver import ( + ps "erupe-ce/common/pascalstring" + "fmt" "io/ioutil" "path/filepath" @@ -17,7 +19,19 @@ func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) { if err != nil { s.logger.Fatal("Failed to update rengokudata savedata in db", zap.Error(err)) } - + bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) + bf.Seek(71, 0) + maxStageMp := bf.ReadUint32() + maxScoreMp := bf.ReadUint32() + bf.Seek(4, 1) + maxStageSp := bf.ReadUint32() + maxScoreSp := bf.ReadUint32() + var t int + err = s.server.db.QueryRow("SELECT character_id FROM rengoku_score WHERE character_id=$1", s.charID).Scan(&t) + if err != nil { + s.server.db.Exec("INSERT INTO rengoku_score (character_id) VALUES ($1)", s.charID) + } + s.server.db.Exec("UPDATE rengoku_score SET max_stages_mp=$1, max_points_mp=$2, max_stages_sp=$3, max_points_sp=$4 WHERE character_id=$5", maxStageMp, maxScoreMp, maxStageSp, maxScoreSp, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } @@ -81,16 +95,191 @@ func handleMsgMhfGetRengokuBinary(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, data) } +const rengokuScoreQuery = ` +SELECT max_stages_mp, max_points_mp, max_stages_sp, max_points_sp, c.name, gc.guild_id +FROM rengoku_score rs +LEFT JOIN characters c ON c.id = rs.character_id +LEFT JOIN guild_characters gc ON gc.character_id = rs.character_id +` + +type RengokuScore struct { + Name string `db:"name"` + GuildID int `db:"guild_id"` + MaxStagesMP uint32 `db:"max_stages_mp"` + MaxPointsMP uint32 `db:"max_points_mp"` + MaxStagesSP uint32 `db:"max_stages_sp"` + MaxPointsSP uint32 `db:"max_points_sp"` +} + func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateRengokuRanking) - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + + guild, _ := GetGuildInfoByCharacterId(s, s.charID) + isApplicant, _ := guild.HasApplicationForCharID(s, s.charID) + if isApplicant { + guild = nil + } + + var score RengokuScore + i := uint32(1) + bf := byteframe.NewByteFrame() + scoreData := byteframe.NewByteFrame() + switch pkt.Leaderboard { + case 0: // Max stage overall MP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_mp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 1: // Max RdP overall MP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_mp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 2: // Max stage guild MP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_mp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + case 3: // Max RdP guild MP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_mp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsMP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsMP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + case 4: // Max stage overall SP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_sp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 5: // Max RdP overall SP + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_sp DESC", rengokuScoreQuery)) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + case 6: // Max stage guild SP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_sp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxStagesSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxStagesSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + case 7: // Max RdP guild SP + if guild != nil { + rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_sp DESC", rengokuScoreQuery), guild.ID) + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.MaxPointsSP) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.MaxPointsSP) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ + } + } else { + bf.WriteBytes(make([]byte, 11)) + } + } + bf.WriteUint8(uint8(i) - 1) + bf.WriteBytes(scoreData.Data()) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfGetRengokuRankingRank(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetRengokuRankingRank) - - resp := byteframe.NewByteFrame() - resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + // What is this for? + bf := byteframe.NewByteFrame() + bf.WriteUint32(0) // Max stage overall MP rank + bf.WriteUint32(0) // Max RdP overall MP rank + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } From c4d128ec182c7bd64447f84ebc6cb3f578447da5 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 14:00:59 +1000 Subject: [PATCH 58/86] correctly handle yearly sub exchanges --- network/mhfpacket/msg_mhf_exchange_weekly_stamp.go | 4 ++-- server/channelserver/handlers.go | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go b/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go index 758925936..918a870ac 100644 --- a/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go +++ b/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go @@ -12,7 +12,7 @@ import ( type MsgMhfExchangeWeeklyStamp struct { AckHandle uint32 StampType string - Unk1 bool + Unk1 uint8 Unk2 uint16 } @@ -31,7 +31,7 @@ func (m *MsgMhfExchangeWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientct case 2: m.StampType = "ex" } - m.Unk1 = bf.ReadBool() + m.Unk1 = bf.ReadUint8() m.Unk2 = bf.ReadUint16() return nil } diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 28164b103..b5d6d0d38 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -697,7 +697,11 @@ func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp) var total, redeemed uint16 - s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) + if pkt.Unk1 == 0xA { // Yearly Sub Ex + s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed) + } else { + s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) + } bf := byteframe.NewByteFrame() bf.WriteUint16(total) bf.WriteUint16(redeemed) From 8a3daa1cfac02e4a316b8ef0bb9bf40a417b32e1 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 14:42:00 +1000 Subject: [PATCH 59/86] add exchanged tkts to warehouse gift box --- server/channelserver/handlers.go | 8 ++++++++ server/channelserver/handlers_house.go | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index b5d6d0d38..53e59d25f 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -697,11 +697,19 @@ func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp) var total, redeemed uint16 + var tktStack mhfpacket.WarehouseStack if pkt.Unk1 == 0xA { // Yearly Sub Ex s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed) + tktStack = mhfpacket.WarehouseStack{ItemID: 0x08A2, Quantity: 1} } else { s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) + if pkt.StampType == "hl" { + tktStack = mhfpacket.WarehouseStack{ItemID: 0x065E, Quantity: 5} + } else { + tktStack = mhfpacket.WarehouseStack{ItemID: 0x065F, Quantity: 5} + } } + addWarehouseGift(s, "item", tktStack) bf := byteframe.NewByteFrame() bf.WriteUint16(total) bf.WriteUint16(redeemed) diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index b4b97e3fa..b65c5fbeb 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -426,6 +426,26 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +func addWarehouseGift(s *Session, boxType string, giftStack mhfpacket.WarehouseStack) { + giftBox := getWarehouseBox(s, boxType, 10) + if boxType == "item" { + exists := false + for i, stack := range giftBox { + if stack.ItemID == giftStack.ItemID { + exists = true + giftBox[i].Quantity += giftStack.Quantity + break + } + } + if exists == false { + giftBox = append(giftBox, giftStack) + } + } else { + giftBox = append(giftBox, giftStack) + } + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s10=$1 WHERE character_id=$2", boxType), boxToBytes(giftBox, boxType), s.charID) +} + func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.WarehouseStack { var data []byte s.server.db.QueryRow(fmt.Sprintf("SELECT %s%d FROM warehouse WHERE character_id=$1", boxType, boxIndex), s.charID).Scan(&data) From a56309014b629d4c1b721ce54f8abf0b017b87aa Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 16:39:07 +1000 Subject: [PATCH 60/86] persistent BackStage --- server/channelserver/handlers_stage.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers_stage.go b/server/channelserver/handlers_stage.go index 4bf19d114..906480be7 100644 --- a/server/channelserver/handlers_stage.go +++ b/server/channelserver/handlers_stage.go @@ -135,7 +135,6 @@ func removeSessionFromStage(s *Session) { // Remove client from old stage. s.stage.Lock() delete(s.stage.clients, s) - delete(s.stage.reservedClientSlots, s.charID) // Delete old stage objects owned by the client. s.logger.Info("Sending notification to old stage clients") @@ -157,6 +156,7 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { if s.stageID == "" { s.stageMoveStack.Set(pkt.StageID) } else { + s.stage.reservedClientSlots[s.charID] = false s.stageMoveStack.Push(s.stageID) s.stageMoveStack.Lock() } @@ -175,11 +175,18 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) { // Transfer back to the saved stage ID before the previous move or enter. s.stageMoveStack.Unlock() backStage, err := s.stageMoveStack.Pop() - if err != nil { panic(err) } + if _, exists := s.stage.reservedClientSlots[s.charID]; exists { + delete(s.stage.reservedClientSlots, s.charID) + } + + if _, exists := s.server.stages[backStage].reservedClientSlots[s.charID]; exists { + delete(s.server.stages[backStage].reservedClientSlots, s.charID) + } + doStageTransfer(s, pkt.AckHandle, backStage) } From 2a1ce2b79dbf45f75f816a62223ea8efd55efb10 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 17:06:15 +1000 Subject: [PATCH 61/86] match houses by wildcard name --- server/channelserver/handlers_house.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index b65c5fbeb..280f27238 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -92,7 +92,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) { } case 3: house := HouseData{} - row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name=$1", pkt.Name) + row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name ILIKE $1", fmt.Sprintf(`%%%s%%`, pkt.Name)) err := row.StructScan(&house) if err != nil { panic(err) From f87c1e480f3596c21c02a0700e925b1364df7478 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 17:27:40 +1000 Subject: [PATCH 62/86] add missing static stages --- server/channelserver/sys_channel_server.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 635706d1a..6b2d46f3c 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -151,6 +151,12 @@ func NewServer(config *Config) *Server { // Pallone Carvan s.stages["sl1Ns260p0a0u0"] = NewStage("sl1Ns260p0a0u0") + // Pallone Guest House 1st Floor + s.stages["sl1Ns262p0a0u0"] = NewStage("sl1Ns262p0a0u0") + + // Pallone Guest House 2nd Floor + s.stages["sl1Ns263p0a0u0"] = NewStage("sl1Ns263p0a0u0") + // Diva fountain / prayer fountain. s.stages["sl2Ns379p0a0u0"] = NewStage("sl2Ns379p0a0u0") From 6e7259a0688ace07fc2f7186e58fb51b09d78a2d Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 19 Aug 2022 19:46:55 +1000 Subject: [PATCH 63/86] dump more save data types to backup --- server/channelserver/handlers_data.go | 6 +++--- server/channelserver/handlers_mercenary.go | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index d9cdcca17..8ed55c54a 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -56,7 +56,7 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { s.logger.Fatal("Failed to update savedata in db", zap.Error(err)) } s.logger.Info("Wrote recompressed savedata back to DB.") - dumpSaveData(s, pkt.RawDataPayload, "") + dumpSaveData(s, pkt.RawDataPayload, "savedata") _, err = s.server.db.Exec("UPDATE characters SET weapon_type=$1 WHERE id=$2", uint16(decompressedData[128789]), s.charID) if err != nil { @@ -275,8 +275,8 @@ func dumpSaveData(s *Session, data []byte, suffix string) { if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled { return } else { - dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%s_", s.Name)) - path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%s_", s.Name), fmt.Sprintf("%d_%s_%s%s.bin", s.charID, s.Name, Time_Current().Format("2006-01-02_15.04.05"), suffix)) + dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name)) + path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name), fmt.Sprintf("%d_%s_%s.bin", s.charID, s.Name, suffix)) if _, err := os.Stat(dir); os.IsNotExist(err) { os.Mkdir(dir, os.ModeDir) diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index ca3dd2a71..31bc26249 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -39,7 +39,7 @@ func handleMsgMhfLoadPartner(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSavePartner(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSavePartner) - dumpSaveData(s, pkt.RawDataPayload, "_partner") + dumpSaveData(s, pkt.RawDataPayload, "partner") _, err := s.server.db.Exec("UPDATE characters SET partner=$1 WHERE id=$2", pkt.RawDataPayload, s.charID) if err != nil { @@ -75,7 +75,7 @@ func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveHunterNavi) - dumpSaveData(s, pkt.RawDataPayload, "_hunternavi") + dumpSaveData(s, pkt.RawDataPayload, "hunternavi") if pkt.IsDataDiff { var data []byte @@ -228,6 +228,7 @@ func handleMsgMhfLoadOtomoAirou(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveOtomoAirou) + dumpSaveData(s, pkt.RawDataPayload, "otomoairou") decomp, err := nullcomp.Decompress(pkt.RawDataPayload[1:]) if err != nil { s.logger.Error("Failed to decompress airou", zap.Error(err)) From 62dacd3a78c6fabebd9ddb29242d8e2e764b362b Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 21 Aug 2022 18:44:28 +1000 Subject: [PATCH 64/86] guild improvements --- server/channelserver/handlers_guild.go | 36 ++++++++++++++++--- server/channelserver/handlers_guild_member.go | 2 +- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index de71aa79c..e03f0a04d 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -164,8 +164,8 @@ SELECT func (guild *Guild) Save(s *Session) error { _, err := s.server.db.Exec(` - UPDATE guilds SET main_motto=$2, sub_motto=$3, comment=$4, pugi_name_1=$5, pugi_name_2=$6, pugi_name_3=$7, icon=$8 WHERE id=$1 - `, guild.ID, guild.MainMotto, guild.SubMotto, guild.Comment, guild.PugiName1, guild.PugiName2, guild.PugiName3, guild.Icon) + UPDATE guilds SET main_motto=$2, sub_motto=$3, comment=$4, pugi_name_1=$5, pugi_name_2=$6, pugi_name_3=$7, icon=$8, leader_id=$9 WHERE id=$1 + `, guild.ID, guild.MainMotto, guild.SubMotto, guild.Comment, guild.PugiName1, guild.PugiName2, guild.PugiName3, guild.Icon, guild.GuildLeader.LeaderCharID) if err != nil { s.logger.Error("failed to update guild data", zap.Error(err), zap.Uint32("guildID", guild.ID)) @@ -649,6 +649,33 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(uint32(response)) doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) return + case mhfpacket.OPERATE_GUILD_RESIGN: + guildMembers, err := GetGuildMembers(s, guild.ID, false) + success := false + if err == nil { + sort.Slice(guildMembers[:], func(i, j int) bool { + return guildMembers[i].OrderIndex < guildMembers[j].OrderIndex + }) + for i := 1; i < len(guildMembers); i++ { + if !guildMembers[i].AvoidLeadership { + guild.LeaderCharID = guildMembers[i].CharID + guildMembers[0].OrderIndex = guildMembers[i].OrderIndex + guildMembers[i].OrderIndex = 1 + guildMembers[0].Save(s) + guildMembers[i].Save(s) + bf.WriteUint32(guildMembers[i].CharID) + success = true + break + } + } + guild.Save(s) + doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) + } + if !success { + bf.WriteUint32(0) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + } + return case mhfpacket.OPERATE_GUILD_APPLY: err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil) @@ -802,7 +829,7 @@ func handleMsgMhfOperateGuildMember(s *Session, p mhfpacket.MHFPacket) { actorCharacter, err := GetCharacterGuildData(s, s.charID) - if err != nil || (!actorCharacter.IsSubLeader() && guild.LeaderCharID != s.charID) { + if err != nil || (!actorCharacter.IsSubLeader() && guild.LeaderCharID != s.charID) || (!actorCharacter.Recruiter && guild.LeaderCharID != s.charID) { doAckSimpleFail(s, pkt.AckHandle, nil) return } @@ -1311,7 +1338,8 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(0x0600) } bf.WriteUint8(member.OrderIndex) - ps.Uint16(bf, member.Name, true) + bf.WriteBool(member.AvoidLeadership) + ps.Uint8(bf, member.Name, true) } for _, member := range guildMembers { diff --git a/server/channelserver/handlers_guild_member.go b/server/channelserver/handlers_guild_member.go index 0bc528fc7..506c78bb1 100644 --- a/server/channelserver/handlers_guild_member.go +++ b/server/channelserver/handlers_guild_member.go @@ -31,7 +31,7 @@ func (gm *GuildMember) IsSubLeader() bool { } func (gm *GuildMember) Save(s *Session) error { - _, err := s.server.db.Exec("UPDATE guild_characters SET avoid_leadership=$1 WHERE character_id=$2", gm.AvoidLeadership, gm.CharID) + _, err := s.server.db.Exec("UPDATE guild_characters SET avoid_leadership=$1, order_index=$2 WHERE character_id=$3", gm.AvoidLeadership, gm.OrderIndex, gm.CharID) if err != nil { s.logger.Error( From 1364de9a5e1d4b4b16a4a7296d89419ebcb55a0f Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 21 Aug 2022 23:11:07 +1000 Subject: [PATCH 65/86] guild scouting improvements --- network/binpacket/msg_bin_mail_notify.go | 6 +- patch-schema/mail-system-messages.sql | 13 +++ server/channelserver/handlers_guild.go | 70 +++++++------- server/channelserver/handlers_guild_scout.go | 99 ++++++++++---------- server/channelserver/handlers_mail.go | 32 +++---- 5 files changed, 118 insertions(+), 102 deletions(-) create mode 100644 patch-schema/mail-system-messages.sql diff --git a/network/binpacket/msg_bin_mail_notify.go b/network/binpacket/msg_bin_mail_notify.go index 5e1687512..125dc57ef 100644 --- a/network/binpacket/msg_bin_mail_notify.go +++ b/network/binpacket/msg_bin_mail_notify.go @@ -16,11 +16,7 @@ func (m MsgBinMailNotify) Parse(bf *byteframe.ByteFrame) error { func (m MsgBinMailNotify) Build(bf *byteframe.ByteFrame) error { bf.WriteUint8(0x01) // Unk - byteName, _ := stringsupport.ConvertUTF8ToShiftJIS(m.SenderName) - - bf.WriteBytes(byteName) - bf.WriteBytes(make([]byte, 21-len(byteName))) - + bf.WriteBytes(stringsupport.PaddedString(m.SenderName, 21, true)) return nil } diff --git a/patch-schema/mail-system-messages.sql b/patch-schema/mail-system-messages.sql new file mode 100644 index 000000000..4ce8dfaf6 --- /dev/null +++ b/patch-schema/mail-system-messages.sql @@ -0,0 +1,13 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.mail + ADD COLUMN IF NOT EXISTS is_sys_message bool DEFAULT false; + +UPDATE mail SET is_sys_message=false; + +ALTER TABLE IF EXISTS public.mail + DROP CONSTRAINT IF EXISTS mail_sender_id_fkey; + +INSERT INTO public.characters (id, name) VALUES (0, ''); + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index e03f0a04d..e4e7ddd53 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -700,6 +700,14 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) { if err != nil { // All successful acks return 0x01, assuming 0x00 is failure response = 0x00 + } else { + mail := Mail{ + RecipientID: s.charID, + Subject: "Withdrawal", + Body: fmt.Sprintf("You have withdrawn from 「%s」.", guild.Name), + IsSystemMessage: true, + } + mail.Send(s, nil) } bf.WriteUint32(uint32(response)) @@ -823,14 +831,14 @@ func handleMsgMhfOperateGuildMember(s *Session, p mhfpacket.MHFPacket) { guild, err := GetGuildInfoByCharacterId(s, pkt.CharID) if err != nil || guild == nil { - doAckSimpleFail(s, pkt.AckHandle, nil) + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } actorCharacter, err := GetCharacterGuildData(s, s.charID) if err != nil || (!actorCharacter.IsSubLeader() && guild.LeaderCharID != s.charID) || (!actorCharacter.Recruiter && guild.LeaderCharID != s.charID) { - doAckSimpleFail(s, pkt.AckHandle, nil) + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } @@ -839,41 +847,45 @@ func handleMsgMhfOperateGuildMember(s *Session, p mhfpacket.MHFPacket) { case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_ACCEPT: err = guild.AcceptApplication(s, pkt.CharID) mail = Mail{ - SenderID: s.charID, - RecipientID: pkt.CharID, - Subject: "Accepted!", - Body: fmt.Sprintf("Your application to join 「%s」 was accepted.", guild.Name), - IsGuildInvite: false, + RecipientID: pkt.CharID, + Subject: "Accepted!", + Body: fmt.Sprintf("Your application to join 「%s」 was accepted.", guild.Name), + IsSystemMessage: true, } case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_REJECT: err = guild.RejectApplication(s, pkt.CharID) mail = Mail{ - SenderID: s.charID, - RecipientID: pkt.CharID, - Subject: "Rejected", - Body: fmt.Sprintf("Your application to join 「%s」 was rejected.", guild.Name), - IsGuildInvite: false, + RecipientID: pkt.CharID, + Subject: "Rejected", + Body: fmt.Sprintf("Your application to join 「%s」 was rejected.", guild.Name), + IsSystemMessage: true, } case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_KICK: err = guild.RemoveCharacter(s, pkt.CharID) mail = Mail{ - SenderID: s.charID, - RecipientID: pkt.CharID, - Subject: "Kicked", - Body: fmt.Sprintf("You were kicked from 「%s」.", guild.Name), - IsGuildInvite: false, + RecipientID: pkt.CharID, + Subject: "Kicked", + Body: fmt.Sprintf("You were kicked from 「%s」.", guild.Name), + IsSystemMessage: true, } default: - doAckSimpleFail(s, pkt.AckHandle, nil) + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) s.logger.Warn(fmt.Sprintf("unhandled operateGuildMember action '%d'", pkt.Action)) } if err != nil { - doAckSimpleFail(s, pkt.AckHandle, nil) + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) } else { mail.Send(s, nil) + for _, channel := range s.server.Channels { + for _, session := range channel.sessions { + if session.charID == pkt.CharID { + SendMailNotification(s, &mail, session) + } + } + } + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } - doAckSimpleSucceed(s, pkt.AckHandle, nil) } func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { @@ -947,7 +959,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteBytes(guildLeaderName) bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00}) // Unk bf.WriteBool(false) // isReturnGuild - bf.WriteBytes([]byte{0x01, 0x02, 0x02}) // Unk + bf.WriteBool(false) // Unk + bf.WriteBytes([]byte{0x02, 0x02}) // Unk bf.WriteUint32(guild.EventRP) if guild.PugiName1 == "" { @@ -1099,12 +1112,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } else { - //// REALLY large/complex format... stubbing it out here for simplicity. - //resp := byteframe.NewByteFrame() - //resp.WriteUint32(0) // Count - //resp.WriteUint8(0) // Unk, read if count == 0. - - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 8)) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 5)) } } @@ -1448,12 +1456,8 @@ func handleMsgMhfGetGuildTargetMemberNum(s *Session, p mhfpacket.MHFPacket) { guild, err = GetGuildInfoByID(s, pkt.GuildID) } - if err != nil { - s.logger.Warn("failed to find guild", zap.Error(err), zap.Uint32("guildID", pkt.GuildID)) - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) - return - } else if guild == nil { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + if err != nil || guild == nil { + doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02}) return } diff --git a/server/channelserver/handlers_guild_scout.go b/server/channelserver/handlers_guild_scout.go index 4bcbb6b91..906f25154 100644 --- a/server/channelserver/handlers_guild_scout.go +++ b/server/channelserver/handlers_guild_scout.go @@ -21,7 +21,7 @@ func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) { panic(err) } - if actorCharGuildData == nil || !actorCharGuildData.Recruiter { + if actorCharGuildData == nil || (!actorCharGuildData.Recruiter && !actorCharGuildData.IsLeader && !actorCharGuildData.IsSubLeader()) { doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) return } @@ -59,19 +59,13 @@ func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) { panic(err) } - senderName, err := getCharacterName(s, s.charID) - - if err != nil { - panic(err) - } - mail := &Mail{ SenderID: s.charID, RecipientID: pkt.CharID, - Subject: "Guild! ヽ(・∀・)ノ", + Subject: "Invitation!", Body: fmt.Sprintf( - "%s has invited you to join the wonderful guild %s, do you accept this challenge?", - senderName, + "%s has invited you to join 「%s」\nDo you want to accept?", + getCharacterName(s, s.charID), guildInfo.Name, ), IsGuildInvite: true, @@ -104,7 +98,7 @@ func handleMsgMhfCancelGuildScout(s *Session, p mhfpacket.MHFPacket) { panic(err) } - if guildCharData == nil || !guildCharData.Recruiter { + if guildCharData == nil || (!guildCharData.Recruiter && !guildCharData.IsLeader && !guildCharData.IsSubLeader()) { doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) return } @@ -128,61 +122,72 @@ func handleMsgMhfCancelGuildScout(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAnswerGuildScout(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAnswerGuildScout) - + bf := byteframe.NewByteFrame() guild, err := GetGuildInfoByCharacterId(s, pkt.LeaderID) if err != nil { panic(err) } - _, err = guild.GetApplicationForCharID(s, s.charID, GuildApplicationTypeInvited) + app, err := guild.GetApplicationForCharID(s, s.charID, GuildApplicationTypeInvited) - if err != nil { + if app == nil || err != nil { s.logger.Warn( - "could not retrieve guild invitation", + "Guild invite missing, deleted?", zap.Error(err), zap.Uint32("guildID", guild.ID), zap.Uint32("charID", s.charID), ) - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) + bf.WriteUint32(7) + bf.WriteUint32(guild.ID) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) return } + var mail []Mail if pkt.Answer { err = guild.AcceptApplication(s, s.charID) + mail = append(mail, Mail{ + RecipientID: s.charID, + Subject: "Success!", + Body: fmt.Sprintf("You successfully joined 「%s」.", guild.Name), + IsSystemMessage: true, + }) + mail = append(mail, Mail{ + SenderID: s.charID, + RecipientID: pkt.LeaderID, + Subject: "Accepted", + Body: fmt.Sprintf("%s accepted your invitation to join 「%s」.", s.Name, guild.Name), + IsSystemMessage: true, + }) } else { err = guild.RejectApplication(s, s.charID) + mail = append(mail, Mail{ + RecipientID: s.charID, + Subject: "Declined", + Body: fmt.Sprintf("You declined the invitation to join 「%s」.", guild.Name), + IsSystemMessage: true, + }) + mail = append(mail, Mail{ + SenderID: s.charID, + RecipientID: pkt.LeaderID, + Subject: "Declined", + Body: fmt.Sprintf("%s declined your invitation to join 「%s」.", s.Name, guild.Name), + IsSystemMessage: true, + }) } - if err != nil { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - return + bf.WriteUint32(7) + bf.WriteUint32(guild.ID) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + } else { + bf.WriteUint32(0) + bf.WriteUint32(guild.ID) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + for _, m := range mail { + m.Send(s, nil) + } } - - senderName, err := getCharacterName(s, pkt.LeaderID) - - if err != nil { - doAckSimpleFail(s, pkt.AckHandle, nil) - panic(err) - } - - successMail := Mail{ - SenderID: pkt.LeaderID, - RecipientID: s.charID, - Subject: "Happy days!", - Body: fmt.Sprintf("You successfully joined %s and should be proud of all you have accomplished.", guild.Name), - IsGuildInvite: false, - SenderName: senderName, - } - - err = successMail.Send(s, nil) - - if err != nil { - doAckSimpleFail(s, pkt.AckHandle, nil) - panic(err) - } - - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x81, 0x7e}) } func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) { @@ -191,12 +196,12 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) { guildInfo, err := GetGuildInfoByCharacterId(s, s.charID) if guildInfo == nil && s.prevGuildID == 0 { - doAckSimpleFail(s, pkt.AckHandle, nil) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) return } else { guildInfo, err = GetGuildInfoByID(s, s.prevGuildID) if guildInfo == nil || err != nil { - doAckSimpleFail(s, pkt.AckHandle, nil) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) return } } @@ -210,7 +215,7 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) { if err != nil { s.logger.Error("failed to retrieve scouted characters", zap.Error(err)) - doAckSimpleFail(s, pkt.AckHandle, nil) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) return } diff --git a/server/channelserver/handlers_mail.go b/server/channelserver/handlers_mail.go index 1e060b245..e31e90eed 100644 --- a/server/channelserver/handlers_mail.go +++ b/server/channelserver/handlers_mail.go @@ -25,21 +25,22 @@ type Mail struct { AttachedItemAmount uint16 `db:"attached_item_amount"` CreatedAt time.Time `db:"created_at"` IsGuildInvite bool `db:"is_guild_invite"` + IsSystemMessage bool `db:"is_sys_message"` SenderName string `db:"sender_name"` } func (m *Mail) Send(s *Session, transaction *sql.Tx) error { query := ` - INSERT INTO mail (sender_id, recipient_id, subject, body, attached_item, attached_item_amount, is_guild_invite) - VALUES ($1, $2, $3, $4, $5, $6, $7) + INSERT INTO mail (sender_id, recipient_id, subject, body, attached_item, attached_item_amount, is_guild_invite, is_sys_message) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ` var err error if transaction == nil { - _, err = s.server.db.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite) + _, err = s.server.db.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage) } else { - _, err = transaction.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite) + _, err = transaction.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage) } if err != nil { @@ -53,6 +54,7 @@ func (m *Mail) Send(s *Session, transaction *sql.Tx) error { zap.Uint16("itemID", m.AttachedItemID), zap.Uint16("itemAmount", m.AttachedItemAmount), zap.Bool("isGuildInvite", m.IsGuildInvite), + zap.Bool("isSystemMessage", m.IsSystemMessage), ) return err } @@ -141,6 +143,7 @@ func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) { m.attached_item_amount, m.created_at, m.is_guild_invite, + m.is_sys_message, m.deleted, m.locked, c.name as sender_name @@ -189,6 +192,7 @@ func GetMailByID(s *Session, ID int) (*Mail, error) { m.attached_item_amount, m.created_at, m.is_guild_invite, + m.is_sys_message, m.deleted, m.locked, c.name as sender_name @@ -215,16 +219,10 @@ func GetMailByID(s *Session, ID int) (*Mail, error) { } func SendMailNotification(s *Session, m *Mail, recipient *Session) { - senderName, err := getCharacterName(s, m.SenderID) - - if err != nil { - panic(err) - } - bf := byteframe.NewByteFrame() notification := &binpacket.MsgBinMailNotify{ - SenderName: senderName, + SenderName: getCharacterName(s, m.SenderID), } notification.Build(bf) @@ -241,7 +239,7 @@ func SendMailNotification(s *Session, m *Mail, recipient *Session) { recipient.QueueSendMHF(castedBinary) } -func getCharacterName(s *Session, charID uint32) (string, error) { +func getCharacterName(s *Session, charID uint32) string { row := s.server.db.QueryRow("SELECT name FROM characters WHERE id = $1", charID) charName := "" @@ -249,10 +247,9 @@ func getCharacterName(s *Session, charID uint32) (string, error) { err := row.Scan(&charName) if err != nil { - return "", err + return "" } - - return charName, nil + return charName } func handleMsgMhfReadMail(s *Session, p mhfpacket.MHFPacket) { @@ -325,8 +322,9 @@ func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) { flags |= 0x02 } - // System message, hides ID - // flags |= 0x04 + if m.IsSystemMessage { + flags |= 0x04 + } // Workaround until EN mail items are patched if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.DisableMailItems { From 602aa8440b21c3d9f96fb5499d10fefa5f3e1aa4 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 22 Aug 2022 11:24:29 +1000 Subject: [PATCH 66/86] Update go.mod --- go.mod | 5 ----- 1 file changed, 5 deletions(-) diff --git a/go.mod b/go.mod index 59d5c7eb9..784727f3b 100644 --- a/go.mod +++ b/go.mod @@ -4,24 +4,19 @@ go 1.16 require ( github.com/bwmarrin/discordgo v0.23.2 - github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 // indirect github.com/jmoiron/sqlx v1.3.4 github.com/lib/pq v1.10.4 - github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/sachaos/lottery v0.0.0-20180520074626-61949d99bd96 github.com/spf13/viper v1.8.1 go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.18.1 golang.org/x/crypto v0.0.0-20211202192323-5770296d904e - golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect golang.org/x/text v0.3.7 golang.org/x/tools v0.1.8 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) From 5ecf8d945f39aee414c1abc89dcd37c37ac247cd Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 22 Aug 2022 11:31:10 +1000 Subject: [PATCH 67/86] Revert "Update go.mod" This reverts commit 602aa8440b21c3d9f96fb5499d10fefa5f3e1aa4. --- go.mod | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/go.mod b/go.mod index 784727f3b..59d5c7eb9 100644 --- a/go.mod +++ b/go.mod @@ -4,19 +4,24 @@ go 1.16 require ( github.com/bwmarrin/discordgo v0.23.2 + github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 // indirect github.com/jmoiron/sqlx v1.3.4 github.com/lib/pq v1.10.4 + github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/sachaos/lottery v0.0.0-20180520074626-61949d99bd96 github.com/spf13/viper v1.8.1 go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.18.1 golang.org/x/crypto v0.0.0-20211202192323-5770296d904e + golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect golang.org/x/text v0.3.7 golang.org/x/tools v0.1.8 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) From af667d8b00ba8e2b449f0ee416dee134c2f584dc Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 22 Aug 2022 12:03:15 +1000 Subject: [PATCH 68/86] Update go.mod --- go.mod | 4 ---- 1 file changed, 4 deletions(-) diff --git a/go.mod b/go.mod index 59d5c7eb9..8107625a2 100644 --- a/go.mod +++ b/go.mod @@ -4,16 +4,13 @@ go 1.16 require ( github.com/bwmarrin/discordgo v0.23.2 - github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 // indirect github.com/jmoiron/sqlx v1.3.4 github.com/lib/pq v1.10.4 - github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/sachaos/lottery v0.0.0-20180520074626-61949d99bd96 github.com/spf13/viper v1.8.1 go.uber.org/atomic v1.9.0 // indirect @@ -23,5 +20,4 @@ require ( golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect golang.org/x/text v0.3.7 golang.org/x/tools v0.1.8 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) From 745b7ee6e1a2b650fe339c5a5f6da582ed038b90 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 22 Aug 2022 12:04:51 +1000 Subject: [PATCH 69/86] Update go.mod --- go.mod | 4 ---- 1 file changed, 4 deletions(-) diff --git a/go.mod b/go.mod index 59d5c7eb9..8107625a2 100644 --- a/go.mod +++ b/go.mod @@ -4,16 +4,13 @@ go 1.16 require ( github.com/bwmarrin/discordgo v0.23.2 - github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 // indirect github.com/jmoiron/sqlx v1.3.4 github.com/lib/pq v1.10.4 - github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/sachaos/lottery v0.0.0-20180520074626-61949d99bd96 github.com/spf13/viper v1.8.1 go.uber.org/atomic v1.9.0 // indirect @@ -23,5 +20,4 @@ require ( golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect golang.org/x/text v0.3.7 golang.org/x/tools v0.1.8 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) From f6148271eae37172c088407931349d8999739035 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 23 Aug 2022 09:26:09 +1000 Subject: [PATCH 70/86] simplify guild code --- server/channelserver/handlers_festa.go | 18 ++++++--- server/channelserver/handlers_guild.go | 53 +++++++------------------- 2 files changed, 26 insertions(+), 45 deletions(-) diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index 4db9b97a2..6c35f770c 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -270,16 +270,22 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStateFestaG) guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil || guild == nil { - doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + applicant, _ := guild.HasApplicationForCharID(s, s.charID) + resp := byteframe.NewByteFrame() + if err != nil || guild == nil || applicant { + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0xFFFFFFFF) + resp.WriteUint32(0) + resp.WriteUint32(0) + doAckBufSucceed(s, pkt.AckHandle, resp.Data()) return } - resp := byteframe.NewByteFrame() resp.WriteUint32(guild.Souls) - resp.WriteUint32(1) // unk - resp.WriteUint32(1) // unk + resp.WriteUint32(0) // unk resp.WriteUint32(1) // unk, rank? - resp.WriteUint32(1) // unk + resp.WriteUint32(0) // unk + resp.WriteUint32(0) // unk doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index e4e7ddd53..0537bf723 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -122,18 +122,12 @@ SELECT leader_id, lc.name as leader_name, comment, - pugi_name_1, - pugi_name_2, - pugi_name_3, + COALESCE(pugi_name_1, '') AS pugi_name_1, + COALESCE(pugi_name_2, '') AS pugi_name_2, + COALESCE(pugi_name_3, '') AS pugi_name_3, recruiting, - CASE WHEN ( - SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id - ) IS NULL THEN 'none' ELSE ( - SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id - ) END festival_colour, - ( - SELECT SUM(souls) FROM guild_characters gc WHERE gc.guild_id = g.id - ) AS souls, + COALESCE((SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id), 'none') AS festival_colour, + (SELECT SUM(souls) FROM guild_characters gc WHERE gc.guild_id = g.id) AS souls, CASE WHEN rank_rp <= 48 THEN rank_rp/24 WHEN rank_rp <= 288 THEN rank_rp/48+1 @@ -142,21 +136,14 @@ SELECT WHEN rank_rp < 1200 THEN 16 ELSE 17 END rank, - CASE WHEN ( + COALESCE(( SELECT id FROM guild_alliances ga WHERE ga.parent_id = g.id OR ga.sub1_id = g.id OR ga.sub2_id = g.id - ) IS NULL THEN 0 ELSE ( - SELECT id FROM guild_alliances ga WHERE - ga.parent_id = g.id OR - ga.sub1_id = g.id OR - ga.sub2_id = g.id - ) END alliance_id, + ), 0) AS alliance_id, icon, - ( - SELECT count(1) FROM guild_characters gc WHERE gc.guild_id = g.id - ) AS member_count + (SELECT count(1) FROM guild_characters gc WHERE gc.guild_id = g.id) AS member_count FROM guilds g JOIN guild_characters lgc ON lgc.character_id = leader_id JOIN characters lc on leader_id = lc.id @@ -962,22 +949,9 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteBool(false) // Unk bf.WriteBytes([]byte{0x02, 0x02}) // Unk bf.WriteUint32(guild.EventRP) - - if guild.PugiName1 == "" { - bf.WriteUint16(0x0100) - } else { - ps.Uint8(bf, guild.PugiName1, true) - } - if guild.PugiName2 == "" { - bf.WriteUint16(0x0100) - } else { - ps.Uint8(bf, guild.PugiName2, true) - } - if guild.PugiName3 == "" { - bf.WriteUint16(0x0100) - } else { - ps.Uint8(bf, guild.PugiName3, true) - } + ps.Uint8(bf, guild.PugiName1, true) + ps.Uint8(bf, guild.PugiName2, true) + ps.Uint8(bf, guild.PugiName3, true) // probably guild pugi properties, should be status, stamina and luck outfits bf.WriteBytes([]byte{ @@ -998,7 +972,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { } else { bf.WriteUint32(alliance.ID) bf.WriteUint32(uint32(alliance.CreatedAt.Unix())) - bf.WriteUint16(uint16(alliance.TotalMembers)) + bf.WriteUint16(alliance.TotalMembers) bf.WriteUint16(0) // Unk0 ps.Uint16(bf, alliance.Name, true) if alliance.SubGuild1ID > 0 { @@ -1061,7 +1035,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } - if err != nil { + if err != nil || characterGuildData.IsApplicant { bf.WriteUint16(0) } else { bf.WriteUint16(uint16(len(applicants))) @@ -1109,6 +1083,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { } else { bf.WriteUint8(0x00) } + bf.WriteUint8(0) // Unk doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } else { From 8099c5fd666f23a9c75159fa96cfcc9337da8734 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 23 Aug 2022 18:38:13 +1000 Subject: [PATCH 71/86] make launcher server optional --- config.json | 1 + config/config.go | 35 ++++++++++++++++++----------------- main.go | 29 +++++++++++++++++------------ 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/config.json b/config.json index 67087d28c..c035f60c0 100644 --- a/config.json +++ b/config.json @@ -5,6 +5,7 @@ "devmode": true, "devmodeoptions": { "serverName" : "", + "EnableLauncherServer": false, "hideLoginNotice": false, "loginNotice": "
Welcome to Erupe SU9 (Patch 1)!
Erupe is experimental software, we are not liable for any
issues caused by installing the software!

■Report bugs on Discord!

■Test everything!

■Don't talk to softlocking NPCs!

■Fork the code on GitHub!

Thank you to all of the contributors,

this wouldn't exist without you.", "cleandb": false, diff --git a/config/config.go b/config/config.go index b025e7a0a..bfbf8086a 100644 --- a/config/config.go +++ b/config/config.go @@ -24,23 +24,24 @@ type Config struct { // DevModeOptions holds various debug/temporary options for use while developing Erupe. type DevModeOptions struct { - ServerName string // To get specific instance server about (Current Players/Event Week) - HideLoginNotice bool // Hide the Erupe notice on login - LoginNotice string // MHFML string of the login notice displayed - CleanDB bool // Automatically wipes the DB on server reset. - MaxLauncherHR bool // Sets the HR returned in the launcher to HR9 so that you can join non-beginner worlds. - FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages - LogInboundMessages bool // Log all messages sent to the server - LogOutboundMessages bool // Log all messages sent to the clients - MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled - DivaEvent int // Diva Defense event status - FestaEvent int // Hunter's Festa event status - TournamentEvent int // VS Tournament event status - MezFesEvent bool // MezFes status - MezFesAlt bool // Swaps out Volpakkun for Tokotoko - DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!) - DisableMailItems bool // Hack to prevent english versions of MHF from crashing - SaveDumps SaveDumpOptions + ServerName string // To get specific instance server about (Current Players/Event Week) + EnableLauncherServer bool // Enables the launcher server to be served on port 80 + HideLoginNotice bool // Hide the Erupe notice on login + LoginNotice string // MHFML string of the login notice displayed + CleanDB bool // Automatically wipes the DB on server reset. + MaxLauncherHR bool // Sets the HR returned in the launcher to HR9 so that you can join non-beginner worlds. + FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages + LogInboundMessages bool // Log all messages sent to the server + LogOutboundMessages bool // Log all messages sent to the clients + MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled + DivaEvent int // Diva Defense event status + FestaEvent int // Hunter's Festa event status + TournamentEvent int // VS Tournament event status + MezFesEvent bool // MezFes status + MezFesAlt bool // Swaps out Volpakkun for Tokotoko + DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!) + DisableMailItems bool // Hack to prevent english versions of MHF from crashing + SaveDumps SaveDumpOptions } type SaveDumpOptions struct { diff --git a/main.go b/main.go index 2bc598a06..b9dc62538 100644 --- a/main.go +++ b/main.go @@ -125,18 +125,21 @@ func main() { // Now start our server(s). // Launcher HTTP server. - launcherServer := launcherserver.NewServer( - &launcherserver.Config{ - Logger: logger.Named("launcher"), - ErupeConfig: erupeConfig, - DB: db, - UseOriginalLauncherFiles: erupeConfig.Launcher.UseOriginalLauncherFiles, - }) - err = launcherServer.Start() - if err != nil { - preventClose(fmt.Sprintf("Failed to start launcher server: %s", err.Error())) + var launcherServer *launcherserver.Server + if erupeConfig.DevMode && erupeConfig.DevModeOptions.EnableLauncherServer { + launcherServer = launcherserver.NewServer( + &launcherserver.Config{ + Logger: logger.Named("launcher"), + ErupeConfig: erupeConfig, + DB: db, + UseOriginalLauncherFiles: erupeConfig.Launcher.UseOriginalLauncherFiles, + }) + err = launcherServer.Start() + if err != nil { + preventClose(fmt.Sprintf("Failed to start launcher server: %s", err.Error())) + } + logger.Info("Started launcher server") } - logger.Info("Started launcher server") // Entrance server. entranceServer := entranceserver.NewServer( @@ -219,7 +222,9 @@ func main() { } signServer.Shutdown() entranceServer.Shutdown() - launcherServer.Shutdown() + if erupeConfig.DevModeOptions.EnableLauncherServer { + launcherServer.Shutdown() + } time.Sleep(1 * time.Second) } From 2c51ddc518fc1a2ecb876f61dd822ae4364a738f Mon Sep 17 00:00:00 2001 From: wish Date: Thu, 25 Aug 2022 20:53:28 +1000 Subject: [PATCH 72/86] fix launcher character enumeration --- server/signserver/dbutils.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/signserver/dbutils.go b/server/signserver/dbutils.go index 8f1b89a01..973860ed5 100644 --- a/server/signserver/dbutils.go +++ b/server/signserver/dbutils.go @@ -29,7 +29,7 @@ func (s *Server) newUserChara(username string) error { INSERT INTO characters ( user_id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login) - VALUES($1, False, True, '', '', 1, 0, 0, $2)`, + VALUES($1, False, True, '', '', 0, 0, 0, $2)`, id, uint32(time.Now().Unix()), ) @@ -88,7 +88,7 @@ type character struct { func (s *Server) getCharactersForUser(uid int) ([]character, error) { characters := make([]character, 0) - err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false", uid) + err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false ORDER BY id ASC", uid) if err != nil { return nil, err } From 951c8e9c216d795366a17b5a886f8a0609522e48 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 26 Aug 2022 15:46:31 +1000 Subject: [PATCH 73/86] correct some festa functions --- server/channelserver/handlers_festa.go | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index 6c35f770c..a1afd8b05 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -253,16 +253,7 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { s.server.db.QueryRow("SELECT souls FROM guild_characters WHERE character_id=$1", s.charID).Scan(&souls) bf := byteframe.NewByteFrame() bf.WriteUint32(souls) - - // This definitely isn't right, but it does stop you from claiming the festa infinitely. - var claimed uint32 - s.server.db.QueryRow("SELECT count(*) FROM festa_prizes_accepted fpa WHERE fpa.prize_id=0 AND fpa.character_id=$1", s.charID).Scan(&claimed) - if claimed > 0 { - bf.WriteUint32(0) // unk - } else { - bf.WriteUint32(0x01000000) // unk - } - + bf.WriteUint32(0) // unk doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -303,8 +294,8 @@ func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) { } bf := byteframe.NewByteFrame() bf.WriteUint16(uint16(len(members))) + bf.WriteUint16(0) // Unk for _, member := range members { - bf.WriteUint16(0) bf.WriteUint32(member.CharID) bf.WriteUint32(member.Souls) } @@ -344,7 +335,6 @@ func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireFesta) - s.server.db.Exec("INSERT INTO public.festa_prizes_accepted VALUES (0, $1)", s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } From f80a807fb1d9911625562f06b0c53dc0c39d1a9c Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 26 Aug 2022 16:08:51 +1000 Subject: [PATCH 74/86] fix unneeded enumerations --- server/channelserver/handlers_festa.go | 13 ++++++++++--- server/channelserver/handlers_guild.go | 8 ++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index a1afd8b05..6e5080ac3 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -245,7 +245,11 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStateFestaU) guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil || guild == nil { + applicant := false + if guild != nil { + applicant, _ = guild.HasApplicationForCharID(s, s.charID) + } + if err != nil || guild == nil || applicant { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } @@ -261,7 +265,10 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStateFestaG) guild, err := GetGuildInfoByCharacterId(s, s.charID) - applicant, _ := guild.HasApplicationForCharID(s, s.charID) + applicant := false + if guild != nil { + applicant, _ = guild.HasApplicationForCharID(s, s.charID) + } resp := byteframe.NewByteFrame() if err != nil || guild == nil || applicant { resp.WriteUint32(0) @@ -274,7 +281,7 @@ func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { } resp.WriteUint32(guild.Souls) resp.WriteUint32(0) // unk - resp.WriteUint32(1) // unk, rank? + resp.WriteUint32(0) // unk, rank? resp.WriteUint32(0) // unk resp.WriteUint32(0) // unk doAckBufSucceed(s, pkt.AckHandle, resp.Data()) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 0537bf723..d8d4509bc 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1276,6 +1276,14 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) { guild, err = GetGuildInfoByCharacterId(s, s.charID) } + if guild != nil { + isApplicant, _ := guild.HasApplicationForCharID(s, s.charID) + if isApplicant { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + return + } + } + if guild == nil && s.prevGuildID > 0 { guild, err = GetGuildInfoByID(s, s.prevGuildID) } From 09b5de79d377623e14cada34a87e594126cc6b71 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 26 Aug 2022 17:26:24 +1000 Subject: [PATCH 75/86] diva scheduler and cleanup --- server/channelserver/handlers_diva.go | 89 ++++++++++++++++++++++++++ server/channelserver/handlers_event.go | 44 ------------- server/channelserver/handlers_guild.go | 2 +- 3 files changed, 90 insertions(+), 45 deletions(-) diff --git a/server/channelserver/handlers_diva.go b/server/channelserver/handlers_diva.go index 8927cd792..0219d9e3b 100644 --- a/server/channelserver/handlers_diva.go +++ b/server/channelserver/handlers_diva.go @@ -2,11 +2,100 @@ package channelserver import ( "encoding/hex" + "time" "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" ) +func cleanupDiva(s *Session) { + s.server.db.Exec("DELETE FROM events WHERE event_type='diva'") +} + +func generateDivaTimestamps(s *Session, start uint32, debug bool) []uint32 { + timestamps := make([]uint32, 6) + midnight := Time_Current_Midnight() + if debug && start <= 3 { + midnight := uint32(midnight.Unix()) + switch start { + case 1: + timestamps[0] = midnight + timestamps[1] = timestamps[0] + 601200 + timestamps[2] = timestamps[1] + 3900 + timestamps[3] = timestamps[1] + 604800 + timestamps[4] = timestamps[3] + 3900 + timestamps[5] = timestamps[3] + 604800 + case 2: + timestamps[0] = midnight - 605100 + timestamps[1] = midnight - 3900 + timestamps[2] = midnight + timestamps[3] = timestamps[1] + 604800 + timestamps[4] = timestamps[3] + 3900 + timestamps[5] = timestamps[3] + 604800 + case 3: + timestamps[0] = midnight - 1213800 + timestamps[1] = midnight - 608700 + timestamps[2] = midnight - 604800 + timestamps[3] = midnight - 3900 + timestamps[4] = midnight + timestamps[5] = timestamps[3] + 604800 + } + return timestamps + } + if start == 0 || Time_Current_Adjusted().Unix() > int64(start)+2977200 { + cleanupDiva(s) + // Generate a new diva defense, starting midnight tomorrow + start = uint32(midnight.Add(24 * time.Hour).Unix()) + s.server.db.Exec("INSERT INTO events (event_type, start_time) VALUES ('diva', to_timestamp($1)::timestamp without time zone)", start) + } + timestamps[0] = start + timestamps[1] = timestamps[0] + 601200 + timestamps[2] = timestamps[1] + 3900 + timestamps[3] = timestamps[1] + 604800 + timestamps[4] = timestamps[3] + 3900 + timestamps[5] = timestamps[3] + 604800 + return timestamps +} + +func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdSchedule) + bf := byteframe.NewByteFrame() + + id, start := uint32(0xCAFEBEEF), uint32(0) + rows, _ := s.server.db.Queryx("SELECT id, (EXTRACT(epoch FROM start_time)::int) as start_time FROM events WHERE event_type='diva'") + for rows.Next() { + rows.Scan(&id, &start) + } + + var timestamps []uint32 + if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.DivaEvent >= 0 { + if s.server.erupeConfig.DevModeOptions.DivaEvent == 0 { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + return + } + timestamps = generateDivaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.DivaEvent), true) + } else { + timestamps = generateDivaTimestamps(s, start, false) + } + + if timestamps[0] > uint32(Time_Current_Adjusted().Unix()) { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + return + } + + bf.WriteUint32(id) + for _, timestamp := range timestamps { + bf.WriteUint32(timestamp) + } + + bf.WriteUint16(0x19) // Unk 00011001 + bf.WriteUint16(0x2D) // Unk 00101101 + bf.WriteUint16(0x02) // Unk 00000010 + bf.WriteUint16(0x02) // Unk 00000010 + + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} + func handleMsgMhfGetKijuInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetKijuInfo) // Temporary canned response diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go index 7ec7c30e7..88cacc41d 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -7,7 +7,6 @@ import ( "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" - timeServerFix "erupe-ce/server/channelserver/timeserver" ) func handleMsgMhfRegisterEvent(s *Session, p mhfpacket.MHFPacket) { @@ -234,49 +233,6 @@ func handleMsgMhfUseKeepLoginBoost(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } -func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetUdSchedule) - var t = timeServerFix.Tstatic_midnight() - var event int = s.server.erupeConfig.DevModeOptions.DivaEvent - - year, month, day := t.Date() - midnight := time.Date(year, month, day, 0, 0, 0, 0, t.Location()) - // Events with time limits are Festival with Sign up, Soul Week and Winners Weeks - // Diva Defense with Prayer, Interception and Song weeks - // Mezeporta Festival with simply 'available' being a weekend thing - resp := byteframe.NewByteFrame() - resp.WriteUint32(0x1d5fda5c) // Unk (1d5fda5c, 0b5397df) - - if event == 1 { - resp.WriteUint32(uint32(midnight.Add(24 * 21 * time.Hour).Unix())) // Week 1 Timestamp, Festi start? - } else { - resp.WriteUint32(uint32(midnight.Add(-24 * 21 * time.Hour).Unix())) // Week 1 Timestamp, Festi start? - } - - if event == 2 { - resp.WriteUint32(uint32(midnight.Add(24 * 14 * time.Hour).Unix())) // Week 2 Timestamp - resp.WriteUint32(uint32(midnight.Add(24 * 14 * time.Hour).Unix())) // Week 2 Timestamp - } else { - resp.WriteUint32(uint32(midnight.Add(-24 * 14 * time.Hour).Unix())) // Week 2 Timestamp - resp.WriteUint32(uint32(midnight.Add(-24 * 14 * time.Hour).Unix())) // Week 2 Timestamp - } - - if event == 3 { - resp.WriteUint32(uint32(midnight.Add((24) * 7 * time.Hour).Unix())) // Diva Defense Interception - resp.WriteUint32(uint32(midnight.Add((24) * 14 * time.Hour).Unix())) // Diva Defense Greeting Song - } else { - resp.WriteUint32(uint32(midnight.Add((-24) * 7 * time.Hour).Unix())) // Diva Defense Interception - resp.WriteUint32(uint32(midnight.Add((-24) * 14 * time.Hour).Unix())) // Diva Defense Greeting Song - } - - resp.WriteUint16(0x19) // Unk 00011001 - resp.WriteUint16(0x2d) // Unk 00101101 - resp.WriteUint16(0x02) // Unk 00000010 - resp.WriteUint16(0x02) // Unk 00000010 - - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) -} - func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetUdInfo) // Message that appears on the Diva Defense NPC and triggers the green exclamation mark diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index d8d4509bc..e13719dba 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -946,7 +946,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteBytes(guildLeaderName) bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00}) // Unk bf.WriteBool(false) // isReturnGuild - bf.WriteBool(false) // Unk + bf.WriteBool(false) // earnedSpecialHall bf.WriteBytes([]byte{0x02, 0x02}) // Unk bf.WriteUint32(guild.EventRP) ps.Uint8(bf, guild.PugiName1, true) From 4537ba9492ef2c0f8d53d566f04f87536802c1be Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 26 Aug 2022 17:30:35 +1000 Subject: [PATCH 76/86] update config --- config.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config.json b/config.json index 6b9d85944..6f439baa8 100644 --- a/config.json +++ b/config.json @@ -12,8 +12,7 @@ "LogInboundMessages": false, "LogOutboundMessages": false, "MaxHexdumpLength": 256, - "Event": 0, - "DivaEvent": 0, + "DivaEvent": -1, "FestaEvent": -1, "TournamentEvent": 0, "MezFesEvent": true, From 9d589b33383f7ee2ffda029a8b965d317eef8ac0 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 26 Aug 2022 19:51:52 +1000 Subject: [PATCH 77/86] diva cleanup --- server/channelserver/handlers_diva.go | 28 ++++++++++++++++++++------ server/channelserver/handlers_event.go | 26 ------------------------ 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/server/channelserver/handlers_diva.go b/server/channelserver/handlers_diva.go index 0219d9e3b..48e6dcecf 100644 --- a/server/channelserver/handlers_diva.go +++ b/server/channelserver/handlers_diva.go @@ -2,6 +2,7 @@ package channelserver import ( "encoding/hex" + "erupe-ce/common/stringsupport" "time" "erupe-ce/common/byteframe" @@ -70,7 +71,7 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) { var timestamps []uint32 if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.DivaEvent >= 0 { if s.server.erupeConfig.DevModeOptions.DivaEvent == 0 { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36)) return } timestamps = generateDivaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.DivaEvent), true) @@ -78,11 +79,6 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) { timestamps = generateDivaTimestamps(s, start, false) } - if timestamps[0] > uint32(Time_Current_Adjusted().Unix()) { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) - return - } - bf.WriteUint32(id) for _, timestamp := range timestamps { bf.WriteUint32(timestamp) @@ -96,6 +92,26 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdInfo) + // Message that appears on the Diva Defense NPC and triggers the green exclamation mark + udInfos := []struct { + Text string + StartTime time.Time + EndTime time.Time + }{} + + resp := byteframe.NewByteFrame() + resp.WriteUint8(uint8(len(udInfos))) + for _, udInfo := range udInfos { + resp.WriteBytes(stringsupport.PaddedString(udInfo.Text, 1024, true)) + resp.WriteUint32(uint32(udInfo.StartTime.Unix())) + resp.WriteUint32(uint32(udInfo.EndTime.Unix())) + } + + doAckBufSucceed(s, pkt.AckHandle, resp.Data()) +} + func handleMsgMhfGetKijuInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetKijuInfo) // Temporary canned response diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go index 88cacc41d..b6765f998 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -233,32 +233,6 @@ func handleMsgMhfUseKeepLoginBoost(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } -func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetUdInfo) - // Message that appears on the Diva Defense NPC and triggers the green exclamation mark - udInfos := []struct { - Text string - StartTime time.Time - EndTime time.Time - }{ - /*{ - Text: " ~C17【Erupe】 is dead event!\n\n■Features\n~C18 Dont bother walking around!\n~C17 Take down your DB by doing \n~C17 nearly anything!", - StartTime: Time_static().Add(time.Duration(-5) * time.Minute), // Event started 5 minutes ago, - EndTime: Time_static().Add(time.Duration(24) * time.Hour), // Event ends in 5 minutes, - }, */ - } - - resp := byteframe.NewByteFrame() - resp.WriteUint8(uint8(len(udInfos))) - for _, udInfo := range udInfos { - resp.WriteBytes(fixedSizeShiftJIS(udInfo.Text, 1024)) - resp.WriteUint32(uint32(udInfo.StartTime.Unix())) - resp.WriteUint32(uint32(udInfo.EndTime.Unix())) - } - - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) -} - func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfSetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) { From dc838adbcfb3abd2b42b7531a7966783d7c19319 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 26 Aug 2022 20:01:10 +1000 Subject: [PATCH 78/86] CanRecruit function in guild --- server/channelserver/handlers_guild.go | 2 +- server/channelserver/handlers_guild_member.go | 15 ++++++++++++++- server/channelserver/handlers_guild_scout.go | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index e13719dba..2a0ec1256 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -824,7 +824,7 @@ func handleMsgMhfOperateGuildMember(s *Session, p mhfpacket.MHFPacket) { actorCharacter, err := GetCharacterGuildData(s, s.charID) - if err != nil || (!actorCharacter.IsSubLeader() && guild.LeaderCharID != s.charID) || (!actorCharacter.Recruiter && guild.LeaderCharID != s.charID) { + if err != nil || (!actorCharacter.IsSubLeader() && guild.LeaderCharID != s.charID) { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } diff --git a/server/channelserver/handlers_guild_member.go b/server/channelserver/handlers_guild_member.go index 506c78bb1..2c3f0cd1a 100644 --- a/server/channelserver/handlers_guild_member.go +++ b/server/channelserver/handlers_guild_member.go @@ -26,8 +26,21 @@ type GuildMember struct { WeaponType uint16 `db:"weapon_type"` } +func (gm *GuildMember) CanRecruit() bool { + if gm.Recruiter { + return true + } + if gm.OrderIndex <= 3 { + return true + } + if gm.IsLeader { + return true + } + return false +} + func (gm *GuildMember) IsSubLeader() bool { - return gm.OrderIndex <= 3 && !gm.AvoidLeadership + return gm.OrderIndex <= 3 } func (gm *GuildMember) Save(s *Session) error { diff --git a/server/channelserver/handlers_guild_scout.go b/server/channelserver/handlers_guild_scout.go index 906f25154..f47c28bc6 100644 --- a/server/channelserver/handlers_guild_scout.go +++ b/server/channelserver/handlers_guild_scout.go @@ -21,7 +21,7 @@ func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) { panic(err) } - if actorCharGuildData == nil || (!actorCharGuildData.Recruiter && !actorCharGuildData.IsLeader && !actorCharGuildData.IsSubLeader()) { + if actorCharGuildData == nil || !actorCharGuildData.CanRecruit() { doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) return } @@ -98,7 +98,7 @@ func handleMsgMhfCancelGuildScout(s *Session, p mhfpacket.MHFPacket) { panic(err) } - if guildCharData == nil || (!guildCharData.Recruiter && !guildCharData.IsLeader && !guildCharData.IsSubLeader()) { + if guildCharData == nil || !guildCharData.CanRecruit() { doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) return } From 7495731f8706d36e530b6831a4cac0649dc167ce Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 26 Aug 2022 20:03:26 +1000 Subject: [PATCH 79/86] update default diva status --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 6f439baa8..847da5af2 100644 --- a/config.json +++ b/config.json @@ -12,7 +12,7 @@ "LogInboundMessages": false, "LogOutboundMessages": false, "MaxHexdumpLength": 256, - "DivaEvent": -1, + "DivaEvent": 0, "FestaEvent": -1, "TournamentEvent": 0, "MezFesEvent": true, From 20c6ad2c8cb2268e73ae29ea1fe000ddf37c3ee4 Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 26 Aug 2022 20:17:50 +1000 Subject: [PATCH 80/86] update default login notice --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 6df68d8c2..00e541adf 100644 --- a/config.json +++ b/config.json @@ -7,7 +7,7 @@ "serverName" : "", "EnableLauncherServer": false, "hideLoginNotice": false, - "loginNotice": "
Welcome to Erupe SU9 (Patch 1)!
Erupe is experimental software, we are not liable for any
issues caused by installing the software!

■Report bugs on Discord!

■Test everything!

■Don't talk to softlocking NPCs!

■Fork the code on GitHub!

Thank you to all of the contributors,

this wouldn't exist without you.", + "loginNotice": "
Welcome to Erupe SU9.1 Beta!
Erupe is experimental software, we are not liable for any
issues caused by installing the software!

■Report bugs on Discord!

■Test everything!

■Don't talk to softlocking NPCs!

■Fork the code on GitHub!

Thank you to all of the contributors,

this wouldn't exist without you.", "cleandb": false, "maxlauncherhr": false, "LogInboundMessages": false, From dd1460b74ba0237cd22690c2194bad685d29bd5c Mon Sep 17 00:00:00 2001 From: wish Date: Fri, 26 Aug 2022 21:02:31 +1000 Subject: [PATCH 81/86] implement return course --- patch-schema/return.sql | 9 +++++++++ server/signserver/dbutils.go | 19 ++++++++++++++++++- server/signserver/dsgn_resp.go | 12 +++--------- 3 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 patch-schema/return.sql diff --git a/patch-schema/return.sql b/patch-schema/return.sql new file mode 100644 index 000000000..4e09d7e47 --- /dev/null +++ b/patch-schema/return.sql @@ -0,0 +1,9 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.users + ADD COLUMN IF NOT EXISTS last_login timestamp without time zone; + +ALTER TABLE IF EXISTS public.users + ADD COLUMN IF NOT EXISTS return_expires timestamp without time zone; + +END; \ No newline at end of file diff --git a/server/signserver/dbutils.go b/server/signserver/dbutils.go index 973860ed5..d39d96c92 100644 --- a/server/signserver/dbutils.go +++ b/server/signserver/dbutils.go @@ -47,7 +47,7 @@ func (s *Server) registerDBAccount(username string, password string) error { return err } - _, err = s.db.Exec("INSERT INTO users (username, password) VALUES ($1, $2)", username, string(passwordHash)) + _, err = s.db.Exec("INSERT INTO users (username, password, return_expires) VALUES ($1, $2, $3)", username, string(passwordHash), time.Now().Add(time.Hour*24*30)) if err != nil { return err } @@ -95,6 +95,23 @@ func (s *Server) getCharactersForUser(uid int) ([]character, error) { return characters, nil } +func (s *Server) getReturnExpiry(uid int) time.Time { + var returnExpiry, lastLogin time.Time + s.db.Get(&lastLogin, "SELECT COALESCE(last_login, now()) FROM users WHERE id=$1", uid) + if time.Now().Add((time.Hour * 24) * -90).After(lastLogin) { + returnExpiry = time.Now().Add(time.Hour * 24 * 30) + s.db.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid) + } else { + err := s.db.Get(&returnExpiry, "SELECT return_expires FROM users WHERE id=$1", uid) + if err != nil { + returnExpiry = time.Now() + s.db.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid) + } + } + s.db.Exec("UPDATE users SET last_login=$1 WHERE id=$2", time.Now(), uid) + return returnExpiry +} + func (s *Server) getLastCID(uid int) uint32 { var lastPlayed uint32 _ = s.db.QueryRow("SELECT last_character FROM users WHERE id=$1", uid).Scan(&lastPlayed) diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 650188000..a169d93a1 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -28,6 +28,8 @@ func randSeq(n int) string { } func (s *Session) makeSignInResp(uid int) []byte { + returnExpiry := s.server.getReturnExpiry(uid) + // Get the characters from the DB. chars, err := s.server.getCharactersForUser(uid) if err != nil { @@ -115,15 +117,7 @@ func (s *Session) makeSignInResp(uid int) []byte { bf.WriteUint8(0x00) bf.WriteUint32(0xCA110001) bf.WriteUint32(0x4E200000) - - returning := false - // return course end time - if returning { - bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(30 * 24 * time.Hour).Unix())) - } else { - bf.WriteUint32(0) - } - + bf.WriteUint32(uint32(returnExpiry.Unix())) bf.WriteUint32(0x00000000) bf.WriteUint32(0x0A5197DF) From 63a829c91378ed74116e033e4c2680a9c9553aa7 Mon Sep 17 00:00:00 2001 From: Yslan Ramos Date: Sat, 27 Aug 2022 20:05:25 -0300 Subject: [PATCH 82/86] refactor: change `FindSessionByCharID` to search on channel sessions --- server/channelserver/sys_channel_server.go | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 6b2d46f3c..82f29d03a 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -11,6 +11,7 @@ import ( "erupe-ce/network/binpacket" "erupe-ce/network/mhfpacket" "erupe-ce/server/discordbot" + "github.com/jmoiron/sqlx" "go.uber.org/zap" ) @@ -343,19 +344,11 @@ func (s *Server) DiscordChannelSend(charName string, content string) { func (s *Server) FindSessionByCharID(charID uint32) *Session { for _, c := range s.Channels { - c.stagesLock.RLock() - for _, stage := range c.stages { - stage.RLock() - for client := range stage.clients { - if client.charID == charID { - stage.RUnlock() - c.stagesLock.RUnlock() - return client - } + for _, session := range c.sessions { + if session.charID == charID { + return session } - stage.RUnlock() } - c.stagesLock.RUnlock() } return nil } From 991b79a64db97836fea52d534dcb5c46a29bcc79 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 30 Aug 2022 18:38:51 +1000 Subject: [PATCH 83/86] stub rookie guild entry --- network/mhfpacket/msg_mhf_entry_rookie_guild.go | 17 +++++++++++------ server/channelserver/handlers_guild.go | 5 ++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/network/mhfpacket/msg_mhf_entry_rookie_guild.go b/network/mhfpacket/msg_mhf_entry_rookie_guild.go index d210be01a..e3f12660f 100644 --- a/network/mhfpacket/msg_mhf_entry_rookie_guild.go +++ b/network/mhfpacket/msg_mhf_entry_rookie_guild.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" ) // MsgMhfEntryRookieGuild represents the MSG_MHF_ENTRY_ROOKIE_GUILD -type MsgMhfEntryRookieGuild struct{} +type MsgMhfEntryRookieGuild struct { + AckHandle uint32 + Unk uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfEntryRookieGuild) Opcode() network.PacketID { @@ -18,7 +21,9 @@ func (m *MsgMhfEntryRookieGuild) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEntryRookieGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 2a0ec1256..6bbee7eba 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1886,7 +1886,10 @@ func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } -func handleMsgMhfEntryRookieGuild(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEntryRookieGuild(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEntryRookieGuild) + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) +} func handleMsgMhfUpdateForceGuildRank(s *Session, p mhfpacket.MHFPacket) {} From 8c42dcafe81b42657bdf8b398bb4f258751d0684 Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 31 Aug 2022 16:44:35 +1000 Subject: [PATCH 84/86] partial courses rework --- server/channelserver/handlers.go | 56 ++++++++++++-------------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 53e59d25f..6c49ba0af 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -1,12 +1,12 @@ package channelserver import ( - "bytes" "encoding/binary" "encoding/hex" "erupe-ce/common/stringsupport" "fmt" "io" + "math" "net" "strings" "time" @@ -14,9 +14,6 @@ import ( "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" ) @@ -77,40 +74,32 @@ func doAckSimpleFail(s *Session, ackHandle uint32, data []byte) { } func updateRights(s *Session) { + s.rights = uint32(0x0E) + s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.charID).Scan(&s.rights) + + rights := make([]mhfpacket.ClientRight, 0) + tempRights := s.rights + for i := 30; i > 0; i-- { + right := uint32(math.Pow(2, float64(i))) + if tempRights-right < 0x80000000 { + if i == 1 { + continue + } + rights = append(rights, mhfpacket.ClientRight{ID: uint16(i), Timestamp: 0x70DB59F0}) + tempRights -= right + } + } + rights = append(rights, mhfpacket.ClientRight{ID: 1, Timestamp: 0}) + update := &mhfpacket.MsgSysUpdateRight{ ClientRespAckHandle: 0, Bitfield: s.rights, - Rights: []mhfpacket.ClientRight{ - { - ID: 1, - Timestamp: 0, - }, - { - ID: 2, - Timestamp: 0x5FEA1781, - }, - { - ID: 3, - Timestamp: 0x5FEA1781, - }, - }, - UnkSize: 0, + Rights: rights, + UnkSize: 0, } s.QueueSendMHF(update) } -func fixedSizeShiftJIS(text string, size int) []byte { - r := bytes.NewBuffer([]byte(text)) - encoded, err := ioutil.ReadAll(transform.NewReader(r, japanese.ShiftJIS.NewEncoder())) - if err != nil { - panic(err) - } - - out := make([]byte, size) - copy(out, encoded) - out[len(out)-1] = 0 - return out -} func handleMsgHead(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysExtendThreshold(s *Session, p mhfpacket.MHFPacket) { @@ -148,14 +137,11 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { } } - rights := uint32(0x0E) - s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", pkt.CharID0).Scan(&rights) - s.Lock() s.charID = pkt.CharID0 - s.rights = rights s.token = pkt.LoginTokenString s.Unlock() + updateRights(s) bf := byteframe.NewByteFrame() bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp From fd873be34f3f855fd6d5a6373493769e5ee7f72e Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 31 Aug 2022 17:07:39 +1000 Subject: [PATCH 85/86] Update issue templates --- .github/ISSUE_TEMPLATE/bug.md | 21 +++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature.md | 17 +++++++++++++++++ .github/ISSUE_TEMPLATE/issue-bug.md | 21 +++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug.md create mode 100644 .github/ISSUE_TEMPLATE/feature.md create mode 100644 .github/ISSUE_TEMPLATE/issue-bug.md diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 000000000..375aa424a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,21 @@ +--- +name: Bug +about: Bug +title: '' +labels: bug +assignees: '' + +--- + +**Description**: +A clear and concise description of what the bug is. + +**Reproduction Steps**: +1. +2. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md new file mode 100644 index 000000000..1d8650d16 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -0,0 +1,17 @@ +--- +name: Feature +about: Feature +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to an existing issue?** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/issue-bug.md b/.github/ISSUE_TEMPLATE/issue-bug.md new file mode 100644 index 000000000..c550145db --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue-bug.md @@ -0,0 +1,21 @@ +--- +name: Issue-Bug +about: Bug Report +title: '' +labels: bug +assignees: '' + +--- + +**Description**: +A clear and concise description of what the bug is. + +**Reproduction Steps**: +1. +2. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. From 5fa634871470e95e8dead2a3a5618c05685a24fd Mon Sep 17 00:00:00 2001 From: wish Date: Wed, 31 Aug 2022 17:08:09 +1000 Subject: [PATCH 86/86] Delete issue-bug.md --- .github/ISSUE_TEMPLATE/issue-bug.md | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/issue-bug.md diff --git a/.github/ISSUE_TEMPLATE/issue-bug.md b/.github/ISSUE_TEMPLATE/issue-bug.md deleted file mode 100644 index c550145db..000000000 --- a/.github/ISSUE_TEMPLATE/issue-bug.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Issue-Bug -about: Bug Report -title: '' -labels: bug -assignees: '' - ---- - -**Description**: -A clear and concise description of what the bug is. - -**Reproduction Steps**: -1. -2. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Additional context** -Add any other context about the problem here.