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<>();
params.put("web_email", userEmail);
if (userEmail != null) {
params.put("web_email", userEmail);
}
if (playerUuid != null) {
String simpleUuid = playerUuid.toString().replace("-", "");

View File

@@ -1,15 +1,13 @@
package online.mineroo.common;
import java.util.Locale;
import java.util.ResourceBundle;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import java.util.Locale;
import java.util.ResourceBundle;
public class MessageManager {
private ResourceBundle bundle;
private final MiniMessage miniMessage;
@@ -54,7 +52,7 @@ public class MessageManager {
return miniMessage.deserialize(raw, builder.build());
}
private String getString(String key) {
public String getString(String key) {
try {
return bundle.getString(key);
} 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.exceptions.CommandSyntaxException;
import com.mojang.brigadier.tree.LiteralCommandNode;
import io.papermc.paper.command.brigadier.CommandSourceStack;
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.PlayerBindStatusEnum;
import online.mineroo.common.MessageManager;
import online.mineroo.paper.MinerooCore;
import online.mineroo.paper.ProxyNetworkService;
import org.bukkit.entity.Player;
import org.slf4j.Logger;
public class BindCommand {
private MinerooCore plugin;
public BindCommand(MinerooCore plugin) {
@@ -23,28 +28,61 @@ public class BindCommand {
}
public LiteralCommandNode<CommandSourceStack> build() {
LiteralArgumentBuilder<CommandSourceStack> bind = Commands.literal("bind")
.executes(context -> {
CommandSourceStack sender = context.getSource();
if (!sender.getSender().hasPermission("mineroo.admin.bind.server")) {
return bindPlayer(context);
}
return 0;
});
LiteralArgumentBuilder<CommandSourceStack> bind = Commands.literal("bind").executes(context -> {
CommandSourceStack sender = context.getSource();
if (!sender.getSender().hasPermission("mineroo.admin.bind.server")) {
return bindPlayer(context);
}
return 0;
});
bind.then(
Commands.literal("server")
.requires(sender -> sender.getSender().hasPermission("mineroo.admin.bind.server"))
.executes(this::bindServer));
bind.then(Commands.literal("server")
.requires(sender -> sender.getSender().hasPermission("mineroo.admin.bind.server"))
.executes(this::bindServer));
bind.then(
Commands.literal("player").requires(sender -> sender.getSender().hasPermission("mineroo.admin.bind.server"))
.executes(this::bindPlayer));
bind.then(Commands.literal("player")
.requires(sender -> sender.getSender().hasPermission("mineroo.admin.bind.server"))
.executes(this::bindPlayer));
return bind.build();
}
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;
}
@@ -58,13 +96,16 @@ public class BindCommand {
private void sendMotdTokenToVelocity(String motdToken) {
// 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)
return;
com.google.common.io.ByteArrayDataOutput out = com.google.common.io.ByteStreams.newDataOutput();
out.writeUTF(online.mineroo.common.ProtocolConstants.BIND_MOTD_TOKEN);
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 {
@@ -73,9 +114,9 @@ public class BindCommand {
Logger logger = plugin.getSLF4JLogger();
BindRequest bindRequest = plugin.getBindRequest();
try {
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) {
address = "$hostname";
port = "$port";
@@ -84,8 +125,9 @@ public class BindCommand {
// 1. Check binding status
if (bindRequest.checkBindStatus(address, port).join()) {
plugin.getMessageManager().get("command.bind.server.already-bound");
context.getSource().getSender()
.sendMessage(plugin.getMessageManager().get("command.bind.server.already-bound"));
context.getSource().getSender().sendMessage(
plugin.getMessageManager().get("command.bind.server.already-bound")
);
return;
}
// 2. Initiate MOTD verification
@@ -94,7 +136,9 @@ public class BindCommand {
String motdToken = bindRequest.getMotdToken();
// Send MOTD token to Velocity
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 {
Thread.sleep(2 * 60 * 1000 + 5000); // 2m 5s
} catch (InterruptedException e) {
@@ -105,16 +149,23 @@ public class BindCommand {
boolean success = false;
int maxRetries = 3;
for (int i = 1; i <= maxRetries; i++) {
if (bindRequest.finalizeServerBinding(
plugin.getConfigObject().getServer().getServerName(),
plugin.getConfigObject().getServer().getDescription()).join()) {
if (bindRequest
.finalizeServerBinding(
plugin.getConfigObject().getServer().getServerName(),
plugin.getConfigObject().getServer().getDescription()
)
.join()) {
success = true;
break;
}
if (i < maxRetries) {
context.getSource().getSender().sendMessage(
plugin.getMessageManager().get("command.bind.server.retry", "current", String.valueOf(i), "max",
String.valueOf(maxRetries)));
context.getSource().getSender().sendMessage(plugin.getMessageManager().get(
"command.bind.server.retry",
"current",
String.valueOf(i),
"max",
String.valueOf(maxRetries)
));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
@@ -123,18 +174,24 @@ public class BindCommand {
}
}
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 {
context.getSource().getSender().sendMessage(plugin.getMessageManager().get("command.bind.server.failed"));
context.getSource().getSender().sendMessage(
plugin.getMessageManager().get("command.bind.server.failed")
);
}
} else {
context.getSource().getSender()
.sendMessage(plugin.getMessageManager().get("command.bind.server.inital.failed"));
context.getSource().getSender().sendMessage(
plugin.getMessageManager().get("command.bind.server.inital.failed")
);
}
} catch (Exception ex) {
logger.error("Bind process error", ex);
context.getSource().getSender().sendMessage(
plugin.getMessageManager().get("command.bind.server.failed"));
plugin.getMessageManager().get("command.bind.server.failed")
);
} finally {
// After binding is complete, you can send a message to clear the MOTD token
applyMotdToken(""); // Clear MOTD

View File

@@ -3,6 +3,7 @@ package online.mineroo.paper.listeners;
import com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent;
import io.papermc.paper.connection.PlayerConfigurationConnection;
import io.papermc.paper.connection.PlayerGameConnection;
import io.papermc.paper.dialog.Dialog;
import io.papermc.paper.dialog.DialogResponseView;
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.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.key.Key;
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.paper.Config;
import online.mineroo.paper.MinerooCore;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.jspecify.annotations.NullMarked;
import org.slf4j.Logger;
@NullMarked
public class PlayerBindListener implements Listener {
@@ -98,7 +100,7 @@ public class PlayerBindListener implements Listener {
} else if (bindResponse.getStatus() == PlayerBindEnum.PENDING) {
audience.closeDialog();
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));
} else {
audience.closeDialog();
@@ -134,17 +136,40 @@ public class PlayerBindListener implements Listener {
@EventHandler
void onHandleDialog(PlayerCustomClickEvent event) {
Logger logger = plugin.getSLF4JLogger();
// Handle custom click only for configuration connection.
if (!(event.getCommonConnection()
instanceof PlayerConfigurationConnection configurationConnection)) {
return;
// Main handlr only use for PlayerConfigureationConnection,
// Inside Block use for handle command bind request.
if (event.getCommonConnection() instanceof PlayerGameConnection playerConn) {
// handle command bind
Player player = playerConn.getPlayer();
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();
if (uniqueId == null) {
private void PlayerBindProcess(
PlayerCustomClickEvent event, UUID playerUuid, Consumer<PlayerBindResponse> callback
) {
if (playerUuid == null) {
return;
}
@@ -157,32 +182,29 @@ public class PlayerBindListener implements Listener {
}
String userEmail = view.getText("user_email");
String playerName = "Unknown";
if (event.getCommonConnection() instanceof PlayerConfigurationConnection conn) {
BindRequest bindRequest = plugin.getBindRequest();
String playerName = conn.getProfile().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;
});
;
playerName = conn.getProfile().getName();
} else if (event.getCommonConnection() instanceof PlayerGameConnection conn) {
playerName = conn.getPlayer().getName();
}
} else if (key.equals(Key.key("mineroo:bind_user/cancel"))) {
// If it is the same as the agree one, set the result to true.
BindRequest bindRequest = plugin.getBindRequest();
setConnectionJoinResult(
uniqueId, BindRequest.createPlayerBindErrorResponse("Canceled the bind requirement")
);
bindRequest.PlayerBindRequest(userEmail, playerUuid, playerName)
.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.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
command.reload.success=<green>配置文件重载成功!耗时 <time>ms</green>
command.reload.failed=<red>配置文件重载失败,请查看控制台日志。</red>