Tile Prediction improvements

each tile modification is now validated and then added to prediction one-by-one
This commit is contained in:
Kae 2023-07-31 17:31:02 +10:00
parent 72e33fdef5
commit 2dc10fa5ad
3 changed files with 112 additions and 70 deletions

View File

@ -322,23 +322,27 @@ TileModificationList WorldClient::applyTileModifications(TileModificationList co
if (!inWorld()) if (!inWorld())
return {}; return {};
auto extraCheck = [this](Vec2I pos, TileModification) { // thanks to new prediction: do each one by one so that previous modifications affect placeability
return !isTileProtected(pos);
};
// thanks to new prediction: do it aggressively until no changes occur TileModificationList success;
auto result = WorldImpl::splitTileModifications(m_entityMap, modificationList, allowEntityOverlap, m_tileGetterFunction, extraCheck); TileModificationList failures;
while (true) { for (auto const& pair : modificationList) {
if (!result.first.empty()) { if (!isTileProtected(pair.first)) {
informTilePredictions(result.first); auto result = WorldImpl::validateTileModification(m_entityMap, pair.first, pair.second, allowEntityOverlap, m_tileGetterFunction);
m_outgoingPackets.append(make_shared<ModifyTileListPacket>(result.first, true));
auto list = move(result.second); if (result.first) {
result = WorldImpl::splitTileModifications(m_entityMap, list, allowEntityOverlap, m_tileGetterFunction, extraCheck); informTilePrediction(pair.first, pair.second);
success.append(pair);
continue;
} }
else
return result.second;
} }
failures.append(pair);
}
if (!success.empty())
m_outgoingPackets.append(make_shared<ModifyTileListPacket>(move(success), true));
return failures;
} }
float WorldClient::gravity(Vec2F const& pos) const { float WorldClient::gravity(Vec2F const& pos) const {
@ -817,8 +821,41 @@ void WorldClient::handleIncomingPackets(List<PacketPtr> const& packets) {
// player, but this may not be true in the future. In the future, there // player, but this may not be true in the future. In the future, there
// may be context hints with tile modifications to figure out what to do // may be context hints with tile modifications to figure out what to do
// with failures. // with failures.
for (auto modification : tileModificationFailure->modifications) { for (auto& modification : tileModificationFailure->modifications) {
m_predictedTiles.remove(modification.first); auto findPrediction = m_predictedTiles.find(modification.first);
if (findPrediction != m_predictedTiles.end()) {
auto& p = findPrediction->second;
if (auto placeMaterial = modification.second.ptr<PlaceMaterial>()) {
if (placeMaterial->layer == TileLayer::Foreground) {
p.foreground.reset();
p.foregroundHueShift.reset();
}
else {
p.background.reset();
p.backgroundHueShift.reset();
}
} else if (auto placeMod = modification.second.ptr<PlaceMod>()) {
if (placeMod->layer == TileLayer::Foreground) {
p.foregroundMod.reset();
p.foregroundModHueShift.reset();
}
else {
p.backgroundMod.reset();
p.backgroundModHueShift.reset();
}
} else if (auto placeColor = modification.second.ptr<PlaceMaterialColor>()) {
if (placeColor->layer == TileLayer::Foreground)
p.foregroundColorVariant.reset();
else
p.backgroundColorVariant.reset();
} else if (auto placeLiquid = modification.second.ptr<PlaceLiquid>()) {
p.liquid.reset();
}
if (!p)
m_predictedTiles.erase(findPrediction);
}
if (auto placeMaterial = modification.second.ptr<PlaceMaterial>()) { if (auto placeMaterial = modification.second.ptr<PlaceMaterial>()) {
auto stack = materialDatabase->materialItemDrop(placeMaterial->material); auto stack = materialDatabase->materialItemDrop(placeMaterial->material);
tryGiveMainPlayerItem(itemDatabase->item(stack)); tryGiveMainPlayerItem(itemDatabase->item(stack));
@ -2148,12 +2185,11 @@ void WorldClient::renderCollisionDebug() {
} }
} }
void WorldClient::informTilePredictions(TileModificationList const& modifications) { void WorldClient::informTilePrediction(Vec2I const& pos, TileModification const& modification) {
auto now = Time::monotonicMilliseconds(); auto now = Time::monotonicMilliseconds();
for (auto& pair : modifications) { auto& p = m_predictedTiles[pos];
auto& p = m_predictedTiles[pair.first];
p.time = now; p.time = now;
if (auto placeMaterial = pair.second.ptr<PlaceMaterial>()) { if (auto placeMaterial = modification.ptr<PlaceMaterial>()) {
if (placeMaterial->layer == TileLayer::Foreground) { if (placeMaterial->layer == TileLayer::Foreground) {
p.foreground = placeMaterial->material; p.foreground = placeMaterial->material;
p.foregroundHueShift = placeMaterial->materialHueShift; p.foregroundHueShift = placeMaterial->materialHueShift;
@ -2162,25 +2198,24 @@ void WorldClient::informTilePredictions(TileModificationList const& modification
p.backgroundHueShift = placeMaterial->materialHueShift; p.backgroundHueShift = placeMaterial->materialHueShift;
} }
} }
else if (auto placeMod = pair.second.ptr<PlaceMod>()) { else if (auto placeMod = modification.ptr<PlaceMod>()) {
if (placeMod->layer == TileLayer::Foreground) if (placeMod->layer == TileLayer::Foreground)
p.foregroundMod = placeMod->mod; p.foregroundMod = placeMod->mod;
else else
p.backgroundMod = placeMod->mod; p.backgroundMod = placeMod->mod;
} }
else if (auto placeColor = pair.second.ptr<PlaceMaterialColor>()) { else if (auto placeColor = modification.ptr<PlaceMaterialColor>()) {
if (placeColor->layer == TileLayer::Foreground) if (placeColor->layer == TileLayer::Foreground)
p.foregroundColorVariant = placeColor->color; p.foregroundColorVariant = placeColor->color;
else else
p.backgroundColorVariant = placeColor->color; p.backgroundColorVariant = placeColor->color;
} }
else if (auto placeLiquid = pair.second.ptr<PlaceLiquid>()) { else if (auto placeLiquid = modification.ptr<PlaceLiquid>()) {
if (!p.liquid || p.liquid->liquid != placeLiquid->liquid) if (!p.liquid || p.liquid->liquid != placeLiquid->liquid)
p.liquid = LiquidLevel(placeLiquid->liquid, placeLiquid->liquidLevel); p.liquid = LiquidLevel(placeLiquid->liquid, placeLiquid->liquidLevel);
else else
p.liquid->level += placeLiquid->liquidLevel; p.liquid->level += placeLiquid->liquidLevel;
} }
}
} }
void WorldClient::setupForceRegions() { void WorldClient::setupForceRegions() {

View File

@ -236,7 +236,7 @@ private:
void freshenCollision(RectI const& region); void freshenCollision(RectI const& region);
void renderCollisionDebug(); void renderCollisionDebug();
void informTilePredictions(TileModificationList const& modifications); void informTilePrediction(Vec2I const& pos, TileModification const& modification);
void setTileProtection(DungeonId dungeonId, bool isProtected); void setTileProtection(DungeonId dungeonId, bool isProtected);

View File

@ -49,10 +49,12 @@ namespace WorldImpl {
bool canPlaceMaterialColorVariant(Vec2I const& pos, TileLayer layer, MaterialColorVariant color, GetTileFunction& getTile); bool canPlaceMaterialColorVariant(Vec2I const& pos, TileLayer layer, MaterialColorVariant color, GetTileFunction& getTile);
template <typename GetTileFunction> template <typename GetTileFunction>
bool canPlaceMod(Vec2I const& pos, TileLayer layer, ModId mod, GetTileFunction& getTile); bool canPlaceMod(Vec2I const& pos, TileLayer layer, ModId mod, GetTileFunction& getTile);
template <typename GetTileFunction>
pair<bool, bool> validateTileModification(EntityMapPtr const& entityMap, Vec2I const& pos, TileModification const& modification, bool allowEntityOverlap, GetTileFunction& getTile);
// Split modification list into good and bad // Split modification list into good and bad
template <typename GetTileFunction> template <typename GetTileFunction>
pair<TileModificationList, TileModificationList> splitTileModifications(EntityMapPtr const& entityMap, TileModificationList const& modificationList, pair<TileModificationList, TileModificationList> splitTileModifications(EntityMapPtr const& entityMap, TileModificationList const& modificationList,
bool allowEntityOverlap, function<bool(Vec2I pos, GetTileFunction& getTile, TileModification modification)> extraCheck = {}); bool allowEntityOverlap, GetTileFunction& getTile, function<bool(Vec2I pos, 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);
@ -314,21 +316,11 @@ namespace WorldImpl {
} }
template <typename GetTileFunction> template <typename GetTileFunction>
pair<TileModificationList, TileModificationList> splitTileModifications(EntityMapPtr const& entityMap, pair<bool, bool> validateTileModification(EntityMapPtr const& entityMap, Vec2I const& pos, TileModification const& modification, bool allowEntityOverlap, GetTileFunction& getTile) {
TileModificationList const& modificationList, bool allowEntityOverlap, GetTileFunction& getTile, function<bool(Vec2I pos, TileModification modification)> extraCheck) {
TileModificationList success;
TileModificationList unknown;
TileModificationList failures;
for (auto const& pair : modificationList) {
Vec2I pos;
TileModification modification;
std::tie(pos, modification) = pair;
bool good = false; bool good = false;
bool perhaps = false; bool perhaps = false;
if (extraCheck && !extraCheck(pos, modification)) {
good = false; if (auto placeMaterial = modification.ptr<PlaceMaterial>()) {
} else if (auto placeMaterial = modification.ptr<PlaceMaterial>()) {
perhaps = WorldImpl::perhapsCanPlaceMaterial(entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, getTile); perhaps = WorldImpl::perhapsCanPlaceMaterial(entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, getTile);
if (perhaps) if (perhaps)
good = WorldImpl::canPlaceMaterial(entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, getTile); good = WorldImpl::canPlaceMaterial(entityMap, pos, placeMaterial->layer, placeMaterial->material, allowEntityOverlap, getTile);
@ -342,6 +334,21 @@ namespace WorldImpl {
good = false; good = false;
} }
return { good, perhaps };
}
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;
for (auto const& pair : modificationList) {
bool good = false, perhaps = false;
if (!extraCheck || extraCheck(pair.first, pair.second))
std::tie(good, perhaps) = validateTileModification(entityMap, pair.first, pair.second, allowEntityOverlap, getTile);
if (good) if (good)
success.append(pair); success.append(pair);
else if (perhaps) else if (perhaps)