Add jline-based console with colors and tab-completion
This commit is contained in:
parent
1246f1a791
commit
e24c38bbb0
12 changed files with 541 additions and 5 deletions
|
@ -8,6 +8,11 @@
|
||||||
{
|
{
|
||||||
"name": "net.minecraft:launchwrapper:1.12"
|
"name": "net.minecraft:launchwrapper:1.12"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "jline:jline:2.13",
|
||||||
|
"children": ["sources"],
|
||||||
|
"url" : "http://repo.maven.apache.org/maven2"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "com.google.code.findbugs:jsr305:1.3.9",
|
"name": "com.google.code.findbugs:jsr305:1.3.9",
|
||||||
"children": ["sources"],
|
"children": ["sources"],
|
||||||
|
|
|
@ -39,6 +39,13 @@
|
||||||
"name": "org.ow2.asm:asm-all:5.0.3",
|
"name": "org.ow2.asm:asm-all:5.0.3",
|
||||||
"serverreq":true
|
"serverreq":true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "jline:jline:2.13",
|
||||||
|
"url" : "http://files.minecraftforge.net/maven/",
|
||||||
|
"checksums" : [ "TODO", "TODO" ],
|
||||||
|
"serverreq":true,
|
||||||
|
"clientreq":false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "com.typesafe.akka:akka-actor_2.11:2.3.3",
|
"name": "com.typesafe.akka:akka-actor_2.11:2.3.3",
|
||||||
"url" : "http://files.minecraftforge.net/maven/",
|
"url" : "http://files.minecraftforge.net/maven/",
|
||||||
|
|
|
@ -8,7 +8,15 @@
|
||||||
private static final String __OBFID = "CL_00001784";
|
private static final String __OBFID = "CL_00001784";
|
||||||
|
|
||||||
public DedicatedServer(File p_i1508_1_)
|
public DedicatedServer(File p_i1508_1_)
|
||||||
@@ -113,6 +114,8 @@
|
@@ -88,6 +89,7 @@
|
||||||
|
private static final String __OBFID = "CL_00001786";
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
+ if (net.minecraftforge.server.console.TerminalHandler.handleCommands(DedicatedServer.this)) return;
|
||||||
|
BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(System.in));
|
||||||
|
String s4;
|
||||||
|
|
||||||
|
@@ -113,6 +115,8 @@
|
||||||
field_155771_h.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
|
field_155771_h.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +25,7 @@
|
||||||
field_155771_h.info("Loading properties");
|
field_155771_h.info("Loading properties");
|
||||||
this.field_71340_o = new PropertyManager(new File("server.properties"));
|
this.field_71340_o = new PropertyManager(new File("server.properties"));
|
||||||
this.field_154332_n = new ServerEula(new File("eula.txt"));
|
this.field_154332_n = new ServerEula(new File("eula.txt"));
|
||||||
@@ -204,6 +207,7 @@
|
@@ -204,6 +208,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -25,7 +33,7 @@
|
||||||
this.func_152361_a(new DedicatedPlayerList(this));
|
this.func_152361_a(new DedicatedPlayerList(this));
|
||||||
long j = System.nanoTime();
|
long j = System.nanoTime();
|
||||||
|
|
||||||
@@ -250,6 +254,7 @@
|
@@ -250,6 +255,7 @@
|
||||||
this.func_71191_d((this.func_71207_Z() + 8) / 16 * 16);
|
this.func_71191_d((this.func_71207_Z() + 8) / 16 * 16);
|
||||||
this.func_71191_d(MathHelper.func_76125_a(this.func_71207_Z(), 64, 256));
|
this.func_71191_d(MathHelper.func_76125_a(this.func_71207_Z(), 64, 256));
|
||||||
this.field_71340_o.func_73667_a("max-build-height", Integer.valueOf(this.func_71207_Z()));
|
this.field_71340_o.func_73667_a("max-build-height", Integer.valueOf(this.func_71207_Z()));
|
||||||
|
@ -33,7 +41,7 @@
|
||||||
field_155771_h.info("Preparing level \"" + this.func_71270_I() + "\"");
|
field_155771_h.info("Preparing level \"" + this.func_71270_I() + "\"");
|
||||||
this.func_71247_a(this.func_71270_I(), this.func_71270_I(), k, worldtype, s2);
|
this.func_71247_a(this.func_71270_I(), this.func_71270_I(), k, worldtype, s2);
|
||||||
long i1 = System.nanoTime() - j;
|
long i1 = System.nanoTime() - j;
|
||||||
@@ -278,7 +283,7 @@
|
@@ -278,10 +284,11 @@
|
||||||
thread1.start();
|
thread1.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,3 +50,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
+ public void func_145747_a(net.minecraft.util.IChatComponent message) { field_155771_h.info(message.func_150254_d()); }
|
||||||
|
|
||||||
|
public void func_71235_a(WorldSettings.GameType p_71235_1_)
|
||||||
|
{
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
--- ../src-base/minecraft/net/minecraft/util/ChatComponentStyle.java
|
||||||
|
+++ ../src-work/minecraft/net/minecraft/util/ChatComponentStyle.java
|
||||||
|
@@ -81,7 +81,6 @@
|
||||||
|
return stringbuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
- @SideOnly(Side.CLIENT)
|
||||||
|
public final String func_150254_d()
|
||||||
|
{
|
||||||
|
StringBuilder stringbuilder = new StringBuilder();
|
10
patches/minecraft/net/minecraft/util/ChatStyle.java.patch
Normal file
10
patches/minecraft/net/minecraft/util/ChatStyle.java.patch
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
--- ../src-base/minecraft/net/minecraft/util/ChatStyle.java
|
||||||
|
+++ ../src-work/minecraft/net/minecraft/util/ChatStyle.java
|
||||||
|
@@ -230,7 +230,6 @@
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
- @SideOnly(Side.CLIENT)
|
||||||
|
public String func_150218_j()
|
||||||
|
{
|
||||||
|
if (this.func_150229_g())
|
|
@ -0,0 +1,10 @@
|
||||||
|
--- ../src-base/minecraft/net/minecraft/util/IChatComponent.java
|
||||||
|
+++ ../src-work/minecraft/net/minecraft/util/IChatComponent.java
|
||||||
|
@@ -32,7 +32,6 @@
|
||||||
|
|
||||||
|
String func_150260_c();
|
||||||
|
|
||||||
|
- @SideOnly(Side.CLIENT)
|
||||||
|
String func_150254_d();
|
||||||
|
|
||||||
|
List func_150253_a();
|
|
@ -1,9 +1,28 @@
|
||||||
package net.minecraftforge.fml.common.launcher;
|
package net.minecraftforge.fml.common.launcher;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.core.LoggerContext;
|
||||||
|
|
||||||
import net.minecraft.launchwrapper.LaunchClassLoader;
|
import net.minecraft.launchwrapper.LaunchClassLoader;
|
||||||
import net.minecraftforge.fml.relauncher.FMLLaunchHandler;
|
import net.minecraftforge.fml.relauncher.FMLLaunchHandler;
|
||||||
|
|
||||||
public class FMLServerTweaker extends FMLTweaker {
|
public class FMLServerTweaker extends FMLTweaker {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile)
|
||||||
|
{
|
||||||
|
super.acceptOptions(args, gameDir, assetsDir, profile);
|
||||||
|
|
||||||
|
if (System.getProperty("log4j.configurationFile") == null)
|
||||||
|
{
|
||||||
|
System.setProperty("log4j.configurationFile", "log4j2_server.xml");
|
||||||
|
((LoggerContext) LogManager.getContext(false)).reconfigure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLaunchTarget()
|
public String getLaunchTarget()
|
||||||
{
|
{
|
||||||
|
@ -16,6 +35,11 @@ public class FMLServerTweaker extends FMLTweaker {
|
||||||
// The log4j2 queue is excluded so it is correctly visible from the obfuscated
|
// The log4j2 queue is excluded so it is correctly visible from the obfuscated
|
||||||
// and deobfuscated parts of the code. Without, the UI won't show anything
|
// and deobfuscated parts of the code. Without, the UI won't show anything
|
||||||
classLoader.addClassLoaderExclusion("com.mojang.util.QueueLogAppender");
|
classLoader.addClassLoaderExclusion("com.mojang.util.QueueLogAppender");
|
||||||
|
|
||||||
|
classLoader.addClassLoaderExclusion("jline.");
|
||||||
|
classLoader.addClassLoaderExclusion("org.fusesource.");
|
||||||
|
classLoader.addClassLoaderExclusion("net.minecraftforge.server.console.TerminalConsoleAppender");
|
||||||
|
|
||||||
FMLLaunchHandler.configureForServerLaunch(classLoader, this);
|
FMLLaunchHandler.configureForServerLaunch(classLoader, this);
|
||||||
FMLLaunchHandler.appendCoreMods();
|
FMLLaunchHandler.appendCoreMods();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package net.minecraftforge.server.console;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import jline.console.completer.Completer;
|
||||||
|
import net.minecraft.server.dedicated.DedicatedServer;
|
||||||
|
|
||||||
|
public final class ConsoleCommandCompleter implements Completer
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
private final DedicatedServer server;
|
||||||
|
|
||||||
|
public ConsoleCommandCompleter(DedicatedServer server)
|
||||||
|
{
|
||||||
|
this.server = checkNotNull(server, "server");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int complete(String buffer, int cursor, List<CharSequence> candidates)
|
||||||
|
{
|
||||||
|
int len = buffer.length();
|
||||||
|
buffer = buffer.trim();
|
||||||
|
if (buffer.isEmpty())
|
||||||
|
{
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean prefix;
|
||||||
|
if (buffer.charAt(0) != '/')
|
||||||
|
{
|
||||||
|
buffer = '/' + buffer;
|
||||||
|
prefix = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prefix = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String input = buffer;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Future<List<String>> tabComplete = this.server.callFromMainThread(new Callable<List<String>>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> call() throws Exception
|
||||||
|
{
|
||||||
|
return ConsoleCommandCompleter.this.server.getTabCompletions(ConsoleCommandCompleter.this.server, input,
|
||||||
|
ConsoleCommandCompleter.this.server.getPosition());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<String> completions = tabComplete.get();
|
||||||
|
if (prefix)
|
||||||
|
{
|
||||||
|
candidates.addAll(completions);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (String completion : completions)
|
||||||
|
{
|
||||||
|
candidates.add(completion.charAt(0) == '/' ? completion.substring(1) : completion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = buffer.lastIndexOf(' ');
|
||||||
|
if (pos == -1)
|
||||||
|
{
|
||||||
|
return cursor - len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return cursor - len + pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InterruptedException e)
|
||||||
|
{
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
catch (ExecutionException e)
|
||||||
|
{
|
||||||
|
logger.error("Failed to tab complete", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package net.minecraftforge.server.console;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.fusesource.jansi.Ansi;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
|
||||||
|
public final class ConsoleFormatter implements Function<String, String>
|
||||||
|
{
|
||||||
|
|
||||||
|
public ConsoleFormatter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String RESET = Ansi.ansi().reset().toString();
|
||||||
|
|
||||||
|
private static final ImmutableMap<Pattern, String> REPLACEMENTS = ImmutableMap.<Pattern, String> builder()
|
||||||
|
.put(compile(EnumChatFormatting.BLACK), Ansi.ansi().reset().fg(Ansi.Color.BLACK).boldOff().toString())
|
||||||
|
.put(compile(EnumChatFormatting.DARK_BLUE), Ansi.ansi().reset().fg(Ansi.Color.BLUE).boldOff().toString())
|
||||||
|
.put(compile(EnumChatFormatting.DARK_GREEN), Ansi.ansi().reset().fg(Ansi.Color.GREEN).boldOff().toString())
|
||||||
|
.put(compile(EnumChatFormatting.DARK_AQUA), Ansi.ansi().reset().fg(Ansi.Color.CYAN).boldOff().toString())
|
||||||
|
.put(compile(EnumChatFormatting.DARK_RED), Ansi.ansi().reset().fg(Ansi.Color.RED).boldOff().toString())
|
||||||
|
.put(compile(EnumChatFormatting.DARK_PURPLE), Ansi.ansi().reset().fg(Ansi.Color.MAGENTA).boldOff().toString())
|
||||||
|
.put(compile(EnumChatFormatting.GOLD), Ansi.ansi().reset().fg(Ansi.Color.YELLOW).boldOff().toString())
|
||||||
|
.put(compile(EnumChatFormatting.GRAY), Ansi.ansi().reset().fg(Ansi.Color.WHITE).boldOff().toString())
|
||||||
|
.put(compile(EnumChatFormatting.DARK_GRAY), Ansi.ansi().reset().fg(Ansi.Color.BLACK).bold().toString())
|
||||||
|
.put(compile(EnumChatFormatting.BLUE), Ansi.ansi().reset().fg(Ansi.Color.BLUE).bold().toString())
|
||||||
|
.put(compile(EnumChatFormatting.GREEN), Ansi.ansi().reset().fg(Ansi.Color.GREEN).bold().toString())
|
||||||
|
.put(compile(EnumChatFormatting.AQUA), Ansi.ansi().reset().fg(Ansi.Color.CYAN).bold().toString())
|
||||||
|
.put(compile(EnumChatFormatting.RED), Ansi.ansi().reset().fg(Ansi.Color.RED).bold().toString())
|
||||||
|
.put(compile(EnumChatFormatting.LIGHT_PURPLE), Ansi.ansi().reset().fg(Ansi.Color.MAGENTA).bold().toString())
|
||||||
|
.put(compile(EnumChatFormatting.YELLOW), Ansi.ansi().reset().fg(Ansi.Color.YELLOW).bold().toString())
|
||||||
|
.put(compile(EnumChatFormatting.WHITE), Ansi.ansi().reset().fg(Ansi.Color.WHITE).bold().toString())
|
||||||
|
.put(compile(EnumChatFormatting.OBFUSCATED), Ansi.ansi().a(Ansi.Attribute.BLINK_SLOW).toString())
|
||||||
|
.put(compile(EnumChatFormatting.BOLD), Ansi.ansi().a(Ansi.Attribute.UNDERLINE_DOUBLE).toString())
|
||||||
|
.put(compile(EnumChatFormatting.STRIKETHROUGH), Ansi.ansi().a(Ansi.Attribute.STRIKETHROUGH_ON).toString())
|
||||||
|
.put(compile(EnumChatFormatting.UNDERLINE), Ansi.ansi().a(Ansi.Attribute.UNDERLINE).toString())
|
||||||
|
.put(compile(EnumChatFormatting.ITALIC), Ansi.ansi().a(Ansi.Attribute.ITALIC).toString())
|
||||||
|
.put(compile(EnumChatFormatting.RESET), RESET)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private static Pattern compile(EnumChatFormatting formatting)
|
||||||
|
{
|
||||||
|
return Pattern.compile(formatting.toString(), Pattern.LITERAL | Pattern.CASE_INSENSITIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String apply(String text)
|
||||||
|
{
|
||||||
|
for (Map.Entry<Pattern, String> entry : REPLACEMENTS.entrySet())
|
||||||
|
{
|
||||||
|
text = entry.getKey().matcher(text).replaceAll(entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return text + RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
package net.minecraftforge.server.console;
|
||||||
|
|
||||||
|
import static jline.TerminalFactory.OFF;
|
||||||
|
import static jline.console.ConsoleReader.RESET_LINE;
|
||||||
|
import static org.apache.logging.log4j.core.helpers.Booleans.parseBoolean;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.io.Writer;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.core.Filter;
|
||||||
|
import org.apache.logging.log4j.core.Layout;
|
||||||
|
import org.apache.logging.log4j.core.LogEvent;
|
||||||
|
import org.apache.logging.log4j.core.appender.AbstractAppender;
|
||||||
|
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||||
|
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
|
||||||
|
import org.apache.logging.log4j.core.config.plugins.PluginElement;
|
||||||
|
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
||||||
|
import org.apache.logging.log4j.core.layout.PatternLayout;
|
||||||
|
import org.apache.logging.log4j.util.PropertiesUtil;
|
||||||
|
import org.fusesource.jansi.AnsiConsole;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Functions;
|
||||||
|
|
||||||
|
import jline.TerminalFactory;
|
||||||
|
import jline.console.ConsoleReader;
|
||||||
|
|
||||||
|
@Plugin(name = "TerminalConsole", category = "Core", elementType = "appender", printObject = true)
|
||||||
|
public class TerminalConsoleAppender extends AbstractAppender
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final boolean ENABLE_JLINE = PropertiesUtil.getProperties().getBooleanProperty("jline.enable", true);
|
||||||
|
|
||||||
|
private static final PrintStream out = System.out;
|
||||||
|
|
||||||
|
private static boolean initialized;
|
||||||
|
private static ConsoleReader reader;
|
||||||
|
|
||||||
|
public static ConsoleReader getReader()
|
||||||
|
{
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Function<String, String> formatter = Functions.identity();
|
||||||
|
|
||||||
|
public static void setFormatter(Function<String, String> format)
|
||||||
|
{
|
||||||
|
formatter = format != null ? format : Functions.<String> identity();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TerminalConsoleAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions)
|
||||||
|
{
|
||||||
|
super(name, filter, layout, ignoreExceptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PluginFactory
|
||||||
|
public static TerminalConsoleAppender createAppender(@PluginAttribute("name") String name, @PluginElement("Filters") Filter filter,
|
||||||
|
@PluginElement("Layout") Layout<? extends Serializable> layout, @PluginAttribute("ignoreExceptions") String ignore)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
LOGGER.error("No name provided for TerminalConsoleAppender");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (layout == null)
|
||||||
|
{
|
||||||
|
layout = PatternLayout.createLayout(null, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean ignoreExceptions = parseBoolean(ignore, true);
|
||||||
|
|
||||||
|
// This is handled by jline
|
||||||
|
System.setProperty("log4j.skipJansi", "true");
|
||||||
|
return new TerminalConsoleAppender(name, filter, layout, ignoreExceptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start()
|
||||||
|
{
|
||||||
|
// Initialize the reader if that hasn't happened yet
|
||||||
|
if (!initialized && reader == null)
|
||||||
|
{
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
if (ENABLE_JLINE)
|
||||||
|
{
|
||||||
|
final boolean hasConsole = System.console() != null;
|
||||||
|
if (hasConsole)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AnsiConsole.systemInstall();
|
||||||
|
reader = new ConsoleReader();
|
||||||
|
reader.setExpandEvents(false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.warn("Failed to initialize terminal. Falling back to default.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader == null)
|
||||||
|
{
|
||||||
|
// Eclipse doesn't support colors and characters like \r so enabling jline2 on it will
|
||||||
|
// just cause a lot of issues with empty lines and weird characters.
|
||||||
|
// Enable jline2 only on IntelliJ IDEA to prevent that.
|
||||||
|
// Also see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=76936
|
||||||
|
|
||||||
|
if (hasConsole || System.getProperty("java.class.path").contains("idea_rt.jar"))
|
||||||
|
{
|
||||||
|
// Disable advanced jline features
|
||||||
|
TerminalFactory.configure(OFF);
|
||||||
|
TerminalFactory.reset();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader = new ConsoleReader();
|
||||||
|
reader.setExpandEvents(false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.warn("Failed to initialize fallback terminal. Falling back to standard output console.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGGER.warn("Disabling terminal, you're running in an unsupported environment.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void append(LogEvent event)
|
||||||
|
{
|
||||||
|
if (reader != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Writer out = reader.getOutput();
|
||||||
|
out.write(RESET_LINE);
|
||||||
|
out.write(formatEvent(event));
|
||||||
|
|
||||||
|
reader.drawLine();
|
||||||
|
reader.flush();
|
||||||
|
}
|
||||||
|
catch (IOException ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.print(formatEvent(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String formatEvent(LogEvent event)
|
||||||
|
{
|
||||||
|
return formatter.apply(getLayout().toSerializable(event).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package net.minecraftforge.server.console;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
import jline.console.ConsoleReader;
|
||||||
|
import net.minecraft.server.dedicated.DedicatedServer;
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
|
||||||
|
public final class TerminalHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
|
private TerminalHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean handleCommands(DedicatedServer server)
|
||||||
|
{
|
||||||
|
final ConsoleReader reader = TerminalConsoleAppender.getReader();
|
||||||
|
if (reader != null)
|
||||||
|
{
|
||||||
|
TerminalConsoleAppender.setFormatter(new ConsoleFormatter());
|
||||||
|
reader.addCompleter(new ConsoleCommandCompleter(server));
|
||||||
|
|
||||||
|
String line;
|
||||||
|
while (!server.isServerStopped() && server.isServerRunning())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
line = reader.readLine("> ");
|
||||||
|
|
||||||
|
if (line != null)
|
||||||
|
{
|
||||||
|
line = line.trim();
|
||||||
|
if (!line.isEmpty())
|
||||||
|
{
|
||||||
|
server.addPendingCommand(line, server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.error("Exception handling console input", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TerminalConsoleAppender.setFormatter(new Function<String, String>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String apply(String text)
|
||||||
|
{
|
||||||
|
return EnumChatFormatting.getTextWithoutFormattingCodes(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
src/main/resources/log4j2_server.xml
Normal file
61
src/main/resources/log4j2_server.xml
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration status="WARN" packages="com.mojang.util,net.minecraftforge.server.console">
|
||||||
|
<Appenders>
|
||||||
|
<TerminalConsole name="FmlConsole">
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level] [%logger]: %msg%n" />
|
||||||
|
</TerminalConsole>
|
||||||
|
<TerminalConsole name="Console">
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
|
||||||
|
</TerminalConsole>
|
||||||
|
<!-- Keep a console appender open so log4j2 doesn't close our main out stream if we redirect System.out to the logger -->
|
||||||
|
<Console name="SysOut" target="SYSTEM_OUT"/>
|
||||||
|
<Queue name="ServerGuiConsole">
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level] [%logger]: %replace{%msg}{(?i)\u00A7[0-9A-FK-OR]}{}%n" />
|
||||||
|
</Queue>
|
||||||
|
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
|
||||||
|
<Policies>
|
||||||
|
<TimeBasedTriggeringPolicy />
|
||||||
|
<OnStartupTriggeringPolicy />
|
||||||
|
</Policies>
|
||||||
|
</RollingRandomAccessFile>
|
||||||
|
<Routing name="FmlFile">
|
||||||
|
<Routes pattern="$${ctx:side}">
|
||||||
|
<Route>
|
||||||
|
<RollingRandomAccessFile name="FmlFile" fileName="logs/fml-${ctx:side}-latest.log" filePattern="logs/fml-${ctx:side}-%i.log">
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level] [%logger/%X{mod}]: %replace{%msg}{(?i)\u00A7[0-9A-FK-OR]}{}%n" />
|
||||||
|
<DefaultRolloverStrategy max="3" fileIndex="max" />
|
||||||
|
<Policies>
|
||||||
|
<OnStartupTriggeringPolicy />
|
||||||
|
</Policies>
|
||||||
|
</RollingRandomAccessFile>
|
||||||
|
</Route>
|
||||||
|
<Route key="$${ctx:side}">
|
||||||
|
<RandomAccessFile name="FmlFile" fileName="logs/fml-junk-earlystartup.log" >
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level] [%logger]: %replace{%msg}{(?i)\u00A7[0-9A-FK-OR]}{}%n" />
|
||||||
|
</RandomAccessFile>
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
</Routing>
|
||||||
|
</Appenders>
|
||||||
|
<Loggers>
|
||||||
|
<Logger level="info" name="com.mojang" additivity="false">
|
||||||
|
<AppenderRef ref="Console" level="INFO" />
|
||||||
|
<AppenderRef ref="File" />
|
||||||
|
<AppenderRef ref="ServerGuiConsole" level="INFO" />
|
||||||
|
</Logger>
|
||||||
|
<Logger level="info" name="net.minecraft" additivity="false">
|
||||||
|
<filters>
|
||||||
|
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL" />
|
||||||
|
</filters>
|
||||||
|
<AppenderRef ref="Console" level="INFO" />
|
||||||
|
<AppenderRef ref="File" />
|
||||||
|
<AppenderRef ref="ServerGuiConsole" level="INFO" />
|
||||||
|
</Logger>
|
||||||
|
<Root level="all">
|
||||||
|
<AppenderRef ref="FmlConsole" level="INFO" />
|
||||||
|
<AppenderRef ref="ServerGuiConsole" level="INFO" />
|
||||||
|
<AppenderRef ref="FmlFile"/>
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
Loading…
Reference in a new issue