fix(handlers): guard GetBoostTimeLimit against past boost_time

Live-server testing via protbot surfaced an inconsistency between
GetBoostTimeLimit and GetBoostRight: on the same character, the
former reported a far-future boost limit (2288912640 = year 2042)
while the latter correctly reported "expired / available". The two
handlers read the same boost_time row and disagreed.

Root cause: GetBoostTimeLimit was doing a naked uint32(int64) cast
on boostLimit.Unix(). The test character's boost_time was actually
year 1906 (a pre-1970 sentinel left behind by the pre-#187 bug),
whose negative int64 Unix timestamp wraps through uint32 to a huge
positive value the client interprets as a permanently active boost.

Harmonise the guard with GetBoostRight: return 0 whenever the stored
boost_time is not strictly after TimeAdjusted(), covering both the
pre-1970 wraparound and the "already expired" case.

Add a healing migration (0011_fix_stale_boost_time) that NULLs out
any boost_time column older than 1970 or more than 10 years in the
future, so affected characters recover on upgrade without waiting
for a fresh boost start.

Regression test uses the exact year-1906 value observed on the live
frontier.mogapedia.fr test account.
This commit is contained in:
Houmgaor
2026-04-06 18:26:15 +02:00
parent e2b0a8ad8c
commit 4ad0012f62
3 changed files with 49 additions and 3 deletions

View File

@@ -219,9 +219,14 @@ func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit)
bf := byteframe.NewByteFrame()
boostLimit, err := s.server.charRepo.ReadTime(s.charID, "boost_time", time.Time{})
// Return 0 when disabled, on read error, or when boost_time is unset
// (zero time.Time.Unix() wraps to a large uint32 the client interprets as active).
if err != nil || s.server.erupeConfig.GameplayOptions.DisableBoostTime || boostLimit.IsZero() {
// Return 0 when disabled, on read error, when boost_time is unset,
// or when boost_time is already in the past. A naked uint32() cast
// on a pre-1970 int64 wraps to a huge future-looking timestamp the
// client interprets as permanently active (see the year-1906 rows
// left behind by the pre-#187 bug). Harmonise with GetBoostRight
// so the two handlers always agree on the same row.
if err != nil || s.server.erupeConfig.GameplayOptions.DisableBoostTime ||
boostLimit.IsZero() || !boostLimit.After(TimeAdjusted()) {
bf.WriteUint32(0)
} else {
bf.WriteUint32(uint32(boostLimit.Unix()))