protected dungeon ID optimization + /settileprotection improvements

This commit is contained in:
Kae 2024-09-14 15:59:01 +10:00
parent 9dbc4daacc
commit 8155ec6715
8 changed files with 73 additions and 30 deletions

View File

@ -211,32 +211,32 @@ Json jsonFromList(List<Value> const& list, Converter&& valueConvert) {
return res;
}
template <typename Value>
Set<Value> jsonToSet(Json const& v) {
return jsonToSet<Value>(v, construct<Value>());
template <typename SetType>
SetType jsonToSet(Json const& v) {
return jsonToSet<SetType>(v, construct<Value>());
}
template <typename Value, typename Converter>
Set<Value> jsonToSet(Json const& v, Converter&& valueConvert) {
template <typename SetType, typename Converter>
SetType jsonToSet(Json const& v, Converter&& valueConvert) {
if (v.type() != Json::Type::Array)
throw JsonException("Json type is not an array in jsonToSet");
Set<Value> res;
SetType res;
for (auto const& entry : v.iterateArray())
res.add(valueConvert(entry));
return res;
}
template <typename Value>
Json jsonFromSet(Set<Value> const& Set) {
return jsonFromSet<Value>(Set, construct<Json>());
template <typename SetType>
Json jsonFromSet(SetType const& Set) {
return jsonFromSet<SetType>(Set, construct<Json>());
}
template <typename Value, typename Converter>
Json jsonFromSet(Set<Value> const& Set, Converter&& valueConvert) {
template <typename SetType, typename Converter>
Json jsonFromSet(SetType const& Set, Converter&& valueConvert) {
JsonArray res;
for (auto entry : Set)
for (auto& entry : Set)
res.push_back(valueConvert(entry));
return res;

View File

@ -262,16 +262,33 @@ String CommandProcessor::setTileProtection(ConnectionId connectionId, String con
return "Not enough arguments to /settileprotection. Use /settileprotection <dungeonId> <protected>";
try {
DungeonId dungeonId = lexicalCast<DungeonId>(arguments.at(0));
bool isProtected = lexicalCast<bool>(arguments.at(1));
bool done = m_universe->executeForClient(connectionId, [dungeonId, isProtected](WorldServer* world, PlayerPtr const&) {
world->setTileProtection(dungeonId, isProtected);
});
return done ? "" : "Failed to set block protection.";
bool isProtected = lexicalCast<bool>(arguments.takeLast());
List<DungeonId> dungeonIds;
for (auto& banana : arguments) {
auto slices = banana.split("..");
auto it = slices.begin();
DungeonId previous = 0;
while (it != slices.end()) {
DungeonId current = lexicalCast<DungeonId>(*it);
dungeonIds.append(current);
if (it++ != slices.begin() && previous != current) {
if (current < previous) swap(previous, current);
for (DungeonId id = previous + 1; id != current; ++id)
dungeonIds.append(id);
}
previous = current;
}
}
size_t changed = 0;
if (!m_universe->executeForClient(connectionId, [&](WorldServer* world, PlayerPtr const&) {
changed = world->setTileProtection(dungeonIds, isProtected);
})) {
return "Invalid client state";
}
String output = strf("{} {} dungeon IDs", isProtected ? "Protected" : "Unprotected", changed);
return changed < dungeonIds.size() ? strf("{} ({} unchanged)", output, dungeonIds.size() - changed) : output;
} catch (BadLexicalCast const&) {
return strf("Could not parse /settileprotection parameters. Use /settileprotection <dungeonId> <protected>", argumentString);
return strf("Could not parse /settileprotection parameters. Use /settileprotection <dungeonId...> <protected>", argumentString);
}
}

View File

@ -94,14 +94,14 @@ void EffectEmitter::render(RenderCallback* renderCallback) {
Json EffectEmitter::toJson() const {
return JsonObject{{"activeSources",
jsonFromSet<pair<String, String>>(m_activeSources.get(),
jsonFromSet<Set<pair<String, String>>>(m_activeSources.get(),
[](pair<String, String> const& entry) {
return JsonObject{{"position", entry.first}, {"source", entry.second}};
})}};
}
void EffectEmitter::fromJson(Json const& diskStore) {
m_activeSources.set(jsonToSet<pair<String, String>>(diskStore.get("activeSources"),
m_activeSources.set(jsonToSet<Set<pair<String, String>>>(diskStore.get("activeSources"),
[](Json const& v) {
return pair<String, String>{v.getString("position"), v.getString("source")};
}));

View File

@ -415,7 +415,7 @@ struct WorldStartPacket : PacketBase<PacketType::WorldStart> {
bool respawnInWorld;
HashMap<DungeonId, float> dungeonIdGravity;
HashMap<DungeonId, bool> dungeonIdBreathable;
Set<DungeonId> protectedDungeonIds;
StableHashSet<DungeonId> protectedDungeonIds;
Json worldProperties;
ConnectionId clientId;
bool localInterpolationMode;

View File

@ -1435,7 +1435,7 @@ bool WorldClient::isTileProtected(Vec2I const& pos) const {
if (!inWorld())
return true;
auto tile = m_tileArray->tile(pos);
auto const& tile = m_tileArray->tile(pos);
return m_protectedDungeonIds.contains(tile.dungeonId);
}

View File

@ -367,7 +367,7 @@ private:
HashMap<DungeonId, float> m_dungeonIdGravity;
HashMap<DungeonId, bool> m_dungeonIdBreathable;
Set<DungeonId> m_protectedDungeonIds;
StableHashSet<DungeonId> m_protectedDungeonIds;
HashMap<String, List<RpcPromiseKeeper<Vec2F>>> m_findUniqueEntityResponses;
HashMap<Uuid, RpcPromiseKeeper<Json>> m_entityMessageResponses;

View File

@ -1190,10 +1190,14 @@ bool WorldServer::isTileProtected(Vec2I const& pos) const {
if (!m_tileProtectionEnabled)
return false;
auto tile = m_tileArray->tile(pos);
auto const& tile = m_tileArray->tile(pos);
return m_protectedDungeonIds.contains(tile.dungeonId);
}
bool WorldServer::getTileProtection(DungeonId dungeonId) const {
return m_protectedDungeonIds.contains(dungeonId);
}
void WorldServer::setTileProtection(DungeonId dungeonId, bool isProtected) {
bool updated = false;
if (isProtected) {
@ -1205,10 +1209,29 @@ void WorldServer::setTileProtection(DungeonId dungeonId, bool isProtected) {
if (updated) {
for (auto const& pair : m_clientInfo)
pair.second->outgoingPackets.append(make_shared<UpdateTileProtectionPacket>(dungeonId, isProtected));
}
Logger::info("Protected dungeonIds for world set to {}", m_protectedDungeonIds);
}
}
size_t WorldServer::setTileProtection(List<DungeonId> const& dungeonIds, bool isProtected) {
List<PacketPtr> updates;
updates.reserve(dungeonIds.size());
for (auto const& dungeonId : dungeonIds)
if (isProtected ? m_protectedDungeonIds.add(dungeonId) : m_protectedDungeonIds.remove(dungeonId))
updates.append(make_shared<UpdateTileProtectionPacket>(dungeonId, isProtected));
if (updates.empty())
return 0;
for (auto const& pair : m_clientInfo)
pair.second->outgoingPackets.appendAll(updates);
auto newDungeonIds = m_protectedDungeonIds.values();
sort(newDungeonIds);
Logger::info("Protected dungeonIds for world set to {}", newDungeonIds);
return updates.size();
}
void WorldServer::setTileProtectionEnabled(bool enabled) {
m_tileProtectionEnabled = enabled;
@ -2345,7 +2368,7 @@ void WorldServer::readMetadata() {
m_adjustPlayerStart = metadata.getBool("adjustPlayerStart");
m_worldTemplate = make_shared<WorldTemplate>(metadata.get("worldTemplate"));
m_centralStructure = WorldStructure(metadata.get("centralStructure"));
m_protectedDungeonIds = jsonToSet<DungeonId>(metadata.get("protectedDungeonIds"), mem_fn(&Json::toUInt));
m_protectedDungeonIds = jsonToSet<StableHashSet<DungeonId>>(metadata.get("protectedDungeonIds"), mem_fn(&Json::toUInt));
m_worldProperties = metadata.getObject("worldProperties");
m_spawner.setActive(metadata.getBool("spawningEnabled"));

View File

@ -180,7 +180,10 @@ public:
RpcPromise<Json> sendEntityMessage(Variant<EntityId, String> const& entity, String const& message, JsonArray const& args = {}) override;
bool isTileProtected(Vec2I const& pos) const override;
bool getTileProtection(DungeonId dungeonId) const;
void setTileProtection(DungeonId dungeonId, bool isProtected);
// sets a provided list of DungeonIds all at once and returns how many were changed
size_t setTileProtection(List<DungeonId> const& dungeonIds, bool isProtected);
// used to globally, temporarily disable protection for certain operations
void setTileProtectionEnabled(bool enabled);
@ -396,7 +399,7 @@ private:
bool m_generatingDungeon;
HashMap<DungeonId, float> m_dungeonIdGravity;
HashMap<DungeonId, bool> m_dungeonIdBreathable;
Set<DungeonId> m_protectedDungeonIds;
StableHashSet<DungeonId> m_protectedDungeonIds;
bool m_tileProtectionEnabled;
HashMap<Uuid, pair<ConnectionId, MVariant<ConnectionId, RpcPromiseKeeper<Json>>>> m_entityMessageResponses;