Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/game/StarWireProcessor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/game/StarWireProcessor.cpp')
-rw-r--r--source/game/StarWireProcessor.cpp132
1 files changed, 132 insertions, 0 deletions
diff --git a/source/game/StarWireProcessor.cpp b/source/game/StarWireProcessor.cpp
new file mode 100644
index 0000000..9b0d674
--- /dev/null
+++ b/source/game/StarWireProcessor.cpp
@@ -0,0 +1,132 @@
+#include "StarWireProcessor.hpp"
+#include "StarWorldStorage.hpp"
+#include "StarEntityMap.hpp"
+#include "StarWireEntity.hpp"
+#include "StarLogging.hpp"
+
+namespace Star {
+
+WireProcessor::WireProcessor(WorldStoragePtr worldStorage) {
+ m_worldStorage = worldStorage;
+}
+
+void WireProcessor::process() {
+ // First, populate all the working entities that are already live
+ m_worldStorage->entityMap()->forAllEntities([&](EntityPtr const& entity) {
+ if (auto wireEntity = as<WireEntity>(entity.get()))
+ populateWorking(wireEntity);
+ });
+
+ // Then, scan the network of each entity in the working set. This may, as a
+ // side effect, load further unconnected wire entities. Because our policy is
+ // to try as hard as possible to make sure that the entire wire entity
+ // network to be loaded at once or not at all, we need to make sure that each
+ // new disconnected entity also has its network loaded and so on. Thus, if
+ // the working entities size changes during scanning, simply scan the whole
+ // thing again until the size stops changing.
+ while (true) {
+ size_t oldWorkingSize = m_workingWireEntities.size();
+ for (auto const& p : m_workingWireEntities.keys()) {
+ if (!m_workingWireEntities.get(p).networkLoaded)
+ loadNetwork(p);
+ }
+ if (m_workingWireEntities.size() == oldWorkingSize)
+ break;
+ }
+
+ for (auto const& p : m_workingWireEntities)
+ p.second.wireEntity->evaluate(this);
+
+ m_workingWireEntities.clear();
+}
+
+bool WireProcessor::readInputConnection(WireConnection const& connection) {
+ if (auto wes = m_workingWireEntities.ptr(connection.entityLocation))
+ return wes->outputStates.get(connection.nodeIndex);
+ return false;
+}
+
+void WireProcessor::populateWorking(WireEntity* wireEntity) {
+ auto p = m_workingWireEntities.insert(wireEntity->tilePosition(), WireEntityState{nullptr, {}, false});
+ if (!p.second) {
+ if (p.first->second.wireEntity != wireEntity)
+ Logger::debug("Multiple wire entities share tile position: %s", wireEntity->position());
+ return;
+ }
+ auto& wes = p.first->second;
+ wes.wireEntity = wireEntity;
+ size_t outputNodeCount = wes.wireEntity->nodeCount(WireDirection::Output);
+ wes.outputStates.resize(outputNodeCount);
+ for (size_t i = 0; i < outputNodeCount; ++i)
+ wes.outputStates[i] = wes.wireEntity->nodeState({WireDirection::Output, i});
+}
+
+void WireProcessor::loadNetwork(Vec2I tilePosition) {
+ HashSet<WorldStorage::Sector> networkSectors;
+ Maybe<float> highestTtl;
+
+ // Recursively load a given WireEntity at the given position. Returns true
+ // if that wire entity was found.
+ // TODO: This is depth first recursive, because that is the simplest thing,
+ // but if this causes issues with recursion depth it can be changed.
+ function<bool(Vec2I)> doLoad;
+ doLoad = [&](Vec2I const& pos) {
+ auto sector = m_worldStorage->sectorForPosition(pos);
+ if (!sector)
+ return false;
+
+ if (m_worldStorage->sectorLoadLevel(*sector) == SectorLoadLevel::Loaded) {
+ auto ttl = *m_worldStorage->sectorTimeToLive(*sector);
+ if (highestTtl)
+ highestTtl = max(*highestTtl, ttl);
+ else
+ highestTtl = ttl;
+ } else {
+ m_worldStorage->loadSector(*sector);
+ m_worldStorage->entityMap()->forEachEntity(RectF(*m_worldStorage->regionForSector(*sector)), [&](EntityPtr const& entity) {
+ if (auto wireEntity = as<WireEntity>(entity.get()))
+ populateWorking(wireEntity);
+ });
+ }
+
+ auto wes = m_workingWireEntities.ptr(pos);
+ if (!wes)
+ return false;
+ if (wes->networkLoaded)
+ return true;
+
+ wes->networkLoaded = true;
+ networkSectors.add(*sector);
+
+ // Recursively descend into all the inbound and outbound nodes, and if we
+ // ever cannot load the wire entity for a connection, go ahead and remove
+ // the connection.
+ size_t inboundNodeCount = wes->wireEntity->nodeCount(WireDirection::Input);
+ for (size_t i = 0; i < inboundNodeCount; ++i) {
+ for (auto const& connection : wes->wireEntity->connectionsForNode({WireDirection::Input, i})) {
+ if (!doLoad(connection.entityLocation))
+ wes->wireEntity->removeNodeConnection({WireDirection::Input, i}, connection);
+ }
+ }
+ size_t outboundNodeCount = wes->wireEntity->nodeCount(WireDirection::Output);
+ for (size_t i = 0; i < outboundNodeCount; ++i) {
+ for (auto const& connection : wes->wireEntity->connectionsForNode({WireDirection::Output, i})) {
+ if (!doLoad(connection.entityLocation))
+ wes->wireEntity->removeNodeConnection({WireDirection::Output, i}, connection);
+ }
+ }
+ return true;
+ };
+
+ doLoad(tilePosition);
+
+ // Set the sector ttl for the entire network to be equal to the highest
+ // entry, so that the entire network either lives or dies together, but
+ // without artificially extending the lifetime of the network.
+ if (highestTtl) {
+ for (auto const& sector : networkSectors)
+ m_worldStorage->setSectorTimeToLive(sector, *highestTtl);
+ }
+}
+
+}