Skip to content
This repository has been archived by the owner on Jan 10, 2022. It is now read-only.

Commit

Permalink
Bukkit Counterpart
Browse files Browse the repository at this point in the history
  • Loading branch information
regulad committed Jul 17, 2021
1 parent a600b6a commit 121e133
Show file tree
Hide file tree
Showing 17 changed files with 516 additions and 41 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,9 @@ Data will be sent on `matchmaker:out`, and data will be received on `matchmaker:
* Returns `Number of players in servers hosting that game. If the game is not found, return 0.`
* Subchannel `GetGame`
* Returns `Name of the game the player is currently in.`
* Subchannel `GetGames`
* Returns `A list of all games the server can connect you to, seperated by a ,`
* Subchannel `SentToGame`
* Data `name of the game`
* This message will be dispatched when a player joins the server via Matchmaker. May be multiple seperated by a `, `. Note that only one message will be sent a server, and the player used to send it may be disregarded.

22 changes: 21 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>quest.ender</groupId>
<artifactId>Matchmaker</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>Matchmaker</name>
<properties>
Expand Down Expand Up @@ -97,10 +97,18 @@
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>simonsators Repo</id>
<url>https://simonsator.de/repo</url>
</repository>
<repository>
<id>placeholderapi</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
</repositories>
<dependencies>
<dependency>
Expand All @@ -110,6 +118,18 @@
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.13-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.9.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
Expand Down
43 changes: 21 additions & 22 deletions src/main/java/quest/ender/Matchmaker/Matchmaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
import net.md_5.bungee.config.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import quest.ender.Matchmaker.command.LobbyCommand;
import quest.ender.Matchmaker.command.MakeMatchCommand;
import quest.ender.Matchmaker.command.LobbyCommand;
import quest.ender.Matchmaker.command.ReloadCommand;
import quest.ender.Matchmaker.events.PreGameSendEvent;
import quest.ender.Matchmaker.handler.ForcedServerReconnectHandler;
import quest.ender.Matchmaker.listener.ConnectionListener;
import quest.ender.Matchmaker.listener.GameSendListener;
import quest.ender.Matchmaker.listener.PluginMessageListener;
Expand All @@ -38,21 +40,20 @@ public void onEnable() {

if (this.getConfig() == null) this.getLogger().warning("Couldn't load configuration!");

final @NotNull ProxyServer proxyServer = this.getProxy();

proxyServer.registerChannel("matchmaker:in");
proxyServer.registerChannel("matchmaker:out");
this.getProxy().registerChannel("matchmaker:in");
this.getProxy().registerChannel("matchmaker:out");

final @NotNull PluginManager pluginManager = proxyServer.getPluginManager();
this.getProxy().getPluginManager().registerCommand(this, new MakeMatchCommand(this, this.getConfig().getString("commands.makematch.name"), this.getConfig().getString("commands.makematch.permission"), Iterables.toArray(this.getConfig().getStringList("commands.makematch.aliases"), String.class)));
this.getProxy().getPluginManager().registerCommand(this, new LobbyCommand(this, this.getConfig().getString("commands.lobby.name"), this.getConfig().getString("commands.lobby.permission"), Iterables.toArray(this.getConfig().getStringList("commands.lobby.aliases"), String.class)));
this.getProxy().getPluginManager().registerCommand(this, new ReloadCommand(this, this.getConfig().getString("commands.mmreload.name"), this.getConfig().getString("commands.mmreload.permission"), Iterables.toArray(this.getConfig().getStringList("commands.mmreload.aliases"), String.class)));

pluginManager.registerCommand(this, new MakeMatchCommand(this, this.getConfig().getString("commands.makematch.name"), this.getConfig().getString("commands.makematch.permission"), Iterables.toArray(this.getConfig().getStringList("commands.makematch.aliases"), String.class)));
pluginManager.registerCommand(this, new LobbyCommand(this, this.getConfig().getString("commands.lobby.name"), this.getConfig().getString("commands.lobby.permission"), Iterables.toArray(this.getConfig().getStringList("commands.lobby.aliases"), String.class)));
this.getProxy().getPluginManager().registerListener(this, new GameSendListener(this));
this.getProxy().getPluginManager().registerListener(this, new PluginMessageListener(this));
this.getProxy().getPluginManager().registerListener(this, new ConnectionListener(this));

pluginManager.registerListener(this, new GameSendListener(this));
pluginManager.registerListener(this, new PluginMessageListener(this));
pluginManager.registerListener(this, new ConnectionListener(this));
final @Nullable ServerInfo forcedServer = this.getProxy().getServerInfo(this.getConfig().getString("login.holding"));

this.getLogger().info("All done loading.");
if (forcedServer != null) this.getProxy().setReconnectHandler(new ForcedServerReconnectHandler(forcedServer));
}

@Override
Expand All @@ -65,8 +66,6 @@ public void onDisable() {
final @NotNull PluginManager pluginManager = proxyServer.getPluginManager();
pluginManager.unregisterCommands(this);
pluginManager.unregisterListeners(this);

this.getLogger().info("Bye!");
}

public void saveDefaultConfig() {
Expand Down Expand Up @@ -174,18 +173,20 @@ public int getGamePlayerCount(String gameName) {
* Get a {@link ServerInfo} capable of receiving the proxiedPlayers. This may take as long as a second, since it has to ping servers.
*
* @param gameName The name of the game, in {@link String} form.
* @param proxiedPlayers A {@link List} of players that must be accommodated.
* @param proxiedPlayers The number of players the server must accept.
* @return A future that returns a {@link ServerInfo} that must accommodate players. If no servers are found, the future will not complete. This is only valid in the instant that is received, since the state of the server may change. (i.e. a player joining, putting the server over it's limit)
*/
public @NotNull CompletableFuture<ServerInfo> getServer(String gameName, List<ProxiedPlayer> proxiedPlayers) {
public @Nullable CompletableFuture<ServerInfo> getServer(String gameName, int proxiedPlayers) {
if (!this.getGames().contains(gameName)) return null;

final @NotNull ArrayList<ServerInfo> serverList = this.getServers(gameName);

final @NotNull CompletableFuture<ServerInfo> serverPingCompletableFuture = new CompletableFuture<>();
for (ServerInfo serverInfo : serverList) {
serverInfo.ping((ServerPing serverPing, Throwable throwable) -> {
if (serverPing != null && throwable == null) { // This is so if one server causes an issue, the proxy will continue pinging.
final @NotNull ServerPing.Players players = serverPing.getPlayers();
if (players.getMax() - players.getOnline() >= proxiedPlayers.size()) {
if (players.getMax() - players.getOnline() >= proxiedPlayers) {
serverPingCompletableFuture.complete(serverInfo); // The first server to respond that can accept players will get selected.
}
}
Expand All @@ -206,12 +207,10 @@ public int getGamePlayerCount(String gameName) {
this.getProxy().getPluginManager().callEvent(preGameSendEvent);
if (!preGameSendEvent.isCancelled()) {
final @NotNull ArrayList<ProxiedPlayer> players = PartyUtil.getAffiliatedPlayers(player);
final @Nullable CompletableFuture<ServerInfo> targetServer = this.getServer(gameName, players);
final @Nullable CompletableFuture<ServerInfo> targetServer = this.getServer(gameName, players.size());

targetServer.thenApply(serverInfo -> {
for (ProxiedPlayer proxiedPlayer : players) {
if (!serverInfo.getPlayers().contains(proxiedPlayer)) proxiedPlayer.connect(serverInfo);
}
if (targetServer != null) targetServer.thenApply(serverInfo -> {
PartyUtil.getLeader(player).connect(serverInfo);

return serverInfo;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public LobbyCommand(@NotNull Matchmaker matchmaker, @NotNull String name, @Nulla

@Override
public void execute(@NotNull CommandSender sender, @NotNull String[] args) {
this.matchmaker.getProxy().getPluginManager().dispatchCommand(sender, this.matchmaker.getConfig().getString("commands.makematch.name") + " " + this.matchmaker.getConfig().getString("login"));
this.matchmaker.getProxy().getPluginManager().dispatchCommand(sender, this.matchmaker.getConfig().getString("commands.makematch.name") + " " + this.matchmaker.getConfig().getString("lobby_game"));
// I hate this, but BungeeCord is weird as hell.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import quest.ender.Matchmaker.Matchmaker;
import quest.ender.Matchmaker.util.PartyUtil;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -40,6 +41,8 @@ public void execute(@NotNull CommandSender sender, @NotNull String[] args) {

if (!sender.hasPermission(this.matchmaker.getConfig().getString("base_game_permission") + "." + gameName)) {
sender.sendMessage(new ComponentBuilder("You do not have the permission to join this game.").color(ChatColor.RED).create());
} else if (!PartyUtil.leadsParty(proxiedPlayer)) {
sender.sendMessage(new ComponentBuilder("Only the leader of a party can queue you for a game!").color(ChatColor.RED).create());
} else {
final @Nullable CompletableFuture<ServerInfo> serverInfoCompletableFuture = this.matchmaker.sendToGame(proxiedPlayer, gameName);

Expand All @@ -52,7 +55,7 @@ public void execute(@NotNull CommandSender sender, @NotNull String[] args) {
this.matchmaker.getProxy().getScheduler().schedule(this.matchmaker, () -> {
if (!serverInfoCompletableFuture.isDone()) {
serverInfoCompletableFuture.cancel(true);
sender.sendMessage(new ComponentBuilder("We were unable to send you to a game.").color(ChatColor.RED).create());
sender.sendMessage(new ComponentBuilder("We were unable to send you to a game. Try again later.").color(ChatColor.RED).create());
}
}, this.matchmaker.getConfig().getLong("timeout"), TimeUnit.MILLISECONDS);
}
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/quest/ender/Matchmaker/command/ReloadCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package quest.ender.Matchmaker.command;

import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.plugin.Command;
import org.jetbrains.annotations.NotNull;
import quest.ender.Matchmaker.Matchmaker;

public class ReloadCommand extends Command {
private final @NotNull Matchmaker matchmaker;

public ReloadCommand(@NotNull Matchmaker matchmaker, String name) {
super(name);
this.matchmaker = matchmaker;
}

public ReloadCommand(@NotNull Matchmaker matchmaker, String name, String permission, String... aliases) {
super(name, permission, aliases);
this.matchmaker = matchmaker;
}

@Override
public void execute(CommandSender sender, String[] args) {
this.matchmaker.reloadConfig();
sender.sendMessage(new ComponentBuilder("Reloaded configuration.").color(ChatColor.GREEN).create());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package quest.ender.Matchmaker.handler;

import net.md_5.bungee.api.ReconnectHandler;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import org.jetbrains.annotations.NotNull;

public class ForcedServerReconnectHandler implements ReconnectHandler {
private final @NotNull ServerInfo targetServerInfo;

public ForcedServerReconnectHandler(@NotNull ServerInfo targetServerInfo) {
this.targetServerInfo = targetServerInfo;
}

@Override
public ServerInfo getServer(ProxiedPlayer player) {
return targetServerInfo;
}

@Override
public void setServer(ProxiedPlayer player) {
/* no-op, we don't implement this */
}

@Override
public void save() {
/* no-op, we don't implement this */
}

@Override
public void close() {
/* no-op, we don't implement this */
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
import org.jetbrains.annotations.Nullable;
import quest.ender.Matchmaker.Matchmaker;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

Expand All @@ -37,18 +35,22 @@ public void onPlayerJoin(PostLoginEvent postLoginEvent) {

if (currentGame == null || !currentGame.equals(targetGame)) {
// In short: Schedule this task login.game seconds away, if either the current game is not part of a game or the current game login.game.
final @NotNull List<ProxiedPlayer> affectedPlayers = Collections.singletonList(postLoginEvent.getPlayer());
final @NotNull CompletableFuture<ServerInfo> serverInfoCompletableFuture = this.matchmaker.getServer(targetGame, affectedPlayers);
final @NotNull ProxiedPlayer targetPlayer = postLoginEvent.getPlayer();
final @NotNull CompletableFuture<ServerInfo> serverInfoCompletableFuture = this.matchmaker.getServer(targetGame, 1);

final boolean[] isMessagedSingleton = {false};

final @NotNull ScheduledTask scheduledTask = this.matchmaker.getProxy().getScheduler().schedule(this.matchmaker, () -> {
if (!serverInfoCompletableFuture.isDone()) {
postLoginEvent.getPlayer().sendMessage(new ComponentBuilder("Couldn't find you a lobby. We are still trying...").color(ChatColor.RED).create());
isMessagedSingleton[0] = true;
targetPlayer.sendMessage(new ComponentBuilder("Couldn't find you a lobby. We are still trying...").color(ChatColor.RED).create());
}
}, this.matchmaker.getConfig().getLong("timeout"), this.matchmaker.getConfig().getLong("timeout") * 2, TimeUnit.MILLISECONDS); // Arbitrary.
}, this.matchmaker.getConfig().getLong("timeout"), this.matchmaker.getConfig().getLong("timeout") * 4, TimeUnit.MILLISECONDS); // Arbitrary.

serverInfoCompletableFuture.thenApply((targetServerInfo) -> {
scheduledTask.cancel();
postLoginEvent.getPlayer().sendMessage(new ComponentBuilder("Found one! Sending you to " + targetServerInfo.getName() + "...").color(ChatColor.RED).create());
if (isMessagedSingleton[0]) targetPlayer.sendMessage(new ComponentBuilder("Found one! Sending you to " + targetServerInfo.getName() + "...").color(ChatColor.GREEN).create());
targetPlayer.connect(targetServerInfo);

return targetServerInfo;
});
Expand All @@ -58,13 +60,13 @@ public void onPlayerJoin(PostLoginEvent postLoginEvent) {

@EventHandler
public void onPlayerKick(ServerKickEvent serverKickEvent) { // It may take too long for the fallback server to be found. That's fine, I guess?
final @NotNull List<ProxiedPlayer> affectedPlayers = Collections.singletonList(serverKickEvent.getPlayer());
final @NotNull CompletableFuture<ServerInfo> serverInfoCompletableFuture = this.matchmaker.getServer(this.matchmaker.getConfig().getString("fallback.game"), affectedPlayers);
final @NotNull ProxiedPlayer affectedPlayer = serverKickEvent.getPlayer();
final @NotNull CompletableFuture<ServerInfo> serverInfoCompletableFuture = this.matchmaker.getServer(this.matchmaker.getConfig().getString("fallback.game"), 1);

serverInfoCompletableFuture.thenApply((targetServerInfo) -> {
serverKickEvent.setCancelServer(targetServerInfo);
serverKickEvent.setCancelled(true);
serverKickEvent.getPlayer().sendMessage(new ComponentBuilder(this.matchmaker.getConfig().getString("fallback.message")).create());
affectedPlayer.sendMessage(new ComponentBuilder(this.matchmaker.getConfig().getString("fallback.message")).create());

return targetServerInfo;
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package quest.ender.Matchmaker.listener;

import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import org.jetbrains.annotations.NotNull;
import quest.ender.Matchmaker.Matchmaker;
import quest.ender.Matchmaker.events.GameSendFailureEvent;
import quest.ender.Matchmaker.events.GameSendSuccessEvent;
Expand All @@ -17,12 +20,21 @@ public GameSendListener(Matchmaker matchmaker) {
}

@EventHandler
public void onSendSuccess(GameSendSuccessEvent gameSendSuccessEvent) {
ArrayList<String> displayNameList = new ArrayList<>();
public void onGameSuccess(GameSendSuccessEvent gameSendSuccessEvent) {
final @NotNull ArrayList<String> displayNameList = new ArrayList<>();
for (ProxiedPlayer proxiedPlayer : gameSendSuccessEvent.getMovedPlayers()) {
displayNameList.add(proxiedPlayer.getDisplayName());
}

if (gameSendSuccessEvent.getTargetServer().getPlayers().size() > 0) {
final @NotNull ByteArrayDataOutput byteArrayDataOutput = ByteStreams.newDataOutput();

byteArrayDataOutput.writeUTF("SentToGame");
byteArrayDataOutput.writeUTF(String.join(", ", displayNameList));

gameSendSuccessEvent.getTargetServer().sendData("matchmaker:in", byteArrayDataOutput.toByteArray());
}

this.matchmaker.getLogger().info(String.join(", ", displayNameList) + " connected to " + gameSendSuccessEvent.getTargetServer().getName() + " for " + this.matchmaker.getGame(gameSendSuccessEvent.getTargetServer()));
}

Expand Down
Loading

0 comments on commit 121e133

Please sign in to comment.