Skip to content

Commit

Permalink
lookup links from bedrock players in the DB as opposed to letting flo…
Browse files Browse the repository at this point in the history
…odgate handle that
  • Loading branch information
onebeastchris committed Jan 17, 2025
1 parent 90ecbd2 commit 6571d42
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 60 deletions.
25 changes: 17 additions & 8 deletions src/main/java/org/geysermc/globallinkserver/GlobalLinkServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import io.papermc.paper.command.brigadier.Commands;
import io.papermc.paper.command.brigadier.argument.ArgumentTypes;
import io.papermc.paper.event.player.AsyncChatEvent;
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
Expand Down Expand Up @@ -48,9 +47,9 @@ public class GlobalLinkServer extends JavaPlugin implements Listener {
public static List<String> permittedCommands;
public static Plugin plugin;

public final static Component LINK_INSTRUCTIONS = Component.text("Run the ").color(NamedTextColor.AQUA)
public final static Component LINK_INSTRUCTIONS = Component.text("You are not linked. To link, run the ").color(NamedTextColor.AQUA)
.append(Component.text("`/link`", NamedTextColor.GREEN))
.append(Component.text(" command to link your accounts.", NamedTextColor.AQUA));
.append(Component.text(" command.", NamedTextColor.AQUA));

public final static Component UNLINK_INSTRUCTIONS = Component.text("You are currently linked. To unlink, use ").color(NamedTextColor.AQUA)
.append(Component.text("`/unlink`", NamedTextColor.RED))
Expand All @@ -70,10 +69,12 @@ public void onEnable() {
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, linkManager::cleanupTempLinks, 0, 1);
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> {
Bukkit.getOnlinePlayers().forEach(player -> {
if (Utils.isLinked(player)) {
player.sendActionBar(UNLINK_INSTRUCTIONS);
} else {
player.sendActionBar(LINK_INSTRUCTIONS);
if (Utils.shouldShowSuggestion(player)) {
if (Utils.isLinked(player)) {
player.sendActionBar(UNLINK_INSTRUCTIONS);
} else {
player.sendActionBar(LINK_INSTRUCTIONS);
}
}
});
}, 10, 15);
Expand All @@ -86,7 +87,7 @@ public void onEnable() {
Commands.literal("link")
.requires(ctx -> ctx.getSender() instanceof Player)
.executes(commandUtils::startLink)
.then(Commands.argument("code", IntegerArgumentType.integer())
.then(Commands.argument("code", IntegerArgumentType.integer(1, 9999))
.executes(commandUtils::linkWithCode)
)
.build(),
Expand Down Expand Up @@ -184,11 +185,19 @@ public void preCommand(PlayerCommandPreprocessEvent event) {

if (command.equalsIgnoreCase("help")) {
event.setCancelled(true);

if (!Utils.shouldShowSuggestion(player)) {
player.sendMessage(Component.text("Your linking information is currently unavailable. Please wait!")
.color(NamedTextColor.RED));
return;
}

if (Utils.isLinked(player)) {
player.sendMessage(UNLINK_INSTRUCTIONS);
} else {
player.sendMessage(LINK_INSTRUCTIONS);
}
return;
}

if (!permittedCommands.contains(command)) {
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/org/geysermc/globallinkserver/link/Link.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ public class Link {
public Link() {
}

public Link(Player javaPlayer) {
this.javaId = javaPlayer.getUniqueId();
this.javaUsername = javaPlayer.getName();
public static Link createFromJavaPlayer(Player javaPlayer) {
return new Link()
.javaId(javaPlayer.getUniqueId())
.javaUsername(javaPlayer.getName());
}

public UUID bedrockId() {
Expand Down Expand Up @@ -58,4 +59,8 @@ public Link bedrockUsername(String bedrockUsername) {
this.bedrockUsername = bedrockUsername;
return this;
}

public UUID getOpposed(Player player) {
return player.getUniqueId().equals(bedrockId) ? javaId : bedrockId;
}
}
84 changes: 59 additions & 25 deletions src/main/java/org/geysermc/globallinkserver/link/LinkManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public CompletableFuture<Boolean> unlinkAccount(Player player) {
try (Connection connection = dataSource.getConnection()) {

PreparedStatement query;
if (Utils.isBedrockPlayerId(player)) { // Should never happen
if (Utils.isBedrockPlayerId(player)) {
query = connection.prepareStatement("DELETE FROM `links` WHERE `bedrock_id` = ?;");
query.setLong(1, player.getUniqueId().getLeastSignificantBits());
} else {
Expand All @@ -141,34 +141,68 @@ public CompletableFuture<Boolean> unlinkAccount(Player player) {
}

public CompletableFuture<Optional<Link>> attemptFindJavaLink(Player player) {
return CompletableFuture.supplyAsync(
() -> {
try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement query = connection.prepareStatement(
"SELECT `bedrock_id` FROM `links` WHERE `java_id` = ?")) {
query.setString(1, player.getUniqueId().toString());
return attemptFindLink(
"SELECT `bedrock_id` FROM `links` WHERE `java_id` = ?",
stmt -> stmt.setString(1, player.getUniqueId().toString()),
resultSet -> {
long bedrockId = resultSet.getLong("bedrock_id");
String bedrockTag = FloodgateApi.getInstance().getGamertagFor(bedrockId).join();
return Optional.of(Link.createFromJavaPlayer(player)
.bedrockId(new UUID(0, bedrockId))
.bedrockUsername(bedrockTag));
}
);
}

try (ResultSet resultSet = query.executeQuery()) {
if (resultSet.next()) {
long bedrockId = resultSet.getLong("bedrock_id");
String bedrockTag = FloodgateApi.getInstance().getGamertagFor(bedrockId).join();
return Optional.of(
new Link(player)
.bedrockId(new UUID(0, bedrockId))
.bedrockUsername(bedrockTag)
);
} else {
return Optional.empty(); // No match found
}
}
}
} catch (SQLException exception) {
throw new CompletionException("Error while finding Java link", exception);
public CompletableFuture<Optional<Link>> attemptFindBedrockLink(Player player, String bedrockTag) {
return attemptFindLink(
"SELECT `java_id`, `java_name` FROM `links` WHERE `bedrock_id` = ?",
stmt -> stmt.setLong(1, player.getUniqueId().getLeastSignificantBits()),
resultSet -> {
UUID javaId = UUID.fromString(resultSet.getString("java_id"));
String javaName = resultSet.getString("java_name");
return Optional.of(new Link()
.bedrockId(player.getUniqueId())
.bedrockUsername(bedrockTag)
.javaId(javaId)
.javaUsername(javaName));
}
);
}

private CompletableFuture<Optional<Link>> attemptFindLink(
String query,
ThrowingConsumer<PreparedStatement> parameterSetter,
ThrowingFunction<ResultSet, Optional<Link>> resultProcessor
) {
return CompletableFuture.supplyAsync(() -> {
try (Connection connection = dataSource.getConnection();
PreparedStatement queryStmt = connection.prepareStatement(query)) {
parameterSetter.accept(queryStmt);

try (ResultSet resultSet = queryStmt.executeQuery()) {
if (resultSet.next()) {
return resultProcessor.apply(resultSet);
} else {
return Optional.empty();
}
},
executorService);
}

} catch (SQLException exception) {
throw new CompletionException("Error while finding link! ", exception);
}
}, executorService);
}

@FunctionalInterface
interface ThrowingConsumer<T> {
void accept(T t) throws SQLException;
}

@FunctionalInterface
interface ThrowingFunction<T, R> {
R apply(T t) throws SQLException;
}

public void cleanupTempLinks() {
IntSet removedLinks = new IntArraySet();
Expand Down
12 changes: 10 additions & 2 deletions src/main/java/org/geysermc/globallinkserver/util/CommandUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.geysermc.globallinkserver.GlobalLinkServer;
import org.geysermc.globallinkserver.link.Link;
import org.geysermc.globallinkserver.link.LinkManager;
import org.geysermc.globallinkserver.link.TempLink;

Expand Down Expand Up @@ -111,15 +112,15 @@ public int linkWithCode(CommandContext<CommandSourceStack> ctx) {
public int unlink(CommandContext<CommandSourceStack> ctx) {
Player player = getPlayer(ctx);

if (!Utils.isLinked(player)) {
Link currentLink = Utils.getLink(player);
if (currentLink == null) {
player.sendMessage(Component.text("You are not currently linked!").color(NamedTextColor.RED));
return 0;
}

linkManager.unlinkAccount(player).whenComplete((result, error) -> {
if (error != null) {
error.printStackTrace();
System.out.println(result);
player.sendMessage(Component.text("An unknown error occurred while unlinking your account. Try it again later!")
.color(NamedTextColor.RED));
return;
Expand All @@ -128,7 +129,14 @@ public int unlink(CommandContext<CommandSourceStack> ctx) {
Bukkit.getScheduler().callSyncMethod(GlobalLinkServer.plugin, () -> {
if (result) {
player.kick(Component.text("You are successfully unlinked.").color(NamedTextColor.GREEN));

// Lookup whether the player's link is online, kick em too
Player otherLink = Bukkit.getServer().getPlayer(currentLink.getOpposed(player));
if (otherLink != null) {
otherLink.kick(Component.text("You are successfully unlinked.").color(NamedTextColor.GREEN));
}
} else {
// Technically impossible
player.kick(Component.text("You are not linked to any account!").color(NamedTextColor.RED));
}

Expand Down
56 changes: 34 additions & 22 deletions src/main/java/org/geysermc/globallinkserver/util/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,42 @@
import com.mojang.brigadier.context.CommandContext;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.globallinkserver.GlobalLinkServer;
import org.geysermc.globallinkserver.link.Link;

import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

@SuppressWarnings("UnstableApiUsage")
public class Utils {

private static final Map<UUID, Link> linkedPlayers = new Object2ObjectOpenHashMap<>();
private static final Set<UUID> lookupInProcess = new ObjectOpenHashSet<>();

public static boolean shouldShowSuggestion(Player player) {
return !lookupInProcess.contains(player.getUniqueId());
}

public static boolean isBedrockPlayerId(Player player) {
return FloodgateApi.getInstance().isFloodgateId(player.getUniqueId());
return player.getUniqueId().version() == 0;
}

public static boolean isLinked(Player player) {
return linkedPlayers.containsKey(player.getUniqueId());
}

public static @Nullable Link getLink(Player player) {
public static Link getLink(Player player) {
return linkedPlayers.get(player.getUniqueId());
}

Expand All @@ -44,32 +52,36 @@ public static Player getPlayer(CommandContext<CommandSourceStack> ctx) {
}

public static void processJoin(Player player) {
lookupInProcess.add(player.getUniqueId());

FloodgatePlayer floodgatePlayer = FloodgateApi.getInstance().getPlayer(player.getUniqueId());
if (floodgatePlayer == null) {
// Not dealing with a Bedrock player - now check if this Java player has a link
GlobalLinkServer.linkManager.attemptFindJavaLink(player).whenComplete((link, throwable) -> {
if (throwable != null) {
player.sendMessage(Component.text("Failed to find Java link.").color(NamedTextColor.RED));
throwable.printStackTrace();
return;
}

link.ifPresent(value -> linkedPlayers.put(player.getUniqueId(), value));
});
CompletableFuture<Optional<Link>> linkFuture;

if (floodgatePlayer != null) {
// Dealing with a Bedrock player
linkFuture = GlobalLinkServer.linkManager.attemptFindBedrockLink(player, floodgatePlayer.getUsername());
} else {
// easy
if (floodgatePlayer.isLinked()) {
linkedPlayers.put(player.getUniqueId(), new Link()
.javaUsername(player.getName())
.javaId(player.getUniqueId())
.bedrockUsername(floodgatePlayer.getUsername())
.bedrockId(floodgatePlayer.getJavaUniqueId()));
}
linkFuture = GlobalLinkServer.linkManager.attemptFindJavaLink(player);
}

// Handle the result of the lookup
linkFuture.whenComplete((link, throwable) -> handleLinkLookupResult(player, link, throwable));
}

private static void handleLinkLookupResult(Player player, Optional<Link> link, Throwable throwable) {
lookupInProcess.remove(player.getUniqueId());
if (throwable != null) {
player.sendMessage(Component.text("Failed to find current link!").color(NamedTextColor.RED));
throwable.printStackTrace();
return;
}

link.ifPresent(value -> linkedPlayers.put(player.getUniqueId(), value));
}

public static void processLeave(Player player) {
linkedPlayers.remove(player.getUniqueId());
lookupInProcess.remove(player.getUniqueId());
}

public static void sendCurrentLinkInfo(Player player) {
Expand Down

0 comments on commit 6571d42

Please sign in to comment.