diff --git a/src/main/java/net/elytrium/limboreconnect/Config.java b/src/main/java/net/elytrium/limboreconnect/Config.java index d072b89..1a6fdbc 100644 --- a/src/main/java/net/elytrium/limboreconnect/Config.java +++ b/src/main/java/net/elytrium/limboreconnect/Config.java @@ -53,12 +53,16 @@ public class Config extends YamlSerializable { public long joinDelay = 2000; @Comment(value = @CommentValue("Send to limbo or use current server's world")) public boolean useLimbo = false; + @Comment(value = @CommentValue("Connects to Limbo instantly after connecting to the main server")) + public boolean testing = false; @Comment(value = @CommentValue("Require permission to reconnect (limboreconnect.reconnect)")) public boolean requirePermission = false; public World world; public Messages messages; public Sounds sounds; + @Comment(value = @CommentValue("Allows you to transfer a player to the hub if the server is unavailable.")) + public HubTeleport hubTeleport; public boolean debug = false; @@ -67,6 +71,7 @@ public Config() { this.world = new World(); this.messages = new Messages(); this.sounds = new Sounds(); + this.hubTeleport = new HubTeleport(); } @@ -103,9 +108,9 @@ public WorldCoords() { public static class PlayerCoords { - public int x = 0; - public int y = 100; - public int z = 0; + public double x = 0; + public double y = 100; + public double z = 0; public float pitch = 0; public float yaw = 90; @@ -169,4 +174,38 @@ public Sound(String name) { } } } + + public static class HubTeleport { + + public boolean enable = false; + @Comment(value = @CommentValue("sends the player to an available hub"), at = Comment.At.SAME_LINE) + public boolean instantly = false; + public List servers = List.of("hub-1", "hub-2", "hub-3"); + public Messages messages = new Messages(); + + public static class Messages { + + public String teleport = """ + > + ━━━━━━━━━━━━━━━━━━━━ + + The server you were on is currently unavailable + + You may wait here or teleport to the hub + + + Click to teleport to the hub"> + ▶ CLICK HERE TO GO TO HUB ◀ + + + + ━━━━━━━━━━━━━━━━━━━━"""; + public String fully = "All hubs are currently full, please wait..."; + + public Messages() {} + + } + + } + } diff --git a/src/main/java/net/elytrium/limboreconnect/LimboReconnect.java b/src/main/java/net/elytrium/limboreconnect/LimboReconnect.java index 7a5d523..e435258 100644 --- a/src/main/java/net/elytrium/limboreconnect/LimboReconnect.java +++ b/src/main/java/net/elytrium/limboreconnect/LimboReconnect.java @@ -34,14 +34,6 @@ import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.protocol.packet.BossBarPacket; -import java.io.IOException; -import java.nio.file.Path; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; import net.elytrium.limboapi.api.Limbo; import net.elytrium.limboapi.api.LimboFactory; import net.elytrium.limboapi.api.chunk.Dimension; @@ -51,6 +43,7 @@ import net.elytrium.limboapi.api.protocol.packets.PacketMapping; import net.elytrium.limboreconnect.commands.LimboReconnectCommand; import net.elytrium.limboreconnect.handler.ReconnectHandler; +import net.elytrium.limboreconnect.listener.ConnectListener; import net.elytrium.limboreconnect.listener.ReconnectListener; import net.elytrium.limboreconnect.protocol.packets.PlaySound; import net.kyori.adventure.text.Component; @@ -58,6 +51,15 @@ import net.kyori.adventure.title.Title; import org.slf4j.Logger; +import java.io.IOException; +import java.nio.file.Path; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + @Plugin( id = "limboreconnect", name = "LimboReconnect", @@ -196,9 +198,11 @@ public void reload() { this.server.getEventManager().unregisterListeners(this); this.server.getEventManager().register(this, new ReconnectListener(this)); + if(CONFIG.testing) this.server.getEventManager().register(this, new ConnectListener(server, this)); CommandManager commandManager = this.server.getCommandManager(); commandManager.unregister("limboreconnect"); commandManager.register(this.commandMeta, new LimboReconnectCommand(this)); + } public void addPlayer(Player player, RegisteredServer server) { @@ -218,6 +222,7 @@ public void addPlayer(Player player, RegisteredServer server) { } connectedPlayer.getTabList().clearAll(); - this.limbo.spawnPlayer(player, new ReconnectHandler(this, server)); + this.limbo.spawnPlayer(player, new ReconnectHandler(this, server, this.server)); } + } diff --git a/src/main/java/net/elytrium/limboreconnect/handler/ReconnectHandler.java b/src/main/java/net/elytrium/limboreconnect/handler/ReconnectHandler.java index 679d048..7c2d081 100644 --- a/src/main/java/net/elytrium/limboreconnect/handler/ReconnectHandler.java +++ b/src/main/java/net/elytrium/limboreconnect/handler/ReconnectHandler.java @@ -17,12 +17,9 @@ package net.elytrium.limboreconnect.handler; -import static net.elytrium.limboreconnect.LimboReconnect.CONFIG; - +import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.server.PingOptions; import com.velocitypowered.api.proxy.server.RegisteredServer; -import java.time.Duration; -import java.util.concurrent.TimeUnit; import net.elytrium.limboapi.api.Limbo; import net.elytrium.limboapi.api.LimboSessionHandler; import net.elytrium.limboapi.api.player.LimboPlayer; @@ -30,21 +27,30 @@ import net.elytrium.limboreconnect.LimboReconnect; import net.elytrium.limboreconnect.protocol.packets.PlaySound; +import java.time.Duration; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import static net.elytrium.limboreconnect.LimboReconnect.CONFIG; + public class ReconnectHandler implements LimboSessionHandler { private final PingOptions pingOptions = PingOptions.builder().timeout(Duration.ofMillis(CONFIG.pingTimeout)).build(); private final LimboReconnect plugin; private final RegisteredServer server; + private final ProxyServer proxyServer; private LimboPlayer player; private boolean connected = true; private boolean connecting = false; private int titleIndex = -1; private final PlaySound waitSound; private final PlaySound connectSound; + private ScheduledFuture hubRetryTask; - public ReconnectHandler(LimboReconnect plugin, RegisteredServer server) { + public ReconnectHandler(LimboReconnect plugin, RegisteredServer server, ProxyServer proxyServer) { this.plugin = plugin; this.server = server; + this.proxyServer = proxyServer; Config.Sounds sounds = CONFIG.sounds; double playerX = CONFIG.world.playerCoords.x; double playerY = CONFIG.world.playerCoords.y; @@ -60,6 +66,7 @@ public void onSpawn(Limbo server, LimboPlayer player) { this.player.setGameMode(CONFIG.world.gamemode); this.player.getScheduledExecutor().schedule(this::tick, CONFIG.checkInterval, TimeUnit.MILLISECONDS); this.tickMessages(); + this.hubTeleport(); } @Override @@ -73,10 +80,14 @@ public void onMove(double x, double y, double z) { this.connectSound.setPosition(x, y, z); } + @Override + public void onChat(String chat) { + if (!connected || connecting) return; + if ("/hub".equalsIgnoreCase(chat)) findAndSendHub(); + } + private void tick() { - if (!this.connected) { - return; - } + if (!this.connected || CONFIG.testing) return; this.server.ping(this.pingOptions).whenComplete((ping, exception) -> { if (exception != null) { @@ -115,4 +126,61 @@ private void tickMessages() { this.player.writePacket(this.waitSound); this.player.getScheduledExecutor().schedule(this::tickMessages, CONFIG.messages.titles.showDelay * 50, TimeUnit.MILLISECONDS); } + + private void hubTeleport() { + if (!connected || connecting || !CONFIG.hubTeleport.enable) return; + + boolean fromHub = CONFIG.hubTeleport.servers + .contains(server.getServerInfo().getName()); + + if (fromHub && CONFIG.hubTeleport.servers.size() == 1) return; + + if (CONFIG.hubTeleport.instantly) findAndSendHub(); + else player.getProxyPlayer().sendMessage(LimboReconnect.getSerializer().deserialize(CONFIG.hubTeleport.messages.teleport)); + } + + private void findAndSendHub() { + if (!connected || connecting || !CONFIG.hubTeleport.enable) return; + if (hubRetryTask != null && !hubRetryTask.isDone()) return; + + proxyServer.getAllServers().stream() + .filter(s -> CONFIG.hubTeleport.servers.contains(s.getServerInfo().getName())) + .forEach(s -> s.ping(pingOptions).whenComplete((ping, err) -> { + if (!connected || connecting) return; + if (err != null || ping == null) return; + + ping.getPlayers().ifPresent(players -> { + if (players.getOnline() >= players.getMax()) return; + connect(s); + }); + })); + + hubRetryTask = player.getScheduledExecutor().schedule( + () -> { + if (!connected || connecting) return; + player.getProxyPlayer().sendMessage(LimboReconnect.getSerializer().deserialize(CONFIG.hubTeleport.messages.fully)); + hubRetryTask = null; + findAndSendHub(); + }, 2, TimeUnit.SECONDS + ); + } + + private void connect(RegisteredServer target) { + if (!connected || connecting) return; + + connecting = true; + cancelHubRetry(); + + player.writePacket(connectSound); + player.getProxyPlayer().resetTitle(); + player.disconnect(target); + } + + private void cancelHubRetry() { + if (hubRetryTask != null) { + hubRetryTask.cancel(false); + hubRetryTask = null; + } + } + } diff --git a/src/main/java/net/elytrium/limboreconnect/listener/ConnectListener.java b/src/main/java/net/elytrium/limboreconnect/listener/ConnectListener.java new file mode 100644 index 0000000..307d272 --- /dev/null +++ b/src/main/java/net/elytrium/limboreconnect/listener/ConnectListener.java @@ -0,0 +1,24 @@ +package net.elytrium.limboreconnect.listener; + +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.player.ServerPostConnectEvent; +import com.velocitypowered.api.proxy.ProxyServer; +import net.elytrium.limboreconnect.LimboReconnect; + +public class ConnectListener { + + public ProxyServer server; + public LimboReconnect plugin; + + public ConnectListener(ProxyServer server, LimboReconnect plugin) { + this.server = server; + this.plugin = plugin; + } + + @Subscribe + public void onServerPreConnect(ServerPostConnectEvent event) { + event.getPlayer().getCurrentServer().ifPresent(serverConnection -> + plugin.addPlayer(event.getPlayer(), serverConnection.getServer())); + } + +}