Merge branch 'feature/festa' into merge/feature/festa

This commit is contained in:
wish
2022-08-12 00:41:32 +10:00
committed by GitHub
5 changed files with 574 additions and 91 deletions

View File

@@ -14,7 +14,7 @@
"MaxHexdumpLength": 256,
"Event": 0,
"DivaEvent": 0,
"FestaEvent": 0,
"FestaEvent": -1,
"TournamentEvent": 0,
"MezFesEvent": true,
"MezFesAlt": false,

311
patch-schema/festa.sql Normal file
View File

@@ -0,0 +1,311 @@
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
);
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)
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;

View File

@@ -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,73 +68,154 @@ 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("DELETE FROM festa_prizes_accepted")
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 {
midnight := uint32(midnight.Unix())
switch start {
case 1:
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] = midnight - 604800
timestamps[1] = midnight
timestamps[2] = timestamps[1] + 604800
timestamps[3] = timestamps[2] + 9000
timestamps[4] = timestamps[3] + 1240200
case 3:
timestamps[0] = midnight - 1209600
timestamps[1] = midnight - 604800
timestamps[2] = midnight
timestamps[3] = timestamps[2] + 9000
timestamps[4] = timestamps[3] + 1240200
}
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())
// Static bonus rewards
rewards, _ := hex.DecodeString("001901000007015E05F000000000000100000703E81B6300000000010100000C03E8000000000000000100000D0000000000000000000100000100000000000000000002000007015E05F000000000000200000703E81B6300000000010200000C03E8000000000000000200000D0000000000000000000200000400000000000000000003000007015E05F000000000000300000703E81B6300000000010300000C03E8000000000000000300000D0000000000000000000300000100000000000000000004000007015E05F000000000000400000703E81B6300000000010400000C03E8000000000000000400000D0000000000000000000400000400000000000000000005000007015E05F000000000000500000703E81B6300000000010500000C03E8000000000000000500000D00000000000000000005000001000000000000000000")
bf.WriteBytes(rewards)
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
}
unk := 0 // static rewards?
bf.WriteUint16(uint16(unk))
for i := 0; i < unk; i++ {
bf.WriteUint32(0)
bf.WriteUint16(0)
bf.WriteUint16(0)
bf.WriteUint32(0)
bf.WriteBool(false)
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("0001D4C001F4000411B6648100010001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100020001152A81F589A497A793C196B18B528E6D926381F52A000C952CE10003000109E54BE54E89B38F970029FDCE04000400001381818D84836C8352819993A294B091D1818100000811B6648100010001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100020001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100030001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100040001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100050001152A81F589A497A793C196B18B528E6D926381F52A0011B6648100060001152A81F589A497A793C196B18B528E6D926381F52A000C952CE10007000109E54BE54E89B38F9700000000000008000001000000000100001388000007D0000003E800000064012C00C8009600640032")
d, _ := hex.DecodeString("000000000000000100001388000007D0000003E800000064012C00C8009600640032")
bf.WriteBytes(d)
ps.Uint16(bf, "", false)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
@@ -144,17 +224,38 @@ 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(0) // unk
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
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
// 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,69 +265,132 @@ 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))
}
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))
}
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())
}

View File

@@ -56,6 +56,7 @@ type Guild struct {
PugiName3 string `db:"pugi_name_3"`
Recruiting bool `db:"recruiting"`
FestivalColour FestivalColour `db:"festival_colour"`
Souls uint32 `db:"souls"`
Rank uint16 `db:"rank"`
AllianceID uint32 `db:"alliance_id"`
Icon *GuildIcon `db:"icon"`
@@ -125,7 +126,14 @@ SELECT
pugi_name_2,
pugi_name_3,
recruiting,
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
@@ -139,14 +147,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
@@ -158,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, 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))

View File

@@ -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"`
@@ -48,6 +49,7 @@ const guildMembersSelectSQL = `
SELECT
g.id as guild_id,
joined_at,
souls,
c.name,
character.character_id,
coalesce(gc.order_index, 0) as order_index,