Merge pull request #15 from sekaiwish/main

Raviente Patch
This commit is contained in:
xl3lackout
2022-05-03 18:32:15 -04:00
committed by GitHub
42 changed files with 3264 additions and 591 deletions

View File

@@ -9,11 +9,22 @@ import (
) )
// MsgMhfAcquireUdItem represents the MSG_MHF_ACQUIRE_UD_ITEM // MsgMhfAcquireUdItem represents the MSG_MHF_ACQUIRE_UD_ITEM
type MsgMhfAcquireUdItem struct{ type MsgMhfAcquireUdItem struct {
AckHandle uint32 AckHandle uint32
// Valid field size(s), not sure about the types.
Unk0 uint8 Unk0 uint8
Unk1 uint32 // from gal
// daily = 0
// personal = 1
// personal rank = 2
// guild rank = 3
// gcp = 4
// from cat
// treasure achievement = 5
// personal achievement = 6
// guild achievement = 7
RewardType uint8
Unk2 uint8 // Number of uint32s to read?
Unk3 []byte
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -25,9 +36,12 @@ func (m *MsgMhfAcquireUdItem) Opcode() network.PacketID {
func (m *MsgMhfAcquireUdItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfAcquireUdItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint32() m.RewardType = bf.ReadUint8()
m.Unk2 = bf.ReadUint8()
for i := uint8(0); i < m.Unk2; i++ {
bf.ReadUint32()
}
return nil return nil
//return errors.New("NOT IMPLEMENTED")
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -9,7 +9,10 @@ import (
) )
// MsgMhfAddGuildWeeklyBonusExceptionalUser represents the MSG_MHF_ADD_GUILD_WEEKLY_BONUS_EXCEPTIONAL_USER // MsgMhfAddGuildWeeklyBonusExceptionalUser represents the MSG_MHF_ADD_GUILD_WEEKLY_BONUS_EXCEPTIONAL_USER
type MsgMhfAddGuildWeeklyBonusExceptionalUser struct{} type MsgMhfAddGuildWeeklyBonusExceptionalUser struct {
AckHandle uint32
NumUsers uint8
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Opcode() network.PacketID { func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Opcode() network.PacketID {
@@ -18,7 +21,9 @@ func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.NumUsers = bf.ReadUint8()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -11,22 +11,25 @@ import (
type EnumerateGuildType uint8 type EnumerateGuildType uint8
const ( const (
_ = iota ENUMERATE_GUILD_TYPE_GUILD_NAME = 0x01
ENUMERATE_GUILD_TYPE_NAME ENUMERATE_GUILD_TYPE_LEADER_NAME = 0x02
//Numbers correspond to order in guild search menu ENUMERATE_GUILD_TYPE_LEADER_ID = 0x03
ENUMERATE_GUILD_TYPE_6 ENUMERATE_GUILD_TYPE_ORDER_MEMBERS = 0x04
ENUMERATE_GUILD_TYPE_LEADER_ID ENUMERATE_GUILD_TYPE_ORDER_REGISTRATION = 0x05
ENUMERATE_GUILD_TYPE_3 ENUMERATE_GUILD_TYPE_ORDER_RANK = 0x06
ENUMERATE_GUILD_TYPE_2 ENUMERATE_GUILD_TYPE_MOTTO = 0x07
ENUMERATE_GUILD_TYPE_7 ENUMERATE_GUILD_TYPE_RECRUITING = 0x08
ENUMERATE_GUILD_TYPE_8 ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME = 0x09
ENUMERATE_GUILD_TYPE_NEW ENUMERATE_ALLIANCE_TYPE_LEADER_NAME = 0x0A
ENUMERATE_ALLIANCE_TYPE_LEADER_ID = 0x0B
ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS = 0x0C
ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION = 0x0D
) )
// MsgMhfEnumerateGuild represents the MSG_MHF_ENUMERATE_GUILD // MsgMhfEnumerateGuild represents the MSG_MHF_ENUMERATE_GUILD
type MsgMhfEnumerateGuild struct { type MsgMhfEnumerateGuild struct {
AckHandle uint32 AckHandle uint32
Type uint8 Type EnumerateGuildType
RawDataPayload []byte RawDataPayload []byte
} }
@@ -38,9 +41,9 @@ func (m *MsgMhfEnumerateGuild) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfEnumerateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfEnumerateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Type = bf.ReadUint8() m.Type = EnumerateGuildType(bf.ReadUint8())
m.RawDataPayload = bf.DataFromCurrent() m.RawDataPayload = bf.DataFromCurrent()
bf.Seek(int64(len(bf.Data()) - 2), 0)
return nil return nil
} }

View File

@@ -11,6 +11,8 @@ import (
// MsgMhfEnumerateGuildItem represents the MSG_MHF_ENUMERATE_GUILD_ITEM // MsgMhfEnumerateGuildItem represents the MSG_MHF_ENUMERATE_GUILD_ITEM
type MsgMhfEnumerateGuildItem struct { type MsgMhfEnumerateGuildItem struct {
AckHandle uint32 AckHandle uint32
GuildId uint32
Unk0 uint16
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -21,7 +23,8 @@ func (m *MsgMhfEnumerateGuildItem) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfEnumerateGuildItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfEnumerateGuildItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.GuildId = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
return nil return nil
} }

View File

@@ -0,0 +1,37 @@
package mhfpacket
import (
"errors"
"github.com/Solenataris/Erupe/network/clientctx"
"github.com/Solenataris/Erupe/network"
"github.com/Andoryuuta/byteframe"
)
// MsgMhfEnumerateGuildMessageBoard represents the MSG_MHF_ENUMERATE_GUILD_MESSAGE_BOARD
type MsgMhfEnumerateGuildMessageBoard struct{
AckHandle uint32
Unk0 uint32
MaxPosts uint32 // always 100, even on news (00000064)
// returning more than 4 news posts WILL softlock
BoardType uint32 // 0 => message, 1 => news
}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfEnumerateGuildMessageBoard) Opcode() network.PacketID {
return network.MSG_MHF_ENUMERATE_GUILD_MESSAGE_BOARD
}
// Parse parses the packet from binary
func (m *MsgMhfEnumerateGuildMessageBoard) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.MaxPosts = bf.ReadUint32()
m.BoardType = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfEnumerateGuildMessageBoard) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -11,9 +11,8 @@ import (
// MsgMhfGetAchievement represents the MSG_MHF_GET_ACHIEVEMENT // MsgMhfGetAchievement represents the MSG_MHF_GET_ACHIEVEMENT
type MsgMhfGetAchievement struct{ type MsgMhfGetAchievement struct{
AckHandle uint32 AckHandle uint32
Unk0 uint16 // id? Unk0 uint32 // id?
Unk1 uint32 // char? Unk1 uint32 // char?
Unk2 uint32 // pad?
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -24,9 +23,8 @@ func (m *MsgMhfGetAchievement) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfGetAchievement) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetAchievement) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16() m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint32() m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint32()
return nil return nil
} }

View File

@@ -11,6 +11,7 @@ import (
// MsgMhfGetUdRanking represents the MSG_MHF_GET_UD_RANKING // MsgMhfGetUdRanking represents the MSG_MHF_GET_UD_RANKING
type MsgMhfGetUdRanking struct{ type MsgMhfGetUdRanking struct{
AckHandle uint32 AckHandle uint32
Unk0 uint8
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -21,6 +22,7 @@ func (m *MsgMhfGetUdRanking) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfGetUdRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetUdRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8()
return nil return nil
} }

View File

@@ -9,7 +9,10 @@ import (
) )
// MsgMhfGetUdTacticsRanking represents the MSG_MHF_GET_UD_TACTICS_RANKING // MsgMhfGetUdTacticsRanking represents the MSG_MHF_GET_UD_TACTICS_RANKING
type MsgMhfGetUdTacticsRanking struct{} type MsgMhfGetUdTacticsRanking struct {
AckHandle uint32
GuildID uint32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfGetUdTacticsRanking) Opcode() network.PacketID { func (m *MsgMhfGetUdTacticsRanking) Opcode() network.PacketID {
@@ -18,7 +21,9 @@ func (m *MsgMhfGetUdTacticsRanking) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfGetUdTacticsRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetUdTacticsRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.GuildID = bf.ReadUint32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -11,6 +11,7 @@ import (
// MsgMhfListMail represents the MSG_MHF_LIST_MAIL // MsgMhfListMail represents the MSG_MHF_LIST_MAIL
type MsgMhfListMail struct { type MsgMhfListMail struct {
AckHandle uint32 AckHandle uint32
Unk0 uint32
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -21,6 +22,7 @@ func (m *MsgMhfListMail) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfListMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfListMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
return nil return nil
} }

View File

@@ -11,14 +11,29 @@ import (
type OperateGuildAction uint8 type OperateGuildAction uint8
const ( const (
OPERATE_GUILD_ACTION_DISBAND = 0x01 OPERATE_GUILD_DISBAND = 0x01
OPERATE_GUILD_ACTION_APPLY = 0x02 OPERATE_GUILD_APPLY = 0x02
OPERATE_GUILD_ACTION_LEAVE = 0x03 OPERATE_GUILD_LEAVE = 0x03
OPERATE_GUILD_RESIGN = 0x04
OPERATE_GUILD_SET_APPLICATION_DENY = 0x05
OPERATE_GUILD_SET_APPLICATION_ALLOW = 0x06
OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE = 0x07 OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE = 0x07
OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE = 0x08 OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE = 0x08
OPERATE_GUILD_ACTION_UPDATE_COMMENT = 0x09 OPERATE_GUILD_UPDATE_COMMENT = 0x09
OPERATE_GUILD_ACTION_DONATE = 0x0a OPERATE_GUILD_DONATE_RANK = 0x0a
OPERATE_GUILD_ACTION_UPDATE_MOTTO = 0x0b OPERATE_GUILD_UPDATE_MOTTO = 0x0b
OPERATE_GUILD_RENAME_PUGI_1 = 0x0c
OPERATE_GUILD_RENAME_PUGI_2 = 0x0d
OPERATE_GUILD_RENAME_PUGI_3 = 0x0e
OPERATE_GUILD_CHANGE_PUGI_1 = 0x0f
OPERATE_GUILD_CHANGE_PUGI_2 = 0x10
OPERATE_GUILD_CHANGE_PUGI_3 = 0x11
// pugi something
OPERATE_GUILD_DONATE_EVENT = 0x15
// pugi something
OPERATE_GUILD_CHANGE_DIVA_PUGI_1 = 0x19
OPERATE_GUILD_CHANGE_DIVA_PUGI_2 = 0x1a
OPERATE_GUILD_CHANGE_DIVA_PUGI_3 = 0x1b
) )
// MsgMhfOperateGuild represents the MSG_MHF_OPERATE_GUILD // MsgMhfOperateGuild represents the MSG_MHF_OPERATE_GUILD
@@ -40,7 +55,7 @@ func (m *MsgMhfOperateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien
m.GuildID = bf.ReadUint32() m.GuildID = bf.ReadUint32()
m.Action = OperateGuildAction(bf.ReadUint8()) m.Action = OperateGuildAction(bf.ReadUint8())
m.UnkData = bf.DataFromCurrent() m.UnkData = bf.DataFromCurrent()
bf.Seek(int64(len(bf.Data()) - 2), 0)
return nil return nil
} }

View File

@@ -8,8 +8,22 @@ import (
"github.com/Andoryuuta/byteframe" "github.com/Andoryuuta/byteframe"
) )
type OperateJointAction uint8
const (
OPERATE_JOINT_DISBAND = 0x01
OPERATE_JOINT_LEAVE = 0x03
OPERATE_JOINT_KICK = 0x09
)
// MsgMhfOperateJoint represents the MSG_MHF_OPERATE_JOINT // MsgMhfOperateJoint represents the MSG_MHF_OPERATE_JOINT
type MsgMhfOperateJoint struct{} type MsgMhfOperateJoint struct {
AckHandle uint32
AllianceID uint32
GuildID uint32
Action OperateJointAction
UnkData []byte
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfOperateJoint) Opcode() network.PacketID { func (m *MsgMhfOperateJoint) Opcode() network.PacketID {
@@ -18,7 +32,13 @@ func (m *MsgMhfOperateJoint) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfOperateJoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfOperateJoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.AllianceID = bf.ReadUint32()
m.GuildID = bf.ReadUint32()
m.Action = OperateJointAction(bf.ReadUint8())
m.UnkData = bf.DataFromCurrent()
bf.Seek(int64(len(bf.Data()) - 2), 0)
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -11,7 +11,8 @@ import (
type OperateMailOperation uint8 type OperateMailOperation uint8
const ( const (
OperateMailOperationDelete OperateMailOperation = 0x01 OPERATE_MAIL_DELETE = 0x01
OPERATE_MAIL_ACQUIRE_ITEM = 0x05
) )
// MsgMhfOprtMail represents the MSG_MHF_OPRT_MAIL // MsgMhfOprtMail represents the MSG_MHF_OPRT_MAIL
@@ -19,7 +20,11 @@ type MsgMhfOprtMail struct {
AckHandle uint32 AckHandle uint32
AccIndex uint8 AccIndex uint8
Index uint8 Index uint8
Operation uint8 Operation OperateMailOperation
Unk0 uint8
Data []byte
Amount uint16
ItemID uint16
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -32,7 +37,13 @@ func (m *MsgMhfOprtMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientCon
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.AccIndex = bf.ReadUint8() m.AccIndex = bf.ReadUint8()
m.Index = bf.ReadUint8() m.Index = bf.ReadUint8()
m.Operation = bf.ReadUint8() m.Operation = OperateMailOperation(bf.ReadUint8())
m.Unk0 = bf.ReadUint8()
switch m.Operation {
case OPERATE_MAIL_ACQUIRE_ITEM:
m.Amount = bf.ReadUint16()
m.ItemID = bf.ReadUint16()
}
return nil return nil
} }

View File

@@ -19,6 +19,7 @@ type MsgMhfReadMail struct {
// This is the index within the current mail list // This is the index within the current mail list
Index uint8 Index uint8
Unk0 uint16
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -31,6 +32,7 @@ func (m *MsgMhfReadMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientCon
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.AccIndex = bf.ReadUint8() m.AccIndex = bf.ReadUint8()
m.Index = bf.ReadUint8() m.Index = bf.ReadUint8()
m.Unk0 = bf.ReadUint16()
return nil return nil
} }

View File

@@ -9,7 +9,16 @@ import (
) )
// MsgMhfSendMail represents the MSG_MHF_SEND_MAIL // MsgMhfSendMail represents the MSG_MHF_SEND_MAIL
type MsgMhfSendMail struct{} type MsgMhfSendMail struct {
AckHandle uint32
RecipientID uint32
SubjectLength uint16
BodyLength uint16
Quantity uint32
ItemID uint16
Subject []byte
Body []byte
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfSendMail) Opcode() network.PacketID { func (m *MsgMhfSendMail) Opcode() network.PacketID {
@@ -18,7 +27,15 @@ func (m *MsgMhfSendMail) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfSendMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfSendMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.RecipientID = bf.ReadUint32()
m.SubjectLength = bf.ReadUint16()
m.BodyLength = bf.ReadUint16()
m.Quantity = bf.ReadUint32()
m.ItemID = bf.ReadUint16()
m.Subject = bf.ReadNullTerminatedBytes()
m.Body = bf.ReadNullTerminatedBytes()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -9,7 +9,10 @@ import (
) )
// MsgMhfSetGuildMissionTarget represents the MSG_MHF_SET_GUILD_MISSION_TARGET // MsgMhfSetGuildMissionTarget represents the MSG_MHF_SET_GUILD_MISSION_TARGET
type MsgMhfSetGuildMissionTarget struct{} type MsgMhfSetGuildMissionTarget struct {
AckHandle uint32
MissionID uint32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfSetGuildMissionTarget) Opcode() network.PacketID { func (m *MsgMhfSetGuildMissionTarget) Opcode() network.PacketID {
@@ -18,7 +21,9 @@ func (m *MsgMhfSetGuildMissionTarget) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfSetGuildMissionTarget) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfSetGuildMissionTarget) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.MissionID = bf.ReadUint32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -11,11 +11,12 @@ import (
type GuildIconMsgPart struct { type GuildIconMsgPart struct {
Index uint16 Index uint16
ID uint16 ID uint16
Unk0 uint8 Page uint8
Size uint8 Size uint8
Rotation uint8 Rotation uint8
Unk1 uint8 Red uint8
Unk2 uint16 Green uint8
Blue uint8
PosX uint16 PosX uint16
PosY uint16 PosY uint16
} }
@@ -47,11 +48,12 @@ func (m *MsgMhfUpdateGuildIcon) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cl
m.IconParts[i] = GuildIconMsgPart{ m.IconParts[i] = GuildIconMsgPart{
Index: bf.ReadUint16(), Index: bf.ReadUint16(),
ID: bf.ReadUint16(), ID: bf.ReadUint16(),
Unk0: bf.ReadUint8(), Page: bf.ReadUint8(),
Size: bf.ReadUint8(), Size: bf.ReadUint8(),
Rotation: bf.ReadUint8(), Rotation: bf.ReadUint8(),
Unk1: bf.ReadUint8(), Red: bf.ReadUint8(),
Unk2: bf.ReadUint16(), Green: bf.ReadUint8(),
Blue: bf.ReadUint8(),
PosX: bf.ReadUint16(), PosX: bf.ReadUint16(),
PosY: bf.ReadUint16(), PosY: bf.ReadUint16(),
} }

View File

@@ -8,8 +8,21 @@ import (
"github.com/Andoryuuta/byteframe" "github.com/Andoryuuta/byteframe"
) )
type Item struct{
Unk0 uint32
ItemId uint16
Amount uint16
Unk1 uint32
}
// MsgMhfUpdateGuildItem represents the MSG_MHF_UPDATE_GUILD_ITEM // MsgMhfUpdateGuildItem represents the MSG_MHF_UPDATE_GUILD_ITEM
type MsgMhfUpdateGuildItem struct{} type MsgMhfUpdateGuildItem struct{
AckHandle uint32
GuildId uint32
Amount uint16
Unk1 uint16 // 0x00 0x00
Items []Item // Array of updated item IDs
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfUpdateGuildItem) Opcode() network.PacketID { func (m *MsgMhfUpdateGuildItem) Opcode() network.PacketID {
@@ -18,7 +31,20 @@ func (m *MsgMhfUpdateGuildItem) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfUpdateGuildItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfUpdateGuildItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.GuildId = bf.ReadUint32()
m.Amount = bf.ReadUint16()
m.Unk1 = bf.ReadUint16()
m.Items = make([]Item, int(m.Amount))
for i := 0; i < int(m.Amount); i++ {
m.Items[i].Unk0 = bf.ReadUint32()
m.Items[i].ItemId = bf.ReadUint16()
m.Items[i].Amount = bf.ReadUint16()
m.Items[i].Unk1 = bf.ReadUint32()
}
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -0,0 +1,37 @@
package mhfpacket
import (
"errors"
"github.com/Solenataris/Erupe/network/clientctx"
"github.com/Solenataris/Erupe/network"
"github.com/Andoryuuta/byteframe"
)
// MsgMhfUpdateGuildMessageBoard represents the MSG_MHF_UPDATE_GUILD_MESSAGE_BOARD
type MsgMhfUpdateGuildMessageBoard struct {
AckHandle uint32
MessageOp uint32
Request []byte
}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfUpdateGuildMessageBoard) Opcode() network.PacketID {
return network.MSG_MHF_UPDATE_GUILD_MESSAGE_BOARD
}
// Parse parses the packet from binary
func (m *MsgMhfUpdateGuildMessageBoard) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.MessageOp = bf.ReadUint32()
if m.MessageOp != 5 {
m.Request = bf.DataFromCurrent()
bf.Seek(int64(len(bf.Data()) - 2), 0)
}
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfUpdateGuildMessageBoard) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,27 +1 @@
package mhfpacket package mhfpacket
import (
"errors"
"github.com/Solenataris/Erupe/network/clientctx"
"github.com/Solenataris/Erupe/network"
"github.com/Andoryuuta/byteframe"
)
// MsgSysReserve202 represents the MSG_SYS_reserve202
type MsgSysReserve202 struct{}
// Opcode returns the ID associated with this packet type.
func (m *MsgSysReserve202) Opcode() network.PacketID {
return network.MSG_SYS_reserve202
}
// Parse parses the packet from binary
func (m *MsgSysReserve202) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}
// Build builds a binary packet from the current data.
func (m *MsgSysReserve202) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,36 +1 @@
package mhfpacket package mhfpacket
import (
"errors"
"github.com/Solenataris/Erupe/network/clientctx"
"github.com/Solenataris/Erupe/network"
"github.com/Andoryuuta/byteframe"
)
// TODO(Andoryuuta): Make up a name for this packet, not reserved anymore. Called "Is_update_guild_msg_board"
// MsgSysReserve203 represents the MSG_SYS_reserve203
type MsgSysReserve203 struct {
AckHandle uint32
Unk0 uint16 // Hardcoded 0x0000 in the binary
Unk1 uint16 // Hardcoded 0x0500 in the binary.
}
// Opcode returns the ID associated with this packet type.
func (m *MsgSysReserve203) Opcode() network.PacketID {
return network.MSG_SYS_reserve203
}
// Parse parses the packet from binary
func (m *MsgSysReserve203) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Unk1 = bf.ReadUint16()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgSysReserve203) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -9,7 +9,12 @@ import (
) )
// MsgSysReserve205 represents the MSG_SYS_reserve205 // MsgSysReserve205 represents the MSG_SYS_reserve205
type MsgSysReserve205 struct{} type MsgSysReserve205 struct {
AckHandle uint32
Unk0 uint32
Unk1 uint32
Unk2 uint32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgSysReserve205) Opcode() network.PacketID { func (m *MsgSysReserve205) Opcode() network.PacketID {
@@ -18,7 +23,11 @@ func (m *MsgSysReserve205) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgSysReserve205) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgSysReserve205) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -841,10 +841,10 @@ func FromOpcode(opcode network.PacketID) MHFPacket {
return &MsgMhfUpdateForceGuildRank{} return &MsgMhfUpdateForceGuildRank{}
case network.MSG_MHF_RESET_TITLE: case network.MSG_MHF_RESET_TITLE:
return &MsgMhfResetTitle{} return &MsgMhfResetTitle{}
case network.MSG_SYS_reserve202: case network.MSG_MHF_ENUMERATE_GUILD_MESSAGE_BOARD:
return &MsgSysReserve202{} return &MsgMhfEnumerateGuildMessageBoard{}
case network.MSG_SYS_reserve203: case network.MSG_MHF_UPDATE_GUILD_MESSAGE_BOARD:
return &MsgSysReserve203{} return &MsgMhfUpdateGuildMessageBoard{}
case network.MSG_SYS_reserve204: case network.MSG_SYS_reserve204:
return &MsgSysReserve204{} return &MsgSysReserve204{}
case network.MSG_SYS_reserve205: case network.MSG_SYS_reserve205:

View File

@@ -423,8 +423,8 @@ const (
MSG_SYS_reserve19F MSG_SYS_reserve19F
MSG_MHF_UPDATE_FORCE_GUILD_RANK MSG_MHF_UPDATE_FORCE_GUILD_RANK
MSG_MHF_RESET_TITLE MSG_MHF_RESET_TITLE
MSG_SYS_reserve202 MSG_MHF_ENUMERATE_GUILD_MESSAGE_BOARD
MSG_SYS_reserve203 MSG_MHF_UPDATE_GUILD_MESSAGE_BOARD
MSG_SYS_reserve204 MSG_SYS_reserve204
MSG_SYS_reserve205 MSG_SYS_reserve205
MSG_SYS_reserve206 MSG_SYS_reserve206

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,8 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"encoding/binary"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/bits" "math/bits"
@@ -75,16 +77,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: 0x0E, //0e with normal sub 4e when having premium it's probably a bitfield? Unk1: s.rights,
// 01 = Character can take quests at allows
// 02 = Hunter Life, normal quests core sub
// 03 = Extra Course, extra quests, town boxes, QOL course, core sub
// 06 = Premium Course, standard 'premium' which makes ranking etc. faster
// some connection to unk1 above for these maybe?
// 06 0A 0B = Boost Course, just actually 3 subs combined
// 08 09 1E = N Course, gives you the benefits of being in a netcafe (extra quests, N Points, daily freebies etc.) minimal and pointless
// no timestamp after 08 or 1E while active
// 0C = N Boost course, ultra luxury course that ruins the game if in use but also gives a
Rights: []mhfpacket.ClientRight{ Rights: []mhfpacket.ClientRight{
{ {
ID: 1, ID: 1,
@@ -144,10 +137,25 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysLogin) pkt := p.(*mhfpacket.MsgSysLogin)
name := "" name := ""
rights := uint32(0x0E)
// 0e with normal sub 4e when having premium
// 01 = Character can take quests at allows
// 02 = Hunter Life, normal quests core sub
// 03 = Extra Course, extra quests, town boxes, QOL course, core sub
// 06 = Premium Course, standard 'premium' which makes ranking etc. faster
// 06 0A 0B = Boost Course, just actually 3 subs combined
// 08 09 1E = N Course, gives you the benefits of being in a netcafe (extra quests, N Points, daily freebies etc.) minimal and pointless
// 0C = N Boost course, ultra luxury course that ruins the game if in use
err := s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", pkt.CharID0).Scan(&rights)
if err != nil {
panic(err)
}
s.server.db.QueryRow("SELECT name FROM characters WHERE id = $1", pkt.CharID0).Scan(&name) s.server.db.QueryRow("SELECT name FROM characters WHERE id = $1", pkt.CharID0).Scan(&name)
s.Lock() s.Lock()
s.Name = name s.Name = name
s.charID = pkt.CharID0 s.charID = pkt.CharID0
s.rights = rights
s.Unlock() s.Unlock()
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp
@@ -159,7 +167,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
} }
} }
_, err := s.server.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", Time_Current().Unix(), s.charID) _, err = s.server.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", Time_Current().Unix(), s.charID)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -192,6 +200,46 @@ func logoutPlayer(s *Session) {
} }
removeSessionFromStage(s) removeSessionFromStage(s)
if _, exists := s.server.semaphore["hs_l0u3B51J9k3"]; exists {
if _, ok := s.server.semaphore["hs_l0u3B51J9k3"].reservedClientSlots[s.charID]; ok {
removeSessionFromSemaphore(s)
}
}
var timePlayed int
err := s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed)
timePlayed = (int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)) + timePlayed
var rpGained int
if s.rights == 0x08091e4e || s.rights == 0x08091e0e { // N Course
rpGained = timePlayed / 900
timePlayed = timePlayed % 900
} else {
rpGained = timePlayed / 1800
timePlayed = timePlayed % 1800
}
_, err = s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID)
if err != nil {
panic(err)
}
saveData, err := GetCharacterSaveData(s, s.charID)
if err != nil {
panic(err)
}
saveData.RP += uint16(rpGained)
transaction, err := s.server.db.Begin()
err = saveData.Save(s, transaction)
if err != nil {
transaction.Rollback()
panic(err)
} else {
transaction.Commit()
}
} }
func handleMsgSysSetStatus(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysSetStatus(s *Session, p mhfpacket.MHFPacket) {}
@@ -209,6 +257,8 @@ func handleMsgSysTime(s *Session, p mhfpacket.MHFPacket) {
Timestamp: uint32(Time_Current_Adjusted().Unix()), // JP timezone Timestamp: uint32(Time_Current_Adjusted().Unix()), // JP timezone
} }
s.QueueSendMHF(resp) s.QueueSendMHF(resp)
s.notifyticker()
} }
func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) {
@@ -386,7 +436,6 @@ func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) {
} }
} }
doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) {
@@ -448,15 +497,8 @@ func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) {
s.logger.Fatal("Failed to update shared item box contents in db", zap.Error(err)) s.logger.Fatal("Failed to update shared item box contents in db", zap.Error(err))
} }
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} }
func handleMsgMhfCreateJoint(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem) pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem)
var netcafe_points int var netcafe_points int

View File

@@ -2,8 +2,8 @@ package channelserver
import ( import (
"fmt" "fmt"
"math"
"strings" "strings"
"math"
"github.com/Andoryuuta/byteframe" "github.com/Andoryuuta/byteframe"
"github.com/Solenataris/Erupe/network/binpacket" "github.com/Solenataris/Erupe/network/binpacket"
@@ -22,6 +22,7 @@ const (
const ( const (
BroadcastTypeTargeted = 0x01 BroadcastTypeTargeted = 0x01
BroadcastTypeStage = 0x03 BroadcastTypeStage = 0x03
BroadcastTypeRavi = 0x06
BroadcastTypeWorld = 0x0a BroadcastTypeWorld = 0x0a
) )
@@ -52,7 +53,6 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x03 && len(pkt.RawDataPayload) == 0x10 { if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x03 && len(pkt.RawDataPayload) == 0x10 {
tmp := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) tmp := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
if tmp.ReadUint16() == 0x0002 && tmp.ReadUint8() == 0x18 { if tmp.ReadUint16() == 0x0002 && tmp.ReadUint8() == 0x18 {
_ = tmp.ReadBytes(9) _ = tmp.ReadBytes(9)
tmp.SetLE() tmp.SetLE()
@@ -93,6 +93,11 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
s.server.BroadcastMHF(resp, s) s.server.BroadcastMHF(resp, s)
case BroadcastTypeStage: case BroadcastTypeStage:
s.stage.BroadcastMHF(resp, s) s.stage.BroadcastMHF(resp, s)
case BroadcastTypeRavi:
if pkt.MessageType == 1 {
session := s.server.semaphore["hs_l0u3B51J9k3"]
(*session).BroadcastMHF(resp, s)
}
case BroadcastTypeTargeted: case BroadcastTypeTargeted:
for _, targetID := range (*msgBinTargeted).TargetCharIDs { for _, targetID := range (*msgBinTargeted).TargetCharIDs {
char := s.server.FindSessionByCharID(targetID) char := s.server.FindSessionByCharID(targetID)
@@ -129,6 +134,114 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
s.server.discordSession.ChannelMessageSend(s.server.erupeConfig.Discord.ChannelID, message) s.server.discordSession.ChannelMessageSend(s.server.erupeConfig.Discord.ChannelID, message)
} }
// RAVI COMMANDS
if _, exists := s.server.semaphore["hs_l0u3B51J9k3"]; exists {
s.server.semaphoreLock.Lock()
getSemaphore := s.server.semaphore["hs_l0u3B51J9k3"]
s.server.semaphoreLock.Unlock()
if _, exists := getSemaphore.reservedClientSlots[s.charID]; exists {
if strings.HasPrefix(chatMessage.Message, "!ravistart") {
row := s.server.db.QueryRow("SELECT raviposttime, ravistarted FROM raviregister WHERE refid = 12")
var raviPosted, raviStarted uint32
err := row.Scan(&raviPosted, &raviStarted)
if err != nil {
panic(err)
return
}
if raviStarted == 0 {
sendServerChatMessage(s, fmt.Sprintf("Raviente will start in less than 10 seconds"))
s.server.db.Exec("UPDATE raviregister SET ravistarted = $1", raviPosted)
} else {
sendServerChatMessage(s, fmt.Sprintf("Raviente has already started"))
}
}
if strings.HasPrefix(chatMessage.Message, "!bressend") {
row := s.server.db.QueryRow("SELECT unknown20 FROM ravistate WHERE refid = 29")
var berserkRes uint32
err := row.Scan(&berserkRes)
if err != nil {
panic(err)
return
}
if berserkRes > 0 {
sendServerChatMessage(s, fmt.Sprintf("Sending ressurection support"))
s.server.db.Exec("UPDATE ravistate SET unknown20 = $1", 0)
} else {
sendServerChatMessage(s, fmt.Sprintf("Ressurection support has not been requested"))
}
}
if strings.HasPrefix(chatMessage.Message, "!bsedsend") {
hprow := s.server.db.QueryRow("SELECT phase1hp, phase2hp, phase3hp, phase4hp, phase5hp FROM ravistate WHERE refid = 29")
var phase1HP, phase2HP, phase3HP, phase4HP, phase5HP uint32
hperr := hprow.Scan(&phase1HP, &phase2HP, &phase3HP, &phase4HP, &phase5HP)
if hperr != nil {
panic(hperr)
return
}
row := s.server.db.QueryRow("SELECT support2 FROM ravisupport WHERE refid = 25")
var berserkTranq uint32
err := row.Scan(&berserkTranq)
if err != nil {
panic(err)
return
}
sendServerChatMessage(s, fmt.Sprintf("Sending sedation support if requested"))
s.server.db.Exec("UPDATE ravisupport SET support2 = $1", (phase1HP + phase2HP + phase3HP + phase4HP + phase5HP))
}
if strings.HasPrefix(chatMessage.Message, "!bsedreq") {
hprow := s.server.db.QueryRow("SELECT phase1hp, phase2hp, phase3hp, phase4hp, phase5hp FROM ravistate WHERE refid = 29")
var phase1HP, phase2HP, phase3HP, phase4HP, phase5HP uint32
hperr := hprow.Scan(&phase1HP, &phase2HP, &phase3HP, &phase4HP, &phase5HP)
if hperr != nil {
panic(hperr)
return
}
row := s.server.db.QueryRow("SELECT support2 FROM ravisupport WHERE refid = 25")
var berserkTranq uint32
err := row.Scan(&berserkTranq)
if err != nil {
panic(err)
return
}
sendServerChatMessage(s, fmt.Sprintf("Requesting sedation support"))
s.server.db.Exec("UPDATE ravisupport SET support2 = $1", ((phase1HP + phase2HP + phase3HP + phase4HP + phase5HP) + 12))
}
if strings.HasPrefix(chatMessage.Message, "!setmultiplier ") {
var num uint8
n, numerr := fmt.Sscanf(chatMessage.Message, "!setmultiplier %d", &num)
row := s.server.db.QueryRow("SELECT damagemultiplier FROM ravistate WHERE refid = 29")
var damageMultiplier uint32
err := row.Scan(&damageMultiplier)
if err != nil {
panic(err)
return
}
if numerr != nil || n != 1 {
sendServerChatMessage(s, fmt.Sprintf("Please use the format !setmultiplier x"))
} else if damageMultiplier == 1 {
if num > 20 {
sendServerChatMessage(s, fmt.Sprintf("Max multiplier for Ravi is 20, setting to this value"))
s.server.db.Exec("UPDATE ravistate SET damagemultiplier = $1", 20)
} else {
sendServerChatMessage(s, fmt.Sprintf("Setting Ravi damage multiplier to %d", num))
s.server.db.Exec("UPDATE ravistate SET damagemultiplier = $1", num)
}
} else {
sendServerChatMessage(s, fmt.Sprintf("Multiplier can only be set once, please restart Ravi to set again"))
}
}
if strings.HasPrefix(chatMessage.Message, "!checkmultiplier") {
var damageMultiplier uint32
row := s.server.db.QueryRow("SELECT damagemultiplier FROM ravistate WHERE refid = 29").Scan(&damageMultiplier)
if row != nil {
return
}
sendServerChatMessage(s, fmt.Sprintf("Ravi's current damage multiplier is %d", damageMultiplier))
}
}
}
// END OF RAVI COMMANDS
if strings.HasPrefix(chatMessage.Message, "!tele ") { if strings.HasPrefix(chatMessage.Message, "!tele ") {
var x, y int16 var x, y int16
n, err := fmt.Sscanf(chatMessage.Message, "!tele %d %d", &x, &y) n, err := fmt.Sscanf(chatMessage.Message, "!tele %d %d", &x, &y)

View File

@@ -59,7 +59,11 @@ func handleMsgMhfListMember(s *Session, p mhfpacket.MHFPacket) {
doAckBufSucceed(s, pkt.AckHandle, resp.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data())
} }
func handleMsgMhfOprMember(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfOprMember(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfListMember)
// TODO: add targetid(uint32) to charid(uint32)'s database under new field
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfShutClient(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfShutClient(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -2,6 +2,7 @@ package channelserver
import ( import (
"encoding/hex" "encoding/hex"
"encoding/binary"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -54,21 +55,46 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
} }
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, "")
// Temporary server launcher response stuff
// 0x1F715 Weapon Class _, err = s.server.db.Exec("UPDATE characters SET weapon_type=$1 WHERE id=$2", uint16(decompressedData[128789]), s.charID)
// 0x1FDF6 HR (small_gr_level)
// 0x88 Character Name
_, err = s.server.db.Exec("UPDATE characters SET weapon=$1 WHERE id=$2", uint16(decompressedData[128789]), s.charID)
if err != nil { if err != nil {
s.logger.Fatal("Failed to character weapon in db", zap.Error(err)) s.logger.Fatal("Failed to character weapon type in db", zap.Error(err))
}
isMale := uint8(decompressedData[80]) // 0x50
if isMale == 1 {
_, err = s.server.db.Exec("UPDATE characters SET is_female=true WHERE id=$1", s.charID)
} else {
_, err = s.server.db.Exec("UPDATE characters SET is_female=false WHERE id=$1", s.charID)
} }
gr := uint16(decompressedData[130550])<<8 | uint16(decompressedData[130551])
s.logger.Info("Setting db field", zap.Uint16("gr_override_level", gr))
// We have to use `gr_override_level` (uint16), not `small_gr_level` (uint8) to store this.
_, err = s.server.db.Exec("UPDATE characters SET gr_override_mode=true, gr_override_level=$1 WHERE id=$2", gr, s.charID)
if err != nil { if err != nil {
s.logger.Fatal("Failed to update character gr_override_level in db", zap.Error(err)) s.logger.Fatal("Failed to character gender in db", zap.Error(err))
} }
weaponId := binary.LittleEndian.Uint16(decompressedData[128522:128524]) // 0x1F60A
_, err = s.server.db.Exec("UPDATE characters SET weapon_id=$1 WHERE id=$2", weaponId, s.charID)
if err != nil {
s.logger.Fatal("Failed to update character weapon id in db", zap.Error(err))
}
hrp := binary.LittleEndian.Uint16(decompressedData[130550:130552]) // 0x1FDF6
_, err = s.server.db.Exec("UPDATE characters SET hrp=$1 WHERE id=$2", hrp, s.charID)
if err != nil {
s.logger.Fatal("Failed to update character hrp in db", zap.Error(err))
}
grp := binary.LittleEndian.Uint32(decompressedData[130556:130560]) // 0x1FDFC
var gr uint16
if grp > 0 {
gr = grpToGR(grp)
} else {
gr = 0
}
_, err = s.server.db.Exec("UPDATE characters SET gr=$1 WHERE id=$2", gr, s.charID)
if err != nil {
s.logger.Fatal("Failed to update character gr in db", zap.Error(err))
}
characterName := s.clientContext.StrConv.MustDecode(bfutil.UpToNull(decompressedData[88:100])) characterName := s.clientContext.StrConv.MustDecode(bfutil.UpToNull(decompressedData[88:100]))
_, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterName, s.charID) _, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterName, s.charID)
if err != nil { if err != nil {
@@ -77,6 +103,53 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} }
func grpToGR(n uint32) uint16 {
var gr uint16
gr = 1
switch grp := int(n); {
case grp < 208750: // Up to 50
i := 0
for {
grp -= 500
if grp <= 500 {
if grp < 0 {
i--
}
break
} else {
i++
for j := 0; j < i; j++ {
grp -= 150
}
}
}
gr = uint16(i + 2); break
case grp < 593400: // 51-99
grp -= 208750; i := 51; for { if grp < 7850 {break}; i++; grp -= 7850 }; gr = uint16(i); break
case grp < 993400: // 100-149
grp -= 593400; i := 100; for { if grp < 8000 {break}; i++; grp -= 8000 }; gr = uint16(i); break
case grp < 1400900: // 150-199
grp -= 993400; i := 150; for { if grp < 8150 {break}; i++; grp -= 8150 }; gr = uint16(i); break
case grp < 2315900: // 200-299
grp -= 1400900; i := 200; for { if grp < 9150 {break}; i++; grp -= 9150 }; gr = uint16(i); break
case grp < 3340900: // 300-399
grp -= 2315900; i := 300; for { if grp < 10250 {break}; i++; grp -= 10250 }; gr = uint16(i); break
case grp < 4505900: // 400-499
grp -= 3340900; i := 400; for { if grp < 11650 {break}; i++; grp -= 11650 }; gr = uint16(i); break
case grp < 5850900: // 500-599
grp -= 4505900; i := 500; for { if grp < 13450 {break}; i++; grp -= 13450 }; gr = uint16(i); break
case grp < 7415900: // 600-699
grp -= 5850900; i := 600; for { if grp < 15650 {break}; i++; grp -= 15650 }; gr = uint16(i); break
case grp < 9230900: // 700-799
grp -= 7415900; i := 700; for { if grp < 18150 {break}; i++; grp -= 18150 }; gr = uint16(i); break
case grp < 11345900: // 800-899
grp -= 9230900; i := 800; for { if grp < 21150 {break}; i++; grp -= 21150 }; gr = uint16(i); break
default: // 900+
grp -= 11345900; i := 900; for { if grp < 23950 {break}; i++; grp -= 23950 }; gr = uint16(i); break
}
return gr
}
func dumpSaveData(s *Session, data []byte, suffix string) { func dumpSaveData(s *Session, data []byte, suffix string) {
if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled { if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled {
return return

View File

@@ -196,7 +196,10 @@ func handleMsgMhfAcquireUdItem(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} }
func handleMsgMhfGetUdRanking(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdRanking)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfGetUdMyRanking(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetUdMyRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdMyRanking) pkt := p.(*mhfpacket.MsgMhfGetUdMyRanking)

View File

@@ -10,6 +10,8 @@ import (
"fmt" "fmt"
"sort" "sort"
"time" "time"
"strings"
"strconv"
"github.com/Andoryuuta/byteframe" "github.com/Andoryuuta/byteframe"
"github.com/Solenataris/Erupe/common/bfutil" "github.com/Solenataris/Erupe/common/bfutil"
@@ -47,10 +49,11 @@ type Guild struct {
SubMotto uint8 `db:"sub_motto"` SubMotto uint8 `db:"sub_motto"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `db:"created_at"`
MemberCount uint16 `db:"member_count"` MemberCount uint16 `db:"member_count"`
RP uint32 `db:"rp"` RankRP uint32 `db:"rank_rp"`
EventRP uint32 `db:"event_rp"`
Comment string `db:"comment"` Comment string `db:"comment"`
FestivalColour FestivalColour `db:"festival_colour"` FestivalColour FestivalColour `db:"festival_colour"`
GuildHallType uint16 `db:"guild_hall"` Rank uint16 `db:"rank"`
Icon *GuildIcon `db:"icon"` Icon *GuildIcon `db:"icon"`
GuildLeader GuildLeader
@@ -64,8 +67,12 @@ type GuildLeader struct {
type GuildIconPart struct { type GuildIconPart struct {
Index uint16 Index uint16
ID uint16 ID uint16
Page uint8
Size uint8 Size uint8
Rotation uint8 Rotation uint8
Red uint8
Green uint8
Blue uint8
PosX uint16 PosX uint16
PosY uint16 PosY uint16
} }
@@ -99,9 +106,11 @@ func (gi *GuildIcon) Value() (valuer driver.Value, err error) {
} }
const guildInfoSelectQuery = ` const guildInfoSelectQuery = `
SELECT g.id, SELECT
g.id,
g.name, g.name,
g.rp, g.rank_rp,
g.event_rp,
g.main_motto, g.main_motto,
g.sub_motto, g.sub_motto,
created_at, created_at,
@@ -109,12 +118,19 @@ SELECT g.id,
lc.name as leader_name, lc.name as leader_name,
comment, comment,
festival_colour, festival_colour,
guild_hall, CASE
WHEN g.rank_rp <= 48 THEN g.rank_rp/24
WHEN g.rank_rp <= 288 THEN g.rank_rp/48+1
WHEN g.rank_rp <= 504 THEN g.rank_rp/72+3
WHEN g.rank_rp <= 1080 THEN (g.rank_rp-24)/96+5
WHEN g.rank_rp < 1200 THEN 16
ELSE 17
END rank,
icon, icon,
( (
SELECT count(1) FROM guild_characters gc WHERE gc.guild_id = g.id SELECT count(1) FROM guild_characters gc WHERE gc.guild_id = g.id
) AS member_count ) AS member_count
FROM guilds g FROM guilds g
JOIN guild_characters lgc ON lgc.character_id = leader_id JOIN guild_characters lgc ON lgc.character_id = leader_id
JOIN characters lc on leader_id = lc.id JOIN characters lc on leader_id = lc.id
` `
@@ -328,28 +344,6 @@ func (guild *Guild) ArrangeCharacters(s *Session, charIDs []uint32) error {
return nil return nil
} }
func (guild *Guild) DonateRP(s *Session, rp uint16, transaction *sql.Tx) (err error) {
updateSQL := "UPDATE guilds SET rp = rp + $1 WHERE id = $2"
if transaction != nil {
_, err = transaction.Exec(updateSQL, rp, guild.ID)
} else {
_, err = s.server.db.Exec(updateSQL, rp, guild.ID)
}
if err != nil {
s.logger.Error(
"failed to donate RP to guild",
zap.Error(err),
zap.Uint32("guildID", guild.ID),
)
return err
}
return nil
}
func (guild *Guild) GetApplicationForCharID(s *Session, charID uint32, applicationType GuildApplicationType) (*GuildApplication, error) { func (guild *Guild) GetApplicationForCharID(s *Session, charID uint32, applicationType GuildApplicationType) (*GuildApplication, error) {
row := s.server.db.QueryRowx(` row := s.server.db.QueryRowx(`
SELECT * from guild_applications WHERE character_id = $1 AND guild_id = $2 AND application_type = $3 SELECT * from guild_applications WHERE character_id = $1 AND guild_id = $2 AND application_type = $3
@@ -415,8 +409,8 @@ func CreateGuild(s *Session, guildName string) (int32, error) {
} }
guildResult, err := transaction.Query( guildResult, err := transaction.Query(
"INSERT INTO guilds (name, leader_id, rp, guild_hall) VALUES ($1, $2, $3, $4) RETURNING id", "INSERT INTO guilds (name, leader_id) VALUES ($1, $2) RETURNING id",
guildName, s.charID, 48, 17, guildName, s.charID,
) )
if err != nil { if err != nil {
@@ -617,7 +611,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
switch pkt.Action { switch pkt.Action {
case mhfpacket.OPERATE_GUILD_ACTION_DISBAND: case mhfpacket.OPERATE_GUILD_DISBAND:
if guild.LeaderCharID != s.charID { if guild.LeaderCharID != s.charID {
s.logger.Warn(fmt.Sprintf("character '%d' is attempting to manage guild '%d' without permission", s.charID, guild.ID)) s.logger.Warn(fmt.Sprintf("character '%d' is attempting to manage guild '%d' without permission", s.charID, guild.ID))
return return
@@ -632,7 +626,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
} }
bf.WriteUint32(uint32(response)) bf.WriteUint32(uint32(response))
case mhfpacket.OPERATE_GUILD_ACTION_APPLY: case mhfpacket.OPERATE_GUILD_APPLY:
err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil) err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil)
if err != nil { if err != nil {
@@ -641,7 +635,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
} else { } else {
bf.WriteUint32(guild.LeaderCharID) bf.WriteUint32(guild.LeaderCharID)
} }
case mhfpacket.OPERATE_GUILD_ACTION_LEAVE: case mhfpacket.OPERATE_GUILD_LEAVE:
var err error var err error
if characterGuildInfo.IsApplicant { if characterGuildInfo.IsApplicant {
@@ -651,24 +645,27 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
} }
response := 0x01 response := 0x01
if err != nil { if err != nil {
// All successful acks return 0x01, assuming 0x00 is failure // All successful acks return 0x01, assuming 0x00 is failure
response = 0x00 response = 0x00
} }
bf.WriteUint32(uint32(response)) bf.WriteUint32(uint32(response))
case mhfpacket.OPERATE_GUILD_ACTION_DONATE: case mhfpacket.OPERATE_GUILD_DONATE_RANK:
err := handleOperateGuildActionDonate(s, guild, pkt, bf) handleDonateRP(s, pkt, bf, guild, false)
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_DENY:
if err != nil { // TODO: close applications for guild
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_ALLOW:
// TODO: open applications for guild
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return return
}
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE: case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE:
handleAvoidLeadershipUpdate(s, pkt, true) handleAvoidLeadershipUpdate(s, pkt, true)
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE: case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE:
handleAvoidLeadershipUpdate(s, pkt, false) handleAvoidLeadershipUpdate(s, pkt, false)
case mhfpacket.OPERATE_GUILD_ACTION_UPDATE_COMMENT: case mhfpacket.OPERATE_GUILD_UPDATE_COMMENT:
pbf := byteframe.NewByteFrameFromBytes(pkt.UnkData) pbf := byteframe.NewByteFrameFromBytes(pkt.UnkData)
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() { if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
@@ -695,7 +692,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
} }
bf.WriteUint32(0x00) bf.WriteUint32(0x00)
case mhfpacket.OPERATE_GUILD_ACTION_UPDATE_MOTTO: case mhfpacket.OPERATE_GUILD_UPDATE_MOTTO:
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() { if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return return
@@ -710,6 +707,26 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return return
} }
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_1:
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_2:
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_3:
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_1:
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_2:
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_3:
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
case mhfpacket.OPERATE_GUILD_DONATE_EVENT:
handleDonateRP(s, pkt, bf, guild, true)
default: default:
panic(fmt.Sprintf("unhandled operate guild action '%d'", pkt.Action)) panic(fmt.Sprintf("unhandled operate guild action '%d'", pkt.Action))
} }
@@ -717,6 +734,35 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleDonateRP(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, bf *byteframe.ByteFrame, guild *Guild, isEvent bool) error {
rp := binary.BigEndian.Uint16(pkt.UnkData[3:5])
saveData, err := GetCharacterSaveData(s, s.charID)
if err != nil {
return err
}
saveData.RP -= rp
transaction, err := s.server.db.Begin()
err = saveData.Save(s, transaction)
if err != nil {
transaction.Rollback()
return err
}
updateSQL := "UPDATE guilds SET rank_rp = rank_rp + $1 WHERE id = $2"
if isEvent {
updateSQL = "UPDATE guilds SET event_rp = event_rp + $1 WHERE id = $2"
}
_, err = s.server.db.Exec(updateSQL, rp, guild.ID)
if err != nil {
s.logger.Error("Failed to donate rank RP to guild", zap.Error(err), zap.Uint32("guildID", guild.ID))
transaction.Rollback()
return err
} else {
transaction.Commit()
}
bf.WriteUint32(uint32(saveData.RP))
return nil
}
func handleAvoidLeadershipUpdate(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, avoidLeadership bool) { func handleAvoidLeadershipUpdate(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, avoidLeadership bool) {
characterGuildData, err := GetCharacterGuildData(s, s.charID) characterGuildData, err := GetCharacterGuildData(s, s.charID)
@@ -737,93 +783,6 @@ func handleAvoidLeadershipUpdate(s *Session, pkt *mhfpacket.MsgMhfOperateGuild,
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }
func handleOperateGuildActionDonate(s *Session, guild *Guild, pkt *mhfpacket.MsgMhfOperateGuild, bf *byteframe.ByteFrame) error {
var rpDB uint16
var guildHallLvl uint16
rp := binary.BigEndian.Uint16(pkt.UnkData[3:5])
saveData, err := GetCharacterSaveData(s, s.charID)
if err != nil {
return err
}
if saveData.RP < rp {
s.logger.Warn(
"character attempting to donate more RP than they own",
zap.Uint32("charID", s.charID),
zap.Uint16("rp", rp),
)
return err
}
saveData.RP -= rp
transaction, err := s.server.db.Begin()
if err != nil {
s.logger.Error("failed to start db transaction", zap.Error(err))
return err
}
err = saveData.Save(s, transaction)
if err != nil {
err = transaction.Rollback()
if err != nil {
s.logger.Error("failed to rollback transaction", zap.Error(err))
}
return err
}
err = guild.DonateRP(s, rp, transaction)
if err != nil {
err = transaction.Rollback()
if err != nil {
s.logger.Error("failed to rollback transaction", zap.Error(err))
}
return err
}
err = transaction.Commit()
if err != nil {
s.logger.Error("failed to commit transaction", zap.Error(err))
return err
}
bf.WriteUint32(uint32(saveData.RP)) // Points remaining
errSelectSQL := s.server.db.QueryRow("SELECT rp FROM guilds WHERE id=$1", guild.ID).Scan(&rpDB)
if errSelectSQL != nil {
s.logger.Fatal("Failed to get rp from db", zap.Error(errSelectSQL))
}
guildHallLvl = rpDB / 70
if guildHallLvl >= 17 {
guildHallLvl = 17
}
fmt.Printf("\n\nrpDB = %d\n", rpDB)
fmt.Printf("guildHallLvl = %d\n", guildHallLvl)
_, errUpdateSQL := s.server.db.Exec("UPDATE guilds SET guild_hall=$1 WHERE id=$2", guildHallLvl, guild.ID)
if errUpdateSQL == nil {
s.logger.Error("failed to donate RP to guild", zap.Error(errUpdateSQL), zap.Uint32("guildID", guild.ID))
return err
}
return nil
}
func handleMsgMhfOperateGuildMember(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfOperateGuildMember(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfOperateGuildMember) pkt := p.(*mhfpacket.MsgMhfOperateGuildMember)
@@ -924,11 +883,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(guild.ID) bf.WriteUint32(guild.ID)
bf.WriteUint32(guild.LeaderCharID) bf.WriteUint32(guild.LeaderCharID)
// Unk 0x09 = Guild Hall available, maybe guild hall type? bf.WriteUint16(guild.Rank)
// Guild hall available on at least
// 0x09 0x08 0x02
// Should just be outright guild level for guild hall features, 17 gives everything
bf.WriteUint16(guild.GuildHallType)
bf.WriteUint16(guild.MemberCount) bf.WriteUint16(guild.MemberCount)
bf.WriteUint8(guild.MainMotto) bf.WriteUint8(guild.MainMotto)
@@ -939,7 +894,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
if characterGuildData == nil || characterGuildData.IsApplicant { if characterGuildData == nil || characterGuildData.IsApplicant {
bf.WriteUint16(0x00) bf.WriteUint16(0x00)
} else if characterGuildData.IsSubLeader() || guild.LeaderCharID == s.charID { } else if guild.LeaderCharID == s.charID {
bf.WriteUint16(0x01) bf.WriteUint16(0x01)
} else { } else {
bf.WriteUint16(0x02) bf.WriteUint16(0x02)
@@ -955,13 +910,13 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint8(uint8(len(leaderName) + 1)) bf.WriteUint8(uint8(len(leaderName) + 1))
bf.WriteBytes(guildName) bf.WriteBytes(guildName)
bf.WriteBytes(guildComment) bf.WriteBytes(guildComment)
bf.WriteUint8(FestivalColourCodes[guild.FestivalColour]) bf.WriteUint8(FestivalColourCodes[guild.FestivalColour])
bf.WriteUint32(guild.RankRP)
bf.WriteUint32(guild.RP)
bf.WriteNullTerminatedBytes(leaderName) bf.WriteNullTerminatedBytes(leaderName)
//bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00}) // Unk bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00}) // Unk
bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x18, 0xBD}) // Level 17 guild's version bf.WriteBool(false) // isReturnGuild
bf.WriteBytes([]byte{0x01, 0x02, 0x02}) // Unk
bf.WriteUint32(guild.EventRP)
// Pugi's names, probably expected as null until you have them with levels? Null gives them a default japanese name // Pugi's names, probably expected as null until you have them with levels? Null gives them a default japanese name
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
@@ -983,30 +938,6 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(0x00) // Alliance ID bf.WriteUint32(0x00) // Alliance ID
// TODO add alliance parts here
//
//if (AllianceID != 0) {
// uint16 AllianceDataUnk;
// uint16 AllianceDataUnk;
// uint16 AllianceNameLength;
// char AllianceName[AllianceNameLength];
//
// byte NumAllianceMembers;
//
// struct AllianceMember {
// uint32 Unk;
// uint32 Unk;
// uint16 Unk;
// uint16 Unk;
// uint16 Unk;
// uint16 GuildNameLength;
// char GuildName[GuildNameLength];
// uint16 GuildLeaderNameLength;
// char GuildLeaderName[GuildLeaderNameLength];
//
// } member[NumAllianceMembers] <optimize=false>;
//}
applicants, err := GetGuildMembers(s, guild.ID, true) applicants, err := GetGuildMembers(s, guild.ID, true)
if err != nil { if err != nil {
@@ -1028,15 +959,6 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteNullTerminatedBytes(applicantName) bf.WriteNullTerminatedBytes(applicantName)
} }
// This is guild icon data
// temp canned bytes to avoid crashing when a guild has more than 1-2 members and a guild hall
//bf.WriteBytes([]byte{0x00, 0x05, 0x00, 0x03, 0x00, 0x38, 0x01, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x00, 0x04, 0x00, 0x03,
// 0x00, 0x02, 0x00, 0x38, 0x01, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0x00, 0x69, 0x00, 0x60, 0x00, 0x01,
// 0x00, 0x38, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x6A, 0x00, 0x03, 0x00, 0x00, 0x00, 0x38,
// 0x01, 0x00, 0x02, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x63, 0x00, 0x06, 0x00, 0x35, 0x01, 0x03,
// 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x11, 0x00, 0x01, 0x00,
//})
// Unk bool? if true +3 bytes after this // Unk bool? if true +3 bytes after this
bf.WriteUint8(0x00) bf.WriteUint8(0x00)
@@ -1046,11 +968,12 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
for _, p := range guild.Icon.Parts { for _, p := range guild.Icon.Parts {
bf.WriteUint16(p.Index) bf.WriteUint16(p.Index)
bf.WriteUint16(p.ID) bf.WriteUint16(p.ID)
bf.WriteUint8(0x01) bf.WriteUint8(p.Page)
bf.WriteUint8(p.Size) bf.WriteUint8(p.Size)
bf.WriteUint8(p.Rotation) bf.WriteUint8(p.Rotation)
bf.WriteUint8(0xFF) bf.WriteUint8(p.Red)
bf.WriteUint16(0xFFFF) bf.WriteUint8(p.Green)
bf.WriteUint8(p.Blue)
bf.WriteUint16(p.PosX) bf.WriteUint16(p.PosX)
bf.WriteUint16(p.PosY) bf.WriteUint16(p.PosY)
} }
@@ -1073,24 +996,123 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateGuild) pkt := p.(*mhfpacket.MsgMhfEnumerateGuild)
var guilds []*Guild var guilds []*Guild
var rows *sqlx.Rows
var err error var err error
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
switch pkt.Type { switch pkt.Type {
case mhfpacket.ENUMERATE_GUILD_TYPE_NAME: case mhfpacket.ENUMERATE_GUILD_TYPE_GUILD_NAME:
// I have no idea if is really little endian, but it seems too weird to have a random static bf.ReadBytes(8)
// 0x00 before the string searchTermLength := bf.ReadUint16()
searchTermLength := binary.LittleEndian.Uint16(pkt.RawDataPayload[9:11]) bf.ReadBytes(1)
searchTerm := pkt.RawDataPayload[11 : 11+searchTermLength] searchTerm := bf.ReadBytes(uint(searchTermLength))
var searchTermSafe string var searchTermSafe string
searchTermSafe, err = s.clientContext.StrConv.Decode(bfutil.UpToNull(searchTerm)) searchTermSafe, err = s.clientContext.StrConv.Decode(bfutil.UpToNull(searchTerm))
if err != nil { if err != nil {
panic(err) panic(err)
} }
guilds, err = FindGuildsByName(s, searchTermSafe) guilds, err = FindGuildsByName(s, searchTermSafe)
case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_NAME:
bf.ReadBytes(8)
searchTermLength := bf.ReadUint16()
bf.ReadBytes(1)
searchTerm := bf.ReadBytes(uint(searchTermLength))
var searchTermSafe string
searchTermSafe, err = s.clientContext.StrConv.Decode(bfutil.UpToNull(searchTerm))
if err != nil {
panic(err)
}
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE lc.name ILIKE $1`, guildInfoSelectQuery), searchTermSafe)
if err != nil {
s.logger.Error("Failed to retrieve guild by leader name", zap.Error(err))
} else {
for rows.Next() {
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
guilds = append(guilds, guild)
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_ID:
bf.ReadBytes(3)
ID := bf.ReadUint32()
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE leader_id = $1`, guildInfoSelectQuery), ID)
if err != nil {
s.logger.Error("Failed to retrieve guild by leader ID", zap.Error(err))
} else {
for rows.Next() {
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
guilds = append(guilds, guild)
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_MEMBERS:
sorting := bf.ReadUint16()
if sorting == 1 {
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count DESC`, guildInfoSelectQuery))
} else {
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count ASC`, guildInfoSelectQuery))
}
if err != nil {
s.logger.Error("Failed to retrieve guild by member count", zap.Error(err))
} else {
for rows.Next() {
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
guilds = append(guilds, guild)
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_REGISTRATION:
sorting := bf.ReadUint16()
if sorting == 1 {
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id DESC`, guildInfoSelectQuery))
} else {
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id ASC`, guildInfoSelectQuery))
}
if err != nil {
s.logger.Error("Failed to retrieve guild by registration date", zap.Error(err))
} else {
for rows.Next() {
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
guilds = append(guilds, guild)
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_RANK:
sorting := bf.ReadUint16()
if sorting == 1 {
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp DESC`, guildInfoSelectQuery))
} else {
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp ASC`, guildInfoSelectQuery))
}
if err != nil {
s.logger.Error("Failed to retrieve guild by rank", zap.Error(err))
} else {
for rows.Next() {
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
guilds = append(guilds, guild)
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_MOTTO:
bf.ReadBytes(3)
mainMotto := bf.ReadUint16()
subMotto := bf.ReadUint16()
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE main_motto = $1 AND sub_motto = $2`, guildInfoSelectQuery), mainMotto, subMotto)
if err != nil {
s.logger.Error("Failed to retrieve guild by motto", zap.Error(err))
} else {
for rows.Next() {
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
guilds = append(guilds, guild)
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_RECRUITING:
//
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME:
//
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME:
//
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_ID:
//
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS:
//
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION:
//
default: default:
panic(fmt.Sprintf("no handler for guild search type '%d'", pkt.Type)) panic(fmt.Sprintf("no handler for guild search type '%d'", pkt.Type))
} }
@@ -1100,7 +1122,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
return return
} }
bf := byteframe.NewByteFrame() bf = byteframe.NewByteFrame()
bf.WriteUint16(uint16(len(guilds))) bf.WriteUint16(uint16(len(guilds)))
for _, guild := range guilds { for _, guild := range guilds {
@@ -1113,12 +1135,12 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(guild.MemberCount) bf.WriteUint16(guild.MemberCount)
bf.WriteUint8(0x00) // Unk bf.WriteUint8(0x00) // Unk
bf.WriteUint8(0x00) // Unk bf.WriteUint8(0x00) // Unk
bf.WriteUint16(0x00) // Rank bf.WriteUint16(guild.Rank)
bf.WriteUint32(uint32(guild.CreatedAt.Unix())) bf.WriteUint32(uint32(guild.CreatedAt.Unix()))
bf.WriteUint8(uint8(len(guildName))) bf.WriteUint8(uint8(len(guildName)+1))
bf.WriteBytes(guildName) bf.WriteNullTerminatedBytes(guildName)
bf.WriteUint8(uint8(len(leaderName))) bf.WriteUint8(uint8(len(leaderName)+1))
bf.WriteBytes(leaderName) bf.WriteNullTerminatedBytes(leaderName)
bf.WriteUint8(0x01) // Unk bf.WriteUint8(0x01) // Unk
} }
@@ -1203,12 +1225,14 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {
name := s.clientContext.StrConv.MustEncode(member.Name) name := s.clientContext.StrConv.MustEncode(member.Name)
bf.WriteUint32(member.CharID) bf.WriteUint32(member.CharID)
bf.WriteUint16(member.HRP)
// Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999 bf.WriteUint16(member.GR)
bf.WriteUint16(member.Exp) // Rank flags bf.WriteUint16(member.WeaponID)
bf.WriteUint16(0x00) // Grank if member.WeaponType == 1 || member.WeaponType == 5 || member.WeaponType == 10 { // If weapon is ranged
bf.WriteUint16(0x00) // Unk bf.WriteUint16(0x0700)
bf.WriteUint16(0x00) // Some rank? } else {
bf.WriteUint16(0x0600)
}
bf.WriteUint8(member.OrderIndex) bf.WriteUint8(member.OrderIndex)
bf.WriteUint16(uint16(len(name) + 1)) bf.WriteUint16(uint16(len(name) + 1))
bf.WriteNullTerminatedBytes(name) bf.WriteNullTerminatedBytes(name)
@@ -1298,10 +1322,95 @@ func handleMsgMhfGetGuildTargetMemberNum(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfEnumerateGuildItem(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateGuildItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateGuildItem) pkt := p.(*mhfpacket.MsgMhfEnumerateGuildItem)
var boxContents []byte
bf := byteframe.NewByteFrame()
err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", int(pkt.GuildId)).Scan(&boxContents)
if err != nil {
s.logger.Fatal("Failed to get guild item box contents from db", zap.Error(err))
} else {
if len(boxContents) == 0 {
bf.WriteUint32(0x00)
} else {
amount := len(boxContents) / 4
bf.WriteUint16(uint16(amount))
bf.WriteUint32(0x00)
bf.WriteUint16(0x00)
for i := 0; i < amount; i++ {
bf.WriteUint32(binary.BigEndian.Uint32(boxContents[i*4:i*4+4]))
if i + 1 != amount {
bf.WriteUint64(0x00)
}
}
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
data, _ := hex.DecodeString("000100004cfa00010017000300000000") type Item struct{
ItemId uint16
Amount uint16
}
doAckBufSucceed(s, pkt.AckHandle, data) func handleMsgMhfUpdateGuildItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateGuildItem)
// Get item cache from DB
var boxContents []byte
var oldItems []Item
err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", int(pkt.GuildId)).Scan(&boxContents)
if err != nil {
s.logger.Fatal("Failed to get guild item box contents from db", zap.Error(err))
} else {
amount := len(boxContents) / 4
oldItems = make([]Item, amount)
for i := 0; i < amount; i++ {
oldItems[i].ItemId = binary.BigEndian.Uint16(boxContents[i*4:i*4+2])
oldItems[i].Amount = binary.BigEndian.Uint16(boxContents[i*4+2:i*4+4])
}
}
// Update item stacks
newItems := make([]Item, len(oldItems))
copy(newItems, oldItems)
for i := 0; i < int(pkt.Amount); i++ {
for j := 0; j <= len(oldItems); j++ {
if j == len(oldItems) {
var newItem Item
newItem.ItemId = pkt.Items[i].ItemId
newItem.Amount = pkt.Items[i].Amount
newItems = append(newItems, newItem)
break
}
if pkt.Items[i].ItemId == oldItems[j].ItemId {
newItems[j].Amount = pkt.Items[i].Amount
break
}
}
}
// Delete empty item stacks
for i := len(newItems) - 1; i >= 0; i-- {
if int(newItems[i].Amount) == 0 {
copy(newItems[i:], newItems[i + 1:])
newItems[len(newItems) - 1] = make([]Item, 1)[0]
newItems = newItems[:len(newItems) - 1]
}
}
// Create new item cache
bf := byteframe.NewByteFrame()
for i := 0; i < len(newItems); i++ {
bf.WriteUint16(newItems[i].ItemId)
bf.WriteUint16(newItems[i].Amount)
}
// Upload new item cache
_, err = s.server.db.Exec("UPDATE guilds SET item_box = $1 WHERE id = $2", bf.Data(), int(pkt.GuildId))
if err != nil {
s.logger.Fatal("Failed to update guild item box contents in db", zap.Error(err))
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} }
func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) {
@@ -1337,8 +1446,12 @@ func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) {
icon.Parts[i] = GuildIconPart{ icon.Parts[i] = GuildIconPart{
Index: p.Index, Index: p.Index,
ID: p.ID, ID: p.ID,
Page: p.Page,
Size: p.Size, Size: p.Size,
Rotation: p.Rotation, Rotation: p.Rotation,
Red: p.Red,
Green: p.Green,
Blue: p.Blue,
PosX: p.PosX, PosX: p.PosX,
PosY: p.PosY, PosY: p.PosY,
} }
@@ -1434,16 +1547,182 @@ func handleMsgMhfGetGuildWeeklyBonusActiveCount(s *Session, p mhfpacket.MHFPacke
func handleMsgMhfGuildHuntdata(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGuildHuntdata(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGuildHuntdata) pkt := p.(*mhfpacket.MsgMhfGuildHuntdata)
data := []byte{0x01, 0xFE} doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
doAckBufSucceed(s, pkt.AckHandle, data)
} }
// "Enumrate_guild_msg_board" type MessageBoardPost struct {
func handleMsgSysReserve202(s *Session, p mhfpacket.MHFPacket) {} Type uint32 `db:"post_type"`
StampID uint32 `db:"stamp_id"`
Title string `db:"title"`
Body string `db:"body"`
AuthorID uint32 `db:"author_id"`
Timestamp uint64 `db:"created_at"`
LikedBy string `db:"liked_by"`
}
// "Is_update_guild_msg_board" func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
func handleMsgSysReserve203(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuildMessageBoard)
pkt := p.(*mhfpacket.MsgSysReserve203) guild, _ := GetGuildInfoByCharacterId(s, s.charID)
msgs, err := s.server.db.Queryx("SELECT post_type, stamp_id, title, body, author_id, (EXTRACT(epoch FROM created_at)::int) as created_at, liked_by FROM guild_posts WHERE guild_id = $1 AND post_type = $2 ORDER BY created_at DESC", guild.ID, int(pkt.BoardType))
if err != nil {
s.logger.Fatal("Failed to get guild messages from db", zap.Error(err))
}
bf := byteframe.NewByteFrame()
noMsgs := true
postCount := 0
for msgs.Next() {
noMsgs = false
postCount++
postData := &MessageBoardPost{}
err = msgs.StructScan(&postData)
if err != nil {
s.logger.Error("Failed to read guild message board post")
}
bf.WriteUint32(postData.Type)
bf.WriteUint32(postData.AuthorID)
bf.WriteUint64(postData.Timestamp)
liked := false
likedBySlice := strings.Split(postData.LikedBy, ",")
for i := 0; i < len(likedBySlice); i++ {
j, _ := strconv.ParseInt(likedBySlice[i], 10, 64)
if int(j) == int(s.charID) {
liked = true; break
}
}
if likedBySlice[0] == "" {
bf.WriteUint32(0)
} else {
bf.WriteUint32(uint32(len(likedBySlice)))
}
bf.WriteBool(liked)
bf.WriteUint32(postData.StampID)
bf.WriteUint32(uint32(len(postData.Title)))
bf.WriteBytes([]byte(postData.Title))
bf.WriteUint32(uint32(len(postData.Body)))
bf.WriteBytes([]byte(postData.Body))
}
if noMsgs {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} else {
data := byteframe.NewByteFrame()
data.WriteUint32(uint32(postCount))
data.WriteBytes(bf.Data())
doAckBufSucceed(s, pkt.AckHandle, data.Data())
}
}
func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateGuildMessageBoard)
bf := byteframe.NewByteFrameFromBytes(pkt.Request)
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
if guild == nil {
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
}
switch pkt.MessageOp {
case 0: // Create message
postType := bf.ReadUint32() // 0 = message, 1 = news
stampId := bf.ReadUint32()
titleLength := bf.ReadUint32()
bodyLength := bf.ReadUint32()
title := bf.ReadBytes(uint(titleLength))
body := bf.ReadBytes(uint(bodyLength))
_, err := s.server.db.Exec("INSERT INTO guild_posts (guild_id, author_id, stamp_id, post_type, title, body) VALUES ($1, $2, $3, $4, $5, $6)", guild.ID, s.charID, int(stampId), int(postType), string(title), string(body))
if err != nil {
s.logger.Fatal("Failed to add new guild message to db", zap.Error(err))
}
// TODO: if there are too many messages, purge excess
_, err = s.server.db.Exec("")
if err != nil {
s.logger.Fatal("Failed to remove excess guild messages from db", zap.Error(err))
}
case 1: // Delete message
postType := bf.ReadUint32()
timestamp := bf.ReadUint64()
_, err := s.server.db.Exec("DELETE FROM guild_posts WHERE post_type = $1 AND (EXTRACT(epoch FROM created_at)::int) = $2 AND guild_id = $3", int(postType), int(timestamp), guild.ID)
if err != nil {
s.logger.Fatal("Failed to delete guild message from db", zap.Error(err))
}
case 2: // Update message
postType := bf.ReadUint32()
timestamp := bf.ReadUint64()
titleLength := bf.ReadUint32()
bodyLength := bf.ReadUint32()
title := bf.ReadBytes(uint(titleLength))
body := bf.ReadBytes(uint(bodyLength))
_, err := s.server.db.Exec("UPDATE guild_posts SET title = $1, body = $2 WHERE post_type = $3 AND (EXTRACT(epoch FROM created_at)::int) = $4 AND guild_id = $5", string(title), string(body), int(postType), int(timestamp), guild.ID)
if err != nil {
s.logger.Fatal("Failed to update guild message in db", zap.Error(err))
}
case 3: // Update stamp
postType := bf.ReadUint32()
timestamp := bf.ReadUint64()
stampId := bf.ReadUint32()
_, err := s.server.db.Exec("UPDATE guild_posts SET stamp_id = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", int(stampId), int(postType), int(timestamp), guild.ID)
if err != nil {
s.logger.Fatal("Failed to update guild message stamp in db", zap.Error(err))
}
case 4: // Like message
postType := bf.ReadUint32()
timestamp := bf.ReadUint64()
likeState := bf.ReadBool()
var likedBy string
err := s.server.db.QueryRow("SELECT liked_by FROM guild_posts WHERE post_type = $1 AND (EXTRACT(epoch FROM created_at)::int) = $2 AND guild_id = $3", int(postType), int(timestamp), guild.ID).Scan(&likedBy)
if err != nil {
s.logger.Fatal("Failed to get guild message like data from db", zap.Error(err))
} else {
if likeState {
if len(likedBy) == 0 {
likedBy = strconv.Itoa(int(s.charID))
} else {
likedBy += "," + strconv.Itoa(int(s.charID))
}
_, err := s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", likedBy, int(postType), int(timestamp), guild.ID)
if err != nil {
s.logger.Fatal("Failed to like guild message in db", zap.Error(err))
}
} else {
likedBySlice := strings.Split(likedBy, ",")
for i, e := range likedBySlice {
if e == strconv.Itoa(int(s.charID)) {
likedBySlice[i] = likedBySlice[len(likedBySlice) - 1]
likedBySlice = likedBySlice[:len(likedBySlice) - 1]
}
}
likedBy = strings.Join(likedBySlice, ",")
_, err := s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", likedBy, int(postType), int(timestamp), guild.ID)
if err != nil {
s.logger.Fatal("Failed to unlike guild message in db", zap.Error(err))
}
}
}
case 5: // Check for new messages
var timeChecked int
var newPosts int
err := s.server.db.QueryRow("SELECT (EXTRACT(epoch FROM guild_post_checked)::int) FROM characters WHERE id = $1", s.charID).Scan(&timeChecked)
if err != nil {
s.logger.Fatal("Failed to get last guild post check timestamp from db", zap.Error(err))
} else {
_, err = s.server.db.Exec("UPDATE characters SET guild_post_checked = $1 WHERE id = $2", time.Now(), s.charID)
if err != nil {
s.logger.Fatal("Failed to update guild post check timestamp in db", zap.Error(err))
} else {
err = s.server.db.QueryRow("SELECT COUNT(*) FROM guild_posts WHERE guild_id = $1 AND (EXTRACT(epoch FROM created_at)::int) > $2", guild.ID, timeChecked).Scan(&newPosts)
if err != nil {
s.logger.Fatal("Failed to check for new guild posts in db", zap.Error(err))
} else {
if newPosts > 0 {
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
return
}
}
}
}
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} }
@@ -1451,7 +1730,12 @@ func handleMsgMhfEntryRookieGuild(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfUpdateForceGuildRank(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUpdateForceGuildRank(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfAddGuildWeeklyBonusExceptionalUser(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfAddGuildWeeklyBonusExceptionalUser(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAddGuildWeeklyBonusExceptionalUser)
// TODO: record pkt.NumUsers to DB
// must use addition
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfRegistGuildAdventure(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfRegistGuildAdventure(s *Session, p mhfpacket.MHFPacket) {}
@@ -1461,7 +1745,10 @@ func handleMsgMhfChargeGuildAdventure(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfAddGuildMissionCount(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfAddGuildMissionCount(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfSetGuildMissionTarget(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfSetGuildMissionTarget(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSetGuildMissionTarget)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfCancelGuildMissionTarget(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCancelGuildMissionTarget(s *Session, p mhfpacket.MHFPacket) {}
@@ -1477,4 +1764,8 @@ func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfUpdateGuildcard(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUpdateGuildcard(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfUpdateGuildItem(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCreateJoint(s *Session, p mhfpacket.MHFPacket) { }
func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) { }
func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -18,7 +18,10 @@ type GuildMember struct {
LastLogin uint32 `db:"last_login"` LastLogin uint32 `db:"last_login"`
AvoidLeadership bool `db:"avoid_leadership"` AvoidLeadership bool `db:"avoid_leadership"`
IsLeader bool `db:"is_leader"` IsLeader bool `db:"is_leader"`
Exp uint16 `db:"exp"` HRP uint16 `db:"hrp"`
GR uint16 `db:"gr"`
WeaponID uint16 `db:"weapon_id"`
WeaponType uint16 `db:"weapon_type"`
} }
func (gm *GuildMember) IsSubLeader() bool { func (gm *GuildMember) IsSubLeader() bool {
@@ -53,7 +56,10 @@ SELECT g.id as guild_id,
coalesce(gc.order_index, 0) as order_index, coalesce(gc.order_index, 0) as order_index,
c.last_login, c.last_login,
coalesce(gc.avoid_leadership, false) as avoid_leadership, coalesce(gc.avoid_leadership, false) as avoid_leadership,
c.exp, c.hrp,
c.gr,
c.weapon_id,
c.weapon_type,
character.is_applicant, character.is_applicant,
CASE WHEN g.leader_id = c.id THEN 1 ELSE 0 END as is_leader CASE WHEN g.leader_id = c.id THEN 1 ELSE 0 END as is_leader
FROM ( FROM (

View File

@@ -93,6 +93,23 @@ func (m *Mail) MarkDeleted(s *Session) error {
return nil return nil
} }
func (m *Mail) MarkAcquired(s *Session) error {
_, err := s.server.db.Exec(`
UPDATE mail SET attached_item_received = true WHERE id = $1
`, m.ID)
if err != nil {
s.logger.Error(
"failed to mark mail item as claimed",
zap.Error(err),
zap.Int("mailID", m.ID),
)
return err
}
return nil
}
func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) { func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) {
rows, err := s.server.db.Queryx(` rows, err := s.server.db.Queryx(`
SELECT SELECT
@@ -101,6 +118,7 @@ func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) {
m.recipient_id, m.recipient_id,
m.subject, m.subject,
m.read, m.read,
m.attached_item_received,
m.attached_item, m.attached_item,
m.attached_item_amount, m.attached_item_amount,
m.created_at, m.created_at,
@@ -147,6 +165,7 @@ func GetMailByID(s *Session, ID int) (*Mail, error) {
m.subject, m.subject,
m.read, m.read,
m.body, m.body,
m.attached_item_received,
m.attached_item, m.attached_item,
m.attached_item_amount, m.attached_item_amount,
m.created_at, m.created_at,
@@ -235,9 +254,11 @@ func handleMsgMhfReadMail(s *Session, p mhfpacket.MHFPacket) {
_ = mail.MarkRead(s) _ = mail.MarkRead(s)
bf := byteframe.NewByteFrame()
bodyBytes, _ := stringsupport.ConvertUTF8ToShiftJIS(mail.Body) bodyBytes, _ := stringsupport.ConvertUTF8ToShiftJIS(mail.Body)
bf.WriteNullTerminatedBytes(bodyBytes)
doAckBufSucceed(s, pkt.AckHandle, bodyBytes) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) {
@@ -295,10 +316,10 @@ func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) {
msg.WriteUint8(flags) msg.WriteUint8(flags)
msg.WriteBool(itemAttached) msg.WriteBool(itemAttached)
msg.WriteUint8(uint8(len(subjectBytes))) msg.WriteUint8(uint8(len(subjectBytes)+1))
msg.WriteUint8(uint8(len(senderNameBytes))) msg.WriteUint8(uint8(len(senderNameBytes)+1))
msg.WriteBytes(subjectBytes) msg.WriteNullTerminatedBytes(subjectBytes)
msg.WriteBytes(senderNameBytes) msg.WriteNullTerminatedBytes(senderNameBytes)
if itemAttached { if itemAttached {
msg.WriteInt16(m.AttachedItemAmount) msg.WriteInt16(m.AttachedItemAmount)
@@ -320,9 +341,14 @@ func handleMsgMhfOprtMail(s *Session, p mhfpacket.MHFPacket) {
} }
switch mhfpacket.OperateMailOperation(pkt.Operation) { switch mhfpacket.OperateMailOperation(pkt.Operation) {
case mhfpacket.OperateMailOperationDelete: case mhfpacket.OPERATE_MAIL_DELETE:
err = mail.MarkDeleted(s) err = mail.MarkDeleted(s)
if err != nil {
doAckSimpleFail(s, pkt.AckHandle, nil)
panic(err)
}
case mhfpacket.OPERATE_MAIL_ACQUIRE_ITEM:
err = mail.MarkAcquired(s)
if err != nil { if err != nil {
doAckSimpleFail(s, pkt.AckHandle, nil) doAckSimpleFail(s, pkt.AckHandle, nil)
panic(err) panic(err)
@@ -332,4 +358,34 @@ func handleMsgMhfOprtMail(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, nil) doAckSimpleSucceed(s, pkt.AckHandle, nil)
} }
func handleMsgMhfSendMail(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfSendMail(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSendMail)
query := `
INSERT INTO mail (sender_id, recipient_id, subject, body, attached_item, attached_item_amount, is_guild_invite)
VALUES ($1, $2, $3, $4, $5, $6, $7)
`
if pkt.RecipientID == 0 { // Guild mail
g, err := GetGuildInfoByCharacterId(s, s.charID)
if err != nil {
s.logger.Fatal("Failed to get guild info for mail")
}
gm, err := GetGuildMembers(s, g.ID, false)
if err != nil {
s.logger.Fatal("Failed to get guild members for mail")
}
for i := 0; i < len(gm); i++ {
_, err := s.server.db.Exec(query, s.charID, gm[i].CharID, pkt.Subject, pkt.Body, 0, 0, false)
if err != nil {
s.logger.Fatal("Failed to send mail")
}
}
} else {
_, err := s.server.db.Exec(query, s.charID, pkt.RecipientID, pkt.Subject, pkt.Body, pkt.ItemID, pkt.Quantity, false)
if err != nil {
s.logger.Fatal("Failed to send mail")
}
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,13 +12,35 @@ func handleMsgSysCreateSemaphore(s *Session, p mhfpacket.MHFPacket) {
} }
func handleMsgSysDeleteSemaphore(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysDeleteSemaphore(s *Session, p mhfpacket.MHFPacket) {
//pkt := p.(*mhfpacket.MsgSysDeleteSemaphore) pkt := p.(*mhfpacket.MsgSysDeleteSemaphore)
sem := pkt.AckHandle
s.semaphore.Lock() if s.server.semaphore != nil {
s.server.semaphoreLock.Lock()
for id := range s.server.semaphore { for id := range s.server.semaphore {
switch sem {
case 917533:
if s.server.semaphore[id].id_semaphore == "hs_l0u3B51J9k3" {
delete(s.server.semaphore["hs_l0u3B51J9k3"].reservedClientSlots, s.charID)
delete(s.server.semaphore["hs_l0u3B51J9k3"].clients, s)
}
case 851997:
if s.server.semaphore[id].id_semaphore == "hs_l0u3B51J9k4" {
delete(s.server.semaphore["hs_l0u3B51J9k4"].reservedClientSlots, s.charID)
}
case 786461:
if s.server.semaphore[id].id_semaphore == "hs_l0u3B51J9k5" {
delete(s.server.semaphore["hs_l0u3B51J9k5"].reservedClientSlots, s.charID)
}
default:
if len(s.server.semaphore[id].reservedClientSlots) != 0 {
if s.server.semaphore[id].id_semaphore != "hs_l0u3B51J9k3" && s.server.semaphore[id].id_semaphore != "hs_l0u3B51J9k4" && s.server.semaphore[id].id_semaphore != "hs_l0u3B51J9k5" {
delete(s.server.semaphore[id].reservedClientSlots, s.charID) delete(s.server.semaphore[id].reservedClientSlots, s.charID)
} }
s.semaphore.Unlock() }
}
}
s.server.semaphoreLock.Unlock()
}
} }
func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
@@ -49,11 +71,33 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
s.logger.Info("IS ALREADY EXIST !") s.logger.Info("IS ALREADY EXIST !")
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0F, 0x00, 0x1D}) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0F, 0x00, 0x1D})
} else if uint16(len(newSemaphore.reservedClientSlots)) < newSemaphore.maxPlayers { } else if uint16(len(newSemaphore.reservedClientSlots)) < newSemaphore.maxPlayers {
switch SemaphoreID {
case "hs_l0u3B51J9k3":
newSemaphore.reservedClientSlots[s.charID] = nil
newSemaphore.clients[s] = s.charID
s.Lock()
s.semaphore = newSemaphore
s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0E, 0x00, 0x1D})
case "hs_l0u3B51J9k4":
newSemaphore.reservedClientSlots[s.charID] = nil newSemaphore.reservedClientSlots[s.charID] = nil
s.Lock() s.Lock()
s.semaphore = newSemaphore s.semaphore = newSemaphore
s.Unlock() s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0F, 0x00, 0x1D}) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0D, 0x00, 0x1D})
case "hs_l0u3B51J9k5":
newSemaphore.reservedClientSlots[s.charID] = nil
s.Lock()
s.semaphore = newSemaphore
s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0C, 0x00, 0x1D})
default:
newSemaphore.reservedClientSlots[s.charID] = nil
s.Lock()
s.semaphore = newSemaphore
s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0F, 0x00, 0x25})
}
} else { } else {
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} }
@@ -65,15 +109,26 @@ func handleMsgSysAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
func handleMsgSysReleaseSemaphore(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysReleaseSemaphore(s *Session, p mhfpacket.MHFPacket) {
//pkt := p.(*mhfpacket.MsgSysReleaseSemaphore) //pkt := p.(*mhfpacket.MsgSysReleaseSemaphore)
for _, session := range s.server.sessions { if _, exists := s.server.semaphore["hs_l0u3B51J9k3"]; exists {
session.semaphore.Lock() reset := len(s.server.semaphore["hs_l0u3B51J9k3"].reservedClientSlots)
for id := range session.server.semaphore { if reset == 0 {
s.server.db.Exec("CALL ravireset($1)", 0)
}
}
}
func removeSessionFromSemaphore(s *Session) {
s.server.semaphoreLock.Lock()
for id := range s.server.semaphore {
delete(s.server.semaphore[id].reservedClientSlots, s.charID) delete(s.server.semaphore[id].reservedClientSlots, s.charID)
if id == "hs_l0u3B51J9k3" {
delete(s.server.semaphore[id].clients, s)
} else {
continue
} }
session.semaphore.Unlock()
} }
//data, _ := hex.DecodeString("000180e703000d443b37ff006d00131809000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010627426400a936a93600000100cf330600cc31cc31d431000025000000000000000000010218330600bd3cbd3cbd3c01032c280600ee3dee3da9360104f3300600d231a936a93601054a310600e23ae23ae23a00000d0000000000004d814c0000000003008501d723b7334001e7038b3fd437d516113505000000e7030001000002000203000000000000fafafafafafafafafafafafafafa000000000000ecb2000060da0000000000000000000000000000000000000000000000000000000000000000000000000000181818187e2d00003b31702d662d402e000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000") s.server.semaphoreLock.Unlock()
//doAckBufSucceed(s, pkt.AckHandle, data)
} }
func handleMsgSysCheckSemaphore(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysCheckSemaphore(s *Session, p mhfpacket.MHFPacket) {

View File

@@ -429,8 +429,8 @@ func init() {
handlerTable[network.MSG_SYS_reserve19F] = handleMsgSysReserve19F handlerTable[network.MSG_SYS_reserve19F] = handleMsgSysReserve19F
handlerTable[network.MSG_MHF_UPDATE_FORCE_GUILD_RANK] = handleMsgMhfUpdateForceGuildRank handlerTable[network.MSG_MHF_UPDATE_FORCE_GUILD_RANK] = handleMsgMhfUpdateForceGuildRank
handlerTable[network.MSG_MHF_RESET_TITLE] = handleMsgMhfResetTitle handlerTable[network.MSG_MHF_RESET_TITLE] = handleMsgMhfResetTitle
handlerTable[network.MSG_SYS_reserve202] = handleMsgSysReserve202 handlerTable[network.MSG_MHF_ENUMERATE_GUILD_MESSAGE_BOARD] = handleMsgMhfEnumerateGuildMessageBoard
handlerTable[network.MSG_SYS_reserve203] = handleMsgSysReserve203 handlerTable[network.MSG_MHF_UPDATE_GUILD_MESSAGE_BOARD] = handleMsgMhfUpdateGuildMessageBoard
handlerTable[network.MSG_SYS_reserve204] = handleMsgSysReserve204 handlerTable[network.MSG_SYS_reserve204] = handleMsgSysReserve204
handlerTable[network.MSG_SYS_reserve205] = handleMsgSysReserve205 handlerTable[network.MSG_SYS_reserve205] = handleMsgSysReserve205
handlerTable[network.MSG_SYS_reserve206] = handleMsgSysReserve206 handlerTable[network.MSG_SYS_reserve206] = handleMsgSysReserve206

View File

@@ -48,7 +48,12 @@ func handleMsgMhfGetUdTacticsFirstQuestBonus(s *Session, p mhfpacket.MHFPacket)
func handleMsgMhfGetUdTacticsRemainingPoint(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdTacticsRemainingPoint(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetUdTacticsRanking(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdTacticsRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdTacticsRanking)
// Temporary canned response
data, _ := hex.DecodeString("00000515000005150000CEB4000003CE000003CE0000CEB44D49444E494748542D414E47454C0000000000000000000000")
doAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfSetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfSetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -1,6 +1,9 @@
package channelserver package channelserver
import ( import (
"github.com/Andoryuuta/byteframe"
"github.com/Solenataris/Erupe/network/mhfpacket"
"sync" "sync"
) )
@@ -11,6 +14,10 @@ type Semaphore struct {
// Stage ID string // Stage ID string
id_semaphore string id_semaphore string
// Map of session -> charID.
// These are clients that are CURRENTLY in the stage
clients map[*Session]uint32
// Map of charID -> interface{}, only the key is used, value is always nil. // Map of charID -> interface{}, only the key is used, value is always nil.
reservedClientSlots map[uint32]interface{} reservedClientSlots map[uint32]interface{}
@@ -22,8 +29,46 @@ type Semaphore struct {
func NewSemaphore(ID string, MaxPlayers uint16) *Semaphore { func NewSemaphore(ID string, MaxPlayers uint16) *Semaphore {
s := &Semaphore{ s := &Semaphore{
id_semaphore: ID, id_semaphore: ID,
clients: make(map[*Session]uint32),
reservedClientSlots: make(map[uint32]interface{}), reservedClientSlots: make(map[uint32]interface{}),
maxPlayers: MaxPlayers, maxPlayers: MaxPlayers,
} }
return s return s
} }
func (s *Semaphore) BroadcastRavi(pkt mhfpacket.MHFPacket) {
// Broadcast the data.
for session := range s.clients {
// Make the header
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(pkt.Opcode()))
// Build the packet onto the byteframe.
pkt.Build(bf, session.clientContext)
// Enqueue in a non-blocking way that drops the packet if the connections send buffer channel is full.
session.QueueSendNonBlocking(bf.Data())
}
}
// BroadcastMHF queues a MHFPacket to be sent to all sessions in the stage.
func (s *Semaphore) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
// Broadcast the data.
for session := range s.clients {
if session == ignoredSession {
continue
}
// Make the header
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(pkt.Opcode()))
// Build the packet onto the byteframe.
pkt.Build(bf, session.clientContext)
// Enqueue in a non-blocking way that drops the packet if the connections send buffer channel is full.
session.QueueSendNonBlocking(bf.Data())
}
}

View File

@@ -32,6 +32,8 @@ type Session struct {
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet. reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
charID uint32 charID uint32
logKey []byte logKey []byte
sessionStart int64
rights uint32
semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet. semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet.
@@ -62,6 +64,7 @@ func NewSession(server *Server, conn net.Conn) *Session {
Encoding: japanese.ShiftJIS, Encoding: japanese.ShiftJIS,
}, },
}, },
sessionStart: Time_Current_Adjusted().Unix(),
stageMoveStack: stringstack.New(), stageMoveStack: stringstack.New(),
} }
return s return s
@@ -177,6 +180,7 @@ func (s *Session) handlePacketGroup(pktGroup []byte) {
opcode != network.MSG_SYS_PING && opcode != network.MSG_SYS_PING &&
opcode != network.MSG_SYS_NOP && opcode != network.MSG_SYS_NOP &&
opcode != network.MSG_SYS_TIME && opcode != network.MSG_SYS_TIME &&
opcode != network.MSG_SYS_POSITION_OBJECT &&
opcode != network.MSG_SYS_EXTEND_THRESHOLD { opcode != network.MSG_SYS_EXTEND_THRESHOLD {
fmt.Printf("[%s] send to Server\n", s.Name) fmt.Printf("[%s] send to Server\n", s.Name)
fmt.Printf("Opcode: %s\n", opcode) fmt.Printf("Opcode: %s\n", opcode)

View File

@@ -26,9 +26,9 @@ func (s *Server) newUserChara(username string) error {
_, err = s.db.Exec(` _, err = s.db.Exec(`
INSERT INTO characters ( INSERT INTO characters (
user_id, is_female, is_new_character, small_gr_level, gr_override_mode, name, unk_desc_string, user_id, is_female, is_new_character, name, unk_desc_string,
gr_override_level, gr_override_unk0, gr_override_unk1, exp, weapon, last_login) hrp, gr, weapon_type, last_login)
VALUES($1, False, True, 0, True, '', '', 0, 0, 0, 0, 0, $2)`, VALUES($1, False, True, '', '', 1, 0, 0, $2)`,
id, id,
uint32(time.Now().Unix()), uint32(time.Now().Unix()),
) )
@@ -60,9 +60,9 @@ func (s *Server) registerDBAccount(username string, password string) error {
// Create a base new character. // Create a base new character.
_, err = s.db.Exec(` _, err = s.db.Exec(`
INSERT INTO characters ( INSERT INTO characters (
user_id, is_female, is_new_character, small_gr_level, gr_override_mode, name, unk_desc_string, user_id, is_female, is_new_character, name, unk_desc_string,
gr_override_level, gr_override_unk0, gr_override_unk1, exp, weapon, last_login) hrp, gr, weapon_type, last_login)
VALUES($1, False, True, 0, True, '', '', 0, 0, 0, 0, 0, $2)`, VALUES($1, False, True, '', '', 1, 0, 0, $2)`,
id, id,
uint32(time.Now().Unix()), uint32(time.Now().Unix()),
) )
@@ -77,21 +77,17 @@ type character struct {
ID uint32 `db:"id"` ID uint32 `db:"id"`
IsFemale bool `db:"is_female"` IsFemale bool `db:"is_female"`
IsNewCharacter bool `db:"is_new_character"` IsNewCharacter bool `db:"is_new_character"`
SmallGRLevel uint8 `db:"small_gr_level"`
GROverrideMode bool `db:"gr_override_mode"`
Name string `db:"name"` Name string `db:"name"`
UnkDescString string `db:"unk_desc_string"` UnkDescString string `db:"unk_desc_string"`
GROverrideLevel uint16 `db:"gr_override_level"` HRP uint16 `db:"hrp"`
GROverrideUnk0 uint8 `db:"gr_override_unk0"` GR uint16 `db:"gr"`
GROverrideUnk1 uint8 `db:"gr_override_unk1"` WeaponType uint16 `db:"weapon_type"`
Exp uint16 `db:"exp"`
Weapon uint16 `db:"weapon"`
LastLogin uint32 `db:"last_login"` LastLogin uint32 `db:"last_login"`
} }
func (s *Server) getCharactersForUser(uid int) ([]character, error) { func (s *Server) getCharactersForUser(uid int) ([]character, error) {
characters := []character{} characters := []character{}
err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, small_gr_level, gr_override_mode, name, unk_desc_string, gr_override_level, gr_override_unk0, gr_override_unk1, exp, weapon, last_login FROM characters WHERE user_id = $1", uid) err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1", uid)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -2,6 +2,8 @@ package signserver
import ( import (
"fmt" "fmt"
"math/rand"
"time"
"github.com/Andoryuuta/byteframe" "github.com/Andoryuuta/byteframe"
"go.uber.org/zap" "go.uber.org/zap"
@@ -32,6 +34,15 @@ func makeSignInFailureResp(respID RespID) []byte {
return bf.Data() return bf.Data()
} }
func randSeq(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
func (s *Session) makeSignInResp(uid int) []byte { func (s *Session) makeSignInResp(uid int) []byte {
// Get the characters from the DB. // Get the characters from the DB.
chars, err := s.server.getCharactersForUser(uid) chars, err := s.server.getCharactersForUser(uid)
@@ -39,6 +50,10 @@ func (s *Session) makeSignInResp(uid int) []byte {
s.logger.Warn("Error getting characters from DB", zap.Error(err)) s.logger.Warn("Error getting characters from DB", zap.Error(err))
} }
rand.Seed(time.Now().UnixNano())
token := randSeq(16)
// TODO: register token to db, users table
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteUint8(1) // resp_code bf.WriteUint8(1) // resp_code
@@ -46,7 +61,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
bf.WriteUint8(4) // entrance server count bf.WriteUint8(4) // entrance server count
bf.WriteUint8(uint8(len(chars))) // character count bf.WriteUint8(uint8(len(chars))) // character count
bf.WriteUint32(0xFFFFFFFF) // login_token_number bf.WriteUint32(0xFFFFFFFF) // login_token_number
bf.WriteBytes(paddedString("logintokenstrng", 16)) // login_token (16 byte padded string) bf.WriteBytes(paddedString(token, 16)) // login_token (16 byte padded string)
bf.WriteUint32(1576761190) bf.WriteUint32(1576761190)
uint8PascalString(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.HostIP, s.server.erupeConfig.Entrance.Port)) uint8PascalString(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.HostIP, s.server.erupeConfig.Entrance.Port))
uint8PascalString(bf, "") uint8PascalString(bf, "")
@@ -60,24 +75,18 @@ func (s *Session) makeSignInResp(uid int) []byte {
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.MaxLauncherHR { if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.MaxLauncherHR {
bf.WriteUint16(999) bf.WriteUint16(999)
} else { } else {
bf.WriteUint16(char.Exp) bf.WriteUint16(char.HRP)
} }
bf.WriteUint16(char.WeaponType) // Weapon, 0-13.
bf.WriteUint16(char.Weapon) // Weapon, 0-13.
bf.WriteUint32(char.LastLogin) // Last login date, unix timestamp in seconds. bf.WriteUint32(char.LastLogin) // Last login date, unix timestamp in seconds.
bf.WriteBool(char.IsFemale) // Sex, 0=male, 1=female. bf.WriteBool(char.IsFemale) // Sex, 0=male, 1=female.
bf.WriteBool(char.IsNewCharacter) // Is new character, 1 replaces character name with ?????. bf.WriteBool(char.IsNewCharacter) // Is new character, 1 replaces character name with ?????.
bf.WriteUint8(char.SmallGRLevel) // GR level if grMode == 0 bf.WriteUint8(0) // Old GR
bf.WriteBool(char.GROverrideMode) // GR mode. bf.WriteBool(true) // Use uint16 GR, no reason not to
bf.WriteBytes(paddedString(char.Name, 16)) // Character name bf.WriteBytes(paddedString(char.Name, 16)) // Character name
bf.WriteBytes(paddedString(char.UnkDescString, 32)) // unk str bf.WriteBytes(paddedString(char.UnkDescString, 32)) // unk str
if char.GROverrideMode { bf.WriteUint16(char.GR)
bf.SetLE() bf.WriteUint16(0) // Unk
bf.WriteUint16(char.GROverrideLevel) // GR level override.
bf.SetBE()
bf.WriteUint8(char.GROverrideUnk0) // unk
bf.WriteUint8(char.GROverrideUnk1) // unk
}
} }
bf.WriteUint8(0) // friends_list_count bf.WriteUint8(0) // friends_list_count

View File

@@ -72,11 +72,20 @@
</div> </div>
<ul class="article"> <ul class="article">
<li> <li>
<div class="date">2022-05-03</div>
<div class="body">
<a
href="javascript:toggleModal('openLink',&quot;https://discord.com/channels/368424389416583169/929509970624532511/969305400795078656&quot;);"
onclick="soundOk()">Eng 2.0 &amp; Ravi Patch Released!
</a>
</div>
</li>
<li>
<div class="date">2022-04-24</div> <div class="date">2022-04-24</div>
<div class="body"> <div class="body">
<a <a
href="javascript:toggleModal('openLink',&quot;https://discord.com/channels/368424389416583169/929509970624532511/964339905364918272&quot;);" href="javascript:toggleModal('openLink',&quot;https://discord.com/channels/368424389416583169/929509970624532511/969286397301248050&quot;);"
onclick="soundOk()">Launcher Patch v1.0 Released! onclick="soundOk()">Launcher Patch V1.0 Released!
</a> </a>
</div> </div>
</li> </li>
@@ -92,7 +101,7 @@
<div class="body"> <div class="body">
<a <a
href="javascript:toggleModal('openLink',&quot;https://discord.gg/CFnzbhQ&quot;);" href="javascript:toggleModal('openLink',&quot;https://discord.gg/CFnzbhQ&quot;);"
onclick="soundOk()">Join the community discord for updates! onclick="soundOk()">Join the community Discord for updates!
</a> </a>
</div> </div>
</li> </li>

View File

@@ -108,7 +108,7 @@ function createCharItem(name, uid, weapon, hr, gr, date, sex) {
icon = 'img/icons/ss.png'; icon = 'img/icons/ss.png';
break; break;
case '双剣': case '双剣':
weapon = 'Dual Blades'; weapon = 'Dual Swords';
icon = 'img/icons/db.png'; icon = 'img/icons/db.png';
break; break;
case '大剣': case '大剣':
@@ -116,7 +116,7 @@ function createCharItem(name, uid, weapon, hr, gr, date, sex) {
icon = 'img/icons/gs.png'; icon = 'img/icons/gs.png';
break; break;
case '太刀': case '太刀':
weapon = 'Long Sword'; weapon = 'Longsword';
icon = 'img/icons/ls.png'; icon = 'img/icons/ls.png';
break; break;
case 'ハンマー': case 'ハンマー':
@@ -139,7 +139,7 @@ function createCharItem(name, uid, weapon, hr, gr, date, sex) {
weapon = 'Tonfa'; weapon = 'Tonfa';
icon = 'img/icons/tf.png'; icon = 'img/icons/tf.png';
break; break;
case 'スラッシュアックスF': case 'スラッシュアックス':
weapon = 'Switch Axe F'; weapon = 'Switch Axe F';
icon = 'img/icons/sa.png'; icon = 'img/icons/sa.png';
break; break;
@@ -272,7 +272,7 @@ function doLogin(option) {
addLog('Creating new character...', 'normal'); addLog('Creating new character...', 'normal');
window.external.loginCog(username+'+', password, password); window.external.loginCog(username+'+', password, password);
} else { } else {
window.external.loginCog(username, password, password); window.external.loginCog(username, password, 'test');
} }
} catch (e) { } catch (e) {
addLog('Error on loginCog: '+e, 'error'); addLog('Error on loginCog: '+e, 'error');