fix(savedata): skip bookshelf read on pre-G1 clients

Bookshelf was introduced after Forward.5 (verified: F5 mhfo.dll has no
Bookshelf symbols, modern clients export .?AVBookshelfForm@@). For
F4/F5/S6 the configured pointers place the bookshelf region past the
end of the smaller save blob, causing a slice-bounds panic on every
MSG_MHF_SAVEDATA and rolling characters back to creation state. The
read is now bounds-checked and skipped when absent; bookshelf state
is persisted via house packets into user_binary.bookshelf, so leaving
BookshelfData nil is safe.
This commit is contained in:
Houmgaor
2026-04-07 21:56:40 +02:00
parent 44fd637a59
commit 538724e6c9
2 changed files with 11 additions and 1 deletions

View File

@@ -42,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Dashboard channel ports now reflect the actual configured `Entrance.Entries[].Channels[].Port` instead of a hardcoded `54000 + server_id`.
- Fixed backup recovery panic: `recoverFromBackups` now rejects decompressed backup data smaller than the minimum save layout size, preventing a slice-bounds panic when nullcomp passes through garbage bytes as "already decompressed" data ([#182](https://github.com/Mezeporta/Erupe/pull/182)).
- Fixed save-time panic and character rollback on Forward.5 / Forward.4 / Season 6.0 clients: bookshelf was introduced after Forward.5 (verified against the F5 client binary), so the configured pointers overshoot the smaller save blob. The bookshelf read is now bounds-checked and skipped when absent; persistence via house packets is unaffected.
## [9.3.2] - 2026-04-06

View File

@@ -188,7 +188,16 @@ func (save *CharacterSaveData) updateStructWithSaveData() {
save.RP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pRP] : save.Pointers[pRP]+saveFieldRP])
save.HouseTier = save.decompSave[save.Pointers[pHouseTier] : save.Pointers[pHouseTier]+saveFieldHouseTier]
save.HouseData = save.decompSave[save.Pointers[pHouseData] : save.Pointers[pHouseData]+saveFieldHouseData]
save.BookshelfData = save.decompSave[save.Pointers[pBookshelfData] : save.Pointers[pBookshelfData]+save.Pointers[lBookshelfData]]
// Bookshelf was introduced after Forward.5 (verified: F5 mhfo.dll
// contains no Bookshelf symbols, while modern clients export
// .?AVBookshelfForm@@). For F4/F5/S6 the configured pointers
// place the bookshelf region past the end of the save blob, so
// skip the read entirely on those versions. Bookshelf state is
// persisted via house packets into user_binary.bookshelf, not
// from this blob, so leaving BookshelfData nil is safe.
if bsEnd := save.Pointers[pBookshelfData] + save.Pointers[lBookshelfData]; bsEnd <= len(save.decompSave) {
save.BookshelfData = save.decompSave[save.Pointers[pBookshelfData]:bsEnd]
}
save.GalleryData = save.decompSave[save.Pointers[pGalleryData] : save.Pointers[pGalleryData]+saveFieldGallery]
save.ToreData = save.decompSave[save.Pointers[pToreData] : save.Pointers[pToreData]+saveFieldTore]
save.GardenData = save.decompSave[save.Pointers[pGardenData] : save.Pointers[pGardenData]+saveFieldGarden]