make timescale a server command and add a tickrate command

This commit is contained in:
Kae 2024-03-19 13:35:55 +11:00
parent 5f01d2d4d7
commit 5ca42599ef
14 changed files with 103 additions and 80 deletions

View File

@ -1,7 +1,7 @@
{ {
"directives" : "", "directives" : "",
"pickupDistance" : 0.5, "pickupDistance" : 0.5,
"afterTakenLife" : 2.5, "afterTakenLife" : 0.75,
"overheadTime" : 0.5, "overheadTime" : 0.5,
"overheadApproach" : 2000, "overheadApproach" : 2000,
"overheadRandomizedDistance" : 0.25 "overheadRandomizedDistance" : 0.25

View File

@ -147,7 +147,7 @@ void Clock::setMilliseconds(int64_t millis) {
void Clock::adjustTime(double timeAdjustment) { void Clock::adjustTime(double timeAdjustment) {
RecursiveMutexLocker locker(m_mutex); RecursiveMutexLocker locker(m_mutex);
setTime(time() + timeAdjustment); setTime(max<double>(0.0, time() + timeAdjustment));
} }
void Clock::adjustMilliseconds(int64_t millisAdjustment) { void Clock::adjustMilliseconds(int64_t millisAdjustment) {

View File

@ -51,8 +51,7 @@ ClientCommandProcessor::ClientCommandProcessor(UniverseClientPtr universeClient,
{"maketechavailable", bind(&ClientCommandProcessor::makeTechAvailable, this, _1)}, {"maketechavailable", bind(&ClientCommandProcessor::makeTechAvailable, this, _1)},
{"enabletech", bind(&ClientCommandProcessor::enableTech, this, _1)}, {"enabletech", bind(&ClientCommandProcessor::enableTech, this, _1)},
{"upgradeship", bind(&ClientCommandProcessor::upgradeShip, this, _1)}, {"upgradeship", bind(&ClientCommandProcessor::upgradeShip, this, _1)},
{"swap", bind(&ClientCommandProcessor::swap, this, _1)}, {"swap", bind(&ClientCommandProcessor::swap, this, _1)}
{"timescale", bind(&ClientCommandProcessor::timeScale, this, _1)}
}; };
} }
@ -381,7 +380,7 @@ String ClientCommandProcessor::makeTechAvailable(String const& argumentsString)
return "You must be an admin to use this command."; return "You must be an admin to use this command.";
if (arguments.size() == 0) if (arguments.size() == 0)
return "Not enouch arguments to /maketechavailable"; return "Not enough arguments to /maketechavailable";
m_universeClient->mainPlayer()->techs()->makeAvailable(arguments.at(0)); m_universeClient->mainPlayer()->techs()->makeAvailable(arguments.at(0));
return strf("Added {} to player's visible techs", 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."; return "You must be an admin to use this command.";
if (arguments.size() == 0) 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()->makeAvailable(arguments.at(0));
m_universeClient->mainPlayer()->techs()->enable(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."; return "You must be an admin to use this command.";
if (arguments.size() == 0) if (arguments.size() == 0)
return "Not enouch arguments to /upgradeship"; return "Not enough arguments to /upgradeship";
auto shipUpgrades = Json::parseJson(arguments.at(0)); auto shipUpgrades = Json::parseJson(arguments.at(0));
m_universeClient->rpcInterface()->invokeRemote("ship.applyShipUpgrades", shipUpgrades); m_universeClient->rpcInterface()->invokeRemote("ship.applyShipUpgrades", shipUpgrades);
@ -417,7 +416,7 @@ String ClientCommandProcessor::swap(String const& argumentsString) {
auto arguments = m_parser.tokenizeToStringList(argumentsString); auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (arguments.size() == 0) if (arguments.size() == 0)
return "Not enouch arguments to /swap"; return "Not enough arguments to /swap";
if (m_universeClient->switchPlayer(arguments[0])) if (m_universeClient->switchPlayer(arguments[0]))
return "Successfully swapped player"; return "Successfully swapped player";
@ -425,14 +424,4 @@ String ClientCommandProcessor::swap(String const& argumentsString) {
return "Failed to swap player"; 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<float>(arguments[0]), 0.001f, 256.0f);
return strf("Set application timescale to {:6.6f}x", GlobalTimescale);
}
} }

View File

@ -58,7 +58,6 @@ private:
String enableTech(String const& argumentsString); String enableTech(String const& argumentsString);
String upgradeShip(String const& argumentsString); String upgradeShip(String const& argumentsString);
String swap(String const& argumentsString); String swap(String const& argumentsString);
String timeScale(String const& argumentsString);
UniverseClientPtr m_universeClient; UniverseClientPtr m_universeClient;
CinematicPtr m_cinematicOverlay; CinematicPtr m_cinematicOverlay;

View File

@ -193,22 +193,56 @@ String CommandProcessor::warpRandom(ConnectionId connectionId, String const& typ
return strf("warping to {}", *target); 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")) if (auto errorMsg = adminCheck(connectionId, "do the time warp again"))
return *errorMsg; return *errorMsg;
auto arguments = m_parser.tokenizeToStringList(argumentsString);
if (arguments.empty())
return "Not enough arguments to /timewarp";
try { try {
auto time = lexicalCast<double>(argumentString); auto time = lexicalCast<double>(arguments.at(0));
if (time < 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!"; return "Great Scott! We can't go back in time!";
m_universe->universeClock()->adjustTime(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&) { } 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<float>(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<unsigned>(lexicalCast<unsigned>(arguments[0]), 5, 500);
m_universe->setTickRate(tickRate);
return strf("Set tick rate to {}Hz", tickRate);
}
String CommandProcessor::setTileProtection(ConnectionId connectionId, String const& argumentString) { String CommandProcessor::setTileProtection(ConnectionId connectionId, String const& argumentString) {
if (auto errorMsg = adminCheck(connectionId, "modify world properties")) { if (auto errorMsg = adminCheck(connectionId, "modify world properties")) {
return *errorMsg; return *errorMsg;
@ -272,7 +306,7 @@ String CommandProcessor::spawnItem(ConnectionId connectionId, String const& argu
auto arguments = m_parser.tokenizeToStringList(argumentString); auto arguments = m_parser.tokenizeToStringList(argumentString);
if (arguments.size() == 0) if (arguments.empty())
return "Not enough arguments to /spawnitem"; return "Not enough arguments to /spawnitem";
try { try {
@ -321,7 +355,7 @@ String CommandProcessor::spawnTreasure(ConnectionId connectionId, String const&
auto arguments = m_parser.tokenizeToStringList(argumentString); auto arguments = m_parser.tokenizeToStringList(argumentString);
if (arguments.size() == 0) if (arguments.empty())
return "Not enough arguments to /spawntreasure"; return "Not enough arguments to /spawntreasure";
try { try {
@ -532,7 +566,7 @@ String CommandProcessor::kick(ConnectionId connectionId, String const& argumentS
auto arguments = m_parser.tokenizeToStringList(argumentString); auto arguments = m_parser.tokenizeToStringList(argumentString);
if (arguments.size() == 0) if (arguments.empty())
return "No player specified"; return "No player specified";
auto toKick = playerCidFromCommand(arguments[0], m_universe); 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); auto arguments = m_parser.tokenizeToStringList(argumentString);
if (arguments.size() == 0) if (arguments.empty())
return "No player specified"; return "No player specified";
auto toKick = playerCidFromCommand(arguments[0], m_universe); 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); auto arguments = m_parser.tokenizeToStringList(argumentString);
if (arguments.size() == 0) if (arguments.empty())
return "No IP specified"; return "No IP specified";
bool success = m_universe->unbanIp(arguments[0]); 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); auto arguments = m_parser.tokenizeToStringList(argumentString);
if (arguments.size() == 0) if (arguments.empty())
return "No UUID specified"; return "No UUID specified";
bool success = m_universe->unbanUuid(arguments[0]); bool success = m_universe->unbanUuid(arguments[0]);
@ -868,121 +902,88 @@ Maybe<ConnectionId> CommandProcessor::playerCidFromCommand(String const& player,
return universe->findNick(player); return universe->findNick(player);
} }
//wow, wtf. TODO: replace with hashmap
String CommandProcessor::handleCommand(ConnectionId connectionId, String const& command, String const& argumentString) { String CommandProcessor::handleCommand(ConnectionId connectionId, String const& command, String const& argumentString) {
if (command == "admin") { if (command == "admin") {
return admin(connectionId, argumentString); return admin(connectionId, argumentString);
} else if (command == "timewarp") { } else if (command == "timewarp") {
return timewarp(connectionId, argumentString); return timewarp(connectionId, argumentString);
} else if (command == "timescale") {
return timescale(connectionId, argumentString);
} else if (command == "tickrate") {
return tickrate(connectionId, argumentString);
} else if (command == "settileprotection") { } else if (command == "settileprotection") {
return setTileProtection(connectionId, argumentString); return setTileProtection(connectionId, argumentString);
} else if (command == "setdungeonid") { } else if (command == "setdungeonid") {
return setDungeonId(connectionId, argumentString); return setDungeonId(connectionId, argumentString);
} else if (command == "setspawnpoint") { } else if (command == "setspawnpoint") {
return setPlayerStart(connectionId, argumentString); return setPlayerStart(connectionId, argumentString);
} else if (command == "spawnitem") { } else if (command == "spawnitem") {
return spawnItem(connectionId, argumentString); return spawnItem(connectionId, argumentString);
} else if (command == "spawntreasure") { } else if (command == "spawntreasure") {
return spawnTreasure(connectionId, argumentString); return spawnTreasure(connectionId, argumentString);
} else if (command == "spawnmonster") { } else if (command == "spawnmonster") {
return spawnMonster(connectionId, argumentString); return spawnMonster(connectionId, argumentString);
} else if (command == "spawnnpc") { } else if (command == "spawnnpc") {
return spawnNpc(connectionId, argumentString); return spawnNpc(connectionId, argumentString);
} else if (command == "spawnstagehand") { } else if (command == "spawnstagehand") {
return spawnStagehand(connectionId, argumentString); return spawnStagehand(connectionId, argumentString);
} else if (command == "clearstagehand") { } else if (command == "clearstagehand") {
return clearStagehand(connectionId, argumentString); return clearStagehand(connectionId, argumentString);
} else if (command == "spawnvehicle") { } else if (command == "spawnvehicle") {
return spawnVehicle(connectionId, argumentString); return spawnVehicle(connectionId, argumentString);
} else if (command == "spawnliquid") { } else if (command == "spawnliquid") {
return spawnLiquid(connectionId, argumentString); return spawnLiquid(connectionId, argumentString);
} else if (command == "pvp") { } else if (command == "pvp") {
return pvp(connectionId, argumentString); return pvp(connectionId, argumentString);
} else if (command == "serverwhoami") { } else if (command == "serverwhoami") {
return whoami(connectionId, argumentString); return whoami(connectionId, argumentString);
} else if (command == "kick") { } else if (command == "kick") {
return kick(connectionId, argumentString); return kick(connectionId, argumentString);
} else if (command == "ban") { } else if (command == "ban") {
return ban(connectionId, argumentString); return ban(connectionId, argumentString);
} else if (command == "unbanip") { } else if (command == "unbanip") {
return unbanIp(connectionId, argumentString); return unbanIp(connectionId, argumentString);
} else if (command == "unbanuuid") { } else if (command == "unbanuuid") {
return unbanUuid(connectionId, argumentString); return unbanUuid(connectionId, argumentString);
} else if (command == "list") { } else if (command == "list") {
return list(connectionId, argumentString); return list(connectionId, argumentString);
} else if (command == "help") { } else if (command == "help") {
return help(connectionId, argumentString); return help(connectionId, argumentString);
} else if (command == "warp") { } else if (command == "warp") {
return warp(connectionId, argumentString); return warp(connectionId, argumentString);
} else if (command == "warprandom") { } else if (command == "warprandom") {
return warpRandom(connectionId, argumentString); return warpRandom(connectionId, argumentString);
} else if (command == "whereami") { } else if (command == "whereami") {
return clientCoordinate(connectionId, argumentString); return clientCoordinate(connectionId, argumentString);
} else if (command == "whereis") { } else if (command == "whereis") {
return clientCoordinate(connectionId, argumentString); return clientCoordinate(connectionId, argumentString);
} else if (command == "serverreload") { } else if (command == "serverreload") {
return serverReload(connectionId, argumentString); return serverReload(connectionId, argumentString);
} else if (command == "eval") { } else if (command == "eval") {
return eval(connectionId, argumentString); return eval(connectionId, argumentString);
} else if (command == "entityeval") { } else if (command == "entityeval") {
return entityEval(connectionId, argumentString); return entityEval(connectionId, argumentString);
} else if (command == "enablespawning") { } else if (command == "enablespawning") {
return enableSpawning(connectionId, argumentString); return enableSpawning(connectionId, argumentString);
} else if (command == "disablespawning") { } else if (command == "disablespawning") {
return disableSpawning(connectionId, argumentString); return disableSpawning(connectionId, argumentString);
} else if (command == "placedungeon") { } else if (command == "placedungeon") {
return placeDungeon(connectionId, argumentString); return placeDungeon(connectionId, argumentString);
} else if (command == "setuniverseflag") { } else if (command == "setuniverseflag") {
return setUniverseFlag(connectionId, argumentString); return setUniverseFlag(connectionId, argumentString);
} else if (command == "resetuniverseflags") { } else if (command == "resetuniverseflags") {
return resetUniverseFlags(connectionId, argumentString); return resetUniverseFlags(connectionId, argumentString);
} else if (command == "addbiomeregion") { } else if (command == "addbiomeregion") {
return addBiomeRegion(connectionId, argumentString); return addBiomeRegion(connectionId, argumentString);
} else if (command == "expandbiomeregion") { } else if (command == "expandbiomeregion") {
return expandBiomeRegion(connectionId, argumentString); return expandBiomeRegion(connectionId, argumentString);
} else if (command == "updateplanettype") { } else if (command == "updateplanettype") {
return updatePlanetType(connectionId, argumentString); return updatePlanetType(connectionId, argumentString);
} else if (command == "setenvironmentbiome") { } else if (command == "setenvironmentbiome") {
return setEnvironmentBiome(connectionId, argumentString); return setEnvironmentBiome(connectionId, argumentString);
} else if (auto res = m_scriptComponent.invoke("command", command, connectionId, jsonFromStringList(m_parser.tokenizeToStringList(argumentString)))) { } else if (auto res = m_scriptComponent.invoke("command", command, connectionId, jsonFromStringList(m_parser.tokenizeToStringList(argumentString)))) {
return toString(*res); return toString(*res);
} else { } else {
return strf("No such command {}", command); return strf("No such command {}", command);
} }

View File

@ -28,6 +28,8 @@ private:
String warp(ConnectionId connectionId, String const& argumentString); String warp(ConnectionId connectionId, String const& argumentString);
String warpRandom(ConnectionId connectionId, String const& argumentString); String warpRandom(ConnectionId connectionId, String const& argumentString);
String timewarp(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 setTileProtection(ConnectionId connectionId, String const& argumentString);
String setDungeonId(ConnectionId connectionId, String const& argumentString); String setDungeonId(ConnectionId connectionId, String const& argumentString);
String setPlayerStart(ConnectionId connectionId, String const& argumentString); String setPlayerStart(ConnectionId connectionId, String const& argumentString);

View File

@ -304,14 +304,25 @@ void PlanetTypeUpdatePacket::write(DataStream& ds) const {
PausePacket::PausePacket() {} 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) { 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 { void PausePacket::write(DataStream& ds) const {
ds.write(pause); writeLegacy(ds);
ds.write(timescale);
} }
ServerInfoPacket::ServerInfoPacket() {} ServerInfoPacket::ServerInfoPacket() {}

View File

@ -226,6 +226,7 @@ struct UniverseTimeUpdatePacket : PacketBase<PacketType::UniverseTimeUpdate> {
void write(DataStream& ds) const override; void write(DataStream& ds) const override;
double universeTime; double universeTime;
float timescale;
}; };
struct CelestialResponsePacket : PacketBase<PacketType::CelestialResponse> { struct CelestialResponsePacket : PacketBase<PacketType::CelestialResponse> {
@ -262,12 +263,15 @@ struct PlanetTypeUpdatePacket : PacketBase<PacketType::PlanetTypeUpdate> {
struct PausePacket : PacketBase<PacketType::Pause> { struct PausePacket : PacketBase<PacketType::Pause> {
PausePacket(); PausePacket();
PausePacket(bool pause); PausePacket(bool pause, float timescale = 1.0f);
void readLegacy(DataStream& ds) override;
void read(DataStream& ds) override; void read(DataStream& ds) override;
void writeLegacy(DataStream& ds) const override;
void write(DataStream& ds) const override; void write(DataStream& ds) const override;
bool pause; bool pause;
float timescale;
}; };
struct ServerInfoPacket : PacketBase<PacketType::ServerInfo> { struct ServerInfoPacket : PacketBase<PacketType::ServerInfo> {

View File

@ -91,7 +91,7 @@ void SystemWorldServerThread::update() {
p.second(m_systemWorld->clientShip(p.first).get()); p.second(m_systemWorld->clientShip(p.first).get());
if (!m_pause || *m_pause == false) if (!m_pause || *m_pause == false)
m_systemWorld->update(SystemWorldTimestep); m_systemWorld->update(SystemWorldTimestep * GlobalTimescale);
m_triggerStorage = m_systemWorld->triggeredStorage(); m_triggerStorage = m_systemWorld->triggeredStorage();
// important to set destinations before getting locations // important to set destinations before getting locations

View File

@ -164,6 +164,7 @@ void UniverseClient::disconnect() {
break; break;
} }
GlobalTimescale = 1.0f;
reset(); reset();
m_mainPlayer = {}; m_mainPlayer = {};
} }
@ -675,6 +676,7 @@ void UniverseClient::handlePackets(List<PacketPtr> const& packets) {
m_celestialDatabase->invalidateCacheFor(planetTypeUpdate->coordinate); m_celestialDatabase->invalidateCacheFor(planetTypeUpdate->coordinate);
} else if (auto pausePacket = as<PausePacket>(packet)) { } else if (auto pausePacket = as<PausePacket>(packet)) {
setPause(pausePacket->pause); setPause(pausePacket->pause);
GlobalTimescale = clamp(pausePacket->timescale, 0.0f, 1024.f);
} else if (auto serverInfoPacket = as<ServerInfoPacket>(packet)) { } else if (auto serverInfoPacket = as<ServerInfoPacket>(packet)) {
m_serverInfo = ServerInfo{serverInfoPacket->players, serverInfoPacket->maxPlayers}; m_serverInfo = ServerInfo{serverInfoPacket->players, serverInfoPacket->maxPlayers};
} else if (!m_systemWorldClient->handleIncomingPacket(packet)) { } else if (!m_systemWorldClient->handleIncomingPacket(packet)) {

View File

@ -143,8 +143,19 @@ void UniverseServer::setPause(bool pause) {
else else
m_universeClock->start(); m_universeClock->start();
for (auto p : m_clients) for (auto& p : m_clients)
m_connectionServer->sendPackets(p.first, {make_shared<PausePacket>(*m_pause)}); m_connectionServer->sendPackets(p.first, {make_shared<PausePacket>(*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<PausePacket>(*m_pause, GlobalTimescale)});
}
void UniverseServer::setTickRate(float tickRate) {
ServerGlobalTimestep = 1.0f / tickRate;
} }
List<WorldId> UniverseServer::activeWorlds() const { List<WorldId> UniverseServer::activeWorlds() const {
@ -1677,7 +1688,8 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe<HostA
m_connectionServer->sendPackets(clientId, { m_connectionServer->sendPackets(clientId, {
make_shared<ConnectSuccessPacket>(clientId, m_universeSettings->uuid(), m_celestialDatabase->baseInformation()), make_shared<ConnectSuccessPacket>(clientId, m_universeSettings->uuid(), m_celestialDatabase->baseInformation()),
make_shared<UniverseTimeUpdatePacket>(m_universeClock->time()) make_shared<UniverseTimeUpdatePacket>(m_universeClock->time()),
make_shared<PausePacket>(*m_pause, GlobalTimescale)
}); });
setPvp(clientId, false); setPvp(clientId, false);
@ -1701,8 +1713,8 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe<HostA
bool useReviveWarp = true; bool useReviveWarp = true;
if (reviveWarp.world.is<InstanceWorldId>()) { if (reviveWarp.world.is<InstanceWorldId>()) {
String instance = reviveWarp.world.get<InstanceWorldId>().instance; String instance = reviveWarp.world.get<InstanceWorldId>().instance;
Json worldConfig = Root::singleton().assets()->json("/instance_worlds.config").get(instance); auto worldConfig = Root::singleton().assets()->json("/instance_worlds.config").opt(instance);
if (!worldConfig.getBool("persistent", false)) if (!worldConfig || !worldConfig->getBool("persistent", false))
useReviveWarp = false; useReviveWarp = false;
} }

View File

@ -48,6 +48,8 @@ public:
void stop(); void stop();
void setPause(bool pause); void setPause(bool pause);
void setTimescale(float timescale);
void setTickRate(float tickRate);
List<WorldId> activeWorlds() const; List<WorldId> activeWorlds() const;
bool isWorldActive(WorldId const& worldId) const; bool isWorldActive(WorldId const& worldId) const;

View File

@ -207,7 +207,7 @@ void WorldServerThread::run() {
double storageInterval = root.assets()->json("/universe_server.config:worldStorageInterval").toDouble() / 1000.0; double storageInterval = root.assets()->json("/universe_server.config:worldStorageInterval").toDouble() / 1000.0;
Timer storageTimer = Timer::withTime(storageInterval); Timer storageTimer = Timer::withTime(storageInterval);
TickRateApproacher tickApproacher(1.0 / ServerGlobalTimestep, updateMeasureWindow); TickRateApproacher tickApproacher(1.0f / ServerGlobalTimestep, updateMeasureWindow);
double fidelityScore = 0.0; double fidelityScore = 0.0;
WorldServerFidelity automaticFidelity = WorldServerFidelity::Medium; WorldServerFidelity automaticFidelity = WorldServerFidelity::Medium;
@ -217,6 +217,7 @@ void WorldServerThread::run() {
LogMap::set(strf("server_{}_update", m_worldId), strf("{:4.2f}Hz", tickApproacher.rate())); LogMap::set(strf("server_{}_update", m_worldId), strf("{:4.2f}Hz", tickApproacher.rate()));
update(fidelity); update(fidelity);
tickApproacher.setTargetTickRate(1.0f / ServerGlobalTimestep);
tickApproacher.tick(); tickApproacher.tick();
if (storageTimer.timeUp()) { if (storageTimer.timeUp()) {