Upload via GUI (138 Dateien)
This commit is contained in:
@@ -1,162 +1,180 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: AeroMC Freunde
|
||||
* Description: Backend für das Freundesystem des AeroMC Launchers - meldet, wer gerade online/im Spiel ist, verwaltet Freundschaftsanfragen, Einladungen, Kurznachrichten, per Code geteilte Instanzen und Fehlerberichte aus dem Absturz-Assistenten. Anmeldung über Mojangs Sitzungsprüfung (wie bei Minecraft-Servern) - ohne eigenes Passwort.
|
||||
* Version: 1.4.0
|
||||
* Author: M_Viper
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // kein direkter Aufruf der Datei
|
||||
}
|
||||
|
||||
define('AEROMC_FRIENDS_VERSION', '1.4.0');
|
||||
define('AEROMC_FRIENDS_PRESENCE_TABLE', 'aeromc_presence');
|
||||
define('AEROMC_FRIENDS_FRIENDS_TABLE', 'aeromc_friends');
|
||||
define('AEROMC_FRIENDS_INVITES_TABLE', 'aeromc_invites');
|
||||
define('AEROMC_FRIENDS_SESSIONS_TABLE', 'aeromc_sessions');
|
||||
define('AEROMC_FRIENDS_SHARES_TABLE', 'aeromc_shares');
|
||||
define('AEROMC_FRIENDS_MESSAGES_TABLE', 'aeromc_messages');
|
||||
define('AEROMC_FRIENDS_REPORTS_TABLE', 'aeromc_reports');
|
||||
|
||||
// Übergangsmodus: false = Anfragen ohne Anmeldung werden noch angenommen, aber
|
||||
// nur für Spieler, die sich noch NIE über die neue Anmeldung gemeldet haben
|
||||
// (ältere Launcher-Versionen funktionieren weiter). Sobald alle Nutzer
|
||||
// aktualisiert haben, in der wp-config.php auf true setzen:
|
||||
// define('AEROMC_FRIENDS_REQUIRE_AUTH', true);
|
||||
if (!defined('AEROMC_FRIENDS_REQUIRE_AUTH')) {
|
||||
define('AEROMC_FRIENDS_REQUIRE_AUTH', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Legt die beiden Tabellen an (bzw. bringt sie bei einem Update auf den
|
||||
* neuesten Stand - dbDelta erkennt bestehende Tabellen/Spalten selbst).
|
||||
*/
|
||||
function aeromc_friends_install() {
|
||||
global $wpdb;
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
$presence_table = $wpdb->prefix . AEROMC_FRIENDS_PRESENCE_TABLE;
|
||||
$friends_table = $wpdb->prefix . AEROMC_FRIENDS_FRIENDS_TABLE;
|
||||
$invites_table = $wpdb->prefix . AEROMC_FRIENDS_INVITES_TABLE;
|
||||
$sessions_table = $wpdb->prefix . AEROMC_FRIENDS_SESSIONS_TABLE;
|
||||
$shares_table = $wpdb->prefix . AEROMC_FRIENDS_SHARES_TABLE;
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
|
||||
// uuid ohne Bindestriche gespeichert (siehe aeromc_friends_normalize_uuid
|
||||
// in includes/routes.php) - vermeidet doppelte Zeilen durch
|
||||
// unterschiedliche Schreibweisen derselben UUID
|
||||
$sql_presence = "CREATE TABLE $presence_table (
|
||||
uuid varchar(32) NOT NULL,
|
||||
username varchar(32) NOT NULL,
|
||||
instance_name varchar(128) DEFAULT NULL,
|
||||
server_label varchar(128) DEFAULT NULL,
|
||||
mc_version varchar(32) DEFAULT NULL,
|
||||
last_seen datetime NOT NULL,
|
||||
PRIMARY KEY (uuid)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_presence);
|
||||
|
||||
$sql_friends = "CREATE TABLE $friends_table (
|
||||
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uuid_a varchar(32) NOT NULL,
|
||||
uuid_b varchar(32) NOT NULL,
|
||||
status varchar(20) NOT NULL DEFAULT 'pending',
|
||||
created_at datetime NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY uuid_a (uuid_a),
|
||||
KEY uuid_b (uuid_b)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_friends);
|
||||
|
||||
// Einladungen "Komm auf meinen Server" - gelten nur ein paar Minuten
|
||||
$sql_invites = "CREATE TABLE $invites_table (
|
||||
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
from_uuid varchar(32) NOT NULL,
|
||||
to_uuid varchar(32) NOT NULL,
|
||||
server varchar(128) NOT NULL,
|
||||
mc_version varchar(32) DEFAULT NULL,
|
||||
created_at datetime NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY to_uuid (to_uuid)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_invites);
|
||||
|
||||
// Anmelde-Sitzungen: nur der SHA-256 des Tokens wird gespeichert
|
||||
$sql_sessions = "CREATE TABLE $sessions_table (
|
||||
token_hash char(64) NOT NULL,
|
||||
uuid varchar(32) NOT NULL,
|
||||
username varchar(32) NOT NULL,
|
||||
created_at datetime NOT NULL,
|
||||
last_used datetime NOT NULL,
|
||||
PRIMARY KEY (token_hash),
|
||||
KEY uuid (uuid)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_sessions);
|
||||
|
||||
// per Code geteilte Instanzen (Mod-Liste, keine Dateien)
|
||||
$sql_shares = "CREATE TABLE $shares_table (
|
||||
code varchar(9) NOT NULL,
|
||||
uuid varchar(32) NOT NULL,
|
||||
manifest longtext NOT NULL,
|
||||
created_at datetime NOT NULL,
|
||||
PRIMARY KEY (code),
|
||||
KEY uuid (uuid)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_shares);
|
||||
|
||||
// Kurznachrichten zwischen Freunden (werden nach einigen Tagen gelöscht)
|
||||
$messages_table = $wpdb->prefix . AEROMC_FRIENDS_MESSAGES_TABLE;
|
||||
$sql_messages = "CREATE TABLE $messages_table (
|
||||
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
from_uuid varchar(32) NOT NULL,
|
||||
to_uuid varchar(32) NOT NULL,
|
||||
text varchar(400) NOT NULL,
|
||||
image_url varchar(255) DEFAULT NULL,
|
||||
created_at datetime NOT NULL,
|
||||
read_at datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY to_uuid (to_uuid),
|
||||
KEY from_uuid (from_uuid)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_messages);
|
||||
|
||||
// Fehlerberichte aus dem Absturz-Assistenten (Ansicht: Werkzeuge → AeroMC Fehlerberichte)
|
||||
$reports_table = $wpdb->prefix . AEROMC_FRIENDS_REPORTS_TABLE;
|
||||
$sql_reports = "CREATE TABLE $reports_table (
|
||||
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
created_at datetime NOT NULL,
|
||||
uuid varchar(32) DEFAULT NULL,
|
||||
username varchar(32) DEFAULT NULL,
|
||||
launcher_version varchar(32) DEFAULT NULL,
|
||||
mc_version varchar(32) DEFAULT NULL,
|
||||
loader varchar(32) DEFAULT NULL,
|
||||
loader_version varchar(64) DEFAULT NULL,
|
||||
system varchar(128) DEFAULT NULL,
|
||||
funde text,
|
||||
mods longtext,
|
||||
bericht longtext,
|
||||
kommentar text,
|
||||
status varchar(20) NOT NULL DEFAULT 'neu',
|
||||
PRIMARY KEY (id),
|
||||
KEY status (status)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_reports);
|
||||
|
||||
update_option('aeromc_friends_db_version', AEROMC_FRIENDS_VERSION);
|
||||
}
|
||||
register_activation_hook(__FILE__, 'aeromc_friends_install');
|
||||
|
||||
// Beim Ersetzen der Plugin-Dateien (Update per Upload) läuft der
|
||||
// Aktivierungs-Hook NICHT erneut - neue Spalten/Tabellen deshalb anlegen,
|
||||
// sobald die gespeicherte DB-Version nicht mehr zur Plugin-Version passt.
|
||||
add_action('plugins_loaded', function () {
|
||||
if (get_option('aeromc_friends_db_version') !== AEROMC_FRIENDS_VERSION) {
|
||||
aeromc_friends_install();
|
||||
}
|
||||
});
|
||||
|
||||
require_once plugin_dir_path(__FILE__) . 'includes/routes.php';
|
||||
if (is_admin()) {
|
||||
require_once plugin_dir_path(__FILE__) . 'includes/admin.php';
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: AeroMC Freunde
|
||||
* Description: Backend für das Freundesystem des AeroMC Launchers - meldet, wer gerade online/im Spiel ist, verwaltet Freundschaftsanfragen, Einladungen, Kurznachrichten, per Code geteilte Instanzen und Fehlerberichte aus dem Absturz-Assistenten sowie eigene Umhänge (auch HD) aus dem Skin-Editor. Anmeldung über Mojangs Sitzungsprüfung (wie bei Minecraft-Servern) - ohne eigenes Passwort.
|
||||
* Version: 1.5.0
|
||||
* Author: M_Viper
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // kein direkter Aufruf der Datei
|
||||
}
|
||||
|
||||
define('AEROMC_FRIENDS_VERSION', '1.5.0');
|
||||
define('AEROMC_FRIENDS_PRESENCE_TABLE', 'aeromc_presence');
|
||||
define('AEROMC_FRIENDS_FRIENDS_TABLE', 'aeromc_friends');
|
||||
define('AEROMC_FRIENDS_INVITES_TABLE', 'aeromc_invites');
|
||||
define('AEROMC_FRIENDS_SESSIONS_TABLE', 'aeromc_sessions');
|
||||
define('AEROMC_FRIENDS_SHARES_TABLE', 'aeromc_shares');
|
||||
define('AEROMC_FRIENDS_MESSAGES_TABLE', 'aeromc_messages');
|
||||
define('AEROMC_FRIENDS_REPORTS_TABLE', 'aeromc_reports');
|
||||
define('AEROMC_FRIENDS_CAPES_TABLE', 'aeromc_capes');
|
||||
|
||||
// Übergangsmodus: false = Anfragen ohne Anmeldung werden noch angenommen, aber
|
||||
// nur für Spieler, die sich noch NIE über die neue Anmeldung gemeldet haben
|
||||
// (ältere Launcher-Versionen funktionieren weiter). Sobald alle Nutzer
|
||||
// aktualisiert haben, in der wp-config.php auf true setzen:
|
||||
// define('AEROMC_FRIENDS_REQUIRE_AUTH', true);
|
||||
if (!defined('AEROMC_FRIENDS_REQUIRE_AUTH')) {
|
||||
define('AEROMC_FRIENDS_REQUIRE_AUTH', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Legt die beiden Tabellen an (bzw. bringt sie bei einem Update auf den
|
||||
* neuesten Stand - dbDelta erkennt bestehende Tabellen/Spalten selbst).
|
||||
*/
|
||||
function aeromc_friends_install() {
|
||||
global $wpdb;
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
$presence_table = $wpdb->prefix . AEROMC_FRIENDS_PRESENCE_TABLE;
|
||||
$friends_table = $wpdb->prefix . AEROMC_FRIENDS_FRIENDS_TABLE;
|
||||
$invites_table = $wpdb->prefix . AEROMC_FRIENDS_INVITES_TABLE;
|
||||
$sessions_table = $wpdb->prefix . AEROMC_FRIENDS_SESSIONS_TABLE;
|
||||
$shares_table = $wpdb->prefix . AEROMC_FRIENDS_SHARES_TABLE;
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
|
||||
// uuid ohne Bindestriche gespeichert (siehe aeromc_friends_normalize_uuid
|
||||
// in includes/routes.php) - vermeidet doppelte Zeilen durch
|
||||
// unterschiedliche Schreibweisen derselben UUID
|
||||
$sql_presence = "CREATE TABLE $presence_table (
|
||||
uuid varchar(32) NOT NULL,
|
||||
username varchar(32) NOT NULL,
|
||||
instance_name varchar(128) DEFAULT NULL,
|
||||
server_label varchar(128) DEFAULT NULL,
|
||||
mc_version varchar(32) DEFAULT NULL,
|
||||
last_seen datetime NOT NULL,
|
||||
PRIMARY KEY (uuid)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_presence);
|
||||
|
||||
$sql_friends = "CREATE TABLE $friends_table (
|
||||
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uuid_a varchar(32) NOT NULL,
|
||||
uuid_b varchar(32) NOT NULL,
|
||||
status varchar(20) NOT NULL DEFAULT 'pending',
|
||||
created_at datetime NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY uuid_a (uuid_a),
|
||||
KEY uuid_b (uuid_b)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_friends);
|
||||
|
||||
// Einladungen "Komm auf meinen Server" - gelten nur ein paar Minuten
|
||||
$sql_invites = "CREATE TABLE $invites_table (
|
||||
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
from_uuid varchar(32) NOT NULL,
|
||||
to_uuid varchar(32) NOT NULL,
|
||||
server varchar(128) NOT NULL,
|
||||
mc_version varchar(32) DEFAULT NULL,
|
||||
created_at datetime NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY to_uuid (to_uuid)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_invites);
|
||||
|
||||
// Anmelde-Sitzungen: nur der SHA-256 des Tokens wird gespeichert
|
||||
$sql_sessions = "CREATE TABLE $sessions_table (
|
||||
token_hash char(64) NOT NULL,
|
||||
uuid varchar(32) NOT NULL,
|
||||
username varchar(32) NOT NULL,
|
||||
created_at datetime NOT NULL,
|
||||
last_used datetime NOT NULL,
|
||||
PRIMARY KEY (token_hash),
|
||||
KEY uuid (uuid)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_sessions);
|
||||
|
||||
// per Code geteilte Instanzen (Mod-Liste, keine Dateien)
|
||||
$sql_shares = "CREATE TABLE $shares_table (
|
||||
code varchar(9) NOT NULL,
|
||||
uuid varchar(32) NOT NULL,
|
||||
manifest longtext NOT NULL,
|
||||
created_at datetime NOT NULL,
|
||||
PRIMARY KEY (code),
|
||||
KEY uuid (uuid)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_shares);
|
||||
|
||||
// Kurznachrichten zwischen Freunden (werden nach einigen Tagen gelöscht)
|
||||
$messages_table = $wpdb->prefix . AEROMC_FRIENDS_MESSAGES_TABLE;
|
||||
$sql_messages = "CREATE TABLE $messages_table (
|
||||
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
from_uuid varchar(32) NOT NULL,
|
||||
to_uuid varchar(32) NOT NULL,
|
||||
text varchar(400) NOT NULL,
|
||||
image_url varchar(255) DEFAULT NULL,
|
||||
created_at datetime NOT NULL,
|
||||
read_at datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY to_uuid (to_uuid),
|
||||
KEY from_uuid (from_uuid)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_messages);
|
||||
|
||||
// Fehlerberichte aus dem Absturz-Assistenten (Ansicht: Werkzeuge → AeroMC Fehlerberichte)
|
||||
$reports_table = $wpdb->prefix . AEROMC_FRIENDS_REPORTS_TABLE;
|
||||
$sql_reports = "CREATE TABLE $reports_table (
|
||||
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
created_at datetime NOT NULL,
|
||||
uuid varchar(32) DEFAULT NULL,
|
||||
username varchar(32) DEFAULT NULL,
|
||||
launcher_version varchar(32) DEFAULT NULL,
|
||||
mc_version varchar(32) DEFAULT NULL,
|
||||
loader varchar(32) DEFAULT NULL,
|
||||
loader_version varchar(64) DEFAULT NULL,
|
||||
system varchar(128) DEFAULT NULL,
|
||||
funde text,
|
||||
mods longtext,
|
||||
bericht longtext,
|
||||
kommentar text,
|
||||
status varchar(20) NOT NULL DEFAULT 'neu',
|
||||
PRIMARY KEY (id),
|
||||
KEY status (status)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_reports);
|
||||
|
||||
// eigene Umhänge (Bild liegt unter wp-content/uploads/aeromc-umhaenge)
|
||||
$capes_table = $wpdb->prefix . AEROMC_FRIENDS_CAPES_TABLE;
|
||||
$sql_capes = "CREATE TABLE $capes_table (
|
||||
uuid varchar(32) NOT NULL,
|
||||
username varchar(32) NOT NULL,
|
||||
datei varchar(64) NOT NULL DEFAULT '',
|
||||
breite smallint(5) unsigned NOT NULL DEFAULT 0,
|
||||
hoehe smallint(5) unsigned NOT NULL DEFAULT 0,
|
||||
hash varchar(40) NOT NULL DEFAULT '',
|
||||
status varchar(16) NOT NULL DEFAULT 'aktiv',
|
||||
updated_at datetime NOT NULL,
|
||||
PRIMARY KEY (uuid),
|
||||
KEY status (status)
|
||||
) $charset_collate;";
|
||||
dbDelta($sql_capes);
|
||||
|
||||
update_option('aeromc_friends_db_version', AEROMC_FRIENDS_VERSION);
|
||||
}
|
||||
register_activation_hook(__FILE__, 'aeromc_friends_install');
|
||||
|
||||
// Beim Ersetzen der Plugin-Dateien (Update per Upload) läuft der
|
||||
// Aktivierungs-Hook NICHT erneut - neue Spalten/Tabellen deshalb anlegen,
|
||||
// sobald die gespeicherte DB-Version nicht mehr zur Plugin-Version passt.
|
||||
add_action('plugins_loaded', function () {
|
||||
if (get_option('aeromc_friends_db_version') !== AEROMC_FRIENDS_VERSION) {
|
||||
aeromc_friends_install();
|
||||
}
|
||||
});
|
||||
|
||||
require_once plugin_dir_path(__FILE__) . 'includes/routes.php';
|
||||
require_once plugin_dir_path(__FILE__) . 'includes/capes.php';
|
||||
if (is_admin()) {
|
||||
require_once plugin_dir_path(__FILE__) . 'includes/admin.php';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
/**
|
||||
* AeroMC-Umhänge: eigene (auch HD-)Umhänge, die Spieler im Launcher gestalten.
|
||||
*
|
||||
* POST /aeromc/v1/cape { uuid, image } Umhang hochladen/ersetzen (Anmeldung Pflicht)
|
||||
* POST /aeromc/v1/cape/remove { uuid } eigenen Umhang entfernen (Anmeldung Pflicht)
|
||||
* GET /aeromc/v1/cape?uuid= Umhang eines Spielers (öffentlich)
|
||||
* GET /aeromc/v1/capes?uuids=a,b,c mehrere auf einmal, für Launcher und Mod (öffentlich)
|
||||
*
|
||||
* Bilder werden streng geprüft (echtes PNG, Seitenverhältnis 2:1, 64×32 bis
|
||||
* 1024×512) und – falls GD verfügbar ist – neu kodiert, damit keine fremden
|
||||
* Daten im Bild mitreisen. Der Dateiname enthält einen Teil der Prüfsumme, so
|
||||
* ändert sich die Adresse bei jedem neuen Umhang (kein veralteter Zwischenspeicher).
|
||||
* Admins können Umhänge unter "AeroMC Fehler → Umhänge" löschen oder sperren.
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
define('AEROMC_FRIENDS_CAPE_DIR', 'aeromc-umhaenge');
|
||||
define('AEROMC_FRIENDS_CAPE_MAX_BYTES', 3000000);
|
||||
define('AEROMC_FRIENDS_CAPE_MIN_GAP', 15); // Sekunden zwischen zwei Uploads
|
||||
define('AEROMC_FRIENDS_CAPE_MAX_UUIDS', 100); // je Sammelabfrage
|
||||
|
||||
function aeromc_friends_capes_table() {
|
||||
global $wpdb;
|
||||
return $wpdb->prefix . AEROMC_FRIENDS_CAPES_TABLE;
|
||||
}
|
||||
|
||||
function aeromc_friends_cape_dir() {
|
||||
$uploads = wp_upload_dir();
|
||||
return [
|
||||
'pfad' => trailingslashit($uploads['basedir']) . AEROMC_FRIENDS_CAPE_DIR,
|
||||
'url' => trailingslashit($uploads['baseurl']) . AEROMC_FRIENDS_CAPE_DIR,
|
||||
];
|
||||
}
|
||||
|
||||
/** Datei eines Umhangs löschen (nur Namen in unserem Schema). */
|
||||
function aeromc_friends_cape_delete_file($datei) {
|
||||
if (!$datei || !preg_match('/^[a-f0-9]{32}-[a-f0-9]{8}\.png$/', (string) $datei)) {
|
||||
return;
|
||||
}
|
||||
$ort = aeromc_friends_cape_dir();
|
||||
$voll = $ort['pfad'] . '/' . $datei;
|
||||
if (file_exists($voll)) {
|
||||
@unlink($voll);
|
||||
}
|
||||
}
|
||||
|
||||
/** Zeile -> öffentliche Angaben */
|
||||
function aeromc_friends_cape_info($r) {
|
||||
$ort = aeromc_friends_cape_dir();
|
||||
return [
|
||||
'url' => $ort['url'] . '/' . $r->datei,
|
||||
'width' => (int) $r->breite,
|
||||
'height' => (int) $r->hoehe,
|
||||
'hash' => $r->hash,
|
||||
'updated' => $r->updated_at,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ein Umhang-Bild und gibt [daten, breite, hoehe] zurück oder WP_Error.
|
||||
*/
|
||||
function aeromc_friends_cape_check_image($base64) {
|
||||
$daten = base64_decode(preg_replace('/^data:image\/png;base64,/', '', (string) $base64), true);
|
||||
if ($daten === false || strlen($daten) < 60) {
|
||||
return new WP_Error('aeromc_invalid', 'Ungültiges Bild.', ['status' => 400]);
|
||||
}
|
||||
if (strlen($daten) > AEROMC_FRIENDS_CAPE_MAX_BYTES) {
|
||||
return new WP_Error('aeromc_too_big', 'Der Umhang ist zu groß (Datei).', ['status' => 413]);
|
||||
}
|
||||
if (strncmp($daten, "\x89PNG\r\n\x1A\n", 8) !== 0 || substr($daten, 12, 4) !== 'IHDR') {
|
||||
return new WP_Error('aeromc_invalid', 'Nur PNG-Bilder.', ['status' => 400]);
|
||||
}
|
||||
$kopf = unpack('Nbreite/Nhoehe', substr($daten, 16, 8));
|
||||
$breite = (int) $kopf['breite'];
|
||||
$hoehe = (int) $kopf['hoehe'];
|
||||
if (!in_array($breite, [64, 128, 256, 512, 1024], true) || $breite !== 2 * $hoehe) {
|
||||
return new WP_Error('aeromc_invalid', 'Ein Umhang muss 64×32, 128×64, 256×128, 512×256 oder 1024×512 Pixel groß sein.', ['status' => 400]);
|
||||
}
|
||||
// neu kodieren: entfernt alles außer den Bildpunkten
|
||||
if (function_exists('imagecreatefromstring')) {
|
||||
$bild = @imagecreatefromstring($daten);
|
||||
if (!$bild || imagesx($bild) !== $breite || imagesy($bild) !== $hoehe) {
|
||||
return new WP_Error('aeromc_invalid', 'Das Bild ist beschädigt.', ['status' => 400]);
|
||||
}
|
||||
imagealphablending($bild, false);
|
||||
imagesavealpha($bild, true);
|
||||
ob_start();
|
||||
imagepng($bild, null, 9);
|
||||
$daten = ob_get_clean();
|
||||
imagedestroy($bild);
|
||||
}
|
||||
return [$daten, $breite, $hoehe];
|
||||
}
|
||||
|
||||
add_action('rest_api_init', function () {
|
||||
register_rest_route('aeromc/v1', '/cape', [
|
||||
[
|
||||
'methods' => 'POST',
|
||||
'callback' => 'aeromc_friends_route_cape_upload',
|
||||
'permission_callback' => '__return_true',
|
||||
],
|
||||
[
|
||||
'methods' => 'GET',
|
||||
'callback' => 'aeromc_friends_route_cape_get',
|
||||
'permission_callback' => '__return_true',
|
||||
],
|
||||
]);
|
||||
register_rest_route('aeromc/v1', '/cape/remove', [
|
||||
'methods' => 'POST',
|
||||
'callback' => 'aeromc_friends_route_cape_remove',
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
register_rest_route('aeromc/v1', '/capes', [
|
||||
'methods' => 'GET',
|
||||
'callback' => 'aeromc_friends_route_capes',
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
});
|
||||
|
||||
/** Anmeldung ist hier immer Pflicht – auch im Übergangsmodus (öffentlich sichtbare Bilder). */
|
||||
function aeromc_friends_cape_auth($request, $uuid) {
|
||||
$sitzung = aeromc_friends_check_auth($request, $uuid);
|
||||
if (is_wp_error($sitzung)) {
|
||||
return $sitzung;
|
||||
}
|
||||
if (!$sitzung) {
|
||||
return new WP_Error('aeromc_auth', 'Anmeldung erforderlich.', ['status' => 401]);
|
||||
}
|
||||
return $sitzung;
|
||||
}
|
||||
|
||||
function aeromc_friends_route_cape_upload($request) {
|
||||
global $wpdb;
|
||||
$uuid = aeromc_friends_normalize_uuid($request->get_param('uuid'));
|
||||
if (!$uuid) {
|
||||
return new WP_Error('aeromc_invalid', 'Ungültige uuid.', ['status' => 400]);
|
||||
}
|
||||
$sitzung = aeromc_friends_cape_auth($request, $uuid);
|
||||
if (is_wp_error($sitzung)) {
|
||||
return $sitzung;
|
||||
}
|
||||
$table = aeromc_friends_capes_table();
|
||||
$alt = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE uuid = %s", $uuid));
|
||||
if ($alt && $alt->status === 'gesperrt') {
|
||||
return new WP_Error('aeromc_blocked', 'Dein Umhang wurde vom Betreiber gesperrt.', ['status' => 403]);
|
||||
}
|
||||
if ($alt && time() - strtotime($alt->updated_at . ' UTC') < AEROMC_FRIENDS_CAPE_MIN_GAP) {
|
||||
return new WP_Error('aeromc_too_fast', 'Bitte einen Moment warten, bevor du den Umhang erneut hochlädst.', ['status' => 429]);
|
||||
}
|
||||
|
||||
$geprueft = aeromc_friends_cape_check_image($request->get_param('image'));
|
||||
if (is_wp_error($geprueft)) {
|
||||
return $geprueft;
|
||||
}
|
||||
list($daten, $breite, $hoehe) = $geprueft;
|
||||
$hash = sha1($daten);
|
||||
|
||||
$ort = aeromc_friends_cape_dir();
|
||||
if (!wp_mkdir_p($ort['pfad'])) {
|
||||
return new WP_Error('aeromc_error', 'Umhang konnte nicht gespeichert werden.', ['status' => 500]);
|
||||
}
|
||||
if (!file_exists($ort['pfad'] . '/index.html')) {
|
||||
file_put_contents($ort['pfad'] . '/index.html', '');
|
||||
}
|
||||
$datei = $uuid . '-' . substr($hash, 0, 8) . '.png';
|
||||
if (file_put_contents($ort['pfad'] . '/' . $datei, $daten) === false) {
|
||||
return new WP_Error('aeromc_error', 'Umhang konnte nicht gespeichert werden.', ['status' => 500]);
|
||||
}
|
||||
if ($alt && $alt->datei !== $datei) {
|
||||
aeromc_friends_cape_delete_file($alt->datei);
|
||||
}
|
||||
$zeile = [
|
||||
'uuid' => $uuid,
|
||||
'username' => $sitzung->username,
|
||||
'datei' => $datei,
|
||||
'breite' => $breite,
|
||||
'hoehe' => $hoehe,
|
||||
'hash' => $hash,
|
||||
'status' => 'aktiv',
|
||||
'updated_at' => current_time('mysql', true),
|
||||
];
|
||||
$wpdb->replace($table, $zeile);
|
||||
return ['ok' => true, 'cape' => aeromc_friends_cape_info((object) $zeile)];
|
||||
}
|
||||
|
||||
function aeromc_friends_route_cape_remove($request) {
|
||||
global $wpdb;
|
||||
$uuid = aeromc_friends_normalize_uuid($request->get_param('uuid'));
|
||||
if (!$uuid) {
|
||||
return new WP_Error('aeromc_invalid', 'Ungültige uuid.', ['status' => 400]);
|
||||
}
|
||||
$sitzung = aeromc_friends_cape_auth($request, $uuid);
|
||||
if (is_wp_error($sitzung)) {
|
||||
return $sitzung;
|
||||
}
|
||||
$table = aeromc_friends_capes_table();
|
||||
$alt = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE uuid = %s", $uuid));
|
||||
if ($alt) {
|
||||
aeromc_friends_cape_delete_file($alt->datei);
|
||||
if ($alt->status === 'gesperrt') {
|
||||
// Sperre bleibt bestehen, nur das Bild ist weg
|
||||
$wpdb->update($table, ['datei' => '', 'hash' => ''], ['uuid' => $uuid]);
|
||||
} else {
|
||||
$wpdb->delete($table, ['uuid' => $uuid]);
|
||||
}
|
||||
}
|
||||
return ['ok' => true];
|
||||
}
|
||||
|
||||
function aeromc_friends_route_cape_get($request) {
|
||||
global $wpdb;
|
||||
$uuid = aeromc_friends_normalize_uuid($request->get_param('uuid'));
|
||||
if (!$uuid) {
|
||||
return new WP_Error('aeromc_invalid', 'Ungültige uuid.', ['status' => 400]);
|
||||
}
|
||||
$table = aeromc_friends_capes_table();
|
||||
$r = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE uuid = %s AND status = 'aktiv'", $uuid));
|
||||
return ['ok' => true, 'cape' => $r ? aeromc_friends_cape_info($r) : null];
|
||||
}
|
||||
|
||||
function aeromc_friends_route_capes($request) {
|
||||
global $wpdb;
|
||||
$liste = array_filter(array_map('aeromc_friends_normalize_uuid', explode(',', (string) $request->get_param('uuids'))));
|
||||
$liste = array_slice(array_values(array_unique($liste)), 0, AEROMC_FRIENDS_CAPE_MAX_UUIDS);
|
||||
$ergebnis = [];
|
||||
if ($liste) {
|
||||
$table = aeromc_friends_capes_table();
|
||||
$platz = implode(',', array_fill(0, count($liste), '%s'));
|
||||
$rows = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT * FROM $table WHERE status = 'aktiv' AND uuid IN ($platz)", $liste
|
||||
));
|
||||
foreach ($rows as $r) {
|
||||
$ergebnis[$r->uuid] = aeromc_friends_cape_info($r);
|
||||
}
|
||||
}
|
||||
$antwort = new WP_REST_Response(['ok' => true, 'capes' => (object) $ergebnis]);
|
||||
// kurz zwischenspeichern lassen – der Mod fragt beim Betreten eines Servers viele auf einmal ab
|
||||
$antwort->header('Cache-Control', 'public, max-age=60');
|
||||
return $antwort;
|
||||
}
|
||||
|
||||
// ---------- Admin: Umhänge prüfen, löschen, sperren ----------
|
||||
if (is_admin()) {
|
||||
add_action('admin_menu', function () {
|
||||
add_submenu_page('aeromc-reports', 'AeroMC Umhänge', 'Umhänge', 'manage_options', 'aeromc-capes', 'aeromc_friends_capes_page');
|
||||
}, 20);
|
||||
}
|
||||
|
||||
function aeromc_friends_capes_page() {
|
||||
global $wpdb;
|
||||
if (!current_user_can('manage_options')) {
|
||||
return;
|
||||
}
|
||||
$table = aeromc_friends_capes_table();
|
||||
if (isset($_POST['aeromc_cape'], $_POST['aeromc_aktion']) && check_admin_referer('aeromc_capes')) {
|
||||
$uuid = aeromc_friends_normalize_uuid($_POST['aeromc_cape']);
|
||||
$r = $uuid ? $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE uuid = %s", $uuid)) : null;
|
||||
if ($r) {
|
||||
$aktion = (string) $_POST['aeromc_aktion'];
|
||||
if ($aktion === 'loeschen') {
|
||||
aeromc_friends_cape_delete_file($r->datei);
|
||||
$wpdb->delete($table, ['uuid' => $uuid]);
|
||||
echo '<div class="notice notice-success"><p>Umhang von ' . esc_html($r->username) . ' gelöscht.</p></div>';
|
||||
} elseif ($aktion === 'sperren') {
|
||||
aeromc_friends_cape_delete_file($r->datei);
|
||||
$wpdb->update($table, ['status' => 'gesperrt', 'datei' => '', 'hash' => ''], ['uuid' => $uuid]);
|
||||
echo '<div class="notice notice-success"><p>' . esc_html($r->username) . ' gesperrt – der Umhang ist entfernt, neue Uploads werden abgelehnt.</p></div>';
|
||||
} elseif ($aktion === 'entsperren') {
|
||||
$wpdb->delete($table, ['uuid' => $uuid]);
|
||||
echo '<div class="notice notice-success"><p>Sperre für ' . esc_html($r->username) . ' aufgehoben.</p></div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ort = aeromc_friends_cape_dir();
|
||||
$rows = $wpdb->get_results("SELECT * FROM $table ORDER BY updated_at DESC LIMIT 300");
|
||||
echo '<div class="wrap"><h1>AeroMC Umhänge</h1>';
|
||||
echo '<p>Eigene Umhänge, die Spieler im AeroMC-Skin-Editor hochgeladen haben. „Sperren“ entfernt den Umhang und verhindert neue Uploads dieses Spielers.</p>';
|
||||
if (!$rows) {
|
||||
echo '<p>Noch keine Umhänge hochgeladen.</p></div>';
|
||||
return;
|
||||
}
|
||||
echo '<table class="widefat striped"><thead><tr><th style="width:150px">Umhang</th><th>Spieler</th><th>Größe</th><th>Geändert (UTC)</th><th>Status</th><th></th></tr></thead><tbody>';
|
||||
foreach ($rows as $r) {
|
||||
$bild = $r->datei
|
||||
? '<img src="' . esc_url($ort['url'] . '/' . $r->datei) . '" alt="" style="width:128px;height:64px;image-rendering:pixelated;background:#ddd;border:1px solid #ccc" />'
|
||||
: '–';
|
||||
echo '<tr><td>' . $bild . '</td><td>' . esc_html($r->username) . '<br /><small>' . esc_html($r->uuid) . '</small></td>'
|
||||
. '<td>' . esc_html($r->breite . '×' . $r->hoehe) . '</td><td>' . esc_html($r->updated_at) . '</td><td>' . esc_html($r->status) . '</td><td>';
|
||||
echo '<form method="post" style="display:flex;gap:6px">';
|
||||
wp_nonce_field('aeromc_capes');
|
||||
echo '<input type="hidden" name="aeromc_cape" value="' . esc_attr($r->uuid) . '" />';
|
||||
if ($r->status === 'gesperrt') {
|
||||
echo '<button class="button" name="aeromc_aktion" value="entsperren">Sperre aufheben</button>';
|
||||
} else {
|
||||
echo '<button class="button" name="aeromc_aktion" value="loeschen" onclick="return confirm(\'Umhang löschen?\')">Löschen</button>';
|
||||
echo '<button class="button button-link-delete" name="aeromc_aktion" value="sperren" onclick="return confirm(\'Spieler sperren? Der Umhang wird entfernt und neue Uploads abgelehnt.\')">Sperren</button>';
|
||||
}
|
||||
echo '</form></td></tr>';
|
||||
}
|
||||
echo '</tbody></table></div>';
|
||||
}
|
||||
Reference in New Issue
Block a user