mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
feat(achievement): add rank-up notifications (#165)
RE'd putDisplayed_achievement from ZZ client DLL via Ghidra: the packet sends opcode + 1 zero byte with no achievement ID, acting as a blanket "I saw everything" signal. Server changes: - Track per-character last-displayed levels in new displayed_levels column (migration 0008) - GetAchievement compares current vs displayed levels per entry - DisplayedAchievement snapshots current levels to clear notifications - Repo, service, mock, and 3 new service tests Protbot changes: - New --action achievement: fetches achievements, shows rank-up markers, sends DISPLAYED_ACHIEVEMENT, re-fetches to verify notifications clear - Packet builders for GET/ADD/DISPLAYED_ACHIEVEMENT
This commit is contained in:
@@ -19,5 +19,8 @@ const (
|
||||
MSG_SYS_RIGHTS_RELOAD uint16 = 0x005D
|
||||
MSG_MHF_LOADDATA uint16 = 0x0061
|
||||
MSG_MHF_ENUMERATE_QUEST uint16 = 0x009F
|
||||
MSG_MHF_GET_WEEKLY_SCHED uint16 = 0x00E1
|
||||
MSG_MHF_GET_ACHIEVEMENT uint16 = 0x00D4
|
||||
MSG_MHF_ADD_ACHIEVEMENT uint16 = 0x00D6
|
||||
MSG_MHF_DISPLAYED_ACHIEVEMENT uint16 = 0x00D8
|
||||
MSG_MHF_GET_WEEKLY_SCHED uint16 = 0x00E1
|
||||
)
|
||||
|
||||
@@ -215,6 +215,56 @@ func BuildEnumerateQuestPacket(ackHandle uint32, world uint8, counter, offset ui
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
// BuildGetAchievementPacket builds a MSG_MHF_GET_ACHIEVEMENT packet.
|
||||
// Layout mirrors Erupe's MsgMhfGetAchievement.Parse:
|
||||
//
|
||||
// uint16 opcode
|
||||
// uint32 ackHandle
|
||||
// uint32 charID
|
||||
// uint32 zeroed
|
||||
// 0x00 0x10 terminator
|
||||
func BuildGetAchievementPacket(ackHandle, charID uint32) []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(MSG_MHF_GET_ACHIEVEMENT)
|
||||
bf.WriteUint32(ackHandle)
|
||||
bf.WriteUint32(charID)
|
||||
bf.WriteUint32(0) // Zeroed
|
||||
bf.WriteBytes([]byte{0x00, 0x10})
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
// BuildAddAchievementPacket builds a MSG_MHF_ADD_ACHIEVEMENT packet (fire-and-forget, no ACK).
|
||||
// Layout mirrors Erupe's MsgMhfAddAchievement.Parse:
|
||||
//
|
||||
// uint16 opcode
|
||||
// uint8 achievementID
|
||||
// uint16 unk1
|
||||
// uint16 unk2
|
||||
// 0x00 0x10 terminator
|
||||
func BuildAddAchievementPacket(achievementID uint8) []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(MSG_MHF_ADD_ACHIEVEMENT)
|
||||
bf.WriteUint8(achievementID)
|
||||
bf.WriteUint16(0) // Unk1
|
||||
bf.WriteUint16(0) // Unk2
|
||||
bf.WriteBytes([]byte{0x00, 0x10})
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
// BuildDisplayedAchievementPacket builds a MSG_MHF_DISPLAYED_ACHIEVEMENT packet (fire-and-forget).
|
||||
// Layout mirrors Erupe's MsgMhfDisplayedAchievement.Parse:
|
||||
//
|
||||
// uint16 opcode
|
||||
// uint8 zeroed
|
||||
// 0x00 0x10 terminator
|
||||
func BuildDisplayedAchievementPacket() []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(MSG_MHF_DISPLAYED_ACHIEVEMENT)
|
||||
bf.WriteUint8(0) // Zeroed
|
||||
bf.WriteBytes([]byte{0x00, 0x10})
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
// BuildGetWeeklySchedulePacket builds a MSG_MHF_GET_WEEKLY_SCHEDULE packet.
|
||||
//
|
||||
// uint16 opcode
|
||||
|
||||
Reference in New Issue
Block a user