diff --git a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp index 9d8a5d85de..bc1288f5f6 100644 --- a/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp +++ b/apps/openmw_test_suite/detournavigator/asyncnavmeshupdater.cpp @@ -299,4 +299,41 @@ namespace << " present=" << (present.find(tilePosition) != present.end()); } } + + TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, next_tile_id_should_be_updated_on_duplicate) + { + mRecastMeshManager.setWorldspace(mWorldspace, nullptr); + addHeightFieldPlane(mRecastMeshManager); + addObject(mBox, mRecastMeshManager); + auto db = std::make_unique(":memory:", std::numeric_limits::max()); + NavMeshDb* const dbPtr = db.get(); + AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, std::move(db)); + + const TileId nextTileId(dbPtr->getMaxTileId() + 1); + ASSERT_EQ(dbPtr->insertTile(nextTileId, "worldspace", TilePosition{}, TileVersion{ 1 }, {}, {}), 1); + + const auto navMeshCacheItem = std::make_shared(1, mSettings); + const TilePosition tilePosition{ 0, 0 }; + const std::map changedTiles{ { tilePosition, ChangeType::add } }; + + updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); + updater.wait(WaitConditionType::allJobsDone, &mListener); + + const AgentBounds agentBounds{ CollisionShapeType::Cylinder, { 29, 29, 66 } }; + updater.post(agentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles); + updater.wait(WaitConditionType::allJobsDone, &mListener); + + updater.stop(); + + const auto recastMesh = mRecastMeshManager.getMesh(mWorldspace, tilePosition); + ASSERT_NE(recastMesh, nullptr); + ShapeId nextShapeId{ 1 }; + const std::vector objects = makeDbRefGeometryObjects(recastMesh->getMeshSources(), + [&](const MeshSource& v) { return resolveMeshSource(*dbPtr, v, nextShapeId); }); + const auto tile = dbPtr->findTile( + mWorldspace, tilePosition, serialize(mSettings.mRecast, agentBounds, *recastMesh, objects)); + ASSERT_TRUE(tile.has_value()); + EXPECT_EQ(tile->mTileId, 2); + EXPECT_EQ(tile->mVersion, navMeshFormatVersion); + } } diff --git a/components/detournavigator/asyncnavmeshupdater.cpp b/components/detournavigator/asyncnavmeshupdater.cpp index ec6313d6f1..03f6062788 100644 --- a/components/detournavigator/asyncnavmeshupdater.cpp +++ b/components/detournavigator/asyncnavmeshupdater.cpp @@ -814,6 +814,23 @@ namespace DetourNavigator Log(Debug::Warning) << "Writes to navmeshdb are disabled to avoid concurrent writes from multiple processes"; } + else if (message.find("UNIQUE constraint failed: tiles.tile_id") != std::string_view::npos) + { + Log(Debug::Warning) << "Found duplicate navmeshdb tile_id, please report the " + "issue to https://gitlab.com/OpenMW/openmw/-/issues, attach openmw.log: " + << mNextTileId; + try + { + mNextTileId = TileId(mDb->getMaxTileId() + 1); + Log(Debug::Info) << "Updated navmeshdb tile_id to: " << mNextTileId; + } + catch (const std::exception& e) + { + mWriteToDb = false; + Log(Debug::Warning) + << "Failed to update next tile_id, writes to navmeshdb are disabled: " << e.what(); + } + } } } };