Update to JLine 3

This commit is contained in:
md_5 2025-07-14 19:26:00 +10:00
parent d257c87b12
commit 2ed8fab2ee
No known key found for this signature in database
GPG key ID: E8E901AC7C617C11
11 changed files with 148 additions and 228 deletions

View file

@ -1,6 +1,6 @@
--- a/net/minecraft/server/MinecraftServer.java --- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java
@@ -177,13 +177,39 @@ @@ -177,13 +177,40 @@
import net.minecraft.world.phys.Vec3D; import net.minecraft.world.phys.Vec3D;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -9,7 +9,6 @@
+import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.Lifecycle;
+import java.io.File; +import java.io.File;
+import java.util.Random; +import java.util.Random;
+import jline.console.ConsoleReader;
+import joptsimple.OptionSet; +import joptsimple.OptionSet;
+import net.minecraft.nbt.NbtException; +import net.minecraft.nbt.NbtException;
+import net.minecraft.nbt.ReportedNbtException; +import net.minecraft.nbt.ReportedNbtException;
@ -28,6 +27,8 @@
+import org.bukkit.craftbukkit.Main; +import org.bukkit.craftbukkit.Main;
+import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.server.ServerLoadEvent; +import org.bukkit.event.server.ServerLoadEvent;
+import org.jline.terminal.Terminal;
+import org.jline.terminal.TerminalBuilder;
+// CraftBukkit end +// CraftBukkit end
+ +
public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTask> implements ServerInfo, ChunkIOErrorReporter, ICommandListener { public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTask> implements ServerInfo, ChunkIOErrorReporter, ICommandListener {
@ -41,7 +42,7 @@
private static final int OVERLOADED_TICKS_THRESHOLD = 20; private static final int OVERLOADED_TICKS_THRESHOLD = 20;
private static final long OVERLOADED_WARNING_INTERVAL_NANOS = 10L * TimeRange.NANOSECONDS_PER_SECOND; private static final long OVERLOADED_WARNING_INTERVAL_NANOS = 10L * TimeRange.NANOSECONDS_PER_SECOND;
private static final int OVERLOADED_TICKS_WARNING_INTERVAL = 100; private static final int OVERLOADED_TICKS_WARNING_INTERVAL = 100;
@@ -274,6 +300,19 @@ @@ -274,6 +301,19 @@
private final SuppressedExceptionCollector suppressedExceptions; private final SuppressedExceptionCollector suppressedExceptions;
private final DiscontinuousFrame tickFrame; private final DiscontinuousFrame tickFrame;
@ -50,7 +51,7 @@
+ public org.bukkit.craftbukkit.CraftServer server; + public org.bukkit.craftbukkit.CraftServer server;
+ public OptionSet options; + public OptionSet options;
+ public org.bukkit.command.ConsoleCommandSender console; + public org.bukkit.command.ConsoleCommandSender console;
+ public ConsoleReader reader; + public Terminal terminal;
+ public static int currentTick = (int) (System.currentTimeMillis() / 50); + public static int currentTick = (int) (System.currentTimeMillis() / 50);
+ public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>(); + public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
+ public int autosavePeriod; + public int autosavePeriod;
@ -61,7 +62,7 @@
public static <S extends MinecraftServer> S spin(Function<Thread, S> function) { public static <S extends MinecraftServer> S spin(Function<Thread, S> function) {
AtomicReference<S> atomicreference = new AtomicReference(); AtomicReference<S> atomicreference = new AtomicReference();
Thread thread = new Thread(() -> { Thread thread = new Thread(() -> {
@@ -294,7 +333,7 @@ @@ -294,7 +334,7 @@
return s0; return s0;
} }
@ -70,7 +71,7 @@
super("Server"); super("Server");
this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; this.metricsRecorder = InactiveMetricsRecorder.INSTANCE;
this.onMetricsRecordingStopped = (methodprofilerresults) -> { this.onMetricsRecordingStopped = (methodprofilerresults) -> {
@@ -318,7 +357,7 @@ @@ -318,7 +358,7 @@
this.suppressedExceptions = new SuppressedExceptionCollector(); this.suppressedExceptions = new SuppressedExceptionCollector();
this.registries = worldstem.registries(); this.registries = worldstem.registries();
this.worldData = worldstem.worldData(); this.worldData = worldstem.worldData();
@ -79,7 +80,7 @@
throw new IllegalStateException("Missing Overworld dimension data"); throw new IllegalStateException("Missing Overworld dimension data");
} else { } else {
this.proxy = proxy; this.proxy = proxy;
@@ -346,6 +385,33 @@ @@ -346,6 +386,30 @@
this.fuelValues = FuelValues.vanillaBurnTimes(this.registries.compositeAccess(), this.worldData.enabledFeatures()); this.fuelValues = FuelValues.vanillaBurnTimes(this.registries.compositeAccess(), this.worldData.enabledFeatures());
this.tickFrame = TracyClient.createDiscontinuousFrame("Server Tick"); this.tickFrame = TracyClient.createDiscontinuousFrame("Server Tick");
} }
@ -88,22 +89,19 @@
+ this.worldLoader = worldLoader; + this.worldLoader = worldLoader;
+ this.vanillaCommandDispatcher = worldstem.dataPackResources().commands; // CraftBukkit + this.vanillaCommandDispatcher = worldstem.dataPackResources().commands; // CraftBukkit
+ // Try to see if we're actually running in a terminal, disable jline if not + // Try to see if we're actually running in a terminal, disable jline if not
+ if (System.console() == null && System.getProperty("jline.terminal") == null) { + if (System.console() == null && System.getProperty(TerminalBuilder.PROP_PROVIDER) == null) {
+ System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); + System.setProperty(TerminalBuilder.PROP_PROVIDER, Terminal.TYPE_DUMB);
+ Main.useJline = false; + Main.useJline = false;
+ } + }
+ +
+ try { + try {
+ reader = new ConsoleReader(System.in, System.out); + terminal = TerminalBuilder.builder().dumb(false).build();
+ reader.setExpandEvents(false); // Avoid parsing exceptions for uncommonly used event designators
+ } catch (Throwable e) { + } catch (Throwable e) {
+ try { + try {
+ // Try again with jline disabled for Windows users without C++ 2008 Redistributable + // Try again with jline disabled for Windows users without C++ 2008 Redistributable
+ System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); + System.setProperty(TerminalBuilder.PROP_PROVIDER, Terminal.TYPE_DUMB);
+ System.setProperty("user.language", "en");
+ Main.useJline = false; + Main.useJline = false;
+ reader = new ConsoleReader(System.in, System.out); + terminal = TerminalBuilder.builder().build();
+ reader.setExpandEvents(false);
+ } catch (IOException ex) { + } catch (IOException ex) {
+ LOGGER.warn((String) null, ex); + LOGGER.warn((String) null, ex);
+ } + }
@ -113,7 +111,7 @@
} }
private void readScoreboard(WorldPersistentData worldpersistentdata) { private void readScoreboard(WorldPersistentData worldpersistentdata) {
@@ -354,7 +420,7 @@ @@ -354,7 +418,7 @@
protected abstract boolean initServer() throws IOException; protected abstract boolean initServer() throws IOException;
@ -122,7 +120,7 @@
if (!JvmProfiler.INSTANCE.isRunning()) { if (!JvmProfiler.INSTANCE.isRunning()) {
; ;
} }
@@ -362,12 +428,8 @@ @@ -362,12 +426,8 @@
boolean flag = false; boolean flag = false;
ProfiledDuration profiledduration = JvmProfiler.INSTANCE.onWorldLoadedStarted(); ProfiledDuration profiledduration = JvmProfiler.INSTANCE.onWorldLoadedStarted();
@ -136,7 +134,7 @@
if (profiledduration != null) { if (profiledduration != null) {
profiledduration.finish(true); profiledduration.finish(true);
} }
@@ -384,23 +446,217 @@ @@ -384,23 +444,217 @@
protected void forceDifficulty() {} protected void forceDifficulty() {}
@ -368,7 +366,7 @@
if (!iworlddataserver.isInitialized()) { if (!iworlddataserver.isInitialized()) {
try { try {
@@ -424,28 +680,8 @@ @@ -424,28 +678,8 @@
iworlddataserver.setInitialized(true); iworlddataserver.setInitialized(true);
} }
@ -398,7 +396,7 @@
private static void setInitialSpawn(WorldServer worldserver, IWorldDataServer iworlddataserver, boolean flag, boolean flag1) { private static void setInitialSpawn(WorldServer worldserver, IWorldDataServer iworlddataserver, boolean flag, boolean flag1) {
if (flag1) { if (flag1) {
@@ -453,6 +689,21 @@ @@ -453,6 +687,21 @@
} else { } else {
ChunkProviderServer chunkproviderserver = worldserver.getChunkSource(); ChunkProviderServer chunkproviderserver = worldserver.getChunkSource();
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(chunkproviderserver.randomState().sampler().findSpawnPosition()); ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(chunkproviderserver.randomState().sampler().findSpawnPosition());
@ -420,7 +418,7 @@
int i = chunkproviderserver.getGenerator().getSpawnHeight(worldserver); int i = chunkproviderserver.getGenerator().getSpawnHeight(worldserver);
if (i < worldserver.getMinY()) { if (i < worldserver.getMinY()) {
@@ -511,8 +762,11 @@ @@ -511,8 +760,11 @@
iworlddataserver.setGameType(EnumGamemode.SPECTATOR); iworlddataserver.setGameType(EnumGamemode.SPECTATOR);
} }
@ -434,7 +432,7 @@
MinecraftServer.LOGGER.info("Preparing start region for dimension {}", worldserver.dimension().location()); MinecraftServer.LOGGER.info("Preparing start region for dimension {}", worldserver.dimension().location());
BlockPosition blockposition = worldserver.getSharedSpawnPos(); BlockPosition blockposition = worldserver.getSharedSpawnPos();
@@ -522,18 +776,21 @@ @@ -522,18 +774,21 @@
this.nextTickTimeNanos = SystemUtils.getNanos(); this.nextTickTimeNanos = SystemUtils.getNanos();
worldserver.setDefaultSpawnPos(blockposition, worldserver.getSharedSpawnAngle()); worldserver.setDefaultSpawnPos(blockposition, worldserver.getSharedSpawnAngle());
@ -462,7 +460,7 @@
TicketStorage ticketstorage = (TicketStorage) worldserver1.getDataStorage().get(TicketStorage.TYPE); TicketStorage ticketstorage = (TicketStorage) worldserver1.getDataStorage().get(TicketStorage.TYPE);
if (ticketstorage != null) { if (ticketstorage != null) {
@@ -541,10 +798,17 @@ @@ -541,10 +796,17 @@
} }
} }
@ -483,7 +481,7 @@
} }
public EnumGamemode getDefaultGameType() { public EnumGamemode getDefaultGameType() {
@@ -573,12 +837,16 @@ @@ -573,12 +835,16 @@
flag3 = true; flag3 = true;
} }
@ -500,7 +498,7 @@
if (flag1) { if (flag1) {
for (WorldServer worldserver2 : this.getAllLevels()) { for (WorldServer worldserver2 : this.getAllLevels()) {
MinecraftServer.LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", worldserver2.getChunkSource().chunkMap.getStorageName()); MinecraftServer.LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", worldserver2.getChunkSource().chunkMap.getStorageName());
@@ -609,18 +877,40 @@ @@ -609,18 +875,40 @@
this.stopServer(); this.stopServer();
} }
@ -541,7 +539,7 @@
} }
MinecraftServer.LOGGER.info("Saving worlds"); MinecraftServer.LOGGER.info("Saving worlds");
@@ -700,7 +990,7 @@ @@ -700,7 +988,7 @@
} }
this.nextTickTimeNanos = SystemUtils.getNanos(); this.nextTickTimeNanos = SystemUtils.getNanos();
@ -550,7 +548,7 @@
this.status = this.buildServerStatus(); this.status = this.buildServerStatus();
while (this.running) { while (this.running) {
@@ -717,6 +1007,7 @@ @@ -717,6 +1005,7 @@
if (j > MinecraftServer.OVERLOADED_THRESHOLD_NANOS + 20L * i && this.nextTickTimeNanos - this.lastOverloadWarningNanos >= MinecraftServer.OVERLOADED_WARNING_INTERVAL_NANOS + 100L * i) { if (j > MinecraftServer.OVERLOADED_THRESHOLD_NANOS + 20L * i && this.nextTickTimeNanos - this.lastOverloadWarningNanos >= MinecraftServer.OVERLOADED_WARNING_INTERVAL_NANOS + 100L * i) {
long k = j / i; long k = j / i;
@ -558,7 +556,7 @@
MinecraftServer.LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", j / TimeRange.NANOSECONDS_PER_MILLISECOND, k); MinecraftServer.LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", j / TimeRange.NANOSECONDS_PER_MILLISECOND, k);
this.nextTickTimeNanos += k * i; this.nextTickTimeNanos += k * i;
this.lastOverloadWarningNanos = this.nextTickTimeNanos; this.lastOverloadWarningNanos = this.nextTickTimeNanos;
@@ -730,6 +1021,7 @@ @@ -730,6 +1019,7 @@
this.debugCommandProfiler = new MinecraftServer.TimeProfiler(SystemUtils.getNanos(), this.tickCount); this.debugCommandProfiler = new MinecraftServer.TimeProfiler(SystemUtils.getNanos(), this.tickCount);
} }
@ -566,20 +564,20 @@
this.nextTickTimeNanos += i; this.nextTickTimeNanos += i;
try { try {
@@ -803,6 +1095,12 @@ @@ -803,6 +1093,12 @@
this.services.profileCache().clearExecutor(); this.services.profileCache().clearExecutor();
} }
+ // CraftBukkit start - Restore terminal to original settings + // CraftBukkit start - Restore terminal to original settings
+ try { + try {
+ reader.getTerminal().restore(); + terminal.close();
+ } catch (Exception ignored) { + } catch (Exception ignored) {
+ } + }
+ // CraftBukkit end + // CraftBukkit end
this.onServerExit(); this.onServerExit();
} }
@@ -862,7 +1160,14 @@ @@ -862,7 +1158,14 @@
} }
private boolean haveTime() { private boolean haveTime() {
@ -595,7 +593,7 @@
} }
public static boolean throwIfFatalException() { public static boolean throwIfFatalException() {
@@ -876,7 +1181,7 @@ @@ -876,7 +1179,7 @@
} }
public static void setFatalException(RuntimeException runtimeexception) { public static void setFatalException(RuntimeException runtimeexception) {
@ -604,7 +602,7 @@
} }
@Override @Override
@@ -946,7 +1251,7 @@ @@ -946,7 +1249,7 @@
} }
} }
@ -613,7 +611,7 @@
Profiler.get().incrementCounter("runTask"); Profiler.get().incrementCounter("runTask");
super.doRunTask(ticktask); super.doRunTask(ticktask);
} }
@@ -1010,6 +1315,7 @@ @@ -1010,6 +1313,7 @@
this.autoSave(); this.autoSave();
} }
@ -621,7 +619,7 @@
this.tickConnection(); this.tickConnection();
return; return;
} }
@@ -1024,7 +1330,7 @@ @@ -1024,7 +1328,7 @@
} }
--this.ticksUntilAutosave; --this.ticksUntilAutosave;
@ -630,7 +628,7 @@
this.autoSave(); this.autoSave();
} }
@@ -1043,7 +1349,7 @@ @@ -1043,7 +1347,7 @@
} }
private void autoSave() { private void autoSave() {
@ -639,7 +637,7 @@
MinecraftServer.LOGGER.debug("Autosave started"); MinecraftServer.LOGGER.debug("Autosave started");
GameProfilerFiller gameprofilerfiller = Profiler.get(); GameProfilerFiller gameprofilerfiller = Profiler.get();
@@ -1123,21 +1429,38 @@ @@ -1123,21 +1427,38 @@
this.getPlayerList().getPlayers().forEach((entityplayer) -> { this.getPlayerList().getPlayers().forEach((entityplayer) -> {
entityplayer.connection.suspendFlushing(); entityplayer.connection.suspendFlushing();
}); });
@ -678,7 +676,7 @@
gameprofilerfiller.push("tick"); gameprofilerfiller.push("tick");
@@ -1227,6 +1550,22 @@ @@ -1227,6 +1548,22 @@
return (WorldServer) this.levels.get(resourcekey); return (WorldServer) this.levels.get(resourcekey);
} }
@ -701,7 +699,7 @@
public Set<ResourceKey<World>> levelKeys() { public Set<ResourceKey<World>> levelKeys() {
return this.levels.keySet(); return this.levels.keySet();
} }
@@ -1256,7 +1595,7 @@ @@ -1256,7 +1593,7 @@
@DontObfuscate @DontObfuscate
public String getServerModName() { public String getServerModName() {
@ -710,7 +708,7 @@
} }
public SystemReport fillSystemReport(SystemReport systemreport) { public SystemReport fillSystemReport(SystemReport systemreport) {
@@ -1590,11 +1929,11 @@ @@ -1590,11 +1927,11 @@
public CompletableFuture<Void> reloadResources(Collection<String> collection) { public CompletableFuture<Void> reloadResources(Collection<String> collection) {
CompletableFuture<Void> completablefuture = CompletableFuture.supplyAsync(() -> { CompletableFuture<Void> completablefuture = CompletableFuture.supplyAsync(() -> {
@ -724,7 +722,7 @@
}, this).thenCompose((immutablelist) -> { }, this).thenCompose((immutablelist) -> {
IReloadableResourceManager ireloadableresourcemanager = new ResourceManager(EnumResourcePackType.SERVER_DATA, immutablelist); IReloadableResourceManager ireloadableresourcemanager = new ResourceManager(EnumResourcePackType.SERVER_DATA, immutablelist);
List<IRegistry.a<?>> list = TagDataPack.loadTagsForExistingRegistries(ireloadableresourcemanager, this.registries.compositeAccess()); List<IRegistry.a<?>> list = TagDataPack.loadTagsForExistingRegistries(ireloadableresourcemanager, this.registries.compositeAccess());
@@ -1610,6 +1949,7 @@ @@ -1610,6 +1947,7 @@
}).thenAcceptAsync((minecraftserver_reloadableresources) -> { }).thenAcceptAsync((minecraftserver_reloadableresources) -> {
this.resources.close(); this.resources.close();
this.resources = minecraftserver_reloadableresources; this.resources = minecraftserver_reloadableresources;
@ -732,7 +730,7 @@
this.packRepository.setSelected(collection); this.packRepository.setSelected(collection);
WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures()); WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures());
@@ -1939,6 +2279,22 @@ @@ -1939,6 +2277,22 @@
} }
} }
@ -755,7 +753,7 @@
private GameProfilerFiller createProfiler() { private GameProfilerFiller createProfiler() {
if (this.willStartRecordingMetrics) { if (this.willStartRecordingMetrics) {
this.metricsRecorder = ActiveMetricsRecorder.createStarted(new ServerMetricsSamplersProvider(SystemUtils.timeSource, this.isDedicatedServer()), SystemUtils.timeSource, SystemUtils.ioPool(), new MetricsPersister("server"), this.onMetricsRecordingStopped, (path) -> { this.metricsRecorder = ActiveMetricsRecorder.createStarted(new ServerMetricsSamplersProvider(SystemUtils.timeSource, this.isDedicatedServer()), SystemUtils.timeSource, SystemUtils.ioPool(), new MetricsPersister("server"), this.onMetricsRecordingStopped, (path) -> {
@@ -2066,6 +2422,11 @@ @@ -2066,6 +2420,11 @@
} }
@ -767,7 +765,7 @@
public ChatDecorator getChatDecorator() { public ChatDecorator getChatDecorator() {
return ChatDecorator.PLAIN; return ChatDecorator.PLAIN;
} }
@@ -2076,8 +2437,11 @@ @@ -2076,8 +2435,11 @@
public void subscribeToDebugSample(EntityPlayer entityplayer, RemoteDebugSampleType remotedebugsampletype) {} public void subscribeToDebugSample(EntityPlayer entityplayer, RemoteDebugSampleType remotedebugsampletype) {}

View file

@ -1,6 +1,6 @@
--- a/net/minecraft/server/dedicated/DedicatedServer.java --- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java +++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -59,6 +59,18 @@ @@ -59,6 +59,21 @@
import net.minecraft.world.level.storage.Convertable; import net.minecraft.world.level.storage.Convertable;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -10,16 +10,19 @@
+import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.io.IoBuilder; +import org.apache.logging.log4j.io.IoBuilder;
+import org.bukkit.command.CommandSender; +import org.bukkit.command.CommandSender;
+import org.bukkit.craftbukkit.util.TerminalCompletionHandler;
+import org.bukkit.craftbukkit.util.TerminalConsoleWriterThread; +import org.bukkit.craftbukkit.util.TerminalConsoleWriterThread;
+import org.bukkit.event.server.ServerCommandEvent; +import org.bukkit.event.server.ServerCommandEvent;
+import org.bukkit.event.server.RemoteServerCommandEvent; +import org.bukkit.event.server.RemoteServerCommandEvent;
+import org.jline.reader.EndOfFileException;
+import org.jline.reader.LineReader;
+import org.jline.reader.LineReaderBuilder;
+import org.jline.reader.UserInterruptException;
+// CraftBukkit end +// CraftBukkit end
+ +
public class DedicatedServer extends MinecraftServer implements IMinecraftServer { public class DedicatedServer extends MinecraftServer implements IMinecraftServer {
static final Logger LOGGER = LogUtils.getLogger(); static final Logger LOGGER = LogUtils.getLogger();
@@ -67,7 +79,7 @@ @@ -67,7 +82,7 @@
private final List<ServerCommand> consoleInput = Collections.synchronizedList(Lists.newArrayList()); private final List<ServerCommand> consoleInput = Collections.synchronizedList(Lists.newArrayList());
@Nullable @Nullable
private RemoteStatusListener queryThreadGs4; private RemoteStatusListener queryThreadGs4;
@ -28,7 +31,7 @@
@Nullable @Nullable
private RemoteControlListener rconThread; private RemoteControlListener rconThread;
public DedicatedServerSettings settings; public DedicatedServerSettings settings;
@@ -81,10 +93,12 @@ @@ -81,33 +96,93 @@
private DebugSampleSubscriptionTracker debugSampleSubscriptionTracker; private DebugSampleSubscriptionTracker debugSampleSubscriptionTracker;
public ServerLinks serverLinks; public ServerLinks serverLinks;
@ -44,8 +47,15 @@
this.serverTextFilter = ServerTextFilter.createFromConfig(dedicatedserversettings.getProperties()); this.serverTextFilter = ServerTextFilter.createFromConfig(dedicatedserversettings.getProperties());
this.serverLinks = createServerLinks(dedicatedserversettings); this.serverLinks = createServerLinks(dedicatedserversettings);
} }
@@ -93,13 +107,44 @@
@Override
public boolean initServer() throws IOException { public boolean initServer() throws IOException {
+ LineReader reader = LineReaderBuilder.builder()
+ .terminal(terminal)
+ .option(LineReader.Option.DISABLE_EVENT_EXPANSION, true)
+ .completer(new org.bukkit.craftbukkit.command.ConsoleCommandCompleter())
+ .build();
+
Thread thread = new Thread("Server console handler") { Thread thread = new Thread("Server console handler") {
public void run() { public void run() {
- BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); - BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
@ -53,7 +63,6 @@
+ if (!org.bukkit.craftbukkit.Main.useConsole) { + if (!org.bukkit.craftbukkit.Main.useConsole) {
+ return; + return;
+ } + }
+ jline.console.ConsoleReader bufferedreader = reader;
+ +
+ // MC-33041, SPIGOT-5538: if System.in is not valid due to javaw, then return + // MC-33041, SPIGOT-5538: if System.in is not valid due to javaw, then return
+ try { + try {
@ -70,29 +79,31 @@
- DedicatedServer.this.handleConsoleInput(s, DedicatedServer.this.createCommandSourceStack()); - DedicatedServer.this.handleConsoleInput(s, DedicatedServer.this.createCommandSourceStack());
+ // CraftBukkit start - JLine disabling compatibility + // CraftBukkit start - JLine disabling compatibility
+ while (!DedicatedServer.this.isStopped() && DedicatedServer.this.isRunning()) { + while (!DedicatedServer.this.isStopped() && DedicatedServer.this.isRunning()) {
+ if (org.bukkit.craftbukkit.Main.useJline) { + try {
+ s = bufferedreader.readLine(">", null); + if (org.bukkit.craftbukkit.Main.useJline) {
+ } else { + s = reader.readLine(">", null);
+ s = bufferedreader.readLine(); + } else {
+ } + s = reader.readLine();
+ }
+ +
+ // SPIGOT-5220: Throttle if EOF (ctrl^d) or stdin is /dev/null + if (s.trim().length() > 0) { // Trim to filter lines which are just spaces
+ if (s == null) { + DedicatedServer.this.handleConsoleInput(s, DedicatedServer.this.createCommandSourceStack());
+ }
+ } catch (EndOfFileException | UserInterruptException eof) {
+ // SPIGOT-5220: Throttle if EOF (ctrl^d) or stdin is /dev/null
+ try { + try {
+ Thread.sleep(50L); + Thread.sleep(50L);
+ } catch (InterruptedException ex) { + } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt(); + Thread.currentThread().interrupt();
+ } + }
+ continue;
+ } + }
+ if (s.trim().length() > 0) { // Trim to filter lines which are just spaces
+ DedicatedServer.this.handleConsoleInput(s, DedicatedServer.this.createCommandSourceStack());
+ }
+ // CraftBukkit end
} }
} catch (IOException ioexception) { - } catch (IOException ioexception) {
+ } catch (Throwable ioexception) {
+ // CraftBukkit end
DedicatedServer.LOGGER.error("Exception handling console input", ioexception); DedicatedServer.LOGGER.error("Exception handling console input", ioexception);
@@ -108,6 +153,29 @@ }
} }
}; };
@ -111,8 +122,7 @@
+ } + }
+ } + }
+ +
+ TerminalConsoleWriterThread writerThread = new TerminalConsoleWriterThread(System.out, this.reader); + TerminalConsoleWriterThread writerThread = new TerminalConsoleWriterThread(System.out, reader);
+ this.reader.setCompletionHandler(new TerminalCompletionHandler(writerThread, this.reader.getCompletionHandler()));
+ writerThread.start(); + writerThread.start();
+ +
+ System.setOut(IoBuilder.forLogger(logger).setLevel(Level.INFO).buildPrintStream()); + System.setOut(IoBuilder.forLogger(logger).setLevel(Level.INFO).buildPrintStream());
@ -122,7 +132,7 @@
thread.setDaemon(true); thread.setDaemon(true);
thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER)); thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER));
thread.start(); thread.start();
@@ -132,7 +200,7 @@ @@ -132,7 +207,7 @@
this.setMotd(dedicatedserverproperties.motd); this.setMotd(dedicatedserverproperties.motd);
super.setPlayerIdleTimeout((Integer) dedicatedserverproperties.playerIdleTimeout.get()); super.setPlayerIdleTimeout((Integer) dedicatedserverproperties.playerIdleTimeout.get());
this.setEnforceWhitelist(dedicatedserverproperties.enforceWhitelist); this.setEnforceWhitelist(dedicatedserverproperties.enforceWhitelist);
@ -131,7 +141,7 @@
DedicatedServer.LOGGER.info("Default game type: {}", dedicatedserverproperties.gamemode); DedicatedServer.LOGGER.info("Default game type: {}", dedicatedserverproperties.gamemode);
InetAddress inetaddress = null; InetAddress inetaddress = null;
@@ -156,6 +224,12 @@ @@ -156,6 +231,12 @@
return false; return false;
} }
@ -144,7 +154,7 @@
if (!this.usesAuthentication()) { if (!this.usesAuthentication()) {
DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!");
DedicatedServer.LOGGER.warn("The server will make no attempt to authenticate usernames. Beware."); DedicatedServer.LOGGER.warn("The server will make no attempt to authenticate usernames. Beware.");
@@ -170,7 +244,7 @@ @@ -170,7 +251,7 @@
if (!NameReferencingFileConverter.serverReadyAfterUserconversion(this)) { if (!NameReferencingFileConverter.serverReadyAfterUserconversion(this)) {
return false; return false;
} else { } else {
@ -153,7 +163,7 @@
this.debugSampleSubscriptionTracker = new DebugSampleSubscriptionTracker(this.getPlayerList()); this.debugSampleSubscriptionTracker = new DebugSampleSubscriptionTracker(this.getPlayerList());
this.tickTimeLogger = new RemoteSampleLogger(TpsDebugDimensions.values().length, this.debugSampleSubscriptionTracker, RemoteDebugSampleType.TICK_TIME); this.tickTimeLogger = new RemoteSampleLogger(TpsDebugDimensions.values().length, this.debugSampleSubscriptionTracker, RemoteDebugSampleType.TICK_TIME);
long i = SystemUtils.getNanos(); long i = SystemUtils.getNanos();
@@ -178,13 +252,13 @@ @@ -178,13 +259,13 @@
TileEntitySkull.setup(this.services, this); TileEntitySkull.setup(this.services, this);
UserCache.setUsesAuthentication(this.usesAuthentication()); UserCache.setUsesAuthentication(this.usesAuthentication());
DedicatedServer.LOGGER.info("Preparing level \"{}\"", this.getLevelIdName()); DedicatedServer.LOGGER.info("Preparing level \"{}\"", this.getLevelIdName());
@ -169,7 +179,7 @@
} }
if (dedicatedserverproperties.enableQuery) { if (dedicatedserverproperties.enableQuery) {
@@ -278,6 +352,7 @@ @@ -278,6 +359,7 @@
this.queryThreadGs4.stop(); this.queryThreadGs4.stop();
} }
@ -177,7 +187,7 @@
} }
@Override @Override
@@ -299,7 +374,15 @@ @@ -299,7 +381,15 @@
while (!this.consoleInput.isEmpty()) { while (!this.consoleInput.isEmpty()) {
ServerCommand servercommand = (ServerCommand) this.consoleInput.remove(0); ServerCommand servercommand = (ServerCommand) this.consoleInput.remove(0);
@ -194,7 +204,7 @@
} }
} }
@@ -524,16 +607,52 @@ @@ -524,16 +614,52 @@
@Override @Override
public String getPluginNames() { public String getPluginNames() {
@ -251,7 +261,7 @@
} }
public void storeUsingWhiteList(boolean flag) { public void storeUsingWhiteList(boolean flag) {
@@ -643,4 +762,15 @@ @@ -643,4 +769,15 @@
} }
} }
} }

View file

@ -49,7 +49,7 @@
public final WorldNBTStorage playerIo; public final WorldNBTStorage playerIo;
private boolean doWhiteList; private boolean doWhiteList;
private final LayeredRegistryAccess<RegistryLayer> registries; private final LayeredRegistryAccess<RegistryLayer> registries;
@@ -131,13 +155,23 @@ @@ -131,13 +155,22 @@
private static final boolean ALLOW_LOGOUTIVATOR = false; private static final boolean ALLOW_LOGOUTIVATOR = false;
private int sendAllPlayerInfoIn; private int sendAllPlayerInfoIn;
@ -59,7 +59,6 @@
public PlayerList(MinecraftServer minecraftserver, LayeredRegistryAccess<RegistryLayer> layeredregistryaccess, WorldNBTStorage worldnbtstorage, int i) { public PlayerList(MinecraftServer minecraftserver, LayeredRegistryAccess<RegistryLayer> layeredregistryaccess, WorldNBTStorage worldnbtstorage, int i) {
+ this.cserver = minecraftserver.server = new CraftServer((DedicatedServer) minecraftserver, this); + this.cserver = minecraftserver.server = new CraftServer((DedicatedServer) minecraftserver, this);
+ minecraftserver.console = org.bukkit.craftbukkit.command.ColouredConsoleSender.getInstance(); + minecraftserver.console = org.bukkit.craftbukkit.command.ColouredConsoleSender.getInstance();
+ minecraftserver.reader.addCompleter(new org.bukkit.craftbukkit.command.ConsoleCommandCompleter(minecraftserver.server));
+ // CraftBukkit end + // CraftBukkit end
+ +
this.bans = new GameProfileBanList(PlayerList.USERBANLIST_FILE); this.bans = new GameProfileBanList(PlayerList.USERBANLIST_FILE);
@ -75,7 +74,7 @@
this.server = minecraftserver; this.server = minecraftserver;
this.registries = layeredregistryaccess; this.registries = layeredregistryaccess;
this.maxPlayers = i; this.maxPlayers = i;
@@ -160,9 +194,16 @@ @@ -160,9 +193,16 @@
try (ProblemReporter.j problemreporter_j = new ProblemReporter.j(entityplayer.problemPath(), PlayerList.LOGGER)) { try (ProblemReporter.j problemreporter_j = new ProblemReporter.j(entityplayer.problemPath(), PlayerList.LOGGER)) {
Optional<ValueInput> optional1 = this.load(entityplayer, problemreporter_j); Optional<ValueInput> optional1 = this.load(entityplayer, problemreporter_j);
@ -93,7 +92,7 @@
WorldServer worldserver = this.server.getLevel(resourcekey); WorldServer worldserver = this.server.getLevel(resourcekey);
WorldServer worldserver1; WorldServer worldserver1;
@@ -181,10 +222,11 @@ @@ -181,10 +221,11 @@
worldserver1.waitForChunkAndEntities(entityplayer.chunkPosition(), 1); worldserver1.waitForChunkAndEntities(entityplayer.chunkPosition(), 1);
String s1 = networkmanager.getLoggableAddress(this.server.logIPs()); String s1 = networkmanager.getLoggableAddress(this.server.logIPs());
@ -107,7 +106,7 @@
PlayerConnection playerconnection = new PlayerConnection(this.server, networkmanager, entityplayer, commonlistenercookie); PlayerConnection playerconnection = new PlayerConnection(this.server, networkmanager, entityplayer, commonlistenercookie);
networkmanager.setupInboundProtocol(GameProtocols.SERVERBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()), playerconnection), playerconnection); networkmanager.setupInboundProtocol(GameProtocols.SERVERBOUND_TEMPLATE.bind(RegistryFriendlyByteBuf.decorator(this.server.registryAccess()), playerconnection), playerconnection);
@@ -194,6 +236,7 @@ @@ -194,6 +235,7 @@
boolean flag2 = gamerules.getBoolean(GameRules.RULE_LIMITED_CRAFTING); boolean flag2 = gamerules.getBoolean(GameRules.RULE_LIMITED_CRAFTING);
playerconnection.send(new PacketPlayOutLogin(entityplayer.getId(), worlddata.isHardcore(), this.server.levelKeys(), this.getMaxPlayers(), this.viewDistance, this.simulationDistance, flag1, !flag, flag2, entityplayer.createCommonSpawnInfo(worldserver1), this.server.enforceSecureProfile())); playerconnection.send(new PacketPlayOutLogin(entityplayer.getId(), worlddata.isHardcore(), this.server.levelKeys(), this.getMaxPlayers(), this.viewDistance, this.simulationDistance, flag1, !flag, flag2, entityplayer.createCommonSpawnInfo(worldserver1), this.server.enforceSecureProfile()));
@ -115,7 +114,7 @@
playerconnection.send(new PacketPlayOutServerDifficulty(worlddata.getDifficulty(), worlddata.isDifficultyLocked())); playerconnection.send(new PacketPlayOutServerDifficulty(worlddata.getDifficulty(), worlddata.isDifficultyLocked()));
playerconnection.send(new PacketPlayOutAbilities(entityplayer.getAbilities())); playerconnection.send(new PacketPlayOutAbilities(entityplayer.getAbilities()));
playerconnection.send(new PacketPlayOutHeldItemSlot(entityplayer.getInventory().getSelectedSlot())); playerconnection.send(new PacketPlayOutHeldItemSlot(entityplayer.getInventory().getSelectedSlot()));
@@ -212,8 +255,10 @@ @@ -212,8 +254,10 @@
} else { } else {
ichatmutablecomponent = IChatBaseComponent.translatable("multiplayer.player.joined.renamed", entityplayer.getDisplayName(), s); ichatmutablecomponent = IChatBaseComponent.translatable("multiplayer.player.joined.renamed", entityplayer.getDisplayName(), s);
} }
@ -127,7 +126,7 @@
playerconnection.teleport(entityplayer.getX(), entityplayer.getY(), entityplayer.getZ(), entityplayer.getYRot(), entityplayer.getXRot()); playerconnection.teleport(entityplayer.getX(), entityplayer.getY(), entityplayer.getZ(), entityplayer.getYRot(), entityplayer.getXRot());
ServerPing serverping = this.server.getStatus(); ServerPing serverping = this.server.getStatus();
@@ -221,19 +266,72 @@ @@ -221,19 +265,72 @@
entityplayer.sendServerStatus(serverping); entityplayer.sendServerStatus(serverping);
} }
@ -204,7 +203,7 @@
} }
} }
@@ -260,30 +358,31 @@ @@ -260,30 +357,31 @@
} }
public void addWorldborderListener(WorldServer worldserver) { public void addWorldborderListener(WorldServer worldserver) {
@ -241,7 +240,7 @@
} }
@Override @Override
@@ -312,14 +411,15 @@ @@ -312,14 +410,15 @@
} }
protected void save(EntityPlayer entityplayer) { protected void save(EntityPlayer entityplayer) {
@ -259,7 +258,7 @@
if (advancementdataplayer != null) { if (advancementdataplayer != null) {
advancementdataplayer.save(); advancementdataplayer.save();
@@ -327,10 +427,24 @@ @@ -327,10 +426,24 @@
} }
@ -285,7 +284,7 @@
this.save(entityplayer); this.save(entityplayer);
if (entityplayer.isPassenger()) { if (entityplayer.isPassenger()) {
Entity entity = entityplayer.getRootVehicle(); Entity entity = entityplayer.getRootVehicle();
@@ -339,7 +453,7 @@ @@ -339,7 +452,7 @@
PlayerList.LOGGER.debug("Removing player mount"); PlayerList.LOGGER.debug("Removing player mount");
entityplayer.stopRiding(); entityplayer.stopRiding();
entity.getPassengersAndSelf().forEach((entity1) -> { entity.getPassengersAndSelf().forEach((entity1) -> {
@ -294,7 +293,7 @@
}); });
} }
} }
@@ -347,7 +461,7 @@ @@ -347,7 +460,7 @@
entityplayer.unRide(); entityplayer.unRide();
for (EntityEnderPearl entityenderpearl : entityplayer.getEnderPearls()) { for (EntityEnderPearl entityenderpearl : entityplayer.getEnderPearls()) {
@ -303,7 +302,7 @@
} }
worldserver.removePlayerImmediately(entityplayer, Entity.RemovalReason.UNLOADED_WITH_PLAYER); worldserver.removePlayerImmediately(entityplayer, Entity.RemovalReason.UNLOADED_WITH_PLAYER);
@@ -359,15 +473,58 @@ @@ -359,15 +472,58 @@
if (entityplayer1 == entityplayer) { if (entityplayer1 == entityplayer) {
this.playersByUUID.remove(uuid); this.playersByUUID.remove(uuid);
@ -367,7 +366,7 @@
if (this.bans.isBanned(gameprofile)) { if (this.bans.isBanned(gameprofile)) {
GameProfileBanEntry gameprofilebanentry = (GameProfileBanEntry) this.bans.get(gameprofile); GameProfileBanEntry gameprofilebanentry = (GameProfileBanEntry) this.bans.get(gameprofile);
IChatMutableComponent ichatmutablecomponent = IChatBaseComponent.translatable("multiplayer.disconnect.banned.reason", gameprofilebanentry.getReason()); IChatMutableComponent ichatmutablecomponent = IChatBaseComponent.translatable("multiplayer.disconnect.banned.reason", gameprofilebanentry.getReason());
@@ -376,9 +533,11 @@ @@ -376,9 +532,11 @@
ichatmutablecomponent.append((IChatBaseComponent) IChatBaseComponent.translatable("multiplayer.disconnect.banned.expiration", PlayerList.BAN_DATE_FORMAT.format(gameprofilebanentry.getExpires()))); ichatmutablecomponent.append((IChatBaseComponent) IChatBaseComponent.translatable("multiplayer.disconnect.banned.expiration", PlayerList.BAN_DATE_FORMAT.format(gameprofilebanentry.getExpires())));
} }
@ -381,7 +380,7 @@
} else if (this.ipBans.isBanned(socketaddress)) { } else if (this.ipBans.isBanned(socketaddress)) {
IpBanEntry ipbanentry = this.ipBans.get(socketaddress); IpBanEntry ipbanentry = this.ipBans.get(socketaddress);
IChatMutableComponent ichatmutablecomponent1 = IChatBaseComponent.translatable("multiplayer.disconnect.banned_ip.reason", ipbanentry.getReason()); IChatMutableComponent ichatmutablecomponent1 = IChatBaseComponent.translatable("multiplayer.disconnect.banned_ip.reason", ipbanentry.getReason());
@@ -387,13 +546,25 @@ @@ -387,13 +545,25 @@
ichatmutablecomponent1.append((IChatBaseComponent) IChatBaseComponent.translatable("multiplayer.disconnect.banned_ip.expiration", PlayerList.BAN_DATE_FORMAT.format(ipbanentry.getExpires()))); ichatmutablecomponent1.append((IChatBaseComponent) IChatBaseComponent.translatable("multiplayer.disconnect.banned_ip.expiration", PlayerList.BAN_DATE_FORMAT.format(ipbanentry.getExpires())));
} }
@ -410,7 +409,7 @@
UUID uuid = gameprofile.getId(); UUID uuid = gameprofile.getId();
Set<EntityPlayer> set = Sets.newIdentityHashSet(); Set<EntityPlayer> set = Sets.newIdentityHashSet();
@@ -414,32 +585,66 @@ @@ -414,32 +584,66 @@
} }
return !set.isEmpty(); return !set.isEmpty();
@ -479,7 +478,7 @@
} }
byte b0 = (byte) (flag ? 1 : 0); byte b0 = (byte) (flag ? 1 : 0);
@@ -447,17 +652,19 @@ @@ -447,17 +651,19 @@
WorldData worlddata = worldserver1.getLevelData(); WorldData worlddata = worldserver1.getLevelData();
entityplayer1.connection.send(new PacketPlayOutRespawn(entityplayer1.createCommonSpawnInfo(worldserver1), b0)); entityplayer1.connection.send(new PacketPlayOutRespawn(entityplayer1.createCommonSpawnInfo(worldserver1), b0));
@ -504,7 +503,7 @@
entityplayer1.setHealth(entityplayer1.getHealth()); entityplayer1.setHealth(entityplayer1.getHealth());
EntityPlayer.RespawnConfig entityplayer_respawnconfig = entityplayer1.getRespawnConfig(); EntityPlayer.RespawnConfig entityplayer_respawnconfig = entityplayer1.getRespawnConfig();
@@ -473,6 +680,27 @@ @@ -473,6 +679,27 @@
} }
} }
} }
@ -532,7 +531,7 @@
return entityplayer1; return entityplayer1;
} }
@@ -497,7 +725,18 @@ @@ -497,7 +724,18 @@
public void tick() { public void tick() {
if (++this.sendAllPlayerInfoIn > 600) { if (++this.sendAllPlayerInfoIn > 600) {
@ -552,7 +551,7 @@
this.sendAllPlayerInfoIn = 0; this.sendAllPlayerInfoIn = 0;
} }
@@ -510,6 +749,25 @@ @@ -510,6 +748,25 @@
} }
@ -578,7 +577,7 @@
public void broadcastAll(Packet<?> packet, ResourceKey<World> resourcekey) { public void broadcastAll(Packet<?> packet, ResourceKey<World> resourcekey) {
for (EntityPlayer entityplayer : this.players) { for (EntityPlayer entityplayer : this.players) {
if (entityplayer.level().dimension() == resourcekey) { if (entityplayer.level().dimension() == resourcekey) {
@@ -604,6 +862,7 @@ @@ -604,6 +861,7 @@
entityplayer.connection.send(new PacketPlayOutEntityStatus(entityplayer, b0)); entityplayer.connection.send(new PacketPlayOutEntityStatus(entityplayer, b0));
} }
@ -586,7 +585,7 @@
this.server.getCommands().sendCommands(entityplayer); this.server.getCommands().sendCommands(entityplayer);
} }
@@ -634,6 +893,12 @@ @@ -634,6 +892,12 @@
for (int i = 0; i < this.players.size(); ++i) { for (int i = 0; i < this.players.size(); ++i) {
EntityPlayer entityplayer = (EntityPlayer) this.players.get(i); EntityPlayer entityplayer = (EntityPlayer) this.players.get(i);
@ -599,7 +598,7 @@
if (entityplayer != entityhuman && entityplayer.level().dimension() == resourcekey) { if (entityplayer != entityhuman && entityplayer.level().dimension() == resourcekey) {
double d4 = d0 - entityplayer.getX(); double d4 = d0 - entityplayer.getX();
double d5 = d1 - entityplayer.getY(); double d5 = d1 - entityplayer.getY();
@@ -673,15 +938,19 @@ @@ -673,15 +937,19 @@
public void reloadWhiteList() {} public void reloadWhiteList() {}
public void sendLevelInfo(EntityPlayer entityplayer, WorldServer worldserver) { public void sendLevelInfo(EntityPlayer entityplayer, WorldServer worldserver) {
@ -623,7 +622,7 @@
} }
entityplayer.connection.send(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.LEVEL_CHUNKS_LOAD_START, 0.0F)); entityplayer.connection.send(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.LEVEL_CHUNKS_LOAD_START, 0.0F));
@@ -690,8 +959,16 @@ @@ -690,8 +958,16 @@
public void sendAllPlayerInfo(EntityPlayer entityplayer) { public void sendAllPlayerInfo(EntityPlayer entityplayer) {
entityplayer.inventoryMenu.sendAllDataToRemote(); entityplayer.inventoryMenu.sendAllDataToRemote();
@ -641,7 +640,7 @@
} }
public int getPlayerCount() { public int getPlayerCount() {
@@ -744,11 +1021,21 @@ @@ -744,11 +1020,21 @@
} }
public void removeAll() { public void removeAll() {
@ -665,7 +664,7 @@
public void broadcastSystemMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { public void broadcastSystemMessage(IChatBaseComponent ichatbasecomponent, boolean flag) {
this.broadcastSystemMessage(ichatbasecomponent, (entityplayer) -> { this.broadcastSystemMessage(ichatbasecomponent, (entityplayer) -> {
@@ -803,16 +1090,23 @@ @@ -803,16 +1089,23 @@
return playerchatmessage.hasSignature() && !playerchatmessage.hasExpiredServer(Instant.now()); return playerchatmessage.hasSignature() && !playerchatmessage.hasExpiredServer(Instant.now());
} }
@ -693,7 +692,7 @@
Path path = file2.toPath(); Path path = file2.toPath();
if (FileUtils.isPathNormalized(path) && FileUtils.isPathPortable(path) && path.startsWith(file.getPath()) && file2.isFile()) { if (FileUtils.isPathNormalized(path) && FileUtils.isPathPortable(path) && path.startsWith(file.getPath()) && file2.isFile()) {
@@ -821,7 +1115,7 @@ @@ -821,7 +1114,7 @@
} }
serverstatisticmanager = new ServerStatisticManager(this.server, file1); serverstatisticmanager = new ServerStatisticManager(this.server, file1);
@ -702,7 +701,7 @@
} }
return serverstatisticmanager; return serverstatisticmanager;
@@ -829,13 +1123,13 @@ @@ -829,13 +1122,13 @@
public AdvancementDataPlayer getPlayerAdvancements(EntityPlayer entityplayer) { public AdvancementDataPlayer getPlayerAdvancements(EntityPlayer entityplayer) {
UUID uuid = entityplayer.getUUID(); UUID uuid = entityplayer.getUUID();
@ -718,7 +717,7 @@
} }
advancementdataplayer.setPlayer(entityplayer); advancementdataplayer.setPlayer(entityplayer);
@@ -880,11 +1174,24 @@ @@ -880,11 +1173,24 @@
} }
public void reloadResources() { public void reloadResources() {

View file

@ -40,9 +40,9 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>jline</groupId> <groupId>org.jline</groupId>
<artifactId>jline</artifactId> <artifactId>jline</artifactId>
<version>2.12.1</version> <version>3.30.4</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>

View file

@ -43,7 +43,6 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import jline.console.ConsoleReader;
import net.minecraft.advancements.AdvancementHolder; import net.minecraft.advancements.AdvancementHolder;
import net.minecraft.commands.CommandDispatcher; import net.minecraft.commands.CommandDispatcher;
import net.minecraft.commands.CommandListenerWrapper; import net.minecraft.commands.CommandListenerWrapper;
@ -264,6 +263,7 @@ import org.bukkit.scoreboard.Criteria;
import org.bukkit.structure.StructureManager; import org.bukkit.structure.StructureManager;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import org.bukkit.util.permissions.DefaultPermissions; import org.bukkit.util.permissions.DefaultPermissions;
import org.jline.terminal.Terminal;
import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.constructor.SafeConstructor;
@ -1334,8 +1334,8 @@ public final class CraftServer implements Server {
return logger; return logger;
} }
public ConsoleReader getReader() { public Terminal getTerminal() {
return console.reader; return console.terminal;
} }
@Override @Override

View file

@ -13,7 +13,8 @@ import java.util.logging.Logger;
import joptsimple.OptionParser; import joptsimple.OptionParser;
import joptsimple.OptionSet; import joptsimple.OptionSet;
import joptsimple.util.PathConverter; import joptsimple.util.PathConverter;
import org.fusesource.jansi.AnsiConsole; import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
public class Main { public class Main {
public static boolean useJline = true; public static boolean useJline = true;
@ -169,22 +170,15 @@ public class Main {
} }
try { try {
// This trick bypasses Maven Shade's clever rewriting of our getProperty call when using String literals useJline = !Terminal.TYPE_DUMB.equals(System.getProperty(TerminalBuilder.PROP_PROVIDER));
String jline_UnsupportedTerminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd', 'T', 'e', 'r', 'm', 'i', 'n', 'a', 'l'});
String jline_terminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 't', 'e', 'r', 'm', 'i', 'n', 'a', 'l'});
useJline = !(jline_UnsupportedTerminal).equals(System.getProperty(jline_terminal));
if (options.has("nojline")) { if (options.has("nojline")) {
System.setProperty("user.language", "en");
useJline = false; useJline = false;
} }
if (useJline) { if (!useJline) {
AnsiConsole.systemInstall();
} else {
// This ensures the terminal literal will always match the jline implementation // This ensures the terminal literal will always match the jline implementation
System.setProperty(jline.TerminalFactory.JLINE_TERMINAL, jline.UnsupportedTerminal.class.getName()); System.setProperty(TerminalBuilder.PROP_PROVIDER, Terminal.TYPE_DUMB);
} }
if (options.has("noconsole")) { if (options.has("noconsole")) {

View file

@ -5,27 +5,29 @@ import java.util.EnumMap;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import jline.Terminal;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftServer;
import org.fusesource.jansi.Ansi; import org.jline.jansi.Ansi;
import org.fusesource.jansi.Ansi.Attribute; import org.jline.jansi.Ansi.Attribute;
import org.jline.terminal.Terminal;
public class ColouredConsoleSender extends CraftConsoleCommandSender { public class ColouredConsoleSender extends CraftConsoleCommandSender {
private final Terminal terminal; private final Terminal terminal;
private final Map<ChatColor, String> replacements = new EnumMap<ChatColor, String>(ChatColor.class); private final Map<ChatColor, String> replacements = new EnumMap<ChatColor, String>(ChatColor.class);
private final ChatColor[] colors = ChatColor.values(); private final ChatColor[] colors = ChatColor.values();
private final boolean jansiPassthrough; private final boolean jansiPassthrough;
private final boolean supportsAnsi;
private static final char ANSI_ESC_CHAR = '\u001B'; private static final char ANSI_ESC_CHAR = '\u001B';
private static final String RGB_STRING = String.valueOf(ANSI_ESC_CHAR) + "[38;2;%d;%d;%dm"; private static final String RGB_STRING = String.valueOf(ANSI_ESC_CHAR) + "[38;2;%d;%d;%dm";
private static final Pattern RBG_TRANSLATE = Pattern.compile(String.valueOf(ChatColor.COLOR_CHAR) + "x(" + String.valueOf(ChatColor.COLOR_CHAR) + "[A-F0-9]){6}", Pattern.CASE_INSENSITIVE); private static final Pattern RBG_TRANSLATE = Pattern.compile(String.valueOf(ChatColor.COLOR_CHAR) + "x(" + String.valueOf(ChatColor.COLOR_CHAR) + "[A-F0-9]){6}", Pattern.CASE_INSENSITIVE);
protected ColouredConsoleSender() { protected ColouredConsoleSender() {
super(); super();
this.terminal = ((CraftServer) getServer()).getReader().getTerminal(); this.terminal = ((CraftServer) getServer()).getTerminal();
this.jansiPassthrough = Boolean.getBoolean("jansi.passthrough"); this.jansiPassthrough = Boolean.getBoolean("jansi.passthrough");
this.supportsAnsi = !Terminal.TYPE_DUMB.equals(terminal.getType());
replacements.put(ChatColor.BLACK, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLACK).boldOff().toString()); replacements.put(ChatColor.BLACK, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLACK).boldOff().toString());
replacements.put(ChatColor.DARK_BLUE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLUE).boldOff().toString()); replacements.put(ChatColor.DARK_BLUE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLUE).boldOff().toString());
@ -54,7 +56,7 @@ public class ColouredConsoleSender extends CraftConsoleCommandSender {
@Override @Override
public void sendMessage(String message) { public void sendMessage(String message) {
// support jansi passthrough VM option when jansi doesn't detect an ANSI supported terminal // support jansi passthrough VM option when jansi doesn't detect an ANSI supported terminal
if (jansiPassthrough || terminal.isAnsiSupported()) { if (jansiPassthrough || supportsAnsi) {
if (!conversationTracker.isConversingModaly()) { if (!conversationTracker.isConversingModaly()) {
String result = convertRGBColors(message); String result = convertRGBColors(message);
for (ChatColor color : colors) { for (ChatColor color : colors) {

View file

@ -4,20 +4,25 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import jline.console.completer.Completer; import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.util.Waitable; import org.bukkit.craftbukkit.util.Waitable;
import org.bukkit.event.server.TabCompleteEvent; import org.bukkit.event.server.TabCompleteEvent;
import org.jline.reader.Candidate;
import org.jline.reader.Completer;
import org.jline.reader.LineReader;
import org.jline.reader.ParsedLine;
public class ConsoleCommandCompleter implements Completer { public class ConsoleCommandCompleter implements Completer {
private final CraftServer server;
public ConsoleCommandCompleter(CraftServer server) {
this.server = server;
}
@Override @Override
public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) { public void complete(LineReader reader, ParsedLine line, List<Candidate> candidates) {
CraftServer server = (CraftServer) Bukkit.getServer();
if (server == null) {
return;
}
String buffer = line.line();
Waitable<List<String>> waitable = new Waitable<List<String>>() { Waitable<List<String>> waitable = new Waitable<List<String>>() {
@Override @Override
protected List<String> evaluate() { protected List<String> evaluate() {
@ -29,25 +34,17 @@ public class ConsoleCommandCompleter implements Completer {
return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions(); return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions();
} }
}; };
this.server.getServer().processQueue.add(waitable); server.getServer().processQueue.add(waitable);
try { try {
List<String> offers = waitable.get(); List<String> offers = waitable.get();
if (offers == null) { if (offers == null) {
return cursor; return;
}
candidates.addAll(offers);
final int lastSpace = buffer.lastIndexOf(' ');
if (lastSpace == -1) {
return cursor - buffer.length();
} else {
return cursor - (buffer.length() - lastSpace - 1);
} }
offers.stream().map(Candidate::new).forEach((candidate) -> candidates.add(candidate));
} catch (ExecutionException e) { } catch (ExecutionException e) {
this.server.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e); server.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
return cursor;
} }
} }

View file

@ -15,7 +15,7 @@ public class ServerShutdownThread extends Thread {
server.close(); server.close();
} finally { } finally {
try { try {
server.reader.getTerminal().restore(); server.terminal.close();
} catch (Exception e) { } catch (Exception e) {
} }
} }

View file

@ -1,53 +0,0 @@
package org.bukkit.craftbukkit.util;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import jline.console.ConsoleReader;
import jline.console.completer.CompletionHandler;
/**
* SPIGOT-6705: Make sure we print the display line again on tab completion, so that the user does not get stuck on it
* e.g. The user needs to press y / n to continue
*/
public class TerminalCompletionHandler implements CompletionHandler {
private final TerminalConsoleWriterThread writerThread;
private final CompletionHandler delegate;
public TerminalCompletionHandler(TerminalConsoleWriterThread writerThread, CompletionHandler delegate) {
this.writerThread = writerThread;
this.delegate = delegate;
}
@Override
public boolean complete(ConsoleReader reader, List<CharSequence> candidates, int position) throws IOException {
// First check normal list, so that we do not unnecessarily create a new HashSet if the not distinct list is already lower
if (candidates.size() <= reader.getAutoprintThreshold()) {
return delegate.complete(reader, candidates, position);
}
Set<CharSequence> distinct = new HashSet<>(candidates);
if (distinct.size() <= reader.getAutoprintThreshold()) {
return delegate.complete(reader, candidates, position);
}
writerThread.setCompletion(distinct.size());
// FIXME: Potential concurrency issue, when terminal writer prints the display message before the delegate does it
// resulting in two display message being present, until a new message gets logged or the user presses y / n
// But the probability of this happening are probably lower than the effort needed to fix this
// And seeing the display message at all should be a higher priority than seeing it two times in rare cases.
boolean result = delegate.complete(reader, candidates, position);
writerThread.setCompletion(-1);
// draw line to prevent concurrency issue,
// where terminal write would print the display message between delegate#complete finished and the completion set back to -1
// Resulting in the display message being present even after pressing y / n
reader.drawLine();
reader.flush();
return result;
}
}

View file

@ -3,23 +3,16 @@ package org.bukkit.craftbukkit.util;
import com.mojang.logging.LogQueues; import com.mojang.logging.LogQueues;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import jline.console.ConsoleReader;
import jline.console.completer.CandidateListCompletionHandler;
import org.bukkit.craftbukkit.Main; import org.bukkit.craftbukkit.Main;
import org.fusesource.jansi.Ansi; import org.jline.reader.LineReader;
import org.fusesource.jansi.Ansi.Erase;
public class TerminalConsoleWriterThread extends Thread { public class TerminalConsoleWriterThread extends Thread {
private final ResourceBundle bundle = ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName(), Locale.getDefault()); private final LineReader reader;
private final ConsoleReader reader;
private final OutputStream output; private final OutputStream output;
private volatile int completion = -1;
public TerminalConsoleWriterThread(OutputStream output, ConsoleReader reader) { public TerminalConsoleWriterThread(OutputStream output, LineReader reader) {
super("TerminalConsoleWriter"); super("TerminalConsoleWriter");
this.output = output; this.output = output;
this.reader = reader; this.reader = reader;
@ -40,23 +33,7 @@ public class TerminalConsoleWriterThread extends Thread {
try { try {
if (Main.useJline) { if (Main.useJline) {
reader.print(Ansi.ansi().eraseLine(Erase.ALL).toString() + ConsoleReader.RESET_LINE); reader.printAbove(message);
reader.flush();
output.write(message.getBytes());
output.flush();
try {
reader.drawLine();
} catch (Throwable ex) {
reader.getCursorBuffer().clear();
}
if (completion > -1) {
// SPIGOT-6705: Make sure we print the display line again on tab completion, so that the user does not get stuck on it
reader.print(String.format(bundle.getString("DISPLAY_CANDIDATES"), completion));
}
reader.flush();
} else { } else {
output.write(message.getBytes()); output.write(message.getBytes());
output.flush(); output.flush();
@ -66,8 +43,4 @@ public class TerminalConsoleWriterThread extends Thread {
} }
} }
} }
void setCompletion(int completion) {
this.completion = completion;
}
} }