MrNeznamy
Initializing Portal...

Užíváním, instalací nebo reloadem tohoto softwaru potvrzujete, že jste se seznámili s těmito podmínkami. Pokud s nimi nesouhlasíte, software okamžitě odinstalujte a přestaňte jej používat.

01 Sběr technických dat (Telemetrie)

Tento software automaticky odesílá technická metadat na server mrneznamy.eu. Tento sběr slouží výhradně pro technickou diagnostiku a optimalizaci.

  • Co sbíráme: Verze Javy/MC, OS, počet CPU jader, TPS (výkon) a počet hráčů.
  • Kdy: Standardně každých 5 minut. Kritické chyby (stack-traces) okamžitě. Při vypnutí serveru je odeslán signál pro okamžité odhlášení.
  • Inaktivita: Pokud server nepošle žádná data po dobu 1 hodiny, je automaticky považován za offline a vyřazen z aktivních statistik.
  • Soukromí: Data jsou anonymizovaná pro veřejné výstupy. Detailní logy nejsou nikdy sdíleny se třetími stranami.

02 Ochrana soukromí a Maskování

Naše pluginy obsahují bezpečnostní filtry, které před odesláním dat zamaskují citlivé informace. Pokud se v chybovém hlášení vyskytne IP adresa nebo lokální cesta (např. C:/User/Pavel/...), software ji automaticky nahradí řetězcem // PRIVATE INFORMATION //.

03 Ověřování licencí (Premium verze)

Placené (Premium) verze obsahují systém validace. Plugin se spojuje se serverem pro ověření legality licence. Pokud server licenci neuzná jako platnou, plugin se deaktivuje. Toto je nezbytná podmínka pro fungování placeného obsahu.

04 Ukončení provozu

Vzhledem k tomu, že se jedná o soukromý projekt, v případě ukončení činnosti autora (MrNeznamy.eu) může být funkčnost pluginů (zejména těch vázaných na licenci) omezena nebo ukončena, pokud nedojde k převodu práv na jiný subjekt. (Provozováno od roku 2023)

05 Akceptace a odmítnutí

  • Akceptace: Upozornění na tyto podmínky se zobrazí výhradně v konzoli serveru po jeho úplném načtení. Další používání softwaru považujeme za potvrzení, že s těmito technickými principy souhlasíte.
  • Odmítnutí: Pokud s těmito podmínkami nesouhlasíte, nemáte licenci k užívání tohoto softwaru a musíte jej odstranit.

By using, installing, or reloading this software, you confirm that you have read and agreed to these terms. If you do not agree, uninstall the software immediately and cease all use.

01 Technical Data Collection (Telemetry)

This software automatically transmits technical metadata to the mrneznamy.eu server. This collection is used exclusively for technical diagnostics and optimization.

  • What we collect: Java/MC versions, OS, CPU core count, TPS (performance), and player count.
  • When: Standard interval is every 5 minutes. Critical errors (stack-traces) are sent instantly. A signal is sent upon server shutdown for immediate disconnection.
  • Inactivity: If a server fails to report for more than 1 hour, it is automatically marked as offline and removed from active statistics.
  • Privacy: Data is anonymized for public outputs. Detailed logs are never shared with third parties.

02 Privacy Protection & Masking

Our plugins feature security filters that mask sensitive information before transmission. If an IP address or a local system path (e.g., C:/User/Name/...) is detected in an error report, it is automatically redacted and replaced with // PRIVATE INFORMATION //.

03 License Validation (Premium versions)

Paid (Premium) versions contain a validation system. The plugin connects to the server to verify license legality. If the server does not recognize the license as valid, the plugin will deactivate. This is a mandatory condition for the operation of paid content.

04 Cessation of Operations

As this is a private project, in the event of the cessation of the author's activity (MrNeznamy.eu), the functionality of the plugins (especially those tied to a license) may be limited or terminated, unless the rights are transferred to another entity. (Operating since 2023)

05 Acceptance and Rejection

  • Acceptance: Notice of these terms is displayed exclusively in the server console after the server has fully loaded. Continued use of the software is considered confirmation that you agree to these technical principles.
  • Rejection: If you do not agree with these terms, you do not have a license to use this software and must remove it.

MrNeznamyStats.java

Full 1:1 Implementation
eu.mrneznamy.mrlibcore.stats.MrNeznamyStats
package eu.mrneznamy.mrlibcore.stats;

import eu.mrneznamy.mrlibcore.MrLibRegisterPlugin;
import eu.mrneznamy.mrlibcore.scheduler.MrLibScheduler;
import eu.mrneznamy.mrlibcore.utils.MrLibColors;
import eu.mrneznamy.mrlibcore.utils.MrLibConsoleSayer;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;

import java.io.*;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.UUID;

/**
 * Custom analytics for MrNeznamy plugins.
 * Tracks active servers and reports automated crash reports.
 */
public class MrNeznamyStats {

    private static final String PING_URL = "https://dev.mrneznamy.eu/api/analytics/ping";
    private static final String ERROR_URL = "https://dev.mrneznamy.eu/api/analytics/report-error";
    private static final String SHUTDOWN_URL = "https://dev.mrneznamy.eu/api/analytics/shutdown";
    private static String serverHash;
    private static boolean accepted = false;
    private static final HttpClient client = HttpClient.newBuilder()
            .connectTimeout(java.time.Duration.ofSeconds(10))
            .build();

    /**
     * Initializes the statistics system.
     */
    public static void init(JavaPlugin core) {
        File file = new File(core.getDataFolder(), "server-id.txt");
        if (file.exists()) {
            accepted = true;
            serverHash = loadServerHash(file);
        } else {
            accepted = false;
            // Print notice to console after server has fully loaded
            MrLibScheduler.runTaskLater(core, () -> printPolicyToConsole(), 20 * 5);
        }
        
        // Start heartbeat ping task every 5 minutes
        MrLibScheduler.runTaskTimerAsynchronously(core, () -> {
            if (!accepted) return;
            for (MrLibRegisterPlugin.RegisteredPlugin registered : MrLibRegisterPlugin.getAllPlugins().values()) {
                sendPing(registered);
            }
        }, 20 * 10, 20 * 60 * 5);
    }

    public static void shutdown() {
        if (!accepted || serverHash == null) return;
        
        for (MrLibRegisterPlugin.RegisteredPlugin registered : MrLibRegisterPlugin.getAllPlugins().values()) {
            String slug = registered.getModrinthSlug();
            if (slug == null || slug.isEmpty()) slug = registered.getName().toLowerCase();
            
            String json = String.format("{\"plugin_slug\":\"%s\",\"server_hash\":\"%s\"}", 
                escapeJson(slug), escapeJson(serverHash));
            
            sendPostSync(SHUTDOWN_URL, json);
        }
    }

    public static void reportError(String pluginSlug, Throwable throwable, String message) {
        if (!accepted || serverHash == null) return;

        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        throwable.printStackTrace(pw);
        String stackTrace = sw.toString();

        String mcVersion = Bukkit.getBukkitVersion().split("-")[0];
        String javaVersion = System.getProperty("java.version");
        String msg = maskPrivateInfo(message != null ? message : throwable.toString());
        String maskedStackTrace = maskPrivateInfo(stackTrace);
        
        String json = String.format(
            "{\"plugin_slug\":\"%s\",\"message\":\"%s\",\"stack_trace\":\"%s\",\"java_version\":\"%s\",\"mc_version\":\"%s\"}",
            escapeJson(pluginSlug), escapeJson(msg), escapeJson(maskedStackTrace), escapeJson(javaVersion), escapeJson(mcVersion)
        );

        sendPost(ERROR_URL, json);
    }

    private static String maskPrivateInfo(String text) {
        if (text == null) return null;
        text = text.replaceAll("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b", "// PRIVATE INFORMATION //");
        text = text.replaceAll("(?i)[A-Z]:\\\\Users\\\\[^\\\\\\s]+", "// PRIVATE INFORMATION //");
        text = text.replaceAll("/home/[^/\\s]+", "// PRIVATE INFORMATION //");
        text = text.replaceAll("/Users/[^/\\s]+", "// PRIVATE INFORMATION //");
        return text;
    }

    private static void sendPostSync(String url, String json) {
        try {
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .header("Content-Type", "application/json")
                    .POST(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8))
                    .build();
            client.send(request, HttpResponse.BodyHandlers.ofString());
        } catch (Exception ignored) {}
    }

    private static void sendPost(String url, String json) {
        try {
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .header("Content-Type", "application/json")
                    .POST(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8))
                    .build();
            client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
        } catch (Exception e) {}
    }
}
ACCEPTED BY USAGE