Skip to content

Commit 2f19424

Browse files
committed
Add reproducer for crash due to too far distance to harbor
1 parent 673c08b commit 2f19424

2 files changed

Lines changed: 100 additions & 1 deletion

File tree

libs/s25main/world/World.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ unsigned World::CalcHarborDistance(HarborId haborId1, HarborId harborId2) const
425425
}
426426
}
427427

428-
return 0xffffffff;
428+
return std::numeric_limits<unsigned>::max();
429429
}
430430

431431
SeaId World::GetSeaFromCoastalPoint(const MapPoint pt) const

tests/s25Main/integration/testSeafaring.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,105 @@ BOOST_FIXTURE_TEST_CASE(HarborDestroyed, ShipAndHarborsReadyFixture<1>)
712712
BOOST_TEST_REQUIRE(ship.GetTargetHarbor() == hb1Id);
713713
}
714714

715+
// Dimensions must be large enough so path tolerances get too small
716+
using ShipReadyFixtureLarger = ShipReadyFixture<1, 2, 90, 260>;
717+
BOOST_FIXTURE_TEST_CASE(IncreasedPathLengthWhenHarborDestroyed, ShipReadyFixtureLarger)
718+
{
719+
// Edge case: Path to harbor longer than any direct connection
720+
/*
721+
* 2
722+
* W
723+
* WLW
724+
* WLW
725+
* WWLWW
726+
* 4WLLLLLW5
727+
* WWLWW
728+
* WWLWW
729+
* 7
730+
*/
731+
// Shortest path 4->5 is over south, ship might be between 2 & 4 => still finds path to 5
732+
constexpr HarborId hbIdTop(2);
733+
constexpr HarborId hbIdLeft(4);
734+
constexpr HarborId hbIdRight(5);
735+
constexpr HarborId hbIdBottom(7);
736+
const MapPoint hbTopPos = world.GetHarborPoint(hbIdTop);
737+
const MapPoint hbLeftPos = world.GetHarborPoint(hbIdLeft);
738+
const MapPoint hbRightPos = world.GetHarborPoint(hbIdRight);
739+
const MapPoint hbBottomPos = world.GetHarborPoint(hbIdBottom);
740+
// Sanity check expected geometry, ensured by fixture
741+
BOOST_TEST_REQUIRE(hbLeftPos.x < hbRightPos.x);
742+
BOOST_TEST_REQUIRE(hbLeftPos.y == hbRightPos.y);
743+
BOOST_TEST_REQUIRE(hbTopPos.y < hbLeftPos.y);
744+
BOOST_TEST_REQUIRE(hbLeftPos.y < hbBottomPos.y);
745+
const unsigned yDiff = hbLeftPos.y - hbTopPos.y;
746+
BOOST_TEST_REQUIRE(yDiff >= 4u * 3u); // So divisions below yield noticeable difference
747+
const auto t = GetLandTerrain(world.GetDescription(), ETerrain::Buildable);
748+
for(const auto y : helpers::range(hbLeftPos.y - yDiff * 3u / 4u, hbLeftPos.y + yDiff / 2u))
749+
{
750+
auto& node = world.GetNodeWriteable(MapPoint(hbTopPos.x, y));
751+
node.t1 = node.t2 = t;
752+
}
753+
constexpr auto horStripHeight = 2u;
754+
// Horizontal land strip to avoid short cuts
755+
for(const auto y : helpers::range(hbLeftPos.y - horStripHeight, hbLeftPos.y + horStripHeight))
756+
{
757+
for(const auto x : helpers::range(hbLeftPos.x + 4, hbRightPos.x - 4))
758+
{
759+
auto& node = world.GetNodeWriteable(MapPoint(x, y));
760+
node.t1 = node.t2 = t;
761+
}
762+
}
763+
MapLoader::InitSeasAndHarbors(world);
764+
// Harbors unchanged
765+
BOOST_TEST_REQUIRE(world.GetHarborPointID(hbTopPos) == hbIdTop);
766+
BOOST_TEST_REQUIRE(world.GetHarborPointID(hbLeftPos) == hbIdLeft);
767+
BOOST_TEST_REQUIRE(world.GetHarborPointID(hbRightPos) == hbIdRight);
768+
BOOST_TEST_REQUIRE(world.GetHarborPointID(hbBottomPos) == hbIdBottom);
769+
770+
const GamePlayer& player = world.GetPlayer(curPlayer);
771+
const noShip& ship = ensureNonNull(player.GetShipByID(0));
772+
const SeaId seaId = ship.GetSeaID();
773+
774+
// Start shipment to left harbor
775+
nobHarborBuilding& topHarbor = *player.GetBuildingRegister().GetHarbors().front();
776+
BuildingFactory::CreateBuilding(world, BuildingType::HarborBuilding, hbLeftPos, curPlayer, Nation::Romans);
777+
778+
topHarbor.AddToInventory(GoodCounts::make(GoodType::Wood, 10), true);
779+
SetInventorySetting(hbLeftPos, GoodType::Wood, EInventorySetting::Collect);
780+
781+
RTTR_EXEC_TILL(90, ship.IsLoading());
782+
BOOST_TEST_REQUIRE(ship.GetPos() == world.GetCoastalPoint(hbIdTop, seaId));
783+
RTTR_EXEC_TILL(400, ship.IsMoving());
784+
BOOST_TEST_REQUIRE(ship.GetTargetHarbor() == hbIdLeft);
785+
const auto leftCoastPt = world.GetCoastalPoint(hbIdLeft, seaId);
786+
const auto dist = world.CalcDistance(ship.GetPos(), leftCoastPt);
787+
788+
// Wait till ship is close to left harbor
789+
constexpr auto extraDist = 16u; // Choosen to be larger than a magic number used as tolerance
790+
RTTR_EXEC_TILL(2000, dist < extraDist * 2);
791+
792+
// Destroy home so it cannot go back, then target harbor
793+
destroyBldAndFire(world, hbTopPos);
794+
destroyBldAndFire(world, hbLeftPos);
795+
RTTR_EXEC_TILL(10, ship.IsLost());
796+
BOOST_TEST_REQUIRE(!ship.GetHomeHarbor());
797+
BOOST_TEST_REQUIRE(!ship.GetTargetHarbor());
798+
799+
// Building harbor at right makes ship go there even though distance is (too) long
800+
unsigned len;
801+
BOOST_TEST_REQUIRE(
802+
world.FindShipPath(ship.GetPos(), world.GetCoastalPoint(hbIdRight, seaId), unsigned(-1), nullptr, &len));
803+
const auto hbDistance = world.CalcHarborDistance(hbIdLeft, hbIdRight);
804+
// Reproducer requires the path to be longer than the direct distance and other connections
805+
BOOST_TEST_REQUIRE(len > hbDistance + extraDist);
806+
BOOST_TEST_REQUIRE(hbDistance > world.CalcHarborDistance(hbIdRight, hbIdTop));
807+
BOOST_TEST_REQUIRE(hbDistance > world.CalcHarborDistance(hbIdRight, hbIdBottom));
808+
BuildingFactory::CreateBuilding(world, BuildingType::HarborBuilding, hbRightPos, curPlayer, Nation::Romans);
809+
BOOST_TEST_REQUIRE(ship.IsMoving());
810+
BOOST_TEST_REQUIRE(ship.GetHomeHarbor() == hbIdRight);
811+
BOOST_TEST_REQUIRE(ship.GetTargetHarbor() == hbIdRight);
812+
}
813+
715814
struct MockWare : Ware
716815
{
717816
static bool destroyed;

0 commit comments

Comments
 (0)