Skip to main content

Overview

The Maintenance Proxy API extends the base Maintenance API with additional functionality for managing maintenance mode on individual backend servers in a proxy network. This is available on BungeeCord and Velocity platforms.

Getting the Proxy API Instance

The proxy API is accessed through the same MaintenanceProvider, but cast to MaintenanceProxy:
import eu.kennytv.maintenance.api.MaintenanceProvider;
import eu.kennytv.maintenance.api.proxy.MaintenanceProxy;
import net.md_5.bungee.api.plugin.Plugin;

public class MyBungeePlugin extends Plugin {
    private MaintenanceProxy maintenanceProxy;

    @Override
    public void onEnable() {
        // Get the proxy API instance
        maintenanceProxy = (MaintenanceProxy) MaintenanceProvider.get();
        
        if (maintenanceProxy == null) {
            getLogger().warning("Maintenance plugin not loaded yet!");
            return;
        }
        
        getLogger().info("Successfully hooked into Maintenance v" + maintenanceProxy.getVersion());
    }
}
The MaintenanceProxy interface extends Maintenance, so you have access to all base API methods plus proxy-specific functionality.

Managing Per-Server Maintenance

Control maintenance mode for individual backend servers:
import eu.kennytv.maintenance.api.MaintenanceProvider;
import eu.kennytv.maintenance.api.proxy.MaintenanceProxy;
import eu.kennytv.maintenance.api.proxy.Server;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Command;

public class ServerMaintenanceCommand extends Command {
    private final MaintenanceProxy maintenanceProxy;
    
    public ServerMaintenanceCommand() {
        super("servermaintenance", "maintenance.admin", "sm");
        this.maintenanceProxy = (MaintenanceProxy) MaintenanceProvider.get();
    }
    
    @Override
    public void execute(CommandSender sender, String[] args) {
        if (maintenanceProxy == null) {
            sender.sendMessage("§cMaintenance plugin not available!");
            return;
        }
        
        if (args.length < 2) {
            sender.sendMessage("§cUsage: /servermaintenance <server> <on|off|status>");
            return;
        }
        
        String serverName = args[0];
        String action = args[1].toLowerCase();
        
        // Get the server wrapper
        Server server = maintenanceProxy.getServer(serverName);
        
        if (server == null) {
            sender.sendMessage("§cServer '" + serverName + "' not found!");
            return;
        }
        
        switch (action) {
            case "on" -> {
                boolean changed = maintenanceProxy.setMaintenanceToServer(server, true);
                if (changed) {
                    sender.sendMessage("§aMaintenance enabled for " + serverName);
                } else {
                    sender.sendMessage("§eMaintenance was already enabled for " + serverName);
                }
            }
            case "off" -> {
                boolean changed = maintenanceProxy.setMaintenanceToServer(server, false);
                if (changed) {
                    sender.sendMessage("§aMaintenance disabled for " + serverName);
                } else {
                    sender.sendMessage("§eMaintenance was already disabled for " + serverName);
                }
            }
            case "status" -> {
                boolean maintenance = maintenanceProxy.isMaintenance(server);
                sender.sendMessage("§7Maintenance on " + serverName + ": " + 
                    (maintenance ? "§cEnabled" : "§aDisabled"));
                
                if (maintenanceProxy.isServerTaskRunning(server)) {
                    sender.sendMessage("§eA timer task is currently running for this server");
                }
            }
            default -> sender.sendMessage("§cUsage: /servermaintenance <server> <on|off|status>");
        }
    }
}

Listing Servers and Maintenance Status

Get information about all registered servers:
import eu.kennytv.maintenance.api.MaintenanceProvider;
import eu.kennytv.maintenance.api.proxy.MaintenanceProxy;
import eu.kennytv.maintenance.api.proxy.Server;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Command;

import java.util.Set;

public class MaintenanceListCommand extends Command {
    private final MaintenanceProxy maintenanceProxy;
    
    public MaintenanceListCommand() {
        super("maintenancelist", "maintenance.admin", "mlist");
        this.maintenanceProxy = (MaintenanceProxy) MaintenanceProvider.get();
    }
    
    @Override
    public void execute(CommandSender sender, String[] args) {
        if (maintenanceProxy == null) {
            sender.sendMessage("§cMaintenance plugin not available!");
            return;
        }
        
        // Get all registered servers
        Set<String> servers = maintenanceProxy.getServers();
        
        // Get servers currently under maintenance
        Set<String> maintenanceServers = maintenanceProxy.getMaintenanceServers();
        
        sender.sendMessage("§7----- Maintenance Status -----");
        sender.sendMessage("§7Global Maintenance: " + 
            (maintenanceProxy.isMaintenance() ? "§cEnabled" : "§aDisabled"));
        sender.sendMessage("");
        sender.sendMessage("§7Server Status:");
        
        for (String serverName : servers) {
            Server server = maintenanceProxy.getServer(serverName);
            if (server == null) continue;
            
            boolean inMaintenance = maintenanceServers.contains(serverName);
            boolean hasPlayers = server.hasPlayers();
            
            sender.sendMessage("§7- " + serverName + ": " + 
                (inMaintenance ? "§cMaintenance" : "§aOnline") + 
                (hasPlayers ? " §7(" + "has players" + ")" : ""));
        }
        
        sender.sendMessage("§7Total: " + servers.size() + " servers, " + 
            maintenanceServers.size() + " in maintenance");
    }
}

Working with Server Objects

The Server interface provides information and actions for backend servers:
import eu.kennytv.maintenance.api.proxy.MaintenanceProxy;
import eu.kennytv.maintenance.api.proxy.Server;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;

public void manageServer(MaintenanceProxy proxy, String serverName) {
    // Get server wrapper
    Server server = proxy.getServer(serverName);
    
    if (server == null) {
        System.out.println("Server not found!");
        return;
    }
    
    // Get server information
    String name = server.getName();
    boolean hasPlayers = server.hasPlayers();
    boolean isRegistered = server.isRegisteredServer();
    
    System.out.println("Server: " + name);
    System.out.println("Has players: " + hasPlayers);
    System.out.println("Registered: " + isRegistered);
    
    // Broadcast a message to all players on the server
    if (hasPlayers) {
        Component message = Component.text("Server entering maintenance mode!")
            .color(NamedTextColor.RED);
        server.broadcast(message);
    }
    
    // Enable maintenance on the server
    proxy.setMaintenanceToServer(server, true);
}

public void getServerSafely(MaintenanceProxy proxy, String serverName) {
    // Use getServerOrDummy to always get a Server object
    // (useful when maintenance is disabled on a previously registered server)
    Server server = proxy.getServerOrDummy(serverName);
    
    if (!server.isRegisteredServer()) {
        System.out.println("Warning: " + serverName + " is not currently registered");
    }
}
The Server.isRegisteredServer() method will return false if maintenance is disabled on a previously registered server that has been removed from the proxy.

Handling ServerMaintenanceChangedEvent

Listen for per-server maintenance changes:
import eu.kennytv.maintenance.api.MaintenanceProvider;
import eu.kennytv.maintenance.api.event.manager.EventListener;
import eu.kennytv.maintenance.api.proxy.MaintenanceProxy;
import eu.kennytv.maintenance.api.event.proxy.ServerMaintenanceChangedEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.md_5.bungee.api.plugin.Plugin;

public class ServerMaintenanceListener extends Plugin {
    
    @Override
    public void onEnable() {
        MaintenanceProxy proxy = (MaintenanceProxy) MaintenanceProvider.get();
        
        if (proxy == null) {
            getLogger().warning("Maintenance plugin not available!");
            return;
        }
        
        // Register listener for per-server maintenance changes
        proxy.getEventManager().registerListener(
            new EventListener<ServerMaintenanceChangedEvent>() {
                @Override
                public void onEvent(ServerMaintenanceChangedEvent event) {
                    handleServerMaintenanceChange(event);
                }
            },
            ServerMaintenanceChangedEvent.class
        );
    }
    
    private void handleServerMaintenanceChange(ServerMaintenanceChangedEvent event) {
        var server = event.getServer();
        boolean maintenance = event.isMaintenance();
        
        if (!server.isRegisteredServer()) {
            getLogger().warning("Maintenance changed for unregistered server: " + server.getName());
            return;
        }
        
        if (maintenance) {
            getLogger().info("Server " + server.getName() + " entered maintenance mode");
            
            // Warn players on the server
            if (server.hasPlayers()) {
                Component warning = Component.text("This server is now in maintenance mode!")
                    .color(NamedTextColor.RED);
                server.broadcast(warning);
            }
        } else {
            getLogger().info("Server " + server.getName() + " exited maintenance mode");
            
            // Notify players
            if (server.hasPlayers()) {
                Component notice = Component.text("This server is now open!")
                    .color(NamedTextColor.GREEN);
                server.broadcast(notice);
            }
        }
    }
}

Complete Proxy Plugin Example

Here’s a complete BungeeCord plugin that integrates with the Maintenance Proxy API:
import eu.kennytv.maintenance.api.MaintenanceProvider;
import eu.kennytv.maintenance.api.event.MaintenanceChangedEvent;
import eu.kennytv.maintenance.api.event.MaintenanceReloadedEvent;
import eu.kennytv.maintenance.api.event.manager.EventListener;
import eu.kennytv.maintenance.api.event.proxy.ServerMaintenanceChangedEvent;
import eu.kennytv.maintenance.api.proxy.MaintenanceProxy;
import eu.kennytv.maintenance.api.proxy.Server;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.Plugin;

import java.util.Set;

public class ProxyMaintenanceIntegration extends Plugin {
    private MaintenanceProxy maintenanceProxy;
    
    @Override
    public void onEnable() {
        // Get the proxy API
        maintenanceProxy = (MaintenanceProxy) MaintenanceProvider.get();
        
        if (maintenanceProxy == null) {
            getLogger().severe("Maintenance plugin not found! Disabling...");
            return;
        }
        
        // Register event listeners
        registerEventListeners();
        
        // Register commands
        getProxy().getPluginManager().registerCommand(this, new MaintenanceInfoCommand());
        
        getLogger().info("Successfully hooked into Maintenance v" + maintenanceProxy.getVersion());
    }
    
    private void registerEventListeners() {
        var eventManager = maintenanceProxy.getEventManager();
        
        // Listen for global maintenance changes
        eventManager.registerListener(
            new EventListener<MaintenanceChangedEvent>() {
                @Override
                public void onEvent(MaintenanceChangedEvent event) {
                    onGlobalMaintenanceChanged(event.isMaintenance());
                }
            },
            MaintenanceChangedEvent.class
        );
        
        // Listen for per-server maintenance changes
        eventManager.registerListener(
            new EventListener<ServerMaintenanceChangedEvent>() {
                @Override
                public void onEvent(ServerMaintenanceChangedEvent event) {
                    onServerMaintenanceChanged(event);
                }
            },
            ServerMaintenanceChangedEvent.class
        );
        
        // Listen for config reloads
        eventManager.registerListener(
            new EventListener<MaintenanceReloadedEvent>() {
                @Override
                public void onEvent(MaintenanceReloadedEvent event) {
                    onConfigReloaded();
                }
            },
            MaintenanceReloadedEvent.class
        );
    }
    
    private void onGlobalMaintenanceChanged(boolean enabled) {
        if (enabled) {
            getLogger().info("Global maintenance mode enabled");
        } else {
            getLogger().info("Global maintenance mode disabled");
        }
    }
    
    private void onServerMaintenanceChanged(ServerMaintenanceChangedEvent event) {
        Server server = event.getServer();
        boolean maintenance = event.isMaintenance();
        
        getLogger().info("Server " + server.getName() + " maintenance: " + maintenance);
        
        // Broadcast to the affected server
        if (server.isRegisteredServer() && server.hasPlayers()) {
            Component message = maintenance
                ? Component.text("This server is now in maintenance mode").color(NamedTextColor.RED)
                : Component.text("This server is now operational").color(NamedTextColor.GREEN);
            server.broadcast(message);
        }
    }
    
    private void onConfigReloaded() {
        getLogger().info("Maintenance configuration reloaded");
        getLogger().info("Servers in maintenance: " + maintenanceProxy.getMaintenanceServers());
    }
    
    private class MaintenanceInfoCommand extends Command {
        public MaintenanceInfoCommand() {
            super("maintenanceinfo", "maintenance.info", "minfo");
        }
        
        @Override
        public void execute(CommandSender sender, String[] args) {
            if (maintenanceProxy == null) {
                sender.sendMessage("§cMaintenance API not available!");
                return;
            }
            
            sender.sendMessage("§7----- Maintenance Information -----");
            sender.sendMessage("§7Version: " + maintenanceProxy.getVersion());
            sender.sendMessage("§7Global Maintenance: " + 
                (maintenanceProxy.isMaintenance() ? "§cEnabled" : "§aDisabled"));
            
            Set<String> maintenanceServers = maintenanceProxy.getMaintenanceServers();
            sender.sendMessage("§7Servers in Maintenance: " + maintenanceServers.size());
            
            if (!maintenanceServers.isEmpty()) {
                sender.sendMessage("§7Maintenance Servers:");
                for (String serverName : maintenanceServers) {
                    Server server = maintenanceProxy.getServer(serverName);
                    if (server != null) {
                        sender.sendMessage("§7  - " + serverName + 
                            (server.hasPlayers() ? " §e(has players)" : ""));
                    }
                }
            }
            
            sender.sendMessage("§7Whitelisted Players: " + 
                maintenanceProxy.getSettings().getWhitelistedPlayers().size());
        }
    }
}

Redis Integration

When Redis is enabled in the Maintenance configuration, per-server maintenance status changes are automatically synchronized across all proxy instances in your network.
The proxy API automatically handles Redis synchronization:
public void enableMaintenanceWithRedis(MaintenanceProxy proxy, String serverName) {
    Server server = proxy.getServer(serverName);
    
    if (server == null) {
        return;
    }
    
    // This will automatically sync to Redis if enabled
    boolean changed = proxy.setMaintenanceToServer(server, true);
    
    if (changed) {
        System.out.println("Maintenance enabled for " + serverName);
        System.out.println("Status synchronized to Redis (if enabled)");
    }
}

Best Practices

Always verify server.isRegisteredServer() before performing operations on a server, especially when handling events.
The getServerOrDummy() method is useful for ensuring you always get a Server object, but remember to check isRegisteredServer() before using it.
When running on a proxy, listen for both MaintenanceChangedEvent (global) and ServerMaintenanceChangedEvent (per-server) to handle all scenarios.
Use server.broadcast() to warn players before enabling maintenance mode on their server.

Next Steps

Basic Usage

Learn basic API operations and usage

Event Listeners

Listen to maintenance events in your plugin

Build docs developers (and LLMs) love