fissh

termios terminal aquarium. demo at ssh://fish@kloet.net
Download | Log | Files | Refs

commit 629ad21824b143f63c20067fa7e4796f87312513
parent e0dd2a786b68cd6ab0a0f33c4e0124775567580f
Author: amrfti <andrew@kloet.net>
Date:   Tue,  8 Jul 2025 11:08:38 -0400

fix segfault for real

Diffstat:
Msrc/Aquarium.cpp | 43++++++++++++++++++++++++-------------------
Msrc/Castle.cpp | 2+-
Msrc/SeaMonster.cpp | 5+++--
Msrc/Ship.cpp | 4++--
Msrc/Whale.cpp | 4++--
Msrc/assets/CastleAssets.h | 7+++++--
Msrc/assets/SeaMonsterAssets.h | 44++++++++++++++++++++++++++------------------
Msrc/assets/ShipAssets.h | 32+++++++++++++++++++++++---------
Msrc/assets/WhaleAssets.h | 170+++++++++++++++++++++++++++++++++++++++++--------------------------------------
9 files changed, 174 insertions(+), 137 deletions(-)

diff --git a/src/Aquarium.cpp b/src/Aquarium.cpp @@ -47,15 +47,18 @@ void Aquarium::ensureEntitiesSorted() { void Aquarium::redraw() { clearCurrentFrame(); - ensureBigEntityExists(); - std::vector<std::unique_ptr<Entity>> newEntities; - bool entities_modified = false; + // Use static vectors to avoid per-frame allocations + static std::vector<std::unique_ptr<Entity>> newEntities; + static std::vector<size_t> entitiesToRemove; + + newEntities.clear(); + entitiesToRemove.clear(); - // Update and check for removal/replacement - for (auto it = entities.begin(); it != entities.end();) { - auto &entity = *it; + // Update all entities and collect changes + for (size_t i = 0; i < entities.size(); ++i) { + auto &entity = entities[i]; entity->update(); // Handle fish bubble spawning @@ -69,30 +72,32 @@ void Aquarium::redraw() { if (entity->shouldBeRemoved()) { auto replacement = entity->createReplacement(); if (replacement) { - *it = std::move(replacement); - entities_modified = true; - ++it; + entity = std::move(replacement); // Replace in-place + entities_need_sorting = true; } else { - it = entities.erase(it); - entities_modified = true; + entitiesToRemove.push_back(i); // Mark for removal } - } else { - ++it; } } - // Add new entities if any + // Remove entities in reverse order to maintain indices + for (auto it = entitiesToRemove.rbegin(); it != entitiesToRemove.rend(); + ++it) { + entities.erase(entities.begin() + *it); + entities_need_sorting = true; + } + + // Add new entities (only if we have them) if (!newEntities.empty()) { + // Reserve space to minimize reallocations + entities.reserve(entities.size() + newEntities.size()); + for (auto &newEntity : newEntities) { entities.emplace_back(std::move(newEntity)); } - entities_modified = true; - } - - // Only sort if entities were modified - if (entities_modified) { entities_need_sorting = true; } + ensureEntitiesSorted(); // Draw all entities diff --git a/src/Castle.cpp b/src/Castle.cpp @@ -5,4 +5,4 @@ Castle::Castle() : Entity(Aquarium::getInstance().getWidth() - 32, Aquarium::getInstance().getHeight() - 13), - image(castleAsset.image), mask(castleAsset.mask) {} + image(getCastleAsset().image), mask(getCastleAsset().mask) {} diff --git a/src/SeaMonster.cpp b/src/SeaMonster.cpp @@ -6,8 +6,9 @@ SeaMonster::SeaMonster() : SeaMonster(getRandomDirection()) {} SeaMonster::SeaMonster(int asset_index) - : Entity(asset_index == 0), frames(seaMonsterAssets[asset_index].frames), - mask(seaMonsterAssets[asset_index].mask), speed(SEAMONSTER_SPEED) { + : Entity(asset_index == 0), + frames(getSeaMonsterAssets()[asset_index].frames), + mask(getSeaMonsterAssets()[asset_index].mask), speed(SEAMONSTER_SPEED) { const auto &aquarium = Aquarium::getInstance(); y = WATER_SURFACE_OFFSET; diff --git a/src/Ship.cpp b/src/Ship.cpp @@ -6,8 +6,8 @@ Ship::Ship() : Ship(getRandomDirection()) {} Ship::Ship(int asset_index) - : Entity(asset_index == 0), image(shipAssets[asset_index].image), - mask(shipAssets[asset_index].mask), speed(SHIP_SPEED) { + : Entity(asset_index == 0), image(getShipAssets()[asset_index].image), + mask(getShipAssets()[asset_index].mask), speed(SHIP_SPEED) { const auto &aquarium = Aquarium::getInstance(); y = 0; diff --git a/src/Whale.cpp b/src/Whale.cpp @@ -6,8 +6,8 @@ Whale::Whale() : Whale(getRandomDirection()) {} Whale::Whale(int asset_index) - : Entity((asset_index == 0)), frames(whaleAssets[asset_index].frames), - mask(whaleAssets[asset_index].mask), speed(WHALE_SPEED) { + : Entity((asset_index == 0)), frames(getWhaleAssets()[asset_index].frames), + mask(getWhaleAssets()[asset_index].mask), speed(WHALE_SPEED) { const auto &aquarium = Aquarium::getInstance(); y = 0; diff --git a/src/assets/CastleAssets.h b/src/assets/CastleAssets.h @@ -8,7 +8,8 @@ struct CastleAsset { std::vector<std::string> mask; }; -inline CastleAsset castleAsset = { +inline const CastleAsset& getCastleAsset() { + static const CastleAsset castleAsset = { { R"( T~~)", R"( |)", @@ -37,4 +38,6 @@ inline CastleAsset castleAsset = { R"( yy yy)", R"( y y y y)", R"( yyyyyyy)"} -}; + }; + return castleAsset; +} diff --git a/src/assets/SeaMonsterAssets.h b/src/assets/SeaMonsterAssets.h @@ -8,23 +8,31 @@ struct SeaMonsterAsset { std::vector<std::string> mask; }; -const SeaMonsterAsset seaMonster = { - {// Frame 1 +inline const SeaMonsterAsset& getSeaMonster() { + static const SeaMonsterAsset seaMonster = { { - R"(?????????_???_?????????????????????_???_???????_a_a???)", - R"(???????_{.`=`.}_??????_???_??????_{.`=`.}_????{/ ''\_?)", - R"(?_????{.' _ '.}????{.`'`.}????{.' _ '.}??{| ._oo))", - R"({ \??{/ .'?'. \}??{/ .-. \}??{/ .'?'. \}?{/ |????)"}, - { - R"(??????????????????????_???_????????????????????_a_a???)", - R"(??_??????_???_??????_{.`=`.}_??????_???_??????{/ ''\_?)", - R"(?{ \????{.`'`.}????{.' _ '.}????{.`'`.}????{| ._oo))", - R"(??\ \??{/ .-. \}??{/ .'?'. \}??{/ .-. \}???{/ |????)"}}, - { - R"( W W )", - R"()", - R"()", - R"()"}}; + { + R"(?????????_???_?????????????????????_???_???????_a_a???)", + R"(???????_{.`=`.}_??????_???_??????_{.`=`.}_????{/ ''\_?)", + R"(?_????{.' _ '.}????{.`'`.}????{.' _ '.}??{| ._oo))", + R"({ \??{/ .'?'. \}??{/ .-. \}??{/ .'?'. \}?{/ |????)"}, + { + R"(??????????????????????_???_????????????????????_a_a???)", + R"(??_??????_???_??????_{.`=`.}_??????_???_??????{/ ''\_?)", + R"(?{ \????{.`'`.}????{.' _ '.}????{.`'`.}????{| ._oo))", + R"(??\ \??{/ .-. \}??{/ .'?'. \}??{/ .-. \}???{/ |????)"}}, + { + R"( W W )", + R"()", + R"()", + R"()" + } + }; + return seaMonster; +} -inline const std::vector<SeaMonsterAsset> seaMonsterAssets = - createBidirectionalFramedAssets(seaMonster); +inline const std::vector<SeaMonsterAsset>& getSeaMonsterAssets() { + static const std::vector<SeaMonsterAsset> seaMonsterAssets = + createBidirectionalFramedAssets(getSeaMonster()); + return seaMonsterAssets; +} diff --git a/src/assets/ShipAssets.h b/src/assets/ShipAssets.h @@ -3,13 +3,27 @@ #include "../SpriteUtils.h" #include <vector> -const AssetPair ship = { - {R"( | | | )", R"( )_) )_) )_) )", - R"( )___))___))___)\ )", R"( )____)____)_____)\\ )", - R"(_____|____|____|____\\\__)", R"(\ /????)"}, - {R"( y y y )", R"()", R"( w )", - R"( ww )", R"(yyyyyyyyyyyyyyyyyyyywwwyy)", - R"(y y )"}}; +inline const AssetPair &getShip() { + static const AssetPair ship = { + { + R"( | | | )", + R"( )_) )_) )_) )", + R"( )___))___))___)\ )", + R"( )____)____)_____)\\ )", + R"(_____|____|____|____\\\__)", + R"(\ /????)"}, + { + R"( y y y )", + R"()", + R"( w )", + R"( ww )", + R"(yyyyyyyyyyyyyyyyyyyywwwyy)", + R"(y y )"}}; + return ship; +} -inline const std::vector<AssetPair> shipAssets = - createBidirectionalAssets({ship}); +inline const std::vector<AssetPair> &getShipAssets() { + static const std::vector<AssetPair> shipAssets = + createBidirectionalAssets({getShip()}); + return shipAssets; +} diff --git a/src/assets/WhaleAssets.h b/src/assets/WhaleAssets.h @@ -8,91 +8,97 @@ struct WhaleAsset { std::vector<std::string> mask; }; -const WhaleAsset whale = { - { +inline const WhaleAsset& getWhale() { + static const WhaleAsset whale = { { - R"( )", - R"()", - R"()", - R"( .-----. )", - R"( .' `. )", - R"(,????/ (o) \)", - R"(\`._/ ,__)" + { + R"( )", + R"()", + R"()", + R"( .-----. )", + R"( .' `. )", + R"(,????/ (o) \)", + R"(\`._/ ,__)" + }, + { + R"( )", + R"()", + R"( : )", + R"( .-----. )", + R"( .' `. )", + R"(,????/ (o) \)", + R"(\`._/ ,__)" + }, + { + R"( )", + R"( : )", + R"( : )", + R"( .-----. )", + R"( .' `. )", + R"(,????/ (o) \)", + R"(\`._/ ,__)" + }, + { + R"( . . )", + R"( -:- )", + R"( : )", + R"( .-----. )", + R"( .' `. )", + R"(,????/ (o) \)", + R"(\`._/ ,__)" + }, + { + R"( . . )", + R"( .-.-. )", + R"( : )", + R"( .-----. )", + R"( .' `. )", + R"(,????/ (o) \)", + R"(\`._/ ,__)" + }, + { + R"( . . )", + R"( '.-:-.' )", + R"( ' : ' )", + R"( .-----. )", + R"( .' `. )", + R"(,????/ (o) \)", + R"(\`._/ ,__)" + }, + { + R"( )", + R"( .- -. )", + R"( ; : ; )", + R"( .-----. )", + R"( .' `. )", + R"(,????/ (o) \)", + R"(\`._/ ,__)" + }, + { + R"( )", + R"( )", + R"( ; ; )", + R"( .-----. )", + R"( .' `. )", + R"(,????/ (o) \)", + R"(\`._/ ,__)" + } }, { - R"( )", + R"( C C )", + R"( CCCCCCC )", + R"( C C C )", + R"()", R"()", - R"( : )", - R"( .-----. )", - R"( .' `. )", - R"(,????/ (o) \)", - R"(\`._/ ,__)" - }, - { - R"( )", - R"( : )", - R"( : )", - R"( .-----. )", - R"( .' `. )", - R"(,????/ (o) \)", - R"(\`._/ ,__)" - }, - { - R"( . . )", - R"( -:- )", - R"( : )", - R"( .-----. )", - R"( .' `. )", - R"(,????/ (o) \)", - R"(\`._/ ,__)" - }, - { - R"( . . )", - R"( .-.-. )", - R"( : )", - R"( .-----. )", - R"( .' `. )", - R"(,????/ (o) \)", - R"(\`._/ ,__)" - }, - { - R"( . . )", - R"( '.-:-.' )", - R"( ' : ' )", - R"( .-----. )", - R"( .' `. )", - R"(,????/ (o) \)", - R"(\`._/ ,__)" - }, - { - R"( )", - R"( .- -. )", - R"( ; : ; )", - R"( .-----. )", - R"( .' `. )", - R"(,????/ (o) \)", - R"(\`._/ ,__)" - }, - { - R"( )", - R"( )", - R"( ; ; )", - R"( .-----. )", - R"( .' `. )", - R"(,????/ (o) \)", - R"(\`._/ ,__)" + R"( W )", + R"()" } - }, - { - R"( C C )", - R"( CCCCCCC )", - R"( C C C )", - R"()", - R"()", - R"( W )", - R"()" - } -}; + }; + return whale; +} -inline const std::vector<WhaleAsset> whaleAssets = - createBidirectionalFramedAssets(whale); +inline const std::vector<WhaleAsset>& getWhaleAssets() { + static const std::vector<WhaleAsset> whaleAssets = + createBidirectionalFramedAssets(getWhale()); + return whaleAssets; +}