Skip to content

Commit

Permalink
separate info and flush tasks and kick cooldown
Browse files Browse the repository at this point in the history
  • Loading branch information
bierdosenhalter committed Oct 7, 2024
1 parent 4d56198 commit d03c05b
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 79 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
plugin_version=3.0.4-INDEV
plugin_version=3.0.5-INDEV
velocity_api_version=3.3.0-SNAPSHOT
minecraft_version=1.20.6
19 changes: 8 additions & 11 deletions src/main/java/org/zeroBzeroT/anarchyqueue/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import java.nio.file.Path;

public class Config {
public static String serverMain = null;
public static String target = null;

public static String serverQueue = null;
public static String queue = null;

public static String name = null; // TODO: not in use, implement or remove this

Expand All @@ -21,15 +21,13 @@ public class Config {

public static String messageConnecting = null;

public static String messageFull = null;
public static String messageFull = null; // TODO: not in use, implement or remove this

public static String messageOffline = null;
public static String messageOffline = null; // TODO: not in use, implement or remove this

public static boolean kick = true; // TODO: not in use, implement or remove this
public static boolean kick = true;

public static int waitOnKick = 0; // TODO: not in use, implement or remove this

public static long joinDelay = 0;
public static int waitOnKick = 16;

/**
* Load the config from the plugin data folder
Expand Down Expand Up @@ -57,8 +55,8 @@ static void loadConfig(Path path) throws IOException {
}

Toml toml = new Toml().read(file);
serverMain = toml.getString("server-main", "main");
serverQueue = toml.getString("server-queue", "queue");
target = toml.getString("target", "main");
queue = toml.getString("queue", "queue");
name = toml.getString("name", "0b0t");
maxPlayers = toml.getLong("max-players", 420L).intValue();
messagePosition = toml.getString("message-position", "Position in queue: ");
Expand All @@ -67,6 +65,5 @@ static void loadConfig(Path path) throws IOException {
messageOffline = toml.getString("message-offline", "Server is currently down!");
kick = toml.getBoolean("kick", true);
waitOnKick = toml.getLong("wait-on-kick", 16L).intValue();
joinDelay = toml.getLong("join-delay", 0L);
}
}
137 changes: 84 additions & 53 deletions src/main/java/org/zeroBzeroT/anarchyqueue/Queue.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.player.KickedFromServerEvent;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;

import java.time.Duration;
import java.time.Instant;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;

Expand All @@ -27,23 +25,41 @@ public class Queue {

private final ProxyServer proxyServer;

private final Semaphore queueSemaphore = new Semaphore(1);
private final Semaphore mutex = new Semaphore(1);

private final Deque<QueuedPlayer> queuedPlayers = new LinkedList<>();
private final Deque<Player> playerQueue;

private final HashMap<Player, Long> kickedPlayers;

/**
* Initializes the queue.
*/
public Queue(ProxyServer proxyServer) {
this.log = Main.getInstance().log;
this.proxyServer = proxyServer;
playerQueue = new LinkedList<>();
kickedPlayers = new HashMap<>();

// Schedule queue flusher
proxyServer.getScheduler()
.buildTask(Main.getInstance(), this::flushQueue)
.delay(Duration.ofSeconds(1))
.repeat(Duration.ofSeconds(2))
.schedule();

// Schedule player notification
proxyServer.getScheduler()
.buildTask(Main.getInstance(), this::sendUpdate)
.delay(Duration.ofSeconds(1))
.repeat(Duration.ofSeconds(10))
.schedule();

// Schedule kicked players cool down
proxyServer.getScheduler()
.buildTask(Main.getInstance(), () -> kickedPlayers.entrySet().removeIf(pair -> pair.getValue() + Config.waitOnKick < Instant.now().getEpochSecond()))
.delay(Duration.ofSeconds(0))
.repeat(Duration.ofSeconds(1))
.schedule();
}

/**
Expand All @@ -52,22 +68,22 @@ public Queue(ProxyServer proxyServer) {
*/
@Subscribe
public void onServerConnected(ServerConnectedEvent event) {
if (!event.getServer().getServerInfo().getName().equals(Config.serverQueue))
if (!event.getServer().getServerInfo().getName().equals(Config.queue))
return;

// stop adding the player several times
if (queuedPlayers.stream().anyMatch(p -> p.player() == event.getPlayer()))
if (playerQueue.contains(event.getPlayer()))
return;

// Add Player to queue
try {
queueSemaphore.acquire();
queuedPlayers.add(new QueuedPlayer(event.getPlayer(), System.currentTimeMillis()));
log.info(mm("<white>" + event.getPlayer().getUsername() + "<dark_aqua> was added to the <light_purple>queue<dark_aqua>. Queue count is " + queuedPlayers.size() + "."));
mutex.acquire();
playerQueue.add(event.getPlayer());
log.info(mm("<white>" + event.getPlayer().getUsername() + "<dark_aqua> was added to the <light_purple>queue<dark_aqua>. Queue count is " + playerQueue.size() + "."));
} catch (InterruptedException e1) {
e1.printStackTrace();
} finally {
queueSemaphore.release();
mutex.release();
}
}

Expand All @@ -76,22 +92,46 @@ public void onServerConnected(ServerConnectedEvent event) {
*/
@Subscribe
public void onKickedFromServer(KickedFromServerEvent event) {
log.info(mm("<white>" + event.getPlayer().getUsername() + "<dark_aqua> was kicked from <light_purple>" + event.getServer().getServerInfo().getName() + "<dark_aqua> for <light_purple>").append(event.getServerKickReason().isPresent() ? event.getServerKickReason().get() : mm("<empty>")).append(mm("<dark_aqua>.")));
if (event.getServer().getServerInfo().getName().equals(Config.target)) {
// kick from target server
try {
mutex.acquire();
Player player = event.getPlayer();
Component reason = event.getServerKickReason().isPresent() ? event.getServerKickReason().get() : mm("Kicked without a reason.");

// - kicking is not enabled
if (!Config.kick) {
// save the disconnection time
kickedPlayers.put(player, Instant.now().getEpochSecond());

// send message
player.sendMessage(mm("<gold>You were sent back to the queue for: <red>").append(reason).append(mm("<reset>")));
log.info(mm("<white>" + player.getUsername() + "<dark_aqua> was sent back to server <yellow>" + Config.queue + "<dark_aqua> after a disconnection (\"").append(reason).append(mm("<dark_aqua>\"). Kicked count is " + kickedPlayers.size() + ".")));
} else {
// set the disconnect reason from the target server (not the bungee message)
player.disconnect(reason);
}
} catch (InterruptedException e1) {
e1.printStackTrace();
} finally {
mutex.release();
}
}
}

/**
* Try to connect one player to the server.
*/
public void flushQueue() {
// Ignore if queue is empty
if (queuedPlayers.isEmpty())
if (playerQueue.isEmpty())
return;

// check queue server reachability
final RegisteredServer serverQueue;

try {
serverQueue = getServer(Config.serverQueue);
serverQueue = getServer(Config.queue);
} catch (ServerNotReachableException e) {
log.warn(e.getMessage());
return;
Expand All @@ -101,92 +141,83 @@ public void flushQueue() {
final RegisteredServer serverMain;

try {
serverMain = getServer(Config.serverMain);
serverMain = getServer(Config.target);
} catch (ServerNotReachableException e) {
if (Instant.now().getEpochSecond() % 10 == 0) {
serverQueue.getPlayersConnected().forEach(queuedPlayer -> queuedPlayer.sendMessage(Component.text(Config.messageOffline)));
}

// TODO: offline notification
return;
}

// check main server full
boolean full = serverMain.getPlayersConnected().size() >= Config.maxPlayers;

// send info every 10 seconds
if (Instant.now().getEpochSecond() % 10 == 0) {
sendInfo(serverQueue, full);
}

if (full)
if (serverMain.getPlayersConnected().size() >= Config.maxPlayers)
// TODO: full notification
return;

try {
queueSemaphore.acquire();
mutex.acquire();

QueuedPlayer currPlayer = null;
Player currPlayer = null;

// try to find the first player that got not kicked recently
for (QueuedPlayer testPlayer : queuedPlayers) {
if (testPlayer.queueTime() + Config.joinDelay > System.currentTimeMillis())
for (Player testPlayer : playerQueue) {
if (kickedPlayers.containsKey(testPlayer))
continue;

currPlayer = testPlayer;
break;
}

if (currPlayer == null) {
queueSemaphore.release();
mutex.release();
return;
}

// connect next player
UUID uuid = currPlayer.player().getUniqueId();

// lookup player from queue server and ping to be safe the player is connected
QueuedPlayer finalCurrPlayer = currPlayer;
UUID uuid = currPlayer.getUniqueId();
Player finalCurrPlayer = currPlayer;

serverQueue.getPlayersConnected().stream()
.filter(p -> p.getUniqueId().equals(uuid))
.findAny().ifPresentOrElse(p -> {
p.sendMessage(Component.text(Config.messageConnecting));
// TODO: direct connection to the main server if the queue is empty
p.sendMessage(mm(Config.messageConnecting));
try {
if (p.createConnectionRequest(serverMain).connect().get().isSuccessful()) {
queuedPlayers.removeFirst();
playerQueue.removeFirst();
log.info(mm("<white>" + p.getUsername() + "<dark_aqua> connected to server <aqua>" + serverMain.getServerInfo().getName() + "<dark_aqua>. Queue count is " + serverQueue.getPlayersConnected().size() + ". Main count is " + (serverMain.getPlayersConnected().size()) + " of " + Config.maxPlayers + "."));
}
} catch (InterruptedException | ExecutionException e) {
log.error(mm("<white>" + p.getUsername() + "s<red> connection to server <aqua>" + Config.serverMain + "<red> failed: " + e.getMessage()));
// FIXME: requeue
queuedPlayers.removeFirst();
queuedPlayers.add(new QueuedPlayer(finalCurrPlayer.player(), System.currentTimeMillis()));
log.error(mm("<white>" + p.getUsername() + "s<red> connection to server <aqua>" + Config.target + "<red> failed: " + e.getMessage()));
// count that as a kick ;)
kickedPlayers.put(finalCurrPlayer, Instant.now().getEpochSecond());
}
},
() -> {
log.error(mm("<white>" + finalCurrPlayer.player().getUsername() + "s<red> connection to server <aqua>" + Config.serverMain + "<red> failed: player is not connected to " + serverQueue.getServerInfo().getName()));
queuedPlayers.removeFirst();
log.error(mm("<white>" + finalCurrPlayer.getUsername() + "s<red> connection to server <aqua>" + Config.target + "<red> failed: player is not connected to " + serverQueue.getServerInfo().getName()));
playerQueue.removeFirst();
}
);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
queueSemaphore.release();
mutex.release();
}
}

private void sendInfo(RegisteredServer serverQueue, boolean full) {
/**
* Tells players their queue position
*/
public void sendUpdate() {
int i = 1;

for (QueuedPlayer player : queuedPlayers) {
player.player().sendMessage(Component.text(Config.messagePosition + (i + 1) + "/" + queuedPlayers.size()));
for (Player player : playerQueue) {
player.sendMessage(mm(Config.messagePosition.replaceAll("%position%", Integer.toString(i)).replaceAll("%size%", Integer.toString(playerQueue.size()))));
i++;
}

if (full) {
serverQueue.getPlayersConnected().forEach(queuedPlayer -> queuedPlayer.sendMessage(Component.text(Config.messageFull)));
}
}

/**
* Test a server connection and return the server object.
*/
private RegisteredServer getServer(String name) throws ServerNotReachableException {
// Get server configured in velocity.toml by name
Optional<RegisteredServer> serverOpt = proxyServer.getServer(name);
Expand Down
6 changes: 0 additions & 6 deletions src/main/java/org/zeroBzeroT/anarchyqueue/QueuedPlayer.java

This file was deleted.

14 changes: 6 additions & 8 deletions src/main/resources/config.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# ${project.artifactId} v${project.version}
server-main = "main"
server-queue = "queue"
target = "main"
queue = "queue"
name = "0b0t"
max-players = 420
message-position = "Position in queue: "
message-connecting = "Connecting to the server..."
message-full = "Server is currently full!"
message-offline = "Server is currently down!"
message-position = "<gold>Position in queue: <bold>%position%<reset>"
message-connecting = "<gold>Connecting to the server...<reset>"
message-full = "<red>Server is currently full!<reset>"
message-offline = "<red>Server is currently down!<reset>"
kick = true
wait-on-kick = 16
join-delay = 2000

0 comments on commit d03c05b

Please sign in to comment.