mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-04-03 14:32:32 +02:00
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
@@ -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")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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) {}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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) {}
|
||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {}
|
||||||
|
|
||||||
|
|||||||
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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',"https://discord.com/channels/368424389416583169/929509970624532511/969305400795078656");"
|
||||||
|
onclick="soundOk()">Eng 2.0 & 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',"https://discord.com/channels/368424389416583169/929509970624532511/964339905364918272");"
|
href="javascript:toggleModal('openLink',"https://discord.com/channels/368424389416583169/929509970624532511/969286397301248050");"
|
||||||
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',"https://discord.gg/CFnzbhQ");"
|
href="javascript:toggleModal('openLink',"https://discord.gg/CFnzbhQ");"
|
||||||
onclick="soundOk()">Join the community discord for updates!
|
onclick="soundOk()">Join the community Discord for updates!
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -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 'スラッシュアックスF':
|
||||||
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');
|
||||||
|
|||||||
Reference in New Issue
Block a user