From 227e2b19b073b791af0d1bbf914d5f5f68356ffe Mon Sep 17 00:00:00 2001 From: M_Viper Date: Sat, 22 Nov 2025 16:58:18 +0000 Subject: [PATCH] minecraft-server-status.php aktualisiert --- minecraft-server-status.php | 1386 +++++++++++++++++------------------ 1 file changed, 693 insertions(+), 693 deletions(-) diff --git a/minecraft-server-status.php b/minecraft-server-status.php index 8fa6557..62a9d95 100644 --- a/minecraft-server-status.php +++ b/minecraft-server-status.php @@ -1,694 +1,694 @@ - admin_url('admin-ajax.php'), - 'refresh_interval' => max(10, intval(get_option('mcss_cache_ttl', 15))) * 1000 - ]); -}); - -/* ---------------- Settings registrieren ---------------- */ -add_action('admin_menu', function(){ - add_options_page('Minecraft Server', 'Minecraft Server', 'manage_options', 'mcss-settings', 'mcss_settings_page'); -}); - -add_action('admin_init', function(){ - register_setting('mcss_settings_group', 'mcss_host', ['sanitize_callback'=>'sanitize_text_field']); - register_setting('mcss_settings_group', 'mcss_rcon_port', ['sanitize_callback'=>'intval']); - register_setting('mcss_settings_group', 'mcss_rcon_pass', ['sanitize_callback'=>'sanitize_text_field']); - register_setting('mcss_settings_group', 'mcss_cache_ttl', ['sanitize_callback'=>'intval']); - register_setting('mcss_settings_group', 'mcss_show_motd', ['sanitize_callback'=>'boolval']); - register_setting('mcss_settings_group', 'mcss_ranks_json', ['sanitize_callback'=>'mcss_sanitize_ranks']); - register_setting('mcss_settings_group', 'mcss_server_logo_id', ['sanitize_callback'=>'absint']); - register_setting('mcss_settings_group', 'mcss_server_logo_url', ['sanitize_callback'=>'esc_url_raw']); - register_setting('mcss_settings_group', 'mcss_custom_text', ['sanitize_callback'=>'wp_kses_post']); - register_setting('mcss_settings_group', 'mcss_ip_color', ['default' => '#1f2937', 'sanitize_callback' => 'sanitize_hex_color']); - register_setting('mcss_settings_group', 'mcss_ct_color', ['default' => '#1e293b', 'sanitize_callback' => 'sanitize_hex_color']); - register_setting('mcss_settings_group', 'mcss_ip_size', ['default' => '1.5em', 'sanitize_callback' => 'sanitize_text_field']); - register_setting('mcss_settings_group', 'mcss_ct_size', ['default' => '1.05em', 'sanitize_callback' => 'sanitize_text_field']); - register_setting('mcss_settings_group', 'mcss_hide_port', ['default' => true, 'sanitize_callback' => 'rest_sanitize_boolean']); - register_setting('mcss_settings_group', 'mcss_player_port', ['default' => '', 'sanitize_callback' => 'sanitize_text_field']); -}); - -/* Sanitize Ranks */ -function mcss_sanitize_ranks($input) { - $decoded = json_decode($input, true); - if (!is_array($decoded)) return '[]'; - $out = []; - foreach ($decoded as $r) { - if (!is_array($r) || empty($r['name'])) continue; - $out[] = [ - 'name' => sanitize_text_field($r['name']), - 'groups' => sanitize_text_field($r['groups'] ?? ''), - 'color' => sanitize_text_field($r['color'] ?? '#6c5ce7') - ]; - } - return wp_json_encode($out); -} - -/* Settings Page */ -function mcss_settings_page() { - $logo_id = get_option('mcss_server_logo_id', 0); - $logo_url = get_option('mcss_server_logo_url', ''); - $current_logo = $logo_id ? wp_get_attachment_image_url($logo_id, 'medium') : $logo_url; - if (!$current_logo) $current_logo = MCSS_URL . 'img/default-server-logo.png'; - $custom_text = get_option('mcss_custom_text', ''); - $ip_color = get_option('mcss_ip_color', '#1f2937'); - $ct_color = get_option('mcss_ct_color', '#1e293b'); - $ip_size = get_option('mcss_ip_size', '1.5em'); - $ct_size = get_option('mcss_ct_size', '1.05em'); - $hide_port = get_option('mcss_hide_port', true); - $player_port = get_option('mcss_player_port', ''); - - $font_sizes = [ - '0.7em' => 'Sehr klein','0.85em' => 'Klein','1em' => 'Normal','1.2em' => 'Etwas größer', - '1.4em' => 'Groß','1.5em' => 'Sehr groß (Standard)','1.7em' => 'Extra groß','2em' => 'Riesig', - '2.5em' => 'Enorm','3em' => 'Gigantisch', - ]; - ?> -
-

Minecraft Server Einstellungen

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Server Host
RCON Port
RCON Passwort
Cache TTL (Sekunden)
MOTD anzeigen? />
- -

Wird nicht angezeigt, aber beim Klick mitkopiert.
Leer = kein Port wird kopiert.

-
Port in Adresse ausblenden? />
-
- -
- - - - -
- -

HTML + Emojis erlaubt.

-
IP-Adresse – Schrift - - - - -
Zusatztext – Schrift - - - - -
- -

Ränge (LuckPerms Zuordnung)

-

Die Ränge werden automatisch über LuckPerms per RCON abgerufen.

-
- - - -
- -

Tools

-

RCON testen

- -
- -
- - - 'Host oder Passwort nicht gesetzt']; - $rcon = new Rcon($host, $port, $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); -} - -/* ---------------- LuckPerms helper ---------------- */ -function mcss_fetch_luckperms_groups() { - $host = get_option('mcss_host', ''); - $port = intval(get_option('mcss_rcon_port', 25575)); - $pass = get_option('mcss_rcon_pass', ''); - if (empty($host) || empty($pass)) return []; - - $cache_key = 'mcss_lp_groups_' . md5($host . ':' . $port); - $cached = get_transient($cache_key); - if ($cached !== false) return $cached; - - $rcon = new Rcon($host, $port, $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; -} - -/* ---------------- UUID-Resolver & Avatar ---------------- */ -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, - 'user-agent' => 'MCSS-Plugin/1.9' - ]); - - 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', 24 * HOUR_IN_SECONDS); - 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}"; -} - -/* ---------------- Hauptfunktion – PREFIX ALS RANG! ---------------- */ -add_action('wp_ajax_nopriv_mcss_fetch', 'mcss_ajax_fetch'); -add_action('wp_ajax_mcss_fetch', 'mcss_ajax_fetch'); -function mcss_ajax_fetch() { - wp_send_json(mcss_fetch_server_with_ranks()); -} - -function mcss_fetch_server_with_ranks() { - $host = get_option('mcss_host', ''); - if (empty($host)) return ['online'=>false,'players'=>[],'address'=>'','version'=>'Unbekannt','ping'=>0,'motd'=>'','last_update'=>time()]; - - $port = intval(get_option('mcss_rcon_port', 25575)); - $pass = get_option('mcss_rcon_pass', ''); - $ttl = max(5, intval(get_option('mcss_cache_ttl', 15))); - $show_motd = (bool)get_option('mcss_show_motd', true); - - $transient_key = 'mcss_data_' . md5($host . ':' . $port); - $cached = get_transient($transient_key); - if ($cached !== false) { - $cached['last_update'] = get_option($transient_key . '_time', time()); - return $cached; - } - - $result = [ - 'online' => false, - 'players' => [], - 'address' => $host . ':' . $port, - 'version' => 'Unbekannt', - 'ping' => 0, - 'motd' => '', - 'last_update' => time() - ]; - - $raw_players = []; - - if (!empty($pass)) { - $rcon = new Rcon($host, $port, $pass, 3); - if ($rcon->connect()) { - $list_raw = $rcon->sendCommand('list'); - if (preg_match('/: (.*)$/', $list_raw, $m)) { - $list = trim($m[1]); - $entries = array_filter(array_map('trim', explode(',', $list))); - 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 = $port !== 25565 ? $host . ':' . $port : $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'])) { - $raw_players = []; - 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 ($show_motd && !empty($body['motd']['clean'])) { - $motd = is_array($body['motd']['clean']) ? implode(' ', $body['motd']['clean']) : $body['motd']['clean']; - $result['motd'] = $motd; - } - } - } - - $lp_groups = mcss_fetch_luckperms_groups(); - - $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 extrahieren (z. B. "[Owner] M_Viper" → "[Owner]") - $prefix = ''; - if (preg_match('/^(\[[^\]]+\])/', $raw_entry, $m)) { - $prefix = $m[1]; - } - - // Rang = Prefix oder "Spieler" - $rank = $prefix ?: 'Spieler'; - $color = '#94a3b8'; // Default Spieler-Farbe - - // Farbe aus LuckPerms holen - 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($transient_key, $result, $ttl); - update_option($transient_key . '_time', time()); - return $result; -} - -/* ---------------- Player rank detection (vereinfacht) ---------------- */ -function mcss_get_player_rank($player, $ranks_map = []) { - return ['rank' => 'Spieler', 'color' => '#94a3b8']; -} - -/* ---------------- Shortcode ---------------- */ -add_shortcode('minecraft_status', 'mcss_server_card_shortcode'); -add_shortcode('minecraft_server_detail', 'mcss_server_card_shortcode'); - -function mcss_server_card_shortcode($atts = []) { - $data = mcss_fetch_server_with_ranks(); - - $logo_id = get_option('mcss_server_logo_id', 0); - $logo_url = get_option('mcss_server_logo_url', ''); - $logo = $logo_id ? wp_get_attachment_image_url($logo_id, 'full') : ($logo_url ?: ($atts['logo'] ?? MCSS_URL . 'img/default-server-logo.png')); - - $custom_text = wp_kses_post(get_option('mcss_custom_text', '')); - $ip_color = get_option('mcss_ip_color', '#1f2937'); - $ip_size = get_option('mcss_ip_size', '1.5em'); - $ct_color = get_option('mcss_ct_color', '#1e293b'); - $ct_size = get_option('mcss_ct_size', '1.05em'); - - $host = get_option('mcss_host', ''); - $player_port = trim(get_option('mcss_player_port', '')); - $hide_port = (bool)get_option('mcss_hide_port', true); - - $display_address = $host; - if (!$hide_port && $player_port !== '') { - $display_address .= ':' . $player_port; - } - - $copy_address = $host . ($player_port !== '' ? ':' . $player_port : ''); - - $user_defined_ranks = json_decode(get_option('mcss_ranks_json','[]'), true); - $lp_present = is_array($user_defined_ranks) && count($user_defined_ranks) > 0; - $top_rank_name = $lp_present ? ($user_defined_ranks[0]['name'] ?? 'Spieler') : 'Spieler'; - - $uid = md5($host . '|' . $player_port); - - ob_start(); ?> -
-
Adresse kopiert!
- -
-
- Logo -
-
- - - - - -
- - -
- - - - - -
- - - -
-
- -
-
Version:
- - -
Rang:
- -
Spieler:
- - -
Ping: ms
-
- -
-
- Spieler online: -
-
- - -
- <?php echo esc_attr($p['name']); ?> -
-
-
- - -
Keine Spieler online
- -
-
-
-
- - - admin_url('admin-ajax.php'), + 'refresh_interval' => max(10, intval(get_option('mcss_cache_ttl', 15))) * 1000 + ]); +}); + +/* ---------------- Settings registrieren ---------------- */ +add_action('admin_menu', function(){ + add_options_page('Minecraft Server', 'Minecraft Server', 'manage_options', 'mcss-settings', 'mcss_settings_page'); +}); + +add_action('admin_init', function(){ + register_setting('mcss_settings_group', 'mcss_host', ['sanitize_callback'=>'sanitize_text_field']); + register_setting('mcss_settings_group', 'mcss_rcon_port', ['sanitize_callback'=>'intval']); + register_setting('mcss_settings_group', 'mcss_rcon_pass', ['sanitize_callback'=>'sanitize_text_field']); + register_setting('mcss_settings_group', 'mcss_cache_ttl', ['sanitize_callback'=>'intval']); + register_setting('mcss_settings_group', 'mcss_show_motd', ['sanitize_callback'=>'boolval']); + register_setting('mcss_settings_group', 'mcss_ranks_json', ['sanitize_callback'=>'mcss_sanitize_ranks']); + register_setting('mcss_settings_group', 'mcss_server_logo_id', ['sanitize_callback'=>'absint']); + register_setting('mcss_settings_group', 'mcss_server_logo_url', ['sanitize_callback'=>'esc_url_raw']); + register_setting('mcss_settings_group', 'mcss_custom_text', ['sanitize_callback'=>'wp_kses_post']); + register_setting('mcss_settings_group', 'mcss_ip_color', ['default' => '#1f2937', 'sanitize_callback' => 'sanitize_hex_color']); + register_setting('mcss_settings_group', 'mcss_ct_color', ['default' => '#1e293b', 'sanitize_callback' => 'sanitize_hex_color']); + register_setting('mcss_settings_group', 'mcss_ip_size', ['default' => '1.5em', 'sanitize_callback' => 'sanitize_text_field']); + register_setting('mcss_settings_group', 'mcss_ct_size', ['default' => '1.05em', 'sanitize_callback' => 'sanitize_text_field']); + register_setting('mcss_settings_group', 'mcss_hide_port', ['default' => true, 'sanitize_callback' => 'rest_sanitize_boolean']); + register_setting('mcss_settings_group', 'mcss_player_port', ['default' => '', 'sanitize_callback' => 'sanitize_text_field']); +}); + +/* Sanitize Ranks */ +function mcss_sanitize_ranks($input) { + $decoded = json_decode($input, true); + if (!is_array($decoded)) return '[]'; + $out = []; + foreach ($decoded as $r) { + if (!is_array($r) || empty($r['name'])) continue; + $out[] = [ + 'name' => sanitize_text_field($r['name']), + 'groups' => sanitize_text_field($r['groups'] ?? ''), + 'color' => sanitize_text_field($r['color'] ?? '#6c5ce7') + ]; + } + return wp_json_encode($out); +} + +/* Settings Page */ +function mcss_settings_page() { + $logo_id = get_option('mcss_server_logo_id', 0); + $logo_url = get_option('mcss_server_logo_url', ''); + $current_logo = $logo_id ? wp_get_attachment_image_url($logo_id, 'medium') : $logo_url; + if (!$current_logo) $current_logo = MCSS_URL . 'img/default-server-logo.png'; + $custom_text = get_option('mcss_custom_text', ''); + $ip_color = get_option('mcss_ip_color', '#1f2937'); + $ct_color = get_option('mcss_ct_color', '#1e293b'); + $ip_size = get_option('mcss_ip_size', '1.5em'); + $ct_size = get_option('mcss_ct_size', '1.05em'); + $hide_port = get_option('mcss_hide_port', true); + $player_port = get_option('mcss_player_port', ''); + + $font_sizes = [ + '0.7em' => 'Sehr klein','0.85em' => 'Klein','1em' => 'Normal','1.2em' => 'Etwas größer', + '1.4em' => 'Groß','1.5em' => 'Sehr groß (Standard)','1.7em' => 'Extra groß','2em' => 'Riesig', + '2.5em' => 'Enorm','3em' => 'Gigantisch', + ]; + ?> +
+

Minecraft Server Einstellungen

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Server Host
RCON Port
RCON Passwort
Cache TTL (Sekunden)
MOTD anzeigen? />
+ +

Wird nicht angezeigt, aber beim Klick mitkopiert.
Leer = kein Port wird kopiert.

+
Port in Adresse ausblenden? />
+
+ +
+ + + + +
+ +

HTML + Emojis erlaubt.

+
IP-Adresse – Schrift + + + + +
Zusatztext – Schrift + + + + +
+ +

Ränge (LuckPerms Zuordnung)

+

Die Ränge werden automatisch über LuckPerms per RCON abgerufen.

+
+ + + +
+ +

Tools

+

RCON testen

+ +
+ +
+ + + 'Host oder Passwort nicht gesetzt']; + $rcon = new Rcon($host, $port, $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); +} + +/* ---------------- LuckPerms helper ---------------- */ +function mcss_fetch_luckperms_groups() { + $host = get_option('mcss_host', ''); + $port = intval(get_option('mcss_rcon_port', 25575)); + $pass = get_option('mcss_rcon_pass', ''); + if (empty($host) || empty($pass)) return []; + + $cache_key = 'mcss_lp_groups_' . md5($host . ':' . $port); + $cached = get_transient($cache_key); + if ($cached !== false) return $cached; + + $rcon = new Rcon($host, $port, $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; +} + +/* ---------------- UUID-Resolver & Avatar ---------------- */ +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, + 'user-agent' => 'MCSS-Plugin/1.9' + ]); + + 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', 24 * HOUR_IN_SECONDS); + 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}"; +} + +/* ---------------- Hauptfunktion – PREFIX ALS RANG! ---------------- */ +add_action('wp_ajax_nopriv_mcss_fetch', 'mcss_ajax_fetch'); +add_action('wp_ajax_mcss_fetch', 'mcss_ajax_fetch'); +function mcss_ajax_fetch() { + wp_send_json(mcss_fetch_server_with_ranks()); +} + +function mcss_fetch_server_with_ranks() { + $host = get_option('mcss_host', ''); + if (empty($host)) return ['online'=>false,'players'=>[],'address'=>'','version'=>'Unbekannt','ping'=>0,'motd'=>'','last_update'=>time()]; + + $port = intval(get_option('mcss_rcon_port', 25575)); + $pass = get_option('mcss_rcon_pass', ''); + $ttl = max(5, intval(get_option('mcss_cache_ttl', 15))); + $show_motd = (bool)get_option('mcss_show_motd', true); + + $transient_key = 'mcss_data_' . md5($host . ':' . $port); + $cached = get_transient($transient_key); + if ($cached !== false) { + $cached['last_update'] = get_option($transient_key . '_time', time()); + return $cached; + } + + $result = [ + 'online' => false, + 'players' => [], + 'address' => $host . ':' . $port, + 'version' => 'Unbekannt', + 'ping' => 0, + 'motd' => '', + 'last_update' => time() + ]; + + $raw_players = []; + + if (!empty($pass)) { + $rcon = new Rcon($host, $port, $pass, 3); + if ($rcon->connect()) { + $list_raw = $rcon->sendCommand('list'); + if (preg_match('/: (.*)$/', $list_raw, $m)) { + $list = trim($m[1]); + $entries = array_filter(array_map('trim', explode(',', $list))); + 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 = $port !== 25565 ? $host . ':' . $port : $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'])) { + $raw_players = []; + 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 ($show_motd && !empty($body['motd']['clean'])) { + $motd = is_array($body['motd']['clean']) ? implode(' ', $body['motd']['clean']) : $body['motd']['clean']; + $result['motd'] = $motd; + } + } + } + + $lp_groups = mcss_fetch_luckperms_groups(); + + $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 extrahieren (z. B. "[Owner] M_Viper" → "[Owner]") + $prefix = ''; + if (preg_match('/^(\[[^\]]+\])/', $raw_entry, $m)) { + $prefix = $m[1]; + } + + // Rang = Prefix oder "Spieler" + $rank = $prefix ?: 'Spieler'; + $color = '#94a3b8'; // Default Spieler-Farbe + + // Farbe aus LuckPerms holen + 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($transient_key, $result, $ttl); + update_option($transient_key . '_time', time()); + return $result; +} + +/* ---------------- Player rank detection (vereinfacht) ---------------- */ +function mcss_get_player_rank($player, $ranks_map = []) { + return ['rank' => 'Spieler', 'color' => '#94a3b8']; +} + +/* ---------------- Shortcode ---------------- */ +add_shortcode('minecraft_status', 'mcss_server_card_shortcode'); +add_shortcode('minecraft_server_detail', 'mcss_server_card_shortcode'); + +function mcss_server_card_shortcode($atts = []) { + $data = mcss_fetch_server_with_ranks(); + + $logo_id = get_option('mcss_server_logo_id', 0); + $logo_url = get_option('mcss_server_logo_url', ''); + $logo = $logo_id ? wp_get_attachment_image_url($logo_id, 'full') : ($logo_url ?: ($atts['logo'] ?? MCSS_URL . 'img/default-server-logo.png')); + + $custom_text = wp_kses_post(get_option('mcss_custom_text', '')); + $ip_color = get_option('mcss_ip_color', '#1f2937'); + $ip_size = get_option('mcss_ip_size', '1.5em'); + $ct_color = get_option('mcss_ct_color', '#1e293b'); + $ct_size = get_option('mcss_ct_size', '1.05em'); + + $host = get_option('mcss_host', ''); + $player_port = trim(get_option('mcss_player_port', '')); + $hide_port = (bool)get_option('mcss_hide_port', true); + + $display_address = $host; + if (!$hide_port && $player_port !== '') { + $display_address .= ':' . $player_port; + } + + $copy_address = $host . ($player_port !== '' ? ':' . $player_port : ''); + + $user_defined_ranks = json_decode(get_option('mcss_ranks_json','[]'), true); + $lp_present = is_array($user_defined_ranks) && count($user_defined_ranks) > 0; + $top_rank_name = $lp_present ? ($user_defined_ranks[0]['name'] ?? 'Spieler') : 'Spieler'; + + $uid = md5($host . '|' . $player_port); + + ob_start(); ?> +
+
Adresse kopiert!
+ +
+
+ Logo +
+
+ + + + + +
+ + +
+ + + + + +
+ + + +
+
+ +
+
Version:
+ + +
Rang:
+ +
Spieler:
+ + +
Ping: ms
+
+ +
+
+ Spieler online: +
+
+ + +
+ <?php echo esc_attr($p['name']); ?> +
+
+
+ + +
Keine Spieler online
+ +
+
+
+
+ + +