diff --git a/.gitignore b/.gitignore index a9d66693f..deb249051 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ www/jp/ bin/*.bin bin/quests/*.bin +bin/questlists/*.bin bin/scenarios/*.bin bin/debug/*.bin savedata/ diff --git a/migrations/000004_character_additional.down.sql b/migrations/000004_character_additional.down.sql index 5229381cb..242ef904e 100644 --- a/migrations/000004_character_additional.down.sql +++ b/migrations/000004_character_additional.down.sql @@ -9,5 +9,6 @@ ALTER TABLE characters DROP COLUMN platedata, DROP COLUMN platemyset, DROP COLUMN rengokudata; + DROP COLUMN savemercenary; END; \ No newline at end of file diff --git a/migrations/000004_character_additional.up.sql b/migrations/000004_character_additional.up.sql index 555ac5f4e..2faf59809 100644 --- a/migrations/000004_character_additional.up.sql +++ b/migrations/000004_character_additional.up.sql @@ -9,5 +9,6 @@ ALTER TABLE characters ADD COLUMN platedata bytea, ADD COLUMN platemyset bytea, ADD COLUMN rengokudata bytea; + ADD COLUMN savemercenary bytea; END; \ No newline at end of file diff --git a/network/mhfpacket/msg_mhf_add_ud_tactics_point.go b/network/mhfpacket/msg_mhf_add_ud_tactics_point.go index e4e5442d2..fd6b40fd8 100644 --- a/network/mhfpacket/msg_mhf_add_ud_tactics_point.go +++ b/network/mhfpacket/msg_mhf_add_ud_tactics_point.go @@ -6,7 +6,11 @@ import ( ) // MsgMhfAddUdTacticsPoint represents the MSG_MHF_ADD_UD_TACTICS_POINT -type MsgMhfAddUdTacticsPoint struct{} +type MsgMhfAddUdTacticsPoint struct{ + AckHandle uint32 + Unk0 uint16 + Unk1 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfAddUdTacticsPoint) Opcode() network.PacketID { @@ -15,7 +19,10 @@ func (m *MsgMhfAddUdTacticsPoint) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfAddUdTacticsPoint) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_enumerate_shop.go b/network/mhfpacket/msg_mhf_enumerate_shop.go index 1920d75de..597e137eb 100644 --- a/network/mhfpacket/msg_mhf_enumerate_shop.go +++ b/network/mhfpacket/msg_mhf_enumerate_shop.go @@ -8,7 +8,7 @@ import ( // MsgMhfEnumerateShop represents the MSG_MHF_ENUMERATE_SHOP type MsgMhfEnumerateShop struct { AckHandle uint32 - ShopType uint8 // 1 running gachas, 10 normal shop extensions + ShopType uint8 // 1 running gachas, 10 normal shop extensions, 8 Diva Defense shop ShopID uint32 Unk2 uint16 // 00 80 running gachas, 00 20 normal shop Unk3 uint8 diff --git a/network/mhfpacket/msg_mhf_get_earth_value.go b/network/mhfpacket/msg_mhf_get_earth_value.go index de83c0a61..449a251fe 100644 --- a/network/mhfpacket/msg_mhf_get_earth_value.go +++ b/network/mhfpacket/msg_mhf_get_earth_value.go @@ -10,7 +10,7 @@ type MsgMhfGetEarthValue struct { AckHandle uint32 Unk0 uint32 Unk1 uint32 - Unk2 uint32 + ReqType uint32 Unk3 uint32 Unk4 uint32 Unk5 uint32 @@ -27,7 +27,7 @@ func (m *MsgMhfGetEarthValue) Parse(bf *byteframe.ByteFrame) error { m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint32() m.Unk1 = bf.ReadUint32() - m.Unk2 = bf.ReadUint32() + m.ReqType = bf.ReadUint32() m.Unk3 = bf.ReadUint32() m.Unk4 = bf.ReadUint32() m.Unk5 = bf.ReadUint32() diff --git a/network/mhfpacket/msg_mhf_get_ud_ranking.go b/network/mhfpacket/msg_mhf_get_ud_ranking.go index 4833a688b..721269f47 100644 --- a/network/mhfpacket/msg_mhf_get_ud_ranking.go +++ b/network/mhfpacket/msg_mhf_get_ud_ranking.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetUdRanking represents the MSG_MHF_GET_UD_RANKING -type MsgMhfGetUdRanking struct{} +type MsgMhfGetUdRanking struct{ + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetUdRanking) Opcode() network.PacketID { @@ -15,7 +17,8 @@ func (m *MsgMhfGetUdRanking) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetUdRanking) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_register_event.go b/network/mhfpacket/msg_mhf_register_event.go index 2b037227f..126caa400 100644 --- a/network/mhfpacket/msg_mhf_register_event.go +++ b/network/mhfpacket/msg_mhf_register_event.go @@ -6,7 +6,13 @@ import ( ) // MsgMhfRegisterEvent represents the MSG_MHF_REGISTER_EVENT -type MsgMhfRegisterEvent struct{} +type MsgMhfRegisterEvent struct{ + AckHandle uint32 + Unk0 uint16 + Unk1 uint16 + Unk2 uint16 + Unk3 uint16 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfRegisterEvent) Opcode() network.PacketID { @@ -15,7 +21,12 @@ func (m *MsgMhfRegisterEvent) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfRegisterEvent) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() + m.Unk2 = bf.ReadUint16() + m.Unk3 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_save_mercenary.go b/network/mhfpacket/msg_mhf_save_mercenary.go index 9b566af7e..2541b9e9b 100644 --- a/network/mhfpacket/msg_mhf_save_mercenary.go +++ b/network/mhfpacket/msg_mhf_save_mercenary.go @@ -6,7 +6,11 @@ import ( ) // MsgMhfSaveMercenary represents the MSG_MHF_SAVE_MERCENARY -type MsgMhfSaveMercenary struct{} +type MsgMhfSaveMercenary struct{ + AckHandle uint32 + DataSize uint32 + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfSaveMercenary) Opcode() network.PacketID { @@ -15,7 +19,10 @@ func (m *MsgMhfSaveMercenary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfSaveMercenary) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.DataSize = bf.ReadUint32() + m.RawDataPayload = bf.ReadBytes(uint(m.DataSize)) + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_sys_create_acquire_semaphore.go b/network/mhfpacket/msg_sys_create_acquire_semaphore.go index d0300e7e3..4d91486cc 100644 --- a/network/mhfpacket/msg_sys_create_acquire_semaphore.go +++ b/network/mhfpacket/msg_sys_create_acquire_semaphore.go @@ -6,7 +6,10 @@ import ( ) // MsgSysCreateAcquireSemaphore represents the MSG_SYS_CREATE_ACQUIRE_SEMAPHORE -type MsgSysCreateAcquireSemaphore struct{} +type MsgSysCreateAcquireSemaphore struct{ + AckHandle uint32 + Unk0 []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgSysCreateAcquireSemaphore) Opcode() network.PacketID { @@ -15,7 +18,9 @@ func (m *MsgSysCreateAcquireSemaphore) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysCreateAcquireSemaphore) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadBytes(19) + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_sys_load_register.go b/network/mhfpacket/msg_sys_load_register.go index 155c63d9c..ac646b47b 100644 --- a/network/mhfpacket/msg_sys_load_register.go +++ b/network/mhfpacket/msg_sys_load_register.go @@ -6,7 +6,13 @@ import ( ) // MsgSysLoadRegister represents the MSG_SYS_LOAD_REGISTER -type MsgSysLoadRegister struct{} +type MsgSysLoadRegister struct{ + AckHandle uint32 + Unk0 uint16 + Unk1 uint16 + Unk2 uint16 + Unk3 uint16 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysLoadRegister) Opcode() network.PacketID { @@ -15,7 +21,12 @@ func (m *MsgSysLoadRegister) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysLoadRegister) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() + m.Unk2 = bf.ReadUint16() + m.Unk3 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 19433c21d..38665bc2e 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -23,7 +23,7 @@ import ( // Temporary function to just return no results for a MSG_MHF_ENUMERATE* packet func stubEnumerateNoResults(s *Session, ackHandle uint32) { enumBf := byteframe.NewByteFrame() - enumBf.WriteUint16(0) // Entry count (count for quests, rankings, events, etc.) + enumBf.WriteUint32(0) // Entry count (count for quests, rankings, events, etc.) doSizedAckResp(s, ackHandle, enumBf.Data()) } @@ -177,7 +177,7 @@ func saveCompress(rawData []byte) ([]byte, error) { func updateRights(s *Session) { update := &mhfpacket.MsgSysUpdateRight{ Unk0: 0, - Unk1: 0x4E, + Unk1: 0x0E, //0e with normal sub 4e when having premium it's probably a bitfield? // 01 = Character can take quests at allows // 02 = Hunter Life, normal quests core sub // 03 = Extra Course, extra quests, town boxes, QOL course, core sub @@ -368,7 +368,7 @@ func handleMsgSysTime(s *Session, p mhfpacket.MHFPacket) { resp := &mhfpacket.MsgSysTime{ GetRemoteTime: false, - Timestamp: uint32(time.Now().Unix()), + Timestamp: uint32(time.Now().In(time.FixedZone("UTC+9", 9*60*60)).Unix()), // JP timezone } s.QueueSendMHF(resp) } @@ -837,7 +837,10 @@ func handleMsgSysCloseMutex(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysCreateSemaphore(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysCreateAcquireSemaphore) + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x1D}) +} func handleMsgSysDeleteSemaphore(s *Session, p mhfpacket.MHFPacket) {} @@ -853,7 +856,11 @@ func handleMsgSysCheckSemaphore(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysLoadRegister(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysLoadRegister(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysLoadRegister) + data, _ := hex.DecodeString("000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + doSizedAckResp(s, pkt.AckHandle, data) +} func handleMsgSysNotifyRegister(s *Session, p mhfpacket.MHFPacket) {} @@ -1094,7 +1101,10 @@ func handleMsgMhfSaveFavoriteQuest(s *Session, p mhfpacket.MHFPacket) { s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) } -func handleMsgMhfRegisterEvent(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfRegisterEvent(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfRegisterEvent) + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfReleaseEvent(s *Session, p mhfpacket.MHFPacket) {} @@ -1210,26 +1220,14 @@ func handleMsgMhfMercenaryHuntdata(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfEntryRookieGuild(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { + // local files are easier for now, probably best would be to generate dynamically pkt := p.(*mhfpacket.MsgMhfEnumerateQuest) - - // questlists seem to be returned based on their internal values, intended order has these as: - // 0 > 42 > 84 > 126 > 168, what actually makes it stop requesting quests I do not know yet - // INSERT INTO questlists (ind, questlist) VALUES ('0', pg_read_binary_file('c:\save\quest_0_0.bin')); - // INSERT INTO questlists (ind, questlist) VALUES ('42', pg_read_binary_file('c:\save\quest_42_2A.bin')); - // INSERT INTO questlists (ind, questlist) VALUES ('84', pg_read_binary_file('c:\save\quest_84_54.bin')); - // INSERT INTO questlists (ind, questlist) VALUES ('126', pg_read_binary_file('c:\save\quest_126_7E.bin')); - // INSERT INTO questlists (ind, questlist) VALUES ('168', pg_read_binary_file('c:\save\quest_168_A8.bin')); - var data []byte - err := s.server.db.QueryRow("SELECT questlist FROM questlists WHERE ind = $1", int(pkt.QuestList)).Scan(&data) + data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("questlists/list_%d.bin",pkt.QuestList))) if err != nil { - fmt.Println("Couldn't find quest list.") + fmt.Printf("questlists/list_%d.bin",pkt.QuestList) stubEnumerateNoResults(s, pkt.AckHandle) } else { - if len(data) > 0 { doSizedAckResp(s, pkt.AckHandle, data) - } else { - stubEnumerateNoResults(s, pkt.AckHandle) - } } // Update the client's rights as well: updateRights(s) @@ -1277,32 +1275,50 @@ func handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateShop) - + // SHOP TYPES: + // 01 = Running Gachas, 04 = N Points, 05 = GCP, 07 = Item to GCP, 08 = Diva Defense, 10 = Hunter's Road + // STORE FORMAT: + // Int16: total item count + // Int16: total item count + // ITEM FORMAT: + // int16 x 2: Unique item hash for tracking server side purchases? Swapping across items didn't change image/cost/function etc. + // int16: Unk, padding? + // int16: Item ID + // int16: Unk, likely padding? + // int16: GCP returns + // int16: Number traded at once? + // int16: HR or SR Requirement + // int16: Whichever of the above it isn't? + // int16: GR Requirement + // int16: Store level requirement + // int16: Maximum quantity purchasable + // int16: Unk + // int16: Road floors cleared requirement + // int16: Road White Fatalis weekly kills if pkt.ShopType == 1 { stubEnumerateNoResults(s, pkt.AckHandle) - } else if pkt.ShopType == 10 { - // shops might be better off being in a persistent database as they changed semi-frequently - // TODO (Fist): Identify what shops are actually on which IDs (GCP, N Point, Road, Festival, Diva Defense GCP, Zeny, Netcafe, Gacha Coins) - if pkt.ShopID == 4 { - data, _ := hex.DecodeString("000800082C93447100003B80000003E80001000000000001000000000000000100000C7BE2C200003A66000003E80001000000000001000000000000000100000B51B75F00003AF5000003E80001000000000001000000000000000B00003693367000003DF7000003E80001000000000001000000000000000B000019B22C8D00003DF8000003E80001000000000001000000000000000B00001B3F4BBE00003DF9000003E80001000000000001000000000000000B00001A91C77B00003DFA000003E80001000000000001000000000000000B00000A5FDB5C00003DFB000003E80001000000000001000000000000000B0000") - doSizedAckResp(s, pkt.AckHandle, data) - } else if pkt.ShopID == 6 { - data, _ := hex.DecodeString("0008000807749AA900004027000000C800010000000000C8000000000000000B000031950D2A00004028000000DC00010000000000C8000000000000001400003697CA6700004029000000FA00010000000000C8000000000000001E0000005788980000402A0000012C00010000000000C8000000000000002800000C727D150000406E000000C800010000000000C8000000000000000B000024D1B6460000406F000000DC00010000000000C80000000000000014000020B429A300004070000000FA00010000000000C8000000000000001E00003FFDDD84000040710000012C00010000000000C800000000000000280000") - doSizedAckResp(s, pkt.AckHandle, data) - } else if pkt.ShopID == 7 { - // hunter's road, 30 bytes chunks, item ID at 0x10, cost at 0x14, floor req at 0x04 - data, _ := hex.DecodeString("002C002C2C934441000030AC000001F400010000000000010000008C0000000100000A51B75F000030AD000001F400010000000000010000008C00000001000019B22C9D000030AE000003E80001000000000001000000410000000100001B91C74B000030AF000003E800010000000000010000004100000001000007749AB9000030B0000005DC00010000000000010000000F0000000100003797CA67000030B1000005DC0001000000000001000000230000000100000C707D05000030B2000005DC00010000000000010000003200000001000020B629B3000030B3000005DC00010000000000010000000F0000000100002D934471000030B4000005DC0001000000000001000000140000000100000B53B74F000030B5000005DC00010000000000010000003200000001000018B02CBD0000386D000003E80001000000000001000000010000000100001B91C76B0000386B00000BB800010000000000010000000500000001000006769AA90000386C000013880001000000000001000000050000000100003697CA5700003997000001F400010000000000010000002E0000000A00000C707D2500003A05000001F400010000000000010000002E0000000A000020B6298300003AAC000001F400010000000000010000002E0000000A00002D91445100003B08000001F400010000000000010000002E0000000A00000B53B75F00003FD2000001F400010000000000010000001F0000000A000019B02C8D00003FD3000001F400010000000000010000001F0000000A00001B91C77B00003835000001F400010000000000010000002E0000000A000007769AA900003AAD000001F400010000000000010000002E0000000A00003695CA4700003B07000001F400010000000000010000002E0000000A00000C727D3500003D9D000001F400010000000000010000002E0000000A000021B629B300003FD4000001F400010000000000010000002E0000003200002C91445100003FD5000001F400010000000000010000002E0000003200000A51B77F00003858000003E80001000000000001000000050005001E000019B02CAD00003AB8000003E80001000000000001000000050000002800001A93C76B00003B0D000003E800010000000000010000000500000032000006769A9900003DFC000003E80001000000000001000000050000003C00003695CA670000407C000003E80001000000000001000000050000004600000C727D050000407D000003E800010000000000010000000500000050000021B4299300003A62000003E80001000000000001000000050000006400002D93444100002C16000001F40001000000000001000000140000000100000B51B74F00003C58000003E800010000000000C8000000020002000B000019B02CBD0000392F000003E800010000000000C8000000050000000B00001B93C76B00003860000003E80001000000000001000000050000005A000006749A8900003861000003E80001000000000001000000050000006400003697CA7700003862000003E80001000000000001000000050000006E00000D727D3500003863000003E800010000000000010000000500000078000020B4299300003864000003E80001000000000001000000050000008200002D934451000036C5000003E80001000000000001000000050000008C00000B51B76F0000399600000FA000010000000000010000000A00000014000018B22CAD00003996000007D000010000000000010000000A0000002800001A93C75B000037DA000003E800010000000000C8000000050000000A0000") - doSizedAckResp(s, pkt.AckHandle, data) - } else if pkt.ShopID == 8 { - data, _ := hex.DecodeString("000B000B00758A5800003836000000C800010000000000010000000A00000000000130F3B54600003837000000C800010000000000010000000A0000000000012BFDDC840000383C000001900001000000000001000000020000000000010C79E3420000383E00000190000100000000000100000002000000000001269337B000003839000001F40001000000000001000000020000000000011B1D4B7E0000383A000002BC0001000000000001000000020000000000020A7FDA9C0000383D0000032000010000000000010000000200000000000235950C2A0000383B000003E8000100000000000100000002000000000002007588580000383F0000044C00010000000000010000000200000000000224F3B5C600003838000007D00001000000000001000000030000000000033FFDDE840000386E000003E8000100000000000100000005000000000004") + } else if pkt.ShopType == 7 { + // GCP conversion store + if pkt.ShopID == 0{ + // Items to GCP exchange. Gou Tickets, Shiten Tickets, GP Tickets + data, _ := hex.DecodeString("000300033a9186fb000033860000000a000100000000000000000000000000000000097fdb1c0000067e0000000a0001000000000000000000000000000000001374db29000027c300000064000100000000000000000000000000000000") doSizedAckResp(s, pkt.AckHandle, data) } else { - // generic respose for no items doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } - + } else if pkt.ShopType == 8 { + // Dive Defense sections + // 00 = normal level limited exchange store, 05 = GCP skill store, 07 = limited quantity exchange + if pkt.ShopID == 5{ + // diva defense skill level limited store + data, _ := hex.DecodeString("001f001f2c9365c1000000010000001e000a0000000000000000000a0000000000001979f1c2000000020000003c000a0000000000000000000a0000000000003e5197df000000030000003c000a0000000000000000000a000000000000219337c0000000040000001e000a0000000000000000000a00000000000009b24c9d000000140000001e000a0000000000000000000a0000000000001f1d496e000000150000001e000a0000000000000000000a0000000000003b918fcb000000160000003c000a0000000000000000000a0000000000000b7fd81c000000170000003c000a0000000000000000000a0000000000001374f239000000180000003c000a0000000000000000000a00000000000026950cba0000001c0000003c000a0000000000000000000a0000000000003797eae70000001d0000003c000a012b000000000000000a00000000000015758ad8000000050000003c00000000000000000000000a0000000000003c7035050000000600000050000a0000000000000001000a00000000000024f3b5560000000700000050000a0000000000000001000a00000000000000b600330000000800000050000a0000000000000001000a0000000000002efdce840000001900000050000a0000000000000001000a0000000000002d9365f10000001a00000050000a0000000000000001000a0000000000001979f3420000001f00000050000a012b000000000001000a0000000000003f5397cf0000002000000050000a012b000000000001000a000000000000319337c00000002100000050000a012b000000000001000a00000000000008b04cbd0000000900000064000a0000000000000002000a0000000000000b1d4b6e0000000a00000064000a0000000000000002000a0000000000003b918feb0000000b00000064000a0000000000000002000a0000000000001b7fd81c0000000c00000064000a0000000000000002000a0000000000001276f2290000000d00000064000a0000000000000002000a00000000000022950cba0000000e000000c8000a0000000000000002000a0000000000003697ead70000000f000001f4000a0000000000000003000a00000000000005758a5800000010000003e8000a0000000000000003000a0000000000003c7035250000001b000001f4000a0000000000010003000a00000000000034f3b5d60000001e00000064000a012b000000000003000a00000000000000b600030000002200000064000a0000000000010003000a000000000000") + doSizedAckResp(s, pkt.AckHandle, data) + } else { + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + } + } else { + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } - } func handleMsgMhfGetExtraInfo(s *Session, p mhfpacket.MHFPacket) {} @@ -1386,7 +1402,7 @@ func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateCafepoint) resp := byteframe.NewByteFrame() - resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x8b}) doSizedAckResp(s, pkt.AckHandle, resp.Data()) } @@ -1418,16 +1434,24 @@ func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfSaveMercenary) + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfReadMercenaryW) - - // Unk format: - doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + var data []byte + err := s.server.db.QueryRow("SELECT savemercenary FROM characters WHERE id = $1", s.charID).Scan(&data) + if err != nil { + s.logger.Fatal("Failed to get savemercenary data from db", zap.Error(err)) + } + doSizedAckResp(s, pkt.AckHandle, data) } -func handleMsgMhfReadMercenaryM(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfReadMercenaryM(s *Session, p mhfpacket.MHFPacket) { + // I'm assuming this is just called if your character is male over female but haven't checked +} func handleMsgMhfContractMercenary(s *Session, p mhfpacket.MHFPacket) {} @@ -1534,8 +1558,8 @@ func handleMsgMhfGetEtcPoints(s *Session, p mhfpacket.MHFPacket) { resp := byteframe.NewByteFrame() resp.WriteUint8(0x3) // Maybe a count of uint32(s)? resp.WriteUint32(0) - resp.WriteUint32(0) - resp.WriteUint32(0) + resp.WriteUint32(14) + resp.WriteUint32(14) doSizedAckResp(s, pkt.AckHandle, resp.Data()) } @@ -1561,25 +1585,41 @@ func handleMsgMhfUpdateMyhouseInfo(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetWeeklySchedule) - + //japanese timestamps as client needs to be in japanese locale + var t = time.Now().In(time.FixedZone("UTC+9", 9*60*60)) + year,month,day := t.Date() + midnight := time.Date(year, month, day, 0, 0, 0, 0, t.Location()).Add(time.Hour) + // ActiveFeatures is a bit field, 0x3FFF is all 14 active features. + // Long term it should probably be made persistent and simply cycle a couple daily + // Times seem to need to be midnight which is likely why matching timezone was required originally eventSchedules := []struct { StartTime time.Time - Unk0 uint32 // Event ID? + ActiveFeatures uint32 Unk1 uint16 }{ { - StartTime: time.Now().Add(time.Duration(-5) * time.Minute), // Event started 5 minutes ago. - Unk0: 4, + StartTime: midnight.Add(-24*time.Hour), // midnight of previous day. + ActiveFeatures: 0x3FFF, + Unk1: 0, + }, + { + StartTime: midnight, // midnight of this day. + ActiveFeatures: 0x3FFF, + Unk1: 0, + }, + { + StartTime: midnight.Add(24*time.Hour), // midnight of following day. + ActiveFeatures: 0x3FFF, Unk1: 0, }, } resp := byteframe.NewByteFrame() resp.WriteUint8(uint8(len(eventSchedules))) // Entry count, client only parses the first 7 or 8. - resp.WriteUint32(uint32(time.Now().Unix())) // Current server time + resp.WriteUint32(uint32(t.Add(-5*time.Minute).Unix())) // 5 minutes ago server time for _, es := range eventSchedules { resp.WriteUint32(uint32(es.StartTime.Unix())) - resp.WriteUint32(es.Unk0) + resp.WriteUint32(es.ActiveFeatures) resp.WriteUint16(es.Unk1) } @@ -2069,23 +2109,54 @@ func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetEarthValue(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetEarthValue) - - earthValues := []struct { - Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32 - }{ - { - Unk0: 0x03E9, - Unk1: 0x5B, - }, - { - Unk0: 0x2329, - Unk1: 0x03, - }, - { - Unk0: 0x232A, - Unk1: 0x0A, - Unk2: 0x012C, - }, + var earthValues []struct{ Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32 } + if pkt.ReqType == 3{ + earthValues = []struct { + Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32 + }{ + // TW identical to JP + { + Unk0: 0x03E9, + Unk1: 0x24, + }, + { + Unk0: 0x2329, + Unk1: 0x03, + }, + { + Unk0: 0x232A, + Unk1: 0x0A, + Unk2: 0x012C, + }, + } + } else if pkt.ReqType == 2{ + earthValues = []struct { + Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32 + }{ + // JP response was empty + { + Unk0: 0x01, + Unk1: 0x168B, + }, + { + Unk0: 0x02, + Unk1: 0x0737, + }, + } + }else if pkt.ReqType == 1{ + earthValues = []struct { + Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32 + }{ + // JP simply sent 01 and 02 respectively + { + Unk0: 0x01, + Unk1: 0x0138, + }, + { + Unk0: 0x02, + Unk1: 0x63, + }, + } } resp := byteframe.NewByteFrame() @@ -2358,15 +2429,20 @@ func handleMsgMhfUseKeepLoginBoost(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetUdSchedule) - + var t = time.Now().In(time.FixedZone("UTC+9", 9*60*60)) + year,month,day := t.Date() + midnight := time.Date(year, month, day, 0, 0, 0, 0, t.Location()).Add(time.Hour) + // Events with time limits are Festival with Sign up, Soul Week and Winners Weeks + // Diva Defense with Prayer, Interception and Song weeks + // Mezeporta Festival with simply 'available' being a weekend thing resp := byteframe.NewByteFrame() - resp.WriteUint32(0x0b5397df) // Unk - resp.WriteUint32(0x5ddde6b0) // Timestamp - resp.WriteUint32(0x5de71320) // Timestamp - resp.WriteUint32(0x5de7225c) // Timestamp - resp.WriteUint32(0x5df04da0) // Timestamp - resp.WriteUint32(0x5df05cdc) // Timestamp - resp.WriteUint32(0x5dfa30e0) // Timestamp + resp.WriteUint32(0x1d5fda5c) // Unk (1d5fda5c, 0b5397df) + resp.WriteUint32(uint32(midnight.Add(-24*21*time.Hour).Unix())) // Week 1 Timestamp, Festi start? + resp.WriteUint32(uint32(midnight.Add(-24*14*time.Hour).Unix())) // Week 2 Timestamp + resp.WriteUint32(uint32(midnight.Add(-24*14*time.Hour).Unix())) // Week 2 Timestamp + resp.WriteUint32(uint32(midnight.Add(24*7*time.Hour).Unix())) // Diva Defense Interception + resp.WriteUint32(uint32(midnight.Add(24*7*time.Hour).Unix())) // Diva Defense Interception + resp.WriteUint32(uint32(midnight.Add(24*14*time.Hour).Unix())) // Diva Defense Greeting Song resp.WriteUint16(0x19) // Unk resp.WriteUint16(0x2d) // Unk resp.WriteUint16(0x02) // Unk @@ -2377,14 +2453,14 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetUdInfo) - + // Message that appears on the Diva Defense NPC and triggers the green exclamation mark udInfos := []struct { Text string StartTime time.Time EndTime time.Time }{ { - Text: " ~C17【Erupe】 launch event!\n\n■Features\n~C18 Walk around!\n~C17 Crash your connection by doing nearly anything!", + Text: " ~C17【Erupe】 launch event!\n\n■Features\n~C18 Walk around!\n~C17 Crash your connection by doing \nnearly anything!", StartTime: time.Now().Add(time.Duration(-5) * time.Minute), // Event started 5 minutes ago, EndTime: time.Now().Add(time.Duration(5) * time.Minute), // Event ends in 5 minutes, }, @@ -2401,15 +2477,30 @@ func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) { doSizedAckResp(s, pkt.AckHandle, resp.Data()) } -func handleMsgMhfGetKijuInfo(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetKijuInfo(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetKijuInfo) + // Temporary canned response + data, _ := hex.DecodeString("04965C959782CC8B468EEC00000000000000000000000000000000000000000000815C82A082E782B582DC82A982BA82CC82AB82B682E3815C0A965C959782C682CD96D282E98E7682A281420A95B782AD8ED282C997458B4382F0975E82A682E98142000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001018BAD8C8282CC8B468EEC00000000000000000000000000000000000000000000815C82AB82E582A482B082AB82CC82AB82B682E3815C0A8BAD8C8282C682CD8BAD82A290BA904681420A95B782AD8ED282CC97CD82F08CA482AC909F82DC82B78142200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003138C8B8F5782CC8B468EEC00000000000000000000000000000000000000000000815C82AF82C182B582E382A482CC82AB82B682E3815C0A8C8B8F5782C682CD8A6D8CC582BD82E9904D978A81420A8F5782DF82E982D982C782C98EEB906C82BD82BF82CC90B8905F97CD82C682C882E9814200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041189CC8CEC82CC8B468EEC00000000000000000000000000000000000000000000815C82A482BD82DC82E082E882CC82AB82B682E3815C0A89CC8CEC82C682CD89CC955082CC8CEC82E881420A8F5782DF82E982D982C782C98EEB906C82BD82BF82CC8E7882A682C682C882E9814220000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000212") + doSizedAckResp(s, pkt.AckHandle, data) +} func handleMsgMhfSetKiju(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfAddUdPoint(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetUdMyPoint(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetUdMyPoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdMyPoint) + // Temporary canned response + data, _ := hex.DecodeString("00040000013C000000FA000000000000000000040000007E0000003C02000000000000000000000000000000000000000000000000000002000004CC00000438000000000000000000000000000000000000000000000000000000020000026E00000230000000000000000000020000007D0000007D000000000000000000000000000000000000000000000000000000") + doSizedAckResp(s, pkt.AckHandle, data) +} -func handleMsgMhfGetUdTotalPointInfo(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetUdTotalPointInfo(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdTotalPointInfo) + // Temporary canned response + data, _ := hex.DecodeString("00000000000007A12000000000000F424000000000001E848000000000002DC6C000000000003D090000000000004C4B4000000000005B8D8000000000006ACFC000000000007A1200000000000089544000000000009896800000000000E4E1C00000000001312D0000000000017D78400000000001C9C3800000000002160EC00000000002625A000000000002AEA5400000000002FAF0800000000003473BC0000000000393870000000000042C1D800000000004C4B40000000000055D4A800000000005F5E10000000000008954400000000001C9C3800000000003473BC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001020300000000000000000000000000000000000000000000000000000000000000000000000000000000101F1420") + doSizedAckResp(s, pkt.AckHandle, data) +} func handleMsgMhfGetUdBonusQuestInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetUdBonusQuestInfo) @@ -2581,7 +2672,12 @@ func handleMsgMhfGetUdRankingRewardList(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfAcquireUdItem(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetRewardSong(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetRewardSong(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetRewardSong) + // Temporary canned response + data, _ := hex.DecodeString("0100001600000A5397DF00000000000000000000000000000000") + doSizedAckResp(s, pkt.AckHandle, data) +} func handleMsgMhfUseRewardSong(s *Session, p mhfpacket.MHFPacket) {} @@ -2589,7 +2685,12 @@ func handleMsgMhfAddRewardSongCount(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdRanking(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetUdMyRanking(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetUdMyRanking(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdMyRanking) + // Temporary canned response + data, _ := hex.DecodeString("00000515000005150000CEB4000003CE000003CE0000CEB44D49444E494748542D414E47454C0000000000000000000000") + doSizedAckResp(s, pkt.AckHandle, data) +} func handleMsgMhfAcquireMonthlyReward(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireMonthlyReward) @@ -2604,13 +2705,28 @@ func handleMsgMhfGetUdGuildMapInfo(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGenerateUdGuildMap(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetUdTacticsPoint(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetUdTacticsPoint(s *Session, p mhfpacket.MHFPacket) { + // Diva defense interception points + pkt := p.(*mhfpacket.MsgMhfGetUdTacticsPoint) + // Temporary canned response + data, _ := hex.DecodeString("000000A08F0BE2DAE30BE30AE2EAE2E9E2E8E2F5E2F3E2F2E2F1E2BB") + doSizedAckResp(s, pkt.AckHandle, data) +} -func handleMsgMhfAddUdTacticsPoint(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfAddUdTacticsPoint(s *Session, p mhfpacket.MHFPacket) { +pkt := p.(*mhfpacket.MsgMhfAddUdTacticsPoint) + stubEnumerateNoResults(s, pkt.AckHandle) +} func handleMsgMhfGetUdTacticsRanking(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetUdTacticsRewardList(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetUdTacticsRewardList(s *Session, p mhfpacket.MHFPacket) { + // Diva defense interception + pkt := p.(*mhfpacket.MsgMhfGetUdTacticsRewardList) + // Temporary canned response + data, _ := hex.DecodeString("000094000000010732DD00010000000000010732DD00010100000000C8071F2800050100000000C80705C000050000000001901A000001F40000000001901A000001F40100000002580705C00005000000000258071F2800050100000003201A000003E80100000003201A000003E80000000003E81A000004B00100000003E81A000004B00000000004B01A000005DC0100000004B01A000005DC0000000005781A000008FC0100000005781A000008FC0000000006401A000009C40000000006401A000009C40100000007081A00000BB80100000007081A00000BB80000000007D00725FA00010000000007D01A00000CE40000000007D00725FC00010100000007D00725FB00010100000007D00725FA00010100000007D01A00000CE40100000007D00725FC00010000000007D00725FB0001000000000BB80705C00005000000000BB8071F280005010000000FA01A00000DAC000000000FA01A00000DAC0100000013880705C00005000000001388071F2800050100000017700725FE00010100000017700725FD00010100000017700725FF00010100000017700725FD00010000000017700725FE00010000000017700725FF0001000000001B581A00000E74000000001B581A00000E74010000001F400727D00005010000001F400727D000050000000023281A00000FA00000000023281A00000FA00100000027100736EF000100000000271007369600010100000027100736EF00010100000027100736EF0001000000002EE00727D10005010000002EE00727D100050000000036B01D000000010100000036B01D00000001000000003A980737DB0001010000003A980736EF00010000000046500725E600010100000046500725E60001000000004E200738C90001010000004E200736EF00010000000055F01A000010680100000055F01A000010680000000061A80736EF00010000000061A80739A600010100000065900727D200050000000065900727D20005010000007530073A0600010100000075300736EF00010000000075300736EF00010000000075300736EF00010100000084D01D000000020000000084D01D00000002010000009C400727D30005010000009C400727D3000500000000B3B01A0000119400000000B3B01A0000119401000000C3500727D4000500000000C3500727D4000501000000D2F01D0000000300000000D2F01D0000000301000000EA600736EF000100000000EA600736EF000101000000F6181A0000125C00000000F6181A0000125C0100000111700727D500050000000111700727D500050100000119400727D600050100000119400727D600050000000121101D000000040000000121101D000000040100000130B01A000013880000000130B01A000013880100000140500727D700050000000140500727D700050100000148201D000000050000000148201D00000005010000014FF01A000014B4000000014FF01A000014B4010000015F900736EF0001000000015F900736EF00010100000167600729EA00050000000167600729EA0005010000016F301D00000006010000016F301D00000006000000017ED00729EB0005000000017ED00729EB0005010000018E701A0000157C010000018E701A0000157C0000000196401D000000070000000196401D00000007010000019E100729EC0005000000019E100729EC000501000001ADB00727CD000100000001ADB00727CD000101000001BD501D0000000800000001BD501D0000000801000001CCF01A0000164401000001CCF01A0000164400000001E4601D0000000901000001E4601D0000000900000001EC300727CC000101000001EC300727CC0001000000020B701D0000000A000000020B701D0000000A010000023A501A0000170C010000023A501A0000170C0000000249F00736EF00010100000249F00736EF00010000000271001A000017D40100000271001A000017D400000002A7B01A0000189C01000002A7B01A0000189C00000002BF200736EF000100000002BF200736EF000101000002D6901A0000196401000002D6901A00001964000000030D400727CB0001000000030D400727CB00010100000343F01A00001A2C0100000343F01A00001A2C0000000372D0072CB0000F0000000372D0072CB0000F01000003A9801A00001BBC00000003A9801A00001BBC01000003F7A01A000003E800010003F7A01A000003E80101000445C01A000003E80101000445C01A000003E80001005E000000020704020005010000000002070402000500000000000307040200140000000000030704020014010000000005071D200003010000000005071D20000300000000000607040200140100000000060704020014000000000008071D210003010000000008071D21000300000000000A070402001401000000000A070402001400000000000C0722EC000501000000000C0722ED000500000000000C0722F2000500000000000C0722EC000500000000000C0722EF000500000000000C0722ED000501000000000C0722F2000501000000000C0722EF000501000000000D1A000003E801000000000D1A000003E800000000000F07357C000501000000000F07357D000501000000000F07357C000500000000000F07357D00050000000000111A000007D00000000000111A000007D00100000000141C00000001000000000014071D2200030000000000141C00000001010000000014071D22000301000000001607357D000701000000001607357C00070000000000160704020028000000000016070402002801000000001607357C000701000000001607357D0007000000000018071D270003000000000018071D27000301000000001A1A00000BB800000000001A1A00000BB801000000001C07357D000701000000001C070402002801000000001C07357D000700000000001C07357C000700000000001C070402002800000000001C07357C000701000000001E070402003C01000000001E070402003C000000000020071D26000301000000002007357C000700000000002007357D000700000000002007357C000701000000002007357D0007010000000020071D260003000000000023071D280003010000000023071D28000300000000002A070402003C00000000002A070402003C01000000002C0725EE000100000000002C0725EE000101000000002E070402005001000000002E07357D000A01000000002E070402005000000000002E07357C000A00000000002E07357D000A00000000002E07357C000A0100000000300725ED00010000000000300725ED0001010000000032071D200003010000000032071D200003000000000034072C7B0001000000000034072C7B0001010000000037071D210003000000000037071D21000301000000003C0722F1000A00000000003C0722F1000A01000000004107040200500000000000410704020050010000000046071D220003010000000046071D22000300000000004B071D27000301000000004B071D2700030000000000500722F1000F0100000000500722F1000F0000000000550704020050010000000055070402005000000000005A071D26000301000000005A071D26000300000000005F071D28000300000000005F071D2800030100000000641A0000C3500100000000641A0000C3500000002607000E00C8000000010000000307000F0032000000010000000307001000320000000100000003070011003200000001000000030700120032000000010000000307000E0096000000040000000A07000F0028000000040000000A0700100028000000040000000A0700110028000000040000000A0700120028000000040000000A07000E00640000000B0000001907000F001E0000000B00000019070010001E0000000B00000019070011001E0000000B00000019070012001E0000000B0000001907000E00320000001A0000002807000F00140000001A0000002807001000140000001A0000002807001100140000001A0000002807001200140000001A0000002807000E001E000000290000004607000F000A0000002900000046070010000A000000290000004607001100010000002900000046070012000A000000290000004607000E0019000000470000006407000F0008000000470000006407001000080000004700000064070011000100000047000000640700120008000000470000006407000E000F000000650000009607000F0006000000650000009607001000010000006500000096070011000600000065000000960700120006000000650000009607000E000500000097000001F407000F000500000097000001F4070010000500000097000001F4") + doSizedAckResp(s, pkt.AckHandle, data) +} func handleMsgMhfGetUdTacticsLog(s *Session, p mhfpacket.MHFPacket) {} @@ -2665,14 +2781,25 @@ func handleMsgMhfAddKouryouPoint(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetKouryouPoint(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetKouryouPoint) - doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x02, 0x14, 0x3E}) } func handleMsgMhfExchangeKouryouPoint(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetUdTacticsBonusQuest(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetUdTacticsBonusQuest(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdTacticsBonusQuest) + // Temporary canned response + data, _ := hex.DecodeString("14E2F55DCBFE505DCC1A7003E8E2C55DCC6ED05DCC8AF00258E2CE5DCCDF505DCCFB700279E3075DCD4FD05DCD6BF0041AE2F15DCDC0505DCDDC700258E2C45DCE30D05DCE4CF00258E2F55DCEA1505DCEBD7003E8E2C25DCF11D05DCF2DF00258E2CE5DCF82505DCF9E700279E3075DCFF2D05DD00EF0041AE2CE5DD063505DD07F700279E2F35DD0D3D05DD0EFF0028AE2C35DD144505DD160700258E2F05DD1B4D05DD1D0F00258E2CE5DD225505DD241700279E2F55DD295D05DD2B1F003E8E2F25DD306505DD3227002EEE2CA5DD376D05DD392F00258E3075DD3E7505DD40370041AE2F55DD457D05DD473F003E82027313220686F757273273A3A696E74657276616C29202B2027313220686F757273273A3A696E74657276616C2047524F5550204259206D6170204F52444552204259206D61703B2000C7312B000032") + doSizedAckResp(s, pkt.AckHandle, data) +} -func handleMsgMhfGetUdTacticsFirstQuestBonus(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetUdTacticsFirstQuestBonus(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdTacticsFirstQuestBonus) + // Temporary canned response + data, _ := hex.DecodeString("0500000005DC01000007D002000009C40300000BB80400001194") + doSizedAckResp(s, pkt.AckHandle, data) + +} func handleMsgMhfGetUdTacticsRemainingPoint(s *Session, p mhfpacket.MHFPacket) {}