From 8155ec671581b051feca4187d5067ddbd149b387 Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Sat, 14 Sep 2024 15:59:01 +1000 Subject: [PATCH] protected dungeon ID optimization + /settileprotection improvements --- source/core/StarJsonExtra.hpp | 24 +++++++++---------- source/game/StarCommandProcessor.cpp | 35 +++++++++++++++++++++------- source/game/StarEffectEmitter.cpp | 4 ++-- source/game/StarNetPackets.hpp | 2 +- source/game/StarWorldClient.cpp | 2 +- source/game/StarWorldClient.hpp | 2 +- source/game/StarWorldServer.cpp | 29 ++++++++++++++++++++--- source/game/StarWorldServer.hpp | 5 +++- 8 files changed, 73 insertions(+), 30 deletions(-) diff --git a/source/core/StarJsonExtra.hpp b/source/core/StarJsonExtra.hpp index 2a0ca28..ca5b510 100644 --- a/source/core/StarJsonExtra.hpp +++ b/source/core/StarJsonExtra.hpp @@ -211,32 +211,32 @@ Json jsonFromList(List const& list, Converter&& valueConvert) { return res; } -template -Set jsonToSet(Json const& v) { - return jsonToSet(v, construct()); +template +SetType jsonToSet(Json const& v) { + return jsonToSet(v, construct()); } -template -Set jsonToSet(Json const& v, Converter&& valueConvert) { +template +SetType jsonToSet(Json const& v, Converter&& valueConvert) { if (v.type() != Json::Type::Array) throw JsonException("Json type is not an array in jsonToSet"); - Set res; + SetType res; for (auto const& entry : v.iterateArray()) res.add(valueConvert(entry)); return res; } -template -Json jsonFromSet(Set const& Set) { - return jsonFromSet(Set, construct()); +template +Json jsonFromSet(SetType const& Set) { + return jsonFromSet(Set, construct()); } -template -Json jsonFromSet(Set const& Set, Converter&& valueConvert) { +template +Json jsonFromSet(SetType const& Set, Converter&& valueConvert) { JsonArray res; - for (auto entry : Set) + for (auto& entry : Set) res.push_back(valueConvert(entry)); return res; diff --git a/source/game/StarCommandProcessor.cpp b/source/game/StarCommandProcessor.cpp index b5beb20..7b6915e 100644 --- a/source/game/StarCommandProcessor.cpp +++ b/source/game/StarCommandProcessor.cpp @@ -262,16 +262,33 @@ String CommandProcessor::setTileProtection(ConnectionId connectionId, String con return "Not enough arguments to /settileprotection. Use /settileprotection "; try { - DungeonId dungeonId = lexicalCast(arguments.at(0)); - bool isProtected = lexicalCast(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(arguments.takeLast()); + List dungeonIds; + for (auto& banana : arguments) { + auto slices = banana.split(".."); + auto it = slices.begin(); + DungeonId previous = 0; + while (it != slices.end()) { + DungeonId current = lexicalCast(*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 ", argumentString); + return strf("Could not parse /settileprotection parameters. Use /settileprotection ", argumentString); } } diff --git a/source/game/StarEffectEmitter.cpp b/source/game/StarEffectEmitter.cpp index ddb0355..28e2986 100644 --- a/source/game/StarEffectEmitter.cpp +++ b/source/game/StarEffectEmitter.cpp @@ -94,14 +94,14 @@ void EffectEmitter::render(RenderCallback* renderCallback) { Json EffectEmitter::toJson() const { return JsonObject{{"activeSources", - jsonFromSet>(m_activeSources.get(), + jsonFromSet>>(m_activeSources.get(), [](pair const& entry) { return JsonObject{{"position", entry.first}, {"source", entry.second}}; })}}; } void EffectEmitter::fromJson(Json const& diskStore) { - m_activeSources.set(jsonToSet>(diskStore.get("activeSources"), + m_activeSources.set(jsonToSet>>(diskStore.get("activeSources"), [](Json const& v) { return pair{v.getString("position"), v.getString("source")}; })); diff --git a/source/game/StarNetPackets.hpp b/source/game/StarNetPackets.hpp index 87a01cf..fc43212 100644 --- a/source/game/StarNetPackets.hpp +++ b/source/game/StarNetPackets.hpp @@ -415,7 +415,7 @@ struct WorldStartPacket : PacketBase { bool respawnInWorld; HashMap dungeonIdGravity; HashMap dungeonIdBreathable; - Set protectedDungeonIds; + StableHashSet protectedDungeonIds; Json worldProperties; ConnectionId clientId; bool localInterpolationMode; diff --git a/source/game/StarWorldClient.cpp b/source/game/StarWorldClient.cpp index ab5f9bf..d5c6bd7 100644 --- a/source/game/StarWorldClient.cpp +++ b/source/game/StarWorldClient.cpp @@ -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); } diff --git a/source/game/StarWorldClient.hpp b/source/game/StarWorldClient.hpp index 1616b10..2957661 100644 --- a/source/game/StarWorldClient.hpp +++ b/source/game/StarWorldClient.hpp @@ -367,7 +367,7 @@ private: HashMap m_dungeonIdGravity; HashMap m_dungeonIdBreathable; - Set m_protectedDungeonIds; + StableHashSet m_protectedDungeonIds; HashMap>> m_findUniqueEntityResponses; HashMap> m_entityMessageResponses; diff --git a/source/game/StarWorldServer.cpp b/source/game/StarWorldServer.cpp index e1e447d..c313771 100644 --- a/source/game/StarWorldServer.cpp +++ b/source/game/StarWorldServer.cpp @@ -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,9 +1209,28 @@ void WorldServer::setTileProtection(DungeonId dungeonId, bool isProtected) { if (updated) { for (auto const& pair : m_clientInfo) pair.second->outgoingPackets.append(make_shared(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 const& dungeonIds, bool isProtected) { + List updates; + updates.reserve(dungeonIds.size()); + for (auto const& dungeonId : dungeonIds) + if (isProtected ? m_protectedDungeonIds.add(dungeonId) : m_protectedDungeonIds.remove(dungeonId)) + updates.append(make_shared(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) { @@ -2345,7 +2368,7 @@ void WorldServer::readMetadata() { m_adjustPlayerStart = metadata.getBool("adjustPlayerStart"); m_worldTemplate = make_shared(metadata.get("worldTemplate")); m_centralStructure = WorldStructure(metadata.get("centralStructure")); - m_protectedDungeonIds = jsonToSet(metadata.get("protectedDungeonIds"), mem_fn(&Json::toUInt)); + m_protectedDungeonIds = jsonToSet>(metadata.get("protectedDungeonIds"), mem_fn(&Json::toUInt)); m_worldProperties = metadata.getObject("worldProperties"); m_spawner.setActive(metadata.getBool("spawningEnabled")); diff --git a/source/game/StarWorldServer.hpp b/source/game/StarWorldServer.hpp index ea650ac..21c8e9f 100644 --- a/source/game/StarWorldServer.hpp +++ b/source/game/StarWorldServer.hpp @@ -180,7 +180,10 @@ public: RpcPromise sendEntityMessage(Variant 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 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 m_dungeonIdGravity; HashMap m_dungeonIdBreathable; - Set m_protectedDungeonIds; + StableHashSet m_protectedDungeonIds; bool m_tileProtectionEnabled; HashMap>>> m_entityMessageResponses;