cn.nukkit.Nukkit Maven / Gradle / Ivy
package cn.nukkit;
import cn.nukkit.api.PowerNukkitOnly;
import cn.nukkit.nbt.stream.PGZIPOutputStream;
import cn.nukkit.network.protocol.ProtocolInfo;
import cn.nukkit.plugin.js.JSIInitiator;
import cn.nukkit.utils.ServerKiller;
import com.google.common.base.Preconditions;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.internal.logging.Log4J2LoggerFactory;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import static cn.nukkit.utils.Utils.dynamic;
/*
* `_ _ _ _ _ _
* | \ | | | | | | (_) |
* | \| |_ _| | _| | ___| |_
* | . ` | | | | |/ / |/ / | __|
* | |\ | |_| | <| <| | |_
* |_| \_|\__,_|_|\_\_|\_\_|\__|
*/
/**
* Nukkit启动类,包含{@code main}函数。
* The launcher class of Nukkit, including the {@code main} function.
*
* @author MagicDroidX(code) @ Nukkit Project
* @author 粉鞋大妈(javadoc) @ Nukkit Project
* @since Nukkit 1.0 | Nukkit API 1.0.0
*/
@Log4j2
public class Nukkit {
public final static Properties GIT_INFO = getGitInfo();
public final static String VERSION = getVersion();
@PowerNukkitOnly
public final static String GIT_COMMIT = getGitCommit();
public final static String API_VERSION = dynamic("1.0.14");
public final static String CODENAME = dynamic("PowerNukkitX");
@Deprecated
public final static String MINECRAFT_VERSION = ProtocolInfo.MINECRAFT_VERSION;
@Deprecated
public final static String MINECRAFT_VERSION_NETWORK = ProtocolInfo.MINECRAFT_VERSION_NETWORK;
public final static String PATH = System.getProperty("user.dir") + "/";
public final static String DATA_PATH = System.getProperty("user.dir") + "/";
public final static String PLUGIN_PATH = DATA_PATH + "plugins";
public static final long START_TIME = System.currentTimeMillis();
public static boolean ANSI = true;
public static boolean TITLE = false;
public static boolean shortTitle = requiresShortTitle();
public static int DEBUG = 1;
public static int CHROME_DEBUG_PORT = -1;
public static List JS_DEBUG_LIST = new LinkedList<>();
public static void main(String[] args) {
AtomicBoolean disableSentry = new AtomicBoolean(false);
disableSentry.set(Boolean.parseBoolean(System.getProperty("disableSentry", "false")));
Path propertiesPath = Paths.get(DATA_PATH, "server.properties");
if (!disableSentry.get() && Files.isRegularFile(propertiesPath)) {
Properties properties = new Properties();
try (FileReader reader = new FileReader(propertiesPath.toFile())) {
properties.load(reader);
String value = properties.getProperty("disable-auto-bug-report", "false");
if (value.equalsIgnoreCase("on") || value.equals("1")) {
value = "true";
}
disableSentry.set(Boolean.parseBoolean(value.toLowerCase(Locale.ENGLISH)));
} catch (IOException e) {
log.error("Failed to load server.properties to check disable-auto-bug-report.", e);
}
}
// Force IPv4 since Nukkit is not compatible with IPv6
System.setProperty("java.net.preferIPv4Stack", "true");
System.setProperty("log4j.skipJansi", "false");
System.getProperties().putIfAbsent("io.netty.allocator.type", "unpooled"); // Disable memory pooling unless specified
// Force Mapped ByteBuffers for LevelDB till fixed.
System.setProperty("leveldb.mmap", "true");
// Netty logger for debug info
InternalLoggerFactory.setDefaultFactory(Log4J2LoggerFactory.INSTANCE);
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);
// Define args
OptionParser parser = new OptionParser();
parser.allowsUnrecognizedOptions();
OptionSpec helpSpec = parser.accepts("help", "Shows this page").forHelp();
OptionSpec ansiSpec = parser.accepts("disable-ansi", "Disables console coloring");
OptionSpec titleSpec = parser.accepts("enable-title", "Enables title at the top of the window");
OptionSpec vSpec = parser.accepts("v", "Set verbosity of logging").withRequiredArg().ofType(String.class);
OptionSpec verbositySpec = parser.accepts("verbosity", "Set verbosity of logging").withRequiredArg().ofType(String.class);
OptionSpec languageSpec = parser.accepts("language", "Set a predefined language").withOptionalArg().ofType(String.class);
OptionSpec chromeDebugPortSpec = parser.accepts("chrome-debug", "Debug javascript using chrome dev tool with specific port.").withRequiredArg().ofType(Integer.class);
OptionSpec jsDebugPortSpec = parser.accepts("js-debug", "Debug javascript using chrome dev tool with specific port.").withRequiredArg().ofType(String.class);
// Parse arguments
OptionSet options = parser.parse(args);
if (options.has(helpSpec)) {
try {
// Display help page
parser.printHelpOn(System.out);
} catch (IOException e) {
// ignore
}
return;
}
ANSI = !options.has(ansiSpec);
TITLE = options.has(titleSpec);
String verbosity = options.valueOf(vSpec);
if (verbosity == null) {
verbosity = options.valueOf(verbositySpec);
}
if (verbosity != null) {
try {
Level level = Level.valueOf(verbosity);
setLogLevel(level);
} catch (Exception e) {
// ignore
}
}
String language = options.valueOf(languageSpec);
if (options.has(chromeDebugPortSpec)) {
CHROME_DEBUG_PORT = options.valueOf(chromeDebugPortSpec);
}
if (options.has(jsDebugPortSpec)) {
JS_DEBUG_LIST = Arrays.stream(options.valueOf(jsDebugPortSpec).split(",")).toList();
}
try {
if (TITLE) {
System.out.print((char) 0x1b + "]0;Nukkit is starting up..." + (char) 0x07);
}
new Server(PATH, DATA_PATH, PLUGIN_PATH, language);
} catch (Throwable t) {
log.catching(t);
}
if (TITLE) {
System.out.print((char) 0x1b + "]0;Stopping Server..." + (char) 0x07);
}
log.info("Stopping other threads");
// 停止JS定时器
JSIInitiator.jsTimer.cancel();
// 强制关闭PGZIPOutputStream中的线程池
PGZIPOutputStream.getSharedThreadPool().shutdownNow();
for (Thread thread : java.lang.Thread.getAllStackTraces().keySet()) {
if (!(thread instanceof InterruptibleThread)) {
continue;
}
log.debug("Stopping {} thread", thread.getClass().getSimpleName());
if (thread.isAlive()) {
thread.interrupt();
}
}
ServerKiller killer = new ServerKiller(8);
killer.start();
if (TITLE) {
System.out.print((char) 0x1b + "]0;Server Stopped" + (char) 0x07);
}
System.exit(0);
}
private static boolean requiresShortTitle() {
//Shorter title for windows 8/2012
String osName = System.getProperty("os.name").toLowerCase();
return osName.contains("windows") && (osName.contains("windows 8") || osName.contains("2012"));
}
private static Properties getGitInfo() {
InputStream gitFileStream = null;
try {
gitFileStream = Nukkit.class.getModule().getResourceAsStream("git.properties");
} catch (IOException e) {
throw new RuntimeException(e);
}
if (gitFileStream == null) {
return null;
}
Properties properties = new Properties();
try {
properties.load(gitFileStream);
} catch (IOException e) {
return null;
}
return properties;
}
private static String getVersion() {
InputStream resourceAsStream = null;
try {
resourceAsStream = Nukkit.class.getModule().getResourceAsStream("VERSION.txt");
} catch (IOException e) {
throw new RuntimeException(e);
}
if (resourceAsStream == null) {
return "Unknown-PNX-SNAPSHOT";
}
try (InputStream is = resourceAsStream;
InputStreamReader reader = new InputStreamReader(is);
BufferedReader buffered = new BufferedReader(reader)) {
String line = buffered.readLine().trim();
if ("${project.version}".equalsIgnoreCase(line)) {
return "Unknown-PNX-SNAPSHOT";
} else {
return line;
}
} catch (IOException e) {
return "Unknown-PNX-SNAPSHOT";
}
}
private static String getGitCommit() {
StringBuilder version = new StringBuilder();
version.append("git-");
String commitId;
if (GIT_INFO == null || (commitId = GIT_INFO.getProperty("git.commit.id.abbrev")) == null) {
return version.append("null").toString();
}
return version.append(commitId).toString();
}
public static void setLogLevel(Level level) {
Preconditions.checkNotNull(level, "level");
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Configuration log4jConfig = ctx.getConfiguration();
LoggerConfig loggerConfig = log4jConfig.getLoggerConfig(org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
loggerConfig.setLevel(level);
ctx.updateLoggers();
}
public static Level getLogLevel() {
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Configuration log4jConfig = ctx.getConfiguration();
LoggerConfig loggerConfig = log4jConfig.getLoggerConfig(org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
return loggerConfig.getLevel();
}
}