mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-02-06 10:06:53 +01:00
Merge branch 'main' into feature/diva
This commit is contained in:
@@ -157,6 +157,11 @@
|
|||||||
"Enabled": false,
|
"Enabled": false,
|
||||||
"Description": "Ban/Temp Ban a user",
|
"Description": "Ban/Temp Ban a user",
|
||||||
"Prefix": "ban"
|
"Prefix": "ban"
|
||||||
|
}, {
|
||||||
|
"Name": "Timer",
|
||||||
|
"Enabled": true,
|
||||||
|
"Description": "Toggle the Quest timer",
|
||||||
|
"Prefix": "timer"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Courses": [
|
"Courses": [
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package mhfpacket
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfChargeFesta represents the MSG_MHF_CHARGE_FESTA
|
// MsgMhfChargeFesta represents the MSG_MHF_CHARGE_FESTA
|
||||||
@@ -13,7 +13,8 @@ type MsgMhfChargeFesta struct {
|
|||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
FestaID uint32
|
FestaID uint32
|
||||||
GuildID uint32
|
GuildID uint32
|
||||||
Souls int
|
Souls []uint16
|
||||||
|
Auto bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -26,11 +27,10 @@ func (m *MsgMhfChargeFesta) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client
|
|||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.FestaID = bf.ReadUint32()
|
m.FestaID = bf.ReadUint32()
|
||||||
m.GuildID = bf.ReadUint32()
|
m.GuildID = bf.ReadUint32()
|
||||||
m.Souls = 0
|
|
||||||
for i := bf.ReadUint16(); i > 0; i-- {
|
for i := bf.ReadUint16(); i > 0; i-- {
|
||||||
m.Souls += int(bf.ReadUint16())
|
m.Souls = append(m.Souls, bf.ReadUint16())
|
||||||
}
|
}
|
||||||
_ = bf.ReadUint8() // Unk
|
m.Auto = bf.ReadBool()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
schemas/patch-schema/18-timer-toggle.sql
Normal file
5
schemas/patch-schema/18-timer-toggle.sql
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS timer bool;
|
||||||
|
|
||||||
|
END;
|
||||||
15
schemas/patch-schema/19-festa-submissions.sql
Normal file
15
schemas/patch-schema/19-festa-submissions.sql
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE festa_submissions (
|
||||||
|
character_id int NOT NULL,
|
||||||
|
guild_id int NOT NULL,
|
||||||
|
trial_type int NOT NULL,
|
||||||
|
souls int NOT NULL,
|
||||||
|
timestamp timestamp with time zone NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE guild_characters DROP COLUMN souls;
|
||||||
|
|
||||||
|
ALTER TYPE festival_colour RENAME TO festival_color;
|
||||||
|
|
||||||
|
END;
|
||||||
@@ -144,6 +144,19 @@ func parseChatCommand(s *Session, command string) {
|
|||||||
} else {
|
} else {
|
||||||
sendServerChatMessage(s, s.server.i18n.commands.noOp)
|
sendServerChatMessage(s, s.server.i18n.commands.noOp)
|
||||||
}
|
}
|
||||||
|
case commands["Timer"].Prefix:
|
||||||
|
if commands["Timer"].Enabled || s.isOp() {
|
||||||
|
var state bool
|
||||||
|
s.server.db.QueryRow(`SELECT COALESCE(timer, false) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&state)
|
||||||
|
s.server.db.Exec(`UPDATE users u SET timer=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, !state, s.charID)
|
||||||
|
if state {
|
||||||
|
sendServerChatMessage(s, s.server.i18n.commands.timer.disabled)
|
||||||
|
} else {
|
||||||
|
sendServerChatMessage(s, s.server.i18n.commands.timer.enabled)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendDisabledCommandMessage(s, commands["Timer"])
|
||||||
|
}
|
||||||
case commands["PSN"].Prefix:
|
case commands["PSN"].Prefix:
|
||||||
if commands["PSN"].Enabled || s.isOp() {
|
if commands["PSN"].Enabled || s.isOp() {
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
@@ -413,10 +426,14 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x03 && len(pkt.RawDataPayload) == 0x10 {
|
if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x03 && len(pkt.RawDataPayload) == 0x10 {
|
||||||
if tmp.ReadUint16() == 0x0002 && tmp.ReadUint8() == 0x18 {
|
if tmp.ReadUint16() == 0x0002 && tmp.ReadUint8() == 0x18 {
|
||||||
|
var timer bool
|
||||||
|
s.server.db.QueryRow(`SELECT COALESCE(timer, false) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&timer)
|
||||||
|
if timer {
|
||||||
_ = tmp.ReadBytes(9)
|
_ = tmp.ReadBytes(9)
|
||||||
tmp.SetLE()
|
tmp.SetLE()
|
||||||
frame := tmp.ReadUint32()
|
frame := tmp.ReadUint32()
|
||||||
sendServerChatMessage(s, fmt.Sprintf("TIME : %d'%d.%03d (%dframe)", frame/30/60, frame/30%60, int(math.Round(float64(frame%30*100)/3)), frame))
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.timer, frame/30/60/60, frame/30/60, frame/30%60, int(math.Round(float64(frame%30*100)/3)), frame))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,17 @@ func getPointers() map[SavePointer]int {
|
|||||||
pointers[pGalleryData] = 72064
|
pointers[pGalleryData] = 72064
|
||||||
pointers[pGardenData] = 74424
|
pointers[pGardenData] = 74424
|
||||||
pointers[pRP] = 74614
|
pointers[pRP] = 74614
|
||||||
|
case _config.S6:
|
||||||
|
pointers[pWeaponID] = 12522
|
||||||
|
pointers[pWeaponType] = 12789
|
||||||
|
pointers[pHouseTier] = 13900
|
||||||
|
pointers[pToreData] = 14228
|
||||||
|
pointers[pHRP] = 14550
|
||||||
|
pointers[pHouseData] = 14561
|
||||||
|
pointers[pBookshelfData] = 9118 // Probably same here
|
||||||
|
pointers[pGalleryData] = 24064
|
||||||
|
pointers[pGardenData] = 26424
|
||||||
|
pointers[pRP] = 26614
|
||||||
}
|
}
|
||||||
if _config.ErupeConfig.RealClientMode == _config.G5 {
|
if _config.ErupeConfig.RealClientMode == _config.G5 {
|
||||||
pointers[lBookshelfData] = 5548
|
pointers[lBookshelfData] = 5548
|
||||||
@@ -212,7 +223,7 @@ func (save *CharacterSaveData) updateStructWithSaveData() {
|
|||||||
save.Gender = false
|
save.Gender = false
|
||||||
}
|
}
|
||||||
if !save.IsNewCharacter {
|
if !save.IsNewCharacter {
|
||||||
if _config.ErupeConfig.RealClientMode >= _config.F4 {
|
if _config.ErupeConfig.RealClientMode >= _config.S6 {
|
||||||
save.RP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pRP] : save.Pointers[pRP]+2])
|
save.RP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pRP] : save.Pointers[pRP]+2])
|
||||||
save.HouseTier = save.decompSave[save.Pointers[pHouseTier] : save.Pointers[pHouseTier]+5]
|
save.HouseTier = save.decompSave[save.Pointers[pHouseTier] : save.Pointers[pHouseTier]+5]
|
||||||
save.HouseData = save.decompSave[save.Pointers[pHouseData] : save.Pointers[pHouseData]+195]
|
save.HouseData = save.decompSave[save.Pointers[pHouseData] : save.Pointers[pHouseData]+195]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package channelserver
|
package channelserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"erupe-ce/common/mhfmon"
|
||||||
"erupe-ce/common/stringsupport"
|
"erupe-ce/common/stringsupport"
|
||||||
_config "erupe-ce/config"
|
_config "erupe-ce/config"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -1042,34 +1043,34 @@ func handleMsgMhfGetPaperData(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
{1105, 1, 10, 500, 0, 0, 0},
|
{1105, 1, 10, 500, 0, 0, 0},
|
||||||
{1105, 2, 10, 500, 0, 0, 0},
|
{1105, 2, 10, 500, 0, 0, 0},
|
||||||
// setServerBoss
|
// setServerBoss
|
||||||
{2001, 1, 17, 58, 0, 6, 700},
|
{2001, 1, mhfmon.Gravios, 58, 0, 6, 700},
|
||||||
{2001, 1, 20, 58, 0, 3, 200},
|
{2001, 1, mhfmon.Gypceros, 58, 0, 3, 200},
|
||||||
{2001, 1, 22, 58, 0, 7, 250},
|
{2001, 1, mhfmon.Basarios, 58, 0, 7, 250},
|
||||||
{2001, 1, 27, 58, 0, 1, 100},
|
{2001, 1, mhfmon.Velocidrome, 58, 0, 1, 100},
|
||||||
{2001, 1, 53, 58, 0, 8, 1000},
|
{2001, 1, mhfmon.Rajang, 58, 0, 8, 1000},
|
||||||
{2001, 1, 67, 58, 0, 9, 500},
|
{2001, 1, mhfmon.ShogunCeanataur, 58, 0, 9, 500},
|
||||||
{2001, 1, 68, 58, 0, 2, 150},
|
{2001, 1, mhfmon.Bulldrome, 58, 0, 2, 150},
|
||||||
{2001, 1, 74, 58, 0, 4, 200},
|
{2001, 1, mhfmon.Hypnocatrice, 58, 0, 4, 200},
|
||||||
{2001, 1, 75, 58, 0, 5, 500},
|
{2001, 1, mhfmon.Lavasioth, 58, 0, 5, 500},
|
||||||
{2001, 1, 76, 58, 0, 10, 800},
|
{2001, 1, mhfmon.Tigrex, 58, 0, 10, 800},
|
||||||
{2001, 1, 80, 58, 0, 11, 900},
|
{2001, 1, mhfmon.Espinas, 58, 0, 11, 900},
|
||||||
{2001, 1, 89, 58, 0, 12, 600},
|
{2001, 1, mhfmon.Pariapuria, 58, 0, 12, 600},
|
||||||
{2001, 2, 17, 60, 0, 6, 700},
|
{2001, 2, mhfmon.Gravios, 60, 0, 6, 700},
|
||||||
{2001, 2, 20, 60, 0, 3, 200},
|
{2001, 2, mhfmon.Gypceros, 60, 0, 3, 200},
|
||||||
{2001, 2, 22, 60, 0, 7, 350},
|
{2001, 2, mhfmon.Basarios, 60, 0, 7, 350},
|
||||||
{2001, 2, 27, 60, 0, 1, 100},
|
{2001, 2, mhfmon.Velocidrome, 60, 0, 1, 100},
|
||||||
{2001, 2, 39, 60, 0, 13, 200},
|
{2001, 2, mhfmon.PurpleGypceros, 60, 0, 13, 200},
|
||||||
{2001, 2, 40, 60, 0, 15, 600},
|
{2001, 2, mhfmon.YianGaruga, 60, 0, 15, 600},
|
||||||
{2001, 2, 53, 60, 0, 8, 1000},
|
{2001, 2, mhfmon.Rajang, 60, 0, 8, 1000},
|
||||||
{2001, 2, 67, 60, 0, 2, 500},
|
{2001, 2, mhfmon.ShogunCeanataur, 60, 0, 2, 500},
|
||||||
{2001, 2, 68, 60, 0, 9, 150},
|
{2001, 2, mhfmon.Bulldrome, 60, 0, 9, 150},
|
||||||
{2001, 2, 74, 60, 0, 4, 200},
|
{2001, 2, mhfmon.Hypnocatrice, 60, 0, 4, 200},
|
||||||
{2001, 2, 75, 60, 0, 5, 500},
|
{2001, 2, mhfmon.Lavasioth, 60, 0, 5, 500},
|
||||||
{2001, 2, 76, 60, 0, 10, 800},
|
{2001, 2, mhfmon.Tigrex, 60, 0, 10, 800},
|
||||||
{2001, 2, 80, 60, 0, 11, 900},
|
{2001, 2, mhfmon.Espinas, 60, 0, 11, 900},
|
||||||
{2001, 2, 81, 60, 0, 14, 900},
|
{2001, 2, mhfmon.BurningEspinas, 60, 0, 14, 900},
|
||||||
{2001, 2, 89, 60, 0, 12, 600},
|
{2001, 2, mhfmon.Pariapuria, 60, 0, 12, 600},
|
||||||
{2001, 2, 94, 60, 0, 16, 1000},
|
{2001, 2, mhfmon.Dyuragaua, 60, 0, 16, 1000},
|
||||||
}
|
}
|
||||||
case 6:
|
case 6:
|
||||||
paperData = []PaperData{
|
paperData = []PaperData{
|
||||||
|
|||||||
@@ -95,8 +95,9 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func cleanupFesta(s *Session) {
|
func cleanupFesta(s *Session) {
|
||||||
s.server.db.Exec("DELETE FROM events WHERE event_type='festa'")
|
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_registrations")
|
||||||
|
s.server.db.Exec("DELETE FROM festa_submissions")
|
||||||
s.server.db.Exec("DELETE FROM festa_prizes_accepted")
|
s.server.db.Exec("DELETE FROM festa_prizes_accepted")
|
||||||
s.server.db.Exec("UPDATE guild_characters SET souls=0, trial_vote=NULL")
|
s.server.db.Exec("UPDATE guild_characters SET trial_vote=NULL")
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
|
func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
|
||||||
@@ -147,7 +148,7 @@ type FestaTrial struct {
|
|||||||
TimesReq uint16 `db:"times_req"`
|
TimesReq uint16 `db:"times_req"`
|
||||||
Locale uint16 `db:"locale_req"`
|
Locale uint16 `db:"locale_req"`
|
||||||
Reward uint16 `db:"reward"`
|
Reward uint16 `db:"reward"`
|
||||||
Monopoly FestivalColour `db:"monopoly"`
|
Monopoly FestivalColor `db:"monopoly"`
|
||||||
Unk uint16
|
Unk uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,8 +190,8 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var blueSouls, redSouls uint32
|
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 COALESCE(SUM(fs.souls), 0) AS souls FROM festa_registrations fr LEFT JOIN festa_submissions fs ON fr.guild_id = fs.guild_id AND 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)
|
s.server.db.QueryRow(`SELECT COALESCE(SUM(fs.souls), 0) AS souls FROM festa_registrations fr LEFT JOIN festa_submissions fs ON fr.guild_id = fs.guild_id AND fr.team = 'red'`).Scan(&redSouls)
|
||||||
|
|
||||||
bf.WriteUint32(id)
|
bf.WriteUint32(id)
|
||||||
for _, timestamp := range timestamps {
|
for _, timestamp := range timestamps {
|
||||||
@@ -209,11 +210,11 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
COALESCE(CASE
|
COALESCE(CASE
|
||||||
WHEN COUNT(gc.id) FILTER (WHERE fr.team = 'blue' AND gc.trial_vote = ft.id) >
|
WHEN COUNT(gc.id) FILTER (WHERE fr.team = 'blue' AND gc.trial_vote = ft.id) >
|
||||||
COUNT(gc.id) FILTER (WHERE fr.team = 'red' AND gc.trial_vote = ft.id)
|
COUNT(gc.id) FILTER (WHERE fr.team = 'red' AND gc.trial_vote = ft.id)
|
||||||
THEN CAST('blue' AS public.festival_colour)
|
THEN CAST('blue' AS public.festival_color)
|
||||||
WHEN COUNT(gc.id) FILTER (WHERE fr.team = 'red' AND gc.trial_vote = ft.id) >
|
WHEN COUNT(gc.id) FILTER (WHERE fr.team = 'red' AND gc.trial_vote = ft.id) >
|
||||||
COUNT(gc.id) FILTER (WHERE fr.team = 'blue' AND gc.trial_vote = ft.id)
|
COUNT(gc.id) FILTER (WHERE fr.team = 'blue' AND gc.trial_vote = ft.id)
|
||||||
THEN CAST('red' AS public.festival_colour)
|
THEN CAST('red' AS public.festival_color)
|
||||||
END, CAST('none' AS public.festival_colour)) AS monopoly
|
END, CAST('none' AS public.festival_color)) AS monopoly
|
||||||
FROM public.festa_trials ft
|
FROM public.festa_trials ft
|
||||||
LEFT JOIN public.guild_characters gc ON ft.id = gc.trial_vote
|
LEFT JOIN public.guild_characters gc ON ft.id = gc.trial_vote
|
||||||
LEFT JOIN public.festa_registrations fr ON gc.guild_id = fr.guild_id
|
LEFT JOIN public.festa_registrations fr ON gc.guild_id = fr.guild_id
|
||||||
@@ -233,9 +234,11 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf.WriteUint16(trial.TimesReq)
|
bf.WriteUint16(trial.TimesReq)
|
||||||
bf.WriteUint16(trial.Locale)
|
bf.WriteUint16(trial.Locale)
|
||||||
bf.WriteUint16(trial.Reward)
|
bf.WriteUint16(trial.Reward)
|
||||||
bf.WriteInt16(int16(FestivalColourCodes[trial.Monopoly]))
|
bf.WriteInt16(FestivalColorCodes[trial.Monopoly])
|
||||||
|
if _config.ErupeConfig.RealClientMode >= _config.F4 { // Not in S6.0
|
||||||
bf.WriteUint16(trial.Unk)
|
bf.WriteUint16(trial.Unk)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The Winner and Loser Armor IDs are missing
|
// The Winner and Loser Armor IDs are missing
|
||||||
// Item 7011 may not exist in older versions, remove to prevent crashes
|
// Item 7011 may not exist in older versions, remove to prevent crashes
|
||||||
@@ -291,22 +294,45 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
bf.WriteUint16(500)
|
bf.WriteUint16(500)
|
||||||
|
|
||||||
categoryWinners := uint16(0) // NYI
|
var temp uint32
|
||||||
bf.WriteUint16(categoryWinners)
|
bf.WriteUint16(4)
|
||||||
for i := uint16(0); i < categoryWinners; i++ {
|
for i := uint16(0); i < 4; i++ {
|
||||||
bf.WriteUint32(0) // Guild ID
|
var guildID uint32
|
||||||
bf.WriteUint16(i + 1) // Category ID
|
var guildName string
|
||||||
bf.WriteUint16(0) // Festa Team
|
var guildTeam = FestivalColorNone
|
||||||
ps.Uint8(bf, "", true) // Guild Name
|
s.server.db.QueryRow(`
|
||||||
|
SELECT fs.guild_id, g.name, fr.team, SUM(fs.souls) as _
|
||||||
|
FROM festa_submissions fs
|
||||||
|
LEFT JOIN festa_registrations fr ON fs.guild_id = fr.guild_id
|
||||||
|
LEFT JOIN guilds g ON fs.guild_id = g.id
|
||||||
|
WHERE fs.trial_type = $1
|
||||||
|
GROUP BY fs.guild_id, g.name, fr.team
|
||||||
|
ORDER BY _ DESC LIMIT 1
|
||||||
|
`, i+1).Scan(&guildID, &guildName, &guildTeam, &temp)
|
||||||
|
bf.WriteUint32(guildID)
|
||||||
|
bf.WriteUint16(i + 1)
|
||||||
|
bf.WriteInt16(FestivalColorCodes[guildTeam])
|
||||||
|
ps.Uint8(bf, guildName, true)
|
||||||
}
|
}
|
||||||
|
bf.WriteUint16(7)
|
||||||
dailyWinners := uint16(0) // NYI
|
for i := uint16(0); i < 7; i++ {
|
||||||
bf.WriteUint16(dailyWinners)
|
var guildID uint32
|
||||||
for i := uint16(0); i < dailyWinners; i++ {
|
var guildName string
|
||||||
bf.WriteUint32(0) // Guild ID
|
var guildTeam = FestivalColorNone
|
||||||
bf.WriteUint16(i + 1) // Category ID
|
offset := 86400 * uint32(i)
|
||||||
bf.WriteUint16(0) // Festa Team
|
s.server.db.QueryRow(`
|
||||||
ps.Uint8(bf, "", true) // Guild Name
|
SELECT fs.guild_id, g.name, fr.team, SUM(fs.souls) as _
|
||||||
|
FROM festa_submissions fs
|
||||||
|
LEFT JOIN festa_registrations fr ON fs.guild_id = fr.guild_id
|
||||||
|
LEFT JOIN guilds g ON fs.guild_id = g.id
|
||||||
|
WHERE EXTRACT(EPOCH FROM fs.timestamp)::int > $1 AND EXTRACT(EPOCH FROM fs.timestamp)::int < $2
|
||||||
|
GROUP BY fs.guild_id, g.name, fr.team
|
||||||
|
ORDER BY _ DESC LIMIT 1
|
||||||
|
`, timestamps[1]+offset, timestamps[1]+offset+86400).Scan(&guildID, &guildName, &guildTeam, &temp)
|
||||||
|
bf.WriteUint32(guildID)
|
||||||
|
bf.WriteUint16(i + 1)
|
||||||
|
bf.WriteInt16(FestivalColorCodes[guildTeam])
|
||||||
|
ps.Uint8(bf, guildName, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteUint32(0) // Clan goal
|
bf.WriteUint32(0) // Clan goal
|
||||||
@@ -321,7 +347,9 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf.WriteUint16(100) // Normal rate
|
bf.WriteUint16(100) // Normal rate
|
||||||
bf.WriteUint16(50) // 50% penalty
|
bf.WriteUint16(50) // 50% penalty
|
||||||
|
|
||||||
|
if _config.ErupeConfig.RealClientMode >= _config.G52 {
|
||||||
ps.Uint16(bf, "", false)
|
ps.Uint16(bf, "", false)
|
||||||
|
}
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,7 +366,7 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var souls, exists uint32
|
var souls, exists uint32
|
||||||
s.server.db.QueryRow("SELECT souls FROM guild_characters WHERE character_id=$1", s.charID).Scan(&souls)
|
s.server.db.QueryRow(`SELECT COALESCE((SELECT SUM(souls) FROM festa_submissions WHERE character_id=$1), 0)`, s.charID).Scan(&souls)
|
||||||
err = s.server.db.QueryRow("SELECT prize_id FROM festa_prizes_accepted WHERE prize_id=0 AND character_id=$1", s.charID).Scan(&exists)
|
err = s.server.db.QueryRow("SELECT prize_id FROM festa_prizes_accepted WHERE prize_id=0 AND character_id=$1", s.charID).Scan(&exists)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint32(souls)
|
bf.WriteUint32(souls)
|
||||||
@@ -371,10 +399,10 @@ func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp.WriteUint32(guild.Souls)
|
resp.WriteUint32(guild.Souls)
|
||||||
resp.WriteInt32(0) // unk
|
resp.WriteInt32(1) // unk
|
||||||
resp.WriteInt32(1) // unk, rank?
|
resp.WriteInt32(1) // unk, rank?
|
||||||
resp.WriteInt32(0) // unk
|
resp.WriteInt32(1) // unk
|
||||||
resp.WriteInt32(0) // unk
|
resp.WriteInt32(1) // unk
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,8 +432,13 @@ func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf.WriteUint16(0) // Unk
|
bf.WriteUint16(0) // Unk
|
||||||
for _, member := range validMembers {
|
for _, member := range validMembers {
|
||||||
bf.WriteUint32(member.CharID)
|
bf.WriteUint32(member.CharID)
|
||||||
|
if _config.ErupeConfig.RealClientMode <= _config.Z1 {
|
||||||
|
bf.WriteUint16(uint16(member.Souls))
|
||||||
|
bf.WriteUint16(0)
|
||||||
|
} else {
|
||||||
bf.WriteUint32(member.Souls)
|
bf.WriteUint32(member.Souls)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,7 +469,14 @@ func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfChargeFesta)
|
pkt := p.(*mhfpacket.MsgMhfChargeFesta)
|
||||||
s.server.db.Exec("UPDATE guild_characters SET souls=souls+$1 WHERE character_id=$2", pkt.Souls, s.charID)
|
tx, _ := s.server.db.Begin()
|
||||||
|
for i := range pkt.Souls {
|
||||||
|
if pkt.Souls[i] == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, _ = tx.Exec(`INSERT INTO festa_submissions VALUES ($1, $2, $3, $4, now())`, s.charID, pkt.GuildID, i, pkt.Souls[i])
|
||||||
|
}
|
||||||
|
_ = tx.Commit()
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,18 +21,18 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FestivalColour string
|
type FestivalColor string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FestivalColourNone FestivalColour = "none"
|
FestivalColorNone FestivalColor = "none"
|
||||||
FestivalColourBlue FestivalColour = "blue"
|
FestivalColorBlue FestivalColor = "blue"
|
||||||
FestivalColourRed FestivalColour = "red"
|
FestivalColorRed FestivalColor = "red"
|
||||||
)
|
)
|
||||||
|
|
||||||
var FestivalColourCodes = map[FestivalColour]int8{
|
var FestivalColorCodes = map[FestivalColor]int16{
|
||||||
FestivalColourNone: -1,
|
FestivalColorNone: -1,
|
||||||
FestivalColourBlue: 0,
|
FestivalColorBlue: 0,
|
||||||
FestivalColourRed: 1,
|
FestivalColorRed: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
type GuildApplicationType string
|
type GuildApplicationType string
|
||||||
@@ -60,7 +60,7 @@ type Guild struct {
|
|||||||
PugiOutfit3 uint8 `db:"pugi_outfit_3"`
|
PugiOutfit3 uint8 `db:"pugi_outfit_3"`
|
||||||
PugiOutfits uint32 `db:"pugi_outfits"`
|
PugiOutfits uint32 `db:"pugi_outfits"`
|
||||||
Recruiting bool `db:"recruiting"`
|
Recruiting bool `db:"recruiting"`
|
||||||
FestivalColour FestivalColour `db:"festival_colour"`
|
FestivalColor FestivalColor `db:"festival_color"`
|
||||||
Souls uint32 `db:"souls"`
|
Souls uint32 `db:"souls"`
|
||||||
AllianceID uint32 `db:"alliance_id"`
|
AllianceID uint32 `db:"alliance_id"`
|
||||||
Icon *GuildIcon `db:"icon"`
|
Icon *GuildIcon `db:"icon"`
|
||||||
@@ -157,7 +157,7 @@ SELECT
|
|||||||
sub_motto,
|
sub_motto,
|
||||||
created_at,
|
created_at,
|
||||||
leader_id,
|
leader_id,
|
||||||
lc.name as leader_name,
|
c.name AS leader_name,
|
||||||
comment,
|
comment,
|
||||||
COALESCE(pugi_name_1, '') AS pugi_name_1,
|
COALESCE(pugi_name_1, '') AS pugi_name_1,
|
||||||
COALESCE(pugi_name_2, '') AS pugi_name_2,
|
COALESCE(pugi_name_2, '') AS pugi_name_2,
|
||||||
@@ -167,8 +167,8 @@ SELECT
|
|||||||
pugi_outfit_3,
|
pugi_outfit_3,
|
||||||
pugi_outfits,
|
pugi_outfits,
|
||||||
recruiting,
|
recruiting,
|
||||||
COALESCE((SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id), 'none') AS festival_colour,
|
COALESCE((SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id), 'none') AS festival_color,
|
||||||
(SELECT SUM(souls) FROM guild_characters gc WHERE gc.guild_id = g.id) AS souls,
|
COALESCE((SELECT SUM(fs.souls) FROM festa_submissions fs WHERE fs.guild_id=g.id), 0) AS souls,
|
||||||
COALESCE((
|
COALESCE((
|
||||||
SELECT id FROM guild_alliances ga WHERE
|
SELECT id FROM guild_alliances ga WHERE
|
||||||
ga.parent_id = g.id OR
|
ga.parent_id = g.id OR
|
||||||
@@ -178,8 +178,8 @@ SELECT
|
|||||||
icon,
|
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
|
FROM guilds g
|
||||||
JOIN guild_characters lgc ON lgc.character_id = leader_id
|
JOIN guild_characters gc ON gc.character_id = leader_id
|
||||||
JOIN characters lc on leader_id = lc.id
|
JOIN characters c on leader_id = c.id
|
||||||
`
|
`
|
||||||
|
|
||||||
func (guild *Guild) Save(s *Session) error {
|
func (guild *Guild) Save(s *Session) error {
|
||||||
@@ -967,7 +967,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf.WriteUint8(uint8(len(guildLeaderName)))
|
bf.WriteUint8(uint8(len(guildLeaderName)))
|
||||||
bf.WriteBytes(guildName)
|
bf.WriteBytes(guildName)
|
||||||
bf.WriteBytes(guildComment)
|
bf.WriteBytes(guildComment)
|
||||||
bf.WriteInt8(FestivalColourCodes[guild.FestivalColour])
|
bf.WriteInt8(int8(FestivalColorCodes[guild.FestivalColor]))
|
||||||
bf.WriteUint32(guild.RankRP)
|
bf.WriteUint32(guild.RankRP)
|
||||||
bf.WriteBytes(guildLeaderName)
|
bf.WriteBytes(guildLeaderName)
|
||||||
bf.WriteUint32(0) // Unk
|
bf.WriteUint32(0) // Unk
|
||||||
|
|||||||
@@ -61,41 +61,35 @@ func (gm *GuildMember) Save(s *Session) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const guildMembersSelectSQL = `
|
const guildMembersSelectSQL = `
|
||||||
|
SELECT * FROM (
|
||||||
SELECT
|
SELECT
|
||||||
g.id as guild_id,
|
g.id AS guild_id,
|
||||||
joined_at,
|
joined_at,
|
||||||
coalesce(souls, 0) as souls,
|
COALESCE((SELECT SUM(souls) FROM festa_submissions fs WHERE fs.character_id=c.id), 0) AS souls,
|
||||||
COALESCE(rp_today, 0) AS rp_today,
|
COALESCE(rp_today, 0) AS rp_today,
|
||||||
COALESCE(rp_yesterday, 0) AS rp_yesterday,
|
COALESCE(rp_yesterday, 0) AS rp_yesterday,
|
||||||
c.name,
|
c.name,
|
||||||
character.character_id,
|
c.id AS character_id,
|
||||||
coalesce(gc.order_index, 0) as order_index,
|
COALESCE(order_index, 0) AS order_index,
|
||||||
c.last_login,
|
c.last_login,
|
||||||
coalesce(gc.recruiter, false) as recruiter,
|
COALESCE(recruiter, false) AS recruiter,
|
||||||
coalesce(gc.avoid_leadership, false) as avoid_leadership,
|
COALESCE(avoid_leadership, false) AS avoid_leadership,
|
||||||
c.hrp,
|
c.hrp,
|
||||||
c.gr,
|
c.gr,
|
||||||
c.weapon_id,
|
c.weapon_id,
|
||||||
c.weapon_type,
|
c.weapon_type,
|
||||||
character.is_applicant,
|
EXISTS(SELECT 1 FROM guild_applications ga WHERE ga.character_id=c.id AND application_type='applied') AS is_applicant,
|
||||||
CASE WHEN g.leader_id = c.id THEN 1 ELSE 0 END as is_leader
|
CASE WHEN g.leader_id = c.id THEN true ELSE false 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
|
FROM guild_characters gc
|
||||||
) character
|
LEFT JOIN characters c ON c.id = gc.character_id
|
||||||
JOIN characters c on character.character_id = c.id
|
LEFT JOIN guilds g ON g.id = gc.guild_id
|
||||||
LEFT JOIN guild_characters gc ON gc.character_id = character.character_id
|
) AS subquery
|
||||||
JOIN guilds g ON g.id = character.guild_id
|
|
||||||
`
|
`
|
||||||
|
|
||||||
func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMember, error) {
|
func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMember, error) {
|
||||||
rows, err := s.server.db.Queryx(fmt.Sprintf(`
|
rows, err := s.server.db.Queryx(fmt.Sprintf(`
|
||||||
%s
|
%s
|
||||||
WHERE character.guild_id = $1 AND is_applicant = $2
|
WHERE guild_id = $1 AND is_applicant = $2
|
||||||
`, guildMembersSelectSQL), guildID, applicants)
|
`, guildMembersSelectSQL), guildID, applicants)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -121,7 +115,7 @@ func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMembe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetCharacterGuildData(s *Session, charID uint32) (*GuildMember, error) {
|
func GetCharacterGuildData(s *Session, charID uint32) (*GuildMember, error) {
|
||||||
rows, err := s.server.db.Queryx(fmt.Sprintf("%s WHERE character.character_id=$1", guildMembersSelectSQL), charID)
|
rows, err := s.server.db.Queryx(fmt.Sprintf("%s WHERE character_id=$1", guildMembersSelectSQL), charID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error(fmt.Sprintf("failed to retrieve membership data for character '%d'", charID))
|
s.logger.Error(fmt.Sprintf("failed to retrieve membership data for character '%d'", charID))
|
||||||
|
|||||||
@@ -22,6 +22,32 @@ type tuneValue struct {
|
|||||||
Value uint16
|
Value uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findSubSliceIndices(data []byte, sub []byte) []int {
|
||||||
|
var indices []int
|
||||||
|
lenSub := len(sub)
|
||||||
|
for i := 0; i < len(data); i++ {
|
||||||
|
if i+lenSub > len(data) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if equal(data[i:i+lenSub], sub) {
|
||||||
|
indices = append(indices, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return indices
|
||||||
|
}
|
||||||
|
|
||||||
|
func equal(a, b []byte) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, v := range a {
|
||||||
|
if v != b[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func BackportQuest(data []byte) []byte {
|
func BackportQuest(data []byte) []byte {
|
||||||
wp := binary.LittleEndian.Uint32(data[0:4]) + 96
|
wp := binary.LittleEndian.Uint32(data[0:4]) + 96
|
||||||
rp := wp + 4
|
rp := wp + 4
|
||||||
@@ -32,7 +58,33 @@ func BackportQuest(data []byte) []byte {
|
|||||||
}
|
}
|
||||||
copy(data[wp:wp+4], data[rp:rp+4])
|
copy(data[wp:wp+4], data[rp:rp+4])
|
||||||
}
|
}
|
||||||
copy(data[wp:wp+180], data[rp:rp+180])
|
|
||||||
|
fillLength := uint32(108)
|
||||||
|
if _config.ErupeConfig.RealClientMode <= _config.S6 {
|
||||||
|
fillLength = 44
|
||||||
|
} else if _config.ErupeConfig.RealClientMode <= _config.F5 {
|
||||||
|
fillLength = 52
|
||||||
|
} else if _config.ErupeConfig.RealClientMode <= _config.G101 {
|
||||||
|
fillLength = 76
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(data[wp:wp+fillLength], data[rp:rp+fillLength])
|
||||||
|
if _config.ErupeConfig.RealClientMode <= _config.G91 {
|
||||||
|
patterns := [][]byte{
|
||||||
|
{0x0A, 0x00, 0x01, 0x33, 0xD7, 0x00}, // 10% Armor Sphere -> Stone
|
||||||
|
{0x06, 0x00, 0x02, 0x33, 0xD8, 0x00}, // 6% Armor Sphere+ -> Iron Ore
|
||||||
|
{0x0A, 0x00, 0x03, 0x33, 0xD7, 0x00}, // 10% Adv Armor Sphere -> Stone
|
||||||
|
{0x06, 0x00, 0x04, 0x33, 0xDB, 0x00}, // 6% Hard Armor Sphere -> Dragonite Ore
|
||||||
|
{0x0A, 0x00, 0x05, 0x33, 0xD9, 0x00}, // 10% Heaven Armor Sphere -> Earth Crystal
|
||||||
|
{0x06, 0x00, 0x06, 0x33, 0xDB, 0x00}, // 6% True Armor Sphere -> Dragonite Ore
|
||||||
|
}
|
||||||
|
for i := range patterns {
|
||||||
|
j := findSubSliceIndices(data, patterns[i][0:4])
|
||||||
|
for k := range j {
|
||||||
|
copy(data[j[k]+2:j[k]+4], patterns[i][4:6])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,21 +201,32 @@ func loadQuestFile(s *Session, questId int) []byte {
|
|||||||
fileBytes.SetLE()
|
fileBytes.SetLE()
|
||||||
fileBytes.Seek(int64(fileBytes.ReadUint32()), 0)
|
fileBytes.Seek(int64(fileBytes.ReadUint32()), 0)
|
||||||
|
|
||||||
// The 320 bytes directly following the data pointer must go directly into the event's body, after the header and before the string pointers.
|
bodyLength := 320
|
||||||
questBody := byteframe.NewByteFrameFromBytes(fileBytes.ReadBytes(320))
|
if _config.ErupeConfig.RealClientMode <= _config.S6 {
|
||||||
|
bodyLength = 160
|
||||||
|
} else if _config.ErupeConfig.RealClientMode <= _config.F5 {
|
||||||
|
bodyLength = 168
|
||||||
|
} else if _config.ErupeConfig.RealClientMode <= _config.G101 {
|
||||||
|
bodyLength = 192
|
||||||
|
} else if _config.ErupeConfig.RealClientMode <= _config.Z1 {
|
||||||
|
bodyLength = 224
|
||||||
|
}
|
||||||
|
|
||||||
|
// The n bytes directly following the data pointer must go directly into the event's body, after the header and before the string pointers.
|
||||||
|
questBody := byteframe.NewByteFrameFromBytes(fileBytes.ReadBytes(uint(bodyLength)))
|
||||||
questBody.SetLE()
|
questBody.SetLE()
|
||||||
// Find the master quest string pointer
|
// Find the master quest string pointer
|
||||||
questBody.Seek(40, 0)
|
questBody.Seek(40, 0)
|
||||||
fileBytes.Seek(int64(questBody.ReadUint32()), 0)
|
fileBytes.Seek(int64(questBody.ReadUint32()), 0)
|
||||||
questBody.Seek(40, 0)
|
questBody.Seek(40, 0)
|
||||||
// Overwrite it
|
// Overwrite it
|
||||||
questBody.WriteUint32(320)
|
questBody.WriteUint32(uint32(bodyLength))
|
||||||
questBody.Seek(0, 2)
|
questBody.Seek(0, 2)
|
||||||
|
|
||||||
// Rewrite the quest strings and their pointers
|
// Rewrite the quest strings and their pointers
|
||||||
var tempString []byte
|
var tempString []byte
|
||||||
newStrings := byteframe.NewByteFrame()
|
newStrings := byteframe.NewByteFrame()
|
||||||
tempPointer := 352
|
tempPointer := bodyLength + 32
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
questBody.WriteUint32(uint32(tempPointer))
|
questBody.WriteUint32(uint32(tempPointer))
|
||||||
temp := int64(fileBytes.Index())
|
temp := int64(fileBytes.Index())
|
||||||
@@ -506,8 +569,13 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
tuneValues = append(tuneValues, getTuneValueRange(3299, 200)...)
|
tuneValues = append(tuneValues, getTuneValueRange(3299, 200)...)
|
||||||
tuneValues = append(tuneValues, getTuneValueRange(3325, 300)...)
|
tuneValues = append(tuneValues, getTuneValueRange(3325, 300)...)
|
||||||
|
|
||||||
offset := uint16(time.Now().Unix())
|
var temp []tuneValue
|
||||||
bf.WriteUint16(offset)
|
for i := range tuneValues {
|
||||||
|
if tuneValues[i].Value > 0 {
|
||||||
|
temp = append(temp, tuneValues[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tuneValues = temp
|
||||||
|
|
||||||
tuneLimit := 770
|
tuneLimit := 770
|
||||||
if _config.ErupeConfig.RealClientMode <= _config.F5 {
|
if _config.ErupeConfig.RealClientMode <= _config.F5 {
|
||||||
@@ -533,6 +601,9 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
tuneValues = tuneValues[:tuneLimit]
|
tuneValues = tuneValues[:tuneLimit]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offset := uint16(time.Now().Unix())
|
||||||
|
bf.WriteUint16(offset)
|
||||||
|
|
||||||
bf.WriteUint16(uint16(len(tuneValues)))
|
bf.WriteUint16(uint16(len(tuneValues)))
|
||||||
for i := range tuneValues {
|
for i := range tuneValues {
|
||||||
bf.WriteUint16(tuneValues[i].ID ^ offset)
|
bf.WriteUint16(tuneValues[i].ID ^ offset)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ type i18n struct {
|
|||||||
cafe struct {
|
cafe struct {
|
||||||
reset string
|
reset string
|
||||||
}
|
}
|
||||||
|
timer string
|
||||||
commands struct {
|
commands struct {
|
||||||
noOp string
|
noOp string
|
||||||
disabled string
|
disabled string
|
||||||
@@ -51,6 +52,10 @@ type i18n struct {
|
|||||||
error string
|
error string
|
||||||
length string
|
length string
|
||||||
}
|
}
|
||||||
|
timer struct {
|
||||||
|
enabled string
|
||||||
|
disabled string
|
||||||
|
}
|
||||||
ravi struct {
|
ravi struct {
|
||||||
noCommand string
|
noCommand string
|
||||||
start struct {
|
start struct {
|
||||||
@@ -112,6 +117,7 @@ func getLangStrings(s *Server) i18n {
|
|||||||
case "jp":
|
case "jp":
|
||||||
i.language = "日本語"
|
i.language = "日本語"
|
||||||
i.cafe.reset = "%d/%dにリセット"
|
i.cafe.reset = "%d/%dにリセット"
|
||||||
|
i.timer = "タイマー:%02d'%02d\"%02d.%03d (%df)"
|
||||||
|
|
||||||
i.diva.prayer.beads = []Bead{
|
i.diva.prayer.beads = []Bead{
|
||||||
{id: 1, name: "暴風の祈珠", description: "ーあらしまかぜのきじゅー\n暴風とは猛る思い。\n聞く者に勇気を与える。"},
|
{id: 1, name: "暴風の祈珠", description: "ーあらしまかぜのきじゅー\n暴風とは猛る思い。\n聞く者に勇気を与える。"},
|
||||||
@@ -215,6 +221,7 @@ func getLangStrings(s *Server) i18n {
|
|||||||
{id: 24, name: "Bead of Thunderproof", description: "ーふうらいのきじゅー"},
|
{id: 24, name: "Bead of Thunderproof", description: "ーふうらいのきじゅー"},
|
||||||
{id: 25, name: "Bead of Immunity", description: "ーふうぞくのきじゅー"},
|
{id: 25, name: "Bead of Immunity", description: "ーふうぞくのきじゅー"},
|
||||||
}
|
}
|
||||||
|
i.timer = "Time: %02d:%02d:%02d.%03d (%df)"
|
||||||
|
|
||||||
i.commands.noOp = "You don't have permission to use this command"
|
i.commands.noOp = "You don't have permission to use this command"
|
||||||
i.commands.disabled = "%s command is disabled"
|
i.commands.disabled = "%s command is disabled"
|
||||||
@@ -243,6 +250,9 @@ func getLangStrings(s *Server) i18n {
|
|||||||
i.commands.ban.error = "Error in command. Format: %s <id> [length]"
|
i.commands.ban.error = "Error in command. Format: %s <id> [length]"
|
||||||
i.commands.ban.length = " until %s"
|
i.commands.ban.length = " until %s"
|
||||||
|
|
||||||
|
i.commands.timer.enabled = "Quest timer enabled"
|
||||||
|
i.commands.timer.disabled = "Quest timer disabled"
|
||||||
|
|
||||||
i.commands.ravi.noCommand = "No Raviente command specified!"
|
i.commands.ravi.noCommand = "No Raviente command specified!"
|
||||||
i.commands.ravi.start.success = "The Great Slaying will begin in a moment"
|
i.commands.ravi.start.success = "The Great Slaying will begin in a moment"
|
||||||
i.commands.ravi.start.error = "The Great Slaying has already begun!"
|
i.commands.ravi.start.error = "The Great Slaying has already begun!"
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func (s *Session) handlePacket(pkt []byte) error {
|
|||||||
bf := byteframe.NewByteFrameFromBytes(pkt)
|
bf := byteframe.NewByteFrameFromBytes(pkt)
|
||||||
reqType := string(bf.ReadNullTerminatedBytes())
|
reqType := string(bf.ReadNullTerminatedBytes())
|
||||||
switch reqType[:len(reqType)-3] {
|
switch reqType[:len(reqType)-3] {
|
||||||
case "DLTSKEYSIGN:", "DSGN:":
|
case "DLTSKEYSIGN:", "DSGN:", "SIGN:":
|
||||||
s.handleDSGN(bf)
|
s.handleDSGN(bf)
|
||||||
case "PS3SGN:":
|
case "PS3SGN:":
|
||||||
s.client = PS3
|
s.client = PS3
|
||||||
|
|||||||
Reference in New Issue
Block a user