Tile Prediction
This commit is contained in:
parent
31f5816e8a
commit
610dc72c6d
@ -343,10 +343,10 @@ void TileSectorArray<Tile, SectorSize>::tileEachTo(MultiArray& results, RectI co
|
|||||||
size_t arrayColumnIndex = (x + split.xOffset + xArrayOffset) * results.size(1) + y + yArrayOffset;
|
size_t arrayColumnIndex = (x + split.xOffset + xArrayOffset) * results.size(1) + y + yArrayOffset;
|
||||||
if (column) {
|
if (column) {
|
||||||
for (size_t i = 0; i < columnSize; ++i)
|
for (size_t i = 0; i < columnSize; ++i)
|
||||||
function(results.atIndex(arrayColumnIndex + i), Vec2I((int)x + split.xOffset, y), column[i]);
|
function(results.atIndex(arrayColumnIndex + i), Vec2I((int)x + split.xOffset, y + i), column[i]);
|
||||||
} else {
|
} else {
|
||||||
for (size_t i = 0; i < columnSize; ++i)
|
for (size_t i = 0; i < columnSize; ++i)
|
||||||
function(results.atIndex(arrayColumnIndex + i), Vec2I((int)x + split.xOffset, y), m_default);
|
function(results.atIndex(arrayColumnIndex + i), Vec2I((int)x + split.xOffset, y + i), m_default);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}, true);
|
}, true);
|
||||||
|
@ -313,8 +313,8 @@ TileModificationList WorldClient::validTileModifications(TileModificationList co
|
|||||||
if (!inWorld())
|
if (!inWorld())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return WorldImpl::splitTileModifications(m_tileArray, m_entityMap, modificationList, allowEntityOverlap, [this](Vec2I pos, TileModification) {
|
return WorldImpl::splitTileModifications(m_entityMap, modificationList, allowEntityOverlap, m_tileGetterFunction, [this](Vec2I pos, TileModification) {
|
||||||
return !m_predictedTiles.contains(pos) && !isTileProtected(pos);
|
return !isTileProtected(pos);
|
||||||
}).first;
|
}).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,14 +322,13 @@ TileModificationList WorldClient::applyTileModifications(TileModificationList co
|
|||||||
if (!inWorld())
|
if (!inWorld())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
auto result = WorldImpl::splitTileModifications(m_tileArray, m_entityMap, modificationList, allowEntityOverlap, [this](Vec2I pos, TileModification) {
|
auto result = WorldImpl::splitTileModifications(m_entityMap, modificationList, allowEntityOverlap, m_tileGetterFunction, [this](Vec2I pos, TileModification) {
|
||||||
return !m_predictedTiles.contains(pos) && !isTileProtected(pos);
|
return !isTileProtected(pos);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.first.empty()) {
|
if (!result.first.empty()) {
|
||||||
for (auto entry : result.first)
|
informTilePredictions(result.first);
|
||||||
m_predictedTiles[entry.first] = 0;
|
m_outgoingPackets.append(make_shared<ModifyTileListPacket>(result.first, true));
|
||||||
m_outgoingPackets.append(make_shared<ModifyTileListPacket>(result.first, allowEntityOverlap));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.second;
|
return result.second;
|
||||||
@ -518,7 +517,7 @@ void WorldClient::render(WorldRenderData& renderData, unsigned bufferTiles) {
|
|||||||
return a->entityId() < b->entityId();
|
return a->entityId() < b->entityId();
|
||||||
});
|
});
|
||||||
|
|
||||||
m_tileArray->tileEachTo(renderData.tiles, tileRange, [](RenderTile& renderTile, Vec2I const&, ClientTile const& clientTile) {
|
m_tileArray->tileEachTo(renderData.tiles, tileRange, [&](RenderTile& renderTile, Vec2I const& position, ClientTile const& clientTile) {
|
||||||
renderTile.foreground = clientTile.foreground;
|
renderTile.foreground = clientTile.foreground;
|
||||||
renderTile.foregroundMod = clientTile.foregroundMod;
|
renderTile.foregroundMod = clientTile.foregroundMod;
|
||||||
|
|
||||||
@ -539,6 +538,22 @@ void WorldClient::render(WorldRenderData& renderData, unsigned bufferTiles) {
|
|||||||
|
|
||||||
renderTile.liquidId = clientTile.liquid.liquid;
|
renderTile.liquidId = clientTile.liquid.liquid;
|
||||||
renderTile.liquidLevel = floatToByte(clientTile.liquid.level);
|
renderTile.liquidLevel = floatToByte(clientTile.liquid.level);
|
||||||
|
|
||||||
|
if (!m_predictedTiles.empty()) {
|
||||||
|
if (auto p = m_predictedTiles.ptr(position)) {
|
||||||
|
if (p->liquid) {
|
||||||
|
auto& liquid = *p->liquid;
|
||||||
|
if (liquid.liquid == renderTile.liquidId)
|
||||||
|
renderTile.liquidLevel = floatToByte(clientTile.liquid.level + liquid.level, true);
|
||||||
|
else {
|
||||||
|
renderTile.liquidId = liquid.liquid;
|
||||||
|
renderTile.liquidLevel = floatToByte(liquid.level, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p->apply(renderTile);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (auto const& previewTile : previewTiles) {
|
for (auto const& previewTile : previewTiles) {
|
||||||
@ -991,13 +1006,8 @@ void WorldClient::update(float dt) {
|
|||||||
|
|
||||||
m_lightingCalculator.setMonochrome(Root::singleton().configuration()->get("monochromeLighting").toBool());
|
m_lightingCalculator.setMonochrome(Root::singleton().configuration()->get("monochromeLighting").toBool());
|
||||||
|
|
||||||
auto predictedTilesIt = makeSMutableMapIterator(m_predictedTiles);
|
auto expiry = Time::monotonicMilliseconds() + min<int64_t>(m_latency + 100, 2000);
|
||||||
while (predictedTilesIt.hasNext()) {
|
eraseWhere(m_predictedTiles, [expiry](auto& pair){ return pair.second.time > expiry; });
|
||||||
auto& entry = predictedTilesIt.next();
|
|
||||||
if (entry.second++ > m_modifiedTilePredictionTimeout) {
|
|
||||||
predictedTilesIt.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Secret broadcasts are transmitted through DamageNotifications for vanilla server compatibility.
|
// Secret broadcasts are transmitted through DamageNotifications for vanilla server compatibility.
|
||||||
// Because DamageNotification packets are spoofable, we have to sign the data so other clients can validate that it is legitimate.
|
// Because DamageNotification packets are spoofable, we have to sign the data so other clients can validate that it is legitimate.
|
||||||
@ -1146,6 +1156,11 @@ void WorldClient::update(float dt) {
|
|||||||
if (m_collisionDebug)
|
if (m_collisionDebug)
|
||||||
renderCollisionDebug();
|
renderCollisionDebug();
|
||||||
|
|
||||||
|
for (auto const& prediction : m_predictedTiles) {
|
||||||
|
auto poly = PolyF(RectF::withCenter(Vec2F(prediction.first) + Vec2F::filled(0.5f), Vec2F::filled(0.875f)));
|
||||||
|
SpatialLogger::logPoly("world", poly, Color::Cyan.toRgba());
|
||||||
|
}
|
||||||
|
|
||||||
LogMap::set("client_entities", m_entityMap->size());
|
LogMap::set("client_entities", m_entityMap->size());
|
||||||
LogMap::set("client_sectors", toString(loadedSectors.size()));
|
LogMap::set("client_sectors", toString(loadedSectors.size()));
|
||||||
LogMap::set("client_lua_mem", m_luaRoot->luaMemoryUsage());
|
LogMap::set("client_lua_mem", m_luaRoot->luaMemoryUsage());
|
||||||
@ -1490,6 +1505,7 @@ void WorldClient::lightingTileGather() {
|
|||||||
auto materialDatabase = Root::singleton().materialDatabase();
|
auto materialDatabase = Root::singleton().materialDatabase();
|
||||||
|
|
||||||
// Each column in tileEvalColumns is guaranteed to be no larger than the sector size.
|
// Each column in tileEvalColumns is guaranteed to be no larger than the sector size.
|
||||||
|
|
||||||
m_tileArray->tileEvalColumns(m_lightingCalculator.calculationRegion(), [&](Vec2I const& pos, ClientTile const* column, size_t ySize) {
|
m_tileArray->tileEvalColumns(m_lightingCalculator.calculationRegion(), [&](Vec2I const& pos, ClientTile const* column, size_t ySize) {
|
||||||
size_t baseIndex = m_lightingCalculator.baseIndexFor(pos);
|
size_t baseIndex = m_lightingCalculator.baseIndexFor(pos);
|
||||||
for (size_t y = 0; y < ySize; ++y) {
|
for (size_t y = 0; y < ySize; ++y) {
|
||||||
@ -1552,6 +1568,23 @@ void WorldClient::initWorld(WorldStartPacket const& startPacket) {
|
|||||||
m_worldTemplate = make_shared<WorldTemplate>(startPacket.templateData);
|
m_worldTemplate = make_shared<WorldTemplate>(startPacket.templateData);
|
||||||
m_entityMap = make_shared<EntityMap>(m_worldTemplate->size(), entitySpace.first, entitySpace.second);
|
m_entityMap = make_shared<EntityMap>(m_worldTemplate->size(), entitySpace.first, entitySpace.second);
|
||||||
m_tileArray = make_shared<ClientTileSectorArray>(m_worldTemplate->size());
|
m_tileArray = make_shared<ClientTileSectorArray>(m_worldTemplate->size());
|
||||||
|
m_tileGetterFunction = [&, tile = ClientTile()](Vec2I pos) mutable -> ClientTile const& {
|
||||||
|
if (!m_predictedTiles.empty()) {
|
||||||
|
if (auto p = m_predictedTiles.ptr(pos)) {
|
||||||
|
p->apply(tile = m_tileArray->tile(pos));
|
||||||
|
if (p->liquid) {
|
||||||
|
if (p->liquid->liquid == tile.liquid.liquid)
|
||||||
|
tile.liquid.level += p->liquid->level;
|
||||||
|
else {
|
||||||
|
tile.liquid.liquid = p->liquid->liquid;
|
||||||
|
tile.liquid.level = p->liquid->level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_tileArray->tile(pos);
|
||||||
|
};
|
||||||
m_damageManager = make_shared<DamageManager>(this, startPacket.clientId);
|
m_damageManager = make_shared<DamageManager>(this, startPacket.clientId);
|
||||||
m_luaRoot->restart();
|
m_luaRoot->restart();
|
||||||
m_luaRoot->tuneAutoGarbageCollection(m_clientConfig.getFloat("luaGcPause"), m_clientConfig.getFloat("luaGcStepMultiplier"));
|
m_luaRoot->tuneAutoGarbageCollection(m_clientConfig.getFloat("luaGcPause"), m_clientConfig.getFloat("luaGcStepMultiplier"));
|
||||||
@ -1735,7 +1768,33 @@ bool WorldClient::readNetTile(Vec2I const& pos, NetTile const& netTile) {
|
|||||||
if (!tile)
|
if (!tile)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_predictedTiles.remove(pos);
|
if (!m_predictedTiles.empty()) {
|
||||||
|
auto findPrediction = m_predictedTiles.find(pos);
|
||||||
|
if (findPrediction != m_predictedTiles.end()) {
|
||||||
|
auto& p = findPrediction->second;
|
||||||
|
|
||||||
|
if (p.foreground && *p.foreground == netTile.foreground)
|
||||||
|
p.foreground.reset();
|
||||||
|
if (p.foregroundMod && *p.foregroundMod == netTile.foregroundMod)
|
||||||
|
p.foregroundMod.reset();
|
||||||
|
if (p.foregroundHueShift && *p.foregroundHueShift == netTile.foregroundHueShift)
|
||||||
|
p.foregroundHueShift.reset();
|
||||||
|
if (p.foregroundModHueShift && *p.foregroundModHueShift == netTile.foregroundModHueShift)
|
||||||
|
p.foregroundModHueShift.reset();
|
||||||
|
|
||||||
|
if (p.background && *p.background == netTile.background)
|
||||||
|
p.background.reset();
|
||||||
|
if (p.backgroundMod && *p.backgroundMod == netTile.backgroundMod)
|
||||||
|
p.backgroundMod.reset();
|
||||||
|
if (p.backgroundHueShift && *p.backgroundHueShift == netTile.backgroundHueShift)
|
||||||
|
p.backgroundHueShift.reset();
|
||||||
|
if (p.backgroundModHueShift && *p.backgroundModHueShift == netTile.backgroundModHueShift)
|
||||||
|
p.backgroundModHueShift.reset();
|
||||||
|
|
||||||
|
if (!p)
|
||||||
|
m_predictedTiles.erase(findPrediction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tile->background = netTile.background;
|
tile->background = netTile.background;
|
||||||
tile->backgroundHueShift = netTile.backgroundHueShift;
|
tile->backgroundHueShift = netTile.backgroundHueShift;
|
||||||
@ -2079,6 +2138,41 @@ void WorldClient::renderCollisionDebug() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldClient::informTilePredictions(TileModificationList const& modifications) {
|
||||||
|
auto now = Time::monotonicMilliseconds();
|
||||||
|
for (auto& pair : modifications) {
|
||||||
|
auto& p = m_predictedTiles[pair.first];
|
||||||
|
p.time = now;
|
||||||
|
if (auto placeMaterial = pair.second.ptr<PlaceMaterial>()) {
|
||||||
|
if (placeMaterial->layer == TileLayer::Foreground) {
|
||||||
|
p.foreground = placeMaterial->material;
|
||||||
|
p.foregroundHueShift = placeMaterial->materialHueShift;
|
||||||
|
} else {
|
||||||
|
p.background = placeMaterial->material;
|
||||||
|
p.backgroundHueShift = placeMaterial->materialHueShift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (auto placeMod = pair.second.ptr<PlaceMod>()) {
|
||||||
|
if (placeMod->layer == TileLayer::Foreground)
|
||||||
|
p.foregroundMod = placeMod->mod;
|
||||||
|
else
|
||||||
|
p.backgroundMod = placeMod->mod;
|
||||||
|
}
|
||||||
|
else if (auto placeColor = pair.second.ptr<PlaceMaterialColor>()) {
|
||||||
|
if (placeColor->layer == TileLayer::Foreground)
|
||||||
|
p.foregroundColorVariant = placeColor->color;
|
||||||
|
else
|
||||||
|
p.backgroundColorVariant = placeColor->color;
|
||||||
|
}
|
||||||
|
else if (auto placeLiquid = pair.second.ptr<PlaceLiquid>()) {
|
||||||
|
if (!p.liquid || p.liquid->liquid != placeLiquid->liquid)
|
||||||
|
p.liquid = LiquidLevel(placeLiquid->liquid, placeLiquid->liquidLevel);
|
||||||
|
else
|
||||||
|
p.liquid->level += placeLiquid->liquidLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WorldClient::setupForceRegions() {
|
void WorldClient::setupForceRegions() {
|
||||||
m_forceRegions.clear();
|
m_forceRegions.clear();
|
||||||
|
|
||||||
|
@ -203,6 +203,8 @@ private:
|
|||||||
bool operator<(DamageNumberKey const& other) const;
|
bool operator<(DamageNumberKey const& other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef function<ClientTile const& (Vec2I)> ClientTileGetter;
|
||||||
|
|
||||||
void lightingTileGather();
|
void lightingTileGather();
|
||||||
void lightingMain();
|
void lightingMain();
|
||||||
|
|
||||||
@ -234,6 +236,8 @@ private:
|
|||||||
void freshenCollision(RectI const& region);
|
void freshenCollision(RectI const& region);
|
||||||
void renderCollisionDebug();
|
void renderCollisionDebug();
|
||||||
|
|
||||||
|
void informTilePredictions(TileModificationList const& modifications);
|
||||||
|
|
||||||
void setTileProtection(DungeonId dungeonId, bool isProtected);
|
void setTileProtection(DungeonId dungeonId, bool isProtected);
|
||||||
|
|
||||||
void setupForceRegions();
|
void setupForceRegions();
|
||||||
@ -247,7 +251,7 @@ private:
|
|||||||
|
|
||||||
EntityMapPtr m_entityMap;
|
EntityMapPtr m_entityMap;
|
||||||
ClientTileSectorArrayPtr m_tileArray;
|
ClientTileSectorArrayPtr m_tileArray;
|
||||||
|
ClientTileGetter m_tileGetterFunction;
|
||||||
DamageManagerPtr m_damageManager;
|
DamageManagerPtr m_damageManager;
|
||||||
LuaRootPtr m_luaRoot;
|
LuaRootPtr m_luaRoot;
|
||||||
|
|
||||||
@ -335,7 +339,7 @@ private:
|
|||||||
bool m_altMusicActive;
|
bool m_altMusicActive;
|
||||||
|
|
||||||
int m_modifiedTilePredictionTimeout;
|
int m_modifiedTilePredictionTimeout;
|
||||||
HashMap<Vec2I, int> m_predictedTiles;
|
HashMap<Vec2I, PredictedTile> m_predictedTiles;
|
||||||
HashSet<EntityId> m_startupHiddenEntities;
|
HashSet<EntityId> m_startupHiddenEntities;
|
||||||
|
|
||||||
HashMap<DungeonId, float> m_dungeonIdGravity;
|
HashMap<DungeonId, float> m_dungeonIdGravity;
|
||||||
|
@ -36,24 +36,23 @@ namespace WorldImpl {
|
|||||||
List<Vec2I> collidingTilesAlongLine(WorldGeometry const& worldGeometry, shared_ptr<TileSectorArray> const& tileSectorArray,
|
List<Vec2I> collidingTilesAlongLine(WorldGeometry const& worldGeometry, shared_ptr<TileSectorArray> const& tileSectorArray,
|
||||||
Vec2F const& begin, Vec2F const& end, CollisionSet const& collisionSet, size_t maxSize, bool includeEdges);
|
Vec2F const& begin, Vec2F const& end, CollisionSet const& collisionSet, size_t maxSize, bool includeEdges);
|
||||||
|
|
||||||
template <typename TileSectorArray>
|
template <typename GetTileFunction>
|
||||||
bool canPlaceMaterial(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap,
|
bool canPlaceMaterial(EntityMapPtr const& entityMap,
|
||||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap);
|
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, GetTileFunction& getTile);
|
||||||
// returns true if this material could be placed if in the same batch other
|
// returns true if this material could be placed if in the same batch other
|
||||||
// tiles can be placed
|
// tiles can be placed
|
||||||
// that connect to it
|
// that connect to it
|
||||||
template <typename TileSectorArray>
|
template <typename GetTileFunction>
|
||||||
bool perhapsCanPlaceMaterial(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap,
|
bool perhapsCanPlaceMaterial(EntityMapPtr const& entityMap,
|
||||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap);
|
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, GetTileFunction& getTile);
|
||||||
template <typename TileSectorArray>
|
template <typename GetTileFunction>
|
||||||
bool canPlaceMaterialColorVariant(shared_ptr<TileSectorArray> const& tileSectorArray, Vec2I const& pos,
|
bool canPlaceMaterialColorVariant(Vec2I const& pos, TileLayer layer, MaterialColorVariant color, GetTileFunction& getTile);
|
||||||
TileLayer layer, MaterialColorVariant color);
|
template <typename GetTileFunction>
|
||||||
template <typename TileSectorArray>
|
bool canPlaceMod(Vec2I const& pos, TileLayer layer, ModId mod, GetTileFunction& getTile);
|
||||||
bool canPlaceMod(shared_ptr<TileSectorArray> const& tileSectorArray, Vec2I const& pos, TileLayer layer, ModId mod);
|
|
||||||
// Split modification list into good and bad
|
// Split modification list into good and bad
|
||||||
template <typename TileSectorArray>
|
template <typename GetTileFunction>
|
||||||
pair<TileModificationList, TileModificationList> splitTileModifications(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap,
|
pair<TileModificationList, TileModificationList> splitTileModifications(EntityMapPtr const& entityMap, TileModificationList const& modificationList,
|
||||||
TileModificationList const& modificationList, function<bool(Vec2I pos, TileModification modification)> extraCheck = {});
|
bool allowEntityOverlap, function<bool(Vec2I pos, GetTileFunction& getTile, TileModification modification)> extraCheck = {});
|
||||||
|
|
||||||
template <typename TileSectorArray>
|
template <typename TileSectorArray>
|
||||||
float windLevel(shared_ptr<TileSectorArray> const& tileSectorArray, Vec2F const& position, float weatherWindLevel);
|
float windLevel(shared_ptr<TileSectorArray> const& tileSectorArray, Vec2F const& position, float weatherWindLevel);
|
||||||
@ -207,43 +206,48 @@ namespace WorldImpl {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TileSectorArray>
|
template <typename GetTileFunction>
|
||||||
bool canPlaceMaterial(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap,
|
bool canPlaceMaterial(EntityMapPtr const& entityMap,
|
||||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap) {
|
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, GetTileFunction& getTile) {
|
||||||
auto materialDatabase = Root::singleton().materialDatabase();
|
auto materialDatabase = Root::singleton().materialDatabase();
|
||||||
|
|
||||||
if (!isRealMaterial(material))
|
if (!isRealMaterial(material))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto isAdjacentToConnectable = [&](Vec2I const& pos, unsigned distance, bool foreground) {
|
auto isAdjacentToConnectable = [&](Vec2I const& pos, unsigned distance, bool foreground) {
|
||||||
return tileSectorArray->tileSatisfies(pos, distance, [&](Vec2I const& tpos, typename TileSectorArray::Tile const& tile) {
|
if (pos.y() - distance < 0)
|
||||||
// Skip if we're looking at the block at pos, allow placement if
|
return true;
|
||||||
// placing on world bottom
|
|
||||||
if (tpos == pos)
|
int maxY = pos.y() + distance + 1;
|
||||||
return false;
|
int maxX = pos.x() + distance + 1;
|
||||||
else if (tpos[1] < 0)
|
for (int y = pos.y() - distance; y != maxY; ++y) {
|
||||||
return true;
|
Vec2I tPos = { 0, y };
|
||||||
else if (foreground)
|
for (int x = pos.x() - distance; x != maxX; ++x) {
|
||||||
return isConnectableMaterial(tile.foreground);
|
tPos[0] = x;
|
||||||
else
|
if (tPos != pos) {
|
||||||
return isConnectableMaterial(tile.background);
|
auto& tile = getTile(tPos);
|
||||||
});
|
if (isConnectableMaterial(foreground ? tile.foreground : tile.background))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!materialDatabase->canPlaceInLayer(material, layer))
|
if (!materialDatabase->canPlaceInLayer(material, layer))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (layer == TileLayer::Background) {
|
if (layer == TileLayer::Background) {
|
||||||
if (tileSectorArray->tile(pos).background != EmptyMaterialId)
|
if (getTile(pos).background != EmptyMaterialId)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Can attach background blocks to other background blocks, *or* the
|
// Can attach background blocks to other background blocks, *or* the
|
||||||
// foreground block in front of it.
|
// foreground block in front of it.
|
||||||
if (!isAdjacentToConnectable(pos, 1, false)
|
if (!isAdjacentToConnectable(pos, 1, false)
|
||||||
&& !isConnectableMaterial(tileSectorArray->tile({pos[0], pos[1]}).foreground))
|
&& !isConnectableMaterial(getTile({pos[0], pos[1]}).foreground))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (tileSectorArray->tile(pos).foreground != EmptyMaterialId)
|
if (getTile(pos).foreground != EmptyMaterialId)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (entityMap->tileIsOccupied(pos))
|
if (entityMap->tileIsOccupied(pos))
|
||||||
@ -252,16 +256,16 @@ namespace WorldImpl {
|
|||||||
if (!allowEntityOverlap && entityMap->spaceIsOccupied(RectF::withSize(Vec2F(pos), Vec2F(1, 1))))
|
if (!allowEntityOverlap && entityMap->spaceIsOccupied(RectF::withSize(Vec2F(pos), Vec2F(1, 1))))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!isAdjacentToConnectable(pos, 1, true) && !isConnectableMaterial(tileSectorArray->tile({pos[0], pos[1]}).background))
|
if (!isAdjacentToConnectable(pos, 1, true) && !isConnectableMaterial(getTile({pos[0], pos[1]}).background))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TileSectorArray>
|
template <typename GetTileFunction>
|
||||||
bool perhapsCanPlaceMaterial(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap,
|
bool perhapsCanPlaceMaterial(EntityMapPtr const& entityMap,
|
||||||
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap) {
|
Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, GetTileFunction& getTile) {
|
||||||
auto materialDatabase = Root::singleton().materialDatabase();
|
auto materialDatabase = Root::singleton().materialDatabase();
|
||||||
|
|
||||||
if (!isRealMaterial(material))
|
if (!isRealMaterial(material))
|
||||||
@ -271,10 +275,10 @@ namespace WorldImpl {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (layer == TileLayer::Background) {
|
if (layer == TileLayer::Background) {
|
||||||
if (tileSectorArray->tile(pos).background != EmptyMaterialId)
|
if (getTile(pos).background != EmptyMaterialId)
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (tileSectorArray->tile(pos).foreground != EmptyMaterialId)
|
if (getTile(pos).foreground != EmptyMaterialId)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (entityMap->tileIsOccupied(pos))
|
if (entityMap->tileIsOccupied(pos))
|
||||||
@ -287,31 +291,31 @@ namespace WorldImpl {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TileSectorArray>
|
template <typename GetTileFunction>
|
||||||
bool canPlaceMaterialColorVariant(shared_ptr<TileSectorArray> const& tileSectorArray,
|
bool canPlaceMaterialColorVariant(Vec2I const& pos, TileLayer layer, MaterialColorVariant color, GetTileFunction& getTile) {
|
||||||
Vec2I const& pos, TileLayer layer, MaterialColorVariant color) {
|
|
||||||
auto materialDatabase = Root::singleton().materialDatabase();
|
auto materialDatabase = Root::singleton().materialDatabase();
|
||||||
auto mat = tileSectorArray->tile(pos).material(layer);
|
auto& tile = getTile(pos);
|
||||||
auto existingColor = tileSectorArray->tile(pos).materialColor(layer);
|
auto mat = tile.material(layer);
|
||||||
auto existingHue = layer == TileLayer::Foreground ? tileSectorArray->tile(pos).foregroundHueShift : tileSectorArray->tile(pos).backgroundHueShift;
|
auto existingColor = tile.materialColor(layer);
|
||||||
|
auto existingHue = layer == TileLayer::Foreground ? tile.foregroundHueShift : tile.backgroundHueShift;
|
||||||
return existingHue != 0 || (existingColor != color && materialDatabase->isMultiColor(mat));
|
return existingHue != 0 || (existingColor != color && materialDatabase->isMultiColor(mat));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TileSectorArray>
|
template <typename GetTileFunction>
|
||||||
bool canPlaceMod(shared_ptr<TileSectorArray> const& tileSectorArray, Vec2I const& pos, TileLayer layer, ModId mod) {
|
bool canPlaceMod(Vec2I const& pos, TileLayer layer, ModId mod, GetTileFunction& getTile) {
|
||||||
if (!isRealMod(mod))
|
if (!isRealMod(mod))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto materialDatabase = Root::singleton().materialDatabase();
|
auto materialDatabase = Root::singleton().materialDatabase();
|
||||||
auto mat = tileSectorArray->tile(pos).material(layer);
|
auto mat = getTile(pos).material(layer);
|
||||||
auto existingMod = tileSectorArray->tile(pos).mod(layer);
|
auto existingMod = getTile(pos).mod(layer);
|
||||||
|
|
||||||
return existingMod != mod && materialDatabase->supportsMod(mat, mod);
|
return existingMod != mod && materialDatabase->supportsMod(mat, mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TileSectorArray>
|
template <typename GetTileFunction>
|
||||||
pair<TileModificationList, TileModificationList> splitTileModifications(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap,
|
pair<TileModificationList, TileModificationList> splitTileModifications(EntityMapPtr const& entityMap,
|
||||||
TileModificationList const& modificationList, bool allowEntityOverlap, function<bool(Vec2I pos, TileModification modification)> extraCheck) {
|
TileModificationList const& modificationList, bool allowEntityOverlap, GetTileFunction& getTile, function<bool(Vec2I pos, TileModification modification)> extraCheck) {
|
||||||
TileModificationList success;
|
TileModificationList success;
|
||||||
TileModificationList unknown;
|
TileModificationList unknown;
|
||||||
TileModificationList failures;
|
TileModificationList failures;
|
||||||
@ -325,15 +329,15 @@ namespace WorldImpl {
|
|||||||
if (extraCheck && !extraCheck(pos, modification)) {
|
if (extraCheck && !extraCheck(pos, modification)) {
|
||||||
good = false;
|
good = false;
|
||||||
} else if (auto placeMaterial = modification.ptr<PlaceMaterial>()) {
|
} else if (auto placeMaterial = modification.ptr<PlaceMaterial>()) {
|
||||||
perhaps = WorldImpl::perhapsCanPlaceMaterial(tileSectorArray, entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap);
|
perhaps = WorldImpl::perhapsCanPlaceMaterial(entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, getTile);
|
||||||
if (perhaps)
|
if (perhaps)
|
||||||
good = WorldImpl::canPlaceMaterial(tileSectorArray, entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap);
|
good = WorldImpl::canPlaceMaterial(entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, getTile);
|
||||||
} else if (auto placeMod = modification.ptr<PlaceMod>()) {
|
} else if (auto placeMod = modification.ptr<PlaceMod>()) {
|
||||||
good = WorldImpl::canPlaceMod(tileSectorArray, pos, placeMod->layer, placeMod->mod);
|
good = WorldImpl::canPlaceMod(pos, placeMod->layer, placeMod->mod, getTile);
|
||||||
} else if (auto placeMaterialColor = modification.ptr<PlaceMaterialColor>()) {
|
} else if (auto placeMaterialColor = modification.ptr<PlaceMaterialColor>()) {
|
||||||
good = WorldImpl::canPlaceMaterialColorVariant(tileSectorArray, pos, placeMaterialColor->layer, placeMaterialColor->color);
|
good = WorldImpl::canPlaceMaterialColorVariant(pos, placeMaterialColor->layer, placeMaterialColor->color, getTile);
|
||||||
} else if (modification.is<PlaceLiquid>()) {
|
} else if (modification.is<PlaceLiquid>()) {
|
||||||
good = tileSectorArray->tile(pos).collision == CollisionKind::None;
|
good = getTile(pos).collision == CollisionKind::None;
|
||||||
} else {
|
} else {
|
||||||
good = false;
|
good = false;
|
||||||
}
|
}
|
||||||
|
@ -822,7 +822,7 @@ void WorldServer::setSpawningEnabled(bool spawningEnabled) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TileModificationList WorldServer::validTileModifications(TileModificationList const& modificationList, bool allowEntityOverlap) const {
|
TileModificationList WorldServer::validTileModifications(TileModificationList const& modificationList, bool allowEntityOverlap) const {
|
||||||
return WorldImpl::splitTileModifications(m_tileArray, m_entityMap, modificationList, allowEntityOverlap, [this](Vec2I pos, TileModification) {
|
return WorldImpl::splitTileModifications(m_entityMap, modificationList, allowEntityOverlap, m_tileGetterFunction, [this](Vec2I pos, TileModification) {
|
||||||
return !isTileProtected(pos);
|
return !isTileProtected(pos);
|
||||||
}).first;
|
}).first;
|
||||||
}
|
}
|
||||||
@ -1235,6 +1235,7 @@ void WorldServer::init(bool firstTime) {
|
|||||||
m_geometry = WorldGeometry(m_worldTemplate->size());
|
m_geometry = WorldGeometry(m_worldTemplate->size());
|
||||||
m_entityMap = m_worldStorage->entityMap();
|
m_entityMap = m_worldStorage->entityMap();
|
||||||
m_tileArray = m_worldStorage->tileArray();
|
m_tileArray = m_worldStorage->tileArray();
|
||||||
|
m_tileGetterFunction = [&](Vec2I pos) -> ServerTile const& { return m_tileArray->tile(pos); };
|
||||||
m_damageManager = make_shared<DamageManager>(this, ServerConnectionId);
|
m_damageManager = make_shared<DamageManager>(this, ServerConnectionId);
|
||||||
m_wireProcessor = make_shared<WireProcessor>(m_worldStorage);
|
m_wireProcessor = make_shared<WireProcessor>(m_worldStorage);
|
||||||
m_luaRoot = make_shared<LuaRoot>();
|
m_luaRoot = make_shared<LuaRoot>();
|
||||||
@ -1347,7 +1348,7 @@ TileModificationList WorldServer::doApplyTileModifications(TileModificationList
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (auto placeMaterial = modification.ptr<PlaceMaterial>()) {
|
if (auto placeMaterial = modification.ptr<PlaceMaterial>()) {
|
||||||
if (!WorldImpl::canPlaceMaterial(m_tileArray, m_entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap))
|
if (!WorldImpl::canPlaceMaterial(m_entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, m_tileGetterFunction))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ServerTile* tile = m_tileArray->modifyTile(pos);
|
ServerTile* tile = m_tileArray->modifyTile(pos);
|
||||||
@ -1396,7 +1397,7 @@ TileModificationList WorldServer::doApplyTileModifications(TileModificationList
|
|||||||
queueTileUpdates(pos);
|
queueTileUpdates(pos);
|
||||||
|
|
||||||
} else if (auto placeMod = modification.ptr<PlaceMod>()) {
|
} else if (auto placeMod = modification.ptr<PlaceMod>()) {
|
||||||
if (!WorldImpl::canPlaceMod(m_tileArray, pos, placeMod->layer, placeMod->mod))
|
if (!WorldImpl::canPlaceMod(pos, placeMod->layer, placeMod->mod, m_tileGetterFunction))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ServerTile* tile = m_tileArray->modifyTile(pos);
|
ServerTile* tile = m_tileArray->modifyTile(pos);
|
||||||
@ -1421,7 +1422,7 @@ TileModificationList WorldServer::doApplyTileModifications(TileModificationList
|
|||||||
queueTileUpdates(pos);
|
queueTileUpdates(pos);
|
||||||
|
|
||||||
} else if (auto placeMaterialColor = modification.ptr<PlaceMaterialColor>()) {
|
} else if (auto placeMaterialColor = modification.ptr<PlaceMaterialColor>()) {
|
||||||
if (!WorldImpl::canPlaceMaterialColorVariant(m_tileArray, pos, placeMaterialColor->layer, placeMaterialColor->color))
|
if (!WorldImpl::canPlaceMaterialColorVariant(pos, placeMaterialColor->layer, placeMaterialColor->color, m_tileGetterFunction))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
WorldTile* tile = m_tileArray->modifyTile(pos);
|
WorldTile* tile = m_tileArray->modifyTile(pos);
|
||||||
|
@ -296,6 +296,8 @@ private:
|
|||||||
List<Vec2I> roots;
|
List<Vec2I> roots;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef function<ServerTile const& (Vec2I)> ServerTileGetter;
|
||||||
|
|
||||||
void init(bool firstTime);
|
void init(bool firstTime);
|
||||||
|
|
||||||
// Returns nothing if the processing defined by the given configuration entry
|
// Returns nothing if the processing defined by the given configuration entry
|
||||||
@ -346,6 +348,7 @@ private:
|
|||||||
|
|
||||||
EntityMapPtr m_entityMap;
|
EntityMapPtr m_entityMap;
|
||||||
ServerTileSectorArrayPtr m_tileArray;
|
ServerTileSectorArrayPtr m_tileArray;
|
||||||
|
ServerTileGetter m_tileGetterFunction;
|
||||||
WorldStoragePtr m_worldStorage;
|
WorldStoragePtr m_worldStorage;
|
||||||
WorldServerFidelity m_fidelity;
|
WorldServerFidelity m_fidelity;
|
||||||
Json m_fidelityConfig;
|
Json m_fidelityConfig;
|
||||||
|
@ -109,6 +109,22 @@ bool ServerTile::updateCollision(CollisionKind kind) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PredictedTile::operator bool() const {
|
||||||
|
return
|
||||||
|
background
|
||||||
|
|| backgroundHueShift
|
||||||
|
|| backgroundColorVariant
|
||||||
|
|| backgroundMod
|
||||||
|
|| backgroundModHueShift
|
||||||
|
|| foreground
|
||||||
|
|| foregroundHueShift
|
||||||
|
|| foregroundColorVariant
|
||||||
|
|| foregroundMod
|
||||||
|
|| foregroundModHueShift
|
||||||
|
|| liquid
|
||||||
|
|| collision;
|
||||||
|
}
|
||||||
|
|
||||||
DataStream& operator>>(DataStream& ds, NetTile& tile) {
|
DataStream& operator>>(DataStream& ds, NetTile& tile) {
|
||||||
ds.read(tile.background);
|
ds.read(tile.background);
|
||||||
if (tile.background == 0) {
|
if (tile.background == 0) {
|
||||||
|
@ -119,6 +119,37 @@ struct NetTile {
|
|||||||
DataStream& operator>>(DataStream& ds, NetTile& tile);
|
DataStream& operator>>(DataStream& ds, NetTile& tile);
|
||||||
DataStream& operator<<(DataStream& ds, NetTile const& tile);
|
DataStream& operator<<(DataStream& ds, NetTile const& tile);
|
||||||
|
|
||||||
|
// For storing predicted tile state.
|
||||||
|
struct PredictedTile {
|
||||||
|
int64_t time;
|
||||||
|
Maybe<MaterialId> background;
|
||||||
|
Maybe<MaterialHue> backgroundHueShift;
|
||||||
|
Maybe<MaterialColorVariant> backgroundColorVariant;
|
||||||
|
Maybe<ModId> backgroundMod;
|
||||||
|
Maybe<MaterialHue> backgroundModHueShift;
|
||||||
|
Maybe<MaterialId> foreground;
|
||||||
|
Maybe<MaterialHue> foregroundHueShift;
|
||||||
|
Maybe<MaterialColorVariant> foregroundColorVariant;
|
||||||
|
Maybe<ModId> foregroundMod;
|
||||||
|
Maybe<MaterialHue> foregroundModHueShift;
|
||||||
|
Maybe<LiquidLevel> liquid;
|
||||||
|
Maybe<CollisionKind> collision;
|
||||||
|
|
||||||
|
operator bool() const;
|
||||||
|
template <typename Tile>
|
||||||
|
void apply(Tile& tile) {
|
||||||
|
if (foreground) tile.foreground = *foreground;
|
||||||
|
if (foregroundMod) tile.foregroundMod = *foregroundMod;
|
||||||
|
if (foregroundHueShift) tile.foregroundHueShift = *foregroundHueShift;
|
||||||
|
if (foregroundModHueShift) tile.foregroundModHueShift = *foregroundModHueShift;
|
||||||
|
|
||||||
|
if (background) tile.background = *background;
|
||||||
|
if (backgroundMod) tile.backgroundMod = *backgroundMod;
|
||||||
|
if (backgroundHueShift) tile.backgroundHueShift = *backgroundHueShift;
|
||||||
|
if (backgroundModHueShift) tile.backgroundModHueShift = *backgroundModHueShift;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Just the parts of a tile that are used to render. The members here are laid
|
// Just the parts of a tile that are used to render. The members here are laid
|
||||||
// out specifically to avoid padding bytes so that a fast path can be taken
|
// out specifically to avoid padding bytes so that a fast path can be taken
|
||||||
// when hashing for chunk render caching.
|
// when hashing for chunk render caching.
|
||||||
|
Loading…
Reference in New Issue
Block a user