Repository navigation
Expand file tree
/
Copy pathstop-server.sh
More file actions
executable file
·321 lines (273 loc) · 9.17 KB
/
Copy pathstop-server.sh
File metadata and controls
executable file
·321 lines (273 loc) · 9.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
#!/bin/bash
# Minecraft Bedrock Server Stop Script
# This script gracefully stops the Minecraft Bedrock server
set -euo pipefail
# Source configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh"
load_config
validate_config || { printf "%s\n" "Unsafe or invalid configuration" >&2; exit 1; }
SAVE_HELD=false
release_save_hold() {
local status=$?
trap - EXIT INT TERM
if [[ "$SAVE_HELD" == true ]]; then
sudo -u "$SERVER_USER" screen -S "$SCREEN_SESSION_NAME" -X stuff "save resume"$'\r' || true
fi
exit "$status"
}
trap release_save_hold EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging function
log() {
local level="$1"
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
case "$level" in
INFO) echo -e "${GREEN}[INFO]${NC} $message" ;;
WARN) echo -e "${YELLOW}[WARN]${NC} $message" ;;
ERROR) echo -e "${RED}[ERROR]${NC} $message" ;;
DEBUG) echo -e "${BLUE}[DEBUG]${NC} $message" ;;
esac
# Also log to file if log directory exists
if [[ -d "$LOG_DIR" ]]; then
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
fi
}
# Check if server is running
is_server_running() {
has_server_process || has_screen_session
}
# Send a message to all players before shutdown
send_shutdown_warning() {
local countdown="$1"
log INFO "Sending shutdown warning to players ($countdown seconds)..."
local message="Server will shut down in $countdown seconds. Please save your progress!"
# Send the message to the server console
sudo -u "$SERVER_USER" screen -S "$SCREEN_SESSION_NAME" -X stuff "say $message"$'\r' || {
log WARN "Failed to send shutdown warning"
return 1
}
}
# Send a command to the server console
send_server_command() {
local command="$1"
if is_server_running; then
sudo -u "$SERVER_USER" screen -S "$SCREEN_SESSION_NAME" -X stuff "$command"$'\r' || {
log ERROR "Failed to send command: $command"
return 1
}
return 0
else
log ERROR "Server is not running"
return 1
fi
}
# Gracefully stop the server
graceful_stop() {
local force="$1"
if ! is_server_running; then
log INFO "Server is not running"
return 0
fi
log INFO "Stopping Minecraft Bedrock Server gracefully..."
# If not forcing, send warnings to players
if [[ "$force" != "force" ]]; then
# Send warnings at 60, 30, 15, 5 seconds
send_shutdown_warning 60 || log WARN "Failed to send first shutdown warning"
sleep 45
if is_server_running; then
send_shutdown_warning 15 || log WARN "Failed to send second shutdown warning"
sleep 10
if is_server_running; then
send_shutdown_warning 5 || log WARN "Failed to send final shutdown warning"
sleep 5
fi
fi
fi
if is_server_running; then
# Save the world before stopping
log INFO "Saving world data..."
SAVE_HELD=true
send_server_command "save hold" || log WARN "Failed to send 'save hold' command"
sleep 2
send_server_command "save query" || log WARN "Failed to send 'save query' command"
sleep 3
if send_server_command "save resume"; then SAVE_HELD=false; fi
sleep 2
# Send stop command
log INFO "Sending stop command to server..."
send_server_command "stop" || log WARN "Failed to send 'stop' command"
# Give the server a moment to process the stop command
sleep 5
# Do an immediate check to see if server stopped quickly
if ! is_server_running; then
log INFO "Server stopped quickly after stop command"
else
log INFO "Server still running, continuing to wait..."
fi
# Wait for server to stop (max 60 seconds)
local count=0
local max_wait=60
log INFO "Waiting for server to stop (max $max_wait seconds)..."
while [[ $count -lt $max_wait ]]; do
# Check if server is still running
if ! is_server_running; then
log INFO "Server stopped after $count seconds"
break
fi
sleep 1
count=$((count + 1))
# Show progress every 10 seconds
if [[ $((count % 10)) -eq 0 ]]; then
log INFO "Still waiting... ($count/$max_wait seconds)"
# Additional debug info every 20 seconds
if [[ $((count % 20)) -eq 0 ]]; then
local screen_status="not found"
local process_status="not found"
if has_screen_session; then
screen_status="running"
fi
if has_server_process; then
process_status="running"
fi
log DEBUG "Status check - Screen: $screen_status, Process: $process_status"
fi
fi
done
if is_server_running; then
log WARN "Server didn't stop gracefully within $max_wait seconds"
log WARN "Killing screen session..."
sudo -u "$SERVER_USER" screen -S "$SCREEN_SESSION_NAME" -X quit || {
log WARN "Failed to kill screen session gracefully"
}
sleep 2
# Also kill any remaining bedrock_server processes
if has_server_process; then
log WARN "Killing remaining bedrock_server processes..."
signal_server TERM || {
log ERROR "Failed to kill bedrock_server processes"
return 1
}
sleep 2
fi
if is_server_running; then
log ERROR "Failed to stop server"
return 1
else
log WARN "Server stopped forcefully"
fi
else
log INFO "Server stopped gracefully"
# Clean up any lingering screen session
if has_screen_session; then
log INFO "Cleaning up screen session..."
sudo -u "$SERVER_USER" screen -S "$SCREEN_SESSION_NAME" -X quit || true
fi
fi
fi
return 0
}
# Force stop the server (immediate)
force_stop() {
log WARN "Force stopping configured server instance..."
signal_server TERM || return 1
local count
for ((count=0; count<10; count++)); do
has_server_process || break
sleep 1
done
if has_server_process; then
signal_server KILL || return 1
sleep 2
fi
if has_screen_session; then
sudo -u "$SERVER_USER" screen -S "$SCREEN_SESSION_NAME" -X quit || return 1
sleep 1
fi
! is_server_running
}
# Show server status
show_status() {
echo ""
log INFO "Server Status:"
if is_server_running; then
log INFO "✓ Server is running"
# Show screen sessions
echo ""
log INFO "Active screen sessions:"
sudo -u "$SERVER_USER" screen -list | grep "$SCREEN_SESSION_NAME" || true
else
log INFO "✗ Server is not running"
fi
echo ""
}
# Show usage information
show_usage() {
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " -f, --force Force stop the server immediately (no warnings)"
echo " -s, --status Show server status only"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
echo " $0 Stop server gracefully with player warnings"
echo " $0 --force Stop server immediately"
echo " $0 --status Check if server is running"
}
# Main function
main() {
local action="stop"
local force_mode="normal"
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-f|--force)
force_mode="force"
shift
;;
-s|--status)
action="status"
shift
;;
-h|--help)
show_usage
exit 0
;;
*)
log ERROR "Unknown option: $1"
show_usage
exit 1
;;
esac
done
log INFO "Minecraft Bedrock Server Stop Script"
case "$action" in
"status")
show_status
;;
"stop")
if [[ $EUID -ne 0 ]]; then
log ERROR "Run stop-server.sh as root"; return 1
fi
acquire_management_lock
if [[ "$force_mode" == "force" ]]; then
force_stop
else
graceful_stop "$force_mode"
fi
show_status
;;
esac
}
# Run main function
main "$@"