Skip to content

Commit 868e14d

Browse files
authored
feat: add /restart command (#372)
* feat: add /restart command for server restarts The command writes an expirable timestamp marker file before triggering shutdown. The Python CLI wrapper detects the marker and relaunches the server process, ignoring stale markers older than 60 seconds. * fix: restrict /restart command to console only
1 parent 8aa8f50 commit 868e14d

6 files changed

Lines changed: 111 additions & 6 deletions

File tree

endstone/cli/__init__.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,25 @@ def main(ctx: click.Context, server_folder: str, no_confirm: bool, remote: str,
9898
raise NotImplementedError(f"{system} is not supported.")
9999

100100
bootstrap = cls(server_folder=server_folder, no_confirm=no_confirm, remote=remote, interactive=interactive)
101-
exit_code = bootstrap.run()
102-
if exit_code != 0:
103-
logger.error(f"Server exited with non-zero code {exit_code}.")
104-
time.sleep(2)
105-
106-
sys.exit(exit_code)
101+
restart_marker = bootstrap.server_path / ".endstone_restart"
102+
103+
while True:
104+
exit_code = bootstrap.run()
105+
106+
if restart_marker.exists():
107+
try:
108+
timestamp = float(restart_marker.read_text().strip())
109+
elapsed = time.time() - timestamp
110+
if elapsed < 60:
111+
restart_marker.unlink()
112+
logger.info("Server is restarting...")
113+
continue
114+
except (ValueError, OSError):
115+
pass
116+
restart_marker.unlink(missing_ok=True)
117+
118+
if exit_code != 0:
119+
logger.error(f"Server exited with non-zero code {exit_code}.")
120+
time.sleep(2)
121+
122+
sys.exit(exit_code)

src/endstone/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ add_library(endstone_core
6161
command/defaults/pardon_ip_command.cpp
6262
command/defaults/plugins_command.cpp
6363
command/defaults/reload_command.cpp
64+
command/defaults/restart_command.cpp
6465
command/defaults/seed_command.cpp
6566
command/defaults/status_command.cpp
6667
command/defaults/version_command.cpp

src/endstone/core/command/command_map.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "endstone/core/command/defaults/pardon_ip_command.h"
3333
#include "endstone/core/command/defaults/plugins_command.h"
3434
#include "endstone/core/command/defaults/reload_command.h"
35+
#include "endstone/core/command/defaults/restart_command.h"
3536
#include "endstone/core/command/defaults/seed_command.h"
3637
#include "endstone/core/command/defaults/status_command.h"
3738
#include "endstone/core/command/defaults/version_command.h"
@@ -148,6 +149,7 @@ void EndstoneCommandMap::setDefaultCommands()
148149
registerCommand(std::make_unique<PardonIpCommand>());
149150
registerCommand(std::make_unique<PluginsCommand>());
150151
registerCommand(std::make_unique<ReloadCommand>());
152+
registerCommand(std::make_unique<RestartCommand>());
151153
registerCommand(std::make_unique<SeedCommand>());
152154
registerCommand(std::make_unique<StatusCommand>());
153155
registerCommand(std::make_unique<VersionCommand>());
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) 2024, The Endstone Project. (https://endstone.dev) All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "endstone/core/command/defaults/restart_command.h"
16+
17+
#include <chrono>
18+
#include <filesystem>
19+
#include <fstream>
20+
21+
#include <entt/entt.hpp>
22+
23+
#include "endstone/color_format.h"
24+
#include "endstone/core/server.h"
25+
26+
namespace endstone::core {
27+
28+
RestartCommand::RestartCommand() : EndstoneCommand("restart")
29+
{
30+
setDescription("Restarts the server.");
31+
setUsages("/restart");
32+
setPermissions("endstone.command.restart");
33+
}
34+
35+
bool RestartCommand::execute(CommandSender &sender, const std::vector<std::string> &args) const
36+
{
37+
if (!testPermission(sender)) {
38+
return true;
39+
}
40+
41+
auto &server = entt::locator<EndstoneServer>::value();
42+
server.broadcast(ColorFormat::Yellow + "Server is restarting...", Server::BroadcastChannelAdmin);
43+
44+
// Write a restart marker file with the current timestamp
45+
auto marker_path = std::filesystem::current_path() / ".endstone_restart";
46+
std::ofstream marker(marker_path);
47+
if (marker.is_open()) {
48+
auto now = std::chrono::system_clock::now();
49+
auto epoch = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
50+
marker << epoch;
51+
}
52+
53+
server.shutdown();
54+
return true;
55+
}
56+
57+
} // namespace endstone::core
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) 2024, The Endstone Project. (https://endstone.dev) All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include "endstone/core/command/endstone_command.h"
18+
19+
namespace endstone::core {
20+
21+
class RestartCommand : public EndstoneCommand {
22+
public:
23+
RestartCommand();
24+
bool execute(CommandSender &sender, const std::vector<std::string> &args) const override;
25+
};
26+
27+
} // namespace endstone::core

src/endstone/core/permissions/default_permissions.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ Permission &CommandPermissions::registerPermissions(Permission &parent)
105105
registerPermission(PREFIX + "reload", "reload",
106106
"Allows the user to reload the configuration and plugins of the server",
107107
PermissionDefault::Operator, commands);
108+
registerPermission(PREFIX + "restart", "restart", "Allows the user to restart the server",
109+
PermissionDefault::Console, commands);
108110
registerPermission(PREFIX + "seed", "seed", "Allows the user to view the seed of the level.",
109111
PermissionDefault::Operator, commands);
110112
registerPermission(PREFIX + "status", "status", "Allows the user to view the status of the server",

0 commit comments

Comments
 (0)