mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
fix(goocoo): backport personal poogie system from main branch
The personal poogie (goocoo) system was completely broken because the code referenced the old "gook" table/column names while the database schema had been renamed to "goocoo". All read/write queries failed silently, resulting in no poogie data being saved for any player. Backports from main: renamed Gook struct to Goocoo with correct structured fields (22 int16 + 2 uint32), updated all SQL queries to use goocoo table/columns, and added comprehensive packet parsing tests.
This commit is contained in:
371
network/mhfpacket/msg_mhf_guacot_test.go
Normal file
371
network/mhfpacket/msg_mhf_guacot_test.go
Normal file
@@ -0,0 +1,371 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
)
|
||||
|
||||
func TestMsgMhfUpdateGuacotOpcode_Guacot(t *testing.T) {
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
if pkt.Opcode() != network.MSG_MHF_UPDATE_GUACOT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_UPDATE_GUACOT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfEnumerateGuacotOpcode_Guacot(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateGuacot{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_GUACOT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_GUACOT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotParse_SingleEntry(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xAABBCCDD) // AckHandle
|
||||
bf.WriteUint16(1) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
// Goocoo entry
|
||||
bf.WriteUint32(2) // Index
|
||||
for i := 0; i < 22; i++ {
|
||||
bf.WriteInt16(int16(i + 1)) // Data1
|
||||
}
|
||||
bf.WriteUint32(100) // Data2[0]
|
||||
bf.WriteUint32(200) // Data2[1]
|
||||
bf.WriteUint8(5) // Name length
|
||||
bf.WriteBytes([]byte("Porky"))
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xAABBCCDD {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xAABBCCDD", pkt.AckHandle)
|
||||
}
|
||||
if pkt.EntryCount != 1 {
|
||||
t.Errorf("EntryCount = %d, want 1", pkt.EntryCount)
|
||||
}
|
||||
if len(pkt.Goocoos) != 1 {
|
||||
t.Fatalf("len(Goocoos) = %d, want 1", len(pkt.Goocoos))
|
||||
}
|
||||
|
||||
g := pkt.Goocoos[0]
|
||||
if g.Index != 2 {
|
||||
t.Errorf("Index = %d, want 2", g.Index)
|
||||
}
|
||||
if len(g.Data1) != 22 {
|
||||
t.Fatalf("len(Data1) = %d, want 22", len(g.Data1))
|
||||
}
|
||||
for i := 0; i < 22; i++ {
|
||||
if g.Data1[i] != int16(i+1) {
|
||||
t.Errorf("Data1[%d] = %d, want %d", i, g.Data1[i], i+1)
|
||||
}
|
||||
}
|
||||
if len(g.Data2) != 2 {
|
||||
t.Fatalf("len(Data2) = %d, want 2", len(g.Data2))
|
||||
}
|
||||
if g.Data2[0] != 100 {
|
||||
t.Errorf("Data2[0] = %d, want 100", g.Data2[0])
|
||||
}
|
||||
if g.Data2[1] != 200 {
|
||||
t.Errorf("Data2[1] = %d, want 200", g.Data2[1])
|
||||
}
|
||||
if string(g.Name) != "Porky" {
|
||||
t.Errorf("Name = %q, want %q", string(g.Name), "Porky")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotParse_MultipleEntries(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1) // AckHandle
|
||||
bf.WriteUint16(3) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
for idx := uint32(0); idx < 3; idx++ {
|
||||
bf.WriteUint32(idx) // Index
|
||||
for i := 0; i < 22; i++ {
|
||||
bf.WriteInt16(int16(idx*100 + uint32(i)))
|
||||
}
|
||||
bf.WriteUint32(idx * 10) // Data2[0]
|
||||
bf.WriteUint32(idx * 20) // Data2[1]
|
||||
name := []byte("Pog")
|
||||
bf.WriteUint8(uint8(len(name)))
|
||||
bf.WriteBytes(name)
|
||||
}
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
if len(pkt.Goocoos) != 3 {
|
||||
t.Fatalf("len(Goocoos) = %d, want 3", len(pkt.Goocoos))
|
||||
}
|
||||
for idx := uint32(0); idx < 3; idx++ {
|
||||
g := pkt.Goocoos[idx]
|
||||
if g.Index != idx {
|
||||
t.Errorf("Goocoos[%d].Index = %d, want %d", idx, g.Index, idx)
|
||||
}
|
||||
if g.Data1[0] != int16(idx*100) {
|
||||
t.Errorf("Goocoos[%d].Data1[0] = %d, want %d", idx, g.Data1[0], idx*100)
|
||||
}
|
||||
if g.Data2[0] != idx*10 {
|
||||
t.Errorf("Goocoos[%d].Data2[0] = %d, want %d", idx, g.Data2[0], idx*10)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotParse_ZeroEntries(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(42) // AckHandle
|
||||
bf.WriteUint16(0) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
if pkt.EntryCount != 0 {
|
||||
t.Errorf("EntryCount = %d, want 0", pkt.EntryCount)
|
||||
}
|
||||
if len(pkt.Goocoos) != 0 {
|
||||
t.Errorf("len(Goocoos) = %d, want 0", len(pkt.Goocoos))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotParse_DeletionEntry(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1) // AckHandle
|
||||
bf.WriteUint16(1) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
bf.WriteUint32(0) // Index
|
||||
// Data1[0] = 0 signals deletion
|
||||
bf.WriteInt16(0)
|
||||
for i := 1; i < 22; i++ {
|
||||
bf.WriteInt16(0)
|
||||
}
|
||||
bf.WriteUint32(0) // Data2[0]
|
||||
bf.WriteUint32(0) // Data2[1]
|
||||
bf.WriteUint8(0) // Empty name
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
g := pkt.Goocoos[0]
|
||||
if g.Data1[0] != 0 {
|
||||
t.Errorf("Data1[0] = %d, want 0 (deletion marker)", g.Data1[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotParse_EmptyName(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1) // AckHandle
|
||||
bf.WriteUint16(1) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
bf.WriteUint32(0) // Index
|
||||
for i := 0; i < 22; i++ {
|
||||
bf.WriteInt16(1)
|
||||
}
|
||||
bf.WriteUint32(0) // Data2[0]
|
||||
bf.WriteUint32(0) // Data2[1]
|
||||
bf.WriteUint8(0) // Empty name
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
if len(pkt.Goocoos[0].Name) != 0 {
|
||||
t.Errorf("Name length = %d, want 0", len(pkt.Goocoos[0].Name))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfEnumerateGuacotParse(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x12345678) // AckHandle
|
||||
bf.WriteUint16(0) // Unk0
|
||||
bf.WriteUint16(0) // Unk1
|
||||
bf.WriteUint16(0) // Unk2
|
||||
|
||||
pkt := &MsgMhfEnumerateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x12345678 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x12345678", pkt.AckHandle)
|
||||
}
|
||||
if pkt.Unk0 != 0 {
|
||||
t.Errorf("Unk0 = %d, want 0", pkt.Unk0)
|
||||
}
|
||||
if pkt.Unk1 != 0 {
|
||||
t.Errorf("Unk1 = %d, want 0", pkt.Unk1)
|
||||
}
|
||||
if pkt.Unk2 != 0 {
|
||||
t.Errorf("Unk2 = %d, want 0", pkt.Unk2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotBuild_NotImplemented(t *testing.T) {
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
err := pkt.Build(byteframe.NewByteFrame(), nil)
|
||||
if err == nil {
|
||||
t.Error("Build() should return error (not implemented)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfEnumerateGuacotBuild_NotImplemented(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateGuacot{}
|
||||
err := pkt.Build(byteframe.NewByteFrame(), nil)
|
||||
if err == nil {
|
||||
t.Error("Build() should return error (not implemented)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoocooStruct_Data1Size(t *testing.T) {
|
||||
// Verify 22 int16 entries = 44 bytes of outfit/appearance data
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1) // AckHandle
|
||||
bf.WriteUint16(1) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
bf.WriteUint32(0) // Index
|
||||
for i := 0; i < 22; i++ {
|
||||
bf.WriteInt16(int16(i * 3))
|
||||
}
|
||||
bf.WriteUint32(0xDEAD) // Data2[0]
|
||||
bf.WriteUint32(0xBEEF) // Data2[1]
|
||||
bf.WriteUint8(0) // No name
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
_ = pkt.Parse(bf, nil)
|
||||
|
||||
g := pkt.Goocoos[0]
|
||||
|
||||
// Verify all 22 data slots are correctly read
|
||||
for i := 0; i < 22; i++ {
|
||||
expected := int16(i * 3)
|
||||
if g.Data1[i] != expected {
|
||||
t.Errorf("Data1[%d] = %d, want %d", i, g.Data1[i], expected)
|
||||
}
|
||||
}
|
||||
|
||||
if g.Data2[0] != 0xDEAD {
|
||||
t.Errorf("Data2[0] = 0x%X, want 0xDEAD", g.Data2[0])
|
||||
}
|
||||
if g.Data2[1] != 0xBEEF {
|
||||
t.Errorf("Data2[1] = 0x%X, want 0xBEEF", g.Data2[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoocooSerialization_Roundtrip(t *testing.T) {
|
||||
// Simulate what handleMsgMhfUpdateGuacot does when saving to DB
|
||||
goocoo := Goocoo{
|
||||
Index: 1,
|
||||
Data1: make([]int16, 22),
|
||||
Data2: []uint32{0x1234, 0x5678},
|
||||
Name: []byte("MyPoogie"),
|
||||
}
|
||||
goocoo.Data1[0] = 5 // outfit type (non-zero = exists)
|
||||
goocoo.Data1[1] = 100 // some appearance data
|
||||
goocoo.Data1[21] = -50 // test negative int16
|
||||
|
||||
// Serialize (matches handler logic)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(goocoo.Index)
|
||||
for i := range goocoo.Data1 {
|
||||
bf.WriteInt16(goocoo.Data1[i])
|
||||
}
|
||||
for i := range goocoo.Data2 {
|
||||
bf.WriteUint32(goocoo.Data2[i])
|
||||
}
|
||||
bf.WriteUint8(uint8(len(goocoo.Name)))
|
||||
bf.WriteBytes(goocoo.Name)
|
||||
|
||||
// Deserialize and verify
|
||||
data := bf.Data()
|
||||
rbf := byteframe.NewByteFrameFromBytes(data)
|
||||
|
||||
index := rbf.ReadUint32()
|
||||
if index != 1 {
|
||||
t.Errorf("index = %d, want 1", index)
|
||||
}
|
||||
|
||||
data1_0 := rbf.ReadInt16()
|
||||
if data1_0 != 5 {
|
||||
t.Errorf("data1[0] = %d, want 5", data1_0)
|
||||
}
|
||||
data1_1 := rbf.ReadInt16()
|
||||
if data1_1 != 100 {
|
||||
t.Errorf("data1[1] = %d, want 100", data1_1)
|
||||
}
|
||||
// Skip to data1[21]
|
||||
for i := 2; i < 21; i++ {
|
||||
rbf.ReadInt16()
|
||||
}
|
||||
data1_21 := rbf.ReadInt16()
|
||||
if data1_21 != -50 {
|
||||
t.Errorf("data1[21] = %d, want -50", data1_21)
|
||||
}
|
||||
|
||||
d2_0 := rbf.ReadUint32()
|
||||
if d2_0 != 0x1234 {
|
||||
t.Errorf("data2[0] = 0x%X, want 0x1234", d2_0)
|
||||
}
|
||||
d2_1 := rbf.ReadUint32()
|
||||
if d2_1 != 0x5678 {
|
||||
t.Errorf("data2[1] = 0x%X, want 0x5678", d2_1)
|
||||
}
|
||||
|
||||
nameLen := rbf.ReadUint8()
|
||||
if nameLen != 8 {
|
||||
t.Errorf("nameLen = %d, want 8", nameLen)
|
||||
}
|
||||
name := rbf.ReadBytes(uint(nameLen))
|
||||
if string(name) != "MyPoogie" {
|
||||
t.Errorf("name = %q, want %q", string(name), "MyPoogie")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoocooEntrySize(t *testing.T) {
|
||||
// Each goocoo entry in the packet should be:
|
||||
// 4 (index) + 22*2 (data1) + 2*4 (data2) + 1 (name len) + N (name)
|
||||
// = 4 + 44 + 8 + 1 + N = 57 + N bytes
|
||||
name := []byte("Test")
|
||||
expectedSize := 4 + 44 + 8 + 1 + len(name)
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0) // index
|
||||
for i := 0; i < 22; i++ {
|
||||
bf.WriteInt16(0)
|
||||
}
|
||||
bf.WriteUint32(0) // data2[0]
|
||||
bf.WriteUint32(0) // data2[1]
|
||||
bf.WriteUint8(uint8(len(name))) // name len
|
||||
bf.WriteBytes(name)
|
||||
|
||||
if len(bf.Data()) != expectedSize {
|
||||
t.Errorf("entry size = %d bytes, want %d bytes (57 + %d name)", len(bf.Data()), expectedSize, len(name))
|
||||
}
|
||||
}
|
||||
@@ -8,21 +8,18 @@ import (
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
type Gook struct {
|
||||
Exists bool
|
||||
Index uint32
|
||||
Type uint16
|
||||
Data []byte
|
||||
NameLen uint8
|
||||
Name []byte
|
||||
type Goocoo struct {
|
||||
Index uint32
|
||||
Data1 []int16
|
||||
Data2 []uint32
|
||||
Name []byte
|
||||
}
|
||||
|
||||
// MsgMhfUpdateGuacot represents the MSG_MHF_UPDATE_GUACOT
|
||||
type MsgMhfUpdateGuacot struct {
|
||||
AckHandle uint32
|
||||
EntryCount uint16
|
||||
Unk0 uint16 // Hardcoded 0 in binary
|
||||
Gooks []Gook
|
||||
Goocoos []Goocoo
|
||||
}
|
||||
|
||||
// Opcode returns the ID associated with this packet type.
|
||||
@@ -34,20 +31,18 @@ func (m *MsgMhfUpdateGuacot) Opcode() network.PacketID {
|
||||
func (m *MsgMhfUpdateGuacot) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.EntryCount = bf.ReadUint16()
|
||||
m.Unk0 = bf.ReadUint16()
|
||||
bf.ReadUint16() // Zeroed
|
||||
for i := 0; i < int(m.EntryCount); i++ {
|
||||
e := Gook{}
|
||||
e.Index = bf.ReadUint32()
|
||||
e.Type = bf.ReadUint16()
|
||||
e.Data = bf.ReadBytes(50)
|
||||
e.NameLen = bf.ReadUint8()
|
||||
e.Name = bf.ReadBytes(uint(e.NameLen))
|
||||
if e.Type > 0 {
|
||||
e.Exists = true
|
||||
} else {
|
||||
e.Exists = false
|
||||
var temp Goocoo
|
||||
temp.Index = bf.ReadUint32()
|
||||
for j := 0; j < 22; j++ {
|
||||
temp.Data1 = append(temp.Data1, bf.ReadInt16())
|
||||
}
|
||||
m.Gooks = append(m.Gooks, e)
|
||||
for j := 0; j < 2; j++ {
|
||||
temp.Data2 = append(temp.Data2, bf.ReadUint32())
|
||||
}
|
||||
temp.Name = bf.ReadBytes(uint(bf.ReadUint8()))
|
||||
m.Goocoos = append(m.Goocoos, temp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -723,59 +723,55 @@ func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func getGookData(s *Session, cid uint32) (uint16, []byte) {
|
||||
var data []byte
|
||||
var count uint16
|
||||
bf := byteframe.NewByteFrame()
|
||||
func getGoocooData(s *Session, cid uint32) [][]byte {
|
||||
var goocoo []byte
|
||||
var goocoos [][]byte
|
||||
for i := 0; i < 5; i++ {
|
||||
err := s.server.db.QueryRow(fmt.Sprintf("SELECT gook%d FROM gook WHERE id=$1", i), cid).Scan(&data)
|
||||
err := s.server.db.QueryRow(fmt.Sprintf("SELECT goocoo%d FROM goocoo WHERE id=$1", i), cid).Scan(&goocoo)
|
||||
if err != nil {
|
||||
s.server.db.Exec("INSERT INTO gook (id) VALUES ($1)", s.charID)
|
||||
return 0, bf.Data()
|
||||
s.server.db.Exec("INSERT INTO goocoo (id) VALUES ($1)", s.charID)
|
||||
return goocoos
|
||||
}
|
||||
if err == nil && data != nil {
|
||||
count++
|
||||
if s.charID == cid && count == 1 {
|
||||
gook := byteframe.NewByteFrameFromBytes(data)
|
||||
bf.WriteBytes(gook.ReadBytes(4))
|
||||
d := gook.ReadBytes(2)
|
||||
bf.WriteBytes(d)
|
||||
bf.WriteBytes(d)
|
||||
bf.WriteBytes(gook.DataFromCurrent())
|
||||
} else {
|
||||
bf.WriteBytes(data)
|
||||
}
|
||||
if err == nil && goocoo != nil {
|
||||
goocoos = append(goocoos, goocoo)
|
||||
}
|
||||
}
|
||||
return count, bf.Data()
|
||||
return goocoos
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateGuacot(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateGuacot)
|
||||
bf := byteframe.NewByteFrame()
|
||||
count, data := getGookData(s, s.charID)
|
||||
bf.WriteUint16(count)
|
||||
bf.WriteBytes(data)
|
||||
goocoos := getGoocooData(s, s.charID)
|
||||
bf.WriteUint16(uint16(len(goocoos)))
|
||||
bf.WriteUint16(0)
|
||||
for _, goocoo := range goocoos {
|
||||
bf.WriteBytes(goocoo)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfUpdateGuacot(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfUpdateGuacot)
|
||||
for _, gook := range pkt.Gooks {
|
||||
if !gook.Exists {
|
||||
s.server.db.Exec(fmt.Sprintf("UPDATE gook SET gook%d=NULL WHERE id=$1", gook.Index), s.charID)
|
||||
for _, goocoo := range pkt.Goocoos {
|
||||
if goocoo.Data1[0] == 0 {
|
||||
s.server.db.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=NULL WHERE id=$1", goocoo.Index), s.charID)
|
||||
} else {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(gook.Index)
|
||||
bf.WriteUint16(gook.Type)
|
||||
bf.WriteBytes(gook.Data)
|
||||
bf.WriteUint8(gook.NameLen)
|
||||
bf.WriteBytes(gook.Name)
|
||||
s.server.db.Exec(fmt.Sprintf("UPDATE gook SET gook%d=$1 WHERE id=$2", gook.Index), bf.Data(), s.charID)
|
||||
dumpSaveData(s, bf.Data(), fmt.Sprintf("goocoo-%d", gook.Index))
|
||||
bf.WriteUint32(goocoo.Index)
|
||||
for i := range goocoo.Data1 {
|
||||
bf.WriteInt16(goocoo.Data1[i])
|
||||
}
|
||||
for i := range goocoo.Data2 {
|
||||
bf.WriteUint32(goocoo.Data2[i])
|
||||
}
|
||||
bf.WriteUint8(uint8(len(goocoo.Name)))
|
||||
bf.WriteBytes(goocoo.Name)
|
||||
s.server.db.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=$1 WHERE id=$2", goocoo.Index), bf.Data(), s.charID)
|
||||
dumpSaveData(s, bf.Data(), fmt.Sprintf("goocoo-%d", goocoo.Index))
|
||||
}
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfInfoScenarioCounter(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
@@ -213,10 +213,12 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteBytes(houseFurniture)
|
||||
case 10: // Garden
|
||||
bf.WriteBytes(garden)
|
||||
c, d := getGookData(s, pkt.CharID)
|
||||
bf.WriteUint16(c)
|
||||
goocoos := getGoocooData(s, pkt.CharID)
|
||||
bf.WriteUint16(uint16(len(goocoos)))
|
||||
bf.WriteUint16(0)
|
||||
bf.WriteBytes(d)
|
||||
for _, goocoo := range goocoos {
|
||||
bf.WriteBytes(goocoo)
|
||||
}
|
||||
}
|
||||
if len(bf.Data()) == 0 {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
|
||||
Reference in New Issue
Block a user