From 4e1faa3edc863640689dec5fec16bd56807dcedb Mon Sep 17 00:00:00 2001 From: YuKun Liu Date: Sat, 13 Dec 2025 23:42:41 -0800 Subject: [PATCH] feat: code --- .../online/mineroo/common/BindRequest.java | 1 - .../mineroo/common/ProxyNetworkRequest.java | 41 +++++++ .../java/online/mineroo/paper/Config.java | 32 ++++-- .../mineroo/paper/ConfigSerializable.java | 0 .../online/mineroo/paper/MinerooCore.java | 45 ++++++++ .../mineroo/paper/ProxyNetworkService.java | 102 ++++++++++++++++++ .../mineroo/paper/commands/BindCommand.java | 47 ++++++++ .../mineroo/paper/commands/MainCommand.java | 59 ++++++++++ paper/src/main/resources/config.yml | 10 +- .../main/resources/i18n/messages.properties | 2 + paper/src/main/resources/plugin.yml | 21 ++++ 11 files changed, 349 insertions(+), 11 deletions(-) create mode 100644 common/src/main/java/online/mineroo/common/ProxyNetworkRequest.java delete mode 100644 paper/src/main/java/online/mineroo/paper/ConfigSerializable.java create mode 100644 paper/src/main/java/online/mineroo/paper/ProxyNetworkService.java create mode 100644 paper/src/main/java/online/mineroo/paper/commands/BindCommand.java create mode 100644 paper/src/main/java/online/mineroo/paper/commands/MainCommand.java diff --git a/common/src/main/java/online/mineroo/common/BindRequest.java b/common/src/main/java/online/mineroo/common/BindRequest.java index 3edce4e..b31585a 100644 --- a/common/src/main/java/online/mineroo/common/BindRequest.java +++ b/common/src/main/java/online/mineroo/common/BindRequest.java @@ -2,7 +2,6 @@ package online.mineroo.common; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import online.mineroo.common.NetworkServiceInterface; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; diff --git a/common/src/main/java/online/mineroo/common/ProxyNetworkRequest.java b/common/src/main/java/online/mineroo/common/ProxyNetworkRequest.java new file mode 100644 index 0000000..cd3ddf8 --- /dev/null +++ b/common/src/main/java/online/mineroo/common/ProxyNetworkRequest.java @@ -0,0 +1,41 @@ +package online.mineroo.common; + +import com.google.gson.JsonObject; +import java.util.Map; +import java.util.UUID; + +public class ProxyNetworkRequest { + private final String requestId; + private final String method; // "GET" or "POST" + private final String endpoint; + private final Map params; + private final String jsonBody; + + public ProxyNetworkRequest(String method, String endpoint, Map params, JsonObject body) { + this.requestId = UUID.randomUUID().toString(); + this.method = method; + this.endpoint = endpoint; + this.params = params; + this.jsonBody = (body != null) ? body.toString() : null; + } + + public String getRequestId() { + return requestId; + } + + public String getMethod() { + return method; + } + + public String getEndpoint() { + return endpoint; + } + + public Map getParams() { + return params; + } + + public String getJsonBody() { + return jsonBody; + } +} diff --git a/paper/src/main/java/online/mineroo/paper/Config.java b/paper/src/main/java/online/mineroo/paper/Config.java index dd8dda1..98fd0d3 100644 --- a/paper/src/main/java/online/mineroo/paper/Config.java +++ b/paper/src/main/java/online/mineroo/paper/Config.java @@ -5,21 +5,39 @@ import org.bukkit.configuration.file.FileConfiguration; public class Config { private final ServerSection server; - private final PlayerSection player; + private final PlayersSection player; + private final ProxySection proxy; public Config(FileConfiguration config) { + this.proxy = new ProxySection(config); this.server = new ServerSection(config); - this.player = new PlayerSection(config); + this.player = new PlayersSection(config); + } + + public ProxySection getProxy() { + return proxy; } public ServerSection getServer() { return server; } - public PlayerSection getPlayer() { + public PlayersSection getPlayer() { return player; } + public static class ProxySection { + private final boolean useVelocity; + + public ProxySection(FileConfiguration config) { + this.useVelocity = config.getBoolean("proxy.use_velocity", false); + } + + public boolean isUseVelocity() { + return useVelocity; + } + } + public static class ServerSection { private final String serverName; private final String description; @@ -71,13 +89,13 @@ public class Config { } } - public static class PlayerSection { + public static class PlayersSection { private final PlayerBindSection bind; - public PlayerSection(FileConfiguration config) { + public PlayersSection(FileConfiguration config) { this.bind = new PlayerBindSection( - config.getBoolean("player.bind.required", false), - config.getBoolean("player.bind.share_player_info", true)); + config.getBoolean("players.bind.required", false), + config.getBoolean("players.bind.share_player_info", true)); } public PlayerBindSection getPlayerBind() { diff --git a/paper/src/main/java/online/mineroo/paper/ConfigSerializable.java b/paper/src/main/java/online/mineroo/paper/ConfigSerializable.java deleted file mode 100644 index e69de29..0000000 diff --git a/paper/src/main/java/online/mineroo/paper/MinerooCore.java b/paper/src/main/java/online/mineroo/paper/MinerooCore.java index a9b82ed..28e0ca1 100644 --- a/paper/src/main/java/online/mineroo/paper/MinerooCore.java +++ b/paper/src/main/java/online/mineroo/paper/MinerooCore.java @@ -2,7 +2,11 @@ package online.mineroo.paper; import org.bukkit.plugin.java.JavaPlugin; +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; +import online.mineroo.common.HttpNetworkService; import online.mineroo.common.MessageManager; +import online.mineroo.common.NetworkServiceInterface; +import online.mineroo.paper.commands.MainCommand; import online.mineroo.paper.utils.PlayerBindDialog; import org.bukkit.entity.Player; @@ -13,12 +17,20 @@ import org.bukkit.event.player.PlayerJoinEvent; public class MinerooCore extends JavaPlugin implements Listener { private MessageManager messageManager; + private NetworkServiceInterface networkService; + private Config config; @Override public void onEnable() { saveDefaultConfig(); + reloadAll(); + + this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, commands -> { + commands.registrar().register(new MainCommand(this).build()); + }); + messageManager = new MessageManager(); getServer().getPluginManager().registerEvents(this, this); @@ -38,4 +50,37 @@ public class MinerooCore extends JavaPlugin implements Listener { player.sendMessage(messageManager.get("message.test", "player", player.getName())); } + + public void reloadAll() { + reloadConfig(); + this.config = new Config(getConfig()); + + boolean useVelocity = this.config.getProxy().isUseVelocity(); + if (useVelocity) { + this.networkService = new ProxyNetworkService(this); + getLogger().info("Using Velocity proxy network service."); + } else { + String token = config.getServer().getServerBind().getBindToken(); + this.networkService = new HttpNetworkService("https://oapi.mineroo.online", "mbind", token); + getLogger().info("Using direct HTTP network service."); + } + + if (this.messageManager == null) { + this.messageManager = new MessageManager(); + } else { + this.messageManager.reload(); + } + } + + public Config getConfigObject() { + return config; + } + + public NetworkServiceInterface getNetworkService() { + return networkService; + } + + public MessageManager getMessageManager() { + return messageManager; + } } diff --git a/paper/src/main/java/online/mineroo/paper/ProxyNetworkService.java b/paper/src/main/java/online/mineroo/paper/ProxyNetworkService.java new file mode 100644 index 0000000..792424b --- /dev/null +++ b/paper/src/main/java/online/mineroo/paper/ProxyNetworkService.java @@ -0,0 +1,102 @@ +package online.mineroo.paper; + +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import online.mineroo.common.ProxyNetworkRequest; +import online.mineroo.common.NetworkServiceInterface; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.plugin.messaging.PluginMessageListener; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +public class ProxyNetworkService implements NetworkServiceInterface, PluginMessageListener { + + private final JavaPlugin plugin; + private final Gson gson = new Gson(); + private final String CHANNEL = "mineroo:net"; + + // Store pending requests + private final Map> pendingRequests = new ConcurrentHashMap<>(); + + public ProxyNetworkService(JavaPlugin plugin) { + this.plugin = plugin; + // Register channels + plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, CHANNEL); + plugin.getServer().getMessenger().registerIncomingPluginChannel(plugin, CHANNEL, this); + } + + @Override + public CompletableFuture getData(String endpoint, Map params) { + return sendRequest("GET", endpoint, params, null); + } + + @Override + public CompletableFuture postData(String endpoint, JsonObject body) { + return sendRequest("POST", endpoint, null, body); + } + + private CompletableFuture sendRequest(String method, String endpoint, Map params, + JsonObject body) { + CompletableFuture future = new CompletableFuture<>(); + + // 1. Check if there is any player online (Plugin Message must be sent via a + // player) + Player player = com.google.common.collect.Iterables.getFirst(Bukkit.getOnlinePlayers(), null); + if (player == null) { + future.completeExceptionally(new IllegalStateException("No player online to proxy request to Velocity")); + return future; + } + + // 2. Prepare the packet + ProxyNetworkRequest req = new ProxyNetworkRequest(method, endpoint, params, body); + pendingRequests.put(req.getRequestId(), future); + + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("API_REQ"); // Sub-channel + out.writeUTF(gson.toJson(req)); // Payload + + // 3. Send + player.sendPluginMessage(plugin, CHANNEL, out.toByteArray()); + + // 4. Set timeout (prevent memory leak if Velocity does not respond) + plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, () -> { + CompletableFuture f = pendingRequests.remove(req.getRequestId()); + if (f != null) { + f.completeExceptionally(new java.util.concurrent.TimeoutException("Proxy request timed out")); + } + }, 100L); // 5 seconds timeout + + return future; + } + + @Override + public void onPluginMessageReceived(String channel, Player player, byte[] message) { + if (!channel.equals(CHANNEL)) + return; + + ByteArrayDataInput in = ByteStreams.newDataInput(message); + String subChannel = in.readUTF(); + + if (subChannel.equals("API_RESP")) { + String reqId = in.readUTF(); + boolean success = in.readBoolean(); + String data = in.readUTF(); // If success: body, if failure: error message + + CompletableFuture future = pendingRequests.remove(reqId); + if (future != null) { + if (success) { + future.complete(data); + } else { + future.completeExceptionally(new RuntimeException(data)); + } + } + } + } +} diff --git a/paper/src/main/java/online/mineroo/paper/commands/BindCommand.java b/paper/src/main/java/online/mineroo/paper/commands/BindCommand.java new file mode 100644 index 0000000..c163086 --- /dev/null +++ b/paper/src/main/java/online/mineroo/paper/commands/BindCommand.java @@ -0,0 +1,47 @@ +package online.mineroo.paper.commands; + +import com.mojang.brigadier.Command; +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 online.mineroo.common.BindRequest; +import online.mineroo.paper.MinerooCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BindCommand { + + private MinerooCore plugin; + + public BindCommand(MinerooCore plugin) { + this.plugin = plugin; + } + + public LiteralCommandNode build() { + LiteralArgumentBuilder bind = Commands.literal("bind"); + + bind.then( + Commands.literal("server") + .requires(sender -> sender.getSender().hasPermission("mineroo.admin.bind.server"))) + .executes(this::bindServer); + + bind.then(Commands.literal("player")); + + return bind.build(); + } + + private int bindServer(CommandContext context) throws CommandSyntaxException { + // Use slf4j logger for BindRequest to keep log style consistent across + // platforms + Logger logger = LoggerFactory.getLogger("MinerooPaper"); + BindRequest bindRequest = new BindRequest( + logger, + plugin.getNetworkService()); + + return Command.SINGLE_SUCCESS; + } +} diff --git a/paper/src/main/java/online/mineroo/paper/commands/MainCommand.java b/paper/src/main/java/online/mineroo/paper/commands/MainCommand.java new file mode 100644 index 0000000..22af9b8 --- /dev/null +++ b/paper/src/main/java/online/mineroo/paper/commands/MainCommand.java @@ -0,0 +1,59 @@ +package online.mineroo.paper.commands; + +import org.bukkit.entity.Player; + +import com.mojang.brigadier.Command; +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 online.mineroo.paper.MinerooCore; +import online.mineroo.common.MessageManager; +import net.kyori.adventure.text.Component; + +public class MainCommand { + + private final MinerooCore plugin; + + public MainCommand(MinerooCore plugin) { + this.plugin = plugin; + } + + public LiteralCommandNode build() { + LiteralArgumentBuilder root = Commands.literal("mineroo"); + + root.then( + Commands.literal("reload") + .requires(sender -> sender.getSender().hasPermission("mineroo.admin.reload")) + .executes(this::executeReload)); + + root.then(new BindCommand(plugin).build()); + + return root.build(); + } + + private int executeReload(CommandContext context) throws CommandSyntaxException { + // Reload plugin config and message manager + plugin.reloadConfig(); + plugin.reloadAll(); + + if (context.getSource().getExecutor() instanceof Player player) { + player.updateCommands(); + } + + MessageManager messageManager = plugin.getMessageManager(); + + messageManager.reload(); + + // Send feedback + Component msg = messageManager.get("message.reload.success"); + context.getSource().getSender().sendMessage(msg); + + return Command.SINGLE_SUCCESS; + } + +} diff --git a/paper/src/main/resources/config.yml b/paper/src/main/resources/config.yml index b71bd50..fcafae9 100644 --- a/paper/src/main/resources/config.yml +++ b/paper/src/main/resources/config.yml @@ -1,15 +1,19 @@ +proxy: + use_velocity: false + server: - # Only for bind commands + # Only for `bind server` commands serverName: "" description: "A minecraft server" - # Only for bind commands + # Only for `bind server` commands bind: address: "" port: 0 token: "get token from `mineroo.online/resources/servers` page!" -player: +players: + # for player bind bind: # Only bound player can enter this server # > server will kick all mineroo unknown player diff --git a/paper/src/main/resources/i18n/messages.properties b/paper/src/main/resources/i18n/messages.properties index 5bbead6..e280455 100644 --- a/paper/src/main/resources/i18n/messages.properties +++ b/paper/src/main/resources/i18n/messages.properties @@ -5,3 +5,5 @@ dialog.bind.player.confirm = 确认 dialog.bind.player.cancel = 取消 message.bind.player.success = 请前往 Mineroo 个人中心完成验证。 + +message.reload.success = 插件配置文件重载成功! diff --git a/paper/src/main/resources/plugin.yml b/paper/src/main/resources/plugin.yml index 99a95f0..45df806 100644 --- a/paper/src/main/resources/plugin.yml +++ b/paper/src/main/resources/plugin.yml @@ -1,6 +1,27 @@ +# yaml-language-server: $schema=https://json.schemastore.org/paper-plugin.json name: MinerooCore main: online.mineroo.paper.MinerooCore version: 1.0.0 author: YuKun Liu +website: https://mineroo.online description: Mineroo Base Plugin api-version: "1.21.10" +permissions: + mineroo.admin.reload: + description: "Reload plugin config files." + default: op + + mineroo.admin.bind.server: + description: Bind the server instance + default: op + + mineroo.user.bind.player: + description: Bind user account + default: "true" + + mineroo.admin: + description: Grant all administrative permissions + default: op + children: + mineroo.admin.reload: true + mineroo.admin.bind.server: true