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; return res;
} }
template <typename Value> template <typename SetType>
Set<Value> jsonToSet(Json const& v) { SetType jsonToSet(Json const& v) {
return jsonToSet<Value>(v, construct<Value>()); return jsonToSet<SetType>(v, construct<Value>());
} }
template <typename Value, typename Converter> template <typename SetType, typename Converter>
Set<Value> jsonToSet(Json const& v, Converter&& valueConvert) { SetType jsonToSet(Json const& v, Converter&& valueConvert) {
if (v.type() != Json::Type::Array) if (v.type() != Json::Type::Array)
throw JsonException("Json type is not an array in jsonToSet"); throw JsonException("Json type is not an array in jsonToSet");
Set<Value> res; SetType res;
for (auto const& entry : v.iterateArray()) for (auto const& entry : v.iterateArray())
res.add(valueConvert(entry)); res.add(valueConvert(entry));
return res; return res;
} }
template <typename Value> template <typename SetType>
Json jsonFromSet(Set<Value> const& Set) { Json jsonFromSet(SetType const& Set) {
return jsonFromSet<Value>(Set, construct<Json>()); return jsonFromSet<SetType>(Set, construct<Json>());
} }
template <typename Value, typename Converter> template <typename SetType, typename Converter>
Json jsonFromSet(Set<Value> const& Set, Converter&& valueConvert) { Json jsonFromSet(SetType const& Set, Converter&& valueConvert) {
JsonArray res; JsonArray res;
for (auto entry : Set) for (auto& entry : Set)
res.push_back(valueConvert(entry)); res.push_back(valueConvert(entry));
return res; 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>"; return "Not enough arguments to /settileprotection. Use /settileprotection <dungeonId> <protected>";
try { try {
DungeonId dungeonId = lexicalCast<DungeonId>(arguments.at(0)); bool isProtected = lexicalCast<bool>(arguments.takeLast());
bool isProtected = lexicalCast<bool>(arguments.at(1)); List<DungeonId> dungeonIds;
for (auto& banana : arguments) {
bool done = m_universe->executeForClient(connectionId, [dungeonId, isProtected](WorldServer* world, PlayerPtr const&) { auto slices = banana.split("..");
world->setTileProtection(dungeonId, isProtected); auto it = slices.begin();
}); DungeonId previous = 0;
while (it != slices.end()) {
return done ? "" : "Failed to set block protection."; 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&) { } 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 { Json EffectEmitter::toJson() const {
return JsonObject{{"activeSources", return JsonObject{{"activeSources",
jsonFromSet<pair<String, String>>(m_activeSources.get(), jsonFromSet<Set<pair<String, String>>>(m_activeSources.get(),
[](pair<String, String> const& entry) { [](pair<String, String> const& entry) {
return JsonObject{{"position", entry.first}, {"source", entry.second}}; return JsonObject{{"position", entry.first}, {"source", entry.second}};
})}}; })}};
} }
void EffectEmitter::fromJson(Json const& diskStore) { 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) { [](Json const& v) {
return pair<String, String>{v.getString("position"), v.getString("source")}; return pair<String, String>{v.getString("position"), v.getString("source")};
})); }));

View File

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

View File

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

View File

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

View File

@ -1190,10 +1190,14 @@ bool WorldServer::isTileProtected(Vec2I const& pos) const {
if (!m_tileProtectionEnabled) if (!m_tileProtectionEnabled)
return false; return false;
auto tile = m_tileArray->tile(pos); auto const& tile = m_tileArray->tile(pos);
return m_protectedDungeonIds.contains(tile.dungeonId); return m_protectedDungeonIds.contains(tile.dungeonId);
} }
bool WorldServer::getTileProtection(DungeonId dungeonId) const {
return m_protectedDungeonIds.contains(dungeonId);
}
void WorldServer::setTileProtection(DungeonId dungeonId, bool isProtected) { void WorldServer::setTileProtection(DungeonId dungeonId, bool isProtected) {
bool updated = false; bool updated = false;
if (isProtected) { if (isProtected) {
@ -1205,9 +1209,28 @@ void WorldServer::setTileProtection(DungeonId dungeonId, bool isProtected) {
if (updated) { if (updated) {
for (auto const& pair : m_clientInfo) for (auto const& pair : m_clientInfo)
pair.second->outgoingPackets.append(make_shared<UpdateTileProtectionPacket>(dungeonId, isProtected)); pair.second->outgoingPackets.append(make_shared<UpdateTileProtectionPacket>(dungeonId, isProtected));
Logger::info("Protected dungeonIds for world set to {}", m_protectedDungeonIds);
} }
}
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) { void WorldServer::setTileProtectionEnabled(bool enabled) {
@ -2345,7 +2368,7 @@ void WorldServer::readMetadata() {
m_adjustPlayerStart = metadata.getBool("adjustPlayerStart"); m_adjustPlayerStart = metadata.getBool("adjustPlayerStart");
m_worldTemplate = make_shared<WorldTemplate>(metadata.get("worldTemplate")); m_worldTemplate = make_shared<WorldTemplate>(metadata.get("worldTemplate"));
m_centralStructure = WorldStructure(metadata.get("centralStructure")); 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_worldProperties = metadata.getObject("worldProperties");
m_spawner.setActive(metadata.getBool("spawningEnabled")); 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; RpcPromise<Json> sendEntityMessage(Variant<EntityId, String> const& entity, String const& message, JsonArray const& args = {}) override;
bool isTileProtected(Vec2I const& pos) const override; bool isTileProtected(Vec2I const& pos) const override;
bool getTileProtection(DungeonId dungeonId) const;
void setTileProtection(DungeonId dungeonId, bool isProtected); 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 // used to globally, temporarily disable protection for certain operations
void setTileProtectionEnabled(bool enabled); void setTileProtectionEnabled(bool enabled);
@ -396,7 +399,7 @@ private:
bool m_generatingDungeon; bool m_generatingDungeon;
HashMap<DungeonId, float> m_dungeonIdGravity; HashMap<DungeonId, float> m_dungeonIdGravity;
HashMap<DungeonId, bool> m_dungeonIdBreathable; HashMap<DungeonId, bool> m_dungeonIdBreathable;
Set<DungeonId> m_protectedDungeonIds; StableHashSet<DungeonId> m_protectedDungeonIds;
bool m_tileProtectionEnabled; bool m_tileProtectionEnabled;
HashMap<Uuid, pair<ConnectionId, MVariant<ConnectionId, RpcPromiseKeeper<Json>>>> m_entityMessageResponses; HashMap<Uuid, pair<ConnectionId, MVariant<ConnectionId, RpcPromiseKeeper<Json>>>> m_entityMessageResponses;