From 617e85342f7d6531fbb37e13ce9e6c8eecf3f05b Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sun, 15 Mar 2026 10:30:27 +0100 Subject: [PATCH 1/2] Increase GameData version This should have been done in #1901 but was missed. We can detect the faulty versions as the "invalid" harbor at the front had an explicitly invalid MapPoint. Fixes #1904 --- libs/s25main/SerializedGameData.cpp | 2 +- libs/s25main/world/MapSerializer.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libs/s25main/SerializedGameData.cpp b/libs/s25main/SerializedGameData.cpp index aab7577029..80e5961dd5 100644 --- a/libs/s25main/SerializedGameData.cpp +++ b/libs/s25main/SerializedGameData.cpp @@ -107,7 +107,7 @@ /// 11: wineaddon added, three new building types and two new goods /// 12: leatheraddon added, three new building types and three new goods /// 13: SeaId & HarborId: World::harbor_pos w/o dummy entry at 0 -static const unsigned currentGameDataVersion = 12; +static const unsigned currentGameDataVersion = 13; // clang-format on std::unique_ptr SerializedGameData::Create_GameObject(const GO_Type got, const unsigned obj_id) diff --git a/libs/s25main/world/MapSerializer.cpp b/libs/s25main/world/MapSerializer.cpp index 7e175ed559..6f19b2f339 100644 --- a/libs/s25main/world/MapSerializer.cpp +++ b/libs/s25main/world/MapSerializer.cpp @@ -153,7 +153,11 @@ void MapSerializer::Deserialize(GameWorldBase& world, SerializedGameData& sgd, G } } if(sgd.GetGameDataVersion() < 13 && !world.harbor_pos.empty()) - world.harbor_pos.erase(world.harbor_pos.begin()); + { + // Workaround for save games without increased game data version after introducing the change + if(!world.harbor_pos.front().pos.isValid()) + world.harbor_pos.erase(world.harbor_pos.begin()); + } sgd.PopObjectContainer(world.harbor_building_sites_from_sea, GO_Type::Buildingsite); From cc216ffd02b8012e247af2a24061a27fe3b650ae Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sun, 15 Mar 2026 10:39:29 +0100 Subject: [PATCH 2/2] Rename `harbor_pos` to `harborData` --- libs/s25main/SerializedGameData.cpp | 2 +- libs/s25main/world/MapLoader.cpp | 28 ++++++++++++++-------------- libs/s25main/world/MapSerializer.cpp | 18 +++++++++--------- libs/s25main/world/World.cpp | 14 +++++++------- libs/s25main/world/World.h | 4 ++-- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/libs/s25main/SerializedGameData.cpp b/libs/s25main/SerializedGameData.cpp index 80e5961dd5..cbfdab34f8 100644 --- a/libs/s25main/SerializedGameData.cpp +++ b/libs/s25main/SerializedGameData.cpp @@ -106,7 +106,7 @@ /// 10: troop_limits state introduced to military buildings /// 11: wineaddon added, three new building types and two new goods /// 12: leatheraddon added, three new building types and three new goods -/// 13: SeaId & HarborId: World::harbor_pos w/o dummy entry at 0 +/// 13: SeaId & HarborId: World::harborData w/o dummy entry at 0 static const unsigned currentGameDataVersion = 13; // clang-format on diff --git a/libs/s25main/world/MapLoader.cpp b/libs/s25main/world/MapLoader.cpp index 8610f82458..b6769250bd 100644 --- a/libs/s25main/world/MapLoader.cpp +++ b/libs/s25main/world/MapLoader.cpp @@ -144,7 +144,7 @@ bool MapLoader::InitNodes(const libsiedler2::ArchivItem_Map& map, Exploration ex // Hafenplatz? if((t1 & libsiedler2::HARBOR_MASK) != 0) - world_.harbor_pos.push_back(HarborPos(pt)); + world_.harborData.push_back(HarborPos(pt)); // Will be set later node.harborId.reset(); @@ -423,7 +423,7 @@ bool MapLoader::PlaceHQs(GameWorldBase& world, std::vector hqPositions bool MapLoader::InitSeasAndHarbors(World& world, const std::vector& additionalHarbors) { for(MapPoint pt : additionalHarbors) - world.harbor_pos.push_back(HarborPos(pt)); + world.harborData.push_back(HarborPos(pt)); // Clear current harbors and seas RTTR_FOREACH_PT(MapPoint, world.GetSize()) //-V807 { @@ -448,7 +448,7 @@ bool MapLoader::InitSeasAndHarbors(World& world, const std::vector& ad /// Determine seas adjacent to the harbor places HarborId curHarborId(1); - for(auto it = world.harbor_pos.begin(); it != world.harbor_pos.end();) + for(auto it = world.harborData.begin(); it != world.harborData.end();) { helpers::StrongIdVector hasCoastAtSea(world.seas.size(), false); bool foundCoast = false; @@ -474,7 +474,7 @@ bool MapLoader::InitSeasAndHarbors(World& world, const std::vector& ad if(!foundCoast) { LOG.write("Map Bug: Found harbor without coast at %1%. Removing!\n") % it->pos; - it = world.harbor_pos.erase(it); + it = world.harborData.erase(it); } else { world.GetNodeInt(it->pos).harborId = curHarborId; @@ -487,9 +487,9 @@ bool MapLoader::InitSeasAndHarbors(World& world, const std::vector& ad CalcHarborPosNeighbors(world); // Validate - for(const auto startHbId : helpers::idRange(world.harbor_pos.size())) + for(const auto startHbId : helpers::idRange(world.harborData.size())) { - const HarborPos& startHbPos = world.harbor_pos[startHbId]; + const HarborPos& startHbPos = world.harborData[startHbId]; for(const std::vector& neighbors : startHbPos.neighbors) { for(const HarborPos::Neighbor& neighbor : neighbors) @@ -519,7 +519,7 @@ struct CalcHarborPosNeighborsNode /// Calculate the distance from each harbor to the others void MapLoader::CalcHarborPosNeighbors(World& world) { - for(HarborPos& harbor : world.harbor_pos) + for(HarborPos& harbor : world.harborData) { for(const auto dir : helpers::EnumRange{}) harbor.neighbors[dir].clear(); @@ -538,7 +538,7 @@ void MapLoader::CalcHarborPosNeighbors(World& world) // FIFO queue used for a BFS std::queue todo_list; - for(const auto startHbId : helpers::idRange(world.harbor_pos.size())) + for(const auto startHbId : helpers::idRange(world.harborData.size())) { RTTR_Assert(todo_list.empty()); @@ -548,13 +548,13 @@ void MapLoader::CalcHarborPosNeighbors(World& world) // 1 - Coast to a harbor std::vector ptToVisitOrHb(ptIsSeaPt); - helpers::StrongIdVector hbFound(world.harbor_pos.size(), false); + helpers::StrongIdVector hbFound(world.harborData.size(), false); // For each sea, store the coastal point indices and their harbor helpers::StrongIdVector, SeaId> coastToHarborPerSea(world.seas.size()); std::vector ownCoastalPoints; // mark coastal points around harbors - for(const auto otherHbId : helpers::idRange(world.harbor_pos.size())) + for(const auto otherHbId : helpers::idRange(world.harborData.size())) { for(const auto dir : helpers::EnumRange{}) { @@ -586,7 +586,7 @@ void MapLoader::CalcHarborPosNeighbors(World& world) for(auto it = coastToHbs.first; it != coastToHbs.second; ++it) { ShipDirection shipDir = world.GetShipDir(ownCoastPt, ownCoastPt); - world.harbor_pos[startHbId].neighbors[shipDir].push_back(HarborPos::Neighbor(it->second, 0)); + world.harborData[startHbId].neighbors[shipDir].push_back(HarborPos::Neighbor(it->second, 0)); hbFound[it->second] = true; } todo_list.push(CalcHarborPosNeighborsNode(ownCoastPt, 0)); @@ -612,7 +612,7 @@ void MapLoader::CalcHarborPosNeighbors(World& world) if(ptValue > 0) // found harbor(s) { - ShipDirection shipDir = world.GetShipDir(world.harbor_pos[startHbId].pos, curPt); + ShipDirection shipDir = world.GetShipDir(world.harborData[startHbId].pos, curPt); SeaId seaId = world.GetSeaFromCoastalPoint(curPt); auto const coastToHbs = coastToHarborPerSea[seaId].equal_range(idx); for(auto it = coastToHbs.first; it != coastToHbs.second; ++it) @@ -622,11 +622,11 @@ void MapLoader::CalcHarborPosNeighbors(World& world) continue; hbFound[otherHbId] = true; - world.harbor_pos[startHbId].neighbors[shipDir].push_back( + world.harborData[startHbId].neighbors[shipDir].push_back( HarborPos::Neighbor(otherHbId, curNode.distance + 1)); // Make this the only coastal point of this harbor for this sea - HarborPos& otherHb = world.harbor_pos[otherHbId]; + HarborPos& otherHb = world.harborData[otherHbId]; RTTR_Assert(seaId); for(const auto hbDir : helpers::EnumRange{}) { diff --git a/libs/s25main/world/MapSerializer.cpp b/libs/s25main/world/MapSerializer.cpp index 6f19b2f339..85fe82a23c 100644 --- a/libs/s25main/world/MapSerializer.cpp +++ b/libs/s25main/world/MapSerializer.cpp @@ -37,8 +37,8 @@ void MapSerializer::Serialize(const GameWorldBase& world, SerializedGameData& sg sgd.PushUnsignedInt(sea.nodes_count); } // Hafenpositionen serialisieren - sgd.PushUnsignedInt(world.harbor_pos.size()); - for(const auto& curHarborPos : world.harbor_pos) + sgd.PushUnsignedInt(world.harborData.size()); + for(const auto& curHarborPos : world.harborData) { helpers::pushPoint(sgd, curHarborPos.pos); helpers::pushContainer(sgd, curHarborPos.seaIds); @@ -131,13 +131,13 @@ void MapSerializer::Deserialize(GameWorldBase& world, SerializedGameData& sgd, G // Deserialize harbor data const unsigned numHarborPositions = sgd.PopUnsignedInt(); - world.harbor_pos.clear(); - world.harbor_pos.reserve(numHarborPositions); + world.harborData.clear(); + world.harborData.reserve(numHarborPositions); for(const auto i : helpers::range(numHarborPositions)) { RTTR_UNUSED(i); - world.harbor_pos.emplace_back(sgd.PopMapPoint()); - auto& curHarborPos = world.harbor_pos.back(); + world.harborData.emplace_back(sgd.PopMapPoint()); + auto& curHarborPos = world.harborData.back(); helpers::popContainer(sgd, curHarborPos.seaIds); for(auto& neighbor : curHarborPos.neighbors) { @@ -152,11 +152,11 @@ void MapSerializer::Deserialize(GameWorldBase& world, SerializedGameData& sgd, G } } } - if(sgd.GetGameDataVersion() < 13 && !world.harbor_pos.empty()) + if(sgd.GetGameDataVersion() < 13 && !world.harborData.empty()) { // Workaround for save games without increased game data version after introducing the change - if(!world.harbor_pos.front().pos.isValid()) - world.harbor_pos.erase(world.harbor_pos.begin()); + if(!world.harborData.front().pos.isValid()) + world.harborData.erase(world.harborData.begin()); } sgd.PopObjectContainer(world.harbor_building_sites_from_sea, GO_Type::Buildingsite); diff --git a/libs/s25main/world/World.cpp b/libs/s25main/world/World.cpp index b9025cac6f..d33a37ad54 100644 --- a/libs/s25main/world/World.cpp +++ b/libs/s25main/world/World.cpp @@ -68,7 +68,7 @@ void World::Unload() node.figures.clear(); catapult_stones.clear(); - harbor_pos.clear(); + harborData.clear(); description_ = WorldDescription(); noNodeObj.reset(); Resize(MapExtent::all(0)); @@ -345,7 +345,7 @@ unsigned World::GetSeaSize(const SeaId seaId) const SeaId World::GetSeaId(const HarborId harborId, const Direction dir) const { RTTR_Assert(harborId); - return harbor_pos[harborId].seaIds[dir]; + return harborData[harborId].seaIds[dir]; } bool World::IsHarborAtSea(const HarborId harborId, const SeaId seaId) const @@ -361,8 +361,8 @@ MapPoint World::GetCoastalPoint(const HarborId harborId, const SeaId seaId) cons // Take point at NW last as often there is no path from it if the harbor is north of an island for(auto dir : helpers::enumRange(Direction::NorthEast)) { - if(harbor_pos[harborId].seaIds[dir] == seaId) - return GetNeighbour(harbor_pos[harborId].pos, dir); + if(harborData[harborId].seaIds[dir] == seaId) + return GetNeighbour(harborData[harborId].pos, dir); } // Keinen Punkt gefunden @@ -402,14 +402,14 @@ MapPoint World::GetHarborPoint(const HarborId harborId) const { RTTR_Assert(harborId); - return harbor_pos[harborId].pos; + return harborData[harborId].pos; } const std::vector& World::GetHarborNeighbors(const HarborId harborId, const ShipDirection& dir) const { RTTR_Assert(harborId); - return harbor_pos[harborId].neighbors[dir]; + return harborData[harborId].neighbors[dir]; } unsigned World::CalcHarborDistance(HarborId haborId1, HarborId harborId2) const @@ -418,7 +418,7 @@ unsigned World::CalcHarborDistance(HarborId haborId1, HarborId harborId2) const return 0; for(const auto dir : helpers::EnumRange{}) { - for(const HarborPos::Neighbor& n : harbor_pos[haborId1].neighbors[dir]) + for(const HarborPos::Neighbor& n : harborData[haborId1].neighbors[dir]) { if(n.id == harborId2) return n.distance; diff --git a/libs/s25main/world/World.h b/libs/s25main/world/World.h index 41333546ae..6d5041eda8 100644 --- a/libs/s25main/world/World.h +++ b/libs/s25main/world/World.h @@ -57,7 +57,7 @@ class World : public MapBase helpers::StrongIdVector seas; /// Alle Hafenpositionen - helpers::StrongIdVector harbor_pos; + helpers::StrongIdVector harborData; WorldDescription description_; @@ -186,7 +186,7 @@ class World : public MapBase /// Return the coast pt for a given harbor (where ships can land) if any MapPoint GetCoastalPoint(HarborId harborId, SeaId seaId) const; /// Return the number of harbor points - unsigned GetNumHarborPoints() const { return harbor_pos.size(); } + unsigned GetNumHarborPoints() const { return harborData.size(); } /// Return the coordinates for a given harbor point MapPoint GetHarborPoint(HarborId harborId) const; /// Return the ID of the harbor point on that node or 0 if there is none