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