【Lv:2】
- 注册
- 2025/06/24
- 消息
- 4
- 金粒
- 16.00金粒
我去蓝夜服务器搞了一台我的世界服务器面板,结果发现很多朋友没有正版,于是给自己的社区写了一个外置登录功能
dynet.work
然后发现面板服没办法修改启动命令
经过我n秒思考,决定写一个虚假核心拼接带-javaagent的启动命令启动真核心。
如果你也有相同需求,请把附件的.jar上传到与authlib和server核心的同级目录,然后创建一个文件,名字:fake-core.properties
然后写入:
即可,其次由于这个假核心代码过于简单,我就不上传GitHub了,你们自己看着吧
对了,请把面板的启动核心名字换成这个或者修改两个核心的名字,保证以虚假核心启动即可。
经过我n秒思考,决定写一个虚假核心拼接带-javaagent的启动命令启动真核心。
如果你也有相同需求,请把附件的.jar上传到与authlib和server核心的同级目录,然后创建一个文件,名字:fake-core.properties
然后写入:
代码:
# 真实服务端核心文件名(默认 server.jar)
realCore = paper-1.20.4.jar
# authlib-injector 文件名(默认 authlib-injector.jar)
authInjector = authlib-injector-1.2.5.jar
# 外置登录验证服务器地址
authUrl = https://littleskin.cn/api/yggdrasil
# Java 命令(一般不用改)
javaCmd = java
# 默认 JVM 内存参数(可被环境变量 FAKE_JVM_OPTS 覆盖)
jvmOpts = -Xmx2G -Xms1G
即可,其次由于这个假核心代码过于简单,我就不上传GitHub了,你们自己看着吧
对了,请把面板的启动核心名字换成这个或者修改两个核心的名字,保证以虚假核心启动即可。
代码:
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.lang.reflect.Method;
public class FakeCoreBootstrap {
private static String REAL_CORE = "server.jar";
private static String AUTH_INJECTOR = "authlib-injector.jar";
private static String AUTH_URL = "https://littleskin.cn/api/yggdrasil";
private static String JAVA_CMD = "java";
private static String JVM_OPTS = "-Xmx1024M -Xms1024M";
public static void main(String[] args) throws Exception {
loadConfig();
Path workDir = Paths.get("").toAbsolutePath();
Path realCorePath = workDir.resolve(REAL_CORE);
Path authJarPath = workDir.resolve(AUTH_INJECTOR);
if (!Files.exists(realCorePath)) {
System.err.println("[ERROR] 找不到真实核心文件: " + realCorePath);
System.err.println("请将真实核心命名为 " + REAL_CORE + " 并放在同一目录下。");
System.exit(1);
}
if (!Files.exists(authJarPath)) {
System.err.println("[ERROR] 找不到 authlib-injector: " + authJarPath);
System.err.println("请从 https://github.com/yushijinhun/authlib-injector/releases 下载并放入目录。");
System.exit(1);
}
List<String> command = new ArrayList<>();
command.add(JAVA_CMD);
String extraJvm = System.getenv("FAKE_JVM_OPTS");
if (extraJvm != null && !extraJvm.isEmpty()) {
command.addAll(Arrays.asList(extraJvm.split("\\s+")));
} else if (JVM_OPTS != null && !JVM_OPTS.isEmpty()) {
command.addAll(Arrays.asList(JVM_OPTS.split("\\s+")));
}
String agentArg = "-javaagent:" + authJarPath.toString() + "=" + AUTH_URL;
command.add(agentArg);
command.add("-jar");
command.add(realCorePath.toString());
command.addAll(Arrays.asList(args));
System.out.println("[INFO] 启动命令: " + command.stream().collect(Collectors.joining(" ")));
ProcessBuilder pb = new ProcessBuilder(command);
pb.inheritIO();
Process process = pb.start();
// 注册关闭钩子,确保面板停止时能终止子进程(兼容 Android)
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (process.isAlive()) {
System.out.println("[INFO] 接收到停止信号,正在终止服务端子进程...");
process.destroy(); // 先尝试优雅终止
try {
if (!process.waitFor(5, TimeUnit.SECONDS)) {
System.out.println("[WARN] 子进程未能正常退出,尝试强制终止...");
// 尝试调用 destroyForced(如果可用)
boolean forced = false;
try {
Method destroyForced = process.getClass().getMethod("destroyForced");
destroyForced.invoke(process);
forced = true;
} catch (Exception e) {
// 方法不存在或调用失败,忽略
}
if (!forced) {
System.out.println("[WARN] 无法强制终止,进程可能仍然残留。");
}
} else {
System.out.println("[INFO] 子进程正常退出。");
}
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
}));
int exitCode = process.waitFor();
System.exit(exitCode);
}
private static void loadConfig() {
Path configPath = Paths.get("fake-core.properties");
if (!Files.exists(configPath)) {
return;
}
Properties props = new Properties();
try (InputStream in = Files.newInputStream(configPath)) {
props.load(in);
REAL_CORE = props.getProperty("realCore", REAL_CORE);
AUTH_INJECTOR = props.getProperty("authInjector", AUTH_INJECTOR);
AUTH_URL = props.getProperty("authUrl", AUTH_URL);
JAVA_CMD = props.getProperty("javaCmd", JAVA_CMD);
JVM_OPTS = props.getProperty("jvmOpts", JVM_OPTS);
} catch (IOException e) {
System.err.println("[WARN] 读取配置文件失败,使用默认值。");
}
}
}
附件