@minecraft-docker/mcctl 1.6.13 → 1.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/commands/backup.d.ts +1 -0
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +28 -2
- package/dist/commands/backup.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
- package/scripts/backup.sh +569 -0
- package/scripts/create-server.sh +580 -0
- package/scripts/delete-server.sh +266 -0
- package/scripts/init.sh +390 -0
- package/scripts/lib/common.sh +248 -0
- package/scripts/lock.sh +448 -0
- package/scripts/logs.sh +283 -0
- package/scripts/mcctl.sh +543 -0
- package/scripts/migrate-nip-io.sh +258 -0
- package/scripts/player.sh +329 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# migrate-nip-io.sh - Add nip.io domains to existing servers
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Usage: ./scripts/migrate-nip-io.sh [options]
|
|
6
|
+
#
|
|
7
|
+
# This script updates all existing server docker-compose.yml files to include
|
|
8
|
+
# nip.io magic DNS hostnames alongside the existing .local hostnames.
|
|
9
|
+
#
|
|
10
|
+
# Options:
|
|
11
|
+
# --dry-run Show what would be changed without making changes
|
|
12
|
+
# -h, --help Show this help message
|
|
13
|
+
#
|
|
14
|
+
# Requirements:
|
|
15
|
+
# - HOST_IP must be set in platform/.env
|
|
16
|
+
#
|
|
17
|
+
# Example:
|
|
18
|
+
# ./scripts/migrate-nip-io.sh # Apply changes
|
|
19
|
+
# ./scripts/migrate-nip-io.sh --dry-run # Preview changes only
|
|
20
|
+
# =============================================================================
|
|
21
|
+
|
|
22
|
+
set -e
|
|
23
|
+
|
|
24
|
+
# Colors for output
|
|
25
|
+
RED='\033[0;31m'
|
|
26
|
+
GREEN='\033[0;32m'
|
|
27
|
+
YELLOW='\033[1;33m'
|
|
28
|
+
BLUE='\033[0;34m'
|
|
29
|
+
NC='\033[0m' # No Color
|
|
30
|
+
|
|
31
|
+
# Track current backup for cleanup on error
|
|
32
|
+
CURRENT_BACKUP=""
|
|
33
|
+
|
|
34
|
+
# Cleanup function for trap
|
|
35
|
+
cleanup_on_error() {
|
|
36
|
+
if [ -n "$CURRENT_BACKUP" ] && [ -f "$CURRENT_BACKUP" ]; then
|
|
37
|
+
echo -e "${RED}Error occurred. Restoring backup...${NC}"
|
|
38
|
+
local original_file="${CURRENT_BACKUP%.bak}"
|
|
39
|
+
mv "$CURRENT_BACKUP" "$original_file"
|
|
40
|
+
echo -e "${YELLOW}Restored: $original_file${NC}"
|
|
41
|
+
fi
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Set trap for cleanup on error
|
|
45
|
+
trap cleanup_on_error ERR
|
|
46
|
+
|
|
47
|
+
# Get script/platform directories
|
|
48
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
49
|
+
PLATFORM_DIR="$(dirname "$SCRIPT_DIR")"
|
|
50
|
+
SERVERS_DIR="$PLATFORM_DIR/servers"
|
|
51
|
+
ENV_FILE="$PLATFORM_DIR/.env"
|
|
52
|
+
|
|
53
|
+
# Options
|
|
54
|
+
DRY_RUN=false
|
|
55
|
+
|
|
56
|
+
# =============================================================================
|
|
57
|
+
# Helper Functions
|
|
58
|
+
# =============================================================================
|
|
59
|
+
|
|
60
|
+
show_usage() {
|
|
61
|
+
echo "Usage: $0 [options]"
|
|
62
|
+
echo ""
|
|
63
|
+
echo "Add nip.io magic DNS hostnames to existing server configurations."
|
|
64
|
+
echo ""
|
|
65
|
+
echo "Options:"
|
|
66
|
+
echo " --dry-run Show what would be changed without making changes"
|
|
67
|
+
echo " -h, --help Show this help message"
|
|
68
|
+
echo ""
|
|
69
|
+
echo "Requirements:"
|
|
70
|
+
echo " HOST_IP must be set in platform/.env"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Get host IP from .env file or auto-detect
|
|
74
|
+
get_host_ip() {
|
|
75
|
+
# Try .env file first
|
|
76
|
+
if [ -f "$ENV_FILE" ]; then
|
|
77
|
+
local env_ip
|
|
78
|
+
env_ip=$(grep "^HOST_IP=" "$ENV_FILE" 2>/dev/null | cut -d'=' -f2 | tr -d '"' | tr -d "'")
|
|
79
|
+
if [ -n "$env_ip" ]; then
|
|
80
|
+
echo "$env_ip"
|
|
81
|
+
return
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Auto-detect: Try ip route
|
|
86
|
+
local detected_ip
|
|
87
|
+
detected_ip=$(ip route get 1 2>/dev/null | grep -oP 'src \K\S+' | head -1)
|
|
88
|
+
if [ -n "$detected_ip" ]; then
|
|
89
|
+
echo "$detected_ip"
|
|
90
|
+
return
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Auto-detect: Try hostname -I
|
|
94
|
+
detected_ip=$(hostname -I 2>/dev/null | awk '{print $1}')
|
|
95
|
+
if [ -n "$detected_ip" ]; then
|
|
96
|
+
echo "$detected_ip"
|
|
97
|
+
return
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
echo ""
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# Extract server name from docker-compose.yml
|
|
104
|
+
get_server_name() {
|
|
105
|
+
local compose_file="$1"
|
|
106
|
+
# Look for mc-router.host label and extract the .local hostname
|
|
107
|
+
local hostname
|
|
108
|
+
hostname=$(grep -E "mc-router\.host.*\.local" "$compose_file" 2>/dev/null | head -1 | grep -oP '[a-z0-9-]+(?=\.local)' | head -1)
|
|
109
|
+
echo "$hostname"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Check if nip.io is already configured
|
|
113
|
+
has_nip_io() {
|
|
114
|
+
local compose_file="$1"
|
|
115
|
+
grep -q "nip\.io" "$compose_file" 2>/dev/null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# =============================================================================
|
|
119
|
+
# Parse Arguments
|
|
120
|
+
# =============================================================================
|
|
121
|
+
|
|
122
|
+
while [[ $# -gt 0 ]]; do
|
|
123
|
+
case $1 in
|
|
124
|
+
--dry-run)
|
|
125
|
+
DRY_RUN=true
|
|
126
|
+
shift
|
|
127
|
+
;;
|
|
128
|
+
-h|--help)
|
|
129
|
+
show_usage
|
|
130
|
+
exit 0
|
|
131
|
+
;;
|
|
132
|
+
*)
|
|
133
|
+
echo -e "${RED}Error: Unknown option: $1${NC}"
|
|
134
|
+
show_usage
|
|
135
|
+
exit 1
|
|
136
|
+
;;
|
|
137
|
+
esac
|
|
138
|
+
done
|
|
139
|
+
|
|
140
|
+
# =============================================================================
|
|
141
|
+
# Main Script
|
|
142
|
+
# =============================================================================
|
|
143
|
+
|
|
144
|
+
echo -e "${GREEN}========================================${NC}"
|
|
145
|
+
echo -e "${GREEN}nip.io Migration Script${NC}"
|
|
146
|
+
echo -e "${GREEN}========================================${NC}"
|
|
147
|
+
echo ""
|
|
148
|
+
|
|
149
|
+
# Check HOST_IP
|
|
150
|
+
HOST_IP=$(get_host_ip)
|
|
151
|
+
if [ -z "$HOST_IP" ]; then
|
|
152
|
+
echo -e "${RED}Error: HOST_IP not set in $ENV_FILE${NC}"
|
|
153
|
+
echo "Please set HOST_IP to your server's local IP address."
|
|
154
|
+
echo "Example: HOST_IP=192.168.20.37"
|
|
155
|
+
exit 1
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
echo "HOST_IP: $HOST_IP"
|
|
159
|
+
echo "Servers directory: $SERVERS_DIR"
|
|
160
|
+
if [ "$DRY_RUN" = true ]; then
|
|
161
|
+
echo -e "${YELLOW}DRY RUN MODE - No changes will be made${NC}"
|
|
162
|
+
fi
|
|
163
|
+
echo ""
|
|
164
|
+
|
|
165
|
+
# Find all server directories (excluding _template)
|
|
166
|
+
SERVERS_FOUND=0
|
|
167
|
+
SERVERS_UPDATED=0
|
|
168
|
+
SERVERS_SKIPPED=0
|
|
169
|
+
|
|
170
|
+
for server_dir in "$SERVERS_DIR"/*/; do
|
|
171
|
+
# Skip _template directory
|
|
172
|
+
if [[ "$(basename "$server_dir")" == "_template" ]]; then
|
|
173
|
+
continue
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
compose_file="$server_dir/docker-compose.yml"
|
|
177
|
+
|
|
178
|
+
# Check if docker-compose.yml exists
|
|
179
|
+
if [ ! -f "$compose_file" ]; then
|
|
180
|
+
continue
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
SERVERS_FOUND=$((SERVERS_FOUND + 1))
|
|
184
|
+
server_name=$(basename "$server_dir")
|
|
185
|
+
|
|
186
|
+
echo -e "${BLUE}Processing:${NC} $server_name"
|
|
187
|
+
|
|
188
|
+
# Check if already has nip.io
|
|
189
|
+
if has_nip_io "$compose_file"; then
|
|
190
|
+
echo -e " ${YELLOW}Skipped${NC} - nip.io already configured"
|
|
191
|
+
SERVERS_SKIPPED=$((SERVERS_SKIPPED + 1))
|
|
192
|
+
continue
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
# Get the current hostname from the file
|
|
196
|
+
current_hostname=$(get_server_name "$compose_file")
|
|
197
|
+
if [ -z "$current_hostname" ]; then
|
|
198
|
+
echo -e " ${YELLOW}Skipped${NC} - Could not find mc-router.host label"
|
|
199
|
+
SERVERS_SKIPPED=$((SERVERS_SKIPPED + 1))
|
|
200
|
+
continue
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
# Construct new hostname value
|
|
204
|
+
new_hostname="$current_hostname.local,$current_hostname.$HOST_IP.nip.io"
|
|
205
|
+
|
|
206
|
+
echo " Current: $current_hostname.local"
|
|
207
|
+
echo " New: $new_hostname"
|
|
208
|
+
|
|
209
|
+
if [ "$DRY_RUN" = true ]; then
|
|
210
|
+
echo -e " ${YELLOW}Would update${NC} (dry run)"
|
|
211
|
+
SERVERS_UPDATED=$((SERVERS_UPDATED + 1))
|
|
212
|
+
else
|
|
213
|
+
# Create backup (tracked for cleanup on error)
|
|
214
|
+
CURRENT_BACKUP="$compose_file.bak"
|
|
215
|
+
cp "$compose_file" "$CURRENT_BACKUP"
|
|
216
|
+
|
|
217
|
+
# Update the mc-router.host label
|
|
218
|
+
# Handle both YAML format (mc-router.host: "value") and array format (- "mc-router.host=value")
|
|
219
|
+
if grep -q "mc-router\.host:" "$compose_file"; then
|
|
220
|
+
# YAML format
|
|
221
|
+
sed -i "s/mc-router\.host:.*\"$current_hostname\.local\"/mc-router.host: \"$new_hostname\"/" "$compose_file"
|
|
222
|
+
else
|
|
223
|
+
# Array format
|
|
224
|
+
sed -i "s/mc-router\.host=$current_hostname\.local/mc-router.host=$new_hostname/" "$compose_file"
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
echo -e " ${GREEN}Updated${NC}"
|
|
228
|
+
|
|
229
|
+
# Remove backup on success and clear tracking
|
|
230
|
+
rm -f "$CURRENT_BACKUP"
|
|
231
|
+
CURRENT_BACKUP=""
|
|
232
|
+
SERVERS_UPDATED=$((SERVERS_UPDATED + 1))
|
|
233
|
+
fi
|
|
234
|
+
done
|
|
235
|
+
|
|
236
|
+
echo ""
|
|
237
|
+
echo -e "${GREEN}========================================${NC}"
|
|
238
|
+
echo -e "${GREEN}Migration Summary${NC}"
|
|
239
|
+
echo -e "${GREEN}========================================${NC}"
|
|
240
|
+
echo "Servers found: $SERVERS_FOUND"
|
|
241
|
+
echo "Servers updated: $SERVERS_UPDATED"
|
|
242
|
+
echo "Servers skipped: $SERVERS_SKIPPED"
|
|
243
|
+
echo ""
|
|
244
|
+
|
|
245
|
+
if [ "$DRY_RUN" = true ]; then
|
|
246
|
+
echo -e "${YELLOW}This was a dry run. Run without --dry-run to apply changes.${NC}"
|
|
247
|
+
elif [ "$SERVERS_UPDATED" -gt 0 ]; then
|
|
248
|
+
echo -e "${GREEN}Migration complete!${NC}"
|
|
249
|
+
echo ""
|
|
250
|
+
echo "Next steps:"
|
|
251
|
+
echo " 1. Validate configuration: docker compose -f $PLATFORM_DIR/docker-compose.yml config"
|
|
252
|
+
echo " 2. Restart servers: docker compose down && docker compose up -d"
|
|
253
|
+
echo ""
|
|
254
|
+
echo "Clients can now connect via:"
|
|
255
|
+
echo " <server>.$HOST_IP.nip.io:25565"
|
|
256
|
+
else
|
|
257
|
+
echo "No servers needed migration."
|
|
258
|
+
fi
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# player.sh - Player UUID lookup using PlayerDB API
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Look up Minecraft player information including UUID.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# ./scripts/player.sh lookup <playerName> # Full player info
|
|
9
|
+
# ./scripts/player.sh lookup <playerName> --json # JSON output
|
|
10
|
+
# ./scripts/player.sh uuid <playerName> # Online UUID only
|
|
11
|
+
# ./scripts/player.sh uuid <playerName> --offline # Offline UUID only
|
|
12
|
+
#
|
|
13
|
+
# Exit codes:
|
|
14
|
+
# 0 - Success
|
|
15
|
+
# 1 - Error (API error, player not found, etc.)
|
|
16
|
+
# 2 - Warning
|
|
17
|
+
# =============================================================================
|
|
18
|
+
|
|
19
|
+
set -e
|
|
20
|
+
|
|
21
|
+
# Get script directory and source common functions
|
|
22
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
24
|
+
|
|
25
|
+
# =============================================================================
|
|
26
|
+
# Configuration
|
|
27
|
+
# =============================================================================
|
|
28
|
+
|
|
29
|
+
PLAYERDB_API="https://playerdb.co/api/player/minecraft"
|
|
30
|
+
AVATAR_BASE="https://crafthead.net/avatar"
|
|
31
|
+
|
|
32
|
+
# =============================================================================
|
|
33
|
+
# Usage
|
|
34
|
+
# =============================================================================
|
|
35
|
+
|
|
36
|
+
usage() {
|
|
37
|
+
cat <<EOF
|
|
38
|
+
Usage: $(basename "$0") <command> <playerName> [options]
|
|
39
|
+
|
|
40
|
+
Look up Minecraft player information using PlayerDB API.
|
|
41
|
+
|
|
42
|
+
Commands:
|
|
43
|
+
lookup <playerName> Show full player information
|
|
44
|
+
uuid <playerName> Get player's online UUID
|
|
45
|
+
|
|
46
|
+
Options:
|
|
47
|
+
--json Output in JSON format
|
|
48
|
+
--offline Get offline UUID (for uuid command)
|
|
49
|
+
-h, --help Show this help message
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
$(basename "$0") lookup Notch
|
|
53
|
+
$(basename "$0") lookup Notch --json
|
|
54
|
+
$(basename "$0") uuid Notch
|
|
55
|
+
$(basename "$0") uuid Notch --offline
|
|
56
|
+
|
|
57
|
+
Notes:
|
|
58
|
+
- Online UUID is fetched from Mojang API via PlayerDB
|
|
59
|
+
- Offline UUID is calculated from player name (UUID v3)
|
|
60
|
+
- Requires: curl and jq
|
|
61
|
+
EOF
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# =============================================================================
|
|
65
|
+
# Helper Functions
|
|
66
|
+
# =============================================================================
|
|
67
|
+
|
|
68
|
+
# Check required commands
|
|
69
|
+
check_requirements() {
|
|
70
|
+
local missing=()
|
|
71
|
+
|
|
72
|
+
if ! command -v curl &> /dev/null; then
|
|
73
|
+
missing+=("curl")
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
if ! command -v jq &> /dev/null; then
|
|
77
|
+
missing+=("jq")
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
if [[ ${#missing[@]} -gt 0 ]]; then
|
|
81
|
+
error "Missing required commands: ${missing[*]}"
|
|
82
|
+
error "Please install them and try again"
|
|
83
|
+
return 1
|
|
84
|
+
fi
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Calculate offline UUID from player name
|
|
88
|
+
# Offline UUID = UUID v3 based on "OfflinePlayer:<name>"
|
|
89
|
+
calculate_offline_uuid() {
|
|
90
|
+
local player_name="$1"
|
|
91
|
+
local input="OfflinePlayer:$player_name"
|
|
92
|
+
|
|
93
|
+
# Calculate MD5 hash and format as UUID v3
|
|
94
|
+
local md5_hash
|
|
95
|
+
md5_hash=$(echo -n "$input" | md5sum | cut -d' ' -f1)
|
|
96
|
+
|
|
97
|
+
# Format as UUID with version 3 marker
|
|
98
|
+
# UUID format: xxxxxxxx-xxxx-3xxx-yxxx-xxxxxxxxxxxx
|
|
99
|
+
# where 3 is the version and y is 8, 9, a, or b (variant)
|
|
100
|
+
local uuid
|
|
101
|
+
uuid=$(echo "$md5_hash" | sed 's/\(........\)\(....\)\(....\)\(....\)\(............\)/\1-\2-\3-\4-\5/')
|
|
102
|
+
|
|
103
|
+
# Set version to 3 (third section starts with 3)
|
|
104
|
+
uuid=$(echo "$uuid" | sed 's/\(........-....\)-\(.\)/\1-3/')
|
|
105
|
+
|
|
106
|
+
# Set variant (first char of fourth section should be 8, 9, a, or b)
|
|
107
|
+
# We'll use 8 for simplicity
|
|
108
|
+
local fourth_section
|
|
109
|
+
fourth_section=$(echo "$uuid" | cut -d'-' -f4)
|
|
110
|
+
local first_char=${fourth_section:0:1}
|
|
111
|
+
local rest=${fourth_section:1}
|
|
112
|
+
|
|
113
|
+
# Convert first char to variant (8-b range)
|
|
114
|
+
case $first_char in
|
|
115
|
+
0|1|2|3) first_char="8" ;;
|
|
116
|
+
4|5|6|7) first_char="9" ;;
|
|
117
|
+
8|9) ;; # Already valid
|
|
118
|
+
a|b) ;; # Already valid
|
|
119
|
+
c|d|e|f) first_char="a" ;;
|
|
120
|
+
esac
|
|
121
|
+
|
|
122
|
+
# Reconstruct UUID
|
|
123
|
+
uuid=$(echo "$uuid" | sed "s/\(........-....-....-\).\(...-............\)/\1${first_char}\2/")
|
|
124
|
+
|
|
125
|
+
echo "$uuid"
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# Fetch player info from PlayerDB API
|
|
129
|
+
fetch_player_info() {
|
|
130
|
+
local player_name="$1"
|
|
131
|
+
local response
|
|
132
|
+
local http_code
|
|
133
|
+
|
|
134
|
+
# Make API request with error handling
|
|
135
|
+
response=$(curl -s -w "\n%{http_code}" "${PLAYERDB_API}/${player_name}" 2>/dev/null) || {
|
|
136
|
+
error "Failed to connect to PlayerDB API"
|
|
137
|
+
return 1
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Extract HTTP code and body
|
|
141
|
+
http_code=$(echo "$response" | tail -n1)
|
|
142
|
+
local body
|
|
143
|
+
body=$(echo "$response" | sed '$d')
|
|
144
|
+
|
|
145
|
+
# Check HTTP status
|
|
146
|
+
if [[ "$http_code" != "200" ]]; then
|
|
147
|
+
error "API request failed with HTTP $http_code"
|
|
148
|
+
return 1
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
# Check API success
|
|
152
|
+
local success
|
|
153
|
+
success=$(echo "$body" | jq -r '.success' 2>/dev/null)
|
|
154
|
+
|
|
155
|
+
if [[ "$success" != "true" ]]; then
|
|
156
|
+
local code
|
|
157
|
+
code=$(echo "$body" | jq -r '.code' 2>/dev/null)
|
|
158
|
+
if [[ "$code" == "minecraft.invalid_username" ]]; then
|
|
159
|
+
error "Invalid username format: $player_name"
|
|
160
|
+
elif [[ "$code" == "minecraft.api_failure" ]]; then
|
|
161
|
+
error "Player not found: $player_name"
|
|
162
|
+
else
|
|
163
|
+
error "API error: $(echo "$body" | jq -r '.message' 2>/dev/null)"
|
|
164
|
+
fi
|
|
165
|
+
return 1
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
echo "$body"
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# =============================================================================
|
|
172
|
+
# Commands
|
|
173
|
+
# =============================================================================
|
|
174
|
+
|
|
175
|
+
# Lookup command - show full player info
|
|
176
|
+
cmd_lookup() {
|
|
177
|
+
local player_name=""
|
|
178
|
+
local json_output=false
|
|
179
|
+
|
|
180
|
+
while [[ $# -gt 0 ]]; do
|
|
181
|
+
case "$1" in
|
|
182
|
+
--json)
|
|
183
|
+
json_output=true
|
|
184
|
+
JSON_OUTPUT=true
|
|
185
|
+
setup_colors
|
|
186
|
+
shift
|
|
187
|
+
;;
|
|
188
|
+
-*)
|
|
189
|
+
error "Unknown option: $1"
|
|
190
|
+
return 1
|
|
191
|
+
;;
|
|
192
|
+
*)
|
|
193
|
+
if [[ -z "$player_name" ]]; then
|
|
194
|
+
player_name="$1"
|
|
195
|
+
else
|
|
196
|
+
error "Unexpected argument: $1"
|
|
197
|
+
return 1
|
|
198
|
+
fi
|
|
199
|
+
shift
|
|
200
|
+
;;
|
|
201
|
+
esac
|
|
202
|
+
done
|
|
203
|
+
|
|
204
|
+
if [[ -z "$player_name" ]]; then
|
|
205
|
+
error "Player name is required"
|
|
206
|
+
return 1
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
check_requirements || return 1
|
|
210
|
+
|
|
211
|
+
local response
|
|
212
|
+
response=$(fetch_player_info "$player_name") || return 1
|
|
213
|
+
|
|
214
|
+
# Extract player data
|
|
215
|
+
local username
|
|
216
|
+
local online_uuid
|
|
217
|
+
local raw_id
|
|
218
|
+
local avatar
|
|
219
|
+
local skin_texture
|
|
220
|
+
|
|
221
|
+
username=$(echo "$response" | jq -r '.data.player.username')
|
|
222
|
+
online_uuid=$(echo "$response" | jq -r '.data.player.id')
|
|
223
|
+
raw_id=$(echo "$response" | jq -r '.data.player.raw_id')
|
|
224
|
+
avatar=$(echo "$response" | jq -r '.data.player.avatar')
|
|
225
|
+
skin_texture=$(echo "$response" | jq -r '.data.player.meta.name_history[0].name // empty' 2>/dev/null || echo "")
|
|
226
|
+
|
|
227
|
+
# Calculate offline UUID
|
|
228
|
+
local offline_uuid
|
|
229
|
+
offline_uuid=$(calculate_offline_uuid "$username")
|
|
230
|
+
|
|
231
|
+
if $json_output; then
|
|
232
|
+
cat <<EOF
|
|
233
|
+
{
|
|
234
|
+
"username": "$username",
|
|
235
|
+
"online_uuid": "$online_uuid",
|
|
236
|
+
"offline_uuid": "$offline_uuid",
|
|
237
|
+
"raw_id": "$raw_id",
|
|
238
|
+
"avatar": "$avatar"
|
|
239
|
+
}
|
|
240
|
+
EOF
|
|
241
|
+
else
|
|
242
|
+
echo -e "${BOLD}Player: ${CYAN}$username${NC}"
|
|
243
|
+
echo -e "Online UUID: ${GREEN}$online_uuid${NC}"
|
|
244
|
+
echo -e "Offline UUID: ${YELLOW}$offline_uuid${NC}"
|
|
245
|
+
echo -e "Avatar: $avatar"
|
|
246
|
+
fi
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
# UUID command - get specific UUID
|
|
250
|
+
cmd_uuid() {
|
|
251
|
+
local player_name=""
|
|
252
|
+
local offline_mode=false
|
|
253
|
+
|
|
254
|
+
while [[ $# -gt 0 ]]; do
|
|
255
|
+
case "$1" in
|
|
256
|
+
--offline)
|
|
257
|
+
offline_mode=true
|
|
258
|
+
shift
|
|
259
|
+
;;
|
|
260
|
+
-*)
|
|
261
|
+
error "Unknown option: $1"
|
|
262
|
+
return 1
|
|
263
|
+
;;
|
|
264
|
+
*)
|
|
265
|
+
if [[ -z "$player_name" ]]; then
|
|
266
|
+
player_name="$1"
|
|
267
|
+
else
|
|
268
|
+
error "Unexpected argument: $1"
|
|
269
|
+
return 1
|
|
270
|
+
fi
|
|
271
|
+
shift
|
|
272
|
+
;;
|
|
273
|
+
esac
|
|
274
|
+
done
|
|
275
|
+
|
|
276
|
+
if [[ -z "$player_name" ]]; then
|
|
277
|
+
error "Player name is required"
|
|
278
|
+
return 1
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
if $offline_mode; then
|
|
282
|
+
# Just calculate offline UUID (no API call needed)
|
|
283
|
+
calculate_offline_uuid "$player_name"
|
|
284
|
+
else
|
|
285
|
+
check_requirements || return 1
|
|
286
|
+
|
|
287
|
+
local response
|
|
288
|
+
response=$(fetch_player_info "$player_name") || return 1
|
|
289
|
+
|
|
290
|
+
echo "$response" | jq -r '.data.player.id'
|
|
291
|
+
fi
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
# =============================================================================
|
|
295
|
+
# Main
|
|
296
|
+
# =============================================================================
|
|
297
|
+
|
|
298
|
+
main() {
|
|
299
|
+
local command="${1:-}"
|
|
300
|
+
shift || true
|
|
301
|
+
|
|
302
|
+
case "$command" in
|
|
303
|
+
lookup)
|
|
304
|
+
cmd_lookup "$@"
|
|
305
|
+
;;
|
|
306
|
+
uuid)
|
|
307
|
+
cmd_uuid "$@"
|
|
308
|
+
;;
|
|
309
|
+
-h|--help|help)
|
|
310
|
+
usage
|
|
311
|
+
exit 0
|
|
312
|
+
;;
|
|
313
|
+
"")
|
|
314
|
+
error "No command specified"
|
|
315
|
+
usage
|
|
316
|
+
exit 1
|
|
317
|
+
;;
|
|
318
|
+
*)
|
|
319
|
+
error "Unknown command: $command"
|
|
320
|
+
usage
|
|
321
|
+
exit 1
|
|
322
|
+
;;
|
|
323
|
+
esac
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
# Run main if script is executed directly
|
|
327
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
328
|
+
main "$@"
|
|
329
|
+
fi
|