diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-07-31 02:40:06 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-07-31 02:40:06 +1000 |
commit | 610dc72c6d6cae1fc9b469f56043fc9c65ca5791 (patch) | |
tree | ee0af7ed2fd683576fac30047f89d8c7c2c7e969 | |
parent | 31f5816e8a545a86f22df6d1a95015ce611f0784 (diff) |
Tile Prediction
-rw-r--r-- | source/game/StarTileSectorArray.hpp | 4 | ||||
-rw-r--r-- | source/game/StarWorldClient.cpp | 126 | ||||
-rw-r--r-- | source/game/StarWorldClient.hpp | 8 | ||||
-rw-r--r-- | source/game/StarWorldImpl.hpp | 116 | ||||
-rw-r--r-- | source/game/StarWorldServer.cpp | 9 | ||||
-rw-r--r-- | source/game/StarWorldServer.hpp | 3 | ||||
-rw-r--r-- | source/game/StarWorldTiles.cpp | 16 | ||||
-rw-r--r-- | source/game/StarWorldTiles.hpp | 31 |
8 files changed, 233 insertions, 80 deletions
diff --git a/source/game/StarTileSectorArray.hpp b/source/game/StarTileSectorArray.hpp index 398e920..40378db 100644 --- a/source/game/StarTileSectorArray.hpp +++ b/source/game/StarTileSectorArray.hpp @@ -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; if (column) { 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 { 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; }, true); diff --git a/source/game/StarWorldClient.cpp b/source/game/StarWorldClient.cpp index ee16a91..280c336 100644 --- a/source/game/StarWorldClient.cpp +++ b/source/game/StarWorldClient.cpp @@ -313,8 +313,8 @@ TileModificationList WorldClient::validTileModifications(TileModificationList co if (!inWorld()) return {}; - return WorldImpl::splitTileModifications(m_tileArray, m_entityMap, modificationList, allowEntityOverlap, [this](Vec2I pos, TileModification) { - return !m_predictedTiles.contains(pos) && !isTileProtected(pos); + return WorldImpl::splitTileModifications(m_entityMap, modificationList, allowEntityOverlap, m_tileGetterFunction, [this](Vec2I pos, TileModification) { + return !isTileProtected(pos); }).first; } @@ -322,14 +322,13 @@ TileModificationList WorldClient::applyTileModifications(TileModificationList co if (!inWorld()) return {}; - auto result = WorldImpl::splitTileModifications(m_tileArray, m_entityMap, modificationList, allowEntityOverlap, [this](Vec2I pos, TileModification) { - return !m_predictedTiles.contains(pos) && !isTileProtected(pos); + auto result = WorldImpl::splitTileModifications(m_entityMap, modificationList, allowEntityOverlap, m_tileGetterFunction, [this](Vec2I pos, TileModification) { + return !isTileProtected(pos); }); if (!result.first.empty()) { - for (auto entry : result.first) - m_predictedTiles[entry.first] = 0; - m_outgoingPackets.append(make_shared<ModifyTileListPacket>(result.first, allowEntityOverlap)); + informTilePredictions(result.first); + m_outgoingPackets.append(make_shared<ModifyTileListPacket>(result.first, true)); } return result.second; @@ -518,7 +517,7 @@ void WorldClient::render(WorldRenderData& renderData, unsigned bufferTiles) { 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.foregroundMod = clientTile.foregroundMod; @@ -539,6 +538,22 @@ void WorldClient::render(WorldRenderData& renderData, unsigned bufferTiles) { renderTile.liquidId = clientTile.liquid.liquid; 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) { @@ -991,13 +1006,8 @@ void WorldClient::update(float dt) { m_lightingCalculator.setMonochrome(Root::singleton().configuration()->get("monochromeLighting").toBool()); - auto predictedTilesIt = makeSMutableMapIterator(m_predictedTiles); - while (predictedTilesIt.hasNext()) { - auto& entry = predictedTilesIt.next(); - if (entry.second++ > m_modifiedTilePredictionTimeout) { - predictedTilesIt.remove(); - } - } + auto expiry = Time::monotonicMilliseconds() + min<int64_t>(m_latency + 100, 2000); + eraseWhere(m_predictedTiles, [expiry](auto& pair){ return pair.second.time > expiry; }); // 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. @@ -1146,6 +1156,11 @@ void WorldClient::update(float dt) { if (m_collisionDebug) 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_sectors", toString(loadedSectors.size())); LogMap::set("client_lua_mem", m_luaRoot->luaMemoryUsage()); @@ -1490,6 +1505,7 @@ void WorldClient::lightingTileGather() { auto materialDatabase = Root::singleton().materialDatabase(); // 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) { size_t baseIndex = m_lightingCalculator.baseIndexFor(pos); 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_entityMap = make_shared<EntityMap>(m_worldTemplate->size(), entitySpace.first, entitySpace.second); 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_luaRoot->restart(); 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) 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->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() { m_forceRegions.clear(); diff --git a/source/game/StarWorldClient.hpp b/source/game/StarWorldClient.hpp index 3f9c0bb..d271f3f 100644 --- a/source/game/StarWorldClient.hpp +++ b/source/game/StarWorldClient.hpp @@ -203,6 +203,8 @@ private: bool operator<(DamageNumberKey const& other) const; }; + typedef function<ClientTile const& (Vec2I)> ClientTileGetter; + void lightingTileGather(); void lightingMain(); @@ -234,6 +236,8 @@ private: void freshenCollision(RectI const& region); void renderCollisionDebug(); + void informTilePredictions(TileModificationList const& modifications); + void setTileProtection(DungeonId dungeonId, bool isProtected); void setupForceRegions(); @@ -247,7 +251,7 @@ private: EntityMapPtr m_entityMap; ClientTileSectorArrayPtr m_tileArray; - + ClientTileGetter m_tileGetterFunction; DamageManagerPtr m_damageManager; LuaRootPtr m_luaRoot; @@ -335,7 +339,7 @@ private: bool m_altMusicActive; int m_modifiedTilePredictionTimeout; - HashMap<Vec2I, int> m_predictedTiles; + HashMap<Vec2I, PredictedTile> m_predictedTiles; HashSet<EntityId> m_startupHiddenEntities; HashMap<DungeonId, float> m_dungeonIdGravity; diff --git a/source/game/StarWorldImpl.hpp b/source/game/StarWorldImpl.hpp index 0105deb..e8c0823 100644 --- a/source/game/StarWorldImpl.hpp +++ b/source/game/StarWorldImpl.hpp @@ -36,24 +36,23 @@ namespace WorldImpl { 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); - template <typename TileSectorArray> - bool canPlaceMaterial(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap, - Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap); + template <typename GetTileFunction> + bool canPlaceMaterial(EntityMapPtr const& entityMap, + 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 // tiles can be placed // that connect to it - template <typename TileSectorArray> - bool perhapsCanPlaceMaterial(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap, - Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap); - template <typename TileSectorArray> - bool canPlaceMaterialColorVariant(shared_ptr<TileSectorArray> const& tileSectorArray, Vec2I const& pos, - TileLayer layer, MaterialColorVariant color); - template <typename TileSectorArray> - bool canPlaceMod(shared_ptr<TileSectorArray> const& tileSectorArray, Vec2I const& pos, TileLayer layer, ModId mod); + template <typename GetTileFunction> + bool perhapsCanPlaceMaterial(EntityMapPtr const& entityMap, + Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, GetTileFunction& getTile); + template <typename GetTileFunction> + bool canPlaceMaterialColorVariant(Vec2I const& pos, TileLayer layer, MaterialColorVariant color, GetTileFunction& getTile); + template <typename GetTileFunction> + bool canPlaceMod(Vec2I const& pos, TileLayer layer, ModId mod, GetTileFunction& getTile); // Split modification list into good and bad - template <typename TileSectorArray> - pair<TileModificationList, TileModificationList> splitTileModifications(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap, - TileModificationList const& modificationList, function<bool(Vec2I pos, TileModification modification)> extraCheck = {}); + template <typename GetTileFunction> + pair<TileModificationList, TileModificationList> splitTileModifications(EntityMapPtr const& entityMap, TileModificationList const& modificationList, + bool allowEntityOverlap, function<bool(Vec2I pos, GetTileFunction& getTile, TileModification modification)> extraCheck = {}); template <typename TileSectorArray> float windLevel(shared_ptr<TileSectorArray> const& tileSectorArray, Vec2F const& position, float weatherWindLevel); @@ -207,43 +206,48 @@ namespace WorldImpl { return res; } - template <typename TileSectorArray> - bool canPlaceMaterial(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap, - Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap) { + template <typename GetTileFunction> + bool canPlaceMaterial(EntityMapPtr const& entityMap, + Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, GetTileFunction& getTile) { auto materialDatabase = Root::singleton().materialDatabase(); if (!isRealMaterial(material)) return false; auto isAdjacentToConnectable = [&](Vec2I const& pos, unsigned distance, bool foreground) { - return tileSectorArray->tileSatisfies(pos, distance, [&](Vec2I const& tpos, typename TileSectorArray::Tile const& tile) { - // Skip if we're looking at the block at pos, allow placement if - // placing on world bottom - if (tpos == pos) - return false; - else if (tpos[1] < 0) - return true; - else if (foreground) - return isConnectableMaterial(tile.foreground); - else - return isConnectableMaterial(tile.background); - }); + if (pos.y() - distance < 0) + return true; + + int maxY = pos.y() + distance + 1; + int maxX = pos.x() + distance + 1; + for (int y = pos.y() - distance; y != maxY; ++y) { + Vec2I tPos = { 0, y }; + for (int x = pos.x() - distance; x != maxX; ++x) { + tPos[0] = x; + if (tPos != pos) { + auto& tile = getTile(tPos); + if (isConnectableMaterial(foreground ? tile.foreground : tile.background)) + return true; + } + } + } + return false; }; if (!materialDatabase->canPlaceInLayer(material, layer)) return false; if (layer == TileLayer::Background) { - if (tileSectorArray->tile(pos).background != EmptyMaterialId) + if (getTile(pos).background != EmptyMaterialId) return false; // Can attach background blocks to other background blocks, *or* the // foreground block in front of it. if (!isAdjacentToConnectable(pos, 1, false) - && !isConnectableMaterial(tileSectorArray->tile({pos[0], pos[1]}).foreground)) + && !isConnectableMaterial(getTile({pos[0], pos[1]}).foreground)) return false; } else { - if (tileSectorArray->tile(pos).foreground != EmptyMaterialId) + if (getTile(pos).foreground != EmptyMaterialId) return false; if (entityMap->tileIsOccupied(pos)) @@ -252,16 +256,16 @@ namespace WorldImpl { if (!allowEntityOverlap && entityMap->spaceIsOccupied(RectF::withSize(Vec2F(pos), Vec2F(1, 1)))) 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 true; } - template <typename TileSectorArray> - bool perhapsCanPlaceMaterial(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap, - Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap) { + template <typename GetTileFunction> + bool perhapsCanPlaceMaterial(EntityMapPtr const& entityMap, + Vec2I const& pos, TileLayer layer, MaterialId material, bool allowEntityOverlap, GetTileFunction& getTile) { auto materialDatabase = Root::singleton().materialDatabase(); if (!isRealMaterial(material)) @@ -271,10 +275,10 @@ namespace WorldImpl { return false; if (layer == TileLayer::Background) { - if (tileSectorArray->tile(pos).background != EmptyMaterialId) + if (getTile(pos).background != EmptyMaterialId) return false; } else { - if (tileSectorArray->tile(pos).foreground != EmptyMaterialId) + if (getTile(pos).foreground != EmptyMaterialId) return false; if (entityMap->tileIsOccupied(pos)) @@ -287,31 +291,31 @@ namespace WorldImpl { return true; } - template <typename TileSectorArray> - bool canPlaceMaterialColorVariant(shared_ptr<TileSectorArray> const& tileSectorArray, - Vec2I const& pos, TileLayer layer, MaterialColorVariant color) { + template <typename GetTileFunction> + bool canPlaceMaterialColorVariant(Vec2I const& pos, TileLayer layer, MaterialColorVariant color, GetTileFunction& getTile) { auto materialDatabase = Root::singleton().materialDatabase(); - auto mat = tileSectorArray->tile(pos).material(layer); - auto existingColor = tileSectorArray->tile(pos).materialColor(layer); - auto existingHue = layer == TileLayer::Foreground ? tileSectorArray->tile(pos).foregroundHueShift : tileSectorArray->tile(pos).backgroundHueShift; + auto& tile = getTile(pos); + auto mat = tile.material(layer); + auto existingColor = tile.materialColor(layer); + auto existingHue = layer == TileLayer::Foreground ? tile.foregroundHueShift : tile.backgroundHueShift; return existingHue != 0 || (existingColor != color && materialDatabase->isMultiColor(mat)); } - template <typename TileSectorArray> - bool canPlaceMod(shared_ptr<TileSectorArray> const& tileSectorArray, Vec2I const& pos, TileLayer layer, ModId mod) { + template <typename GetTileFunction> + bool canPlaceMod(Vec2I const& pos, TileLayer layer, ModId mod, GetTileFunction& getTile) { if (!isRealMod(mod)) return false; auto materialDatabase = Root::singleton().materialDatabase(); - auto mat = tileSectorArray->tile(pos).material(layer); - auto existingMod = tileSectorArray->tile(pos).mod(layer); + auto mat = getTile(pos).material(layer); + auto existingMod = getTile(pos).mod(layer); return existingMod != mod && materialDatabase->supportsMod(mat, mod); } - template <typename TileSectorArray> - pair<TileModificationList, TileModificationList> splitTileModifications(shared_ptr<TileSectorArray> const& tileSectorArray, EntityMapPtr const& entityMap, - TileModificationList const& modificationList, bool allowEntityOverlap, function<bool(Vec2I pos, TileModification modification)> extraCheck) { + template <typename GetTileFunction> + pair<TileModificationList, TileModificationList> splitTileModifications(EntityMapPtr const& entityMap, + TileModificationList const& modificationList, bool allowEntityOverlap, GetTileFunction& getTile, function<bool(Vec2I pos, TileModification modification)> extraCheck) { TileModificationList success; TileModificationList unknown; TileModificationList failures; @@ -325,15 +329,15 @@ namespace WorldImpl { if (extraCheck && !extraCheck(pos, modification)) { good = false; } 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) - 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>()) { - 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>()) { - good = WorldImpl::canPlaceMaterialColorVariant(tileSectorArray, pos, placeMaterialColor->layer, placeMaterialColor->color); + good = WorldImpl::canPlaceMaterialColorVariant(pos, placeMaterialColor->layer, placeMaterialColor->color, getTile); } else if (modification.is<PlaceLiquid>()) { - good = tileSectorArray->tile(pos).collision == CollisionKind::None; + good = getTile(pos).collision == CollisionKind::None; } else { good = false; } diff --git a/source/game/StarWorldServer.cpp b/source/game/StarWorldServer.cpp index fe0c431..be00d1f 100644 --- a/source/game/StarWorldServer.cpp +++ b/source/game/StarWorldServer.cpp @@ -822,7 +822,7 @@ void WorldServer::setSpawningEnabled(bool spawningEnabled) { } 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); }).first; } @@ -1235,6 +1235,7 @@ void WorldServer::init(bool firstTime) { m_geometry = WorldGeometry(m_worldTemplate->size()); m_entityMap = m_worldStorage->entityMap(); m_tileArray = m_worldStorage->tileArray(); + m_tileGetterFunction = [&](Vec2I pos) -> ServerTile const& { return m_tileArray->tile(pos); }; m_damageManager = make_shared<DamageManager>(this, ServerConnectionId); m_wireProcessor = make_shared<WireProcessor>(m_worldStorage); m_luaRoot = make_shared<LuaRoot>(); @@ -1347,7 +1348,7 @@ TileModificationList WorldServer::doApplyTileModifications(TileModificationList continue; 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; ServerTile* tile = m_tileArray->modifyTile(pos); @@ -1396,7 +1397,7 @@ TileModificationList WorldServer::doApplyTileModifications(TileModificationList queueTileUpdates(pos); } 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; ServerTile* tile = m_tileArray->modifyTile(pos); @@ -1421,7 +1422,7 @@ TileModificationList WorldServer::doApplyTileModifications(TileModificationList queueTileUpdates(pos); } 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; WorldTile* tile = m_tileArray->modifyTile(pos); diff --git a/source/game/StarWorldServer.hpp b/source/game/StarWorldServer.hpp index 896bb40..7e14efa 100644 --- a/source/game/StarWorldServer.hpp +++ b/source/game/StarWorldServer.hpp @@ -296,6 +296,8 @@ private: List<Vec2I> roots; }; + typedef function<ServerTile const& (Vec2I)> ServerTileGetter; + void init(bool firstTime); // Returns nothing if the processing defined by the given configuration entry @@ -346,6 +348,7 @@ private: EntityMapPtr m_entityMap; ServerTileSectorArrayPtr m_tileArray; + ServerTileGetter m_tileGetterFunction; WorldStoragePtr m_worldStorage; WorldServerFidelity m_fidelity; Json m_fidelityConfig; diff --git a/source/game/StarWorldTiles.cpp b/source/game/StarWorldTiles.cpp index 218339b..20ced8d 100644 --- a/source/game/StarWorldTiles.cpp +++ b/source/game/StarWorldTiles.cpp @@ -109,6 +109,22 @@ bool ServerTile::updateCollision(CollisionKind kind) { 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) { ds.read(tile.background); if (tile.background == 0) { diff --git a/source/game/StarWorldTiles.hpp b/source/game/StarWorldTiles.hpp index fc6c81c..b33edcf 100644 --- a/source/game/StarWorldTiles.hpp +++ b/source/game/StarWorldTiles.hpp @@ -119,6 +119,37 @@ struct NetTile { DataStream& operator>>(DataStream& ds, NetTile& 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 // out specifically to avoid padding bytes so that a fast path can be taken // when hashing for chunk render caching. |