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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/game/StarTileSectorArray.hpp4
-rw-r--r--source/game/StarWorldClient.cpp126
-rw-r--r--source/game/StarWorldClient.hpp8
-rw-r--r--source/game/StarWorldImpl.hpp116
-rw-r--r--source/game/StarWorldServer.cpp9
-rw-r--r--source/game/StarWorldServer.hpp3
-rw-r--r--source/game/StarWorldTiles.cpp16
-rw-r--r--source/game/StarWorldTiles.hpp31
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.