mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-13 15:34:38 +01:00
@@ -13,9 +13,8 @@
|
||||
"LogInboundMessages": false,
|
||||
"LogOutboundMessages": false,
|
||||
"MaxHexdumpLength": 256,
|
||||
"Event": 0,
|
||||
"DivaEvent": 0,
|
||||
"FestaEvent": 0,
|
||||
"FestaEvent": -1,
|
||||
"TournamentEvent": 0,
|
||||
"MezFesEvent": true,
|
||||
"MezFesAlt": false,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
311
patch-schema/festa.sql
Normal file
311
patch-schema/festa.sql
Normal 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;
|
||||
13
patch-schema/mail-system-messages.sql
Normal file
13
patch-schema/mail-system-messages.sql
Normal file
@@ -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;
|
||||
@@ -2,11 +2,116 @@ package channelserver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"erupe-ce/common/stringsupport"
|
||||
"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, 36))
|
||||
return
|
||||
}
|
||||
timestamps = generateDivaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.DivaEvent), true)
|
||||
} else {
|
||||
timestamps = generateDivaTimestamps(s, start, false)
|
||||
}
|
||||
|
||||
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 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
|
||||
|
||||
@@ -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,75 +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
|
||||
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) {
|
||||
|
||||
@@ -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) {
|
||||
@@ -43,99 +42,200 @@ 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())
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // TS Current Time
|
||||
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
|
||||
}
|
||||
|
||||
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,8 +244,19 @@ 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)
|
||||
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
|
||||
}
|
||||
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,80 +264,156 @@ 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)
|
||||
applicant := false
|
||||
if guild != nil {
|
||||
applicant, _ = guild.HasApplicationForCharID(s, s.charID)
|
||||
}
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(0) // souls
|
||||
resp.WriteUint32(1) // unk
|
||||
resp.WriteUint32(1) // unk
|
||||
resp.WriteUint32(1) // unk, rank?
|
||||
resp.WriteUint32(1) // unk
|
||||
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.WriteUint32(guild.Souls)
|
||||
resp.WriteUint32(0) // unk
|
||||
resp.WriteUint32(0) // unk, rank?
|
||||
resp.WriteUint32(0) // unk
|
||||
resp.WriteUint32(0) // unk
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
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)))
|
||||
bf.WriteUint16(0) // Unk
|
||||
for _, member := range members {
|
||||
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?
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
@@ -121,11 +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,
|
||||
festival_colour,
|
||||
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
|
||||
@@ -134,23 +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
|
||||
@@ -158,8 +151,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, 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))
|
||||
@@ -643,6 +636,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)
|
||||
|
||||
@@ -667,6 +687,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))
|
||||
@@ -790,14 +818,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) {
|
||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -806,41 +834,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,
|
||||
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,
|
||||
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,
|
||||
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) {
|
||||
@@ -914,24 +946,12 @@ 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) // earnedSpecialHall
|
||||
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)
|
||||
}
|
||||
|
||||
// probably guild pugi properties, should be status, stamina and luck outfits
|
||||
bf.WriteBytes([]byte{
|
||||
@@ -952,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 {
|
||||
@@ -1015,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)))
|
||||
@@ -1063,15 +1083,11 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
} else {
|
||||
bf.WriteUint8(0x00)
|
||||
}
|
||||
bf.WriteUint8(0) // Unk
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1260,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)
|
||||
}
|
||||
@@ -1305,7 +1329,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 {
|
||||
@@ -1414,12 +1439,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
|
||||
}
|
||||
|
||||
|
||||
@@ -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"`
|
||||
@@ -25,12 +26,25 @@ 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 {
|
||||
_, 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(
|
||||
@@ -48,6 +62,7 @@ const guildMembersSelectSQL = `
|
||||
SELECT
|
||||
g.id as guild_id,
|
||||
joined_at,
|
||||
coalesce(souls, 0) as souls,
|
||||
c.name,
|
||||
character.character_id,
|
||||
coalesce(gc.order_index, 0) as order_index,
|
||||
|
||||
@@ -21,7 +21,7 @@ func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if actorCharGuildData == nil || !actorCharGuildData.Recruiter {
|
||||
if actorCharGuildData == nil || !actorCharGuildData.CanRecruit() {
|
||||
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.CanRecruit() {
|
||||
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)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
|
||||
senderName, err := getCharacterName(s, pkt.LeaderID)
|
||||
|
||||
if err != nil {
|
||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
successMail := Mail{
|
||||
SenderID: pkt.LeaderID,
|
||||
mail = append(mail, Mail{
|
||||
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,
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
err = successMail.Send(s, nil)
|
||||
|
||||
if err != nil {
|
||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
||||
panic(err)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user