Wird nicht angezeigt, aber beim Klick mitkopiert. Leer = kein Port wird kopiert.
Port in Adresse ausblenden?
/>
HTML + Emojis erlaubt.
IP-Adresse – Schrift
Zusatztext – Schrift
/>
Wenn aktiviert, wird eine Wartungsnachricht anstelle der Server-Informationen angezeigt.
HTML + Emojis erlaubt. Diese Nachricht wird angezeigt, wenn der Wartungsmodus aktiv ist.
/>
Wenn aktiviert, werden zeitlich begrenzte Ankündigungen über dem Widget angezeigt.
HTML + Emojis erlaubt. Diese Nachricht wird als Ankündigung angezeigt.
Die Ankündigung wird nur im angegebenen Zeitraum angezeigt. Leerlassen für dauerhafte Anzeige.
Bestimmt das Aussehen der Ankündigung.
Ränge (LuckPerms Zuordnung)
Die Ränge werden automatisch über LuckPerms per RCON abgerufen.
'Host oder Passwort nicht gesetzt'];
$rcon = new Rcon($srv['host'], $srv['rcon_port'], $srv['rcon_pass'], 3);
$out = ['connected' => false];
if ($rcon->connect()) {
$out['connected'] = true;
$out['list'] = $rcon->sendCommand('list');
$out['version_raw'] = $rcon->sendCommand('version');
$out['lp_listgroups'] = $rcon->sendCommand('lp listgroups');
$rcon->disconnect();
} else $out['error'] = 'RCON Verbindung fehlgeschlagen';
return $out;
}
function mcss_normalize_version($raw) {
$raw = trim((string)$raw);
if ($raw === '') return 'Unbekannt';
$raw = preg_replace('/^\s*v\s*/i', '', $raw);
$softwares = ['spigot','paper','bukkit'];
foreach ($softwares as $soft) {
if (stripos($raw, $soft) !== false) {
if (preg_match('/git-' . preg_quote($soft, '/') . '-(\d+\.\d+(?:\.\d+)?)/i', $raw, $m)) return ucfirst($soft) . ' ' . $m[1];
if (preg_match('/\b' . preg_quote($soft, '/') . '\b[^\d]{0,10}?(\d+\.\d+(?:\.\d+)?)/i', $raw, $m)) return ucfirst($soft) . ' ' . $m[1];
if (preg_match('/(\d+\.\d+(?:\.\d+)?)/', $raw, $m)) return ucfirst($soft) . ' ' . $m[1];
return ucfirst($soft) . ' Unbekannt';
}
}
if (preg_match('/\(mc:\s*(\d+\.\d+(?:\.\d+)?)\)/i', $raw, $m)) return 'Vanilla ' . $m[1];
if (preg_match('/minecraft\s+(\d+\.\d+(?:\.\d+)?)/i', $raw, $m)) return 'Vanilla ' . $m[1];
if (preg_match('/git-(spigot|paper|bukkit)-(\d+\.\d+(?:\.\d+)?)/i', $raw, $m)) return ucfirst($m[1]) . ' ' . $m[2];
if (preg_match('/(\d+\.\d+(?:\.\d+)?)/', $raw, $m)) return $m[1];
return trim($raw);
}
function mcss_fetch_luckperms_groups($srv) {
if (empty($srv['host']) || empty($srv['rcon_pass'])) return [];
$cache_key = 'mcss_lp_groups_' . md5($srv['host'].':'.$srv['rcon_port']);
$cached = get_transient($cache_key);
if ($cached !== false) return $cached;
$rcon = new Rcon($srv['host'], $srv['rcon_port'], $srv['rcon_pass'], 3);
if (!$rcon->connect()) return [];
$groups = [];
$out = $rcon->sendCommand('lp listgroups');
if ($out) {
if (preg_match('/(?:Groups?:\s*)(.+)/i', $out, $m)) {
$names = array_map('trim', explode(',', $m[1]));
foreach ($names as $n) if ($n !== '') $groups[] = $n;
} else {
$lines = preg_split("/\r?\n/", $out);
foreach ($lines as $ln) {
$ln = trim(preg_replace('/^[\-\d\.\s]+/', '', $ln));
if ($ln !== '') $groups[] = $ln;
}
}
}
if (empty($groups)) {
$out2 = $rcon->sendCommand('lp info');
if ($out2 && preg_match_all('/Groups:\s*(.+)/i', $out2, $mm)) {
$names = array_map('trim', explode(',', implode(',', $mm[1])));
foreach ($names as $n) if ($n !== '') $groups[] = $n;
}
}
$groups = array_values(array_unique(array_filter($groups)));
$group_infos = [];
foreach ($groups as $g) {
$info_raw = $rcon->sendCommand('lp group ' . $g . ' info');
$weight = 0; $prefix = null;
if ($info_raw) {
if (preg_match('/weight[:\s]*([\-]?\d+)/i', $info_raw, $wm)) $weight = intval($wm[1]);
elseif (preg_match('/priority[:\s]*([\-]?\d+)/i', $info_raw, $wm2)) $weight = intval($wm2[1]);
if (preg_match('/prefix[:\s]*([^\r\n]+)/i', $info_raw, $pm)) $prefix = trim($pm[1]);
elseif (preg_match('/display name[:\s]*([^\r\n]+)/i', $info_raw, $pm2)) $prefix = trim($pm2[1]);
}
$color = '#6c5ce7';
if ($prefix) { $c = mcss_color_from_prefix($prefix); if ($c) $color = $c; }
$group_infos[] = ['group'=>$g,'weight'=>$weight,'prefix'=>$prefix,'color'=>$color];
}
$rcon->disconnect();
usort($group_infos, fn($a,$b) => $b['weight'] <=> $a['weight']);
$ranks = [];
foreach ($group_infos as $gi) {
$ranks[] = ['name'=>$gi['group'],'groups'=>$gi['group'],'color'=>$gi['color'],'prefix'=>$gi['prefix']];
}
set_transient($cache_key, $ranks, 12 * HOUR_IN_SECONDS);
return $ranks;
}
function mcss_color_from_prefix($prefix) {
if (!$prefix) return null;
if (preg_match('/[§&]([0-9a-fk-or])/i', $prefix, $m)) {
$code = strtolower($m[1]);
$map = ['0'=>'#000000','1'=>'#0000AA','2'=>'#00AA00','3'=>'#00AAAA','4'=>'#AA0000','5'=>'#AA00AA','6'=>'#FFAA00',
'7'=>'#AAAAAA','8'=>'#555555','9'=>'#5555FF','a'=>'#55FF55','b'=>'#55FFFF','c'=>'#FF5555','d'=>'#FF55FF',
'e'=>'#FFFF55','f'=>'#FFFFFF'];
if (isset($map[$code])) return $map[$code];
}
if (preg_match('/#([0-9a-f]{6})/i', $prefix, $m2)) return '#' . $m2[1];
return null;
}
function mcss_get_uuid_from_name($name) {
if (empty($name) || !preg_match('/^[a-zA-Z0-9_]{3,16}$/', $name)) return false;
$key = 'mcss_uuid_' . strtolower($name);
$cached = get_transient($key);
if ($cached !== false && $cached !== 'invalid') return $cached;
$response = wp_remote_get("https://api.mojang.com/users/profiles/minecraft/" . rawurlencode($name), ['timeout'=>6]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
set_transient($key, 'invalid', 60);
return false;
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if (empty($body['id'])) {
set_transient($key, 'invalid', 86400);
return false;
}
$uuid = $body['id'];
set_transient($key, $uuid, 30*DAY_IN_SECONDS);
return $uuid;
}
function mcss_avatar($input, $size = 64) {
$size = intval($size);
if (preg_match('/^[0-9a-f]{32}$/i', $input) || preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $input)) {
$uuid = preg_replace('/-/', '', $input);
} else {
$uuid = mcss_get_uuid_from_name($input);
if (!$uuid) return "https://mc-heads.net/avatar/" . rawurlencode($input) . "/{$size}";
}
return "https://mc-heads.net/avatar/{$uuid}/{$size}";
}
/* ---------------- NEUE FUNKTION FÜR PING-MESSUNG ---------------- */
function mcss_ping_server($host, $port = 25565, $timeout = 2) {
$startTime = microtime(true);
// Versuche, eine TCP-Verbindung zum Server herzustellen
$socket = @fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$socket) {
return false; // Server ist nicht erreichbar
}
// Schließe die Verbindung
fclose($socket);
// Berechne die Ping-Zeit in Millisekunden
$endTime = microtime(true);
$ping = round(($endTime - $startTime) * 1000);
return $ping;
}
/* ---------------- NEUE FUNKTIONEN FÜR ANKÜNDIGUNGEN ---------------- */
function mcss_should_show_announcement($srv) {
// Prüfen, ob Ankündigungen aktiviert sind
if (empty($srv['announcement_enabled'])) {
return false;
}
// Prüfen, ob ein Text vorhanden ist
if (empty($srv['announcement_text'])) {
return false;
}
// Wenn kein Startdatum angegeben ist, immer anzeigen
if (empty($srv['announcement_start'])) {
return true;
}
$current_time = current_time('timestamp');
$start_time = strtotime($srv['announcement_start']);
// Wenn das Startdatum in der Zukunft liegt, nicht anzeigen
if ($start_time > $current_time) {
return false;
}
// Wenn kein Enddatum angegeben ist, nach Startdatum immer anzeigen
if (empty($srv['announcement_end'])) {
return true;
}
$end_time = strtotime($srv['announcement_end']);
// Prüfen, ob das Enddatum noch nicht erreicht ist
return $end_time >= $current_time;
}
function mcss_get_announcement_style($type) {
switch ($type) {
case 'warning':
return [
'bg' => '#fef3c7',
'border' => '#fbbf24',
'text' => '#92400e',
'icon' => '⚠️'
];
case 'success':
return [
'bg' => '#d1fae5',
'border' => '#10b981',
'text' => '#065f46',
'icon' => '✅'
];
case 'error':
return [
'bg' => '#fee2e2',
'border' => '#ef4444',
'text' => '#991b1b',
'icon' => '❌'
];
case 'info':
default:
return [
'bg' => '#dbeafe',
'border' => '#3b82f6',
'text' => '#1e40af',
'icon' => 'ℹ️'
];
}
}
/* ---------------- Fetch & AJAX – 100% wie dein Original ---------------- */
add_action('wp_ajax_mcss_fetch', 'mcss_ajax_fetch');
add_action('wp_ajax_nopriv_mcss_fetch', 'mcss_ajax_fetch');
function mcss_ajax_fetch() {
$id = sanitize_text_field($_POST['server_id'] ?? '');
$servers = get_option('mcss_servers', []);
foreach ($servers as $srv) {
if (($srv['id'] ?? '') === $id) {
wp_send_json(mcss_fetch_server_with_ranks($srv));
}
}
wp_send_json(['online'=>false,'players'=>[],'address'=>'','version'=>'Unbekannt','ping'=>0,'motd'=>'']);
}
function mcss_fetch_server_with_ranks($srv) {
$cache_key = 'mcss_data_' . md5($srv['host'].':'.$srv['rcon_port']);
$cached = get_transient($cache_key);
if ($cached !== false) {
$cached['last_update'] = get_option($cache_key.'_time', time());
return $cached;
}
$result = ['online'=>false,'players'=>[],'address'=>$srv['host'].':'.$srv['rcon_port'],'version'=>'Unbekannt','ping'=>0,'motd'=>'','last_update'=>time()];
$raw_players = [];
if (!empty($srv['rcon_pass'])) {
$rcon = new Rcon($srv['host'], $srv['rcon_port'], $srv['rcon_pass'], 3);
if ($rcon->connect()) {
$list_raw = $rcon->sendCommand('list');
if (preg_match('/: (.*)$/', $list_raw, $m)) {
$entries = array_filter(array_map('trim', explode(',', $m[1])));
foreach ($entries as $entry) {
if (preg_match('/\s+([a-zA-Z0-9_]{3,16})$/', $entry, $n)) {
$clean_name = $n[1];
} else {
$clean_name = trim(preg_replace('/^[^a-zA-Z0-9_]*/', '', $entry));
$clean_name = preg_replace('/[^a-zA-Z0-9_].*$/', '', $clean_name);
}
if ($clean_name && preg_match('/^[a-zA-Z0-9_]{3,16}$/', $clean_name)) {
$raw_players[] = ['name' => $clean_name, 'raw_entry' => $entry];
}
}
}
$result['online'] = true;
$result['version'] = mcss_normalize_version($rcon->sendCommand('version') ?? '');
$rcon->disconnect();
}
}
$api_host = $srv['query_port'] != 25565 ? $srv['host'].':'.$srv['query_port'] : $srv['host'];
$resp = wp_remote_get('https://api.mcsrvstat.us/2/'.rawurlencode($api_host), ['timeout'=>7]);
if (!is_wp_error($resp) && wp_remote_retrieve_response_code($resp)==200) {
$body = json_decode(wp_remote_retrieve_body($resp), true);
if (!empty($body['online'])) {
$result['online'] = true;
if (empty($raw_players) && !empty($body['players']['list']) && is_array($body['players']['list'])) {
foreach ($body['players']['list'] as $pl) {
if (!is_array($pl)) continue;
$name = $pl['name'] ?? '';
$uuid = $pl['uuid'] ?? '';
if ($name === '') continue;
$raw_players[] = ['name' => $name, 'uuid' => $uuid, 'raw_entry' => $name];
}
}
if ($result['version'] === 'Unbekannt') {
$candidate = $body['version'] ?? $body['software'] ?? '';
if ($candidate) $result['version'] = mcss_normalize_version($candidate);
}
$result['ping'] = intval($body['debug']['ping'] ?? $body['ping'] ?? 0);
if (!empty($srv['show_motd']) && !empty($body['motd']['clean'])) {
$motd = is_array($body['motd']['clean']) ? implode(' ', $body['motd']['clean']) : $body['motd']['clean'];
$result['motd'] = $motd;
}
}
}
// PING-MESSUNG - VERWENDE UNSERE EIGENE FUNKTION
$ping_port = !empty($srv['player_port']) ? $srv['player_port'] : $srv['query_port'];
$ping_result = mcss_ping_server($srv['host'], $ping_port, 2);
if ($ping_result !== false) {
$result['ping'] = $ping_result;
} else {
// Wenn unsere Ping-Messung fehlschlägt, versuche den Ping aus der API zu verwenden
if (!empty($body['debug']['ping'])) {
$result['ping'] = intval($body['debug']['ping']);
} elseif (!empty($body['ping'])) {
$result['ping'] = intval($body['ping']);
}
}
$lp_groups = mcss_fetch_luckperms_groups($srv);
$players_info = [];
foreach ($raw_players as $p) {
$name = $p['name'];
$uuid = $p['uuid'] ?? mcss_get_uuid_from_name($name);
$raw_entry = $p['raw_entry'] ?? $name;
$prefix = '';
if (preg_match('/^(\[[^\]]+\])/', $raw_entry, $m)) {
$prefix = $m[1];
}
$rank = $prefix ?: 'Spieler';
$color = '#94a3b8';
foreach ($lp_groups as $g) {
if ($g['prefix'] && stripos($prefix, $g['prefix']) !== false) {
$color = $g['color'];
break;
}
}
$players_info[] = [
'name' => $name,
'avatar' => mcss_avatar($uuid ?: $name, 64),
'rank' => $rank,
'color' => $color,
];
}
$result['players'] = $players_info;
$result['version'] = preg_replace('/^\s*v/i', '', trim($result['version']));
set_transient($cache_key, $result, $srv['cache_ttl']);
update_option($cache_key.'_time', time());
return $result;
}
/* ---------------- Shortcode – 100% DEIN ORIGINAL-DESIGN ---------------- */
add_shortcode('minecraft_status', 'mcss_server_card_shortcode');
add_shortcode('minecraft_server_detail', 'mcss_server_card_shortcode');
function mcss_server_card_shortcode($atts = []) {
$atts = shortcode_atts(['id' => ''], $atts);
if (empty($atts['id'])) return 'Fehler: id fehlt';
$servers = get_option('mcss_servers', []);
$srv = null;
foreach ($servers as $s) {
if (($s['id'] ?? '') === $atts['id'] || ($s['name'] ?? '') === $atts['id']) {
$srv = $s; break;
}
}
if (!$srv) return '
Server nicht gefunden
';
// Prüfen, ob der Wartungsmodus aktiviert ist
$maintenance_mode = !empty($srv['maintenance_mode']);
$maintenance_message = $srv['maintenance_message'] ?? 'Der Server befindet sich derzeit im Wartungsmodus. Wir sind bald wieder für dich da!';
// Prüfen, ob eine Ankündigung angezeigt werden soll
$show_announcement = mcss_should_show_announcement($srv);
$announcement_text = $srv['announcement_text'] ?? '';
$announcement_type = $srv['announcement_type'] ?? 'info';
$announcement_style = mcss_get_announcement_style($announcement_type);
// Wenn Wartungsmodus aktiv ist, nur die Wartungsansicht anzeigen
if ($maintenance_mode) {
$uid = md5($srv['host'] . '|' . ($srv['player_port'] ?? ''));
$logo = $srv['logo_id'] ? wp_get_attachment_image_url($srv['logo_id'], 'full') : ($srv['logo_url'] ?: MCSS_URL.'img/default-server-logo.png');
ob_start(); ?>
Wartungsmodus
Wartungshinweis:
Wir arbeiten daran, den Server so schnell wie möglich wieder verfügbar zu machen. Vielen Dank für deine Geduld!