mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-29 12:02:56 +02:00
Raviente Patch
This commit is contained in:
@@ -11,9 +11,20 @@ import (
|
||||
// MsgMhfAcquireUdItem represents the MSG_MHF_ACQUIRE_UD_ITEM
|
||||
type MsgMhfAcquireUdItem struct {
|
||||
AckHandle uint32
|
||||
// Valid field size(s), not sure about the types.
|
||||
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.
|
||||
@@ -25,9 +36,12 @@ func (m *MsgMhfAcquireUdItem) Opcode() network.PacketID {
|
||||
func (m *MsgMhfAcquireUdItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
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 errors.New("NOT IMPLEMENTED")
|
||||
}
|
||||
|
||||
// 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
|
||||
type MsgMhfAddGuildWeeklyBonusExceptionalUser struct{}
|
||||
type MsgMhfAddGuildWeeklyBonusExceptionalUser struct {
|
||||
AckHandle uint32
|
||||
NumUsers uint8
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Opcode() network.PacketID {
|
||||
@@ -18,7 +21,9 @@ func (m *MsgMhfAddGuildWeeklyBonusExceptionalUser) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
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.
|
||||
|
||||
@@ -11,22 +11,25 @@ import (
|
||||
type EnumerateGuildType uint8
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
ENUMERATE_GUILD_TYPE_NAME
|
||||
//Numbers correspond to order in guild search menu
|
||||
ENUMERATE_GUILD_TYPE_6
|
||||
ENUMERATE_GUILD_TYPE_LEADER_ID
|
||||
ENUMERATE_GUILD_TYPE_3
|
||||
ENUMERATE_GUILD_TYPE_2
|
||||
ENUMERATE_GUILD_TYPE_7
|
||||
ENUMERATE_GUILD_TYPE_8
|
||||
ENUMERATE_GUILD_TYPE_NEW
|
||||
ENUMERATE_GUILD_TYPE_GUILD_NAME = 0x01
|
||||
ENUMERATE_GUILD_TYPE_LEADER_NAME = 0x02
|
||||
ENUMERATE_GUILD_TYPE_LEADER_ID = 0x03
|
||||
ENUMERATE_GUILD_TYPE_ORDER_MEMBERS = 0x04
|
||||
ENUMERATE_GUILD_TYPE_ORDER_REGISTRATION = 0x05
|
||||
ENUMERATE_GUILD_TYPE_ORDER_RANK = 0x06
|
||||
ENUMERATE_GUILD_TYPE_MOTTO = 0x07
|
||||
ENUMERATE_GUILD_TYPE_RECRUITING = 0x08
|
||||
ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME = 0x09
|
||||
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
|
||||
type MsgMhfEnumerateGuild struct {
|
||||
AckHandle uint32
|
||||
Type uint8
|
||||
Type EnumerateGuildType
|
||||
RawDataPayload []byte
|
||||
}
|
||||
|
||||
@@ -38,9 +41,9 @@ func (m *MsgMhfEnumerateGuild) Opcode() network.PacketID {
|
||||
// Parse parses the packet from binary
|
||||
func (m *MsgMhfEnumerateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.Type = bf.ReadUint8()
|
||||
m.Type = EnumerateGuildType(bf.ReadUint8())
|
||||
m.RawDataPayload = bf.DataFromCurrent()
|
||||
|
||||
bf.Seek(int64(len(bf.Data()) - 2), 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
// MsgMhfEnumerateGuildItem represents the MSG_MHF_ENUMERATE_GUILD_ITEM
|
||||
type MsgMhfEnumerateGuildItem struct {
|
||||
AckHandle uint32
|
||||
GuildId uint32
|
||||
Unk0 uint16
|
||||
}
|
||||
|
||||
// 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
|
||||
func (m *MsgMhfEnumerateGuildItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
|
||||
m.GuildId = bf.ReadUint32()
|
||||
m.Unk0 = bf.ReadUint16()
|
||||
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")
|
||||
}
|
||||
@@ -9,7 +9,10 @@ import (
|
||||
)
|
||||
|
||||
// MsgMhfEnumerateUnionItem represents the MSG_MHF_ENUMERATE_UNION_ITEM
|
||||
type MsgMhfEnumerateUnionItem struct{}
|
||||
type MsgMhfEnumerateUnionItem struct {
|
||||
AckHandle uint32
|
||||
Unk0 uint16
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
func (m *MsgMhfEnumerateUnionItem) Opcode() network.PacketID {
|
||||
@@ -18,7 +21,10 @@ func (m *MsgMhfEnumerateUnionItem) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
func (m *MsgMhfEnumerateUnionItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
return errors.New("NOT IMPLEMENTED")
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.Unk0 = bf.ReadUint16()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build builds a binary packet from the current data.
|
||||
|
||||
@@ -11,9 +11,8 @@ import (
|
||||
// MsgMhfGetAchievement represents the MSG_MHF_GET_ACHIEVEMENT
|
||||
type MsgMhfGetAchievement struct{
|
||||
AckHandle uint32
|
||||
Unk0 uint16 // id?
|
||||
Unk0 uint32 // id?
|
||||
Unk1 uint32 // char?
|
||||
Unk2 uint32 // pad?
|
||||
}
|
||||
|
||||
// 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
|
||||
func (m *MsgMhfGetAchievement) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.Unk0 = bf.ReadUint16()
|
||||
m.Unk0 = bf.ReadUint32()
|
||||
m.Unk1 = bf.ReadUint32()
|
||||
m.Unk2 = bf.ReadUint32()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
// MsgMhfGetUdRanking represents the MSG_MHF_GET_UD_RANKING
|
||||
type MsgMhfGetUdRanking struct{
|
||||
AckHandle uint32
|
||||
Unk0 uint8
|
||||
}
|
||||
|
||||
// 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
|
||||
func (m *MsgMhfGetUdRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.Unk0 = bf.ReadUint8()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ import (
|
||||
)
|
||||
|
||||
// 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.
|
||||
func (m *MsgMhfGetUdTacticsRanking) Opcode() network.PacketID {
|
||||
@@ -18,7 +21,9 @@ func (m *MsgMhfGetUdTacticsRanking) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
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.
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
// MsgMhfListMail represents the MSG_MHF_LIST_MAIL
|
||||
type MsgMhfListMail struct {
|
||||
AckHandle uint32
|
||||
Unk0 uint32
|
||||
}
|
||||
|
||||
// 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
|
||||
func (m *MsgMhfListMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.Unk0 = bf.ReadUint32()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,29 @@ import (
|
||||
type OperateGuildAction uint8
|
||||
|
||||
const (
|
||||
OPERATE_GUILD_ACTION_DISBAND = 0x01
|
||||
OPERATE_GUILD_ACTION_APPLY = 0x02
|
||||
OPERATE_GUILD_ACTION_LEAVE = 0x03
|
||||
OPERATE_GUILD_DISBAND = 0x01
|
||||
OPERATE_GUILD_APPLY = 0x02
|
||||
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_FALSE = 0x08
|
||||
OPERATE_GUILD_ACTION_UPDATE_COMMENT = 0x09
|
||||
OPERATE_GUILD_ACTION_DONATE = 0x0a
|
||||
OPERATE_GUILD_ACTION_UPDATE_MOTTO = 0x0b
|
||||
OPERATE_GUILD_UPDATE_COMMENT = 0x09
|
||||
OPERATE_GUILD_DONATE_RANK = 0x0a
|
||||
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
|
||||
@@ -40,7 +55,7 @@ func (m *MsgMhfOperateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien
|
||||
m.GuildID = bf.ReadUint32()
|
||||
m.Action = OperateGuildAction(bf.ReadUint8())
|
||||
m.UnkData = bf.DataFromCurrent()
|
||||
|
||||
bf.Seek(int64(len(bf.Data()) - 2), 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,22 @@ import (
|
||||
"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
|
||||
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.
|
||||
func (m *MsgMhfOperateJoint) Opcode() network.PacketID {
|
||||
@@ -18,7 +32,13 @@ func (m *MsgMhfOperateJoint) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
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.
|
||||
|
||||
@@ -11,7 +11,8 @@ import (
|
||||
type OperateMailOperation uint8
|
||||
|
||||
const (
|
||||
OperateMailOperationDelete OperateMailOperation = 0x01
|
||||
OPERATE_MAIL_DELETE = 0x01
|
||||
OPERATE_MAIL_ACQUIRE_ITEM = 0x05
|
||||
)
|
||||
|
||||
// MsgMhfOprtMail represents the MSG_MHF_OPRT_MAIL
|
||||
@@ -19,7 +20,11 @@ type MsgMhfOprtMail struct {
|
||||
AckHandle uint32
|
||||
AccIndex uint8
|
||||
Index uint8
|
||||
Operation uint8
|
||||
Operation OperateMailOperation
|
||||
Unk0 uint8
|
||||
Data []byte
|
||||
Amount uint16
|
||||
ItemID uint16
|
||||
}
|
||||
|
||||
// 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.AccIndex = 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
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ type MsgMhfReadMail struct {
|
||||
|
||||
// This is the index within the current mail list
|
||||
Index uint8
|
||||
Unk0 uint16
|
||||
}
|
||||
|
||||
// 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.AccIndex = bf.ReadUint8()
|
||||
m.Index = bf.ReadUint8()
|
||||
m.Unk0 = bf.ReadUint16()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,16 @@ import (
|
||||
)
|
||||
|
||||
// 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.
|
||||
func (m *MsgMhfSendMail) Opcode() network.PacketID {
|
||||
@@ -18,7 +27,15 @@ func (m *MsgMhfSendMail) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
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.
|
||||
|
||||
@@ -9,7 +9,10 @@ import (
|
||||
)
|
||||
|
||||
// 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.
|
||||
func (m *MsgMhfSetGuildMissionTarget) Opcode() network.PacketID {
|
||||
@@ -18,7 +21,9 @@ func (m *MsgMhfSetGuildMissionTarget) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
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.
|
||||
|
||||
@@ -11,11 +11,12 @@ import (
|
||||
type GuildIconMsgPart struct {
|
||||
Index uint16
|
||||
ID uint16
|
||||
Unk0 uint8
|
||||
Page uint8
|
||||
Size uint8
|
||||
Rotation uint8
|
||||
Unk1 uint8
|
||||
Unk2 uint16
|
||||
Red uint8
|
||||
Green uint8
|
||||
Blue uint8
|
||||
PosX uint16
|
||||
PosY uint16
|
||||
}
|
||||
@@ -47,11 +48,12 @@ func (m *MsgMhfUpdateGuildIcon) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cl
|
||||
m.IconParts[i] = GuildIconMsgPart{
|
||||
Index: bf.ReadUint16(),
|
||||
ID: bf.ReadUint16(),
|
||||
Unk0: bf.ReadUint8(),
|
||||
Page: bf.ReadUint8(),
|
||||
Size: bf.ReadUint8(),
|
||||
Rotation: bf.ReadUint8(),
|
||||
Unk1: bf.ReadUint8(),
|
||||
Unk2: bf.ReadUint16(),
|
||||
Red: bf.ReadUint8(),
|
||||
Green: bf.ReadUint8(),
|
||||
Blue: bf.ReadUint8(),
|
||||
PosX: bf.ReadUint16(),
|
||||
PosY: bf.ReadUint16(),
|
||||
}
|
||||
|
||||
@@ -8,8 +8,21 @@ import (
|
||||
"github.com/Andoryuuta/byteframe"
|
||||
)
|
||||
|
||||
type Item struct{
|
||||
Unk0 uint32
|
||||
ItemId uint16
|
||||
Amount uint16
|
||||
Unk1 uint32
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (m *MsgMhfUpdateGuildItem) Opcode() network.PacketID {
|
||||
@@ -18,7 +31,20 @@ func (m *MsgMhfUpdateGuildItem) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
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.
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -9,7 +9,12 @@ import (
|
||||
)
|
||||
|
||||
// MsgMhfUpdateUnionItem represents the MSG_MHF_UPDATE_UNION_ITEM
|
||||
type MsgMhfUpdateUnionItem struct{}
|
||||
type MsgMhfUpdateUnionItem struct {
|
||||
AckHandle uint32
|
||||
Amount uint16
|
||||
Unk1 uint16 // 0x00 0x00
|
||||
Items []Item // Array of updated item IDs
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
func (m *MsgMhfUpdateUnionItem) Opcode() network.PacketID {
|
||||
@@ -18,7 +23,19 @@ func (m *MsgMhfUpdateUnionItem) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
func (m *MsgMhfUpdateUnionItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
return errors.New("NOT IMPLEMENTED")
|
||||
m.AckHandle = 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.
|
||||
|
||||
@@ -1,27 +1 @@
|
||||
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
|
||||
|
||||
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
|
||||
type MsgSysReserve205 struct{}
|
||||
type MsgSysReserve205 struct {
|
||||
AckHandle uint32
|
||||
Unk0 uint32
|
||||
Unk1 uint32
|
||||
Unk2 uint32
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
func (m *MsgSysReserve205) Opcode() network.PacketID {
|
||||
@@ -18,7 +23,11 @@ func (m *MsgSysReserve205) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
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.
|
||||
|
||||
@@ -841,10 +841,10 @@ func FromOpcode(opcode network.PacketID) MHFPacket {
|
||||
return &MsgMhfUpdateForceGuildRank{}
|
||||
case network.MSG_MHF_RESET_TITLE:
|
||||
return &MsgMhfResetTitle{}
|
||||
case network.MSG_SYS_reserve202:
|
||||
return &MsgSysReserve202{}
|
||||
case network.MSG_SYS_reserve203:
|
||||
return &MsgSysReserve203{}
|
||||
case network.MSG_MHF_ENUMERATE_GUILD_MESSAGE_BOARD:
|
||||
return &MsgMhfEnumerateGuildMessageBoard{}
|
||||
case network.MSG_MHF_UPDATE_GUILD_MESSAGE_BOARD:
|
||||
return &MsgMhfUpdateGuildMessageBoard{}
|
||||
case network.MSG_SYS_reserve204:
|
||||
return &MsgSysReserve204{}
|
||||
case network.MSG_SYS_reserve205:
|
||||
|
||||
@@ -423,8 +423,8 @@ const (
|
||||
MSG_SYS_reserve19F
|
||||
MSG_MHF_UPDATE_FORCE_GUILD_RANK
|
||||
MSG_MHF_RESET_TITLE
|
||||
MSG_SYS_reserve202
|
||||
MSG_SYS_reserve203
|
||||
MSG_MHF_ENUMERATE_GUILD_MESSAGE_BOARD
|
||||
MSG_MHF_UPDATE_GUILD_MESSAGE_BOARD
|
||||
MSG_SYS_reserve204
|
||||
MSG_SYS_reserve205
|
||||
MSG_SYS_reserve206
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,6 +3,7 @@ package channelserver
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"math/bits"
|
||||
"math/rand"
|
||||
@@ -74,16 +75,7 @@ func doAckSimpleFail(s *Session, ackHandle uint32, data []byte) {
|
||||
func updateRights(s *Session) {
|
||||
update := &mhfpacket.MsgSysUpdateRight{
|
||||
ClientRespAckHandle: 0,
|
||||
Unk1: 0x0E, //0e with normal sub 4e when having premium it's probably a bitfield?
|
||||
// 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
|
||||
Unk1: s.rights,
|
||||
Rights: []mhfpacket.ClientRight{
|
||||
{
|
||||
ID: 1,
|
||||
@@ -143,10 +135,25 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysLogin)
|
||||
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.Lock()
|
||||
s.Name = name
|
||||
s.charID = pkt.CharID0
|
||||
s.rights = rights
|
||||
s.Unlock()
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp
|
||||
@@ -158,7 +165,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 {
|
||||
panic(err)
|
||||
}
|
||||
@@ -191,6 +198,46 @@ func logoutPlayer(s *Session) {
|
||||
}
|
||||
|
||||
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) {}
|
||||
@@ -208,6 +255,8 @@ func handleMsgSysTime(s *Session, p mhfpacket.MHFPacket) {
|
||||
Timestamp: uint32(Time_Current_Adjusted().Unix()), // JP timezone
|
||||
}
|
||||
s.QueueSendMHF(resp)
|
||||
|
||||
s.notifyticker()
|
||||
}
|
||||
|
||||
func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -356,15 +405,92 @@ func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfEnumerateTitle(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateUnionItem)
|
||||
var boxContents []byte
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := s.server.db.QueryRow("SELECT item_box FROM users, characters WHERE characters.id = $1 AND users.id = characters.user_id", int(s.charID)).Scan(&boxContents)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to get shared 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())
|
||||
}
|
||||
|
||||
func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfUpdateUnionItem)
|
||||
// Get item cache from DB
|
||||
var boxContents []byte
|
||||
var oldItems []Item
|
||||
|
||||
func handleMsgMhfCreateJoint(s *Session, p mhfpacket.MHFPacket) {}
|
||||
err := s.server.db.QueryRow("SELECT item_box FROM users, characters WHERE characters.id = $1 AND users.id = characters.user_id", int(s.charID)).Scan(&boxContents)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to get shared 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])
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) {}
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {}
|
||||
// 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 users SET item_box = $1 FROM characters WHERE users.id = characters.user_id AND characters.id = $2", bf.Data(), int(s.charID))
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update shared item box contents in db", zap.Error(err))
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem)
|
||||
|
||||
@@ -2,8 +2,8 @@ package channelserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"math"
|
||||
|
||||
"github.com/Andoryuuta/byteframe"
|
||||
"github.com/Solenataris/Erupe/network/binpacket"
|
||||
@@ -22,6 +22,7 @@ const (
|
||||
const (
|
||||
BroadcastTypeTargeted = 0x01
|
||||
BroadcastTypeStage = 0x03
|
||||
BroadcastTypeRavi = 0x06
|
||||
BroadcastTypeWorld = 0x0a
|
||||
)
|
||||
|
||||
@@ -52,7 +53,6 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x03 && len(pkt.RawDataPayload) == 0x10 {
|
||||
tmp := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
|
||||
|
||||
if tmp.ReadUint16() == 0x0002 && tmp.ReadUint8() == 0x18 {
|
||||
_ = tmp.ReadBytes(9)
|
||||
tmp.SetLE()
|
||||
@@ -93,6 +93,11 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
s.server.BroadcastMHF(resp, s)
|
||||
case BroadcastTypeStage:
|
||||
s.stage.BroadcastMHF(resp, s)
|
||||
case BroadcastTypeRavi:
|
||||
if pkt.MessageType == 1 {
|
||||
session := s.server.semaphore["hs_l0u3B51J9k3"]
|
||||
(*session).BroadcastMHF(resp, s)
|
||||
}
|
||||
case BroadcastTypeTargeted:
|
||||
for _, targetID := range (*msgBinTargeted).TargetCharIDs {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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 ") {
|
||||
var x, y int16
|
||||
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())
|
||||
}
|
||||
|
||||
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) {}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package channelserver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -54,21 +55,46 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
s.logger.Info("Wrote recompressed savedata back to DB.")
|
||||
dumpSaveData(s, pkt.RawDataPayload, "")
|
||||
// Temporary server launcher response stuff
|
||||
// 0x1F715 Weapon Class
|
||||
// 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)
|
||||
|
||||
_, err = s.server.db.Exec("UPDATE characters SET weapon_type=$1 WHERE id=$2", uint16(decompressedData[128789]), s.charID)
|
||||
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 {
|
||||
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]))
|
||||
_, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterName, s.charID)
|
||||
if err != nil {
|
||||
@@ -77,6 +103,53 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
|
||||
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) {
|
||||
if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled {
|
||||
return
|
||||
|
||||
@@ -196,7 +196,10 @@ func handleMsgMhfAcquireUdItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
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) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetUdMyRanking)
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
"strings"
|
||||
"strconv"
|
||||
|
||||
"github.com/Andoryuuta/byteframe"
|
||||
"github.com/Solenataris/Erupe/common/bfutil"
|
||||
@@ -47,10 +49,11 @@ type Guild struct {
|
||||
SubMotto uint8 `db:"sub_motto"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
MemberCount uint16 `db:"member_count"`
|
||||
RP uint32 `db:"rp"`
|
||||
RankRP uint32 `db:"rank_rp"`
|
||||
EventRP uint32 `db:"event_rp"`
|
||||
Comment string `db:"comment"`
|
||||
FestivalColour FestivalColour `db:"festival_colour"`
|
||||
GuildHallType uint16 `db:"guild_hall"`
|
||||
Rank uint16 `db:"rank"`
|
||||
Icon *GuildIcon `db:"icon"`
|
||||
|
||||
GuildLeader
|
||||
@@ -64,8 +67,12 @@ type GuildLeader struct {
|
||||
type GuildIconPart struct {
|
||||
Index uint16
|
||||
ID uint16
|
||||
Page uint8
|
||||
Size uint8
|
||||
Rotation uint8
|
||||
Red uint8
|
||||
Green uint8
|
||||
Blue uint8
|
||||
PosX uint16
|
||||
PosY uint16
|
||||
}
|
||||
@@ -99,9 +106,11 @@ func (gi *GuildIcon) Value() (valuer driver.Value, err error) {
|
||||
}
|
||||
|
||||
const guildInfoSelectQuery = `
|
||||
SELECT g.id,
|
||||
SELECT
|
||||
g.id,
|
||||
g.name,
|
||||
g.rp,
|
||||
g.rank_rp,
|
||||
g.event_rp,
|
||||
g.main_motto,
|
||||
g.sub_motto,
|
||||
created_at,
|
||||
@@ -109,7 +118,14 @@ SELECT g.id,
|
||||
lc.name as leader_name,
|
||||
comment,
|
||||
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,
|
||||
(
|
||||
SELECT count(1) FROM guild_characters gc WHERE gc.guild_id = g.id
|
||||
@@ -328,28 +344,6 @@ func (guild *Guild) ArrangeCharacters(s *Session, charIDs []uint32) error {
|
||||
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) {
|
||||
row := s.server.db.QueryRowx(`
|
||||
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(
|
||||
"INSERT INTO guilds (name, leader_id, rp, guild_hall) VALUES ($1, $2, $3, $4) RETURNING id",
|
||||
guildName, s.charID, 48, 17,
|
||||
"INSERT INTO guilds (name, leader_id) VALUES ($1, $2) RETURNING id",
|
||||
guildName, s.charID,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@@ -617,7 +611,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
switch pkt.Action {
|
||||
case mhfpacket.OPERATE_GUILD_ACTION_DISBAND:
|
||||
case mhfpacket.OPERATE_GUILD_DISBAND:
|
||||
if guild.LeaderCharID != s.charID {
|
||||
s.logger.Warn(fmt.Sprintf("character '%d' is attempting to manage guild '%d' without permission", s.charID, guild.ID))
|
||||
return
|
||||
@@ -632,7 +626,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
bf.WriteUint32(uint32(response))
|
||||
case mhfpacket.OPERATE_GUILD_ACTION_APPLY:
|
||||
case mhfpacket.OPERATE_GUILD_APPLY:
|
||||
err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil)
|
||||
|
||||
if err != nil {
|
||||
@@ -641,7 +635,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
} else {
|
||||
bf.WriteUint32(guild.LeaderCharID)
|
||||
}
|
||||
case mhfpacket.OPERATE_GUILD_ACTION_LEAVE:
|
||||
case mhfpacket.OPERATE_GUILD_LEAVE:
|
||||
var err error
|
||||
|
||||
if characterGuildInfo.IsApplicant {
|
||||
@@ -651,24 +645,27 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
response := 0x01
|
||||
|
||||
if err != nil {
|
||||
// All successful acks return 0x01, assuming 0x00 is failure
|
||||
response = 0x00
|
||||
}
|
||||
|
||||
bf.WriteUint32(uint32(response))
|
||||
case mhfpacket.OPERATE_GUILD_ACTION_DONATE:
|
||||
err := handleOperateGuildActionDonate(s, guild, pkt, bf)
|
||||
|
||||
if err != nil {
|
||||
case mhfpacket.OPERATE_GUILD_DONATE_RANK:
|
||||
handleDonateRP(s, pkt, bf, guild, false)
|
||||
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_DENY:
|
||||
// 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
|
||||
}
|
||||
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE:
|
||||
handleAvoidLeadershipUpdate(s, pkt, true)
|
||||
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE:
|
||||
handleAvoidLeadershipUpdate(s, pkt, false)
|
||||
case mhfpacket.OPERATE_GUILD_ACTION_UPDATE_COMMENT:
|
||||
case mhfpacket.OPERATE_GUILD_UPDATE_COMMENT:
|
||||
pbf := byteframe.NewByteFrameFromBytes(pkt.UnkData)
|
||||
|
||||
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
|
||||
@@ -695,7 +692,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
bf.WriteUint32(0x00)
|
||||
case mhfpacket.OPERATE_GUILD_ACTION_UPDATE_MOTTO:
|
||||
case mhfpacket.OPERATE_GUILD_UPDATE_MOTTO:
|
||||
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
@@ -710,6 +707,26 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
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:
|
||||
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())
|
||||
}
|
||||
|
||||
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) {
|
||||
characterGuildData, err := GetCharacterGuildData(s, s.charID)
|
||||
|
||||
@@ -737,93 +783,6 @@ func handleAvoidLeadershipUpdate(s *Session, pkt *mhfpacket.MsgMhfOperateGuild,
|
||||
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) {
|
||||
pkt := p.(*mhfpacket.MsgMhfOperateGuildMember)
|
||||
|
||||
@@ -924,11 +883,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
bf.WriteUint32(guild.ID)
|
||||
bf.WriteUint32(guild.LeaderCharID)
|
||||
// Unk 0x09 = Guild Hall available, maybe guild hall type?
|
||||
// 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.Rank)
|
||||
bf.WriteUint16(guild.MemberCount)
|
||||
|
||||
bf.WriteUint8(guild.MainMotto)
|
||||
@@ -939,7 +894,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
if characterGuildData == nil || characterGuildData.IsApplicant {
|
||||
bf.WriteUint16(0x00)
|
||||
} else if characterGuildData.IsSubLeader() || guild.LeaderCharID == s.charID {
|
||||
} else if guild.LeaderCharID == s.charID {
|
||||
bf.WriteUint16(0x01)
|
||||
} else {
|
||||
bf.WriteUint16(0x02)
|
||||
@@ -955,13 +910,13 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteUint8(uint8(len(leaderName) + 1))
|
||||
bf.WriteBytes(guildName)
|
||||
bf.WriteBytes(guildComment)
|
||||
|
||||
bf.WriteUint8(FestivalColourCodes[guild.FestivalColour])
|
||||
|
||||
bf.WriteUint32(guild.RP)
|
||||
bf.WriteUint32(guild.RankRP)
|
||||
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, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x18, 0xBD}) // Level 17 guild's version
|
||||
bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00}) // Unk
|
||||
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
|
||||
for i := 0; i < 3; i++ {
|
||||
@@ -983,30 +938,6 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
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)
|
||||
|
||||
if err != nil {
|
||||
@@ -1028,15 +959,6 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
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
|
||||
bf.WriteUint8(0x00)
|
||||
|
||||
@@ -1046,11 +968,12 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
for _, p := range guild.Icon.Parts {
|
||||
bf.WriteUint16(p.Index)
|
||||
bf.WriteUint16(p.ID)
|
||||
bf.WriteUint8(0x01)
|
||||
bf.WriteUint8(p.Page)
|
||||
bf.WriteUint8(p.Size)
|
||||
bf.WriteUint8(p.Rotation)
|
||||
bf.WriteUint8(0xFF)
|
||||
bf.WriteUint16(0xFFFF)
|
||||
bf.WriteUint8(p.Red)
|
||||
bf.WriteUint8(p.Green)
|
||||
bf.WriteUint8(p.Blue)
|
||||
bf.WriteUint16(p.PosX)
|
||||
bf.WriteUint16(p.PosY)
|
||||
}
|
||||
@@ -1073,24 +996,123 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateGuild)
|
||||
|
||||
var guilds []*Guild
|
||||
var rows *sqlx.Rows
|
||||
var err error
|
||||
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
|
||||
|
||||
switch pkt.Type {
|
||||
case mhfpacket.ENUMERATE_GUILD_TYPE_NAME:
|
||||
// I have no idea if is really little endian, but it seems too weird to have a random static
|
||||
// 0x00 before the string
|
||||
searchTermLength := binary.LittleEndian.Uint16(pkt.RawDataPayload[9:11])
|
||||
searchTerm := pkt.RawDataPayload[11 : 11+searchTermLength]
|
||||
|
||||
case mhfpacket.ENUMERATE_GUILD_TYPE_GUILD_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)
|
||||
}
|
||||
|
||||
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:
|
||||
panic(fmt.Sprintf("no handler for guild search type '%d'", pkt.Type))
|
||||
}
|
||||
@@ -1100,7 +1122,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
return
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf = byteframe.NewByteFrame()
|
||||
bf.WriteUint16(uint16(len(guilds)))
|
||||
|
||||
for _, guild := range guilds {
|
||||
@@ -1113,12 +1135,12 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteUint16(guild.MemberCount)
|
||||
bf.WriteUint8(0x00) // Unk
|
||||
bf.WriteUint8(0x00) // Unk
|
||||
bf.WriteUint16(0x00) // Rank
|
||||
bf.WriteUint16(guild.Rank)
|
||||
bf.WriteUint32(uint32(guild.CreatedAt.Unix()))
|
||||
bf.WriteUint8(uint8(len(guildName)))
|
||||
bf.WriteBytes(guildName)
|
||||
bf.WriteUint8(uint8(len(leaderName)))
|
||||
bf.WriteBytes(leaderName)
|
||||
bf.WriteUint8(uint8(len(guildName)+1))
|
||||
bf.WriteNullTerminatedBytes(guildName)
|
||||
bf.WriteUint8(uint8(len(leaderName)+1))
|
||||
bf.WriteNullTerminatedBytes(leaderName)
|
||||
bf.WriteUint8(0x01) // Unk
|
||||
}
|
||||
|
||||
@@ -1203,12 +1225,14 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {
|
||||
name := s.clientContext.StrConv.MustEncode(member.Name)
|
||||
|
||||
bf.WriteUint32(member.CharID)
|
||||
|
||||
// Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999
|
||||
bf.WriteUint16(member.Exp) // Rank flags
|
||||
bf.WriteUint16(0x00) // Grank
|
||||
bf.WriteUint16(0x00) // Unk
|
||||
bf.WriteUint16(0x00) // Some rank?
|
||||
bf.WriteUint16(member.HRP)
|
||||
bf.WriteUint16(member.GR)
|
||||
bf.WriteUint16(member.WeaponID)
|
||||
if member.WeaponType == 1 || member.WeaponType == 5 || member.WeaponType == 10 { // If weapon is ranged
|
||||
bf.WriteUint16(0x0700)
|
||||
} else {
|
||||
bf.WriteUint16(0x0600)
|
||||
}
|
||||
bf.WriteUint8(member.OrderIndex)
|
||||
bf.WriteUint16(uint16(len(name) + 1))
|
||||
bf.WriteNullTerminatedBytes(name)
|
||||
@@ -1298,10 +1322,95 @@ func handleMsgMhfGetGuildTargetMemberNum(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
func handleMsgMhfEnumerateGuildItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
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) {
|
||||
@@ -1337,8 +1446,12 @@ func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) {
|
||||
icon.Parts[i] = GuildIconPart{
|
||||
Index: p.Index,
|
||||
ID: p.ID,
|
||||
Page: p.Page,
|
||||
Size: p.Size,
|
||||
Rotation: p.Rotation,
|
||||
Red: p.Red,
|
||||
Green: p.Green,
|
||||
Blue: p.Blue,
|
||||
PosX: p.PosX,
|
||||
PosY: p.PosY,
|
||||
}
|
||||
@@ -1434,16 +1547,182 @@ func handleMsgMhfGetGuildWeeklyBonusActiveCount(s *Session, p mhfpacket.MHFPacke
|
||||
|
||||
func handleMsgMhfGuildHuntdata(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGuildHuntdata)
|
||||
data := []byte{0x01, 0xFE}
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
// "Enumrate_guild_msg_board"
|
||||
func handleMsgSysReserve202(s *Session, p mhfpacket.MHFPacket) {}
|
||||
type MessageBoardPost struct {
|
||||
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 handleMsgSysReserve203(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysReserve203)
|
||||
func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateGuildMessageBoard)
|
||||
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})
|
||||
}
|
||||
|
||||
@@ -1451,7 +1730,12 @@ func handleMsgMhfEntryRookieGuild(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) {}
|
||||
|
||||
@@ -1461,7 +1745,10 @@ func handleMsgMhfChargeGuildAdventure(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) {}
|
||||
|
||||
@@ -1477,4 +1764,8 @@ func handleMsgMhfOperationInvGuild(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"`
|
||||
AvoidLeadership bool `db:"avoid_leadership"`
|
||||
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 {
|
||||
@@ -53,7 +56,10 @@ SELECT g.id as guild_id,
|
||||
coalesce(gc.order_index, 0) as order_index,
|
||||
c.last_login,
|
||||
coalesce(gc.avoid_leadership, false) as avoid_leadership,
|
||||
c.exp,
|
||||
c.hrp,
|
||||
c.gr,
|
||||
c.weapon_id,
|
||||
c.weapon_type,
|
||||
character.is_applicant,
|
||||
CASE WHEN g.leader_id = c.id THEN 1 ELSE 0 END as is_leader
|
||||
FROM (
|
||||
|
||||
@@ -93,6 +93,23 @@ func (m *Mail) MarkDeleted(s *Session) error {
|
||||
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) {
|
||||
rows, err := s.server.db.Queryx(`
|
||||
SELECT
|
||||
@@ -101,6 +118,7 @@ func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) {
|
||||
m.recipient_id,
|
||||
m.subject,
|
||||
m.read,
|
||||
m.attached_item_received,
|
||||
m.attached_item,
|
||||
m.attached_item_amount,
|
||||
m.created_at,
|
||||
@@ -147,6 +165,7 @@ func GetMailByID(s *Session, ID int) (*Mail, error) {
|
||||
m.subject,
|
||||
m.read,
|
||||
m.body,
|
||||
m.attached_item_received,
|
||||
m.attached_item,
|
||||
m.attached_item_amount,
|
||||
m.created_at,
|
||||
@@ -235,9 +254,11 @@ func handleMsgMhfReadMail(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
_ = mail.MarkRead(s)
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
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) {
|
||||
@@ -295,10 +316,10 @@ func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
msg.WriteUint8(flags)
|
||||
msg.WriteBool(itemAttached)
|
||||
msg.WriteUint8(uint8(len(subjectBytes)))
|
||||
msg.WriteUint8(uint8(len(senderNameBytes)))
|
||||
msg.WriteBytes(subjectBytes)
|
||||
msg.WriteBytes(senderNameBytes)
|
||||
msg.WriteUint8(uint8(len(subjectBytes)+1))
|
||||
msg.WriteUint8(uint8(len(senderNameBytes)+1))
|
||||
msg.WriteNullTerminatedBytes(subjectBytes)
|
||||
msg.WriteNullTerminatedBytes(senderNameBytes)
|
||||
|
||||
if itemAttached {
|
||||
msg.WriteInt16(m.AttachedItemAmount)
|
||||
@@ -320,9 +341,14 @@ func handleMsgMhfOprtMail(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
switch mhfpacket.OperateMailOperation(pkt.Operation) {
|
||||
case mhfpacket.OperateMailOperationDelete:
|
||||
case mhfpacket.OPERATE_MAIL_DELETE:
|
||||
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 {
|
||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
||||
panic(err)
|
||||
@@ -332,4 +358,34 @@ func handleMsgMhfOprtMail(s *Session, p mhfpacket.MHFPacket) {
|
||||
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) {
|
||||
//pkt := p.(*mhfpacket.MsgSysDeleteSemaphore)
|
||||
|
||||
s.semaphore.Lock()
|
||||
pkt := p.(*mhfpacket.MsgSysDeleteSemaphore)
|
||||
sem := pkt.AckHandle
|
||||
if s.server.semaphore != nil {
|
||||
s.server.semaphoreLock.Lock()
|
||||
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)
|
||||
}
|
||||
s.semaphore.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
s.server.semaphoreLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -49,11 +71,33 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
|
||||
s.logger.Info("IS ALREADY EXIST !")
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0F, 0x00, 0x1D})
|
||||
} 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
|
||||
s.Lock()
|
||||
s.semaphore = newSemaphore
|
||||
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 {
|
||||
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) {
|
||||
//pkt := p.(*mhfpacket.MsgSysReleaseSemaphore)
|
||||
for _, session := range s.server.sessions {
|
||||
session.semaphore.Lock()
|
||||
for id := range session.server.semaphore {
|
||||
if _, exists := s.server.semaphore["hs_l0u3B51J9k3"]; exists {
|
||||
reset := len(s.server.semaphore["hs_l0u3B51J9k3"].reservedClientSlots)
|
||||
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)
|
||||
if id == "hs_l0u3B51J9k3" {
|
||||
delete(s.server.semaphore[id].clients, s)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
session.semaphore.Unlock()
|
||||
}
|
||||
//data, _ := hex.DecodeString("000180e703000d443b37ff006d00131809000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010627426400a936a93600000100cf330600cc31cc31d431000025000000000000000000010218330600bd3cbd3cbd3c01032c280600ee3dee3da9360104f3300600d231a936a93601054a310600e23ae23ae23a00000d0000000000004d814c0000000003008501d723b7334001e7038b3fd437d516113505000000e7030001000002000203000000000000fafafafafafafafafafafafafafa000000000000ecb2000060da0000000000000000000000000000000000000000000000000000000000000000000000000000181818187e2d00003b31702d662d402e000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
//doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
s.server.semaphoreLock.Unlock()
|
||||
}
|
||||
|
||||
func handleMsgSysCheckSemaphore(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
@@ -429,8 +429,8 @@ func init() {
|
||||
handlerTable[network.MSG_SYS_reserve19F] = handleMsgSysReserve19F
|
||||
handlerTable[network.MSG_MHF_UPDATE_FORCE_GUILD_RANK] = handleMsgMhfUpdateForceGuildRank
|
||||
handlerTable[network.MSG_MHF_RESET_TITLE] = handleMsgMhfResetTitle
|
||||
handlerTable[network.MSG_SYS_reserve202] = handleMsgSysReserve202
|
||||
handlerTable[network.MSG_SYS_reserve203] = handleMsgSysReserve203
|
||||
handlerTable[network.MSG_MHF_ENUMERATE_GUILD_MESSAGE_BOARD] = handleMsgMhfEnumerateGuildMessageBoard
|
||||
handlerTable[network.MSG_MHF_UPDATE_GUILD_MESSAGE_BOARD] = handleMsgMhfUpdateGuildMessageBoard
|
||||
handlerTable[network.MSG_SYS_reserve204] = handleMsgSysReserve204
|
||||
handlerTable[network.MSG_SYS_reserve205] = handleMsgSysReserve205
|
||||
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 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) {}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"github.com/Andoryuuta/byteframe"
|
||||
"github.com/Solenataris/Erupe/network/mhfpacket"
|
||||
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -11,6 +14,10 @@ type Semaphore struct {
|
||||
// Stage ID 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.
|
||||
reservedClientSlots map[uint32]interface{}
|
||||
|
||||
@@ -22,8 +29,46 @@ type Semaphore struct {
|
||||
func NewSemaphore(ID string, MaxPlayers uint16) *Semaphore {
|
||||
s := &Semaphore{
|
||||
id_semaphore: ID,
|
||||
clients: make(map[*Session]uint32),
|
||||
reservedClientSlots: make(map[uint32]interface{}),
|
||||
maxPlayers: MaxPlayers,
|
||||
}
|
||||
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.
|
||||
charID uint32
|
||||
logKey []byte
|
||||
sessionStart int64
|
||||
rights uint32
|
||||
|
||||
semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet.
|
||||
|
||||
@@ -62,6 +64,7 @@ func NewSession(server *Server, conn net.Conn) *Session {
|
||||
Encoding: japanese.ShiftJIS,
|
||||
},
|
||||
},
|
||||
sessionStart: Time_Current_Adjusted().Unix(),
|
||||
stageMoveStack: stringstack.New(),
|
||||
}
|
||||
return s
|
||||
@@ -177,6 +180,7 @@ func (s *Session) handlePacketGroup(pktGroup []byte) {
|
||||
opcode != network.MSG_SYS_PING &&
|
||||
opcode != network.MSG_SYS_NOP &&
|
||||
opcode != network.MSG_SYS_TIME &&
|
||||
opcode != network.MSG_SYS_POSITION_OBJECT &&
|
||||
opcode != network.MSG_SYS_EXTEND_THRESHOLD {
|
||||
fmt.Printf("[%s] send to Server\n", s.Name)
|
||||
fmt.Printf("Opcode: %s\n", opcode)
|
||||
|
||||
@@ -26,9 +26,9 @@ func (s *Server) newUserChara(username string) error {
|
||||
|
||||
_, err = s.db.Exec(`
|
||||
INSERT INTO characters (
|
||||
user_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)
|
||||
VALUES($1, False, True, 0, True, '', '', 0, 0, 0, 0, 0, $2)`,
|
||||
user_id, is_female, is_new_character, name, unk_desc_string,
|
||||
hrp, gr, weapon_type, last_login)
|
||||
VALUES($1, False, True, '', '', 1, 0, 0, $2)`,
|
||||
id,
|
||||
uint32(time.Now().Unix()),
|
||||
)
|
||||
@@ -60,9 +60,9 @@ func (s *Server) registerDBAccount(username string, password string) error {
|
||||
// Create a base new character.
|
||||
_, err = s.db.Exec(`
|
||||
INSERT INTO characters (
|
||||
user_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)
|
||||
VALUES($1, False, True, 0, True, '', '', 0, 0, 0, 0, 0, $2)`,
|
||||
user_id, is_female, is_new_character, name, unk_desc_string,
|
||||
hrp, gr, weapon_type, last_login)
|
||||
VALUES($1, False, True, '', '', 1, 0, 0, $2)`,
|
||||
id,
|
||||
uint32(time.Now().Unix()),
|
||||
)
|
||||
@@ -77,21 +77,17 @@ type character struct {
|
||||
ID uint32 `db:"id"`
|
||||
IsFemale bool `db:"is_female"`
|
||||
IsNewCharacter bool `db:"is_new_character"`
|
||||
SmallGRLevel uint8 `db:"small_gr_level"`
|
||||
GROverrideMode bool `db:"gr_override_mode"`
|
||||
Name string `db:"name"`
|
||||
UnkDescString string `db:"unk_desc_string"`
|
||||
GROverrideLevel uint16 `db:"gr_override_level"`
|
||||
GROverrideUnk0 uint8 `db:"gr_override_unk0"`
|
||||
GROverrideUnk1 uint8 `db:"gr_override_unk1"`
|
||||
Exp uint16 `db:"exp"`
|
||||
Weapon uint16 `db:"weapon"`
|
||||
HRP uint16 `db:"hrp"`
|
||||
GR uint16 `db:"gr"`
|
||||
WeaponType uint16 `db:"weapon_type"`
|
||||
LastLogin uint32 `db:"last_login"`
|
||||
}
|
||||
|
||||
func (s *Server) getCharactersForUser(uid int) ([]character, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package signserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/Andoryuuta/byteframe"
|
||||
"go.uber.org/zap"
|
||||
@@ -32,6 +34,15 @@ func makeSignInFailureResp(respID RespID) []byte {
|
||||
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 {
|
||||
// Get the characters from the DB.
|
||||
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))
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
token := randSeq(16)
|
||||
// TODO: register token to db, users table
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
bf.WriteUint8(1) // resp_code
|
||||
@@ -46,7 +61,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
||||
bf.WriteUint8(4) // entrance server count
|
||||
bf.WriteUint8(uint8(len(chars))) // character count
|
||||
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)
|
||||
uint8PascalString(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.HostIP, s.server.erupeConfig.Entrance.Port))
|
||||
uint8PascalString(bf, "")
|
||||
@@ -60,24 +75,18 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
||||
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.MaxLauncherHR {
|
||||
bf.WriteUint16(999)
|
||||
} else {
|
||||
bf.WriteUint16(char.Exp)
|
||||
bf.WriteUint16(char.HRP)
|
||||
}
|
||||
|
||||
bf.WriteUint16(char.Weapon) // Weapon, 0-13.
|
||||
bf.WriteUint16(char.WeaponType) // Weapon, 0-13.
|
||||
bf.WriteUint32(char.LastLogin) // Last login date, unix timestamp in seconds.
|
||||
bf.WriteBool(char.IsFemale) // Sex, 0=male, 1=female.
|
||||
bf.WriteBool(char.IsNewCharacter) // Is new character, 1 replaces character name with ?????.
|
||||
bf.WriteUint8(char.SmallGRLevel) // GR level if grMode == 0
|
||||
bf.WriteBool(char.GROverrideMode) // GR mode.
|
||||
bf.WriteUint8(0) // Old GR
|
||||
bf.WriteBool(true) // Use uint16 GR, no reason not to
|
||||
bf.WriteBytes(paddedString(char.Name, 16)) // Character name
|
||||
bf.WriteBytes(paddedString(char.UnkDescString, 32)) // unk str
|
||||
if char.GROverrideMode {
|
||||
bf.SetLE()
|
||||
bf.WriteUint16(char.GROverrideLevel) // GR level override.
|
||||
bf.SetBE()
|
||||
bf.WriteUint8(char.GROverrideUnk0) // unk
|
||||
bf.WriteUint8(char.GROverrideUnk1) // unk
|
||||
}
|
||||
bf.WriteUint16(char.GR)
|
||||
bf.WriteUint16(0) // Unk
|
||||
}
|
||||
|
||||
bf.WriteUint8(0) // friends_list_count
|
||||
|
||||
@@ -72,11 +72,20 @@
|
||||
</div>
|
||||
<ul class="article">
|
||||
<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="body">
|
||||
<a
|
||||
href="javascript:toggleModal('openLink',"https://discord.com/channels/368424389416583169/929509970624532511/964339905364918272");"
|
||||
onclick="soundOk()">Launcher Patch v1.0 Released!
|
||||
href="javascript:toggleModal('openLink',"https://discord.com/channels/368424389416583169/929509970624532511/969286397301248050");"
|
||||
onclick="soundOk()">Launcher Patch V1.0 Released!
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
@@ -92,7 +101,7 @@
|
||||
<div class="body">
|
||||
<a
|
||||
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>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -108,7 +108,7 @@ function createCharItem(name, uid, weapon, hr, gr, date, sex) {
|
||||
icon = 'img/icons/ss.png';
|
||||
break;
|
||||
case '双剣':
|
||||
weapon = 'Dual Blades';
|
||||
weapon = 'Dual Swords';
|
||||
icon = 'img/icons/db.png';
|
||||
break;
|
||||
case '大剣':
|
||||
@@ -116,7 +116,7 @@ function createCharItem(name, uid, weapon, hr, gr, date, sex) {
|
||||
icon = 'img/icons/gs.png';
|
||||
break;
|
||||
case '太刀':
|
||||
weapon = 'Long Sword';
|
||||
weapon = 'Longsword';
|
||||
icon = 'img/icons/ls.png';
|
||||
break;
|
||||
case 'ハンマー':
|
||||
@@ -139,7 +139,7 @@ function createCharItem(name, uid, weapon, hr, gr, date, sex) {
|
||||
weapon = 'Tonfa';
|
||||
icon = 'img/icons/tf.png';
|
||||
break;
|
||||
case 'スラッシュアックスF':
|
||||
case 'スラッシュアックスF':
|
||||
weapon = 'Switch Axe F';
|
||||
icon = 'img/icons/sa.png';
|
||||
break;
|
||||
@@ -272,7 +272,7 @@ function doLogin(option) {
|
||||
addLog('Creating new character...', 'normal');
|
||||
window.external.loginCog(username+'+', password, password);
|
||||
} else {
|
||||
window.external.loginCog(username, password, password);
|
||||
window.external.loginCog(username, password, 'test');
|
||||
}
|
||||
} catch (e) {
|
||||
addLog('Error on loginCog: '+e, 'error');
|
||||
|
||||
Reference in New Issue
Block a user