diff --git a/CHANGELOG.md b/CHANGELOG.md index a31a1f404..16b3930c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed deadlock in zone change causing 60-second timeout when players change zones - Fixed crash when sending empty packets in QueueSend/QueueSendNonBlocking - Fixed missing stage transfer packet for empty zones +- Fixed save data corruption check rejecting valid saves due to name encoding mismatches (SJIS/UTF-8) +- Fixed incomplete saves during logout - character savedata now persisted even during ungraceful disconnects ### Security diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 1558ce107..a0e2c1850 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -184,6 +184,28 @@ func logoutPlayer(s *Session) { delete(s.server.objectIDs, s) s.server.Unlock() + // Save all character data before logout to prevent data loss + // This ensures data is persisted even if client disconnects unexpectedly + if s.charID != 0 { + characterSaveData, err := GetCharacterSaveData(s, s.charID) + if err == nil && characterSaveData != nil { + // Force name to match to prevent corruption detection issues + characterSaveData.Name = s.Name + characterSaveData.updateSaveDataWithStruct() + + // Update playtime in savedata before saving + if !s.playtimeTime.IsZero() { + s.playtime += uint32(time.Since(s.playtimeTime).Seconds()) + } + characterSaveData.Playtime = s.playtime + + characterSaveData.Save(s) + s.logger.Info("Saved character data during logout", zap.Uint32("charID", s.charID)) + } else if err != nil { + s.logger.Warn("Failed to retrieve character save data during logout", zap.Error(err), zap.Uint32("charID", s.charID)) + } + } + for _, stage := range s.server.stages { // Tell sessions registered to disconnecting players quest to unregister if stage.host != nil && stage.host.charID == s.charID { diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index ee69ea4c8..025f87b59 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -14,6 +14,7 @@ import ( "erupe-ce/network/mhfpacket" "erupe-ce/server/channelserver/compression/deltacomp" "erupe-ce/server/channelserver/compression/nullcomp" + "go.uber.org/zap" ) @@ -62,6 +63,14 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { s.Name = characterSaveData.Name } + // Force name to match session to prevent corruption detection false positives + // This handles SJIS/UTF-8 encoding differences and ensures saves succeed across all game versions + if characterSaveData.Name != s.Name && !characterSaveData.IsNewCharacter { + s.logger.Info("Correcting name mismatch in savedata", zap.String("savedata_name", characterSaveData.Name), zap.String("session_name", s.Name)) + characterSaveData.Name = s.Name + characterSaveData.updateSaveDataWithStruct() + } + if characterSaveData.Name == s.Name || _config.ErupeConfig.RealClientMode <= _config.S10 { characterSaveData.Save(s) s.logger.Info("Wrote recompressed savedata back to DB.")