1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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: {}", 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);
}
}
}
|