mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
138 bare db.Exec calls across 22 handler files silently dropped write errors. Each is now wrapped with error check and zap logging. 4 QueryRow sites that legitimately return sql.ErrNoRows during normal operation (new player mezfes, festa rankings, empty guild item box) now filter it out to reduce log noise.
573 lines
20 KiB
Go
573 lines
20 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"erupe-ce/common/byteframe"
|
|
"erupe-ce/common/mhfcid"
|
|
"erupe-ce/common/mhfcourse"
|
|
"erupe-ce/common/token"
|
|
"erupe-ce/config"
|
|
"erupe-ce/network"
|
|
"erupe-ce/network/binpacket"
|
|
"erupe-ce/network/mhfpacket"
|
|
"fmt"
|
|
"math"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// MSG_SYS_CAST[ED]_BINARY types enum
|
|
const (
|
|
BinaryMessageTypeState = 0
|
|
BinaryMessageTypeChat = 1
|
|
BinaryMessageTypeQuest = 2
|
|
BinaryMessageTypeData = 3
|
|
BinaryMessageTypeMailNotify = 4
|
|
BinaryMessageTypeEmote = 6
|
|
)
|
|
|
|
// MSG_SYS_CAST[ED]_BINARY broadcast types enum
|
|
const (
|
|
BroadcastTypeTargeted = 0x01
|
|
BroadcastTypeStage = 0x03
|
|
BroadcastTypeServer = 0x06
|
|
BroadcastTypeWorld = 0x0a
|
|
)
|
|
|
|
var commands map[string]_config.Command
|
|
|
|
func init() {
|
|
commands = make(map[string]_config.Command)
|
|
zapConfig := zap.NewDevelopmentConfig()
|
|
zapConfig.DisableCaller = true
|
|
zapLogger, _ := zapConfig.Build()
|
|
defer func() { _ = zapLogger.Sync() }()
|
|
logger := zapLogger.Named("commands")
|
|
cmds := _config.ErupeConfig.Commands
|
|
for _, cmd := range cmds {
|
|
commands[cmd.Name] = cmd
|
|
if cmd.Enabled {
|
|
logger.Info(fmt.Sprintf("Command %s: Enabled, prefix: %s", cmd.Name, cmd.Prefix))
|
|
} else {
|
|
logger.Info(fmt.Sprintf("Command %s: Disabled", cmd.Name))
|
|
}
|
|
}
|
|
}
|
|
|
|
func sendDisabledCommandMessage(s *Session, cmd _config.Command) {
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.disabled, cmd.Name))
|
|
}
|
|
|
|
func sendServerChatMessage(s *Session, message string) {
|
|
// Make the inside of the casted binary
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
msgBinChat := &binpacket.MsgBinChat{
|
|
Unk0: 0,
|
|
Type: 5,
|
|
Flags: 0x80,
|
|
Message: message,
|
|
SenderName: "Erupe",
|
|
}
|
|
_ = msgBinChat.Build(bf)
|
|
|
|
castedBin := &mhfpacket.MsgSysCastedBinary{
|
|
CharID: 0,
|
|
MessageType: BinaryMessageTypeChat,
|
|
RawDataPayload: bf.Data(),
|
|
}
|
|
|
|
s.QueueSendMHFNonBlocking(castedBin)
|
|
}
|
|
|
|
func parseChatCommand(s *Session, command string) {
|
|
args := strings.Split(command[len(s.server.erupeConfig.CommandPrefix):], " ")
|
|
switch args[0] {
|
|
case commands["Ban"].Prefix:
|
|
if s.isOp() {
|
|
if len(args) > 1 {
|
|
var expiry time.Time
|
|
if len(args) > 2 {
|
|
var length int
|
|
var unit string
|
|
n, err := fmt.Sscanf(args[2], `%d%s`, &length, &unit)
|
|
if err == nil && n == 2 {
|
|
switch unit {
|
|
case "s", "second", "seconds":
|
|
expiry = time.Now().Add(time.Duration(length) * time.Second)
|
|
case "m", "mi", "minute", "minutes":
|
|
expiry = time.Now().Add(time.Duration(length) * time.Minute)
|
|
case "h", "hour", "hours":
|
|
expiry = time.Now().Add(time.Duration(length) * time.Hour)
|
|
case "d", "day", "days":
|
|
expiry = time.Now().Add(time.Duration(length) * time.Hour * 24)
|
|
case "mo", "month", "months":
|
|
expiry = time.Now().Add(time.Duration(length) * time.Hour * 24 * 30)
|
|
case "y", "year", "years":
|
|
expiry = time.Now().Add(time.Duration(length) * time.Hour * 24 * 365)
|
|
}
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.ban.error)
|
|
return
|
|
}
|
|
}
|
|
cid := mhfcid.ConvertCID(args[1])
|
|
if cid > 0 {
|
|
var uid uint32
|
|
var uname string
|
|
err := s.server.db.QueryRow(`SELECT id, username FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, cid).Scan(&uid, &uname)
|
|
if err == nil {
|
|
if expiry.IsZero() {
|
|
if _, err := s.server.db.Exec(`INSERT INTO bans VALUES ($1)
|
|
ON CONFLICT (user_id) DO UPDATE SET expires=NULL`, uid); err != nil {
|
|
s.logger.Error("Failed to ban user", zap.Error(err))
|
|
}
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.ban.success, uname))
|
|
} else {
|
|
if _, err := s.server.db.Exec(`INSERT INTO bans VALUES ($1, $2)
|
|
ON CONFLICT (user_id) DO UPDATE SET expires=$2`, uid, expiry); err != nil {
|
|
s.logger.Error("Failed to ban user with expiry", zap.Error(err))
|
|
}
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.ban.success, uname)+fmt.Sprintf(s.server.i18n.commands.ban.length, expiry.Format(time.DateTime)))
|
|
}
|
|
s.server.DisconnectUser(uid)
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.ban.noUser)
|
|
}
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.ban.invalid)
|
|
}
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.ban.error)
|
|
}
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.noOp)
|
|
}
|
|
case commands["Timer"].Prefix:
|
|
if commands["Timer"].Enabled || s.isOp() {
|
|
var state bool
|
|
if err := s.server.db.QueryRow(`SELECT COALESCE(timer, false) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&state); err != nil {
|
|
s.logger.Error("Failed to get timer state", zap.Error(err))
|
|
}
|
|
if _, err := s.server.db.Exec(`UPDATE users u SET timer=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, !state, s.charID); err != nil {
|
|
s.logger.Error("Failed to update timer setting", zap.Error(err))
|
|
}
|
|
if state {
|
|
sendServerChatMessage(s, s.server.i18n.commands.timer.disabled)
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.timer.enabled)
|
|
}
|
|
} else {
|
|
sendDisabledCommandMessage(s, commands["Timer"])
|
|
}
|
|
case commands["PSN"].Prefix:
|
|
if commands["PSN"].Enabled || s.isOp() {
|
|
if len(args) > 1 {
|
|
var exists int
|
|
if err := s.server.db.QueryRow(`SELECT count(*) FROM users WHERE psn_id = $1`, args[1]).Scan(&exists); err != nil {
|
|
s.logger.Error("Failed to check PSN ID existence", zap.Error(err))
|
|
}
|
|
if exists == 0 {
|
|
_, err := s.server.db.Exec(`UPDATE users u SET psn_id=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, args[1], s.charID)
|
|
if err == nil {
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.psn.success, args[1]))
|
|
}
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.psn.exists)
|
|
}
|
|
} else {
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.psn.error, commands["PSN"].Prefix))
|
|
}
|
|
} else {
|
|
sendDisabledCommandMessage(s, commands["PSN"])
|
|
}
|
|
case commands["Reload"].Prefix:
|
|
if commands["Reload"].Enabled || s.isOp() {
|
|
sendServerChatMessage(s, s.server.i18n.commands.reload)
|
|
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(uint16(network.MSG_SYS_END))
|
|
s.QueueSendNonBlocking(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(uint16(network.MSG_SYS_END))
|
|
s.QueueSendNonBlocking(reloadNotif.Data())
|
|
} else {
|
|
sendDisabledCommandMessage(s, commands["Reload"])
|
|
}
|
|
case commands["KeyQuest"].Prefix:
|
|
if commands["KeyQuest"].Enabled || s.isOp() {
|
|
if s.server.erupeConfig.RealClientMode < _config.G10 {
|
|
sendServerChatMessage(s, s.server.i18n.commands.kqf.version)
|
|
} else {
|
|
if len(args) > 1 {
|
|
switch args[1] {
|
|
case "get":
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.kqf.get, s.kqf))
|
|
case "set":
|
|
if len(args) > 2 && len(args[2]) == 16 {
|
|
hexd, _ := hex.DecodeString(args[2])
|
|
s.kqf = hexd
|
|
s.kqfOverride = true
|
|
sendServerChatMessage(s, s.server.i18n.commands.kqf.set.success)
|
|
} else {
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.kqf.set.error, commands["KeyQuest"].Prefix))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
sendDisabledCommandMessage(s, commands["KeyQuest"])
|
|
}
|
|
case commands["Rights"].Prefix:
|
|
if commands["Rights"].Enabled || s.isOp() {
|
|
if len(args) > 1 {
|
|
v, _ := strconv.Atoi(args[1])
|
|
_, err := s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", v, s.charID)
|
|
if err == nil {
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.rights.success, v))
|
|
} else {
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.rights.error, commands["Rights"].Prefix))
|
|
}
|
|
} else {
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.rights.error, commands["Rights"].Prefix))
|
|
}
|
|
} else {
|
|
sendDisabledCommandMessage(s, commands["Rights"])
|
|
}
|
|
case commands["Course"].Prefix:
|
|
if commands["Course"].Enabled || s.isOp() {
|
|
if len(args) > 1 {
|
|
for _, course := range mhfcourse.Courses() {
|
|
for _, alias := range course.Aliases() {
|
|
if strings.EqualFold(args[1], alias) {
|
|
if slices.Contains(s.server.erupeConfig.Courses, _config.Course{Name: course.Aliases()[0], Enabled: true}) {
|
|
var delta, rightsInt uint32
|
|
if mhfcourse.CourseExists(course.ID, s.courses) {
|
|
ei := slices.IndexFunc(s.courses, func(c mhfcourse.Course) bool {
|
|
for _, alias := range c.Aliases() {
|
|
if strings.EqualFold(args[1], alias) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
if ei != -1 {
|
|
delta = uint32(-1 * math.Pow(2, float64(course.ID)))
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.disabled, course.Aliases()[0]))
|
|
}
|
|
} else {
|
|
delta = uint32(math.Pow(2, float64(course.ID)))
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.enabled, course.Aliases()[0]))
|
|
}
|
|
err := s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.charID).Scan(&rightsInt)
|
|
if err == nil {
|
|
if _, err := s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", rightsInt+delta, s.charID); err != nil {
|
|
s.logger.Error("Failed to update user rights", zap.Error(err))
|
|
}
|
|
}
|
|
updateRights(s)
|
|
} else {
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.locked, course.Aliases()[0]))
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.error, commands["Course"].Prefix))
|
|
}
|
|
} else {
|
|
sendDisabledCommandMessage(s, commands["Course"])
|
|
}
|
|
case commands["Raviente"].Prefix:
|
|
if commands["Raviente"].Enabled || s.isOp() {
|
|
if len(args) > 1 {
|
|
if s.server.getRaviSemaphore() != nil {
|
|
switch args[1] {
|
|
case "start":
|
|
if s.server.raviente.register[1] == 0 {
|
|
s.server.raviente.register[1] = s.server.raviente.register[3]
|
|
sendServerChatMessage(s, s.server.i18n.commands.ravi.start.success)
|
|
s.notifyRavi()
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.ravi.start.error)
|
|
}
|
|
case "cm", "check", "checkmultiplier", "multiplier":
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.ravi.multiplier, s.server.GetRaviMultiplier()))
|
|
case "sr", "sendres", "resurrection", "ss", "sendsed", "rs", "reqsed":
|
|
if s.server.erupeConfig.RealClientMode == _config.ZZ {
|
|
switch args[1] {
|
|
case "sr", "sendres", "resurrection":
|
|
if s.server.raviente.state[28] > 0 {
|
|
sendServerChatMessage(s, s.server.i18n.commands.ravi.res.success)
|
|
s.server.raviente.state[28] = 0
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.ravi.res.error)
|
|
}
|
|
case "ss", "sendsed":
|
|
sendServerChatMessage(s, s.server.i18n.commands.ravi.sed.success)
|
|
// Total BerRavi HP
|
|
HP := s.server.raviente.state[0] + s.server.raviente.state[1] + s.server.raviente.state[2] + s.server.raviente.state[3] + s.server.raviente.state[4]
|
|
s.server.raviente.support[1] = HP
|
|
case "rs", "reqsed":
|
|
sendServerChatMessage(s, s.server.i18n.commands.ravi.request)
|
|
// Total BerRavi HP
|
|
HP := s.server.raviente.state[0] + s.server.raviente.state[1] + s.server.raviente.state[2] + s.server.raviente.state[3] + s.server.raviente.state[4]
|
|
s.server.raviente.support[1] = HP + 1
|
|
}
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.ravi.version)
|
|
}
|
|
default:
|
|
sendServerChatMessage(s, s.server.i18n.commands.ravi.error)
|
|
}
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.ravi.noPlayers)
|
|
}
|
|
} else {
|
|
sendServerChatMessage(s, s.server.i18n.commands.ravi.error)
|
|
}
|
|
} else {
|
|
sendDisabledCommandMessage(s, commands["Raviente"])
|
|
}
|
|
case commands["Teleport"].Prefix:
|
|
if commands["Teleport"].Enabled || s.isOp() {
|
|
if len(args) > 2 {
|
|
x, _ := strconv.ParseInt(args[1], 10, 16)
|
|
y, _ := strconv.ParseInt(args[2], 10, 16)
|
|
payload := byteframe.NewByteFrame()
|
|
payload.SetLE()
|
|
payload.WriteUint8(2) // SetState type(position == 2)
|
|
payload.WriteInt16(int16(x)) // X
|
|
payload.WriteInt16(int16(y)) // Y
|
|
payloadBytes := payload.Data()
|
|
s.QueueSendMHFNonBlocking(&mhfpacket.MsgSysCastedBinary{
|
|
CharID: s.charID,
|
|
MessageType: BinaryMessageTypeState,
|
|
RawDataPayload: payloadBytes,
|
|
})
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.teleport.success, x, y))
|
|
} else {
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.teleport.error, commands["Teleport"].Prefix))
|
|
}
|
|
} else {
|
|
sendDisabledCommandMessage(s, commands["Teleport"])
|
|
}
|
|
case commands["Discord"].Prefix:
|
|
if commands["Discord"].Enabled || s.isOp() {
|
|
var _token string
|
|
err := s.server.db.QueryRow(`SELECT discord_token FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&_token)
|
|
if err != nil {
|
|
randToken := make([]byte, 4)
|
|
_, _ = rand.Read(randToken)
|
|
_token = fmt.Sprintf("%x-%x", randToken[:2], randToken[2:])
|
|
if _, err := s.server.db.Exec(`UPDATE users u SET discord_token = $1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, _token, s.charID); err != nil {
|
|
s.logger.Error("Failed to update discord token", zap.Error(err))
|
|
}
|
|
}
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.discord.success, _token))
|
|
} else {
|
|
sendDisabledCommandMessage(s, commands["Discord"])
|
|
}
|
|
case commands["Playtime"].Prefix:
|
|
if commands["Playtime"].Enabled || s.isOp() {
|
|
playtime := s.playtime + uint32(time.Since(s.playtimeTime).Seconds())
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.playtime, playtime/60/60, playtime/60%60, playtime%60))
|
|
} else {
|
|
sendDisabledCommandMessage(s, commands["Playtime"])
|
|
}
|
|
case commands["Help"].Prefix:
|
|
if commands["Help"].Enabled || s.isOp() {
|
|
for _, command := range commands {
|
|
if command.Enabled || s.isOp() {
|
|
sendServerChatMessage(s, fmt.Sprintf("%s%s: %s", s.server.erupeConfig.CommandPrefix, command.Prefix, command.Description))
|
|
}
|
|
}
|
|
} else {
|
|
sendDisabledCommandMessage(s, commands["Help"])
|
|
}
|
|
}
|
|
}
|
|
|
|
func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
|
pkt := p.(*mhfpacket.MsgSysCastBinary)
|
|
tmp := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
|
|
|
|
if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x03 && len(pkt.RawDataPayload) == 0x10 {
|
|
if tmp.ReadUint16() == 0x0002 && tmp.ReadUint8() == 0x18 {
|
|
var timer bool
|
|
if err := s.server.db.QueryRow(`SELECT COALESCE(timer, false) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&timer); err != nil {
|
|
s.logger.Error("Failed to get timer setting", zap.Error(err))
|
|
}
|
|
if timer {
|
|
_ = tmp.ReadBytes(9)
|
|
tmp.SetLE()
|
|
frame := tmp.ReadUint32()
|
|
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.timer, frame/30/60/60, frame/30/60, frame/30%60, int(math.Round(float64(frame%30*100)/3)), frame))
|
|
}
|
|
}
|
|
}
|
|
|
|
if s.server.erupeConfig.DebugOptions.QuestTools {
|
|
if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x02 && len(pkt.RawDataPayload) > 32 {
|
|
// This is only correct most of the time
|
|
tmp.ReadBytes(20)
|
|
tmp.SetLE()
|
|
x := tmp.ReadFloat32()
|
|
y := tmp.ReadFloat32()
|
|
z := tmp.ReadFloat32()
|
|
s.logger.Debug("Coord", zap.Float32s("XYZ", []float32{x, y, z}))
|
|
}
|
|
}
|
|
|
|
// Parse out the real casted binary payload
|
|
var msgBinTargeted *binpacket.MsgBinTargeted
|
|
var message, author string
|
|
var returnToSender bool
|
|
if pkt.MessageType == BinaryMessageTypeChat {
|
|
tmp.SetLE()
|
|
_, _ = tmp.Seek(8, 0)
|
|
message = string(tmp.ReadNullTerminatedBytes())
|
|
author = string(tmp.ReadNullTerminatedBytes())
|
|
}
|
|
|
|
// Customise payload
|
|
realPayload := pkt.RawDataPayload
|
|
if pkt.BroadcastType == BroadcastTypeTargeted {
|
|
tmp.SetBE()
|
|
_, _ = tmp.Seek(0, 0)
|
|
msgBinTargeted = &binpacket.MsgBinTargeted{}
|
|
err := msgBinTargeted.Parse(tmp)
|
|
if err != nil {
|
|
s.logger.Warn("Failed to parse targeted cast binary")
|
|
return
|
|
}
|
|
realPayload = msgBinTargeted.RawDataPayload
|
|
} else if pkt.MessageType == BinaryMessageTypeChat {
|
|
if message == "@dice" {
|
|
returnToSender = true
|
|
m := binpacket.MsgBinChat{
|
|
Type: BinaryMessageTypeChat,
|
|
Flags: 4,
|
|
Message: fmt.Sprintf(`%d`, token.RNG.Intn(100)+1),
|
|
SenderName: author,
|
|
}
|
|
bf := byteframe.NewByteFrame()
|
|
bf.SetLE()
|
|
_ = m.Build(bf)
|
|
realPayload = bf.Data()
|
|
} else {
|
|
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
|
|
bf.SetLE()
|
|
chatMessage := &binpacket.MsgBinChat{}
|
|
_ = chatMessage.Parse(bf)
|
|
if strings.HasPrefix(chatMessage.Message, s.server.erupeConfig.CommandPrefix) {
|
|
parseChatCommand(s, chatMessage.Message)
|
|
return
|
|
}
|
|
if (pkt.BroadcastType == BroadcastTypeStage && s.stage.id == "sl1Ns200p0a0u0") || pkt.BroadcastType == BroadcastTypeWorld {
|
|
s.server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make the response to forward to the other client(s).
|
|
resp := &mhfpacket.MsgSysCastedBinary{
|
|
CharID: s.charID,
|
|
BroadcastType: pkt.BroadcastType, // (The client never uses Type0 upon receiving)
|
|
MessageType: pkt.MessageType,
|
|
RawDataPayload: realPayload,
|
|
}
|
|
|
|
// Send to the proper recipients.
|
|
switch pkt.BroadcastType {
|
|
case BroadcastTypeWorld:
|
|
s.server.WorldcastMHF(resp, s, nil)
|
|
case BroadcastTypeStage:
|
|
if returnToSender {
|
|
s.stage.BroadcastMHF(resp, nil)
|
|
} else {
|
|
s.stage.BroadcastMHF(resp, s)
|
|
}
|
|
case BroadcastTypeServer:
|
|
if pkt.MessageType == 1 {
|
|
raviSema := s.server.getRaviSemaphore()
|
|
if raviSema != nil {
|
|
raviSema.BroadcastMHF(resp, s)
|
|
}
|
|
} else {
|
|
s.server.BroadcastMHF(resp, s)
|
|
}
|
|
case BroadcastTypeTargeted:
|
|
for _, targetID := range (*msgBinTargeted).TargetCharIDs {
|
|
char := s.server.FindSessionByCharID(targetID)
|
|
|
|
if char != nil {
|
|
char.QueueSendMHFNonBlocking(resp)
|
|
}
|
|
}
|
|
default:
|
|
s.Lock()
|
|
haveStage := s.stage != nil
|
|
if haveStage {
|
|
s.stage.BroadcastMHF(resp, s)
|
|
}
|
|
s.Unlock()
|
|
}
|
|
}
|
|
|
|
func handleMsgSysCastedBinary(s *Session, p mhfpacket.MHFPacket) {}
|