Merge pull request #41 from ZeruLight/feature/systems-rework

feature/systems-rework
This commit is contained in:
wish
2022-10-04 20:25:55 +11:00
committed by GitHub
24 changed files with 628 additions and 438 deletions

View File

@@ -4,7 +4,6 @@
"DisableSoftCrash": false,
"devmode": true,
"devmodeoptions": {
"serverName" : "",
"EnableLauncherServer": false,
"hideLoginNotice": false,
"loginNotice": "<BODY><CENTER><SIZE_3><C_4>Welcome to Erupe SU9.1 Beta!<BR><BODY><LEFT><SIZE_2><C_5>Erupe is experimental software<C_7>, we are not liable for any<BR><BODY>issues caused by installing the software!<BR><BODY><BR><BODY><C_4>■Report bugs on Discord!<C_7><BR><BODY><BR><BODY><C_4>■Test everything!<C_7><BR><BODY><BR><BODY><C_4>■Don't talk to softlocking NPCs!<C_7><BR><BODY><BR><BODY><C_4>■Fork the code on GitHub!<C_7><BR><BODY><BR><BODY>Thank you to all of the contributors,<BR><BODY><BR><BODY>this wouldn't exist without you.",
@@ -47,6 +46,10 @@
"name": "Reload",
"enabled": true,
"prefix": "!reload"
}, {
"name": "KeyQuest",
"enabled": false,
"prefix": "!kqf"
}
],
"database": {

View File

@@ -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

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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
)

View File

@@ -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.

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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))

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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())
}
}

View File

@@ -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))

View File

@@ -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)
}

View File

@@ -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))

View File

@@ -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})
}

View File

@@ -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())
}

View File

@@ -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) {}

View File

@@ -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 {

View File

@@ -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.

View File

@@ -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