feat: use clang-format

This commit is contained in:
2025-12-18 04:41:06 -08:00
parent 35fae3c3b3
commit 4049bc6a0d
18 changed files with 784 additions and 312 deletions

View File

@@ -5,14 +5,15 @@ package online.mineroo.velocity;
* Handles plugin initialization, configuration loading, and provides access to core components.
*/
import com.google.inject.Inject;
import com.velocitypowered.api.command.CommandManager;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import online.mineroo.common.MessageManager;
import online.mineroo.common.ProtocolConstants;
import online.mineroo.velocity.listeners.BindListener;
import java.io.IOException;
@@ -27,7 +28,7 @@ import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
@Plugin(id = "mineroo-velocity", name = "Mineroo Velocity", version = "0.1.0-SNAPSHOT", url = "https://mineroo.online", description = "Mineroo main plugin", authors = {
"YuKun Liu" })
public class MinerooCore {
public class MinerooVelocity {
// Reference to the Velocity ProxyServer instance
private final ProxyServer server;
@@ -53,7 +54,7 @@ public class MinerooCore {
* @param dataDirectory Directory for plugin data
*/
@Inject
public MinerooCore(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
public MinerooVelocity(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
this.server = server;
this.logger = logger;
this.dataDirectory = dataDirectory;
@@ -61,16 +62,19 @@ public class MinerooCore {
@Subscribe
public void onProxyInitialization(ProxyInitializeEvent event) {
// Register event listeners
this.bindListener = new BindListener();
server.getEventManager().register(this, bindListener);
// Register ChannelListener to handle cross-platform MOTD token messages
server.getEventManager().register(this, new online.mineroo.velocity.listeners.ChannelListener(this));
// Load configuration
reloadConfig();
server.getChannelRegistrar().register(MinecraftChannelIdentifier.from(ProtocolConstants.PROTOCOL_CHANNEL));
// Register ChannelListener to handle cross-platform MOTD token messages
server.getEventManager().register(this, new online.mineroo.velocity.listeners.ChannelListener(this));
// Initialize message manager
this.messageManager = new MessageManager();
}

View File

@@ -1,200 +0,0 @@
// package online.mineroo.velocity.commands;
//
// import java.util.List;
// import java.util.Optional;
// import java.util.concurrent.CompletionException;
// import java.util.stream.Collectors;
// import java.util.stream.Stream;
//
// import org.slf4j.Logger;
//
// import com.velocitypowered.api.command.CommandSource;
// import com.velocitypowered.api.command.SimpleCommand;
//
// import net.kyori.adventure.text.Component;
// import net.kyori.adventure.text.format.NamedTextColor;
// import online.mineroo.common.MessageManager;
// import online.mineroo.common.NetworkServiceInterface;
// import online.mineroo.velocity.Config;
// import online.mineroo.velocity.MinerooCore;
// import online.mineroo.common.BindRequest;
// import online.mineroo.common.HttpNetworkService;
//
// public class MainCommand implements SimpleCommand {
//
// private final MinerooCore plugin;
//
// public MainCommand(MinerooCore plugin) {
// this.plugin = plugin;
// }
//
// @Override
// public void execute(Invocation invocation) {
// String[] args = invocation.arguments();
// if (args.length < 1) {
// sendHelp(invocation.source());
// return;
// }
//
// String subCommand = args[0];
//
// if (subCommand.equals("bind")) {
// bindServer(invocation.source());
// }
//
// if (subCommand.equalsIgnoreCase("reload")) {
// reload(invocation.source());
// }
//
// }
//
// @Override
// public List<String> suggest(Invocation invocation) {
// String[] args = invocation.arguments();
//
// if (args.length == 0) {
// return List.of("bind", "reload");
// }
//
// if (args.length == 1) {
// String input = args[0].toLowerCase();
// return Stream.of("bind", "reload")
// .filter(cmd -> cmd.startsWith(input))
// .collect(Collectors.toList());
// }
//
// return List.of();
// }
//
// public void reload(CommandSource source) {
// MessageManager msg = plugin.getMessageManager();
//
// long start = System.currentTimeMillis();
// boolean success = plugin.reloadConfig();
//
// if (success) {
// long time = System.currentTimeMillis() - start;
// source.sendMessage(msg.get("command.reload.success", "time",
// String.valueOf(time)));
// } else {
//
// source.sendMessage(msg.get("command.reload.failed"));
// }
// return;
// }
//
// public void bindServer(CommandSource source) {
//
// Config config = plugin.getConfig();
// MessageManager msg = plugin.getMessageManager();
//
// String bind_name = config.getServerName();
// String bind_description = config.getDescription();
//
// String bind_token = config.getBind().getBindToken();
// String bind_address = config.getBind().getAddress();
//
// int bind_port =
// Optional.ofNullable(config.getBind().getPort()).orElse(25565);
//
// source.sendMessage(msg.get("command.bind.server.start"));
//
// Logger logger = plugin.getLogger();
//
// // Run in a separate worker thread to allow blocking (Thread.sleep)
// plugin.getServer().getScheduler().buildTask(plugin, () -> {
//
// // 1. Initialize the Network Service locally (Velocity connects directly)
// NetworkServiceInterface networkService = new HttpNetworkService(
// "https://oapi.mineroo.online",
// "mserver",
// bind_token);
//
// BindRequest request = new BindRequest(logger, networkService);
//
// try {
//
// // 2. Check Status
// // We use .join() to block the thread until the Future completes.
// if (request.checkBindStatus(bind_address, bind_port).join()) {
// source.sendMessage(msg.get("command.bind.server.already-bound"));
// return;
// }
//
// // 3. Initial Verification
// boolean inital_bind = request.initialMotdVerifyRequest(bind_address,
// bind_port).join();
//
// if (inital_bind) {
// String motdToken = request.getMotdToken();
// plugin.getBindListener().setVerificationToken(motdToken);
//
// source.sendMessage(msg.get("command.bind.server.wait"));
//
// // 4. Wait for external refresh (Blocking this worker thread is intended)
// try {
// Thread.sleep(2 * 60 * 1000 + 5000); // 2m 5s
// } catch (InterruptedException e) {
// logger.error("Bind thread interrupted", e);
// return;
// }
//
// // 5. Final Bind & Retry Loop
// boolean success = false;
// int maxRetries = 3;
//
// for (int i = 1; i <= maxRetries; i++) {
// // Check final status synchronously using join()
// if (request.finalizeServerBinding(bind_name, bind_description).join()) {
// success = true;
// break;
// }
//
// if (i < maxRetries) {
// source.sendMessage(msg.get("command.bind.server.retry",
// "current", String.valueOf(i),
// "max", String.valueOf(maxRetries)));
//
// try {
// Thread.sleep(10000); // Wait 10s before retry
// } catch (InterruptedException e) {
// break;
// }
// }
// }
//
// if (success) {
// source.sendMessage(msg.get("command.bind.server.success"));
// } else {
// source.sendMessage(msg.get("command.bind.server.failed"));
// }
//
// } else {
// source.sendMessage(
// Component.text("Initial handshake failed. Check your token or network.",
// NamedTextColor.RED));
// }
//
// } catch (CompletionException e) {
// // Unpack the wrapper exception from join() to see the real network error
// logger.error("Mineroo Bind Network Error: " + e.getCause().getMessage());
// } catch (Exception e) {
// logger.error("Mineroo Bind Error: " + e.toString());
// } finally {
// plugin.getBindListener().clearVerificationToken();
// }
//
// }).schedule();
// }
//
// public void sendHelp(CommandSource source) {
// source.sendMessage(Component.text("=== # @Mineroo # ===",
// NamedTextColor.AQUA));
// source.sendMessage(Component.text(" /mineroo bind - Start to bind server",
// NamedTextColor.YELLOW));
// source.sendMessage(Component.text(" /mineroo reload - Reload config",
// NamedTextColor.YELLOW));
// source.sendMessage(Component.text("=== mineroo.online ===",
// NamedTextColor.AQUA));
// }
// }

View File

@@ -23,7 +23,7 @@ public class BindListener {
public void onPing(ProxyPingEvent event) {
String token = verificationToken.get();
if (token == null) {
if (token == null || token.isEmpty()) {
return;
}

View File

@@ -1,36 +1,208 @@
package online.mineroo.velocity.listeners;
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.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import online.mineroo.common.*;
import online.mineroo.velocity.MinerooVelocity;
import online.mineroo.common.ProtocolConstants;
import online.mineroo.velocity.MinerooCore;
import java.util.concurrent.CompletableFuture;
public class ChannelListener {
private final MinerooCore plugin;
public ChannelListener(MinerooCore plugin) {
private final MinerooVelocity plugin;
private final Gson gson = new Gson();
private final HttpNetworkService httpService;
public ChannelListener(MinerooVelocity plugin) {
this.plugin = plugin;
String apiUrl = "https://oapi.mineroo.online";
this.httpService = new HttpNetworkService(apiUrl, "mserver", plugin.getConfig().getBind().getBindToken());
}
@Subscribe
public void onPluginMessage(PluginMessageEvent event) {
// Only handle messages from the specified protocol channel
if (!event.getIdentifier().getId().equals(ProtocolConstants.PROTOCOL_CHANNEL))
return;
// Only handle messages from a backend server
if (!(event.getSource() instanceof ServerConnection))
return;
ServerConnection backendServer = (ServerConnection) event.getSource();
ByteArrayDataInput in = event.dataAsDataStream();
String subChannel = in.readUTF();
if (subChannel.equals(ProtocolConstants.BIND_MOTD_TOKEN)) {
String token = in.readUTF();
plugin.getBindListener().setVerificationToken(token);
try {
String subChannel = in.readUTF();
if (subChannel.equals(ProtocolConstants.API_REQUEST)) {
handleApiRequest(in, backendServer);
}
if (subChannel.equals(ProtocolConstants.BIND_MOTD_TOKEN)) {
String token = in.readUTF();
plugin.getBindListener().setVerificationToken(token);
}
} catch (Exception e) {
plugin.getLogger().error("Failed to handle plugin message", e);
}
}
}
private void handleApiRequest(ByteArrayDataInput in, ServerConnection backendServer) {
String reqId = "unknown";
try {
// 1. Parse the request
String jsonPayload = in.readUTF();
ProxyNetworkRequest req = gson.fromJson(jsonPayload, ProxyNetworkRequest.class);
reqId = req.getRequestId();
CompletableFuture<NetworkResponse> future;
// 2. Delegate to HttpNetworkService for processing
if ("POST".equalsIgnoreCase(req.getMethod())) {
JsonObject bodyJson = null;
try {
JsonObject originalJson = gson.fromJson(req.getJsonBody(), JsonObject.class);
if (originalJson == null)
originalJson = new JsonObject();
bodyJson = resolvePlacehodersInJson(originalJson).getAsJsonObject();
plugin.getLogger().info("Proxy POST [ " + req.getEndpoint() + " ]\n\t" + bodyJson.toString());
} catch (Exception e) {
plugin.getLogger().error("JSON parsing error", e);
bodyJson = new JsonObject();
}
future = httpService.postData(req.getEndpoint(), bodyJson);
} else {
// replace placeholders
java.util.Map<String, String> finalParams = new java.util.HashMap<>();
if (req.getParams() != null) {
for (java.util.Map.Entry<String, String> entry : req.getParams().entrySet()) {
String originalKey = entry.getKey();
String originalValue = entry.getValue();
String replacedValue = resolvePlaceholders(originalValue);
finalParams.put(originalKey, replacedValue);
}
}
plugin.getLogger().info("Proxy GET [ " + req.getEndpoint() + " ]\n\t" + finalParams.toString());
future = httpService.getData(req.getEndpoint(), finalParams);
}
// 3. Handle the result and send back the response
String finalReqId = reqId;
future.thenAccept(response -> {
// Check if HTTP status code is 2xx
boolean success = response.getStatusCode() >= 200 && response.getStatusCode() < 300;
sendResponse(backendServer, finalReqId, success, response.getBody());
}).exceptionally(e -> {
plugin.getLogger().error("API Proxy Request Failed", e);
sendResponse(backendServer, finalReqId, false, "Proxy Error: " + e.getMessage());
return null;
});
} catch (Exception e) {
plugin.getLogger().error("Error parsing API request", e);
sendResponse(backendServer, reqId, false, "Protocol Error");
}
}
private void sendResponse(ServerConnection server, String reqId, boolean success, String data) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF(ProtocolConstants.API_RESPONSE);
out.writeUTF(reqId);
out.writeBoolean(success);
out.writeUTF(data != null ? data : "");
server.sendPluginMessage(MinecraftChannelIdentifier.from(ProtocolConstants.PROTOCOL_CHANNEL), out.toByteArray());
}
private JsonElement resolvePlacehodersInJson(JsonElement element) {
if (element.isJsonPrimitive()) {
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (primitive.isString()) {
String original = primitive.getAsString();
if ("$port".equals(original)) {
Integer port = null;
if (plugin.getConfig().getBind() != null) {
port = plugin.getConfig().getBind().getPort();
}
if (port == null || port <= 0) {
return null;
}
return new JsonPrimitive(port);
}
if ("$timestamp".equals(original)) {
return new JsonPrimitive(System.currentTimeMillis());
}
String replaced = resolvePlaceholders(original);
return new JsonPrimitive(replaced);
}
return element;
}
if (element.isJsonObject()) {
JsonObject obj = element.getAsJsonObject();
JsonObject newObj = new JsonObject();
for (java.util.Map.Entry<String, JsonElement> entry : obj.entrySet()) {
JsonElement processedValue = resolvePlacehodersInJson(entry.getValue());
if (processedValue != null) {
newObj.add(entry.getKey(), processedValue);
}
}
return newObj;
}
if (element.isJsonArray()) {
JsonArray array = element.getAsJsonArray();
JsonArray newArray = new JsonArray();
for (JsonElement item : array) {
JsonElement processedItem = resolvePlacehodersInJson(item);
if (processedItem != null) {
newArray.add(processedItem);
}
}
return newArray;
}
return element;
}
private String resolvePlaceholders(String input) {
if (input == null)
return null;
String hostname = plugin.getConfig().getBind().getAddress();
String port = String.valueOf(plugin.getConfig().getBind().getPort());
return input
.replace("$hostname", hostname != null ? hostname : "unknown")
.replace("$port", port)
.replace("$timestamp", String.valueOf(System.currentTimeMillis()));
}
}