feat: code

This commit is contained in:
2025-12-27 11:39:20 +08:00
parent 78cff5697e
commit 486f1dc150
5 changed files with 160 additions and 74 deletions

View File

@@ -282,7 +282,9 @@ public class BindRequest {
Map<String, String> params = new HashMap<>(); Map<String, String> params = new HashMap<>();
params.put("web_email", userEmail); if (userEmail != null) {
params.put("web_email", userEmail);
}
if (playerUuid != null) { if (playerUuid != null) {
String simpleUuid = playerUuid.toString().replace("-", ""); String simpleUuid = playerUuid.toString().replace("-", "");

View File

@@ -1,15 +1,13 @@
package online.mineroo.common; package online.mineroo.common;
import java.util.Locale;
import java.util.ResourceBundle;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import java.util.Locale;
import java.util.ResourceBundle;
public class MessageManager { public class MessageManager {
private ResourceBundle bundle; private ResourceBundle bundle;
private final MiniMessage miniMessage; private final MiniMessage miniMessage;
@@ -54,7 +52,7 @@ public class MessageManager {
return miniMessage.deserialize(raw, builder.build()); return miniMessage.deserialize(raw, builder.build());
} }
private String getString(String key) { public String getString(String key) {
try { try {
return bundle.getString(key); return bundle.getString(key);
} catch (Exception e) { } catch (Exception e) {

View File

@@ -5,17 +5,22 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.LiteralCommandNode;
import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.command.brigadier.Commands;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import java.util.UUID;
import net.kyori.adventure.key.Key;
import online.mineroo.common.BindRequest; import online.mineroo.common.BindRequest;
import online.mineroo.common.BindRequest.PlayerBindStatusEnum;
import online.mineroo.common.MessageManager;
import online.mineroo.paper.MinerooCore; import online.mineroo.paper.MinerooCore;
import online.mineroo.paper.ProxyNetworkService; import online.mineroo.paper.ProxyNetworkService;
import org.bukkit.entity.Player;
import org.slf4j.Logger; import org.slf4j.Logger;
public class BindCommand { public class BindCommand {
private MinerooCore plugin; private MinerooCore plugin;
public BindCommand(MinerooCore plugin) { public BindCommand(MinerooCore plugin) {
@@ -23,28 +28,61 @@ public class BindCommand {
} }
public LiteralCommandNode<CommandSourceStack> build() { public LiteralCommandNode<CommandSourceStack> build() {
LiteralArgumentBuilder<CommandSourceStack> bind = Commands.literal("bind") LiteralArgumentBuilder<CommandSourceStack> bind = Commands.literal("bind").executes(context -> {
.executes(context -> { CommandSourceStack sender = context.getSource();
CommandSourceStack sender = context.getSource(); if (!sender.getSender().hasPermission("mineroo.admin.bind.server")) {
if (!sender.getSender().hasPermission("mineroo.admin.bind.server")) { return bindPlayer(context);
return bindPlayer(context); }
} return 0;
return 0; });
});
bind.then( bind.then(Commands.literal("server")
Commands.literal("server") .requires(sender -> sender.getSender().hasPermission("mineroo.admin.bind.server"))
.requires(sender -> sender.getSender().hasPermission("mineroo.admin.bind.server")) .executes(this::bindServer));
.executes(this::bindServer));
bind.then( bind.then(Commands.literal("player")
Commands.literal("player").requires(sender -> sender.getSender().hasPermission("mineroo.admin.bind.server")) .requires(sender -> sender.getSender().hasPermission("mineroo.admin.bind.server"))
.executes(this::bindPlayer)); .executes(this::bindPlayer));
return bind.build(); return bind.build();
} }
private int bindPlayer(CommandContext<CommandSourceStack> context) { private int bindPlayer(CommandContext<CommandSourceStack> context) {
Logger logger = this.plugin.getSLF4JLogger();
MessageManager messageManager = this.plugin.getMessageManager();
if (context.getSource().getExecutor() instanceof Player player) {
UUID uuid = player.getUniqueId();
if (uuid == null) {
return Command.SINGLE_SUCCESS;
}
try {
this.plugin.getBindRequest().checkPlayerBindStatus(uuid, null).thenAccept(response -> {
PlayerBindStatusEnum status = response.getStatus();
plugin.getSLF4JLogger().debug(status.toString());
if (status == PlayerBindStatusEnum.BOUND) {
player.sendMessage(messageManager.get("info.bind.player.already-bound"));
} else if (status == PlayerBindStatusEnum.NOT_BOUND) {
Dialog dialog = RegistryAccess.registryAccess()
.getRegistry(RegistryKey.DIALOG)
.get(Key.key("mineroo:bind_user"));
if (dialog == null) {
logger.error("load user bind dialog failed.");
player.sendMessage(messageManager.get("info.bind.player.dialog-undefined"));
}
// TODO: Need a timeout check
player.showDialog(dialog);
}
});
} finally {
}
}
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
} }
@@ -58,13 +96,16 @@ public class BindCommand {
private void sendMotdTokenToVelocity(String motdToken) { private void sendMotdTokenToVelocity(String motdToken) {
// Send via any online player (Plugin Messaging must be attached to a player) // Send via any online player (Plugin Messaging must be attached to a player)
org.bukkit.entity.Player player = org.bukkit.Bukkit.getOnlinePlayers().stream().findFirst().orElse(null); org.bukkit.entity.Player player =
org.bukkit.Bukkit.getOnlinePlayers().stream().findFirst().orElse(null);
if (player == null) if (player == null)
return; return;
com.google.common.io.ByteArrayDataOutput out = com.google.common.io.ByteStreams.newDataOutput(); com.google.common.io.ByteArrayDataOutput out = com.google.common.io.ByteStreams.newDataOutput();
out.writeUTF(online.mineroo.common.ProtocolConstants.BIND_MOTD_TOKEN); out.writeUTF(online.mineroo.common.ProtocolConstants.BIND_MOTD_TOKEN);
out.writeUTF(motdToken); out.writeUTF(motdToken);
player.sendPluginMessage(plugin, online.mineroo.common.ProtocolConstants.PROTOCOL_CHANNEL, out.toByteArray()); player.sendPluginMessage(
plugin, online.mineroo.common.ProtocolConstants.PROTOCOL_CHANNEL, out.toByteArray()
);
} }
private int bindServer(CommandContext<CommandSourceStack> context) throws CommandSyntaxException { private int bindServer(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
@@ -73,9 +114,9 @@ public class BindCommand {
Logger logger = plugin.getSLF4JLogger(); Logger logger = plugin.getSLF4JLogger();
BindRequest bindRequest = plugin.getBindRequest(); BindRequest bindRequest = plugin.getBindRequest();
try { try {
String address = plugin.getConfigObject().getServer().getServerBind().getAddress(); String address = plugin.getConfigObject().getServer().getServerBind().getAddress();
String port = String.valueOf(plugin.getConfigObject().getServer().getServerBind().getPort()); String port =
String.valueOf(plugin.getConfigObject().getServer().getServerBind().getPort());
if (plugin.getNetworkService() instanceof ProxyNetworkService) { if (plugin.getNetworkService() instanceof ProxyNetworkService) {
address = "$hostname"; address = "$hostname";
port = "$port"; port = "$port";
@@ -84,8 +125,9 @@ public class BindCommand {
// 1. Check binding status // 1. Check binding status
if (bindRequest.checkBindStatus(address, port).join()) { if (bindRequest.checkBindStatus(address, port).join()) {
plugin.getMessageManager().get("command.bind.server.already-bound"); plugin.getMessageManager().get("command.bind.server.already-bound");
context.getSource().getSender() context.getSource().getSender().sendMessage(
.sendMessage(plugin.getMessageManager().get("command.bind.server.already-bound")); plugin.getMessageManager().get("command.bind.server.already-bound")
);
return; return;
} }
// 2. Initiate MOTD verification // 2. Initiate MOTD verification
@@ -94,7 +136,9 @@ public class BindCommand {
String motdToken = bindRequest.getMotdToken(); String motdToken = bindRequest.getMotdToken();
// Send MOTD token to Velocity // Send MOTD token to Velocity
applyMotdToken(motdToken); applyMotdToken(motdToken);
context.getSource().getSender().sendMessage(plugin.getMessageManager().get("command.bind.server.wait")); context.getSource().getSender().sendMessage(
plugin.getMessageManager().get("command.bind.server.wait")
);
try { try {
Thread.sleep(2 * 60 * 1000 + 5000); // 2m 5s Thread.sleep(2 * 60 * 1000 + 5000); // 2m 5s
} catch (InterruptedException e) { } catch (InterruptedException e) {
@@ -105,16 +149,23 @@ public class BindCommand {
boolean success = false; boolean success = false;
int maxRetries = 3; int maxRetries = 3;
for (int i = 1; i <= maxRetries; i++) { for (int i = 1; i <= maxRetries; i++) {
if (bindRequest.finalizeServerBinding( if (bindRequest
plugin.getConfigObject().getServer().getServerName(), .finalizeServerBinding(
plugin.getConfigObject().getServer().getDescription()).join()) { plugin.getConfigObject().getServer().getServerName(),
plugin.getConfigObject().getServer().getDescription()
)
.join()) {
success = true; success = true;
break; break;
} }
if (i < maxRetries) { if (i < maxRetries) {
context.getSource().getSender().sendMessage( context.getSource().getSender().sendMessage(plugin.getMessageManager().get(
plugin.getMessageManager().get("command.bind.server.retry", "current", String.valueOf(i), "max", "command.bind.server.retry",
String.valueOf(maxRetries))); "current",
String.valueOf(i),
"max",
String.valueOf(maxRetries)
));
try { try {
Thread.sleep(10000); Thread.sleep(10000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
@@ -123,18 +174,24 @@ public class BindCommand {
} }
} }
if (success) { if (success) {
context.getSource().getSender().sendMessage(plugin.getMessageManager().get("command.bind.server.success")); context.getSource().getSender().sendMessage(
plugin.getMessageManager().get("command.bind.server.success")
);
} else { } else {
context.getSource().getSender().sendMessage(plugin.getMessageManager().get("command.bind.server.failed")); context.getSource().getSender().sendMessage(
plugin.getMessageManager().get("command.bind.server.failed")
);
} }
} else { } else {
context.getSource().getSender() context.getSource().getSender().sendMessage(
.sendMessage(plugin.getMessageManager().get("command.bind.server.inital.failed")); plugin.getMessageManager().get("command.bind.server.inital.failed")
);
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Bind process error", ex); logger.error("Bind process error", ex);
context.getSource().getSender().sendMessage( context.getSource().getSender().sendMessage(
plugin.getMessageManager().get("command.bind.server.failed")); plugin.getMessageManager().get("command.bind.server.failed")
);
} finally { } finally {
// After binding is complete, you can send a message to clear the MOTD token // After binding is complete, you can send a message to clear the MOTD token
applyMotdToken(""); // Clear MOTD applyMotdToken(""); // Clear MOTD

View File

@@ -3,6 +3,7 @@ package online.mineroo.paper.listeners;
import com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent; import com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent;
import io.papermc.paper.connection.PlayerConfigurationConnection; import io.papermc.paper.connection.PlayerConfigurationConnection;
import io.papermc.paper.connection.PlayerGameConnection;
import io.papermc.paper.dialog.Dialog; import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.dialog.DialogResponseView; import io.papermc.paper.dialog.DialogResponseView;
import io.papermc.paper.event.connection.configuration.AsyncPlayerConnectionConfigureEvent; import io.papermc.paper.event.connection.configuration.AsyncPlayerConnectionConfigureEvent;
@@ -14,6 +15,7 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@@ -24,10 +26,10 @@ import online.mineroo.common.BindRequest.PlayerBindResponse;
import online.mineroo.common.BindRequest.PlayerBindStatusEnum; import online.mineroo.common.BindRequest.PlayerBindStatusEnum;
import online.mineroo.paper.Config; import online.mineroo.paper.Config;
import online.mineroo.paper.MinerooCore; import online.mineroo.paper.MinerooCore;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.NullMarked;
import org.slf4j.Logger;
@NullMarked @NullMarked
public class PlayerBindListener implements Listener { public class PlayerBindListener implements Listener {
@@ -98,7 +100,7 @@ public class PlayerBindListener implements Listener {
} else if (bindResponse.getStatus() == PlayerBindEnum.PENDING) { } else if (bindResponse.getStatus() == PlayerBindEnum.PENDING) {
audience.closeDialog(); audience.closeDialog();
String msg = "Binding request submitted!\nPlease go to Mineroo.Online to confirm the " String msg = "Binding request submitted!\nPlease go to Mineroo.Online to confirm the "
+ "binding. After confirmation, please re-enter the server."; + "binding. After confirmation, please re-enter the server.";
connection.disconnect(Component.text(msg, NamedTextColor.GREEN)); connection.disconnect(Component.text(msg, NamedTextColor.GREEN));
} else { } else {
audience.closeDialog(); audience.closeDialog();
@@ -134,17 +136,40 @@ public class PlayerBindListener implements Listener {
@EventHandler @EventHandler
void onHandleDialog(PlayerCustomClickEvent event) { void onHandleDialog(PlayerCustomClickEvent event) {
Logger logger = plugin.getSLF4JLogger(); // Main handlr only use for PlayerConfigureationConnection,
// Inside Block use for handle command bind request.
// Handle custom click only for configuration connection. if (event.getCommonConnection() instanceof PlayerGameConnection playerConn) {
if (!(event.getCommonConnection() // handle command bind
instanceof PlayerConfigurationConnection configurationConnection)) { Player player = playerConn.getPlayer();
return; UUID playerUuid = player.getUniqueId();
this.PlayerBindProcess(event, playerUuid, response -> {
if (response.getStatus() == PlayerBindEnum.PENDING) {
player.sendMessage(this.plugin.getMessageManager().get("info.bind.player.pending"));
} else if (response.getStatus() == PlayerBindEnum.BOUND) {
player.sendMessage(this.plugin.getMessageManager().get("info.bind.player.success"));
} else {
String statusText =
(response.getStatus() != null) ? response.getStatus().toString() : "UNKNOWN_STATUS";
String messageText =
(response.getMessage() != null) ? response.getMessage() : "Bind Failed";
String fullMessage = "[ " + statusText + " ]: " + messageText;
player.sendMessage(Component.text(fullMessage, NamedTextColor.RED));
}
});
} else if (event.getCommonConnection()
instanceof PlayerConfigurationConnection configurationConnection) {
// handle required configuration event bind
UUID playerUuid = configurationConnection.getProfile().getId();
this.PlayerBindProcess(event, playerUuid, response -> {
setConnectionJoinResult(playerUuid, response);
});
} }
}
UUID uniqueId = configurationConnection.getProfile().getId(); private void PlayerBindProcess(
PlayerCustomClickEvent event, UUID playerUuid, Consumer<PlayerBindResponse> callback
if (uniqueId == null) { ) {
if (playerUuid == null) {
return; return;
} }
@@ -157,32 +182,29 @@ public class PlayerBindListener implements Listener {
} }
String userEmail = view.getText("user_email"); String userEmail = view.getText("user_email");
String playerName = "Unknown";
if (event.getCommonConnection() instanceof PlayerConfigurationConnection conn) { if (event.getCommonConnection() instanceof PlayerConfigurationConnection conn) {
BindRequest bindRequest = plugin.getBindRequest(); playerName = conn.getProfile().getName();
String playerName = conn.getProfile().getName(); } else if (event.getCommonConnection() instanceof PlayerGameConnection conn) {
playerName = conn.getPlayer().getName();
bindRequest.PlayerBindRequest(userEmail, uniqueId, conn.getProfile().getName())
.thenAccept(response -> { setConnectionJoinResult(uniqueId, response); })
.exceptionally(ex -> {
logger.error("Error occurred during bind request for " + playerName, ex);
setConnectionJoinResult(
uniqueId,
BindRequest.createPlayerBindErrorResponse(
"Network request failed, please contact the server administrator."
)
);
return null;
});
;
} }
} else if (key.equals(Key.key("mineroo:bind_user/cancel"))) { BindRequest bindRequest = plugin.getBindRequest();
// If it is the same as the agree one, set the result to true.
setConnectionJoinResult( bindRequest.PlayerBindRequest(userEmail, playerUuid, playerName)
uniqueId, BindRequest.createPlayerBindErrorResponse("Canceled the bind requirement") .thenAccept(response -> { callback.accept(response); })
); .exceptionally(ex -> {
callback.accept(BindRequest.createPlayerBindErrorResponse(
"Network request failed: " + ex.getMessage()
));
return null;
});
} else if (key.equals(Key.key("mineroo:bind_user/cancel"))) {
callback.accept(BindRequest.createPlayerBindErrorResponse(
this.plugin.getMessageManager().getString("info.bind.player.canceled")
));
} }
} }

View File

@@ -15,6 +15,13 @@ command.bind.server.success=<green>绑定成功!<green>
command.bind.server.failed=<red>绑定失败。</red> command.bind.server.failed=<red>绑定失败。</red>
command.bind.server.retry=<yellow>验证未通过10 秒后重试 (<current>/<max>) ...</yellow> command.bind.server.retry=<yellow>验证未通过10 秒后重试 (<current>/<max>) ...</yellow>
## Player Bind [Both for screen and command]
info.bind.player.already-bound=<yellow>当前用户已绑定</yellow>
info.bind.player.dialog-undefined=<red>Dialog 加载失败,请联系管理员</red>
info.bind.player.pending = "<green>绑定申请已发送,请登录 Mineroo 网站进行后续操作。</green>"
info.bind.player.success = "<green>绑定成功!</green>"
info.bind.player.canceled = "绑定请求已取消。"
# Reload Command # Reload Command
command.reload.success=<green>配置文件重载成功!耗时 <time>ms</green> command.reload.success=<green>配置文件重载成功!耗时 <time>ms</green>
command.reload.failed=<red>配置文件重载失败,请查看控制台日志。</red> command.reload.failed=<red>配置文件重载失败,请查看控制台日志。</red>