MonitoringMinecraft MonitoringMinecraft

Fake Player Count

Плагин Velocity

Displays fake online players between two variables which can be set in the config.yml (velocity ONLY)

38 скачиваний 1 подписчик
Оцените первым

FakePlayerCount

A Velocity plugin that modifies the online player count displayed in the server list (multiplayer menu).
It adds a fluctuating fake player amount to the real player count, making your server appear more populated.

Features

  • Realistic player count faking – The fake count smoothly moves between a configurable minimum and maximum, avoiding sudden jumps.
  • Automatic config reloading – Changes to config.yml are detected and applied without restarting the server.
  • Velocity ONLY – Designed exclusively for the Velocity proxy; no Paper/Spigot installation needed.
  • Lightweight and efficient – Uses Velocity's scheduler and runs with minimal performance impact.

How it works

The plugin intercepts the ProxyPingEvent (server list ping) and adds a fake number of online players before the response is sent to the client.
The fake number is not static – it slowly drifts within the configured range, simulating natural player fluctuation.

Source code:


import com.google.inject.Inject;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.ServerPing;
import org.slf4j.Logger;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * FakePlayerCount
 *
 * Fakes the online player count shown in the server list (multiplayer menu)
 * by intercepting the ProxyPingEvent and modifying the player count before
 * sending it. The proxy itself uses this modified ping response, so it is
 * not a pure client-side display illusion but what the proxy actually returns.
 *
 * The fake number fluctuates realistically between a minimum and maximum
 * (config.yml), and config.yml is automatically reloaded whenever it is
 * modified.
 */
@Plugin(
        id = "fakeplayercount",
        name = "FakePlayerCount",
        version = "1.0.0",
        description = "Fakes the online player count of the proxy by a configurable, fluctuating value",
        authors = {"You"}
)
public class FakePlayerCount {

    private final ProxyServer server;
    private final Logger logger;
    private final Path dataDirectory;

    // Default values if config.yml cannot be read
    private int minFakePlayers = 15;
    private int maxFakePlayers = 25;
    private int maxPlayers = 100;

    // current fake extra count, moves via random walk between min and max
    private volatile int currentFakeExtra;

    private WatchService watchService;

    @Inject
    public FakePlayerCount(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
        this.server = server;
        this.logger = logger;
        this.dataDirectory = dataDirectory;
    }

    @Subscribe
    public void onProxyInitialize(ProxyInitializeEvent event) {
        loadConfig();
        setupConfigWatcher();

        // watches config.yml for changes and reloads if necessary
        server.getScheduler()
                .buildTask(this, this::pollConfigWatcher)
                .repeat(Duration.ofSeconds(2))
                .schedule();

        // lets the fake count fluctuate realistically between min and max
        server.getScheduler()
                .buildTask(this, this::tickFakeCount)
                .repeat(Duration.ofSeconds(10))
                .schedule();

        logger.info("FakePlayerCount loaded! Fake players fluctuate between " + minFakePlayers
                + " and " + maxFakePlayers + " (max-players: " + maxPlayers + ").");
    }

    // PostOrder.LAST ensures that this listener is guaranteed to run after all
    // other plugins, so that our fake count is not overwritten.
    @Subscribe(order = PostOrder.LAST)
    public void onProxyPing(ProxyPingEvent event) {
        int realOnline = server.getPlayerCount();
        int fakeOnline = realOnline + currentFakeExtra;

        // Max should be at least 1 greater than the displayed online count
        // and at the same time not fall below the configured value.
        int dynamicMax = fakeOnline + maxPlayers;

        ServerPing newPing = event.getPing().asBuilder()
                .onlinePlayers(fakeOnline)
                .maximumPlayers(dynamicMax)
                .build();

        event.setPing(newPing);
    }

    /**
     * Moves the current fake count in small, random steps within
     * [minFakePlayers, maxFakePlayers], instead of rolling completely new
     * on each tick. This looks more organic/"real" than a hard jump.
     */
    private void tickFakeCount() {
        if (minFakePlayers >= maxFakePlayers) {
            currentFakeExtra = minFakePlayers;
            return;
        }

        int range = maxFakePlayers - minFakePlayers;
        int maxStep = Math.max(1, range / 4);
        int delta = ThreadLocalRandom.current().nextInt(-maxStep, maxStep + 1);

        int updated = currentFakeExtra + delta;
        updated = Math.min(maxFakePlayers, Math.max(minFakePlayers, updated));
        currentFakeExtra = updated;
    }

    private void setupConfigWatcher() {
        try {
            watchService = FileSystems.getDefault().newWatchService();
            dataDirectory.register(
                    watchService,
                    StandardWatchEventKinds.ENTRY_MODIFY,
                    StandardWatchEventKinds.ENTRY_CREATE
            );
        } catch (IOException e) {
            logger.error("Could not set up config.yml watcher, automatic reloading is disabled.", e);
        }
    }

    private void pollConfigWatcher() {
        if (watchService == null) {
            return;
        }

        WatchKey key = watchService.poll();
        if (key == null) {
            return;
        }

        boolean configChanged = false;
        for (WatchEvent<?> watchEvent : key.pollEvents()) {
            Object context = watchEvent.context();
            if (context instanceof Path && ((Path) context).getFileName().toString().equals("config.yml")) {
                configChanged = true;
            }
        }

        key.reset();

        if (configChanged) {
            logger.info("config.yml was modified, reloading...");
            loadConfig();
        }
    }

    private void loadConfig() {
        try {
            if (!Files.exists(dataDirectory)) {
                Files.createDirectories(dataDirectory);
            }

            Path configFile = dataDirectory.resolve("config.yml");

            if (!Files.exists(configFile)) {
                String defaultConfig =
                        "# FakePlayerCount configuration\n" +
                                "#\n" +
                                "# min-fake-players / max-fake-players: range in which the\n" +
                                "# additional (fake) player count fluctuates realistically.\n" +
                                "min-fake-players: 15\n" +
                                "max-fake-players: 25\n" +
                                "\n" +
                                "# max-players: maximum player count displayed in the server list.\n" +
                                "max-players: 100\n";

                Files.writeString(configFile, defaultConfig);
                logger.info("config.yml was created with default values: " + configFile);
            }

            List<String> lines = Files.readAllLines(configFile);

            minFakePlayers = parseIntSetting(lines, "min-fake-players", 15);
            maxFakePlayers = parseIntSetting(lines, "max-fake-players", 25);
            maxPlayers = parseIntSetting(lines, "max-players", 100);

            if (minFakePlayers > maxFakePlayers) {
                logger.warn("min-fake-players is larger than max-fake-players, swapping values.");
                int tmp = minFakePlayers;
                minFakePlayers = maxFakePlayers;
                maxFakePlayers = tmp;
            }

            // adjust current value into the (new) valid range
            if (currentFakeExtra < minFakePlayers || currentFakeExtra > maxFakePlayers) {
                currentFakeExtra = minFakePlayers + (maxFakePlayers - minFakePlayers) / 2;
            }

        } catch (IOException e) {
            logger.error("Error loading/creating config.yml, using previous/default values.", e);
        }
    }

    private int parseIntSetting(List<String> lines, String key, int defaultValue) {
        Pattern pattern = Pattern.compile("^\\s*" + Pattern.quote(key) + "\\s*:\\s*(-?\\d+)\\s*(#.*)?$");

        for (String line : lines) {
            Matcher matcher = pattern.matcher(line);
            if (matcher.matches()) {
                return Integer.parseInt(matcher.group(1));
            }
        }

        logger.warn("Could not find '" + key + "' in config.yml, using default: " + defaultValue);
        return defaultValue;
    }
}
Смотри также

Похожие подборки плагины — по версиям Майнкрафта, загрузчикам и жанрам.

Сервера Майнкрафт

Играть интереснее на сервере — выбирай в рейтинге серверов Майнкрафт и заходи прямо сейчас.

SkyBars
SkyBars Java + BE
1215 онлайн
1.8 — 26.2 версия
🎮 ВЫЖИВАНИЕ ⚔️ АНАРХИЯ 🚗 ГТА РП 🎤 ГОЛОСОВОЙ ЧАТ 🎁 БЕСПЛАТНЫЙ ДОНАТ 🌟 СМП 💻 ПК+ТЕЛЕФОН
SparkTime
SparkTime Bedrock
132 онлайн
1.0 — 26.20 версия
SparkTime | Анархия на телефон
PazikCraft
11 онлайн
1.17 — 26.1.2 версия
Ванильное выживание со свадьбами, прокачкой боевых навыков и голосовым чатом
MigosMc
MigosMc Java + BE
1448 онлайн
1.8 — 26.2 версия
🌿 MigosMc.net | Гриферский сервер с войс-чатом | Награды за онлайн ⭐ ВЫЖИВАНИЕ⭐ ОДИНБЛОК⭐ МИНИ-ИГРЫ
MineLauncher
Лаунчер Майнкрафт без лицензии — все версии
Бесплатный лаунчер для ПК и Андроид — все версии 26.2, 1.21.11, 26.1.1, 26.1. Fabric, NeoForge, Forge, шейдеры, моды и скины в один клик.
Без лицензии Fabric, NeoForge, Forge Моды, шейдеры, скины Все версии Майнкрафта ПК и Андроид Для слабых ПК Сервера в лаунчере
Скачать бесплатно
Windows и Андроид · Бесплатно · Без лицензии
Наш чат