mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-30 12:33:18 +02:00
Merge branch 'main' into feature/festa
This commit is contained in:
@@ -120,7 +120,11 @@ func CSVAdd(csv string, v int) string {
|
|||||||
if len(csv) == 0 {
|
if len(csv) == 0 {
|
||||||
return strconv.Itoa(v)
|
return strconv.Itoa(v)
|
||||||
}
|
}
|
||||||
return csv + "," + strconv.Itoa(v)
|
if CSVContains(csv, v) {
|
||||||
|
return csv
|
||||||
|
} else {
|
||||||
|
return csv + "," + strconv.Itoa(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CSVRemove(csv string, v int) string {
|
func CSVRemove(csv string, v int) string {
|
||||||
|
|||||||
@@ -28,10 +28,7 @@
|
|||||||
"discord": {
|
"discord": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"bottoken": "",
|
"bottoken": "",
|
||||||
"realtimeChannelID": "",
|
"realtimeChannelID": ""
|
||||||
"serverId": "",
|
|
||||||
"devRoles": [],
|
|
||||||
"devMode": false
|
|
||||||
},
|
},
|
||||||
"database": {
|
"database": {
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
|
|||||||
7
main.go
7
main.go
@@ -65,7 +65,7 @@ func main() {
|
|||||||
var discordBot *discordbot.DiscordBot = nil
|
var discordBot *discordbot.DiscordBot = nil
|
||||||
|
|
||||||
if erupeConfig.Discord.Enabled {
|
if erupeConfig.Discord.Enabled {
|
||||||
bot, err := discordbot.NewDiscordBot(discordbot.DiscordBotOptions{
|
bot, err := discordbot.NewDiscordBot(discordbot.Options{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
Config: erupeConfig,
|
Config: erupeConfig,
|
||||||
})
|
})
|
||||||
@@ -82,6 +82,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
discordBot = bot
|
discordBot = bot
|
||||||
|
logger.Info("Discord bot is enabled")
|
||||||
} else {
|
} else {
|
||||||
logger.Info("Discord bot is disabled")
|
logger.Info("Discord bot is disabled")
|
||||||
}
|
}
|
||||||
@@ -108,9 +109,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
logger.Info("Connected to database")
|
logger.Info("Connected to database")
|
||||||
|
|
||||||
// Clear existing tokens
|
// Clear stale data
|
||||||
_ = db.MustExec("DELETE FROM sign_sessions")
|
_ = db.MustExec("DELETE FROM sign_sessions")
|
||||||
_ = db.MustExec("DELETE FROM servers")
|
_ = db.MustExec("DELETE FROM servers")
|
||||||
|
_ = db.MustExec("DELETE FROM cafe_accepted")
|
||||||
|
_ = db.MustExec("UPDATE characters SET cafe_time=0")
|
||||||
|
|
||||||
// Clean the DB if the option is on.
|
// Clean the DB if the option is on.
|
||||||
if erupeConfig.DevMode && erupeConfig.DevModeOptions.CleanDB {
|
if erupeConfig.DevMode && erupeConfig.DevModeOptions.CleanDB {
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
package mhfpacket
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfAcquireMonthlyItem represents the MSG_MHF_ACQUIRE_MONTHLY_ITEM
|
// MsgMhfAcquireMonthlyItem represents the MSG_MHF_ACQUIRE_MONTHLY_ITEM
|
||||||
type MsgMhfAcquireMonthlyItem struct{}
|
type MsgMhfAcquireMonthlyItem struct {
|
||||||
|
AckHandle uint32
|
||||||
|
Unk0 uint16
|
||||||
|
Unk1 uint16
|
||||||
|
Unk2 uint32
|
||||||
|
Unk3 uint32
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfAcquireMonthlyItem) Opcode() network.PacketID {
|
func (m *MsgMhfAcquireMonthlyItem) Opcode() network.PacketID {
|
||||||
@@ -18,7 +24,12 @@ func (m *MsgMhfAcquireMonthlyItem) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfAcquireMonthlyItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfAcquireMonthlyItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
m.Unk0 = bf.ReadUint16()
|
||||||
|
m.Unk1 = bf.ReadUint16()
|
||||||
|
m.Unk2 = bf.ReadUint32()
|
||||||
|
m.Unk3 = bf.ReadUint32()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
package mhfpacket
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfCheckMonthlyItem represents the MSG_MHF_CHECK_MONTHLY_ITEM
|
// MsgMhfCheckMonthlyItem represents the MSG_MHF_CHECK_MONTHLY_ITEM
|
||||||
type MsgMhfCheckMonthlyItem struct{}
|
type MsgMhfCheckMonthlyItem struct {
|
||||||
|
AckHandle uint32
|
||||||
|
Unk uint32
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfCheckMonthlyItem) Opcode() network.PacketID {
|
func (m *MsgMhfCheckMonthlyItem) Opcode() network.PacketID {
|
||||||
@@ -18,7 +21,9 @@ func (m *MsgMhfCheckMonthlyItem) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfCheckMonthlyItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfCheckMonthlyItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
m.Unk = bf.ReadUint32()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/network"
|
"erupe-ce/network"
|
||||||
"erupe-ce/network/clientctx"
|
"erupe-ce/network/clientctx"
|
||||||
"erupe-ce/common/byteframe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfCheckWeeklyStamp represents the MSG_MHF_CHECK_WEEKLY_STAMP
|
// MsgMhfCheckWeeklyStamp represents the MSG_MHF_CHECK_WEEKLY_STAMP
|
||||||
type MsgMhfCheckWeeklyStamp struct {
|
type MsgMhfCheckWeeklyStamp struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Unk0 uint8
|
StampType string
|
||||||
Unk1 bool
|
Unk1 bool
|
||||||
Unk2 uint16 // Hardcoded 0 in the binary
|
Unk2 uint16 // Hardcoded 0 in the binary
|
||||||
}
|
}
|
||||||
@@ -22,7 +23,13 @@ func (m *MsgMhfCheckWeeklyStamp) Opcode() network.PacketID {
|
|||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfCheckWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfCheckWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Unk0 = bf.ReadUint8()
|
stampType := bf.ReadUint8()
|
||||||
|
switch stampType {
|
||||||
|
case 1:
|
||||||
|
m.StampType = "hl"
|
||||||
|
case 2:
|
||||||
|
m.StampType = "ex"
|
||||||
|
}
|
||||||
m.Unk1 = bf.ReadBool()
|
m.Unk1 = bf.ReadBool()
|
||||||
m.Unk2 = bf.ReadUint16()
|
m.Unk2 = bf.ReadUint16()
|
||||||
return nil
|
return nil
|
||||||
@@ -30,9 +37,5 @@ func (m *MsgMhfCheckWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.C
|
|||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
func (m *MsgMhfCheckWeeklyStamp) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfCheckWeeklyStamp) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
bf.WriteUint32(m.AckHandle)
|
return errors.New("NOT IMPLEMENTED")
|
||||||
bf.WriteUint8(m.Unk0)
|
|
||||||
bf.WriteBool(m.Unk1)
|
|
||||||
bf.WriteUint16(m.Unk2)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
package mhfpacket
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfEnumerateRengokuRanking represents the MSG_MHF_ENUMERATE_RENGOKU_RANKING
|
// MsgMhfEnumerateRengokuRanking represents the MSG_MHF_ENUMERATE_RENGOKU_RANKING
|
||||||
type MsgMhfEnumerateRengokuRanking struct {
|
type MsgMhfEnumerateRengokuRanking struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Unk0 uint32
|
Leaderboard uint32
|
||||||
Unk1 uint16 // Hardcoded 0 in the binary
|
Unk1 uint16 // Hardcoded 0 in the binary
|
||||||
Unk2 uint16 // Hardcoded 00 01 in the binary
|
Unk2 uint16 // Hardcoded 00 01 in the binary
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -24,7 +24,7 @@ func (m *MsgMhfEnumerateRengokuRanking) Opcode() network.PacketID {
|
|||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfEnumerateRengokuRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfEnumerateRengokuRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Unk0 = bf.ReadUint32()
|
m.Leaderboard = bf.ReadUint32()
|
||||||
m.Unk1 = bf.ReadUint16()
|
m.Unk1 = bf.ReadUint16()
|
||||||
m.Unk2 = bf.ReadUint16()
|
m.Unk2 = bf.ReadUint16()
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
package mhfpacket
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE
|
// MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE
|
||||||
type MsgMhfEnumerateWarehouse struct{}
|
type MsgMhfEnumerateWarehouse struct {
|
||||||
|
AckHandle uint32
|
||||||
|
BoxType string
|
||||||
|
BoxIndex uint8
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID {
|
func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID {
|
||||||
@@ -18,7 +22,17 @@ func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
boxType := bf.ReadUint8()
|
||||||
|
switch boxType {
|
||||||
|
case 0:
|
||||||
|
m.BoxType = "item"
|
||||||
|
case 1:
|
||||||
|
m.BoxType = "equip"
|
||||||
|
}
|
||||||
|
m.BoxIndex = bf.ReadUint8()
|
||||||
|
_ = bf.ReadUint16()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
package mhfpacket
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfExchangeWeeklyStamp represents the MSG_MHF_EXCHANGE_WEEKLY_STAMP
|
// MsgMhfExchangeWeeklyStamp represents the MSG_MHF_EXCHANGE_WEEKLY_STAMP
|
||||||
type MsgMhfExchangeWeeklyStamp struct{}
|
type MsgMhfExchangeWeeklyStamp struct {
|
||||||
|
AckHandle uint32
|
||||||
|
StampType string
|
||||||
|
Unk1 uint8
|
||||||
|
Unk2 uint16
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfExchangeWeeklyStamp) Opcode() network.PacketID {
|
func (m *MsgMhfExchangeWeeklyStamp) Opcode() network.PacketID {
|
||||||
@@ -18,7 +23,17 @@ func (m *MsgMhfExchangeWeeklyStamp) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfExchangeWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfExchangeWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
stampType := bf.ReadUint8()
|
||||||
|
switch stampType {
|
||||||
|
case 1:
|
||||||
|
m.StampType = "hl"
|
||||||
|
case 2:
|
||||||
|
m.StampType = "ex"
|
||||||
|
}
|
||||||
|
m.Unk1 = bf.ReadUint8()
|
||||||
|
m.Unk2 = bf.ReadUint16()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"erupe-ce/common/stringsupport"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfOperateWarehouse represents the MSG_MHF_OPERATE_WAREHOUSE
|
// MsgMhfOperateWarehouse represents the MSG_MHF_OPERATE_WAREHOUSE
|
||||||
type MsgMhfOperateWarehouse struct{}
|
type MsgMhfOperateWarehouse struct {
|
||||||
|
AckHandle uint32
|
||||||
|
Operation uint8
|
||||||
|
BoxType string
|
||||||
|
BoxIndex uint8
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID {
|
func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID {
|
||||||
@@ -18,7 +25,20 @@ func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfOperateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfOperateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
m.Operation = bf.ReadUint8()
|
||||||
|
boxType := bf.ReadUint8()
|
||||||
|
switch boxType {
|
||||||
|
case 0:
|
||||||
|
m.BoxType = "item"
|
||||||
|
case 1:
|
||||||
|
m.BoxType = "equip"
|
||||||
|
}
|
||||||
|
m.BoxIndex = bf.ReadUint8()
|
||||||
|
_ = bf.ReadUint8() // lenName
|
||||||
|
_ = bf.ReadUint16() // Unk
|
||||||
|
m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
package mhfpacket
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfOperationInvGuild represents the MSG_MHF_OPERATION_INV_GUILD
|
// MsgMhfOperationInvGuild represents the MSG_MHF_OPERATION_INV_GUILD
|
||||||
type MsgMhfOperationInvGuild struct{}
|
type MsgMhfOperationInvGuild struct {
|
||||||
|
AckHandle uint32
|
||||||
|
Operation uint8
|
||||||
|
ActiveHours uint8
|
||||||
|
DaysActive uint8
|
||||||
|
PlayStyle uint8
|
||||||
|
GuildRequest uint8
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfOperationInvGuild) Opcode() network.PacketID {
|
func (m *MsgMhfOperationInvGuild) Opcode() network.PacketID {
|
||||||
@@ -18,7 +25,13 @@ func (m *MsgMhfOperationInvGuild) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfOperationInvGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfOperationInvGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
m.Operation = bf.ReadUint8()
|
||||||
|
m.ActiveHours = bf.ReadUint8()
|
||||||
|
m.DaysActive = bf.ReadUint8()
|
||||||
|
m.PlayStyle = bf.ReadUint8()
|
||||||
|
m.GuildRequest = bf.ReadUint8()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
package mhfpacket
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfPostCafeDurationBonusReceived represents the MSG_MHF_POST_CAFE_DURATION_BONUS_RECEIVED
|
// MsgMhfPostCafeDurationBonusReceived represents the MSG_MHF_POST_CAFE_DURATION_BONUS_RECEIVED
|
||||||
type MsgMhfPostCafeDurationBonusReceived struct{}
|
type MsgMhfPostCafeDurationBonusReceived struct {
|
||||||
|
AckHandle uint32
|
||||||
|
CafeBonusID []uint32
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfPostCafeDurationBonusReceived) Opcode() network.PacketID {
|
func (m *MsgMhfPostCafeDurationBonusReceived) Opcode() network.PacketID {
|
||||||
@@ -18,7 +21,12 @@ func (m *MsgMhfPostCafeDurationBonusReceived) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfPostCafeDurationBonusReceived) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfPostCafeDurationBonusReceived) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
ids := int(bf.ReadUint32())
|
||||||
|
for i := 0; i < ids; i++ {
|
||||||
|
m.CafeBonusID = append(m.CafeBonusID, bf.ReadUint32())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
package mhfpacket
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfStartBoostTime represents the MSG_MHF_START_BOOST_TIME
|
// MsgMhfStartBoostTime represents the MSG_MHF_START_BOOST_TIME
|
||||||
type MsgMhfStartBoostTime struct{}
|
type MsgMhfStartBoostTime struct {
|
||||||
|
AckHandle uint32
|
||||||
|
Unk0 uint32
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfStartBoostTime) Opcode() network.PacketID {
|
func (m *MsgMhfStartBoostTime) Opcode() network.PacketID {
|
||||||
@@ -18,7 +21,9 @@ func (m *MsgMhfStartBoostTime) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfStartBoostTime) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfStartBoostTime) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
m.Unk0 = bf.ReadUint32()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,15 +1,28 @@
|
|||||||
package mhfpacket
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type WarehouseStack struct {
|
||||||
|
ID uint32
|
||||||
|
Index uint16
|
||||||
|
EquipType uint16
|
||||||
|
ItemID uint16
|
||||||
|
Quantity uint16
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
// MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE
|
// MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE
|
||||||
type MsgMhfUpdateWarehouse struct{}
|
type MsgMhfUpdateWarehouse struct {
|
||||||
|
AckHandle uint32
|
||||||
|
BoxType string
|
||||||
|
BoxIndex uint8
|
||||||
|
Updates []WarehouseStack
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID {
|
func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID {
|
||||||
@@ -18,7 +31,37 @@ func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
boxType := bf.ReadUint8()
|
||||||
|
switch boxType {
|
||||||
|
case 0:
|
||||||
|
m.BoxType = "item"
|
||||||
|
case 1:
|
||||||
|
m.BoxType = "equip"
|
||||||
|
}
|
||||||
|
m.BoxIndex = bf.ReadUint8()
|
||||||
|
changes := int(bf.ReadUint16())
|
||||||
|
var stackUpdate WarehouseStack
|
||||||
|
for i := 0; i < changes; i++ {
|
||||||
|
switch boxType {
|
||||||
|
case 0:
|
||||||
|
stackUpdate.ID = bf.ReadUint32()
|
||||||
|
stackUpdate.Index = bf.ReadUint16()
|
||||||
|
stackUpdate.ItemID = bf.ReadUint16()
|
||||||
|
stackUpdate.Quantity = bf.ReadUint16()
|
||||||
|
_ = bf.ReadUint16() // Unk
|
||||||
|
m.Updates = append(m.Updates, stackUpdate)
|
||||||
|
case 1:
|
||||||
|
stackUpdate.ID = bf.ReadUint32()
|
||||||
|
stackUpdate.Index = bf.ReadUint16()
|
||||||
|
stackUpdate.EquipType = bf.ReadUint16()
|
||||||
|
stackUpdate.ItemID = bf.ReadUint16()
|
||||||
|
stackUpdate.Data = bf.ReadBytes(56)
|
||||||
|
m.Updates = append(m.Updates, stackUpdate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = bf.ReadUint16()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ type ClientRight struct {
|
|||||||
// MsgSysUpdateRight represents the MSG_SYS_UPDATE_RIGHT
|
// MsgSysUpdateRight represents the MSG_SYS_UPDATE_RIGHT
|
||||||
type MsgSysUpdateRight struct {
|
type MsgSysUpdateRight struct {
|
||||||
ClientRespAckHandle uint32 // If non-0, requests the client to send back a MSG_SYS_ACK packet with this value.
|
ClientRespAckHandle uint32 // If non-0, requests the client to send back a MSG_SYS_ACK packet with this value.
|
||||||
Unk1 uint32
|
Bitfield uint32
|
||||||
Rights []ClientRight
|
Rights []ClientRight
|
||||||
UnkSize uint16 // Count of some buf up to 0x800 bytes following it.
|
UnkSize uint16 // Count of some buf up to 0x800 bytes following it.
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ func (m *MsgSysUpdateRight) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client
|
|||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
func (m *MsgSysUpdateRight) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgSysUpdateRight) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
bf.WriteUint32(m.ClientRespAckHandle)
|
bf.WriteUint32(m.ClientRespAckHandle)
|
||||||
bf.WriteUint32(m.Unk1)
|
bf.WriteUint32(m.Bitfield)
|
||||||
bf.WriteUint16(uint16(len(m.Rights)))
|
bf.WriteUint16(uint16(len(m.Rights)))
|
||||||
bf.WriteUint16(0)
|
bf.WriteUint16(0)
|
||||||
for _, v := range m.Rights {
|
for _, v := range m.Rights {
|
||||||
|
|||||||
@@ -2,4 +2,6 @@ BEGIN;
|
|||||||
|
|
||||||
CREATE SEQUENCE IF NOT EXISTS public.rasta_id_seq;
|
CREATE SEQUENCE IF NOT EXISTS public.rasta_id_seq;
|
||||||
|
|
||||||
|
UPDATE characters SET savemercenary=NULL;
|
||||||
|
|
||||||
END;
|
END;
|
||||||
40
patch-schema/netcafe.sql
Normal file
40
patch-schema/netcafe.sql
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.characters
|
||||||
|
ADD COLUMN IF NOT EXISTS cafe_time integer DEFAULT 0;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.characters
|
||||||
|
DROP COLUMN IF EXISTS netcafe_points;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.characters
|
||||||
|
ADD COLUMN IF NOT EXISTS netcafe_points int DEFAULT 0;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.characters
|
||||||
|
ADD COLUMN IF NOT EXISTS boost_time timestamp without time zone;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.cafebonus
|
||||||
|
(
|
||||||
|
id serial NOT NULL PRIMARY KEY,
|
||||||
|
time_req integer NOT NULL,
|
||||||
|
item_type integer NOT NULL,
|
||||||
|
item_id integer NOT NULL,
|
||||||
|
quantity integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.cafe_accepted
|
||||||
|
(
|
||||||
|
cafe_id integer NOT NULL,
|
||||||
|
character_id integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO public.cafebonus (time_req, item_type, item_id, quantity)
|
||||||
|
VALUES
|
||||||
|
(1800, 17, 0, 250),
|
||||||
|
(3600, 17, 0, 500),
|
||||||
|
(7200, 17, 0, 1000),
|
||||||
|
(10800, 17, 0, 1500),
|
||||||
|
(18000, 17, 0, 1750),
|
||||||
|
(28800, 17, 0, 3000),
|
||||||
|
(43200, 17, 0, 4000);
|
||||||
|
|
||||||
|
END;
|
||||||
11
patch-schema/road-leaderboard.sql
Normal file
11
patch-schema/road-leaderboard.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS rengoku_score (
|
||||||
|
character_id integer PRIMARY KEY,
|
||||||
|
max_stages_mp integer,
|
||||||
|
max_points_mp integer,
|
||||||
|
max_stages_sp integer,
|
||||||
|
max_points_sp integer
|
||||||
|
);
|
||||||
|
|
||||||
|
END;
|
||||||
13
patch-schema/stamps.sql
Normal file
13
patch-schema/stamps.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.stamps (
|
||||||
|
character_id integer PRIMARY KEY,
|
||||||
|
hl_total integer DEFAULT 0,
|
||||||
|
hl_redeemed integer DEFAULT 0,
|
||||||
|
hl_next timestamp without time zone,
|
||||||
|
ex_total integer DEFAULT 0,
|
||||||
|
ex_redeemed integer DEFAULT 0,
|
||||||
|
ex_next timestamp without time zone
|
||||||
|
);
|
||||||
|
|
||||||
|
END;
|
||||||
49
patch-schema/warehouse.sql
Normal file
49
patch-schema/warehouse.sql
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.warehouse (
|
||||||
|
character_id integer PRIMARY KEY,
|
||||||
|
item0 bytea,
|
||||||
|
item1 bytea,
|
||||||
|
item2 bytea,
|
||||||
|
item3 bytea,
|
||||||
|
item4 bytea,
|
||||||
|
item5 bytea,
|
||||||
|
item6 bytea,
|
||||||
|
item7 bytea,
|
||||||
|
item8 bytea,
|
||||||
|
item9 bytea,
|
||||||
|
item10 bytea,
|
||||||
|
item0name text,
|
||||||
|
item1name text,
|
||||||
|
item2name text,
|
||||||
|
item3name text,
|
||||||
|
item4name text,
|
||||||
|
item5name text,
|
||||||
|
item6name text,
|
||||||
|
item7name text,
|
||||||
|
item8name text,
|
||||||
|
item9name text,
|
||||||
|
equip0 bytea,
|
||||||
|
equip1 bytea,
|
||||||
|
equip2 bytea,
|
||||||
|
equip3 bytea,
|
||||||
|
equip4 bytea,
|
||||||
|
equip5 bytea,
|
||||||
|
equip6 bytea,
|
||||||
|
equip7 bytea,
|
||||||
|
equip8 bytea,
|
||||||
|
equip9 bytea,
|
||||||
|
equip10 bytea,
|
||||||
|
equip0name text,
|
||||||
|
equip1name text,
|
||||||
|
equip2name text,
|
||||||
|
equip3name text,
|
||||||
|
equip4name text,
|
||||||
|
equip5name text,
|
||||||
|
equip6name text,
|
||||||
|
equip7name text,
|
||||||
|
equip8name text,
|
||||||
|
equip9name text
|
||||||
|
);
|
||||||
|
|
||||||
|
END;
|
||||||
@@ -9,10 +9,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"io/ioutil"
|
|
||||||
"math/bits"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
@@ -20,6 +16,9 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"golang.org/x/text/encoding/japanese"
|
"golang.org/x/text/encoding/japanese"
|
||||||
"golang.org/x/text/transform"
|
"golang.org/x/text/transform"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/bits"
|
||||||
|
"math/rand"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Temporary function to just return no results for a MSG_MHF_ENUMERATE* packet
|
// Temporary function to just return no results for a MSG_MHF_ENUMERATE* packet
|
||||||
@@ -80,7 +79,7 @@ func doAckSimpleFail(s *Session, ackHandle uint32, data []byte) {
|
|||||||
func updateRights(s *Session) {
|
func updateRights(s *Session) {
|
||||||
update := &mhfpacket.MsgSysUpdateRight{
|
update := &mhfpacket.MsgSysUpdateRight{
|
||||||
ClientRespAckHandle: 0,
|
ClientRespAckHandle: 0,
|
||||||
Unk1: s.rights,
|
Bitfield: s.rights,
|
||||||
Rights: []mhfpacket.ClientRight{
|
Rights: []mhfpacket.ClientRight{
|
||||||
{
|
{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
@@ -192,8 +191,12 @@ func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func logoutPlayer(s *Session) {
|
func logoutPlayer(s *Session) {
|
||||||
delete(s.server.sessions, s.rawConn)
|
if _, exists := s.server.sessions[s.rawConn]; exists {
|
||||||
s.rawConn.Close()
|
delete(s.server.sessions, s.rawConn)
|
||||||
|
s.rawConn.Close()
|
||||||
|
} else {
|
||||||
|
return // Prevent re-running logout logic on real logouts
|
||||||
|
}
|
||||||
|
|
||||||
_, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL, char_id=NULL WHERE token=$1", s.token)
|
_, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL, char_id=NULL WHERE token=$1", s.token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -206,13 +209,13 @@ func logoutPlayer(s *Session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var timePlayed int
|
var timePlayed int
|
||||||
|
var sessionTime int
|
||||||
_ = s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed)
|
_ = s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed)
|
||||||
|
sessionTime = int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)
|
||||||
timePlayed = (int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)) + timePlayed
|
timePlayed += sessionTime
|
||||||
|
|
||||||
var rpGained int
|
var rpGained int
|
||||||
|
if s.rights >= 0x40000000 { // N Course
|
||||||
if s.rights > 0x40000000 { // N Course
|
|
||||||
rpGained = timePlayed / 900
|
rpGained = timePlayed / 900
|
||||||
timePlayed = timePlayed % 900
|
timePlayed = timePlayed % 900
|
||||||
} else {
|
} else {
|
||||||
@@ -220,10 +223,10 @@ func logoutPlayer(s *Session) {
|
|||||||
timePlayed = timePlayed % 1800
|
timePlayed = timePlayed % 1800
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID)
|
s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID)
|
||||||
if err != nil {
|
s.server.db.Exec("UPDATE characters SET cafe_time=cafe_time+$1 WHERE id=$2", sessionTime, s.charID)
|
||||||
panic(err)
|
|
||||||
}
|
treasureHuntUnregister(s)
|
||||||
|
|
||||||
if s.stage == nil {
|
if s.stage == nil {
|
||||||
return
|
return
|
||||||
@@ -243,7 +246,6 @@ func logoutPlayer(s *Session) {
|
|||||||
|
|
||||||
removeSessionFromSemaphore(s)
|
removeSessionFromSemaphore(s)
|
||||||
removeSessionFromStage(s)
|
removeSessionFromStage(s)
|
||||||
treasureHuntUnregister(s)
|
|
||||||
|
|
||||||
saveData, err := GetCharacterSaveData(s, s.charID)
|
saveData, err := GetCharacterSaveData(s, s.charID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -421,6 +423,9 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
for session := range stage.clients {
|
for session := range stage.clients {
|
||||||
count++
|
count++
|
||||||
|
hrp := uint16(1)
|
||||||
|
gr := uint16(0)
|
||||||
|
s.server.db.QueryRow("SELECT hrp, gr FROM characters WHERE id=$1", session.charID).Scan(&hrp, &gr)
|
||||||
sessionStage := stringsupport.UTF8ToSJIS(session.stageID)
|
sessionStage := stringsupport.UTF8ToSJIS(session.stageID)
|
||||||
sessionName := stringsupport.UTF8ToSJIS(session.Name)
|
sessionName := stringsupport.UTF8ToSJIS(session.Name)
|
||||||
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
|
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
|
||||||
@@ -433,8 +438,8 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
resp.WriteBytes(make([]byte, 48))
|
resp.WriteBytes(make([]byte, 48))
|
||||||
resp.WriteNullTerminatedBytes(sessionStage)
|
resp.WriteNullTerminatedBytes(sessionStage)
|
||||||
resp.WriteNullTerminatedBytes(sessionName)
|
resp.WriteNullTerminatedBytes(sessionName)
|
||||||
resp.WriteUint16(999) // HR
|
resp.WriteUint16(hrp)
|
||||||
resp.WriteUint16(999) // GR
|
resp.WriteUint16(gr)
|
||||||
resp.WriteBytes([]byte{0x06, 0x10, 0x00}) // Unk
|
resp.WriteBytes([]byte{0x06, 0x10, 0x00}) // Unk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -443,12 +448,23 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
case 4: // Find Party
|
case 4: // Find Party
|
||||||
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
|
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
|
||||||
bf.ReadUint8()
|
setting := bf.ReadUint8()
|
||||||
maxResults := bf.ReadUint16()
|
maxResults := bf.ReadUint16()
|
||||||
bf.ReadUint8()
|
bf.Seek(2, 1)
|
||||||
bf.ReadUint8()
|
|
||||||
partyType := bf.ReadUint16()
|
partyType := bf.ReadUint16()
|
||||||
_ = bf.DataFromCurrent() // Restrictions
|
rankRestriction := uint16(0)
|
||||||
|
if setting >= 2 {
|
||||||
|
bf.Seek(2, 1)
|
||||||
|
rankRestriction = bf.ReadUint16()
|
||||||
|
}
|
||||||
|
targets := make([]uint16, 4)
|
||||||
|
if setting >= 3 {
|
||||||
|
bf.Seek(1, 1)
|
||||||
|
lenTargets := int(bf.ReadUint8())
|
||||||
|
for i := 0; i < lenTargets; i++ {
|
||||||
|
targets[i] = bf.ReadUint16()
|
||||||
|
}
|
||||||
|
}
|
||||||
var stagePrefix string
|
var stagePrefix string
|
||||||
switch partyType {
|
switch partyType {
|
||||||
case 0: // Public Bar
|
case 0: // Public Bar
|
||||||
@@ -468,31 +484,41 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(stage.id, stagePrefix) {
|
if strings.HasPrefix(stage.id, stagePrefix) {
|
||||||
|
sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}])
|
||||||
|
sb3.Seek(4, 0)
|
||||||
|
stageRankRestriction := sb3.ReadUint16()
|
||||||
|
stageTarget := sb3.ReadUint16()
|
||||||
|
if rankRestriction != 0xFFFF && stageRankRestriction < rankRestriction {
|
||||||
|
continue
|
||||||
|
}
|
||||||
count++
|
count++
|
||||||
sessionStage := stringsupport.UTF8ToSJIS(stage.id)
|
sessionStage := stringsupport.UTF8ToSJIS(stage.id)
|
||||||
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
|
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
|
||||||
resp.WriteUint16(c.Port)
|
resp.WriteUint16(c.Port)
|
||||||
|
resp.WriteUint16(0) // Static?
|
||||||
// TODO: This is half right, could be trimmed
|
resp.WriteUint16(0) // Unk
|
||||||
resp.WriteUint16(0)
|
|
||||||
resp.WriteUint16(uint16(len(stage.clients)))
|
|
||||||
resp.WriteUint16(uint16(len(stage.clients)))
|
resp.WriteUint16(uint16(len(stage.clients)))
|
||||||
resp.WriteUint16(stage.maxPlayers)
|
resp.WriteUint16(stage.maxPlayers)
|
||||||
resp.WriteUint16(0)
|
resp.WriteUint16(0) // Num clients entered from stage
|
||||||
resp.WriteUint16(uint16(len(stage.clients)))
|
resp.WriteUint16(stage.maxPlayers)
|
||||||
//
|
resp.WriteUint8(1) // Static?
|
||||||
|
resp.WriteUint8(uint8(len(sessionStage) + 1))
|
||||||
resp.WriteUint16(uint16(len(sessionStage) + 1))
|
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 0}])))
|
||||||
resp.WriteUint8(1)
|
|
||||||
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}])))
|
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}])))
|
||||||
resp.WriteBytes(make([]byte, 16))
|
resp.WriteUint16(stageRankRestriction)
|
||||||
|
resp.WriteUint16(stageTarget)
|
||||||
|
resp.WriteBytes(make([]byte, 12))
|
||||||
resp.WriteNullTerminatedBytes(sessionStage)
|
resp.WriteNullTerminatedBytes(sessionStage)
|
||||||
resp.WriteBytes([]byte{0x00})
|
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 0}])
|
||||||
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}])
|
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (pkt.SearchType == 1 || pkt.SearchType == 3) && count == 0 {
|
||||||
|
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
return
|
||||||
|
}
|
||||||
resp.Seek(0, io.SeekStart)
|
resp.Seek(0, io.SeekStart)
|
||||||
resp.WriteUint16(count)
|
resp.WriteUint16(count)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
@@ -641,86 +667,57 @@ func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) {
|
|
||||||
pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem)
|
|
||||||
var netcafe_points int
|
|
||||||
err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafe_points)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err))
|
|
||||||
}
|
|
||||||
resp := byteframe.NewByteFrame()
|
|
||||||
resp.WriteUint32(uint32(netcafe_points))
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) {
|
|
||||||
pkt := p.(*mhfpacket.MsgMhfUpdateCafepoint)
|
|
||||||
var netcafe_points int
|
|
||||||
err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafe_points)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err))
|
|
||||||
}
|
|
||||||
resp := byteframe.NewByteFrame()
|
|
||||||
resp.WriteUint32(0)
|
|
||||||
resp.WriteUint32(uint32(netcafe_points))
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
|
|
||||||
pkt := p.(*mhfpacket.MsgMhfCheckDailyCafepoint)
|
|
||||||
|
|
||||||
// I am not sure exactly what this does, but all responses I have seen include this exact sequence of bytes
|
|
||||||
// 1 daily, 5 daily halk pots, 3 point boosted quests, also adds 5 netcafe points but not sent to client
|
|
||||||
// available once after midday every day
|
|
||||||
|
|
||||||
// get next midday
|
|
||||||
var t = Time_static()
|
|
||||||
year, month, day := t.Date()
|
|
||||||
midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location())
|
|
||||||
if t.After(midday) {
|
|
||||||
midday = midday.Add(24 * time.Hour)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get time after which daily claiming would be valid from db
|
|
||||||
var dailyTime time.Time
|
|
||||||
err := s.server.db.QueryRow("SELECT COALESCE(daily_time, $2) FROM characters WHERE id = $1", s.charID, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)).Scan(&dailyTime)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Fatal("Failed to get daily_time savedata from db", zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.After(dailyTime) {
|
|
||||||
// +5 netcafe points and setting next valid window
|
|
||||||
_, err := s.server.db.Exec("UPDATE characters SET daily_time=$1, netcafe_points=netcafe_points::int + 5 WHERE id=$2", midday, s.charID)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Fatal("Failed to update daily_time and netcafe_points savedata in db", zap.Error(err))
|
|
||||||
}
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01})
|
|
||||||
} else {
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfCheckMonthlyItem(s *Session, p mhfpacket.MHFPacket) {}
|
|
||||||
|
|
||||||
func handleMsgMhfAcquireMonthlyItem(s *Session, p mhfpacket.MHFPacket) {}
|
|
||||||
|
|
||||||
func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp)
|
pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp)
|
||||||
|
weekCurrentStart := TimeWeekStart()
|
||||||
resp := byteframe.NewByteFrame()
|
weekNextStart := TimeWeekNext()
|
||||||
resp.WriteUint16(0x000E)
|
var total, redeemed, updated uint16
|
||||||
resp.WriteUint16(0x0001)
|
var nextClaim time.Time
|
||||||
resp.WriteUint16(0x0000)
|
err := s.server.db.QueryRow(fmt.Sprintf("SELECT %s_next FROM stamps WHERE character_id=$1", pkt.StampType), s.charID).Scan(&nextClaim)
|
||||||
resp.WriteUint16(0x0000) // 0x0000 stops the vaguely annoying log in pop up
|
if err != nil {
|
||||||
resp.WriteUint32(0)
|
s.server.db.Exec("INSERT INTO stamps (character_id, hl_next, ex_next) VALUES ($1, $2, $2)", s.charID, weekNextStart)
|
||||||
resp.WriteUint32(0x5dddcbb3) // Timestamp
|
nextClaim = weekNextStart
|
||||||
|
}
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
if nextClaim.Before(weekCurrentStart) {
|
||||||
|
s.server.db.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1, %s_next=$1 WHERE character_id=$2", pkt.StampType, pkt.StampType, pkt.StampType), weekNextStart, s.charID)
|
||||||
|
updated = 1
|
||||||
|
}
|
||||||
|
s.server.db.QueryRow(fmt.Sprintf("SELECT %s_total, %s_redeemed FROM stamps WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteUint16(total)
|
||||||
|
bf.WriteUint16(redeemed)
|
||||||
|
bf.WriteUint16(updated)
|
||||||
|
bf.WriteUint32(0) // Unk
|
||||||
|
bf.WriteUint32(uint32(weekCurrentStart.Unix()))
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp)
|
||||||
|
var total, redeemed uint16
|
||||||
|
var tktStack mhfpacket.WarehouseStack
|
||||||
|
if pkt.Unk1 == 0xA { // Yearly Sub Ex
|
||||||
|
s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed)
|
||||||
|
tktStack = mhfpacket.WarehouseStack{ItemID: 0x08A2, Quantity: 1}
|
||||||
|
} else {
|
||||||
|
s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
|
||||||
|
if pkt.StampType == "hl" {
|
||||||
|
tktStack = mhfpacket.WarehouseStack{ItemID: 0x065E, Quantity: 5}
|
||||||
|
} else {
|
||||||
|
tktStack = mhfpacket.WarehouseStack{ItemID: 0x065F, Quantity: 5}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addWarehouseGift(s, "item", tktStack)
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteUint16(total)
|
||||||
|
bf.WriteUint16(redeemed)
|
||||||
|
bf.WriteUint16(0)
|
||||||
|
bf.WriteUint32(0) // Unk, but has possible values
|
||||||
|
bf.WriteUint32(uint32(TimeWeekStart().Unix()))
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
}
|
||||||
|
|
||||||
func getGookData(s *Session, cid uint32) (uint16, []byte) {
|
func getGookData(s *Session, cid uint32) (uint16, []byte) {
|
||||||
var data []byte
|
var data []byte
|
||||||
|
|||||||
239
server/channelserver/handlers_cafe.go
Normal file
239
server/channelserver/handlers_cafe.go
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"erupe-ce/common/byteframe"
|
||||||
|
ps "erupe-ce/common/pascalstring"
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem)
|
||||||
|
var netcafePoints uint32
|
||||||
|
err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafePoints)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Fatal("Failed to get netcafe points from db", zap.Error(err))
|
||||||
|
}
|
||||||
|
resp := byteframe.NewByteFrame()
|
||||||
|
resp.WriteUint32(netcafePoints)
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfUpdateCafepoint)
|
||||||
|
var netcafePoints uint32
|
||||||
|
err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafePoints)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Fatal("Failed to get netcate points from db", zap.Error(err))
|
||||||
|
}
|
||||||
|
resp := byteframe.NewByteFrame()
|
||||||
|
resp.WriteUint32(netcafePoints)
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfCheckDailyCafepoint)
|
||||||
|
|
||||||
|
// I am not sure exactly what this does, but all responses I have seen include this exact sequence of bytes
|
||||||
|
// 1 daily, 5 daily halk pots, 3 point boosted quests, also adds 5 netcafe points but not sent to client
|
||||||
|
// available once after midday every day
|
||||||
|
|
||||||
|
// get next midday
|
||||||
|
var t = Time_static()
|
||||||
|
year, month, day := t.Date()
|
||||||
|
midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location())
|
||||||
|
if t.After(midday) {
|
||||||
|
midday = midday.Add(24 * time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get time after which daily claiming would be valid from db
|
||||||
|
var dailyTime time.Time
|
||||||
|
err := s.server.db.QueryRow("SELECT COALESCE(daily_time, $2) FROM characters WHERE id = $1", s.charID, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)).Scan(&dailyTime)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Fatal("Failed to get daily_time savedata from db", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.After(dailyTime) {
|
||||||
|
// +5 netcafe points and setting next valid window
|
||||||
|
_, err := s.server.db.Exec("UPDATE characters SET daily_time=$1, netcafe_points=netcafe_points+5 WHERE id=$2", midday, s.charID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Fatal("Failed to update daily_time and netcafe_points savedata in db", zap.Error(err))
|
||||||
|
}
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01})
|
||||||
|
} else {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfGetCafeDuration)
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
|
||||||
|
var cafeTime uint32
|
||||||
|
err := s.server.db.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.charID).Scan(&cafeTime)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime
|
||||||
|
bf.WriteUint32(cafeTime) // Total cafe time
|
||||||
|
bf.WriteUint16(0)
|
||||||
|
ps.Uint16(bf, "Resets at next maintenance", true)
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
}
|
||||||
|
|
||||||
|
type CafeBonus struct {
|
||||||
|
ID uint32 `db:"id"`
|
||||||
|
TimeReq uint32 `db:"time_req"`
|
||||||
|
ItemType uint32 `db:"item_type"`
|
||||||
|
ItemID uint32 `db:"item_id"`
|
||||||
|
Quantity uint32 `db:"quantity"`
|
||||||
|
Claimed bool `db:"claimed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfGetCafeDurationBonusInfo)
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
|
||||||
|
var count uint32
|
||||||
|
rows, err := s.server.db.Queryx(`
|
||||||
|
SELECT cb.id, time_req, item_type, item_id, quantity,
|
||||||
|
(
|
||||||
|
SELECT count(*)
|
||||||
|
FROM cafe_accepted ca
|
||||||
|
WHERE cb.id = ca.cafe_id AND ca.character_id = $1
|
||||||
|
)::int::bool AS claimed
|
||||||
|
FROM cafebonus cb ORDER BY id ASC;`, s.charID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Error getting cafebonus", zap.Error(err))
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
} else {
|
||||||
|
for rows.Next() {
|
||||||
|
count++
|
||||||
|
cafeBonus := &CafeBonus{}
|
||||||
|
err = rows.StructScan(&cafeBonus)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Error scanning cafebonus", zap.Error(err))
|
||||||
|
}
|
||||||
|
bf.WriteUint32(cafeBonus.TimeReq)
|
||||||
|
bf.WriteUint32(cafeBonus.ItemType)
|
||||||
|
bf.WriteUint32(cafeBonus.ItemID)
|
||||||
|
bf.WriteUint32(cafeBonus.Quantity)
|
||||||
|
bf.WriteBool(cafeBonus.Claimed)
|
||||||
|
}
|
||||||
|
resp := byteframe.NewByteFrame()
|
||||||
|
resp.WriteUint32(0)
|
||||||
|
resp.WriteUint32(uint32(time.Now().Unix()))
|
||||||
|
resp.WriteUint32(count)
|
||||||
|
resp.WriteBytes(bf.Data())
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfReceiveCafeDurationBonus)
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
var count uint32
|
||||||
|
bf.WriteUint32(0)
|
||||||
|
rows, err := s.server.db.Queryx(`
|
||||||
|
SELECT c.id, time_req, item_type, item_id, quantity
|
||||||
|
FROM cafebonus c
|
||||||
|
WHERE (
|
||||||
|
SELECT count(*)
|
||||||
|
FROM cafe_accepted ca
|
||||||
|
WHERE c.id = ca.cafe_id AND ca.character_id = $1
|
||||||
|
) < 1 AND (
|
||||||
|
SELECT ch.cafe_time + $2
|
||||||
|
FROM characters ch
|
||||||
|
WHERE ch.id = $1
|
||||||
|
) >= time_req`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart)
|
||||||
|
if err != nil {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
} else {
|
||||||
|
for rows.Next() {
|
||||||
|
cafeBonus := &CafeBonus{}
|
||||||
|
err = rows.StructScan(cafeBonus)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
bf.WriteUint32(cafeBonus.ID)
|
||||||
|
bf.WriteUint32(cafeBonus.ItemType)
|
||||||
|
bf.WriteUint32(cafeBonus.ItemID)
|
||||||
|
bf.WriteUint32(cafeBonus.Quantity)
|
||||||
|
}
|
||||||
|
bf.Seek(0, io.SeekStart)
|
||||||
|
bf.WriteUint32(count)
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfPostCafeDurationBonusReceived)
|
||||||
|
var cafeBonus CafeBonus
|
||||||
|
for _, cbID := range pkt.CafeBonusID {
|
||||||
|
err := s.server.db.QueryRow(`
|
||||||
|
SELECT cb.id, item_type, quantity FROM cafebonus cb WHERE cb.id=$1
|
||||||
|
`, cbID).Scan(&cafeBonus.ID, &cafeBonus.ItemType, &cafeBonus.Quantity)
|
||||||
|
if err == nil {
|
||||||
|
if cafeBonus.ItemType == 17 {
|
||||||
|
s.server.db.Exec("UPDATE characters SET netcafe_points=netcafe_points+$1 WHERE id=$2", cafeBonus.Quantity, s.charID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.server.db.Exec("INSERT INTO public.cafe_accepted VALUES ($1, $2)", cbID, s.charID)
|
||||||
|
}
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfStartBoostTime)
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
boostLimit := Time_Current_Adjusted().Add(100 * time.Minute)
|
||||||
|
s.server.db.Exec("UPDATE characters SET boost_time=$1 WHERE id=$2", boostLimit, s.charID)
|
||||||
|
bf.WriteUint32(uint32(boostLimit.Unix()))
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfGetBoostTime)
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, []byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit)
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
var boostLimit time.Time
|
||||||
|
err := s.server.db.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.charID).Scan(&boostLimit)
|
||||||
|
if err != nil {
|
||||||
|
bf.WriteUint32(0)
|
||||||
|
} else {
|
||||||
|
bf.WriteUint32(uint32(boostLimit.Unix()))
|
||||||
|
}
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfGetBoostRight)
|
||||||
|
var boostLimit time.Time
|
||||||
|
err := s.server.db.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.charID).Scan(&boostLimit)
|
||||||
|
if err != nil {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if boostLimit.Unix() > Time_Current_Adjusted().Unix() {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
||||||
|
} else {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfPostBoostTimeQuestReturn)
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
|
func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
@@ -165,6 +165,66 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
fmt.Printf("Got chat message: %+v\n", chatMessage)
|
fmt.Printf("Got chat message: %+v\n", chatMessage)
|
||||||
|
|
||||||
|
// Flush all objects and users and reload
|
||||||
|
if strings.HasPrefix(chatMessage.Message, "!reload") {
|
||||||
|
sendServerChatMessage(s, "Reloading players...")
|
||||||
|
var temp mhfpacket.MHFPacket
|
||||||
|
deleteNotif := byteframe.NewByteFrame()
|
||||||
|
for _, object := range s.stage.objects {
|
||||||
|
if object.ownerCharID == s.charID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
temp = &mhfpacket.MsgSysDeleteObject{ObjID: object.id}
|
||||||
|
deleteNotif.WriteUint16(uint16(temp.Opcode()))
|
||||||
|
temp.Build(deleteNotif, s.clientContext)
|
||||||
|
}
|
||||||
|
for _, session := range s.server.sessions {
|
||||||
|
if s == session {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
temp = &mhfpacket.MsgSysDeleteUser{CharID: session.charID}
|
||||||
|
deleteNotif.WriteUint16(uint16(temp.Opcode()))
|
||||||
|
temp.Build(deleteNotif, s.clientContext)
|
||||||
|
}
|
||||||
|
deleteNotif.WriteUint16(0x0010)
|
||||||
|
s.QueueSend(deleteNotif.Data())
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
reloadNotif := byteframe.NewByteFrame()
|
||||||
|
for _, session := range s.server.sessions {
|
||||||
|
if s == session {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID}
|
||||||
|
reloadNotif.WriteUint16(uint16(temp.Opcode()))
|
||||||
|
temp.Build(reloadNotif, s.clientContext)
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
temp = &mhfpacket.MsgSysNotifyUserBinary{
|
||||||
|
CharID: session.charID,
|
||||||
|
BinaryType: uint8(i + 1),
|
||||||
|
}
|
||||||
|
reloadNotif.WriteUint16(uint16(temp.Opcode()))
|
||||||
|
temp.Build(reloadNotif, s.clientContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, obj := range s.stage.objects {
|
||||||
|
if obj.ownerCharID == s.charID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
temp = &mhfpacket.MsgSysDuplicateObject{
|
||||||
|
ObjID: obj.id,
|
||||||
|
X: obj.x,
|
||||||
|
Y: obj.y,
|
||||||
|
Z: obj.z,
|
||||||
|
Unk0: 0,
|
||||||
|
OwnerCharID: obj.ownerCharID,
|
||||||
|
}
|
||||||
|
reloadNotif.WriteUint16(uint16(temp.Opcode()))
|
||||||
|
temp.Build(reloadNotif, s.clientContext)
|
||||||
|
}
|
||||||
|
reloadNotif.WriteUint16(0x0010)
|
||||||
|
s.QueueSend(reloadNotif.Data())
|
||||||
|
}
|
||||||
|
|
||||||
// Set account rights
|
// Set account rights
|
||||||
if strings.HasPrefix(chatMessage.Message, "!rights") {
|
if strings.HasPrefix(chatMessage.Message, "!rights") {
|
||||||
var v uint32
|
var v uint32
|
||||||
@@ -180,7 +240,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Discord integration
|
// Discord integration
|
||||||
if chatMessage.Type == binpacket.ChatTypeLocal || chatMessage.Type == binpacket.ChatTypeParty {
|
if (pkt.BroadcastType == BroadcastTypeStage && s.stage.id == "sl1Ns200p0a0u0") || pkt.BroadcastType == BroadcastTypeWorld {
|
||||||
s.server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message)
|
s.server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
s.logger.Fatal("Failed to update savedata in db", zap.Error(err))
|
s.logger.Fatal("Failed to update savedata in db", zap.Error(err))
|
||||||
}
|
}
|
||||||
s.logger.Info("Wrote recompressed savedata back to DB.")
|
s.logger.Info("Wrote recompressed savedata back to DB.")
|
||||||
dumpSaveData(s, pkt.RawDataPayload, "")
|
dumpSaveData(s, pkt.RawDataPayload, "savedata")
|
||||||
|
|
||||||
_, err = s.server.db.Exec("UPDATE characters SET weapon_type=$1 WHERE id=$2", uint16(decompressedData[128789]), s.charID)
|
_, err = s.server.db.Exec("UPDATE characters SET weapon_type=$1 WHERE id=$2", uint16(decompressedData[128789]), s.charID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -275,8 +275,8 @@ func dumpSaveData(s *Session, data []byte, suffix string) {
|
|||||||
if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled {
|
if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%s_", s.Name))
|
dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name))
|
||||||
path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%s_", s.Name), fmt.Sprintf("%d_%s_%s%s.bin", s.charID, s.Name, Time_Current().Format("2006-01-02_15.04.05"), suffix))
|
path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name), fmt.Sprintf("%d_%s_%s.bin", s.charID, s.Name, suffix))
|
||||||
|
|
||||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||||
os.Mkdir(dir, os.ModeDir)
|
os.Mkdir(dir, os.ModeDir)
|
||||||
@@ -297,10 +297,11 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
|
|
||||||
err := s.server.db.QueryRow("SELECT savedata FROM characters WHERE id = $1", s.charID).Scan(&data)
|
err := s.server.db.QueryRow("SELECT savedata FROM characters WHERE id = $1", s.charID).Scan(&data)
|
||||||
if err != nil {
|
if err != nil || len(data) == 0 {
|
||||||
s.logger.Fatal("Failed to get savedata from db", zap.Error(err))
|
s.logger.Warn(fmt.Sprintf("Failed to load savedata (CID: %d)", s.charID), zap.Error(err))
|
||||||
|
s.rawConn.Close() // Terminate the connection
|
||||||
|
return
|
||||||
}
|
}
|
||||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||||
|
|
||||||
@@ -310,11 +311,11 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
bf := byteframe.NewByteFrameFromBytes(decompSaveData)
|
bf := byteframe.NewByteFrameFromBytes(decompSaveData)
|
||||||
bf.Seek(88, io.SeekStart)
|
bf.Seek(88, io.SeekStart)
|
||||||
binary1 := bf.ReadNullTerminatedBytes()
|
name := bf.ReadNullTerminatedBytes()
|
||||||
s.server.userBinaryPartsLock.Lock()
|
s.server.userBinaryPartsLock.Lock()
|
||||||
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: 1}] = append(binary1, []byte{0x00}...)
|
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: 1}] = append(name, []byte{0x00}...)
|
||||||
s.server.userBinaryPartsLock.Unlock()
|
s.server.userBinaryPartsLock.Unlock()
|
||||||
s.Name = stringsupport.SJISToUTF8(binary1)
|
s.Name = stringsupport.SJISToUTF8(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
|||||||
@@ -2,87 +2,45 @@ package channelserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
"github.com/bwmarrin/discordgo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Character struct {
|
type Player struct {
|
||||||
ID uint32 `db:"id"`
|
CharName string
|
||||||
IsFemale bool `db:"is_female"`
|
QuestID int
|
||||||
IsNewCharacter bool `db:"is_new_character"`
|
|
||||||
Name string `db:"name"`
|
|
||||||
UnkDescString string `db:"unk_desc_string"`
|
|
||||||
HRP uint16 `db:"hrp"`
|
|
||||||
GR uint16 `db:"gr"`
|
|
||||||
WeaponType uint16 `db:"weapon_type"`
|
|
||||||
LastLogin uint32 `db:"last_login"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var weapons = []string{
|
func getPlayerSlice(s *Server) []Player {
|
||||||
"<:gs:970861408227049477>",
|
var p []Player
|
||||||
"<:hbg:970861408281563206>",
|
var questIndex int
|
||||||
"<:hm:970861408239628308>",
|
|
||||||
"<:lc:970861408298352660>",
|
|
||||||
"<:sns:970861408319315988>",
|
|
||||||
"<:lbg:970861408327725137>",
|
|
||||||
"<:ds:970861408277368883>",
|
|
||||||
"<:ls:970861408319311872>",
|
|
||||||
"<:hh:970861408222863400>",
|
|
||||||
"<:gl:970861408327720980>",
|
|
||||||
"<:bw:970861408294174780>",
|
|
||||||
"<:tf:970861408424177744>",
|
|
||||||
"<:sw:970861408340283472>",
|
|
||||||
"<:ms:970861408411594842>",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) getCharacterForUser(uid int) (*Character, error) {
|
for _, channel := range s.Channels {
|
||||||
character := Character{}
|
for _, stage := range channel.stages {
|
||||||
err := s.db.Get(&character, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE id = $1", uid)
|
if len(stage.clients) == 0 {
|
||||||
if err != nil {
|
continue
|
||||||
return nil, err
|
}
|
||||||
|
questID := 0
|
||||||
|
if stage.isQuest() {
|
||||||
|
questIndex++
|
||||||
|
questID = questIndex
|
||||||
|
}
|
||||||
|
for client := range stage.clients {
|
||||||
|
p = append(p, Player{
|
||||||
|
CharName: client.Name,
|
||||||
|
QuestID: questID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return p
|
||||||
return &character, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CountChars(s *Server) string {
|
func getCharacterList(s *Server) string {
|
||||||
count := 0
|
|
||||||
for _, stage := range s.stages {
|
|
||||||
count += len(stage.clients)
|
|
||||||
}
|
|
||||||
|
|
||||||
message := fmt.Sprintf("Server [%s]: %d players;", s.name, count)
|
|
||||||
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListPlayer struct {
|
|
||||||
CharName string
|
|
||||||
InQuest bool
|
|
||||||
WeaponEmoji string
|
|
||||||
QuestEmoji string
|
|
||||||
StageName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ListPlayer) toString(length int) string {
|
|
||||||
status := ""
|
|
||||||
if p.InQuest {
|
|
||||||
status = "(in quest " + p.QuestEmoji + ")"
|
|
||||||
} else {
|
|
||||||
status = p.StageName
|
|
||||||
}
|
|
||||||
|
|
||||||
missingSpace := length - len(p.CharName)
|
|
||||||
whitespaces := strings.Repeat(" ", missingSpace+5)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s %s %s %s", p.WeaponEmoji, p.CharName, whitespaces, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPlayerList(s *Server) ([]ListPlayer, int) {
|
|
||||||
list := []ListPlayer{}
|
|
||||||
questEmojis := []string{
|
questEmojis := []string{
|
||||||
|
":person_in_lotus_position:",
|
||||||
":white_circle:",
|
":white_circle:",
|
||||||
":red_circle:",
|
":red_circle:",
|
||||||
":blue_circle:",
|
":blue_circle:",
|
||||||
@@ -91,255 +49,41 @@ func getPlayerList(s *Server) ([]ListPlayer, int) {
|
|||||||
":purple_circle:",
|
":purple_circle:",
|
||||||
":yellow_circle:",
|
":yellow_circle:",
|
||||||
":orange_circle:",
|
":orange_circle:",
|
||||||
|
":black_circle:",
|
||||||
}
|
}
|
||||||
|
|
||||||
bigNameLen := 0
|
playerSlice := getPlayerSlice(s)
|
||||||
|
|
||||||
for _, stage := range s.stages {
|
sort.SliceStable(playerSlice, func(i, j int) bool {
|
||||||
if len(stage.clients) == 0 {
|
return playerSlice[i].QuestID < playerSlice[j].QuestID
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
questEmoji := ":black_circle:"
|
|
||||||
|
|
||||||
if len(questEmojis) > 0 {
|
|
||||||
questEmoji = questEmojis[len(questEmojis)-1]
|
|
||||||
questEmojis = questEmojis[:len(questEmojis)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
isQuest := stage.isQuest()
|
|
||||||
for client := range stage.clients {
|
|
||||||
char, err := s.getCharacterForUser(int(client.charID))
|
|
||||||
if err == nil {
|
|
||||||
if len(char.Name) > bigNameLen {
|
|
||||||
bigNameLen = len(char.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
list = append(list, ListPlayer{
|
|
||||||
CharName: char.Name,
|
|
||||||
InQuest: isQuest,
|
|
||||||
QuestEmoji: questEmoji,
|
|
||||||
WeaponEmoji: weapons[char.WeaponType],
|
|
||||||
StageName: stage.GetName(),
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list, bigNameLen
|
|
||||||
}
|
|
||||||
|
|
||||||
func PlayerList(s *Server) string {
|
|
||||||
list := ""
|
|
||||||
count := 0
|
|
||||||
listPlayers, bigNameLen := getPlayerList(s)
|
|
||||||
|
|
||||||
sort.SliceStable(listPlayers, func(i, j int) bool {
|
|
||||||
return listPlayers[i].CharName < listPlayers[j].CharName
|
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, lp := range listPlayers {
|
message := fmt.Sprintf("===== Online: %d =====\n", len(playerSlice))
|
||||||
list += lp.toString(bigNameLen) + "\n"
|
for _, player := range playerSlice {
|
||||||
count++
|
message += fmt.Sprintf("%s %s", questEmojis[player.QuestID], player.CharName)
|
||||||
}
|
}
|
||||||
|
|
||||||
message := fmt.Sprintf("<:SnS:822963937360347148> Frontier Hunters Online: [%s ] <:switcha:822963906401533992> \n============== Total %d =============\n", s.name, count)
|
|
||||||
message += list
|
|
||||||
|
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
|
||||||
func debug(s *Server) string {
|
|
||||||
list := ""
|
|
||||||
|
|
||||||
for _, stage := range s.stages {
|
|
||||||
if !stage.isQuest() && len(stage.objects) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
list += fmt.Sprintf(" -> Stage: %s StageId: %s\n", stage.GetName(), stage.id)
|
|
||||||
isQuest := "false"
|
|
||||||
hasDeparted := "false"
|
|
||||||
|
|
||||||
if stage.isQuest() {
|
|
||||||
isQuest = "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
list += fmt.Sprintf(" '-> isQuest: %s\n", isQuest)
|
|
||||||
|
|
||||||
if stage.isQuest() {
|
|
||||||
if len(stage.clients) > 0 {
|
|
||||||
hasDeparted = "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
list += fmt.Sprintf(" '-> isDeparted: %s\n", hasDeparted)
|
|
||||||
list += fmt.Sprintf(" '-> reserveSlots (%d/%d)\n", len(stage.reservedClientSlots), stage.maxPlayers)
|
|
||||||
|
|
||||||
for charid, _ := range stage.reservedClientSlots {
|
|
||||||
char, err := s.getCharacterForUser(int(charid))
|
|
||||||
if err == nil {
|
|
||||||
list += fmt.Sprintf(" '-> %s\n", char.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list += " '-> objects: \n"
|
|
||||||
for _, obj := range stage.objects {
|
|
||||||
objInfo := fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z)
|
|
||||||
list += fmt.Sprintf(" '-> ObjectId: %d - %s\n", obj.id, objInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message := fmt.Sprintf("Objects in Server: [%s ]\n", s.name)
|
|
||||||
message += list
|
|
||||||
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
func questlist(s *Server) string {
|
|
||||||
list := ""
|
|
||||||
|
|
||||||
for _, stage := range s.stages {
|
|
||||||
if !stage.isQuest() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
hasDeparted := ""
|
|
||||||
if len(stage.clients) > 0 {
|
|
||||||
hasDeparted = " - departed"
|
|
||||||
}
|
|
||||||
list += fmt.Sprintf(" '-> StageId: %s (%d/%d) %s - %s\n", stage.id, len(stage.reservedClientSlots), stage.maxPlayers, hasDeparted, stage.createdAt)
|
|
||||||
|
|
||||||
for charid, _ := range stage.reservedClientSlots {
|
|
||||||
char, err := s.getCharacterForUser(int(charid))
|
|
||||||
if err == nil {
|
|
||||||
list += fmt.Sprintf(" '-> %s\n", char.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message := fmt.Sprintf("Quests in Server: [%s ]\n", s.name)
|
|
||||||
message += list
|
|
||||||
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeStageById(s *Server, stageId string) string {
|
|
||||||
if s.stages[stageId] != nil {
|
|
||||||
delete(s.stages, stageId)
|
|
||||||
return "Stage deleted!"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Stage not found!"
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanStr(str string) string {
|
|
||||||
return strings.ToLower(strings.Trim(str, " "))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCharInfo(server *Server, charName string) string {
|
|
||||||
var s *Stage
|
|
||||||
var c *Session
|
|
||||||
|
|
||||||
for _, stage := range server.stages {
|
|
||||||
for client := range stage.clients {
|
|
||||||
|
|
||||||
if client.Name == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if cleanStr(client.Name) == cleanStr(charName) {
|
|
||||||
s = stage
|
|
||||||
c = client
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if s == nil {
|
|
||||||
return "Character not found"
|
|
||||||
}
|
|
||||||
|
|
||||||
objInfo := ""
|
|
||||||
|
|
||||||
obj := server.FindObjectByChar(c.charID)
|
|
||||||
// server.logger.Info("Found object: %+v", zap.Object("obj", obj))
|
|
||||||
|
|
||||||
if obj != nil {
|
|
||||||
objInfo = fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("Character: %s\nStage: %s\nStageId: %s\n%s", c.Name, s.GetName(), s.id, objInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) isDiscordAdmin(ds *discordgo.Session, m *discordgo.MessageCreate) bool {
|
|
||||||
for _, role := range m.Member.Roles {
|
|
||||||
for _, id := range s.erupeConfig.Discord.DevRoles {
|
|
||||||
if id == role {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// onDiscordMessage handles receiving messages from discord and forwarding them ingame.
|
// onDiscordMessage handles receiving messages from discord and forwarding them ingame.
|
||||||
func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) {
|
func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
// Ignore messages from our bot, or ones that are not in the correct channel.
|
// Ignore messages from our bot, or ones that are not in the correct channel.
|
||||||
if m.Author.ID == ds.State.User.ID || !s.enable {
|
if m.Author.Bot || m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore other channels in devMode
|
paddedName := strings.TrimSpace(strings.Map(func(r rune) rune {
|
||||||
if s.erupeConfig.Discord.DevMode && m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID {
|
if r > unicode.MaxASCII {
|
||||||
return
|
return -1
|
||||||
}
|
|
||||||
|
|
||||||
args := strings.Split(m.Content, " ")
|
|
||||||
commandName := args[0]
|
|
||||||
|
|
||||||
// Move to slash commadns
|
|
||||||
if commandName == "!players" {
|
|
||||||
ds.ChannelMessageSend(m.ChannelID, PlayerList(s))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if commandName == "-char" {
|
|
||||||
if len(args) < 2 {
|
|
||||||
ds.ChannelMessageSend(m.ChannelID, "Usage: !char <char name>")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return r
|
||||||
|
}, m.Author.Username))
|
||||||
|
|
||||||
charName := strings.Join(args[1:], " ")
|
for i := 0; i < 8-len(m.Author.Username); i++ {
|
||||||
ds.ChannelMessageSend(m.ChannelID, getCharInfo(s, charName))
|
paddedName += " "
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if commandName == "!debug" && s.isDiscordAdmin(ds, m) {
|
message := fmt.Sprintf("[D] %s > %s", paddedName, m.Content)
|
||||||
ds.ChannelMessageSend(m.ChannelID, debug(s))
|
s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message))
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if commandName == "!questlist" && s.isDiscordAdmin(ds, m) {
|
|
||||||
ds.ChannelMessageSend(m.ChannelID, questlist(s))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if commandName == "!remove-stage" && s.isDiscordAdmin(ds, m) {
|
|
||||||
if len(args) < 2 {
|
|
||||||
ds.ChannelMessageSend(m.ChannelID, "Usage: !remove-stage <stage id>")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
stageId := strings.Join(args[1:], " ")
|
|
||||||
ds.ChannelMessageSend(m.ChannelID, removeStageById(s, stageId))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.ChannelID == s.erupeConfig.Discord.RealtimeChannelID {
|
|
||||||
message := fmt.Sprintf("[DISCORD] %s: %s", m.Author.Username, m.Content)
|
|
||||||
s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,34 +303,6 @@ func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) {
|
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetBoostTime)
|
|
||||||
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{})
|
|
||||||
updateRights(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {
|
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit)
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) {
|
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetBoostRight)
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) {
|
|
||||||
pkt := p.(*mhfpacket.MsgMhfPostBoostTimeQuestReturn)
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {}
|
|
||||||
|
|
||||||
func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {}
|
|
||||||
|
|
||||||
func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {}
|
|
||||||
|
|
||||||
func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfSetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfSetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
|||||||
@@ -1892,11 +1892,25 @@ func handleMsgMhfSetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfCheckMonthlyItem(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfCheckMonthlyItem)
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
||||||
|
// TODO: Implement month-by-month tracker, 0 = Not claimed, 1 = Claimed
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfAcquireMonthlyItem(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfAcquireMonthlyItem)
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateInvGuild)
|
pkt := p.(*mhfpacket.MsgMhfEnumerateInvGuild)
|
||||||
stubEnumerateNoResults(s, pkt.AckHandle)
|
stubEnumerateNoResults(s, pkt.AckHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfOperationInvGuild)
|
||||||
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfUpdateGuildcard(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfUpdateGuildcard(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|||||||
@@ -5,11 +5,37 @@ import (
|
|||||||
ps "erupe-ce/common/pascalstring"
|
ps "erupe-ce/common/pascalstring"
|
||||||
"erupe-ce/common/stringsupport"
|
"erupe-ce/common/stringsupport"
|
||||||
"erupe-ce/network/mhfpacket"
|
"erupe-ce/network/mhfpacket"
|
||||||
|
"fmt"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const warehouseNamesQuery = `
|
||||||
|
SELECT
|
||||||
|
COALESCE(item0name, ''),
|
||||||
|
COALESCE(item1name, ''),
|
||||||
|
COALESCE(item2name, ''),
|
||||||
|
COALESCE(item3name, ''),
|
||||||
|
COALESCE(item4name, ''),
|
||||||
|
COALESCE(item5name, ''),
|
||||||
|
COALESCE(item6name, ''),
|
||||||
|
COALESCE(item7name, ''),
|
||||||
|
COALESCE(item8name, ''),
|
||||||
|
COALESCE(item9name, ''),
|
||||||
|
COALESCE(equip0name, ''),
|
||||||
|
COALESCE(equip1name, ''),
|
||||||
|
COALESCE(equip2name, ''),
|
||||||
|
COALESCE(equip3name, ''),
|
||||||
|
COALESCE(equip4name, ''),
|
||||||
|
COALESCE(equip5name, ''),
|
||||||
|
COALESCE(equip6name, ''),
|
||||||
|
COALESCE(equip7name, ''),
|
||||||
|
COALESCE(equip8name, ''),
|
||||||
|
COALESCE(equip9name, '')
|
||||||
|
FROM warehouse
|
||||||
|
`
|
||||||
|
|
||||||
func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfUpdateInterior)
|
pkt := p.(*mhfpacket.MsgMhfUpdateInterior)
|
||||||
_, err := s.server.db.Exec("UPDATE characters SET house=$1 WHERE id=$2", pkt.InteriorData, s.charID)
|
_, err := s.server.db.Exec("UPDATE characters SET house=$1 WHERE id=$2", pkt.InteriorData, s.charID)
|
||||||
@@ -66,7 +92,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
house := HouseData{}
|
house := HouseData{}
|
||||||
row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name=$1", pkt.Name)
|
row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name ILIKE $1", fmt.Sprintf(`%%%s%%`, pkt.Name))
|
||||||
err := row.StructScan(&house)
|
err := row.StructScan(&house)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -341,8 +367,190 @@ func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfOperateWarehouse)
|
||||||
|
var t int
|
||||||
|
err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t)
|
||||||
|
if err != nil {
|
||||||
|
s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID)
|
||||||
|
}
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteUint8(pkt.Operation)
|
||||||
|
switch pkt.Operation {
|
||||||
|
case 0:
|
||||||
|
var count uint8
|
||||||
|
itemNames := make([]string, 10)
|
||||||
|
equipNames := make([]string, 10)
|
||||||
|
s.server.db.QueryRow(fmt.Sprintf("%s WHERE character_id=$1", warehouseNamesQuery), s.charID).Scan(&itemNames[0],
|
||||||
|
&itemNames[1], &itemNames[2], &itemNames[3], &itemNames[4], &itemNames[5], &itemNames[6], &itemNames[7], &itemNames[8], &itemNames[9], &equipNames[0],
|
||||||
|
&equipNames[1], &equipNames[2], &equipNames[3], &equipNames[4], &equipNames[5], &equipNames[6], &equipNames[7], &equipNames[8], &equipNames[9])
|
||||||
|
bf.WriteUint32(0)
|
||||||
|
bf.WriteUint16(10000) // Usages
|
||||||
|
temp := byteframe.NewByteFrame()
|
||||||
|
for i, name := range itemNames {
|
||||||
|
if len(name) > 0 {
|
||||||
|
count++
|
||||||
|
temp.WriteUint8(0)
|
||||||
|
temp.WriteUint8(uint8(i))
|
||||||
|
ps.Uint8(temp, name, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, name := range equipNames {
|
||||||
|
if len(name) > 0 {
|
||||||
|
count++
|
||||||
|
temp.WriteUint8(1)
|
||||||
|
temp.WriteUint8(uint8(i))
|
||||||
|
ps.Uint8(temp, name, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bf.WriteUint8(count)
|
||||||
|
bf.WriteBytes(temp.Data())
|
||||||
|
case 1:
|
||||||
|
bf.WriteUint8(0)
|
||||||
|
case 2:
|
||||||
|
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%dname=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), pkt.Name, s.charID)
|
||||||
|
case 3:
|
||||||
|
bf.WriteUint32(0) // Usage renewal time, >1 = disabled
|
||||||
|
bf.WriteUint16(10000) // Usages
|
||||||
|
case 4:
|
||||||
|
bf.WriteUint32(0)
|
||||||
|
bf.WriteUint16(10000) // Usages
|
||||||
|
bf.WriteUint8(0)
|
||||||
|
}
|
||||||
|
// Opcodes
|
||||||
|
// 0 = Get box names
|
||||||
|
// 1 = Commit usage
|
||||||
|
// 2 = Rename
|
||||||
|
// 3 = Get usage limit
|
||||||
|
// 4 = Get gift box names (doesn't do anything?)
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {}
|
func addWarehouseGift(s *Session, boxType string, giftStack mhfpacket.WarehouseStack) {
|
||||||
|
giftBox := getWarehouseBox(s, boxType, 10)
|
||||||
|
if boxType == "item" {
|
||||||
|
exists := false
|
||||||
|
for i, stack := range giftBox {
|
||||||
|
if stack.ItemID == giftStack.ItemID {
|
||||||
|
exists = true
|
||||||
|
giftBox[i].Quantity += giftStack.Quantity
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if exists == false {
|
||||||
|
giftBox = append(giftBox, giftStack)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
giftBox = append(giftBox, giftStack)
|
||||||
|
}
|
||||||
|
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s10=$1 WHERE character_id=$2", boxType), boxToBytes(giftBox, boxType), s.charID)
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {}
|
func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.WarehouseStack {
|
||||||
|
var data []byte
|
||||||
|
s.server.db.QueryRow(fmt.Sprintf("SELECT %s%d FROM warehouse WHERE character_id=$1", boxType, boxIndex), s.charID).Scan(&data)
|
||||||
|
if len(data) > 0 {
|
||||||
|
box := byteframe.NewByteFrameFromBytes(data)
|
||||||
|
numStacks := box.ReadUint16()
|
||||||
|
stacks := make([]mhfpacket.WarehouseStack, numStacks)
|
||||||
|
for i := 0; i < int(numStacks); i++ {
|
||||||
|
if boxType == "item" {
|
||||||
|
stacks[i].ID = box.ReadUint32()
|
||||||
|
stacks[i].Index = box.ReadUint16()
|
||||||
|
stacks[i].ItemID = box.ReadUint16()
|
||||||
|
stacks[i].Quantity = box.ReadUint16()
|
||||||
|
box.ReadUint16()
|
||||||
|
} else {
|
||||||
|
stacks[i].ID = box.ReadUint32()
|
||||||
|
stacks[i].Index = box.ReadUint16()
|
||||||
|
stacks[i].EquipType = box.ReadUint16()
|
||||||
|
stacks[i].ItemID = box.ReadUint16()
|
||||||
|
stacks[i].Data = box.ReadBytes(56)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stacks
|
||||||
|
} else {
|
||||||
|
return make([]mhfpacket.WarehouseStack, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func boxToBytes(stacks []mhfpacket.WarehouseStack, boxType string) []byte {
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteUint16(uint16(len(stacks)))
|
||||||
|
for i, stack := range stacks {
|
||||||
|
if boxType == "item" {
|
||||||
|
bf.WriteUint32(stack.ID)
|
||||||
|
bf.WriteUint16(uint16(i + 1))
|
||||||
|
bf.WriteUint16(stack.ItemID)
|
||||||
|
bf.WriteUint16(stack.Quantity)
|
||||||
|
bf.WriteUint16(0)
|
||||||
|
} else {
|
||||||
|
bf.WriteUint32(stack.ID)
|
||||||
|
bf.WriteUint16(uint16(i + 1))
|
||||||
|
bf.WriteUint16(stack.EquipType)
|
||||||
|
bf.WriteUint16(stack.ItemID)
|
||||||
|
bf.WriteBytes(stack.Data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bf.WriteUint16(0)
|
||||||
|
return bf.Data()
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse)
|
||||||
|
box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex)
|
||||||
|
if len(box) > 0 {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box, pkt.BoxType))
|
||||||
|
} else {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse)
|
||||||
|
box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex)
|
||||||
|
// Update existing stacks
|
||||||
|
var newStacks []mhfpacket.WarehouseStack
|
||||||
|
for _, update := range pkt.Updates {
|
||||||
|
exists := false
|
||||||
|
if pkt.BoxType == "item" {
|
||||||
|
for i, stack := range box {
|
||||||
|
if stack.Index == update.Index {
|
||||||
|
exists = true
|
||||||
|
box[i].Quantity = update.Quantity
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i, stack := range box {
|
||||||
|
if stack.Index == update.Index {
|
||||||
|
exists = true
|
||||||
|
box[i].ItemID = update.ItemID
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if exists == false {
|
||||||
|
newStacks = append(newStacks, update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Append new stacks
|
||||||
|
for _, stack := range newStacks {
|
||||||
|
box = append(box, stack)
|
||||||
|
}
|
||||||
|
// Slice empty stacks
|
||||||
|
var cleanedBox []mhfpacket.WarehouseStack
|
||||||
|
for _, stack := range box {
|
||||||
|
if pkt.BoxType == "item" {
|
||||||
|
if stack.Quantity > 0 {
|
||||||
|
cleanedBox = append(cleanedBox, stack)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if stack.ItemID != 0 {
|
||||||
|
cleanedBox = append(cleanedBox, stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox, pkt.BoxType), s.charID)
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func handleMsgMhfLoadPartner(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgMhfSavePartner(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfSavePartner(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfSavePartner)
|
pkt := p.(*mhfpacket.MsgMhfSavePartner)
|
||||||
|
|
||||||
dumpSaveData(s, pkt.RawDataPayload, "_partner")
|
dumpSaveData(s, pkt.RawDataPayload, "partner")
|
||||||
|
|
||||||
_, err := s.server.db.Exec("UPDATE characters SET partner=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
|
_, err := s.server.db.Exec("UPDATE characters SET partner=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -75,7 +75,7 @@ func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfSaveHunterNavi)
|
pkt := p.(*mhfpacket.MsgMhfSaveHunterNavi)
|
||||||
|
|
||||||
dumpSaveData(s, pkt.RawDataPayload, "_hunternavi")
|
dumpSaveData(s, pkt.RawDataPayload, "hunternavi")
|
||||||
|
|
||||||
if pkt.IsDataDiff {
|
if pkt.IsDataDiff {
|
||||||
var data []byte
|
var data []byte
|
||||||
@@ -121,10 +121,30 @@ func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfMercenaryHuntdata(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfMercenaryHuntdata(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfMercenaryHuntdata)
|
pkt := p.(*mhfpacket.MsgMhfMercenaryHuntdata)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x0A))
|
if pkt.Unk0 == 1 {
|
||||||
|
// Format:
|
||||||
|
// uint8 Hunts
|
||||||
|
// struct Hunt
|
||||||
|
// uint32 HuntID
|
||||||
|
// uint32 MonID
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
|
||||||
|
} else {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfEnumerateMercenaryLog(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfEnumerateMercenaryLog(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfEnumerateMercenaryLog)
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteUint32(0)
|
||||||
|
// Format:
|
||||||
|
// struct Log
|
||||||
|
// uint32 Timestamp
|
||||||
|
// []byte Name (len 18)
|
||||||
|
// uint8 Unk
|
||||||
|
// uint8 Unk
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfCreateMercenary)
|
pkt := p.(*mhfpacket.MsgMhfCreateMercenary)
|
||||||
@@ -208,6 +228,7 @@ func handleMsgMhfLoadOtomoAirou(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfSaveOtomoAirou)
|
pkt := p.(*mhfpacket.MsgMhfSaveOtomoAirou)
|
||||||
|
dumpSaveData(s, pkt.RawDataPayload, "otomoairou")
|
||||||
decomp, err := nullcomp.Decompress(pkt.RawDataPayload[1:])
|
decomp, err := nullcomp.Decompress(pkt.RawDataPayload[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to decompress airou", zap.Error(err))
|
s.logger.Error("Failed to decompress airou", zap.Error(err))
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
|
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
|
||||||
s.server.raviente.Lock()
|
s.server.raviente.Lock()
|
||||||
switch pkt.SemaphoreID {
|
switch pkt.SemaphoreID {
|
||||||
case 3:
|
case 4:
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
size := 6
|
size := 6
|
||||||
for i := 0; i < len(bf.Data())-1; i += size {
|
for i := 0; i < len(bf.Data())-1; i += size {
|
||||||
@@ -49,7 +49,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
resp.WriteUint8(0)
|
resp.WriteUint8(0)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
case 4:
|
case 5:
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
size := 6
|
size := 6
|
||||||
for i := 0; i < len(bf.Data())-1; i += size {
|
for i := 0; i < len(bf.Data())-1; i += size {
|
||||||
@@ -74,7 +74,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
resp.WriteUint8(0)
|
resp.WriteUint8(0)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
case 5:
|
case 6:
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
size := 6
|
size := 6
|
||||||
for i := 0; i < len(bf.Data())-1; i += size {
|
for i := 0; i < len(bf.Data())-1; i += size {
|
||||||
@@ -242,15 +242,15 @@ func handleMsgSysLoadRegister(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func (s *Session) notifyRavi() {
|
func (s *Session) notifyRavi() {
|
||||||
var temp mhfpacket.MHFPacket
|
var temp mhfpacket.MHFPacket
|
||||||
raviNotif := byteframe.NewByteFrame()
|
raviNotif := byteframe.NewByteFrame()
|
||||||
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 3}
|
|
||||||
raviNotif.WriteUint16(uint16(temp.Opcode()))
|
|
||||||
temp.Build(raviNotif, s.clientContext)
|
|
||||||
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 4}
|
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 4}
|
||||||
raviNotif.WriteUint16(uint16(temp.Opcode()))
|
raviNotif.WriteUint16(uint16(temp.Opcode()))
|
||||||
temp.Build(raviNotif, s.clientContext)
|
temp.Build(raviNotif, s.clientContext)
|
||||||
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 5}
|
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 5}
|
||||||
raviNotif.WriteUint16(uint16(temp.Opcode()))
|
raviNotif.WriteUint16(uint16(temp.Opcode()))
|
||||||
temp.Build(raviNotif, s.clientContext)
|
temp.Build(raviNotif, s.clientContext)
|
||||||
|
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 6}
|
||||||
|
raviNotif.WriteUint16(uint16(temp.Opcode()))
|
||||||
|
temp.Build(raviNotif, s.clientContext)
|
||||||
raviNotif.WriteUint16(0x0010) // End it.
|
raviNotif.WriteUint16(0x0010) // End it.
|
||||||
sema := getRaviSemaphore(s)
|
sema := getRaviSemaphore(s)
|
||||||
if sema != "" {
|
if sema != "" {
|
||||||
@@ -262,7 +262,7 @@ func (s *Session) notifyRavi() {
|
|||||||
|
|
||||||
func getRaviSemaphore(s *Session) string {
|
func getRaviSemaphore(s *Session) string {
|
||||||
for _, semaphore := range s.server.semaphore {
|
for _, semaphore := range s.server.semaphore {
|
||||||
if strings.HasPrefix(semaphore.id_semaphore, "hs_l0u3B5") && strings.HasSuffix(semaphore.id_semaphore, "3") {
|
if strings.HasPrefix(semaphore.id_semaphore, "hs_l0u3B5") && strings.HasSuffix(semaphore.id_semaphore, "4") {
|
||||||
return semaphore.id_semaphore
|
return semaphore.id_semaphore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package channelserver
|
package channelserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
ps "erupe-ce/common/pascalstring"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
@@ -17,7 +19,19 @@ func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to update rengokudata savedata in db", zap.Error(err))
|
s.logger.Fatal("Failed to update rengokudata savedata in db", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
|
||||||
|
bf.Seek(71, 0)
|
||||||
|
maxStageMp := bf.ReadUint32()
|
||||||
|
maxScoreMp := bf.ReadUint32()
|
||||||
|
bf.Seek(4, 1)
|
||||||
|
maxStageSp := bf.ReadUint32()
|
||||||
|
maxScoreSp := bf.ReadUint32()
|
||||||
|
var t int
|
||||||
|
err = s.server.db.QueryRow("SELECT character_id FROM rengoku_score WHERE character_id=$1", s.charID).Scan(&t)
|
||||||
|
if err != nil {
|
||||||
|
s.server.db.Exec("INSERT INTO rengoku_score (character_id) VALUES ($1)", s.charID)
|
||||||
|
}
|
||||||
|
s.server.db.Exec("UPDATE rengoku_score SET max_stages_mp=$1, max_points_mp=$2, max_stages_sp=$3, max_points_sp=$4 WHERE character_id=$5", maxStageMp, maxScoreMp, maxStageSp, maxScoreSp, s.charID)
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,16 +95,191 @@ func handleMsgMhfGetRengokuBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rengokuScoreQuery = `
|
||||||
|
SELECT max_stages_mp, max_points_mp, max_stages_sp, max_points_sp, c.name, gc.guild_id
|
||||||
|
FROM rengoku_score rs
|
||||||
|
LEFT JOIN characters c ON c.id = rs.character_id
|
||||||
|
LEFT JOIN guild_characters gc ON gc.character_id = rs.character_id
|
||||||
|
`
|
||||||
|
|
||||||
|
type RengokuScore struct {
|
||||||
|
Name string `db:"name"`
|
||||||
|
GuildID int `db:"guild_id"`
|
||||||
|
MaxStagesMP uint32 `db:"max_stages_mp"`
|
||||||
|
MaxPointsMP uint32 `db:"max_points_mp"`
|
||||||
|
MaxStagesSP uint32 `db:"max_stages_sp"`
|
||||||
|
MaxPointsSP uint32 `db:"max_points_sp"`
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateRengokuRanking)
|
pkt := p.(*mhfpacket.MsgMhfEnumerateRengokuRanking)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
|
||||||
|
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
|
||||||
|
isApplicant, _ := guild.HasApplicationForCharID(s, s.charID)
|
||||||
|
if isApplicant {
|
||||||
|
guild = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var score RengokuScore
|
||||||
|
i := uint32(1)
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
scoreData := byteframe.NewByteFrame()
|
||||||
|
switch pkt.Leaderboard {
|
||||||
|
case 0: // Max stage overall MP
|
||||||
|
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_mp DESC", rengokuScoreQuery))
|
||||||
|
for rows.Next() {
|
||||||
|
rows.StructScan(&score)
|
||||||
|
if score.Name == s.Name {
|
||||||
|
bf.WriteUint32(i)
|
||||||
|
bf.WriteUint32(score.MaxStagesMP)
|
||||||
|
ps.Uint8(bf, s.Name, true)
|
||||||
|
ps.Uint8(bf, "", false)
|
||||||
|
}
|
||||||
|
scoreData.WriteUint32(i)
|
||||||
|
scoreData.WriteUint32(score.MaxStagesMP)
|
||||||
|
ps.Uint8(scoreData, score.Name, true)
|
||||||
|
ps.Uint8(scoreData, "", false)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
case 1: // Max RdP overall MP
|
||||||
|
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_mp DESC", rengokuScoreQuery))
|
||||||
|
for rows.Next() {
|
||||||
|
rows.StructScan(&score)
|
||||||
|
if score.Name == s.Name {
|
||||||
|
bf.WriteUint32(i)
|
||||||
|
bf.WriteUint32(score.MaxPointsMP)
|
||||||
|
ps.Uint8(bf, s.Name, true)
|
||||||
|
ps.Uint8(bf, "", false)
|
||||||
|
}
|
||||||
|
scoreData.WriteUint32(i)
|
||||||
|
scoreData.WriteUint32(score.MaxPointsMP)
|
||||||
|
ps.Uint8(scoreData, score.Name, true)
|
||||||
|
ps.Uint8(scoreData, "", false)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
case 2: // Max stage guild MP
|
||||||
|
if guild != nil {
|
||||||
|
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_mp DESC", rengokuScoreQuery), guild.ID)
|
||||||
|
for rows.Next() {
|
||||||
|
rows.StructScan(&score)
|
||||||
|
if score.Name == s.Name {
|
||||||
|
bf.WriteUint32(i)
|
||||||
|
bf.WriteUint32(score.MaxStagesMP)
|
||||||
|
ps.Uint8(bf, s.Name, true)
|
||||||
|
ps.Uint8(bf, "", false)
|
||||||
|
}
|
||||||
|
scoreData.WriteUint32(i)
|
||||||
|
scoreData.WriteUint32(score.MaxStagesMP)
|
||||||
|
ps.Uint8(scoreData, score.Name, true)
|
||||||
|
ps.Uint8(scoreData, "", false)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bf.WriteBytes(make([]byte, 11))
|
||||||
|
}
|
||||||
|
case 3: // Max RdP guild MP
|
||||||
|
if guild != nil {
|
||||||
|
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_mp DESC", rengokuScoreQuery), guild.ID)
|
||||||
|
for rows.Next() {
|
||||||
|
rows.StructScan(&score)
|
||||||
|
if score.Name == s.Name {
|
||||||
|
bf.WriteUint32(i)
|
||||||
|
bf.WriteUint32(score.MaxPointsMP)
|
||||||
|
ps.Uint8(bf, s.Name, true)
|
||||||
|
ps.Uint8(bf, "", false)
|
||||||
|
}
|
||||||
|
scoreData.WriteUint32(i)
|
||||||
|
scoreData.WriteUint32(score.MaxPointsMP)
|
||||||
|
ps.Uint8(scoreData, score.Name, true)
|
||||||
|
ps.Uint8(scoreData, "", false)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bf.WriteBytes(make([]byte, 11))
|
||||||
|
}
|
||||||
|
case 4: // Max stage overall SP
|
||||||
|
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_sp DESC", rengokuScoreQuery))
|
||||||
|
for rows.Next() {
|
||||||
|
rows.StructScan(&score)
|
||||||
|
if score.Name == s.Name {
|
||||||
|
bf.WriteUint32(i)
|
||||||
|
bf.WriteUint32(score.MaxStagesSP)
|
||||||
|
ps.Uint8(bf, s.Name, true)
|
||||||
|
ps.Uint8(bf, "", false)
|
||||||
|
}
|
||||||
|
scoreData.WriteUint32(i)
|
||||||
|
scoreData.WriteUint32(score.MaxStagesSP)
|
||||||
|
ps.Uint8(scoreData, score.Name, true)
|
||||||
|
ps.Uint8(scoreData, "", false)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
case 5: // Max RdP overall SP
|
||||||
|
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_sp DESC", rengokuScoreQuery))
|
||||||
|
for rows.Next() {
|
||||||
|
rows.StructScan(&score)
|
||||||
|
if score.Name == s.Name {
|
||||||
|
bf.WriteUint32(i)
|
||||||
|
bf.WriteUint32(score.MaxPointsSP)
|
||||||
|
ps.Uint8(bf, s.Name, true)
|
||||||
|
ps.Uint8(bf, "", false)
|
||||||
|
}
|
||||||
|
scoreData.WriteUint32(i)
|
||||||
|
scoreData.WriteUint32(score.MaxPointsSP)
|
||||||
|
ps.Uint8(scoreData, score.Name, true)
|
||||||
|
ps.Uint8(scoreData, "", false)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
case 6: // Max stage guild SP
|
||||||
|
if guild != nil {
|
||||||
|
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_sp DESC", rengokuScoreQuery), guild.ID)
|
||||||
|
for rows.Next() {
|
||||||
|
rows.StructScan(&score)
|
||||||
|
if score.Name == s.Name {
|
||||||
|
bf.WriteUint32(i)
|
||||||
|
bf.WriteUint32(score.MaxStagesSP)
|
||||||
|
ps.Uint8(bf, s.Name, true)
|
||||||
|
ps.Uint8(bf, "", false)
|
||||||
|
}
|
||||||
|
scoreData.WriteUint32(i)
|
||||||
|
scoreData.WriteUint32(score.MaxStagesSP)
|
||||||
|
ps.Uint8(scoreData, score.Name, true)
|
||||||
|
ps.Uint8(scoreData, "", false)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bf.WriteBytes(make([]byte, 11))
|
||||||
|
}
|
||||||
|
case 7: // Max RdP guild SP
|
||||||
|
if guild != nil {
|
||||||
|
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_sp DESC", rengokuScoreQuery), guild.ID)
|
||||||
|
for rows.Next() {
|
||||||
|
rows.StructScan(&score)
|
||||||
|
if score.Name == s.Name {
|
||||||
|
bf.WriteUint32(i)
|
||||||
|
bf.WriteUint32(score.MaxPointsSP)
|
||||||
|
ps.Uint8(bf, s.Name, true)
|
||||||
|
ps.Uint8(bf, "", false)
|
||||||
|
}
|
||||||
|
scoreData.WriteUint32(i)
|
||||||
|
scoreData.WriteUint32(score.MaxPointsSP)
|
||||||
|
ps.Uint8(scoreData, score.Name, true)
|
||||||
|
ps.Uint8(scoreData, "", false)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bf.WriteBytes(make([]byte, 11))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bf.WriteUint8(uint8(i) - 1)
|
||||||
|
bf.WriteBytes(scoreData.Data())
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfGetRengokuRankingRank(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfGetRengokuRankingRank(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetRengokuRankingRank)
|
pkt := p.(*mhfpacket.MsgMhfGetRengokuRankingRank)
|
||||||
|
// What is this for?
|
||||||
resp := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
bf.WriteUint32(0) // Max stage overall MP rank
|
||||||
|
bf.WriteUint32(0) // Max RdP overall MP rank
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,12 +45,10 @@ func destructEmptySemaphores(s *Session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func releaseRaviSemaphore(s *Session, sema *Semaphore) {
|
func releaseRaviSemaphore(s *Session, sema *Semaphore) {
|
||||||
if !strings.HasSuffix(sema.id_semaphore, "5") {
|
delete(sema.reservedClientSlots, s.charID)
|
||||||
delete(sema.reservedClientSlots, s.charID)
|
delete(sema.clients, s)
|
||||||
delete(sema.clients, s)
|
if strings.HasSuffix(sema.id_semaphore, "2") && len(sema.clients) == 0 {
|
||||||
}
|
s.logger.Debug("Main raviente semaphore is empty, resetting")
|
||||||
if len(sema.reservedClientSlots) == 0 && len(sema.clients) == 0 {
|
|
||||||
s.logger.Debug("Raviente semaphore is empty, resetting")
|
|
||||||
resetRavi(s)
|
resetRavi(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +89,7 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
suffix, _ := strconv.ParseUint(pkt.SemaphoreID[len(pkt.SemaphoreID)-1:], 10, 32)
|
suffix, _ := strconv.ParseUint(pkt.SemaphoreID[len(pkt.SemaphoreID)-1:], 10, 32)
|
||||||
s.server.semaphore[SemaphoreID] = &Semaphore{
|
s.server.semaphore[SemaphoreID] = &Semaphore{
|
||||||
id_semaphore: pkt.SemaphoreID,
|
id_semaphore: pkt.SemaphoreID,
|
||||||
id: uint32(suffix),
|
id: uint32(suffix + 1),
|
||||||
clients: make(map[*Session]uint32),
|
clients: make(map[*Session]uint32),
|
||||||
reservedClientSlots: make(map[uint32]interface{}),
|
reservedClientSlots: make(map[uint32]interface{}),
|
||||||
maxPlayers: 32,
|
maxPlayers: 32,
|
||||||
|
|||||||
@@ -135,7 +135,6 @@ func removeSessionFromStage(s *Session) {
|
|||||||
// Remove client from old stage.
|
// Remove client from old stage.
|
||||||
s.stage.Lock()
|
s.stage.Lock()
|
||||||
delete(s.stage.clients, s)
|
delete(s.stage.clients, s)
|
||||||
delete(s.stage.reservedClientSlots, s.charID)
|
|
||||||
|
|
||||||
// Delete old stage objects owned by the client.
|
// Delete old stage objects owned by the client.
|
||||||
s.logger.Info("Sending notification to old stage clients")
|
s.logger.Info("Sending notification to old stage clients")
|
||||||
@@ -157,6 +156,7 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
if s.stageID == "" {
|
if s.stageID == "" {
|
||||||
s.stageMoveStack.Set(pkt.StageID)
|
s.stageMoveStack.Set(pkt.StageID)
|
||||||
} else {
|
} else {
|
||||||
|
s.stage.reservedClientSlots[s.charID] = false
|
||||||
s.stageMoveStack.Push(s.stageID)
|
s.stageMoveStack.Push(s.stageID)
|
||||||
s.stageMoveStack.Lock()
|
s.stageMoveStack.Lock()
|
||||||
}
|
}
|
||||||
@@ -175,11 +175,18 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
// Transfer back to the saved stage ID before the previous move or enter.
|
// Transfer back to the saved stage ID before the previous move or enter.
|
||||||
s.stageMoveStack.Unlock()
|
s.stageMoveStack.Unlock()
|
||||||
backStage, err := s.stageMoveStack.Pop()
|
backStage, err := s.stageMoveStack.Pop()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, exists := s.stage.reservedClientSlots[s.charID]; exists {
|
||||||
|
delete(s.stage.reservedClientSlots, s.charID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := s.server.stages[backStage].reservedClientSlots[s.charID]; exists {
|
||||||
|
delete(s.server.stages[backStage].reservedClientSlots, s.charID)
|
||||||
|
}
|
||||||
|
|
||||||
doStageTransfer(s, pkt.AckHandle, backStage)
|
doStageTransfer(s, pkt.AckHandle, backStage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package channelserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
|
||||||
"erupe-ce/network/mhfpacket"
|
"erupe-ce/network/mhfpacket"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -58,11 +57,3 @@ func handleMsgMhfGetUdTacticsRanking(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgMhfSetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfSetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfGetUdTacticsLog(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfGetUdTacticsLog(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {}
|
|
||||||
|
|
||||||
func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) {}
|
|
||||||
|
|
||||||
func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {}
|
|
||||||
|
|
||||||
func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) {}
|
|
||||||
|
|||||||
@@ -15,23 +15,6 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StageIdType = string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// GlobalStage is the stage that is used for all users.
|
|
||||||
MezeportaStageId StageIdType = "sl1Ns200p0a0u0"
|
|
||||||
GuildHallLv1StageId StageIdType = "sl1Ns202p0a0u0"
|
|
||||||
GuildHallLv2StageId StageIdType = "sl1Ns203p0a0u0"
|
|
||||||
GuildHallLv3StageId StageIdType = "sl1Ns204p0a0u0"
|
|
||||||
PugiFarmStageId StageIdType = "sl1Ns205p0a0u0"
|
|
||||||
RastaBarStageId StageIdType = "sl1Ns211p0a0u0"
|
|
||||||
PalloneCaravanStageId StageIdType = "sl1Ns260p0a0u0"
|
|
||||||
GookFarmStageId StageIdType = "sl1Ns265p0a0u0"
|
|
||||||
DivaFountainStageId StageIdType = "sl2Ns379p0a0u0"
|
|
||||||
DivaHallStageId StageIdType = "sl1Ns445p0a0u0"
|
|
||||||
MezFesStageId StageIdType = "sl1Ns462p0a0u0"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config struct allows configuring the server.
|
// Config struct allows configuring the server.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ID uint16
|
ID uint16
|
||||||
@@ -80,8 +63,7 @@ type Server struct {
|
|||||||
// Discord chat integration
|
// Discord chat integration
|
||||||
discordBot *discordbot.DiscordBot
|
discordBot *discordbot.DiscordBot
|
||||||
|
|
||||||
name string
|
name string
|
||||||
enable bool
|
|
||||||
|
|
||||||
raviente *Raviente
|
raviente *Raviente
|
||||||
}
|
}
|
||||||
@@ -154,43 +136,30 @@ func NewServer(config *Config) *Server {
|
|||||||
stages: make(map[string]*Stage),
|
stages: make(map[string]*Stage),
|
||||||
userBinaryParts: make(map[userBinaryPartID][]byte),
|
userBinaryParts: make(map[userBinaryPartID][]byte),
|
||||||
semaphore: make(map[string]*Semaphore),
|
semaphore: make(map[string]*Semaphore),
|
||||||
semaphoreIndex: 5,
|
semaphoreIndex: 7,
|
||||||
discordBot: config.DiscordBot,
|
discordBot: config.DiscordBot,
|
||||||
name: config.Name,
|
name: config.Name,
|
||||||
enable: config.Enable,
|
|
||||||
raviente: NewRaviente(),
|
raviente: NewRaviente(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mezeporta
|
// Mezeporta
|
||||||
s.stages["sl1Ns200p0a0u0"] = NewStage("sl1Ns200p0a0u0")
|
s.stages["sl1Ns200p0a0u0"] = NewStage("sl1Ns200p0a0u0")
|
||||||
|
|
||||||
// Guild Hall LV1
|
|
||||||
s.stages["sl1Ns202p0a0u0"] = NewStage("sl1Ns202p0a0u0")
|
|
||||||
|
|
||||||
// Guild Hall LV2
|
|
||||||
s.stages["sl1Ns203p0a0u0"] = NewStage("sl1Ns203p0a0u0")
|
|
||||||
|
|
||||||
// Guild Hall LV3
|
|
||||||
s.stages["sl1Ns204p0a0u0"] = NewStage("sl1Ns204p0a0u0")
|
|
||||||
|
|
||||||
// Pugi Farm
|
|
||||||
s.stages["sl1Ns205p0a0u0"] = NewStage("sl1Ns205p0a0u0")
|
|
||||||
|
|
||||||
// Rasta bar stage
|
// Rasta bar stage
|
||||||
s.stages["sl1Ns211p0a0u0"] = NewStage("sl1Ns211p0a0u0")
|
s.stages["sl1Ns211p0a0u0"] = NewStage("sl1Ns211p0a0u0")
|
||||||
|
|
||||||
// Pallone Carvan
|
// Pallone Carvan
|
||||||
s.stages["sl1Ns260p0a0u0"] = NewStage("sl1Ns260p0a0u0")
|
s.stages["sl1Ns260p0a0u0"] = NewStage("sl1Ns260p0a0u0")
|
||||||
|
|
||||||
// Gook Farm
|
// Pallone Guest House 1st Floor
|
||||||
s.stages["sl1Ns265p0a0u0"] = NewStage("sl1Ns265p0a0u0")
|
s.stages["sl1Ns262p0a0u0"] = NewStage("sl1Ns262p0a0u0")
|
||||||
|
|
||||||
|
// Pallone Guest House 2nd Floor
|
||||||
|
s.stages["sl1Ns263p0a0u0"] = NewStage("sl1Ns263p0a0u0")
|
||||||
|
|
||||||
// Diva fountain / prayer fountain.
|
// Diva fountain / prayer fountain.
|
||||||
s.stages["sl2Ns379p0a0u0"] = NewStage("sl2Ns379p0a0u0")
|
s.stages["sl2Ns379p0a0u0"] = NewStage("sl2Ns379p0a0u0")
|
||||||
|
|
||||||
// Diva Hall
|
|
||||||
s.stages["sl1Ns445p0a0u0"] = NewStage("sl1Ns445p0a0u0")
|
|
||||||
|
|
||||||
// MezFes
|
// MezFes
|
||||||
s.stages["sl1Ns462p0a0u0"] = NewStage("sl1Ns462p0a0u0")
|
s.stages["sl1Ns462p0a0u0"] = NewStage("sl1Ns462p0a0u0")
|
||||||
|
|
||||||
@@ -367,7 +336,7 @@ func (s *Server) BroadcastRaviente(ip uint32, port uint16, stage []byte, _type u
|
|||||||
|
|
||||||
func (s *Server) DiscordChannelSend(charName string, content string) {
|
func (s *Server) DiscordChannelSend(charName string, content string) {
|
||||||
if s.erupeConfig.Discord.Enabled && s.discordBot != nil {
|
if s.erupeConfig.Discord.Enabled && s.discordBot != nil {
|
||||||
message := fmt.Sprintf("**%s** : %s", charName, content)
|
message := fmt.Sprintf("**%s**: %s", charName, content)
|
||||||
s.discordBot.RealtimeChannelSend(message)
|
s.discordBot.RealtimeChannelSend(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,6 +382,9 @@ func (s *Server) NextSemaphoreID() uint32 {
|
|||||||
for {
|
for {
|
||||||
exists := false
|
exists := false
|
||||||
s.semaphoreIndex = s.semaphoreIndex + 1
|
s.semaphoreIndex = s.semaphoreIndex + 1
|
||||||
|
if s.semaphoreIndex == 0 {
|
||||||
|
s.semaphoreIndex = 7 // Skip reserved indexes
|
||||||
|
}
|
||||||
for _, semaphore := range s.semaphore {
|
for _, semaphore := range s.semaphore {
|
||||||
if semaphore.id == s.semaphoreIndex {
|
if semaphore.id == s.semaphoreIndex {
|
||||||
exists = true
|
exists = true
|
||||||
|
|||||||
@@ -98,35 +98,6 @@ func (s *Stage) isQuest() bool {
|
|||||||
return len(s.reservedClientSlots) > 0
|
return len(s.reservedClientSlots) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stage) GetName() string {
|
|
||||||
switch s.id {
|
|
||||||
case MezeportaStageId:
|
|
||||||
return "Mezeporta"
|
|
||||||
case GuildHallLv1StageId:
|
|
||||||
return "Guild Hall Lv1"
|
|
||||||
case GuildHallLv2StageId:
|
|
||||||
return "Guild Hall Lv2"
|
|
||||||
case GuildHallLv3StageId:
|
|
||||||
return "Guild Hall Lv3"
|
|
||||||
case PugiFarmStageId:
|
|
||||||
return "Pugi Farm"
|
|
||||||
case RastaBarStageId:
|
|
||||||
return "Rasta Bar"
|
|
||||||
case PalloneCaravanStageId:
|
|
||||||
return "Pallone Caravan"
|
|
||||||
case GookFarmStageId:
|
|
||||||
return "Gook Farm"
|
|
||||||
case DivaFountainStageId:
|
|
||||||
return "Diva Fountain"
|
|
||||||
case DivaHallStageId:
|
|
||||||
return "Diva Hall"
|
|
||||||
case MezFesStageId:
|
|
||||||
return "Mez Fes"
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Stage) NextObjectID() uint32 {
|
func (s *Stage) NextObjectID() uint32 {
|
||||||
s.objectIndex = s.objectIndex + 1
|
s.objectIndex = s.objectIndex + 1
|
||||||
// Objects beyond 127 do not duplicate correctly
|
// Objects beyond 127 do not duplicate correctly
|
||||||
|
|||||||
@@ -31,6 +31,16 @@ func Time_Current_Midnight() time.Time {
|
|||||||
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location())
|
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TimeWeekStart() time.Time {
|
||||||
|
midnight := Time_Current_Midnight()
|
||||||
|
offset := (int(midnight.Weekday()) - 1) * -24
|
||||||
|
return midnight.Add(time.Hour * time.Duration(offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TimeWeekNext() time.Time {
|
||||||
|
return TimeWeekStart().Add(time.Hour * 24 * 7)
|
||||||
|
}
|
||||||
|
|
||||||
func Time_Current_Week_uint8() uint8 {
|
func Time_Current_Week_uint8() uint8 {
|
||||||
baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust)
|
baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust)
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package discordbot
|
package discordbot
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"erupe-ce/config"
|
"erupe-ce/config"
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DiscordBot struct {
|
type DiscordBot struct {
|
||||||
@@ -16,12 +15,12 @@ type DiscordBot struct {
|
|||||||
RealtimeChannel *discordgo.Channel
|
RealtimeChannel *discordgo.Channel
|
||||||
}
|
}
|
||||||
|
|
||||||
type DiscordBotOptions struct {
|
type Options struct {
|
||||||
Config *config.Config
|
Config *config.Config
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error) {
|
func NewDiscordBot(options Options) (discordBot *DiscordBot, err error) {
|
||||||
session, err := discordgo.New("Bot " + options.Config.Discord.BotToken)
|
session, err := discordgo.New("Bot " + options.Config.Discord.BotToken)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -29,13 +28,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mainGuild, err := session.Guild(options.Config.Discord.ServerID)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
options.Logger.Fatal("Discord failed to get main guild", zap.Error(err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
realtimeChannel, err := session.Channel(options.Config.Discord.RealtimeChannelID)
|
realtimeChannel, err := session.Channel(options.Config.Discord.RealtimeChannelID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -47,7 +39,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error
|
|||||||
config: options.Config,
|
config: options.Config,
|
||||||
logger: options.Logger,
|
logger: options.Logger,
|
||||||
Session: session,
|
Session: session,
|
||||||
MainGuild: mainGuild,
|
|
||||||
RealtimeChannel: realtimeChannel,
|
RealtimeChannel: realtimeChannel,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,21 +51,10 @@ func (bot *DiscordBot) Start() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *DiscordBot) FindRoleByID(id string) *discordgo.Role {
|
|
||||||
for _, role := range bot.MainGuild.Roles {
|
|
||||||
if role.ID == id {
|
|
||||||
return role
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace all mentions to real name from the message.
|
// Replace all mentions to real name from the message.
|
||||||
func (bot *DiscordBot) NormalizeDiscordMessage(message string) string {
|
func (bot *DiscordBot) NormalizeDiscordMessage(message string) string {
|
||||||
userRegex := regexp.MustCompile(`<@!?(\d{17,19})>`)
|
userRegex := regexp.MustCompile(`<@!?(\d{17,19})>`)
|
||||||
emojiRegex := regexp.MustCompile(`(?:<a?)?:(\w+):(?:\d{18}>)?`)
|
emojiRegex := regexp.MustCompile(`(?:<a?)?:(\w+):(?:\d{18}>)?`)
|
||||||
roleRegex := regexp.MustCompile(`<@&(\d{17,19})>`)
|
|
||||||
|
|
||||||
result := ReplaceTextAll(message, userRegex, func(userId string) string {
|
result := ReplaceTextAll(message, userRegex, func(userId string) string {
|
||||||
user, err := bot.Session.User(userId)
|
user, err := bot.Session.User(userId)
|
||||||
@@ -90,17 +70,7 @@ func (bot *DiscordBot) NormalizeDiscordMessage(message string) string {
|
|||||||
return ":" + emojiName + ":"
|
return ":" + emojiName + ":"
|
||||||
})
|
})
|
||||||
|
|
||||||
result = ReplaceTextAll(result, roleRegex, func(roleId string) string {
|
return result
|
||||||
role := bot.FindRoleByID(roleId)
|
|
||||||
|
|
||||||
if role != nil {
|
|
||||||
return "@!" + role.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
return "@!unknown"
|
|
||||||
})
|
|
||||||
|
|
||||||
return string(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) {
|
func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) {
|
||||||
|
|||||||
@@ -118,11 +118,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="launcher_menu" class="unselectable">
|
<div id="launcher_menu" class="unselectable">
|
||||||
<div class="manual btn" onmouseenter="soundSel()" onclick="soundOk(); toggleModal('openLink', 'https://mhfz.capcom.com.tw/manuals/')"></div>
|
<div class="manual btn" onmouseenter="soundSel()" onclick="soundOk(); toggleModal('openLink', 'https://zerulight.github.io/MHFZManual/')"></div>
|
||||||
<div class="pastebin btn" onmouseenter="soundSel()" onclick="soundOk(); toggleModal('openLink', 'https://pastebin.com/QqAwZSTC')"></div>
|
<div class="pastebin btn" onmouseenter="soundSel()" onclick="soundOk(); toggleModal('openLink', 'https://pastebin.com/QqAwZSTC')"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<div class="link unselectable" onclick="toggleModal('openLink', 'https://github.com/xl3lackout/Erupe')"><img src="img/icons/github.png"></div>
|
<div class="link unselectable" onclick="toggleModal('openLink', 'https://github.com/ZeruLight/Erupe')"><img src="img/icons/github.png"></div>
|
||||||
<div class="link unselectable" onclick="toggleModal('openLink', 'https://discord.gg/CFnzbhQ')"><img src="img/icons/discord.png"></div>
|
<div class="link unselectable" onclick="toggleModal('openLink', 'https://discord.gg/CFnzbhQ')"><img src="img/icons/discord.png"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grabbable" id="launcher_modal">
|
<div class="grabbable" id="launcher_modal">
|
||||||
|
|||||||
Reference in New Issue
Block a user