WiIIiam278 / PAPIProxyBridge

A bridge library plugin for using PlaceholderAPI on proxy servers
https://william278.net/project/papiproxybridge
Apache License 2.0
37 stars 11 forks source link

[Feature Request] Method to get all Servers with plugin on it. #43

Closed Andre601 closed 1 year ago

Andre601 commented 1 year ago

It would be really handy to have a method in the plugin to check what servers exist that have the plugin available (And are reachable).

This would allow me to do something similar to this:

// This would be run on BungeeCord
// plugin is a plugin instance and random a Random instance.
public String parsePapiPlaceholders(String text, UUID player){
    if(plugin.getProxy().getPluginManager().getPlugin("PAPIProxyBridge") == null)
        return text;

    PlaceholderAPI papi = plugin.getPAPI();

    // Returns a List<String> containing server names with
    // PAPIProxyBridge present and that are callable.
    List<String> servers = papi.findServers();

    if(servers.isEmpty())
        return text;

    // Just get the first from the list as ServerInfo
    ServerInfo server = plugin.getProxy().getServerInfo(servers.get(0));

    // Safety-check
    if(server == null || server.getPlayers().isEmpty())
        return text;

    UUID randomPlayer = server.getPlayers().get(random.nextInt(server.getPlayers().size()).getUniqueId();

    try{
        CompletableFuture<String> future = papi.formatPlaceholders(text, randomPlayer, player);
        return future.getNow(text);
    }catch(IllegalArgumentException ex){
        return text;
    }
}

Right now, the only alternative for me is to require players to set a List of servers that do have PAPIProxyBridge present, so that I can iterate through it, get a ServerInfo, check if it is valid and players are present and then try to do a call with a random player to carry the payload...

Having a way to obtain the servers that both are available and have the plugin on would already be a huge help in simplifying this (And it would reduce the human error of providing wrong info).

Andre601 commented 1 year ago

While waiting for this to hopefully be added in the future have I currently set up this: https://paste.helpch.at/abemasehac.java

I'm not sure if that is really the best aproach, but I can't think of any other solution right now that wouldn't require a own bridge plugin to have... I could do some caching perhaps, but I feel like this would only add more issues (i.e. cache holding outdated data).

I'm happy for some input on this.

Andre601 commented 1 year ago

Implemented this now into the plugin. I think that's the best way to do it. Especially when making sure the right Plugin version is given:

Method in main event class

    @Override
    public String parsePAPIPlaceholders(String text, VelocityPlayerImpl player){
        if(!plugin.getProxy().getPluginManager().isLoaded("papiproxybridge"))
            return text;

        if(!PingEventHandler.compatiblePAPI())
            return text;

        String server = PingEventHandler.getRandomServer();
        if(server == null || server.isEmpty())
            return text;

        RegisteredServer registeredServer = plugin.getProxy().getServer(server).orElse(null);
        if(registeredServer == null || registeredServer.getPlayersConnected().isEmpty())
            return text;

        Player carrier = PingEventHandler.getRandomPlayer(registeredServer.getPlayersConnected());
        if(carrier == null)
            return text;

        return PingEventHandler.parsePlaceholders(text, carrier.getUniqueId(), player.getUUID());
    }

Static methods in PingEventHandler class:

    // Making sure that the PAPI version we have actually does have the necessary methods...
    public static boolean compatiblePAPI(){
        try{
            Class.forName("net.william278.papiproxybridge.api.PlaceholderAPI").getMethod("findServers");
            return true;
        }catch(ClassNotFoundException | NoSuchMethodException ex){
            return false;
        }
    }

    public static String getRandomServer(){
        List<String> servers;
        try{
            servers = getPAPI().findServers().getNow(Collections.emptyList());
        }catch(CancellationException | CompletionException ex){
            servers = Collections.emptyList();
        }

        if(servers.isEmpty())
            return null;

        // No need to spin up random for just one entry.
        if(servers.size() == 1)
            return servers.get(0);

        synchronized(random){
            return servers.get(random.nextInt(servers.size()));
        }
    }

    public static <P> P getRandomPlayer(Collection<P> players){
        if(players.isEmpty())
            return null;

        // Convert to ArrayList for the getter.
        List<P> list = new ArrayList<>(players);

        synchronized(random){
            return list.get(random.nextInt(players.size()));
        }
    }

    public static String parsePlaceholders(String text, UUID carrier, UUID player){
        try{
            CompletableFuture<String> future = getPAPI().formatPlaceholders(text, carrier, player);
            // Return parsed, or just the text if not yet available.
            return future.getNow(text);
        }catch(IllegalArgumentException | CancellationException | CompletionException ex){
            return text;
        }
    }

    private static PlaceholderAPI getPAPI(){
        if(papi != null)
            return papi;

        return (papi = PlaceholderAPI.getInstance());
    }

Feedback welcome. I may consider not having servers randomly selected, but have the first one selected... Not sure tho.