2
0
mirror of https://github.com/proxiodev/RedisBungee.git synced 2026-04-25 16:00:27 +00:00
## NOTES
data system shouldn't effect anybody, unless you do any direct query to
Redis query data, you should adapt the changes, by viewing classes
`ProxyDataManager` and `PlayerDataManager`

# Changes 
* RedisBungee is compiled with `java 17` now, Due java 11 support is
ending at end of September
* config version is now `2` which will reset your config if older
version
* Adventure API is included inside RedisBungee API
* new Language infrastructure for RedisBungee built-in messages #85
*commands not included yet*
* New data system which replaces Redis PubSub with Redis Streams *see
below*
* Ability to connect player to last server they where on using an config
option #84
* new environment variable `REDISBUNGEE_PROXY_ID` which can be set
before launch
* new environment variable `REDISBUNGEE_NETWORK_ID` which can be set
before launch
* RedisBungee requires redis version 6.2 or above  #88 
* Better command system
https://github.com/ProxioDev/RedisBungee/issues/93

## New data system
Due limitation of Redis PubSub in Cluster environment, Internals of
RedisBungee were changed to support Redis Streams
- Network Ids
  - networks ids used to group network proxies
    - example having 'test' network and 'main' network
- Networks in the same redis server / cluster share the same UUID cache
- Heartbeat system:
- RedisBungee old heartbeat system used hastset on redisbungee to store
the current unix time of the proxy to check what every proxy died or
not, now instead we publish the heartbeat using unix time, and online
count to proxy which proxy store it in their memory, which allow the
`get number of online players` to be faster than pooling whole list in
old data system.

- PubSub
- since redisbungee was initially designed with pubsub in mind,
registration no longer required now for event to fire, see the api
changes below.

## Commands System
* rewritten using [acf lib](https://github.com/aikar/commands) to be
platform independent
* new command `/rb` or `/redisbungee` with sub commands `help`, `info`,
'clean', 'show'.

* 'rb'
  * '/rb' and '/rb info'

![image](https://github.com/ProxioDev/RedisBungee/assets/34905970/70796ab0-b5fd-4578-8c93-c976e517df95)

  * '/rb show'
  

![image](https://github.com/ProxioDev/RedisBungee/assets/34905970/56332c37-701f-43e0-946b-6894b845fab3)

* configuration to disable or override each command from legacy to new
introduced one `/rb`
```yaml

# For redis bungee legacy commands
# either can be run using '/rbl glist' for example
# or if 'install' is set to true '/glist' can be used.
# 'install' also overrides the proxy installed commands
#
# In legacy commands each command got it own permissions since they had it own permission pre new command system,
# so it's also applied to subcommands in '/rbl'.
commands:
  # Permission redisbungee.legacy.use
  redisbungee-legacy:
    enabled: false
    subcommands:
        # Permission redisbungee.command.glist
        glist:
          enabled: false
          install: false
        # Permission redisbungee.command.find
        find:
          enabled: false
          install: false
        # Permission redisbungee.command.lastseen
        lastseen:
          enabled: false
          install: false
        # Permission redisbungee.command.ip
        ip:
          enabled: false
          install: false
        # Permission redisbungee.command.pproxy
        pproxy:
          enabled: false
          install: false
        # Permission redisbungee.command.sendtoall
        sendtoall:
          enabled: false
          install: false
        # Permission redisbungee.command.serverid
        serverid:
          enabled: false
          install: false
        # Permission redisbungee.command.serverids
        serverids:
          enabled: false
          install: false
       # Permission redisbungee.command.plist
        plist:
          enabled: false
          install: false
  # Permission redisbungee.command.use
  redisbungee:
    enabled: true
```

## API changes
- Kick api Deprecated: 
  - `kickPlayer(String playerName, String message) `
  - `kickPlayer(UUID playerUUID, String message) `

- newer where added using adventure api:
  - `kickPlayer(String playerName, Component message) `
  - `kickPlayer(UUID playerUUID, Component message) `

-  PubSub registration api Deprecated:
```java
/**
     * Register (a) PubSub channel(s), so that you may handle PubSubMessageEvent for it.
     *
     * @param channels the channels to register
     * @since 0.3
     * @deprecated No longer required
     */
    @Deprecated
    public final void registerPubSubChannels(String... channels) {
    }

    /**
     * Unregister (a) PubSub channel(s).
     *
     * @param channels the channels to unregister
     * @since 0.3
     * @deprecated No longer required
     */
    @Deprecated
    public final void unregisterPubSubChannels(String... channels) {
    }

```
# Contributors

* `summoncraft.us` for running this branch in production
* @SrBedrock for providing [Brazilian
Portuguese](https://en.wikipedia.org/wiki/Brazilian_Portuguese)
translation #87
 
# issues
closes #84 
closes #88 
closes #92 
closes #81
closes #93

---------

Signed-off-by: mohammed jasem alaajel <xrambad@gmail.com>
Co-authored-by: ThiagoROX <51332006+SrBedrock@users.noreply.github.com>
This commit is contained in:
2024-04-28 15:29:53 +04:00
committed by GitHub
parent 5c4de82714
commit 1593c2d628
88 changed files with 3715 additions and 3181 deletions

View File

@@ -0,0 +1,24 @@
plugins {
`java-library`
}
dependencies {
implementation(project(":RedisBungee-API"))
implementation(libs.acf.core)
}
description = "RedisBungee common commands"
tasks {
compileJava {
options.encoding = Charsets.UTF_8.name()
options.release.set(17)
}
javadoc {
options.encoding = Charsets.UTF_8.name()
}
processResources {
filteringCharset = Charsets.UTF_8.name()
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands;
import co.aikar.commands.CommandManager;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.legacy.LegacyRedisBungeeCommands;
public class CommandLoader {
public static void initCommands(CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) {
var commandsConfiguration = plugin.configuration().commandsConfiguration();
if (commandsConfiguration.redisbungeeEnabled()) {
commandManager.registerCommand(new CommandRedisBungee(plugin));
}
if (commandsConfiguration.redisbungeeLegacyEnabled()) {
commandManager.registerCommand(new LegacyRedisBungeeCommands(commandManager,plugin));
}
}
}

View File

@@ -0,0 +1,189 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.RegisteredCommand;
import co.aikar.commands.annotation.*;
import com.google.common.primitives.Ints;
import com.imaginarycode.minecraft.redisbungee.Constants;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
import com.imaginarycode.minecraft.redisbungee.commands.utils.StopperUUIDCleanupTask;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@CommandAlias("rb|redisbungee")
@CommandPermission("redisbungee.command.use")
@Description("Main command")
public class CommandRedisBungee extends AdventureBaseCommand {
private final RedisBungeePlugin<?> plugin;
public CommandRedisBungee(RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
}
@Default
@Subcommand("info|version|git")
@Description("information about current redisbungee build")
public void info(CommandIssuer issuer) {
final String message = """
<color:aqua>This proxy is running RedisBungee Limework's fork
<color:gold>========================================
<color:aqua>RedisBungee version: <color:green><version>
<color:aqua>Commit: <color:green><commit>
<color:gold>========================================
<color:gold>run /rb help for more commands""";
sendMessage(
issuer,
MiniMessage.miniMessage()
.deserialize(
message,
Placeholder.component("version", Component.text(Constants.VERSION)),
Placeholder.component(
"commit",
Component.text(Constants.GIT_COMMIT.substring(0, 8))
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, Constants.getGithubCommitLink()))
.hoverEvent(HoverEvent.showText(Component.text("Click me to open: " + Constants.getGithubCommitLink())))
)));
}
// <color:aqua>......: <color:green>......
@HelpCommand
@Description("shows the help page")
public void help(CommandIssuer issuer) {
final String barFormat = "<color:gold>========================================";
final String commandFormat = "<color:aqua>/rb <sub-command>: <color:green><description>";
TextComponent.Builder message = Component.text();
message.append(MiniMessage.miniMessage().deserialize(barFormat));
getSubCommands().forEach((subCommand, registeredCommand) -> {
String[] split = registeredCommand.getCommand().split(" ");
if (split.length > 1 && subCommand.equalsIgnoreCase(split[1])) {
message.appendNewline().append(MiniMessage.miniMessage().deserialize(commandFormat, Placeholder.component("sub-command", Component.text(subCommand)),
Placeholder.component("description", MiniMessage.miniMessage().deserialize(registeredCommand.getHelpText()))
));
}
});
message.appendNewline().append(MiniMessage.miniMessage().deserialize(barFormat));
sendMessage(issuer, message.build());
}
@Subcommand("clean")
@Description("cleans up the uuid cache<color:red> <bold>WARNING...</bold> <color:white>command above could cause performance issues")
@Private
public void cleanUp(CommandIssuer issuer) {
if (StopperUUIDCleanupTask.isRunning) {
sendMessage(issuer,
Component.text("cleanup is currently running!").color(NamedTextColor.RED));
return;
}
sendMessage(issuer,
Component.text("cleanup is Starting, you should see the output status in the proxy console").color(NamedTextColor.GOLD));
plugin.executeAsync(new StopperUUIDCleanupTask(plugin));
}
private List<Map.Entry<String, Integer>> subListProxies(List<Map.Entry<String, Integer>> data, final int currentPage, final int pageSize) {
return data.subList(((currentPage * pageSize) - pageSize), Ints.constrainToRange(currentPage * pageSize, 0, data.size()));
}
@Subcommand("show")
@Description("Shows proxies in this network")
public void showProxies(CommandIssuer issuer, String[] args) {
final String closer = "<color:gold>========================================";
final String pageTop = "<color:yellow>Page: <color:green><current>/<max> <color:yellow>Network ID: <color:green><network> <color:yellow>Proxies online: <color:green><proxies>";
final String proxy = "<color:yellow><proxy><here> : <color:green><players> online";
final String proxyHere = " (#) ";
final String nextPage = ">>>>>";
final String previousPage = "<<<<< ";
final String pageInvalid = "<color:red>invalid page";
final String noProxies = "<color:red>No proxies were found :(";
final int pageSize = 16;
int currentPage;
if (args.length > 0) {
try {
currentPage = Integer.parseInt(args[0]);
if (currentPage < 1) currentPage = 1;
} catch (NumberFormatException e) {
sendMessage(issuer, MiniMessage.miniMessage().deserialize(pageInvalid));
return;
}
} else currentPage = 1;
var data = new ArrayList<>(plugin.proxyDataManager().eachProxyCount().entrySet());
// there is no way this runs because there is always an heartbeat.
// if not could be some shenanigans done by devs :P
if (data.isEmpty()) {
sendMessage(issuer, MiniMessage.miniMessage().deserialize(noProxies));
return;
}
// compute the total pages
int maxPages = (int) Math.ceil(data.size() / (double) pageSize);
if (currentPage > maxPages) currentPage = maxPages;
var subList = subListProxies(data, currentPage, pageSize);
TextComponent.Builder builder = Component.text();
builder.append(MiniMessage.miniMessage().deserialize(closer)).appendNewline();
builder.append(MiniMessage.miniMessage().deserialize(pageTop,
Placeholder.component("current", Component.text(currentPage)),
Placeholder.component("max", Component.text(maxPages)),
Placeholder.component("network", Component.text(plugin.proxyDataManager().networkId())),
Placeholder.component("proxies", Component.text(data.size()))
)).appendNewline();
int left = pageSize;
for (Map.Entry<String, Integer> entrySet : subList) {
builder.append(MiniMessage.miniMessage().deserialize(proxy,
Placeholder.component("proxy", Component.text(entrySet.getKey())),
Placeholder.component("here", Component.text(plugin.proxyDataManager().proxyId().equals(entrySet.getKey()) ? proxyHere : "")),
Placeholder.component("players", Component.text(entrySet.getValue()))
)).appendNewline();
left--;
}
while(left > 0) {
builder.appendNewline();
left--;
}
if (currentPage > 1) {
builder.append(MiniMessage.miniMessage().deserialize(previousPage)
.color(NamedTextColor.WHITE).clickEvent(ClickEvent.runCommand("/rb show " + (currentPage - 1))));
} else {
builder.append(MiniMessage.miniMessage().deserialize(previousPage).color(NamedTextColor.GRAY));
}
if (subList.size() == pageSize && !subListProxies(data, currentPage + 1, pageSize).isEmpty()) {
builder.append(MiniMessage.miniMessage().deserialize(nextPage)
.color(NamedTextColor.WHITE).clickEvent(ClickEvent.runCommand("/rb show " + (currentPage + 1))));
} else {
builder.append(MiniMessage.miniMessage().deserialize(nextPage).color(NamedTextColor.GRAY));
}
builder.appendNewline();
builder.append(MiniMessage.miniMessage().deserialize(closer));
sendMessage(issuer, builder.build());
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("find|rfind")
@CommandPermission("redisbungee.command.find")
public class CommandFind extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandFind(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void find(CommandIssuer issuer, String[] args) {
rootCommand.find(issuer, args);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("glist|rglist")
@CommandPermission("redisbungee.command.glist")
public class CommandGList extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandGList(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void gList(CommandIssuer issuer, String[] args) {
rootCommand.gList(issuer, args);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("ip|playerip|rip|rplayerip")
@CommandPermission("redisbungee.command.ip")
public class CommandIp extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandIp(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void ip(CommandIssuer issuer, String[] args) {
this.rootCommand.ip(issuer, args);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("lastseen|rlastseend")
@CommandPermission("redisbungee.command.lastseen")
public class CommandLastSeen extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandLastSeen(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void lastSeen(CommandIssuer issuer, String[] args) {
this.rootCommand.lastSeen(issuer,args);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("pproxy")
@CommandPermission("redisbungee.command.pproxy")
public class CommandPProxy extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandPProxy(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void playerProxy(CommandIssuer issuer, String[] args) {
this.rootCommand.playerProxy(issuer,args);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("plist|rplist")
@CommandPermission("redisbungee.command.plist")
public class CommandPlist extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandPlist(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void playerList(CommandIssuer issuer, String[] args) {
this.rootCommand.playerList(issuer, args);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("sendtoall|rsendtoall")
@CommandPermission("redisbungee.command.sendtoall")
public class CommandSendToAll extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandSendToAll(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void sendToAll(CommandIssuer issuer, String[] args) {
this.rootCommand.sendToAll(issuer, args);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("serverid|rserverid")
@CommandPermission("redisbungee.command.serverid")
public class CommandServerId extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandServerId(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void serverId(CommandIssuer issuer) {
this.rootCommand.serverId(issuer);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
@CommandAlias("serverids|rserverids")
@CommandPermission("redisbungee.command.serverids")
public class CommandServerIds extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandServerIds(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void serverIds(CommandIssuer issuer) {
this.rootCommand.serverIds(issuer);
}
}

View File

@@ -0,0 +1,260 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.CommandManager;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Subcommand;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
@CommandAlias("rbl|redisbungeelegacy")
@CommandPermission("redisbungee.legacy.use")
public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
private final RedisBungeePlugin<?> plugin;
public LegacyRedisBungeeCommands(CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
var commands = plugin.configuration().commandsConfiguration().legacySubCommandsConfiguration();
if (!plugin.configuration().commandsConfiguration().redisbungeeLegacyEnabled()) throw new IllegalStateException("someone tried to init me while disabled!");
if (commands == null) throw new NullPointerException("commands config is null!!");
if (commands.installGlist()) commandManager.registerCommand(new CommandGList(this));
if (commands.installFind()) commandManager.registerCommand(new CommandFind(this));
if (commands.installIp()) commandManager.registerCommand(new CommandIp(this));
if (commands.installLastseen()) commandManager.registerCommand(new CommandLastSeen(this));
if (commands.installPlist()) commandManager.registerCommand(new CommandPlist(this));
if (commands.installPproxy()) commandManager.registerCommand(new CommandPProxy(this));
if (commands.installSendtoall()) commandManager.registerCommand(new CommandSendToAll(this));
if (commands.installServerid()) commandManager.registerCommand(new CommandServerId(this));
if (commands.installServerids()) commandManager.registerCommand(new CommandServerIds(this));
}
private static final Component NO_PLAYER_SPECIFIED =
Component.text("You must specify a player name.", NamedTextColor.RED);
private static final Component PLAYER_NOT_FOUND =
Component.text("No such player found.", NamedTextColor.RED);
private static final Component NO_COMMAND_SPECIFIED =
Component.text("You must specify a command to be run.", NamedTextColor.RED);
private static String playerPlural(int num) {
return num == 1 ? num + " player is" : num + " players are";
}
@Subcommand("glist")
@CommandPermission("redisbungee.command.glist")
public void gList(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
int count = plugin.getAbstractRedisBungeeApi().getPlayerCount();
Component playersOnline = Component.text(playerPlural(count) + " currently online.", NamedTextColor.YELLOW);
if (args.length > 0 && args[0].equals("showall")) {
Multimap<String, UUID> serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
serverToPlayers.forEach((key, value) -> {
// if for any reason UUID translation fails just return the uuid as name, to make command finish executing.
String playerName = plugin.getUuidTranslator().getNameFromUuid(value, false);
human.put(key, playerName != null ? playerName : value.toString());
});
for (String server : new TreeSet<>(serverToPlayers.keySet())) {
Component serverName = Component.text("[" + server + "] ", NamedTextColor.GREEN);
Component serverCount = Component.text("(" + serverToPlayers.get(server).size() + "): ", NamedTextColor.YELLOW);
Component serverPlayers = Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE);
sendMessage(issuer, Component.textOfChildren(serverName, serverCount, serverPlayers));
}
sendMessage(issuer, playersOnline);
} else {
sendMessage(issuer, playersOnline);
sendMessage(issuer, Component.text("To see all players online, use /glist showall.", NamedTextColor.YELLOW));
}
});
}
@Subcommand("find")
@CommandPermission("redisbungee.command.find")
public void find(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sendMessage(issuer, PLAYER_NOT_FOUND);
return;
}
String proxyId = plugin.playerDataManager().getProxyFor(uuid);
if (proxyId != null) {
String serverId = plugin.playerDataManager().getServerFor(uuid);
Component message = Component.text(args[0] + " is on proxy " + proxyId + " on server " + serverId +".", NamedTextColor.BLUE);
sendMessage(issuer, message);
} else {
sendMessage(issuer, PLAYER_NOT_FOUND);
}
} else {
sendMessage(issuer, NO_PLAYER_SPECIFIED);
}
});
}
@Subcommand("lastseen")
@CommandPermission("redisbungee.command.lastseen")
public void lastSeen(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sendMessage(issuer, PLAYER_NOT_FOUND);
return;
}
long secs = plugin.getAbstractRedisBungeeApi().getLastOnline(uuid);
TextComponent.Builder message = Component.text();
if (secs == 0) {
message.color(NamedTextColor.GREEN);
message.content(args[0] + " is currently online.");
} else if (secs != -1) {
message.color(NamedTextColor.BLUE);
message.content(args[0] + " was last online on " + new SimpleDateFormat().format(secs) + ".");
} else {
message.color(NamedTextColor.RED);
message.content(args[0] + " has never been online.");
}
sendMessage(issuer, message.build());
} else {
sendMessage(issuer, NO_PLAYER_SPECIFIED);
}
});
}
@Subcommand("ip")
@CommandPermission("redisbungee.command.ip")
public void ip(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sendMessage(issuer, PLAYER_NOT_FOUND);
return;
}
InetAddress ia = plugin.getAbstractRedisBungeeApi().getPlayerIp(uuid);
if (ia != null) {
TextComponent message = Component.text(args[0] + " is connected from " + ia.toString() + ".", NamedTextColor.GREEN);
sendMessage(issuer, message);
} else {
sendMessage(issuer, PLAYER_NOT_FOUND);
}
} else {
sendMessage(issuer, NO_PLAYER_SPECIFIED);
}
});
}
@Subcommand("pproxy")
@CommandPermission("redisbungee.command.pproxy")
public void playerProxy(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sendMessage(issuer, PLAYER_NOT_FOUND);
return;
}
String proxy = plugin.getAbstractRedisBungeeApi().getProxy(uuid);
if (proxy != null) {
TextComponent message = Component.text(args[0] + " is connected to " + proxy + ".", NamedTextColor.GREEN);
sendMessage(issuer, message);
} else {
sendMessage(issuer, PLAYER_NOT_FOUND);
}
} else {
sendMessage(issuer, NO_PLAYER_SPECIFIED);
}
});
}
@Subcommand("sendtoall")
@CommandPermission("redisbungee.command.sendtoall")
public void sendToAll(CommandIssuer issuer, String[] args) {
if (args.length > 0) {
String command = Joiner.on(" ").skipNulls().join(args);
plugin.getAbstractRedisBungeeApi().sendProxyCommand(command);
TextComponent message = Component.text("Sent the command /" + command + " to all proxies.", NamedTextColor.GREEN);
sendMessage(issuer, message);
} else {
sendMessage(issuer, NO_COMMAND_SPECIFIED);
}
}
@Subcommand("serverid")
@CommandPermission("redisbungee.command.serverid")
public void serverId(CommandIssuer issuer) {
sendMessage(issuer, Component.text("You are on " + plugin.getAbstractRedisBungeeApi().getProxyId() + ".", NamedTextColor.YELLOW));
}
@Subcommand("serverids")
@CommandPermission("redisbungee.command.serverids")
public void serverIds(CommandIssuer issuer) {
sendMessage(issuer, Component.text("All Proxies IDs: " + Joiner.on(", ").join(plugin.getAbstractRedisBungeeApi().getAllProxies()), NamedTextColor.YELLOW));
}
@Subcommand("plist")
@CommandPermission("redisbungee.command.plist")
public void playerList(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
String proxy = args.length >= 1 ? args[0] : plugin.configuration().getProxyId();
if (!plugin.proxyDataManager().proxiesIds().contains(proxy)) {
sendMessage(issuer, Component.text(proxy + " is not a valid proxy. See /serverids for valid proxies.", NamedTextColor.RED));
return;
}
Set<UUID> players = plugin.getAbstractRedisBungeeApi().getPlayersOnProxy(proxy);
Component playersOnline = Component.text(playerPlural(players.size()) + " currently on proxy " + proxy + ".", NamedTextColor.YELLOW);
if (args.length >= 2 && args[1].equals("showall")) {
Multimap<String, UUID> serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
serverToPlayers.forEach((key, value) -> {
if (players.contains(value)) {
human.put(key, plugin.getUuidTranslator().getNameFromUuid(value, false));
}
});
for (String server : new TreeSet<>(human.keySet())) {
TextComponent serverName = Component.text("[" + server + "] ", NamedTextColor.RED);
TextComponent serverCount = Component.text("(" + human.get(server).size() + "): ", NamedTextColor.YELLOW);
TextComponent serverPlayers = Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE);
sendMessage(issuer, Component.textOfChildren(serverName, serverCount, serverPlayers));
}
sendMessage(issuer, playersOnline);
} else {
sendMessage(issuer, playersOnline);
sendMessage(issuer, Component.text("To see all players online, use /plist " + proxy + " showall.", NamedTextColor.YELLOW));
}
});
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.utils;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.CommandIssuer;
import net.kyori.adventure.text.Component;
/**
* this just dumb class that wraps the adventure stuff into base command
*/
public abstract class AdventureBaseCommand extends BaseCommand {
public static void sendMessage(CommandIssuer issuer, Component component) {
CommandPlatformHelper.getPlatformHelper().sendMessage(issuer, component);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.utils;
import co.aikar.commands.CommandIssuer;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import net.kyori.adventure.text.Component;
public abstract class CommandPlatformHelper {
private static CommandPlatformHelper SINGLETON;
public abstract void sendMessage(CommandIssuer issuer, Component component);
public static void init(CommandPlatformHelper platformHelper) {
if (SINGLETON != null) {
throw new IllegalStateException("tried to re init Platform Helper");
}
SINGLETON = platformHelper;
}
public static CommandPlatformHelper getPlatformHelper() {
return SINGLETON;
}
}

View File

@@ -0,0 +1,25 @@
package com.imaginarycode.minecraft.redisbungee.commands.utils;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.tasks.UUIDCleanupTask;
import redis.clients.jedis.UnifiedJedis;
public class StopperUUIDCleanupTask extends UUIDCleanupTask {
public static boolean isRunning = false;
public StopperUUIDCleanupTask(RedisBungeePlugin<?> plugin) {
super(plugin);
}
@Override
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
isRunning = true;
try {
super.unifiedJedisTask(unifiedJedis);
} catch (Exception ignored) {}
isRunning = false;
return null;
}
}