diff --git a/config.json b/config.json
index 5f04778c1..98ea1c0ef 100644
--- a/config.json
+++ b/config.json
@@ -4,7 +4,6 @@
"DisableSoftCrash": false,
"devmode": true,
"devmodeoptions": {
- "serverName" : "",
"EnableLauncherServer": false,
"hideLoginNotice": false,
"loginNotice": "
Welcome to Erupe SU9.1 Beta!
Erupe is experimental software, we are not liable for any
issues caused by installing the software!
■Report bugs on Discord!
■Test everything!
■Don't talk to softlocking NPCs!
■Fork the code on GitHub!
Thank you to all of the contributors,
this wouldn't exist without you.",
@@ -47,6 +46,10 @@
"name": "Reload",
"enabled": true,
"prefix": "!reload"
+ }, {
+ "name": "KeyQuest",
+ "enabled": false,
+ "prefix": "!kqf"
}
],
"database": {
diff --git a/config/config.go b/config/config.go
index 5355b6e42..c90550f53 100644
--- a/config/config.go
+++ b/config/config.go
@@ -29,13 +29,11 @@ type Config struct {
// DevModeOptions holds various debug/temporary options for use while developing Erupe.
type DevModeOptions struct {
- ServerName string // To get specific instance server about (Current Players/Event Week)
EnableLauncherServer bool // Enables the launcher server to be served on port 80
HideLoginNotice bool // Hide the Erupe notice on login
LoginNotice string // MHFML string of the login notice displayed
CleanDB bool // Automatically wipes the DB on server reset.
- MaxLauncherHR bool // Sets the HR returned in the launcher to HR9 so that you can join non-beginner worlds.
- FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages
+ MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds.
LogInboundMessages bool // Log all messages sent to the server
LogOutboundMessages bool // Log all messages sent to the clients
MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled
@@ -58,10 +56,7 @@ type SaveDumpOptions struct {
type Discord struct {
Enabled bool
BotToken string
- ServerID string
RealtimeChannelID string
- DevRoles []string
- DevMode bool
}
// Command is a channelserver chat command
diff --git a/network/mhfpacket/msg_mhf_enumerate_house.go b/network/mhfpacket/msg_mhf_enumerate_house.go
index 9bd1f30ef..da6a25de7 100644
--- a/network/mhfpacket/msg_mhf_enumerate_house.go
+++ b/network/mhfpacket/msg_mhf_enumerate_house.go
@@ -29,8 +29,10 @@ func (m *MsgMhfEnumerateHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli
m.CharID = bf.ReadUint32()
m.Method = bf.ReadUint8()
m.Unk = bf.ReadUint16()
- _ = bf.ReadUint8() // len
- m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
+ lenName := bf.ReadUint8()
+ if lenName > 0 {
+ m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
+ }
return nil
}
diff --git a/network/mhfpacket/msg_mhf_get_gem_info.go b/network/mhfpacket/msg_mhf_get_gem_info.go
index 516aea6be..36eb7948e 100644
--- a/network/mhfpacket/msg_mhf_get_gem_info.go
+++ b/network/mhfpacket/msg_mhf_get_gem_info.go
@@ -1,15 +1,19 @@
package mhfpacket
-import (
- "errors"
+import (
+ "errors"
- "erupe-ce/network/clientctx"
- "erupe-ce/network"
"erupe-ce/common/byteframe"
+ "erupe-ce/network"
+ "erupe-ce/network/clientctx"
)
// MsgMhfGetGemInfo represents the MSG_MHF_GET_GEM_INFO
-type MsgMhfGetGemInfo struct{}
+type MsgMhfGetGemInfo struct {
+ AckHandle uint32
+ Unk uint32
+ Unk1 []byte
+}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfGetGemInfo) Opcode() network.PacketID {
@@ -18,7 +22,10 @@ func (m *MsgMhfGetGemInfo) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfGetGemInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
- return errors.New("NOT IMPLEMENTED")
+ m.AckHandle = bf.ReadUint32()
+ m.Unk = bf.ReadUint32()
+ m.Unk1 = bf.ReadBytes(24)
+ return nil
}
// Build builds a binary packet from the current data.
diff --git a/network/mhfpacket/msg_mhf_get_tower_info.go b/network/mhfpacket/msg_mhf_get_tower_info.go
index 37ac8417b..a0b686485 100644
--- a/network/mhfpacket/msg_mhf_get_tower_info.go
+++ b/network/mhfpacket/msg_mhf_get_tower_info.go
@@ -1,11 +1,11 @@
package mhfpacket
-import (
- "errors"
+import (
+ "errors"
- "erupe-ce/network/clientctx"
- "erupe-ce/network"
"erupe-ce/common/byteframe"
+ "erupe-ce/network"
+ "erupe-ce/network/clientctx"
)
// The server sends different responses based on these values.
@@ -13,7 +13,7 @@ const (
TowerInfoTypeUnk0 = iota
TowerInfoTypeTowerRankPoint
TowerInfoTypeGetOwnTowerSkill
- TowerInfoTypeUnk3
+ TowerInfoTypeGetOwnTowerLevelV3
TowerInfoTypeTowerTouhaHistory
TowerInfoTypeUnk5
)
diff --git a/network/mhfpacket/msg_mhf_info_joint.go b/network/mhfpacket/msg_mhf_info_joint.go
index 6426b9211..17e468c7c 100644
--- a/network/mhfpacket/msg_mhf_info_joint.go
+++ b/network/mhfpacket/msg_mhf_info_joint.go
@@ -1,15 +1,19 @@
package mhfpacket
-import (
- "errors"
+import (
+ "errors"
- "erupe-ce/network/clientctx"
- "erupe-ce/network"
"erupe-ce/common/byteframe"
+ "erupe-ce/network"
+ "erupe-ce/network/clientctx"
)
// MsgMhfInfoJoint represents the MSG_MHF_INFO_JOINT
-type MsgMhfInfoJoint struct{}
+type MsgMhfInfoJoint struct {
+ AckHandle uint32
+ AllianceID uint32
+ Unk uint32
+}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfInfoJoint) Opcode() network.PacketID {
@@ -18,7 +22,10 @@ func (m *MsgMhfInfoJoint) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfInfoJoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
- return errors.New("NOT IMPLEMENTED")
+ m.AckHandle = bf.ReadUint32()
+ m.AllianceID = bf.ReadUint32()
+ m.Unk = bf.ReadUint32()
+ return nil
}
// Build builds a binary packet from the current data.
diff --git a/network/mhfpacket/msg_mhf_operate_guild.go b/network/mhfpacket/msg_mhf_operate_guild.go
index ea986cda2..f141796cb 100644
--- a/network/mhfpacket/msg_mhf_operate_guild.go
+++ b/network/mhfpacket/msg_mhf_operate_guild.go
@@ -1,39 +1,39 @@
package mhfpacket
import (
- "errors"
+ "errors"
- "erupe-ce/network/clientctx"
- "erupe-ce/network"
"erupe-ce/common/byteframe"
+ "erupe-ce/network"
+ "erupe-ce/network/clientctx"
)
type OperateGuildAction uint8
const (
- 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_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
+ 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_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
+ OPERATE_GUILD_EVENT_EXCHANGE = 0x16
+ 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
@@ -41,7 +41,8 @@ type MsgMhfOperateGuild struct {
AckHandle uint32
GuildID uint32
Action OperateGuildAction
- UnkData []byte
+ Data1 *byteframe.ByteFrame
+ Data2 *byteframe.ByteFrame
}
// Opcode returns the ID associated with this packet type.
@@ -54,8 +55,9 @@ func (m *MsgMhfOperateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien
m.AckHandle = bf.ReadUint32()
m.GuildID = bf.ReadUint32()
m.Action = OperateGuildAction(bf.ReadUint8())
- m.UnkData = bf.DataFromCurrent()
- bf.Seek(int64(len(bf.Data()) - 2), 0)
+ dataLen := uint(bf.ReadUint8())
+ m.Data1 = byteframe.NewByteFrameFromBytes(bf.ReadBytes(4))
+ m.Data2 = byteframe.NewByteFrameFromBytes(bf.ReadBytes(dataLen))
return nil
}
diff --git a/network/mhfpacket/msg_mhf_operate_joint.go b/network/mhfpacket/msg_mhf_operate_joint.go
index 9733a2278..1fa360d01 100644
--- a/network/mhfpacket/msg_mhf_operate_joint.go
+++ b/network/mhfpacket/msg_mhf_operate_joint.go
@@ -1,28 +1,28 @@
package mhfpacket
import (
- "errors"
+ "errors"
- "erupe-ce/network/clientctx"
- "erupe-ce/network"
"erupe-ce/common/byteframe"
+ "erupe-ce/network"
+ "erupe-ce/network/clientctx"
)
type OperateJointAction uint8
const (
- OPERATE_JOINT_DISBAND = 0x01
- OPERATE_JOINT_LEAVE = 0x03
- OPERATE_JOINT_KICK = 0x09
+ OPERATE_JOINT_DISBAND = 0x01
+ OPERATE_JOINT_LEAVE = 0x03
+ OPERATE_JOINT_KICK = 0x09
)
// MsgMhfOperateJoint represents the MSG_MHF_OPERATE_JOINT
type MsgMhfOperateJoint struct {
- AckHandle uint32
- AllianceID uint32
- GuildID uint32
- Action OperateJointAction
- UnkData []byte
+ AckHandle uint32
+ AllianceID uint32
+ GuildID uint32
+ Action OperateJointAction
+ UnkData *byteframe.ByteFrame
}
// Opcode returns the ID associated with this packet type.
@@ -32,13 +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 {
- 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
+ m.AckHandle = bf.ReadUint32()
+ m.AllianceID = bf.ReadUint32()
+ m.GuildID = bf.ReadUint32()
+ m.Action = OperateJointAction(bf.ReadUint8())
+ m.UnkData = byteframe.NewByteFrameFromBytes(bf.DataFromCurrent())
+ bf.Seek(int64(len(bf.Data())-2), 0)
+ return nil
}
// Build builds a binary packet from the current data.
diff --git a/patch-schema/persistent-house.sql b/patch-schema/persistent-house.sql
new file mode 100644
index 000000000..43a02da91
--- /dev/null
+++ b/patch-schema/persistent-house.sql
@@ -0,0 +1,37 @@
+BEGIN;
+
+CREATE TABLE IF NOT EXISTS public.user_binary
+(
+ id serial NOT NULL PRIMARY KEY,
+ type2 bytea,
+ type3 bytea,
+ house_tier bytea,
+ house_state int,
+ house_password text,
+ house_data bytea,
+ house_furniture bytea,
+ bookshelf bytea,
+ gallery bytea,
+ tore bytea,
+ garden bytea,
+ mission bytea
+);
+
+-- Create entries for existing users
+INSERT INTO public.user_binary (id) SELECT c.id FROM characters c;
+
+-- Copy existing data
+UPDATE public.user_binary
+ SET house_furniture = (SELECT house FROM characters WHERE user_binary.id = characters.id);
+
+UPDATE public.user_binary
+ SET mission = (SELECT trophy FROM characters WHERE user_binary.id = characters.id);
+
+-- Drop old data location
+ALTER TABLE public.characters
+ DROP COLUMN house;
+
+ALTER TABLE public.characters
+ DROP COLUMN trophy;
+
+END;
\ No newline at end of file
diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go
index 6c49ba0af..2e348c138 100644
--- a/server/channelserver/handlers.go
+++ b/server/channelserver/handlers.go
@@ -235,17 +235,11 @@ func logoutPlayer(s *Session) {
saveData, err := GetCharacterSaveData(s, s.charID)
if err != nil {
- panic(err)
+ s.logger.Error("Failed to get savedata")
+ return
}
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()
- }
+ saveData.Save(s)
}
func handleMsgSysSetStatus(s *Session, p mhfpacket.MHFPacket) {}
@@ -1749,6 +1743,7 @@ func handleMsgMhfUpdateEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {
byteInd := (bit / 8)
bitInByte := bit % 8
data[startByte+byteInd] |= bits.Reverse8((1 << uint(bitInByte)))
+ dumpSaveData(s, data, "skinhist")
_, err = s.server.db.Exec("UPDATE characters SET skin_hist=$1 WHERE id=$2", data, s.charID)
if err != nil {
panic(err)
@@ -1778,6 +1773,7 @@ func handleMsgMhfGetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSetEnhancedMinidata)
+ dumpSaveData(s, pkt.RawDataPayload, "minidata")
_, err := s.server.db.Exec("UPDATE characters SET minidata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
if err != nil {
s.logger.Fatal("Failed to update minidata in db", zap.Error(err))
diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go
index 6910a01ae..3791ea241 100644
--- a/server/channelserver/handlers_cast_binary.go
+++ b/server/channelserver/handlers_cast_binary.go
@@ -1,17 +1,17 @@
package channelserver
import (
+ "encoding/hex"
+ "erupe-ce/common/byteframe"
+ "erupe-ce/config"
+ "erupe-ce/network/binpacket"
+ "erupe-ce/network/mhfpacket"
"fmt"
"math"
"math/rand"
"strings"
"time"
- "erupe-ce/common/byteframe"
- "erupe-ce/config"
- "erupe-ce/network/binpacket"
- "erupe-ce/network/mhfpacket"
-
"go.uber.org/zap"
)
@@ -259,6 +259,27 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
}
}
+ if strings.HasPrefix(chatMessage.Message, commands["KeyQuest"].Prefix) {
+ if commands["KeyQuest"].Enabled {
+ if strings.HasPrefix(chatMessage.Message, "!kqf get") {
+ sendServerChatMessage(s, fmt.Sprintf("KQF: %x", s.kqf))
+ } else if strings.HasPrefix(chatMessage.Message, "!kqf set") {
+ var hexs string
+ n, numerr := fmt.Sscanf(chatMessage.Message, "!kqf set %s", &hexs)
+ if numerr != nil || n != 1 || len(hexs) != 16 {
+ sendServerChatMessage(s, "Error in command. Format: !kqf set xxxxxxxxxxxxxxxx")
+ } else {
+ hexd, _ := hex.DecodeString(hexs)
+ s.kqf = hexd
+ s.kqfOverride = true
+ sendServerChatMessage(s, "KQF set, please switch Land/World")
+ }
+ }
+ } else {
+ sendDisabledCommandMessage(s, commands["KeyQuest"])
+ }
+ }
+
if strings.HasPrefix(chatMessage.Message, commands["Rights"].Prefix) {
// Set account rights
if commands["Rights"].Enabled {
diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go
index b712f960e..78a0b4754 100644
--- a/server/channelserver/handlers_character.go
+++ b/server/channelserver/handlers_character.go
@@ -1,7 +1,6 @@
package channelserver
import (
- "database/sql"
"encoding/binary"
"erupe-ce/network/mhfpacket"
@@ -10,120 +9,149 @@ import (
)
const (
- CharacterSaveRPPointer = 0x22D16
+ pointerGender = 0x81 // +1
+ pointerRP = 0x22D16 // +2
+ pointerHouseTier = 0x1FB6C // +5
+ pointerHouseData = 0x1FE01 // +195
+ pointerBookshelfData = 0x22298 // +5576
+ // Gallery data also exists at 0x21578, is this the contest submission?
+ pointerGalleryData = 0x22320 // +1748
+ pointerToreData = 0x1FCB4 // +240
+ pointerGardenData = 0x22C58 // +68
+ pointerWeaponType = 0x1F715 // +1
+ pointerWeaponID = 0x1F60A // +2
+ pointerHRP = 0x1FDF6 // +2
+ pointerGRP = 0x1FDFC // +4
+ pointerKQF = 0x23D20 // +8
)
type CharacterSaveData struct {
CharID uint32
Name string
- RP uint16
IsNewCharacter bool
- // Use provided setter/getter
- baseSaveData []byte
+ Gender bool
+ RP uint16
+ HouseTier []byte
+ HouseData []byte
+ BookshelfData []byte
+ GalleryData []byte
+ ToreData []byte
+ GardenData []byte
+ WeaponType uint8
+ WeaponID uint16
+ HRP uint16
+ GR uint16
+ KQF []byte
+
+ compSave []byte
+ decompSave []byte
}
func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) {
result, err := s.server.db.Query("SELECT id, savedata, is_new_character, name FROM characters WHERE id = $1", charID)
-
if err != nil {
- s.logger.Error("failed to retrieve save data for character", zap.Error(err), zap.Uint32("charID", charID))
+ s.logger.Error("Failed to get savedata", zap.Error(err), zap.Uint32("charID", charID))
return nil, err
}
-
defer result.Close()
+ if !result.Next() {
+ s.logger.Error("No savedata found", zap.Uint32("charID", charID))
+ return nil, err
+ }
saveData := &CharacterSaveData{}
- var compressedBaseSave []byte
-
- if !result.Next() {
- s.logger.Error("no results found for character save data", zap.Uint32("charID", charID))
- return nil, err
- }
-
- err = result.Scan(&saveData.CharID, &compressedBaseSave, &saveData.IsNewCharacter, &saveData.Name)
-
+ err = result.Scan(&saveData.CharID, &saveData.compSave, &saveData.IsNewCharacter, &saveData.Name)
if err != nil {
- s.logger.Error(
- "failed to retrieve save data for character",
- zap.Error(err),
- zap.Uint32("charID", charID),
- )
-
+ s.logger.Error("Failed to scan savedata", zap.Error(err), zap.Uint32("charID", charID))
return nil, err
}
- if compressedBaseSave == nil {
+ if saveData.compSave == nil {
return saveData, nil
}
- decompressedBaseSave, err := nullcomp.Decompress(compressedBaseSave)
-
+ err = saveData.Decompress()
if err != nil {
- s.logger.Error("Failed to decompress savedata from db", zap.Error(err))
+ s.logger.Error("Failed to decompress savedata", zap.Error(err))
return nil, err
}
- saveData.SetBaseSaveData(decompressedBaseSave)
+ saveData.updateStructWithSaveData()
return saveData, nil
}
-func (save *CharacterSaveData) Save(s *Session, transaction *sql.Tx) error {
- // We need to update the save data byte array before we save it back to the DB
+func (save *CharacterSaveData) Save(s *Session) {
+ if !s.kqfOverride {
+ s.kqf = save.KQF
+ } else {
+ save.KQF = s.kqf
+ }
+
save.updateSaveDataWithStruct()
- compressedData, err := save.CompressedBaseData(s)
-
+ err := save.Compress()
if err != nil {
- return err
+ s.logger.Error("Failed to compress savedata", zap.Error(err))
+ return
}
- updateSQL := "UPDATE characters SET savedata=$1, is_new_character=$3 WHERE id=$2"
-
- if transaction != nil {
- _, err = transaction.Exec(updateSQL, compressedData, save.CharID, save.IsNewCharacter)
- } else {
- _, err = s.server.db.Exec(updateSQL, compressedData, save.CharID, save.IsNewCharacter)
- }
+ _, err = s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=$2, hrp=$3, gr=$4, is_female=$5, weapon_type=$6, weapon_id=$7 WHERE id=$8
+ `, save.compSave, save.IsNewCharacter, save.HRP, save.GR, save.Gender, save.WeaponType, save.WeaponID, save.CharID)
+ if err != nil {
+ s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID))
+ }
+
+ s.server.db.Exec(`UPDATE user_binary SET house_tier=$1, house_data=$2, bookshelf=$3, gallery=$4, tore=$5, garden=$6 WHERE id=$7
+ `, save.HouseTier, save.HouseData, save.BookshelfData, save.GalleryData, save.ToreData, save.GardenData, s.charID)
+}
+
+func (save *CharacterSaveData) Compress() error {
+ var err error
+ save.compSave, err = nullcomp.Compress(save.decompSave)
if err != nil {
- s.logger.Error("failed to save character data", zap.Error(err), zap.Uint32("charID", save.CharID))
return err
}
return nil
}
-func (save *CharacterSaveData) CompressedBaseData(s *Session) ([]byte, error) {
- compressedData, err := nullcomp.Compress(save.baseSaveData)
-
+func (save *CharacterSaveData) Decompress() error {
+ var err error
+ save.decompSave, err = nullcomp.Decompress(save.compSave)
if err != nil {
- s.logger.Error("failed to compress saveData", zap.Error(err), zap.Uint32("charID", save.CharID))
- return nil, err
+ return err
}
- return compressedData, nil
+ return nil
}
-func (save *CharacterSaveData) BaseSaveData() []byte {
- return save.baseSaveData
-}
-
-func (save *CharacterSaveData) SetBaseSaveData(data []byte) {
- save.baseSaveData = data
- // After setting the new save byte array, we can extract the values to update our struct
- // This will be useful when we save it back, we use the struct values to overwrite the saveData
- save.updateStructWithSaveData()
-}
-
-// This will update the save struct with the values stored in the raw savedata arrays
+// This will update the character save with the values stored in the save struct
func (save *CharacterSaveData) updateSaveDataWithStruct() {
rpBytes := make([]byte, 2)
binary.LittleEndian.PutUint16(rpBytes, save.RP)
- copy(save.baseSaveData[CharacterSaveRPPointer:CharacterSaveRPPointer+2], rpBytes)
+ copy(save.decompSave[pointerRP:pointerRP+2], rpBytes)
+ copy(save.decompSave[pointerKQF:pointerKQF+8], save.KQF)
}
-// This will update the character save struct with the values stored in the raw savedata arrays
+// This will update the save struct with the values stored in the character save
func (save *CharacterSaveData) updateStructWithSaveData() {
- save.RP = binary.LittleEndian.Uint16(save.baseSaveData[CharacterSaveRPPointer : CharacterSaveRPPointer+2])
+ if save.decompSave[pointerGender] == 1 {
+ save.Gender = true
+ } else {
+ save.Gender = false
+ }
+ save.RP = binary.LittleEndian.Uint16(save.decompSave[pointerRP : pointerRP+2])
+ save.HouseTier = save.decompSave[pointerHouseTier : pointerHouseTier+5]
+ save.HouseData = save.decompSave[pointerHouseData : pointerHouseData+195]
+ save.BookshelfData = save.decompSave[pointerBookshelfData : pointerBookshelfData+5576]
+ save.GalleryData = save.decompSave[pointerGalleryData : pointerGalleryData+1748]
+ save.ToreData = save.decompSave[pointerToreData : pointerToreData+240]
+ save.GardenData = save.decompSave[pointerGardenData : pointerGardenData+68]
+ save.WeaponType = save.decompSave[pointerWeaponType]
+ save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[pointerWeaponID : pointerWeaponID+2])
+ save.HRP = binary.LittleEndian.Uint16(save.decompSave[pointerHRP : pointerHRP+2])
+ save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[pointerGRP : pointerGRP+4]))
+ save.KQF = save.decompSave[pointerKQF : pointerKQF+8]
}
func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) {
diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go
index 8ed55c54a..5d179e50c 100644
--- a/server/channelserver/handlers_data.go
+++ b/server/channelserver/handlers_data.go
@@ -1,7 +1,6 @@
package channelserver
import (
- "encoding/binary"
"encoding/hex"
"erupe-ce/common/stringsupport"
"fmt"
@@ -26,7 +25,6 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
return
}
// Var to hold the decompressed savedata for updating the launcher response fields.
- var decompressedData []byte
if pkt.SaveType == 1 {
// Diff-based update.
// diffs themselves are also potentially compressed
@@ -36,77 +34,23 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
}
// Perform diff.
s.logger.Info("Diffing...")
- characterSaveData.SetBaseSaveData(deltacomp.ApplyDataDiff(diff, characterSaveData.BaseSaveData()))
+ characterSaveData.decompSave = deltacomp.ApplyDataDiff(diff, characterSaveData.decompSave)
} else {
+ dumpSaveData(s, pkt.RawDataPayload, "savedata")
// Regular blob update.
saveData, err := nullcomp.Decompress(pkt.RawDataPayload)
if err != nil {
s.logger.Fatal("Failed to decompress savedata from packet", zap.Error(err))
}
s.logger.Info("Updating save with blob")
- characterSaveData.SetBaseSaveData(saveData)
+ characterSaveData.decompSave = saveData
}
characterSaveData.IsNewCharacter = false
- characterBaseSaveData := characterSaveData.BaseSaveData()
- // Make a copy for updating the launcher fields.
- decompressedData = make([]byte, len(characterBaseSaveData))
- copy(decompressedData, characterBaseSaveData)
- err = characterSaveData.Save(s, nil)
- if err != nil {
- s.logger.Fatal("Failed to update savedata in db", zap.Error(err))
- }
+ characterSaveData.Save(s)
s.logger.Info("Wrote recompressed savedata back to DB.")
- dumpSaveData(s, pkt.RawDataPayload, "savedata")
- _, 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 type in db", zap.Error(err))
- }
-
- s.myseries.houseTier = decompressedData[129900:129905] // 0x1FB6C + 5
- s.myseries.houseData = decompressedData[130561:130756] // 0x1FE01 + 195
- s.myseries.bookshelfData = decompressedData[139928:145504] // 0x22298 + 5576
- // Gallery data also exists at 0x21578, is this the contest submission?
- s.myseries.galleryData = decompressedData[140064:141812] // 0x22320 + 1748
- s.myseries.toreData = decompressedData[130228:130468] // 0x1FCB4 + 240
- s.myseries.gardenData = decompressedData[142424:142492] // 0x22C58 + 68
-
- isFemale := decompressedData[81] // 0x51
- if isFemale == 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)
- }
- if err != nil {
- 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)
+ characterSaveData.Name = s.clientContext.StrConv.MustDecode(bfutil.UpToNull(characterSaveData.decompSave[88:100]))
+ _, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterSaveData.Name, s.charID)
if err != nil {
s.logger.Fatal("Failed to update character name in db", zap.Error(err))
}
@@ -275,8 +219,8 @@ func dumpSaveData(s *Session, data []byte, suffix string) {
if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled {
return
} else {
- dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name))
- path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name), fmt.Sprintf("%d_%s_%s.bin", s.charID, s.Name, suffix))
+ dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID))
+ path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID), fmt.Sprintf("%d_%s.bin", s.charID, suffix))
if _, err := os.Stat(dir); os.IsNotExist(err) {
os.Mkdir(dir, os.ModeDir)
@@ -320,7 +264,8 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveScenarioData)
- _, err := s.server.db.Exec("UPDATE characters SET scenariodata = $1 WHERE characters.id = $2", pkt.RawDataPayload, int(s.charID))
+ dumpSaveData(s, pkt.RawDataPayload, "scenario")
+ _, err := s.server.db.Exec("UPDATE characters SET scenariodata = $1 WHERE id = $2", pkt.RawDataPayload, s.charID)
if err != nil {
s.logger.Fatal("Failed to update scenario data in db", zap.Error(err))
}
@@ -337,7 +282,7 @@ func handleMsgMhfLoadScenarioData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadScenarioData)
var scenarioData []byte
bf := byteframe.NewByteFrame()
- err := s.server.db.QueryRow("SELECT scenariodata FROM characters WHERE characters.id = $1", int(s.charID)).Scan(&scenarioData)
+ err := s.server.db.QueryRow("SELECT scenariodata FROM characters WHERE id = $1", s.charID).Scan(&scenarioData)
if err != nil {
s.logger.Fatal("Failed to get scenario data contents in db", zap.Error(err))
} else {
diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go
index 6bbee7eba..8891f23ee 100644
--- a/server/channelserver/handlers_guild.go
+++ b/server/channelserver/handlers_guild.go
@@ -164,7 +164,7 @@ func (guild *Guild) Save(s *Session) error {
func (guild *Guild) CreateApplication(s *Session, charID uint32, applicationType GuildApplicationType, transaction *sql.Tx) error {
- sql := `
+ query := `
INSERT INTO guild_applications (guild_id, character_id, actor_id, application_type)
VALUES ($1, $2, $3, $4)
`
@@ -172,9 +172,9 @@ func (guild *Guild) CreateApplication(s *Session, charID uint32, applicationType
var err error
if transaction == nil {
- _, err = s.server.db.Exec(sql, guild.ID, charID, s.charID, applicationType)
+ _, err = s.server.db.Exec(query, guild.ID, charID, s.charID, applicationType)
} else {
- _, err = transaction.Exec(sql, guild.ID, charID, s.charID, applicationType)
+ _, err = transaction.Exec(query, guild.ID, charID, s.charID, applicationType)
}
if err != nil {
@@ -222,7 +222,7 @@ func (guild *Guild) Disband(s *Session) error {
return err
}
- _, err = transaction.Exec("UPDATE guild_alliances SET sub1_id=NULL WHERE sub1_id=$1", guild.ID)
+ _, err = transaction.Exec("UPDATE guild_alliances SET sub1_id=sub2_id, sub2_id=NULL WHERE sub1_id=$1", guild.ID)
if err != nil {
s.logger.Error("failed to remove guild from alliance", zap.Error(err), zap.Uint32("guildID", guild.ID))
@@ -634,11 +634,8 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
}
bf.WriteUint32(uint32(response))
- doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
- return
case mhfpacket.OPERATE_GUILD_RESIGN:
guildMembers, err := GetGuildMembers(s, guild.ID, false)
- success := false
if err == nil {
sort.Slice(guildMembers[:], func(i, j int) bool {
return guildMembers[i].OrderIndex < guildMembers[j].OrderIndex
@@ -651,29 +648,17 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
guildMembers[0].Save(s)
guildMembers[i].Save(s)
bf.WriteUint32(guildMembers[i].CharID)
- success = true
break
}
}
guild.Save(s)
- doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
- if !success {
- bf.WriteUint32(0)
- doAckBufSucceed(s, pkt.AckHandle, bf.Data())
- }
- return
case mhfpacket.OPERATE_GUILD_APPLY:
err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil)
- if err != nil {
- // All successful acks return 0x01, assuming 0x00 is failure
- bf.WriteUint32(0x00)
- } else {
+ if err == nil {
bf.WriteUint32(guild.LeaderCharID)
}
- doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
- return
case mhfpacket.OPERATE_GUILD_LEAVE:
var err error
@@ -698,10 +683,8 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
}
bf.WriteUint32(uint32(response))
- doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
- return
case mhfpacket.OPERATE_GUILD_DONATE_RANK:
- handleDonateRP(s, pkt, bf, guild, false)
+ bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, false))
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_DENY:
s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID)
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_ALLOW:
@@ -711,46 +694,51 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE:
handleAvoidLeadershipUpdate(s, pkt, false)
case mhfpacket.OPERATE_GUILD_UPDATE_COMMENT:
- pbf := byteframe.NewByteFrameFromBytes(pkt.UnkData)
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
- _ = pbf.ReadUint8() // len
- _ = pbf.ReadUint32()
- guild.Comment = stringsupport.SJISToUTF8(pbf.ReadNullTerminatedBytes())
+ guild.Comment = stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())
guild.Save(s)
case mhfpacket.OPERATE_GUILD_UPDATE_MOTTO:
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
- guild.SubMotto = pkt.UnkData[3]
- guild.MainMotto = pkt.UnkData[4]
+ _ = pkt.Data1.ReadUint16()
+ guild.SubMotto = pkt.Data1.ReadUint8()
+ guild.MainMotto = pkt.Data1.ReadUint8()
guild.Save(s)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_1:
- handleRenamePugi(s, pkt.UnkData, guild, 1)
+ handleRenamePugi(s, pkt.Data2, guild, 1)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_2:
- handleRenamePugi(s, pkt.UnkData, guild, 2)
+ handleRenamePugi(s, pkt.Data2, guild, 2)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_3:
- handleRenamePugi(s, pkt.UnkData, guild, 3)
+ handleRenamePugi(s, pkt.Data2, guild, 3)
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_1:
// TODO: decode guild poogie outfits
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_2:
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_3:
case mhfpacket.OPERATE_GUILD_DONATE_EVENT:
- handleDonateRP(s, pkt, bf, guild, true)
+ bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, true))
+ case mhfpacket.OPERATE_GUILD_EVENT_EXCHANGE:
+ rp := uint16(pkt.Data1.ReadUint32())
+ saveData, _ := GetCharacterSaveData(s, s.charID)
+ saveData.RP -= rp
+ saveData.Save(s)
+ bf.WriteUint32(uint32(saveData.RP))
default:
panic(fmt.Sprintf("unhandled operate guild action '%d'", pkt.Action))
}
- doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
+ if len(bf.Data()) > 0 {
+ doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
+ } else {
+ doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
+ }
}
-func handleRenamePugi(s *Session, data []byte, guild *Guild, num int) {
- bf := byteframe.NewByteFrameFromBytes(data)
- _ = bf.ReadUint8() // len
- _ = bf.ReadUint32() // unk
+func handleRenamePugi(s *Session, bf *byteframe.ByteFrame, guild *Guild, num int) {
name := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
switch num {
case 1:
@@ -763,33 +751,23 @@ func handleRenamePugi(s *Session, data []byte, guild *Guild, num int) {
guild.Save(s)
}
-func handleDonateRP(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, bf *byteframe.ByteFrame, guild *Guild, isEvent bool) error {
- rp := binary.BigEndian.Uint16(pkt.UnkData[3:5])
+func handleDonateRP(s *Session, amount uint16, guild *Guild, isEvent bool) []byte {
+ bf := byteframe.NewByteFrame()
+ bf.WriteUint32(0)
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
+ return bf.Data()
}
+ saveData.RP -= amount
+ saveData.Save(s)
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()
- }
+ s.server.db.Exec(updateSQL, amount, guild.ID)
+ bf.Seek(0, 0)
bf.WriteUint32(uint32(saveData.RP))
- return nil
+ return bf.Data()
}
func handleAvoidLeadershipUpdate(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, avoidLeadership bool) {
@@ -1027,15 +1005,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
}
applicants, err := GetGuildMembers(s, guild.ID, true)
-
if err != nil {
- resp := byteframe.NewByteFrame()
- resp.WriteUint32(0) // Count
- resp.WriteUint8(0) // Unk, read if count == 0.
-
- doAckBufSucceed(s, pkt.AckHandle, resp.Data())
- }
- if err != nil || characterGuildData.IsApplicant {
bf.WriteUint16(0)
} else {
bf.WriteUint16(uint16(len(applicants)))
@@ -1049,7 +1019,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
}
}
- bf.WriteUint16(0x0000)
+ bf.WriteUint16(0x0000) // lenAllianceApplications
/*
alliance application format
@@ -1095,6 +1065,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateGuild)
var guilds []*Guild
+ var alliances []*GuildAlliance
var rows *sqlx.Rows
var err error
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
@@ -1189,41 +1160,112 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
guilds = append(guilds, guild)
}
}
- 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))
}
- if err != nil || guilds == nil {
+ if pkt.Type > 8 {
+ var tempAlliances []*GuildAlliance
+ rows, err = s.server.db.Queryx(allianceInfoSelectQuery)
+ if err == nil {
+ for rows.Next() {
+ alliance, _ := buildAllianceObjectFromDbResult(rows, err, s)
+ tempAlliances = append(tempAlliances, alliance)
+ }
+ }
+ switch pkt.Type {
+ case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME:
+ bf.ReadBytes(10)
+ searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
+ for _, alliance := range tempAlliances {
+ if strings.Contains(alliance.Name, searchTerm) {
+ alliances = append(alliances, alliance)
+ }
+ }
+ case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME:
+ bf.ReadBytes(10)
+ searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
+ for _, alliance := range tempAlliances {
+ if strings.Contains(alliance.ParentGuild.LeaderName, searchTerm) {
+ alliances = append(alliances, alliance)
+ }
+ }
+ case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_ID:
+ bf.ReadBytes(2)
+ ID := bf.ReadUint32()
+ for _, alliance := range tempAlliances {
+ if alliance.ParentGuild.LeaderCharID == ID {
+ alliances = append(alliances, alliance)
+ }
+ }
+ case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS:
+ sorting := bf.ReadBool()
+ if sorting {
+ sort.Slice(tempAlliances, func(i, j int) bool {
+ return tempAlliances[i].TotalMembers > tempAlliances[j].TotalMembers
+ })
+ } else {
+ sort.Slice(tempAlliances, func(i, j int) bool {
+ return tempAlliances[i].TotalMembers < tempAlliances[j].TotalMembers
+ })
+ }
+ alliances = tempAlliances
+ case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION:
+ sorting := bf.ReadBool()
+ if sorting {
+ sort.Slice(tempAlliances, func(i, j int) bool {
+ return tempAlliances[i].CreatedAt.Unix() > tempAlliances[j].CreatedAt.Unix()
+ })
+ } else {
+ sort.Slice(tempAlliances, func(i, j int) bool {
+ return tempAlliances[i].CreatedAt.Unix() < tempAlliances[j].CreatedAt.Unix()
+ })
+ }
+ alliances = tempAlliances
+ }
+ }
+
+ if err != nil || (guilds == nil && alliances == nil) {
stubEnumerateNoResults(s, pkt.AckHandle)
return
}
bf = byteframe.NewByteFrame()
- bf.WriteUint16(uint16(len(guilds)))
- bf.WriteUint8(0x01) // Unk
-
- for _, guild := range guilds {
- bf.WriteUint32(guild.ID)
- bf.WriteUint32(guild.LeaderCharID)
- bf.WriteUint16(guild.MemberCount)
- bf.WriteUint16(0x0000) // Unk
- bf.WriteUint16(guild.Rank) // OR guilds in alliance
- bf.WriteUint32(uint32(guild.CreatedAt.Unix()))
- ps.Uint8(bf, guild.Name, true)
- ps.Uint8(bf, guild.LeaderName, true)
+ if pkt.Type > 8 {
+ bf.WriteUint16(uint16(len(alliances)))
+ bf.WriteUint8(0x00) // Unk
+ for _, alliance := range alliances {
+ bf.WriteUint32(alliance.ID)
+ bf.WriteUint32(alliance.ParentGuild.LeaderCharID)
+ bf.WriteUint16(alliance.TotalMembers)
+ bf.WriteUint16(0x0000)
+ if alliance.SubGuild1ID == 0 && alliance.SubGuild2ID == 0 {
+ bf.WriteUint16(1)
+ } else if alliance.SubGuild1ID > 0 && alliance.SubGuild2ID == 0 || alliance.SubGuild1ID == 0 && alliance.SubGuild2ID > 0 {
+ bf.WriteUint16(2)
+ } else {
+ bf.WriteUint16(3)
+ }
+ bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
+ ps.Uint8(bf, alliance.Name, true)
+ ps.Uint8(bf, alliance.ParentGuild.LeaderName, true)
+ bf.WriteUint8(0x01) // Unk
+ bf.WriteBool(true) // TODO: Enable GuildAlliance applications
+ }
+ } else {
bf.WriteUint8(0x01) // Unk
- bf.WriteBool(!guild.Recruiting)
+ bf.WriteUint16(uint16(len(guilds)))
+ for _, guild := range guilds {
+ bf.WriteUint32(guild.ID)
+ bf.WriteUint32(guild.LeaderCharID)
+ bf.WriteUint16(guild.MemberCount)
+ bf.WriteUint16(0x0000) // Unk
+ bf.WriteUint16(guild.Rank) // OR guilds in alliance
+ bf.WriteUint32(uint32(guild.CreatedAt.Unix()))
+ ps.Uint8(bf, guild.Name, true)
+ ps.Uint8(bf, guild.LeaderName, true)
+ bf.WriteUint8(0x01) // Unk
+ bf.WriteBool(!guild.Recruiting)
+ }
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
@@ -1712,9 +1754,11 @@ func handleMsgMhfGetGuildWeeklyBonusMaster(s *Session, p mhfpacket.MHFPacket) {
}
func handleMsgMhfGetGuildWeeklyBonusActiveCount(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGuildWeeklyBonusActiveCount)
-
- // Values taken from brand new guild capture
- doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x03))
+ bf := byteframe.NewByteFrame()
+ bf.WriteUint8(0x3C) // Active count
+ bf.WriteUint8(0x3C) // Current active count
+ bf.WriteUint8(0x00) // New active count
+ doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGuildHuntdata(s *Session, p mhfpacket.MHFPacket) {
diff --git a/server/channelserver/handlers_guild_alliance.go b/server/channelserver/handlers_guild_alliance.go
index 77f204adc..7e2a9a2ac 100644
--- a/server/channelserver/handlers_guild_alliance.go
+++ b/server/channelserver/handlers_guild_alliance.go
@@ -1,6 +1,8 @@
package channelserver
import (
+ "erupe-ce/common/byteframe"
+ ps "erupe-ce/common/pascalstring"
"fmt"
"time"
@@ -139,8 +141,15 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) {
}
case mhfpacket.OPERATE_JOINT_LEAVE:
if guild.LeaderCharID == s.charID {
- // delete alliance application
- // or leave alliance
+ if guild.ID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 {
+ s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID)
+ } else if guild.ID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 {
+ s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID)
+ } else {
+ s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID)
+ }
+ // TODO: Handle deleting Alliance applications
+ doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} else {
s.logger.Warn(
"Non-owner of guild attempted alliance leave",
@@ -148,10 +157,75 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) {
)
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
+ case mhfpacket.OPERATE_JOINT_KICK:
+ if alliance.ParentGuild.LeaderCharID == s.charID {
+ _ = pkt.UnkData.ReadUint8()
+ kickedGuildID := pkt.UnkData.ReadUint32()
+ if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 {
+ s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID)
+ } else if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 {
+ s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID)
+ } else {
+ s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID)
+ }
+ doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
+ } else {
+ s.logger.Warn(
+ "Non-owner of alliance attempted kick",
+ zap.Uint32("CharID", s.charID),
+ zap.Uint32("AllyID", alliance.ID),
+ )
+ doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
+ }
default:
- panic(fmt.Sprintf("Unhandled operate joint action '%d'", pkt.Action))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
+ panic(fmt.Sprintf("Unhandled operate joint action '%d'", pkt.Action))
}
}
-func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {}
+func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
+ pkt := p.(*mhfpacket.MsgMhfInfoJoint)
+ bf := byteframe.NewByteFrame()
+ alliance, err := GetAllianceData(s, pkt.AllianceID)
+ if err != nil {
+ doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
+ } else {
+ bf.WriteUint32(alliance.ID)
+ bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
+ bf.WriteUint16(alliance.TotalMembers)
+ bf.WriteUint16(0x0000) // Unk
+ ps.Uint16(bf, alliance.Name, true)
+ if alliance.SubGuild1ID > 0 {
+ if alliance.SubGuild2ID > 0 {
+ bf.WriteUint8(3)
+ } else {
+ bf.WriteUint8(2)
+ }
+ } else {
+ bf.WriteUint8(1)
+ }
+ bf.WriteUint32(alliance.ParentGuildID)
+ bf.WriteUint32(alliance.ParentGuild.LeaderCharID)
+ bf.WriteUint16(alliance.ParentGuild.Rank)
+ bf.WriteUint16(alliance.ParentGuild.MemberCount)
+ ps.Uint16(bf, alliance.ParentGuild.Name, true)
+ ps.Uint16(bf, alliance.ParentGuild.LeaderName, true)
+ if alliance.SubGuild1ID > 0 {
+ bf.WriteUint32(alliance.SubGuild1ID)
+ bf.WriteUint32(alliance.SubGuild1.LeaderCharID)
+ bf.WriteUint16(alliance.SubGuild1.Rank)
+ bf.WriteUint16(alliance.SubGuild1.MemberCount)
+ ps.Uint16(bf, alliance.SubGuild1.Name, true)
+ ps.Uint16(bf, alliance.SubGuild1.LeaderName, true)
+ }
+ if alliance.SubGuild2ID > 0 {
+ bf.WriteUint32(alliance.SubGuild2ID)
+ bf.WriteUint32(alliance.SubGuild2.LeaderCharID)
+ bf.WriteUint16(alliance.SubGuild2.Rank)
+ bf.WriteUint16(alliance.SubGuild2.MemberCount)
+ ps.Uint16(bf, alliance.SubGuild2.Name, true)
+ ps.Uint16(bf, alliance.SubGuild2.LeaderName, true)
+ }
+ doAckBufSucceed(s, pkt.AckHandle, bf.Data())
+ }
+}
diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go
index 280f27238..b3aadf372 100644
--- a/server/channelserver/handlers_house.go
+++ b/server/channelserver/handlers_house.go
@@ -38,24 +38,26 @@ FROM warehouse
func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateInterior)
- _, err := s.server.db.Exec("UPDATE characters SET house=$1 WHERE id=$2", pkt.InteriorData, s.charID)
- if err != nil {
- panic(err)
- }
+ s.server.db.Exec(`UPDATE user_binary SET house_furniture=$1 WHERE id=$2`, pkt.InteriorData, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
type HouseData struct {
- CharID uint32 `db:"id"`
- HRP uint16 `db:"hrp"`
- GR uint16 `db:"gr"`
- Name string `db:"name"`
+ CharID uint32 `db:"id"`
+ HRP uint16 `db:"hrp"`
+ GR uint16 `db:"gr"`
+ Name string `db:"name"`
+ HouseState uint8 `db:"house_state"`
+ HousePassword string `db:"house_password"`
}
func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateHouse)
bf := byteframe.NewByteFrame()
+ bf.WriteUint16(0)
var houses []HouseData
+ houseQuery := `SELECT c.id, hrp, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
+ FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE c.id=$1`
switch pkt.Method {
case 1:
var friendsList string
@@ -63,17 +65,15 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
cids := stringsupport.CSVElems(friendsList)
for _, cid := range cids {
house := HouseData{}
- row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", cid)
+ row := s.server.db.QueryRowx(houseQuery, cid)
err := row.StructScan(&house)
- if err != nil {
- panic(err)
- } else {
+ if err == nil {
houses = append(houses, house)
}
}
case 2:
guild, err := GetGuildInfoByCharacterId(s, s.charID)
- if err != nil {
+ if err != nil || guild == nil {
break
}
guildMembers, err := GetGuildMembers(s, guild.ID, false)
@@ -82,58 +82,48 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
}
for _, member := range guildMembers {
house := HouseData{}
- row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", member.CharID)
- err := row.StructScan(&house)
- if err != nil {
- panic(err)
- } else {
+ row := s.server.db.QueryRowx(houseQuery, member.CharID)
+ err = row.StructScan(&house)
+ if err == nil {
houses = append(houses, house)
}
}
case 3:
+ houseQuery = `SELECT c.id, hrp, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
+ FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE name ILIKE $1`
house := HouseData{}
- row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name ILIKE $1", fmt.Sprintf(`%%%s%%`, pkt.Name))
- err := row.StructScan(&house)
- if err != nil {
- panic(err)
- } else {
- houses = append(houses, house)
+ rows, _ := s.server.db.Queryx(houseQuery, fmt.Sprintf(`%%%s%%`, pkt.Name))
+ for rows.Next() {
+ err := rows.StructScan(&house)
+ if err == nil {
+ houses = append(houses, house)
+ }
}
case 4:
house := HouseData{}
- row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", pkt.CharID)
+ row := s.server.db.QueryRowx(houseQuery, pkt.CharID)
err := row.StructScan(&house)
- if err != nil {
- panic(err)
- } else {
+ if err == nil {
houses = append(houses, house)
}
case 5: // Recent visitors
break
}
- var exists int
for _, house := range houses {
- for _, session := range s.server.sessions {
- if session.charID == house.CharID {
- exists++
- bf.WriteUint32(house.CharID)
- bf.WriteUint8(session.myseries.state)
- if len(session.myseries.password) > 0 {
- bf.WriteUint8(3)
- } else {
- bf.WriteUint8(0)
- }
- bf.WriteUint16(house.HRP)
- bf.WriteUint16(house.GR)
- ps.Uint8(bf, house.Name, true)
- break
- }
+ bf.WriteUint32(house.CharID)
+ bf.WriteUint8(house.HouseState)
+ if len(house.HousePassword) > 0 {
+ bf.WriteUint8(3)
+ } else {
+ bf.WriteUint8(0)
}
+ bf.WriteUint16(house.HRP)
+ bf.WriteUint16(house.GR)
+ ps.Uint8(bf, house.Name, true)
}
- resp := byteframe.NewByteFrame()
- resp.WriteUint16(uint16(exists))
- resp.WriteBytes(bf.Data())
- doAckBufSucceed(s, pkt.AckHandle, resp.Data())
+ bf.Seek(0, 0)
+ bf.WriteUint16(uint16(len(houses)))
+ doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) {
@@ -143,72 +133,89 @@ func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) {
// 03 = open friends
// 04 = open guild
// 05 = open friends+guild
- s.myseries.state = pkt.State
- s.myseries.password = pkt.Password
+ s.server.db.Exec(`UPDATE user_binary SET house_state=$1, house_password=$2 WHERE id=$3`, pkt.State, pkt.Password, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadHouse)
bf := byteframe.NewByteFrame()
+
+ var state uint8
+ var password string
+ s.server.db.QueryRow(`SELECT COALESCE(house_state, 2) as house_state, COALESCE(house_password, '') as house_password FROM user_binary WHERE id=$1
+ `, pkt.CharID).Scan(&state, &password)
+
if pkt.Destination != 9 && len(pkt.Password) > 0 && pkt.CheckPass {
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID && pkt.Password != session.myseries.password {
- doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
- return
- }
+ if pkt.Password != password {
+ doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
+ return
}
}
- var furniture []byte
- err := s.server.db.QueryRow("SELECT house FROM characters WHERE id=$1", pkt.CharID).Scan(&furniture)
- if err != nil {
- panic(err)
+ if pkt.Destination != 9 && state > 2 {
+ allowed := false
+
+ // Friends list verification
+ if state == 3 || state == 5 {
+ var friendsList string
+ s.server.db.QueryRow(`SELECT friends FROM characters WHERE id=$1`, pkt.CharID).Scan(&friendsList)
+ cids := stringsupport.CSVElems(friendsList)
+ for _, cid := range cids {
+ if uint32(cid) == s.charID {
+ allowed = true
+ break
+ }
+ }
+ }
+
+ // Guild verification
+ if state > 3 {
+ ownGuild, err := GetGuildInfoByCharacterId(s, s.charID)
+ isApplicant, _ := ownGuild.HasApplicationForCharID(s, s.charID)
+ if err == nil && ownGuild != nil {
+ othersGuild, err := GetGuildInfoByCharacterId(s, pkt.CharID)
+ if err == nil && othersGuild != nil {
+ if othersGuild.ID == ownGuild.ID && !isApplicant {
+ allowed = true
+ }
+ }
+ }
+ }
+
+ if !allowed {
+ doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
+ return
+ }
}
- if furniture == nil {
- furniture = make([]byte, 20)
+
+ var houseTier, houseData, houseFurniture, bookshelf, gallery, tore, garden []byte
+ s.server.db.QueryRow(`SELECT house_tier, house_data, house_furniture, bookshelf, gallery, tore, garden FROM user_binary WHERE id=$1
+ `, pkt.CharID).Scan(&houseTier, &houseData, &houseFurniture, &bookshelf, &gallery, &tore, &garden)
+ if houseFurniture == nil {
+ houseFurniture = make([]byte, 20)
}
switch pkt.Destination {
case 3: // Others house
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID {
- bf.WriteBytes(session.myseries.houseTier)
- bf.WriteBytes(session.myseries.houseData)
- bf.WriteBytes(make([]byte, 19)) // Padding?
- bf.WriteBytes(furniture)
- }
- }
+ bf.WriteBytes(houseTier)
+ bf.WriteBytes(houseData)
+ bf.WriteBytes(make([]byte, 19)) // Padding?
+ bf.WriteBytes(houseFurniture)
case 4: // Bookshelf
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID {
- bf.WriteBytes(session.myseries.bookshelfData)
- }
- }
+ bf.WriteBytes(bookshelf)
case 5: // Gallery
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID {
- bf.WriteBytes(session.myseries.galleryData)
- }
- }
+ bf.WriteBytes(gallery)
case 8: // Tore
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID {
- bf.WriteBytes(session.myseries.toreData)
- }
- }
+ bf.WriteBytes(tore)
case 9: // Own house
- bf.WriteBytes(furniture)
+ bf.WriteBytes(houseFurniture)
case 10: // Garden
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID {
- bf.WriteBytes(session.myseries.gardenData)
- c, d := getGookData(s, pkt.CharID)
- bf.WriteUint16(c)
- bf.WriteUint16(0)
- bf.WriteBytes(d)
- }
- }
+ bf.WriteBytes(garden)
+ c, d := getGookData(s, pkt.CharID)
+ bf.WriteUint16(c)
+ bf.WriteUint16(0)
+ bf.WriteBytes(d)
}
if len(bf.Data()) == 0 {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
@@ -219,26 +226,18 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfGetMyhouseInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetMyhouseInfo)
-
var data []byte
- err := s.server.db.QueryRow("SELECT trophy FROM characters WHERE id = $1", s.charID).Scan(&data)
- if err != nil {
- panic(err)
- }
+ s.server.db.QueryRow(`SELECT mission FROM user_binary WHERE id=$1`, s.charID).Scan(&data)
if len(data) > 0 {
doAckBufSucceed(s, pkt.AckHandle, data)
} else {
- doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
+ doAckBufSucceed(s, pkt.AckHandle, make([]byte, 9))
}
}
func handleMsgMhfUpdateMyhouseInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateMyhouseInfo)
-
- _, err := s.server.db.Exec("UPDATE characters SET trophy=$1 WHERE id=$2", pkt.Unk0, s.charID)
- if err != nil {
- panic(err)
- }
+ s.server.db.Exec("UPDATE user_binary SET mission=$1 WHERE id=$2", pkt.Unk0, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
@@ -311,6 +310,7 @@ func handleMsgMhfSaveDecoMyset(s *Session, p mhfpacket.MHFPacket) {
}
loadData[1] = savedSets // update set count
}
+ dumpSaveData(s, loadData, "decomyset")
_, err := s.server.db.Exec("UPDATE characters SET decomyset=$1 WHERE id=$2", loadData, s.charID)
if err != nil {
s.logger.Fatal("Failed to update decomyset savedata in db", zap.Error(err))
diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go
index 66c7da046..bdeb924de 100644
--- a/server/channelserver/handlers_mercenary.go
+++ b/server/channelserver/handlers_mercenary.go
@@ -104,6 +104,7 @@ func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
s.logger.Info("Wrote recompressed hunternavi back to DB.")
} else {
+ dumpSaveData(s, pkt.RawDataPayload, "hunternavi")
// simply update database, no extra processing
_, err := s.server.db.Exec("UPDATE characters SET hunternavi=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
if err != nil {
@@ -162,6 +163,7 @@ func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveMercenary)
+ dumpSaveData(s, pkt.MercData, "mercenary")
if len(pkt.MercData) > 0 {
s.server.db.Exec("UPDATE characters SET savemercenary=$1 WHERE id=$2", pkt.MercData, s.charID)
}
diff --git a/server/channelserver/handlers_plate.go b/server/channelserver/handlers_plate.go
index 72121ef13..b1bfea5d2 100644
--- a/server/channelserver/handlers_plate.go
+++ b/server/channelserver/handlers_plate.go
@@ -25,7 +25,7 @@ func handleMsgMhfLoadPlateData(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSavePlateData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSavePlateData)
- dumpSaveData(s, pkt.RawDataPayload, "_platedata")
+ dumpSaveData(s, pkt.RawDataPayload, "platedata")
if pkt.IsDataDiff {
var data []byte
@@ -90,7 +90,7 @@ func handleMsgMhfLoadPlateBox(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSavePlateBox(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSavePlateBox)
- dumpSaveData(s, pkt.RawDataPayload, "_platebox")
+ dumpSaveData(s, pkt.RawDataPayload, "platebox")
if pkt.IsDataDiff {
var data []byte
@@ -156,7 +156,7 @@ func handleMsgMhfLoadPlateMyset(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSavePlateMyset(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSavePlateMyset)
// looks to always return the full thing, simply update database, no extra processing
-
+ dumpSaveData(s, pkt.RawDataPayload, "platemyset")
_, err := s.server.db.Exec("UPDATE characters SET platemyset=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
if err != nil {
s.logger.Fatal("Failed to update platemyset savedata in db", zap.Error(err))
diff --git a/server/channelserver/handlers_quest.go b/server/channelserver/handlers_quest.go
index 86a0003b9..7ee552232 100644
--- a/server/channelserver/handlers_quest.go
+++ b/server/channelserver/handlers_quest.go
@@ -54,6 +54,7 @@ func handleMsgMhfLoadFavoriteQuest(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSaveFavoriteQuest(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveFavoriteQuest)
+ dumpSaveData(s, pkt.Data, "favquest")
s.server.db.Exec("UPDATE characters SET savefavoritequest=$1 WHERE id=$2", pkt.Data, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
diff --git a/server/channelserver/handlers_rengoku.go b/server/channelserver/handlers_rengoku.go
index 6fdce7c22..d9f58df57 100644
--- a/server/channelserver/handlers_rengoku.go
+++ b/server/channelserver/handlers_rengoku.go
@@ -15,6 +15,7 @@ func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) {
// saved every floor on road, holds values such as floors progressed, points etc.
// can be safely handled by the client
pkt := p.(*mhfpacket.MsgMhfSaveRengokuData)
+ dumpSaveData(s, pkt.RawDataPayload, "rengoku")
_, err := s.server.db.Exec("UPDATE characters SET rengokudata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
if err != nil {
s.logger.Fatal("Failed to update rengokudata savedata in db", zap.Error(err))
@@ -270,8 +271,20 @@ func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
bf.WriteBytes(make([]byte, 11))
}
}
- bf.WriteUint8(uint8(i) - 1)
- bf.WriteBytes(scoreData.Data())
+ if i == 1 {
+ bf.WriteUint32(1)
+ bf.WriteUint32(0)
+ ps.Uint8(bf, s.Name, true)
+ ps.Uint8(bf, "", false)
+ bf.WriteUint8(1)
+ bf.WriteUint32(1)
+ bf.WriteUint32(0)
+ ps.Uint8(bf, s.Name, true)
+ ps.Uint8(bf, "", false)
+ } else {
+ bf.WriteUint8(uint8(i) - 1)
+ bf.WriteBytes(scoreData.Data())
+ }
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
diff --git a/server/channelserver/handlers_tower.go b/server/channelserver/handlers_tower.go
index 49029753f..24cc64033 100644
--- a/server/channelserver/handlers_tower.go
+++ b/server/channelserver/handlers_tower.go
@@ -13,7 +13,7 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) {
type:
1 == TOWER_RANK_POINT,
2 == GET_OWN_TOWER_SKILL
- 3 == ?
+ 3 == GET_OWN_TOWER_LEVEL_V3
4 == TOWER_TOUHA_HISTORY
5 = ?
@@ -39,8 +39,8 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) {
case mhfpacket.TowerInfoTypeGetOwnTowerSkill:
//data, err = hex.DecodeString("0A218EAD000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
data, err = hex.DecodeString("0A218EAD0000000000000000000000010000001C0000000500050000000000020000000000000000000000000000000000030003000000000003000500050000000300030003000300030003000200030001000300020002000300010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
- case mhfpacket.TowerInfoTypeUnk3:
- panic("No known response values for TowerInfoTypeUnk3")
+ case mhfpacket.TowerInfoTypeGetOwnTowerLevelV3:
+ panic("No known response values for GetOwnTowerLevelV3")
case mhfpacket.TowerInfoTypeTowerTouhaHistory:
data, err = hex.DecodeString("0A218EAD0000000000000000000000010000000000000000000000000000000000000000")
case mhfpacket.TowerInfoTypeUnk5:
@@ -58,6 +58,9 @@ func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
-func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {}
+func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {
+ pkt := p.(*mhfpacket.MsgMhfGetGemInfo)
+ doAckBufSucceed(s, pkt.AckHandle, make([]byte, 8))
+}
func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {}
diff --git a/server/channelserver/handlers_users.go b/server/channelserver/handlers_users.go
index 3bebcc97a..c360b82a6 100644
--- a/server/channelserver/handlers_users.go
+++ b/server/channelserver/handlers_users.go
@@ -17,12 +17,12 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {
s.server.userBinaryPartsLock.Unlock()
var exists []byte
- err := s.server.db.QueryRow("SELECT type2 FROM user_binaries WHERE id=$1", s.charID).Scan(&exists)
+ err := s.server.db.QueryRow("SELECT type2 FROM user_binary WHERE id=$1", s.charID).Scan(&exists)
if err != nil {
- s.server.db.Exec("INSERT INTO user_binaries (id) VALUES ($1)", s.charID)
+ s.server.db.Exec("INSERT INTO user_binary (id) VALUES ($1)", s.charID)
}
- s.server.db.Exec(fmt.Sprintf("UPDATE user_binaries SET type%d=$1 WHERE id=$2", pkt.BinaryType), pkt.RawDataPayload, s.charID)
+ s.server.db.Exec(fmt.Sprintf("UPDATE user_binary SET type%d=$1 WHERE id=$2", pkt.BinaryType), pkt.RawDataPayload, s.charID)
msg := &mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
@@ -42,7 +42,7 @@ func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) {
// If we can't get the real data, try to get it from the database.
if !ok {
- err := s.server.db.QueryRow(fmt.Sprintf("SELECT type%d FROM user_binaries WHERE id=$1", pkt.BinaryType), pkt.CharID).Scan(&data)
+ err := s.server.db.QueryRow(fmt.Sprintf("SELECT type%d FROM user_binary WHERE id=$1", pkt.BinaryType), pkt.CharID).Scan(&data)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
} else {
diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go
index 20fc432f8..ec122a521 100644
--- a/server/channelserver/sys_session.go
+++ b/server/channelserver/sys_session.go
@@ -39,6 +39,8 @@ type Session struct {
sessionStart int64
rights uint32
token string
+ kqf []byte
+ kqfOverride bool
semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet.
diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go
index a169d93a1..cbad6405c 100644
--- a/server/signserver/dsgn_resp.go
+++ b/server/signserver/dsgn_resp.go
@@ -112,14 +112,22 @@ func (s *Session) makeSignInResp(uid int) []byte {
bf.WriteUint32(s.server.getLastCID(uid))
bf.WriteUint32(s.server.getUserRights(uid))
ps.Uint16(bf, "", false) // filters
- bf.WriteUint32(0xCA104E20)
- ps.Uint16(bf, "", false) // encryption
+ bf.WriteUint16(0xCA10)
+ bf.WriteUint16(0x4E20)
+ ps.Uint16(bf, "", false) // unk key
bf.WriteUint8(0x00)
- bf.WriteUint32(0xCA110001)
- bf.WriteUint32(0x4E200000)
- bf.WriteUint32(uint32(returnExpiry.Unix()))
+ bf.WriteUint16(0xCA11)
+ bf.WriteUint16(0x0001)
+ bf.WriteUint16(0x4E20)
+ ps.Uint16(bf, "", false) // unk ipv4
+ if returnExpiry.Before(time.Now()) {
+ // Hack to make Return work while having a non-adjusted expiry
+ bf.WriteUint32(0)
+ } else {
+ bf.WriteUint32(uint32(returnExpiry.Unix()))
+ }
bf.WriteUint32(0x00000000)
- bf.WriteUint32(0x0A5197DF)
+ bf.WriteUint32(0x0A5197DF) // unk id
mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent
alt := s.server.erupeConfig.DevModeOptions.MezFesAlt