From 5ca42599ef52236274938dba9bd6e113611cb734 Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Tue, 19 Mar 2024 13:35:55 +1100 Subject: [PATCH] make timescale a server command and add a tickrate command --- assets/opensb/itemdrop.config.patch | 2 +- source/core/StarTime.cpp | 2 +- .../frontend/StarClientCommandProcessor.cpp | 21 +--- .../frontend/StarClientCommandProcessor.hpp | 1 - source/game/StarCommandProcessor.cpp | 99 ++++++++++--------- source/game/StarCommandProcessor.hpp | 2 + source/game/StarNetPackets.cpp | 17 +++- source/game/StarNetPackets.hpp | 6 +- source/game/StarSystemWorldServerThread.cpp | 2 +- source/game/StarUniverseClient.cpp | 2 + source/game/StarUniverseServer.cpp | 22 ++++- source/game/StarUniverseServer.hpp | 2 + source/game/StarWorldServerThread.cpp | 3 +- source/server/main.cpp | 2 +- 14 files changed, 103 insertions(+), 80 deletions(-) diff --git a/assets/opensb/itemdrop.config.patch b/assets/opensb/itemdrop.config.patch index cd3d445..643e90e 100644 --- a/assets/opensb/itemdrop.config.patch +++ b/assets/opensb/itemdrop.config.patch @@ -1,7 +1,7 @@ { "directives" : "", "pickupDistance" : 0.5, - "afterTakenLife" : 2.5, + "afterTakenLife" : 0.75, "overheadTime" : 0.5, "overheadApproach" : 2000, "overheadRandomizedDistance" : 0.25 diff --git a/source/core/StarTime.cpp b/source/core/StarTime.cpp index f64b850..96cbdde 100644 --- a/source/core/StarTime.cpp +++ b/source/core/StarTime.cpp @@ -147,7 +147,7 @@ void Clock::setMilliseconds(int64_t millis) { void Clock::adjustTime(double timeAdjustment) { RecursiveMutexLocker locker(m_mutex); - setTime(time() + timeAdjustment); + setTime(max(0.0, time() + timeAdjustment)); } void Clock::adjustMilliseconds(int64_t millisAdjustment) { diff --git a/source/frontend/StarClientCommandProcessor.cpp b/source/frontend/StarClientCommandProcessor.cpp index 8434043..52882d9 100644 --- a/source/frontend/StarClientCommandProcessor.cpp +++ b/source/frontend/StarClientCommandProcessor.cpp @@ -51,8 +51,7 @@ ClientCommandProcessor::ClientCommandProcessor(UniverseClientPtr universeClient, {"maketechavailable", bind(&ClientCommandProcessor::makeTechAvailable, this, _1)}, {"enabletech", bind(&ClientCommandProcessor::enableTech, this, _1)}, {"upgradeship", bind(&ClientCommandProcessor::upgradeShip, this, _1)}, - {"swap", bind(&ClientCommandProcessor::swap, this, _1)}, - {"timescale", bind(&ClientCommandProcessor::timeScale, this, _1)} + {"swap", bind(&ClientCommandProcessor::swap, this, _1)} }; } @@ -381,7 +380,7 @@ String ClientCommandProcessor::makeTechAvailable(String const& argumentsString) return "You must be an admin to use this command."; if (arguments.size() == 0) - return "Not enouch arguments to /maketechavailable"; + return "Not enough arguments to /maketechavailable"; m_universeClient->mainPlayer()->techs()->makeAvailable(arguments.at(0)); return strf("Added {} to player's visible techs", arguments.at(0)); @@ -393,7 +392,7 @@ String ClientCommandProcessor::enableTech(String const& argumentsString) { return "You must be an admin to use this command."; if (arguments.size() == 0) - return "Not enouch arguments to /enabletech"; + return "Not enough arguments to /enabletech"; m_universeClient->mainPlayer()->techs()->makeAvailable(arguments.at(0)); m_universeClient->mainPlayer()->techs()->enable(arguments.at(0)); @@ -406,7 +405,7 @@ String ClientCommandProcessor::upgradeShip(String const& argumentsString) { return "You must be an admin to use this command."; if (arguments.size() == 0) - return "Not enouch arguments to /upgradeship"; + return "Not enough arguments to /upgradeship"; auto shipUpgrades = Json::parseJson(arguments.at(0)); m_universeClient->rpcInterface()->invokeRemote("ship.applyShipUpgrades", shipUpgrades); @@ -417,7 +416,7 @@ String ClientCommandProcessor::swap(String const& argumentsString) { auto arguments = m_parser.tokenizeToStringList(argumentsString); if (arguments.size() == 0) - return "Not enouch arguments to /swap"; + return "Not enough arguments to /swap"; if (m_universeClient->switchPlayer(arguments[0])) return "Successfully swapped player"; @@ -425,14 +424,4 @@ String ClientCommandProcessor::swap(String const& argumentsString) { return "Failed to swap player"; } -String ClientCommandProcessor::timeScale(String const& argumentsString) { - auto arguments = m_parser.tokenizeToStringList(argumentsString); - - if (arguments.size() == 0) - return "Not enouch arguments to /timescale"; - - GlobalTimescale = clamp(lexicalCast(arguments[0]), 0.001f, 256.0f); - return strf("Set application timescale to {:6.6f}x", GlobalTimescale); -} - } diff --git a/source/frontend/StarClientCommandProcessor.hpp b/source/frontend/StarClientCommandProcessor.hpp index 50e5a06..b06f3d5 100644 --- a/source/frontend/StarClientCommandProcessor.hpp +++ b/source/frontend/StarClientCommandProcessor.hpp @@ -58,7 +58,6 @@ private: String enableTech(String const& argumentsString); String upgradeShip(String const& argumentsString); String swap(String const& argumentsString); - String timeScale(String const& argumentsString); UniverseClientPtr m_universeClient; CinematicPtr m_cinematicOverlay; diff --git a/source/game/StarCommandProcessor.cpp b/source/game/StarCommandProcessor.cpp index 0d9d4e9..ee0b4fb 100644 --- a/source/game/StarCommandProcessor.cpp +++ b/source/game/StarCommandProcessor.cpp @@ -193,22 +193,56 @@ String CommandProcessor::warpRandom(ConnectionId connectionId, String const& typ return strf("warping to {}", *target); } -String CommandProcessor::timewarp(ConnectionId connectionId, String const& argumentString) { +String CommandProcessor::timewarp(ConnectionId connectionId, String const& argumentsString) { if (auto errorMsg = adminCheck(connectionId, "do the time warp again")) return *errorMsg; + auto arguments = m_parser.tokenizeToStringList(argumentsString); + if (arguments.empty()) + return "Not enough arguments to /timewarp"; + try { - auto time = lexicalCast(argumentString); - if (time < 0) + auto time = lexicalCast(arguments.at(0)); + if (time == 0.0) + return "You suck at time travel."; + else if (time < 0.0 && (arguments.size() < 2 || arguments[1] != "please")) return "Great Scott! We can't go back in time!"; m_universe->universeClock()->adjustTime(time); - return "It's just a jump to the left..."; + return strf("It's just a jump to the {}...", time > 0.0 ? "left" : "right"); } catch (BadLexicalCast const&) { - return strf("Could not parse the argument {} as a time adjustment", argumentString); + return strf("Could not parse the argument {} as a time adjustment", arguments[0]); } } +String CommandProcessor::timescale(ConnectionId connectionId, String const& argumentsString) { + if (auto errorMsg = adminCheck(connectionId, "mess with time")) + return *errorMsg; + + auto arguments = m_parser.tokenizeToStringList(argumentsString); + + if (arguments.empty()) + return "Not enough arguments to /timescale"; + + float timescale = clamp(lexicalCast(arguments[0]), 0.001f, 32.0f); + m_universe->setTimescale(timescale); + return strf("Set timescale to {:6.6f}x", timescale); +} + +String CommandProcessor::tickrate(ConnectionId connectionId, String const& argumentsString) { + if (auto errorMsg = adminCheck(connectionId, "change the tick rate")) + return *errorMsg; + + auto arguments = m_parser.tokenizeToStringList(argumentsString); + + if (arguments.empty()) + return "Not enough arguments to /tickrate"; + + unsigned tickRate = clamp(lexicalCast(arguments[0]), 5, 500); + m_universe->setTickRate(tickRate); + return strf("Set tick rate to {}Hz", tickRate); +} + String CommandProcessor::setTileProtection(ConnectionId connectionId, String const& argumentString) { if (auto errorMsg = adminCheck(connectionId, "modify world properties")) { return *errorMsg; @@ -272,7 +306,7 @@ String CommandProcessor::spawnItem(ConnectionId connectionId, String const& argu auto arguments = m_parser.tokenizeToStringList(argumentString); - if (arguments.size() == 0) + if (arguments.empty()) return "Not enough arguments to /spawnitem"; try { @@ -321,7 +355,7 @@ String CommandProcessor::spawnTreasure(ConnectionId connectionId, String const& auto arguments = m_parser.tokenizeToStringList(argumentString); - if (arguments.size() == 0) + if (arguments.empty()) return "Not enough arguments to /spawntreasure"; try { @@ -532,7 +566,7 @@ String CommandProcessor::kick(ConnectionId connectionId, String const& argumentS auto arguments = m_parser.tokenizeToStringList(argumentString); - if (arguments.size() == 0) + if (arguments.empty()) return "No player specified"; auto toKick = playerCidFromCommand(arguments[0], m_universe); @@ -557,7 +591,7 @@ String CommandProcessor::ban(ConnectionId connectionId, String const& argumentSt auto arguments = m_parser.tokenizeToStringList(argumentString); - if (arguments.size() == 0) + if (arguments.empty()) return "No player specified"; auto toKick = playerCidFromCommand(arguments[0], m_universe); @@ -605,7 +639,7 @@ String CommandProcessor::unbanIp(ConnectionId connectionId, String const& argume auto arguments = m_parser.tokenizeToStringList(argumentString); - if (arguments.size() == 0) + if (arguments.empty()) return "No IP specified"; bool success = m_universe->unbanIp(arguments[0]); @@ -622,7 +656,7 @@ String CommandProcessor::unbanUuid(ConnectionId connectionId, String const& argu auto arguments = m_parser.tokenizeToStringList(argumentString); - if (arguments.size() == 0) + if (arguments.empty()) return "No UUID specified"; bool success = m_universe->unbanUuid(arguments[0]); @@ -868,121 +902,88 @@ Maybe CommandProcessor::playerCidFromCommand(String const& player, return universe->findNick(player); } +//wow, wtf. TODO: replace with hashmap String CommandProcessor::handleCommand(ConnectionId connectionId, String const& command, String const& argumentString) { if (command == "admin") { return admin(connectionId, argumentString); - } else if (command == "timewarp") { return timewarp(connectionId, argumentString); - + } else if (command == "timescale") { + return timescale(connectionId, argumentString); + } else if (command == "tickrate") { + return tickrate(connectionId, argumentString); } else if (command == "settileprotection") { return setTileProtection(connectionId, argumentString); - } else if (command == "setdungeonid") { return setDungeonId(connectionId, argumentString); - } else if (command == "setspawnpoint") { return setPlayerStart(connectionId, argumentString); - } else if (command == "spawnitem") { return spawnItem(connectionId, argumentString); - } else if (command == "spawntreasure") { return spawnTreasure(connectionId, argumentString); - } else if (command == "spawnmonster") { return spawnMonster(connectionId, argumentString); - } else if (command == "spawnnpc") { return spawnNpc(connectionId, argumentString); - } else if (command == "spawnstagehand") { return spawnStagehand(connectionId, argumentString); - } else if (command == "clearstagehand") { return clearStagehand(connectionId, argumentString); - } else if (command == "spawnvehicle") { return spawnVehicle(connectionId, argumentString); - } else if (command == "spawnliquid") { return spawnLiquid(connectionId, argumentString); - } else if (command == "pvp") { return pvp(connectionId, argumentString); - } else if (command == "serverwhoami") { return whoami(connectionId, argumentString); - } else if (command == "kick") { return kick(connectionId, argumentString); - } else if (command == "ban") { return ban(connectionId, argumentString); - } else if (command == "unbanip") { return unbanIp(connectionId, argumentString); - } else if (command == "unbanuuid") { return unbanUuid(connectionId, argumentString); - } else if (command == "list") { return list(connectionId, argumentString); - } else if (command == "help") { return help(connectionId, argumentString); - } else if (command == "warp") { return warp(connectionId, argumentString); - } else if (command == "warprandom") { return warpRandom(connectionId, argumentString); - } else if (command == "whereami") { return clientCoordinate(connectionId, argumentString); - } else if (command == "whereis") { return clientCoordinate(connectionId, argumentString); - } else if (command == "serverreload") { return serverReload(connectionId, argumentString); - } else if (command == "eval") { return eval(connectionId, argumentString); - } else if (command == "entityeval") { return entityEval(connectionId, argumentString); - } else if (command == "enablespawning") { return enableSpawning(connectionId, argumentString); - } else if (command == "disablespawning") { return disableSpawning(connectionId, argumentString); - } else if (command == "placedungeon") { return placeDungeon(connectionId, argumentString); - } else if (command == "setuniverseflag") { return setUniverseFlag(connectionId, argumentString); - } else if (command == "resetuniverseflags") { return resetUniverseFlags(connectionId, argumentString); - } else if (command == "addbiomeregion") { return addBiomeRegion(connectionId, argumentString); - } else if (command == "expandbiomeregion") { return expandBiomeRegion(connectionId, argumentString); - } else if (command == "updateplanettype") { return updatePlanetType(connectionId, argumentString); - } else if (command == "setenvironmentbiome") { return setEnvironmentBiome(connectionId, argumentString); - } else if (auto res = m_scriptComponent.invoke("command", command, connectionId, jsonFromStringList(m_parser.tokenizeToStringList(argumentString)))) { return toString(*res); - } else { return strf("No such command {}", command); } diff --git a/source/game/StarCommandProcessor.hpp b/source/game/StarCommandProcessor.hpp index 6d9bd2d..a62cfc9 100644 --- a/source/game/StarCommandProcessor.hpp +++ b/source/game/StarCommandProcessor.hpp @@ -28,6 +28,8 @@ private: String warp(ConnectionId connectionId, String const& argumentString); String warpRandom(ConnectionId connectionId, String const& argumentString); String timewarp(ConnectionId connectionId, String const& argumentString); + String timescale(ConnectionId connectionId, String const& argumentString); + String tickrate(ConnectionId connectionId, String const& argumentString); String setTileProtection(ConnectionId connectionId, String const& argumentString); String setDungeonId(ConnectionId connectionId, String const& argumentString); String setPlayerStart(ConnectionId connectionId, String const& argumentString); diff --git a/source/game/StarNetPackets.cpp b/source/game/StarNetPackets.cpp index 483d244..12b1c74 100644 --- a/source/game/StarNetPackets.cpp +++ b/source/game/StarNetPackets.cpp @@ -304,14 +304,25 @@ void PlanetTypeUpdatePacket::write(DataStream& ds) const { PausePacket::PausePacket() {} -PausePacket::PausePacket(bool pause) : pause(std::move(pause)) {} +PausePacket::PausePacket(bool pause, float timescale) : pause(pause), timescale(timescale) {} + +void PausePacket::readLegacy(DataStream& ds) { + ds.read(pause); + timescale = 1.0f; +} void PausePacket::read(DataStream& ds) { - ds.read(pause); + readLegacy(ds); + ds.read(timescale); +} + +void PausePacket::writeLegacy(DataStream& ds) const { + ds.write(pause); } void PausePacket::write(DataStream& ds) const { - ds.write(pause); + writeLegacy(ds); + ds.write(timescale); } ServerInfoPacket::ServerInfoPacket() {} diff --git a/source/game/StarNetPackets.hpp b/source/game/StarNetPackets.hpp index 432dca4..b71eba7 100644 --- a/source/game/StarNetPackets.hpp +++ b/source/game/StarNetPackets.hpp @@ -226,6 +226,7 @@ struct UniverseTimeUpdatePacket : PacketBase { void write(DataStream& ds) const override; double universeTime; + float timescale; }; struct CelestialResponsePacket : PacketBase { @@ -262,12 +263,15 @@ struct PlanetTypeUpdatePacket : PacketBase { struct PausePacket : PacketBase { PausePacket(); - PausePacket(bool pause); + PausePacket(bool pause, float timescale = 1.0f); + void readLegacy(DataStream& ds) override; void read(DataStream& ds) override; + void writeLegacy(DataStream& ds) const override; void write(DataStream& ds) const override; bool pause; + float timescale; }; struct ServerInfoPacket : PacketBase { diff --git a/source/game/StarSystemWorldServerThread.cpp b/source/game/StarSystemWorldServerThread.cpp index 281ae3e..96e41b4 100644 --- a/source/game/StarSystemWorldServerThread.cpp +++ b/source/game/StarSystemWorldServerThread.cpp @@ -91,7 +91,7 @@ void SystemWorldServerThread::update() { p.second(m_systemWorld->clientShip(p.first).get()); if (!m_pause || *m_pause == false) - m_systemWorld->update(SystemWorldTimestep); + m_systemWorld->update(SystemWorldTimestep * GlobalTimescale); m_triggerStorage = m_systemWorld->triggeredStorage(); // important to set destinations before getting locations diff --git a/source/game/StarUniverseClient.cpp b/source/game/StarUniverseClient.cpp index 57033f8..752d081 100644 --- a/source/game/StarUniverseClient.cpp +++ b/source/game/StarUniverseClient.cpp @@ -164,6 +164,7 @@ void UniverseClient::disconnect() { break; } + GlobalTimescale = 1.0f; reset(); m_mainPlayer = {}; } @@ -675,6 +676,7 @@ void UniverseClient::handlePackets(List const& packets) { m_celestialDatabase->invalidateCacheFor(planetTypeUpdate->coordinate); } else if (auto pausePacket = as(packet)) { setPause(pausePacket->pause); + GlobalTimescale = clamp(pausePacket->timescale, 0.0f, 1024.f); } else if (auto serverInfoPacket = as(packet)) { m_serverInfo = ServerInfo{serverInfoPacket->players, serverInfoPacket->maxPlayers}; } else if (!m_systemWorldClient->handleIncomingPacket(packet)) { diff --git a/source/game/StarUniverseServer.cpp b/source/game/StarUniverseServer.cpp index eafa844..5587b98 100644 --- a/source/game/StarUniverseServer.cpp +++ b/source/game/StarUniverseServer.cpp @@ -143,8 +143,19 @@ void UniverseServer::setPause(bool pause) { else m_universeClock->start(); - for (auto p : m_clients) - m_connectionServer->sendPackets(p.first, {make_shared(*m_pause)}); + for (auto& p : m_clients) + m_connectionServer->sendPackets(p.first, {make_shared(*m_pause, GlobalTimescale)}); +} + +void UniverseServer::setTimescale(float timescale) { + ReadLocker clientsLocker(m_clientsLock); + GlobalTimescale = timescale; + for (auto& p : m_clients) + m_connectionServer->sendPackets(p.first, {make_shared(*m_pause, GlobalTimescale)}); +} + +void UniverseServer::setTickRate(float tickRate) { + ServerGlobalTimestep = 1.0f / tickRate; } List UniverseServer::activeWorlds() const { @@ -1677,7 +1688,8 @@ void UniverseServer::acceptConnection(UniverseConnection connection, MaybesendPackets(clientId, { make_shared(clientId, m_universeSettings->uuid(), m_celestialDatabase->baseInformation()), - make_shared(m_universeClock->time()) + make_shared(m_universeClock->time()), + make_shared(*m_pause, GlobalTimescale) }); setPvp(clientId, false); @@ -1701,8 +1713,8 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe()) { String instance = reviveWarp.world.get().instance; - Json worldConfig = Root::singleton().assets()->json("/instance_worlds.config").get(instance); - if (!worldConfig.getBool("persistent", false)) + auto worldConfig = Root::singleton().assets()->json("/instance_worlds.config").opt(instance); + if (!worldConfig || !worldConfig->getBool("persistent", false)) useReviveWarp = false; } diff --git a/source/game/StarUniverseServer.hpp b/source/game/StarUniverseServer.hpp index 49a9d80..9da38e6 100644 --- a/source/game/StarUniverseServer.hpp +++ b/source/game/StarUniverseServer.hpp @@ -48,6 +48,8 @@ public: void stop(); void setPause(bool pause); + void setTimescale(float timescale); + void setTickRate(float tickRate); List activeWorlds() const; bool isWorldActive(WorldId const& worldId) const; diff --git a/source/game/StarWorldServerThread.cpp b/source/game/StarWorldServerThread.cpp index c03c938..5d126ab 100644 --- a/source/game/StarWorldServerThread.cpp +++ b/source/game/StarWorldServerThread.cpp @@ -207,7 +207,7 @@ void WorldServerThread::run() { double storageInterval = root.assets()->json("/universe_server.config:worldStorageInterval").toDouble() / 1000.0; Timer storageTimer = Timer::withTime(storageInterval); - TickRateApproacher tickApproacher(1.0 / ServerGlobalTimestep, updateMeasureWindow); + TickRateApproacher tickApproacher(1.0f / ServerGlobalTimestep, updateMeasureWindow); double fidelityScore = 0.0; WorldServerFidelity automaticFidelity = WorldServerFidelity::Medium; @@ -217,6 +217,7 @@ void WorldServerThread::run() { LogMap::set(strf("server_{}_update", m_worldId), strf("{:4.2f}Hz", tickApproacher.rate())); update(fidelity); + tickApproacher.setTargetTickRate(1.0f / ServerGlobalTimestep); tickApproacher.tick(); if (storageTimer.timeUp()) { diff --git a/source/server/main.cpp b/source/server/main.cpp index f4d4b91..de09efa 100644 --- a/source/server/main.cpp +++ b/source/server/main.cpp @@ -51,7 +51,7 @@ int main(int argc, char** argv) { if (auto jUpdateRate = configuration->get("updateRate")) { updateRate = jUpdateRate.toFloat(); ServerGlobalTimestep = GlobalTimestep = 1.0f / updateRate; - Logger::info("Configured tickrate is {:4.2f}hz", updateRate); + Logger::info("Configured tick rate is {:4.2f}hz", updateRate); } UniverseServerUPtr server = make_unique(root->toStoragePath("universe"));