diff --git a/nms-patches/net/minecraft/server/MinecraftServer.patch b/nms-patches/net/minecraft/server/MinecraftServer.patch index f76961505..61785df56 100644 --- a/nms-patches/net/minecraft/server/MinecraftServer.patch +++ b/nms-patches/net/minecraft/server/MinecraftServer.patch @@ -1,6 +1,6 @@ --- a/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 org.slf4j.Logger; @@ -9,7 +9,6 @@ +import com.mojang.serialization.Lifecycle; +import java.io.File; +import java.util.Random; -+import jline.console.ConsoleReader; +import joptsimple.OptionSet; +import net.minecraft.nbt.NbtException; +import net.minecraft.nbt.ReportedNbtException; @@ -28,6 +27,8 @@ +import org.bukkit.craftbukkit.Main; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.server.ServerLoadEvent; ++import org.jline.terminal.Terminal; ++import org.jline.terminal.TerminalBuilder; +// CraftBukkit end + public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant implements ServerInfo, ChunkIOErrorReporter, ICommandListener { @@ -41,7 +42,7 @@ private static final int OVERLOADED_TICKS_THRESHOLD = 20; private static final long OVERLOADED_WARNING_INTERVAL_NANOS = 10L * TimeRange.NANOSECONDS_PER_SECOND; private static final int OVERLOADED_TICKS_WARNING_INTERVAL = 100; -@@ -274,6 +300,19 @@ +@@ -274,6 +301,19 @@ private final SuppressedExceptionCollector suppressedExceptions; private final DiscontinuousFrame tickFrame; @@ -50,7 +51,7 @@ + public org.bukkit.craftbukkit.CraftServer server; + public OptionSet options; + public org.bukkit.command.ConsoleCommandSender console; -+ public ConsoleReader reader; ++ public Terminal terminal; + public static int currentTick = (int) (System.currentTimeMillis() / 50); + public java.util.Queue processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); + public int autosavePeriod; @@ -61,7 +62,7 @@ public static S spin(Function function) { AtomicReference atomicreference = new AtomicReference(); Thread thread = new Thread(() -> { -@@ -294,7 +333,7 @@ +@@ -294,7 +334,7 @@ return s0; } @@ -70,7 +71,7 @@ super("Server"); this.metricsRecorder = InactiveMetricsRecorder.INSTANCE; this.onMetricsRecordingStopped = (methodprofilerresults) -> { -@@ -318,7 +357,7 @@ +@@ -318,7 +358,7 @@ this.suppressedExceptions = new SuppressedExceptionCollector(); this.registries = worldstem.registries(); this.worldData = worldstem.worldData(); @@ -79,7 +80,7 @@ throw new IllegalStateException("Missing Overworld dimension data"); } else { this.proxy = proxy; -@@ -346,6 +385,33 @@ +@@ -346,6 +386,30 @@ this.fuelValues = FuelValues.vanillaBurnTimes(this.registries.compositeAccess(), this.worldData.enabledFeatures()); this.tickFrame = TracyClient.createDiscontinuousFrame("Server Tick"); } @@ -88,22 +89,19 @@ + this.worldLoader = worldLoader; + this.vanillaCommandDispatcher = worldstem.dataPackResources().commands; // CraftBukkit + // Try to see if we're actually running in a terminal, disable jline if not -+ if (System.console() == null && System.getProperty("jline.terminal") == null) { -+ System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); ++ if (System.console() == null && System.getProperty(TerminalBuilder.PROP_PROVIDER) == null) { ++ System.setProperty(TerminalBuilder.PROP_PROVIDER, Terminal.TYPE_DUMB); + Main.useJline = false; + } + + try { -+ reader = new ConsoleReader(System.in, System.out); -+ reader.setExpandEvents(false); // Avoid parsing exceptions for uncommonly used event designators ++ terminal = TerminalBuilder.builder().dumb(false).build(); + } catch (Throwable e) { + try { + // Try again with jline disabled for Windows users without C++ 2008 Redistributable -+ System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); -+ System.setProperty("user.language", "en"); ++ System.setProperty(TerminalBuilder.PROP_PROVIDER, Terminal.TYPE_DUMB); + Main.useJline = false; -+ reader = new ConsoleReader(System.in, System.out); -+ reader.setExpandEvents(false); ++ terminal = TerminalBuilder.builder().build(); + } catch (IOException ex) { + LOGGER.warn((String) null, ex); + } @@ -113,7 +111,7 @@ } private void readScoreboard(WorldPersistentData worldpersistentdata) { -@@ -354,7 +420,7 @@ +@@ -354,7 +418,7 @@ protected abstract boolean initServer() throws IOException; @@ -122,7 +120,7 @@ if (!JvmProfiler.INSTANCE.isRunning()) { ; } -@@ -362,12 +428,8 @@ +@@ -362,12 +426,8 @@ boolean flag = false; ProfiledDuration profiledduration = JvmProfiler.INSTANCE.onWorldLoadedStarted(); @@ -136,7 +134,7 @@ if (profiledduration != null) { profiledduration.finish(true); } -@@ -384,23 +446,217 @@ +@@ -384,23 +444,217 @@ protected void forceDifficulty() {} @@ -368,7 +366,7 @@ if (!iworlddataserver.isInitialized()) { try { -@@ -424,28 +680,8 @@ +@@ -424,28 +678,8 @@ iworlddataserver.setInitialized(true); } @@ -398,7 +396,7 @@ private static void setInitialSpawn(WorldServer worldserver, IWorldDataServer iworlddataserver, boolean flag, boolean flag1) { if (flag1) { -@@ -453,6 +689,21 @@ +@@ -453,6 +687,21 @@ } else { ChunkProviderServer chunkproviderserver = worldserver.getChunkSource(); ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(chunkproviderserver.randomState().sampler().findSpawnPosition()); @@ -420,7 +418,7 @@ int i = chunkproviderserver.getGenerator().getSpawnHeight(worldserver); if (i < worldserver.getMinY()) { -@@ -511,8 +762,11 @@ +@@ -511,8 +760,11 @@ iworlddataserver.setGameType(EnumGamemode.SPECTATOR); } @@ -434,7 +432,7 @@ MinecraftServer.LOGGER.info("Preparing start region for dimension {}", worldserver.dimension().location()); BlockPosition blockposition = worldserver.getSharedSpawnPos(); -@@ -522,18 +776,21 @@ +@@ -522,18 +774,21 @@ this.nextTickTimeNanos = SystemUtils.getNanos(); worldserver.setDefaultSpawnPos(blockposition, worldserver.getSharedSpawnAngle()); @@ -462,7 +460,7 @@ TicketStorage ticketstorage = (TicketStorage) worldserver1.getDataStorage().get(TicketStorage.TYPE); if (ticketstorage != null) { -@@ -541,10 +798,17 @@ +@@ -541,10 +796,17 @@ } } @@ -483,7 +481,7 @@ } public EnumGamemode getDefaultGameType() { -@@ -573,12 +837,16 @@ +@@ -573,12 +835,16 @@ flag3 = true; } @@ -500,7 +498,7 @@ if (flag1) { for (WorldServer worldserver2 : this.getAllLevels()) { MinecraftServer.LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", worldserver2.getChunkSource().chunkMap.getStorageName()); -@@ -609,18 +877,40 @@ +@@ -609,18 +875,40 @@ this.stopServer(); } @@ -541,7 +539,7 @@ } MinecraftServer.LOGGER.info("Saving worlds"); -@@ -700,7 +990,7 @@ +@@ -700,7 +988,7 @@ } this.nextTickTimeNanos = SystemUtils.getNanos(); @@ -550,7 +548,7 @@ this.status = this.buildServerStatus(); 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) { 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); this.nextTickTimeNanos += k * i; this.lastOverloadWarningNanos = this.nextTickTimeNanos; -@@ -730,6 +1021,7 @@ +@@ -730,6 +1019,7 @@ this.debugCommandProfiler = new MinecraftServer.TimeProfiler(SystemUtils.getNanos(), this.tickCount); } @@ -566,20 +564,20 @@ this.nextTickTimeNanos += i; try { -@@ -803,6 +1095,12 @@ +@@ -803,6 +1093,12 @@ this.services.profileCache().clearExecutor(); } + // CraftBukkit start - Restore terminal to original settings + try { -+ reader.getTerminal().restore(); ++ terminal.close(); + } catch (Exception ignored) { + } + // CraftBukkit end this.onServerExit(); } -@@ -862,7 +1160,14 @@ +@@ -862,7 +1158,14 @@ } private boolean haveTime() { @@ -595,7 +593,7 @@ } public static boolean throwIfFatalException() { -@@ -876,7 +1181,7 @@ +@@ -876,7 +1179,7 @@ } public static void setFatalException(RuntimeException runtimeexception) { @@ -604,7 +602,7 @@ } @Override -@@ -946,7 +1251,7 @@ +@@ -946,7 +1249,7 @@ } } @@ -613,7 +611,7 @@ Profiler.get().incrementCounter("runTask"); super.doRunTask(ticktask); } -@@ -1010,6 +1315,7 @@ +@@ -1010,6 +1313,7 @@ this.autoSave(); } @@ -621,7 +619,7 @@ this.tickConnection(); return; } -@@ -1024,7 +1330,7 @@ +@@ -1024,7 +1328,7 @@ } --this.ticksUntilAutosave; @@ -630,7 +628,7 @@ this.autoSave(); } -@@ -1043,7 +1349,7 @@ +@@ -1043,7 +1347,7 @@ } private void autoSave() { @@ -639,7 +637,7 @@ MinecraftServer.LOGGER.debug("Autosave started"); GameProfilerFiller gameprofilerfiller = Profiler.get(); -@@ -1123,21 +1429,38 @@ +@@ -1123,21 +1427,38 @@ this.getPlayerList().getPlayers().forEach((entityplayer) -> { entityplayer.connection.suspendFlushing(); }); @@ -678,7 +676,7 @@ gameprofilerfiller.push("tick"); -@@ -1227,6 +1550,22 @@ +@@ -1227,6 +1548,22 @@ return (WorldServer) this.levels.get(resourcekey); } @@ -701,7 +699,7 @@ public Set> levelKeys() { return this.levels.keySet(); } -@@ -1256,7 +1595,7 @@ +@@ -1256,7 +1593,7 @@ @DontObfuscate public String getServerModName() { @@ -710,7 +708,7 @@ } public SystemReport fillSystemReport(SystemReport systemreport) { -@@ -1590,11 +1929,11 @@ +@@ -1590,11 +1927,11 @@ public CompletableFuture reloadResources(Collection collection) { CompletableFuture completablefuture = CompletableFuture.supplyAsync(() -> { @@ -724,7 +722,7 @@ }, this).thenCompose((immutablelist) -> { IReloadableResourceManager ireloadableresourcemanager = new ResourceManager(EnumResourcePackType.SERVER_DATA, immutablelist); List> list = TagDataPack.loadTagsForExistingRegistries(ireloadableresourcemanager, this.registries.compositeAccess()); -@@ -1610,6 +1949,7 @@ +@@ -1610,6 +1947,7 @@ }).thenAcceptAsync((minecraftserver_reloadableresources) -> { this.resources.close(); this.resources = minecraftserver_reloadableresources; @@ -732,7 +730,7 @@ this.packRepository.setSelected(collection); 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() { if (this.willStartRecordingMetrics) { 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() { return ChatDecorator.PLAIN; } -@@ -2076,8 +2437,11 @@ +@@ -2076,8 +2435,11 @@ public void subscribeToDebugSample(EntityPlayer entityplayer, RemoteDebugSampleType remotedebugsampletype) {} diff --git a/nms-patches/net/minecraft/server/dedicated/DedicatedServer.patch b/nms-patches/net/minecraft/server/dedicated/DedicatedServer.patch index 04a3edb88..c76bfebbf 100644 --- a/nms-patches/net/minecraft/server/dedicated/DedicatedServer.patch +++ b/nms-patches/net/minecraft/server/dedicated/DedicatedServer.patch @@ -1,6 +1,6 @@ --- a/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 org.slf4j.Logger; @@ -10,16 +10,19 @@ +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.io.IoBuilder; +import org.bukkit.command.CommandSender; -+import org.bukkit.craftbukkit.util.TerminalCompletionHandler; +import org.bukkit.craftbukkit.util.TerminalConsoleWriterThread; +import org.bukkit.event.server.ServerCommandEvent; +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 + public class DedicatedServer extends MinecraftServer implements IMinecraftServer { static final Logger LOGGER = LogUtils.getLogger(); -@@ -67,7 +79,7 @@ +@@ -67,7 +82,7 @@ private final List consoleInput = Collections.synchronizedList(Lists.newArrayList()); @Nullable private RemoteStatusListener queryThreadGs4; @@ -28,7 +31,7 @@ @Nullable private RemoteControlListener rconThread; public DedicatedServerSettings settings; -@@ -81,10 +93,12 @@ +@@ -81,33 +96,93 @@ private DebugSampleSubscriptionTracker debugSampleSubscriptionTracker; public ServerLinks serverLinks; @@ -44,8 +47,15 @@ this.serverTextFilter = ServerTextFilter.createFromConfig(dedicatedserversettings.getProperties()); this.serverLinks = createServerLinks(dedicatedserversettings); } -@@ -93,13 +107,44 @@ + + @Override 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") { public void run() { - BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); @@ -53,7 +63,6 @@ + if (!org.bukkit.craftbukkit.Main.useConsole) { + return; + } -+ jline.console.ConsoleReader bufferedreader = reader; + + // MC-33041, SPIGOT-5538: if System.in is not valid due to javaw, then return + try { @@ -70,29 +79,31 @@ - DedicatedServer.this.handleConsoleInput(s, DedicatedServer.this.createCommandSourceStack()); + // CraftBukkit start - JLine disabling compatibility + while (!DedicatedServer.this.isStopped() && DedicatedServer.this.isRunning()) { -+ if (org.bukkit.craftbukkit.Main.useJline) { -+ s = bufferedreader.readLine(">", null); -+ } else { -+ s = bufferedreader.readLine(); -+ } ++ try { ++ if (org.bukkit.craftbukkit.Main.useJline) { ++ s = reader.readLine(">", null); ++ } else { ++ s = reader.readLine(); ++ } + -+ // SPIGOT-5220: Throttle if EOF (ctrl^d) or stdin is /dev/null -+ if (s == null) { ++ if (s.trim().length() > 0) { // Trim to filter lines which are just spaces ++ DedicatedServer.this.handleConsoleInput(s, DedicatedServer.this.createCommandSourceStack()); ++ } ++ } catch (EndOfFileException | UserInterruptException eof) { ++ // SPIGOT-5220: Throttle if EOF (ctrl^d) or stdin is /dev/null + try { + Thread.sleep(50L); + } catch (InterruptedException ex) { + 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); -@@ -108,6 +153,29 @@ + } + } }; @@ -111,8 +122,7 @@ + } + } + -+ TerminalConsoleWriterThread writerThread = new TerminalConsoleWriterThread(System.out, this.reader); -+ this.reader.setCompletionHandler(new TerminalCompletionHandler(writerThread, this.reader.getCompletionHandler())); ++ TerminalConsoleWriterThread writerThread = new TerminalConsoleWriterThread(System.out, reader); + writerThread.start(); + + System.setOut(IoBuilder.forLogger(logger).setLevel(Level.INFO).buildPrintStream()); @@ -122,7 +132,7 @@ thread.setDaemon(true); thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER)); thread.start(); -@@ -132,7 +200,7 @@ +@@ -132,7 +207,7 @@ this.setMotd(dedicatedserverproperties.motd); super.setPlayerIdleTimeout((Integer) dedicatedserverproperties.playerIdleTimeout.get()); this.setEnforceWhitelist(dedicatedserverproperties.enforceWhitelist); @@ -131,7 +141,7 @@ DedicatedServer.LOGGER.info("Default game type: {}", dedicatedserverproperties.gamemode); InetAddress inetaddress = null; -@@ -156,6 +224,12 @@ +@@ -156,6 +231,12 @@ return false; } @@ -144,7 +154,7 @@ if (!this.usesAuthentication()) { DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); 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)) { return false; } else { @@ -153,7 +163,7 @@ this.debugSampleSubscriptionTracker = new DebugSampleSubscriptionTracker(this.getPlayerList()); this.tickTimeLogger = new RemoteSampleLogger(TpsDebugDimensions.values().length, this.debugSampleSubscriptionTracker, RemoteDebugSampleType.TICK_TIME); long i = SystemUtils.getNanos(); -@@ -178,13 +252,13 @@ +@@ -178,13 +259,13 @@ TileEntitySkull.setup(this.services, this); UserCache.setUsesAuthentication(this.usesAuthentication()); DedicatedServer.LOGGER.info("Preparing level \"{}\"", this.getLevelIdName()); @@ -169,7 +179,7 @@ } if (dedicatedserverproperties.enableQuery) { -@@ -278,6 +352,7 @@ +@@ -278,6 +359,7 @@ this.queryThreadGs4.stop(); } @@ -177,7 +187,7 @@ } @Override -@@ -299,7 +374,15 @@ +@@ -299,7 +381,15 @@ while (!this.consoleInput.isEmpty()) { ServerCommand servercommand = (ServerCommand) this.consoleInput.remove(0); @@ -194,7 +204,7 @@ } } -@@ -524,16 +607,52 @@ +@@ -524,16 +614,52 @@ @Override public String getPluginNames() { @@ -251,7 +261,7 @@ } public void storeUsingWhiteList(boolean flag) { -@@ -643,4 +762,15 @@ +@@ -643,4 +769,15 @@ } } } diff --git a/nms-patches/net/minecraft/server/players/PlayerList.patch b/nms-patches/net/minecraft/server/players/PlayerList.patch index adb006023..aff7ac9e5 100644 --- a/nms-patches/net/minecraft/server/players/PlayerList.patch +++ b/nms-patches/net/minecraft/server/players/PlayerList.patch @@ -49,7 +49,7 @@ public final WorldNBTStorage playerIo; private boolean doWhiteList; private final LayeredRegistryAccess registries; -@@ -131,13 +155,23 @@ +@@ -131,13 +155,22 @@ private static final boolean ALLOW_LOGOUTIVATOR = false; private int sendAllPlayerInfoIn; @@ -59,7 +59,6 @@ public PlayerList(MinecraftServer minecraftserver, LayeredRegistryAccess layeredregistryaccess, WorldNBTStorage worldnbtstorage, int i) { + this.cserver = minecraftserver.server = new CraftServer((DedicatedServer) minecraftserver, this); + minecraftserver.console = org.bukkit.craftbukkit.command.ColouredConsoleSender.getInstance(); -+ minecraftserver.reader.addCompleter(new org.bukkit.craftbukkit.command.ConsoleCommandCompleter(minecraftserver.server)); + // CraftBukkit end + this.bans = new GameProfileBanList(PlayerList.USERBANLIST_FILE); @@ -75,7 +74,7 @@ this.server = minecraftserver; this.registries = layeredregistryaccess; this.maxPlayers = i; -@@ -160,9 +194,16 @@ +@@ -160,9 +193,16 @@ try (ProblemReporter.j problemreporter_j = new ProblemReporter.j(entityplayer.problemPath(), PlayerList.LOGGER)) { Optional optional1 = this.load(entityplayer, problemreporter_j); @@ -93,7 +92,7 @@ WorldServer worldserver = this.server.getLevel(resourcekey); WorldServer worldserver1; -@@ -181,10 +222,11 @@ +@@ -181,10 +221,11 @@ worldserver1.waitForChunkAndEntities(entityplayer.chunkPosition(), 1); String s1 = networkmanager.getLoggableAddress(this.server.logIPs()); @@ -107,7 +106,7 @@ PlayerConnection playerconnection = new PlayerConnection(this.server, networkmanager, entityplayer, commonlistenercookie); 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); 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 PacketPlayOutAbilities(entityplayer.getAbilities())); playerconnection.send(new PacketPlayOutHeldItemSlot(entityplayer.getInventory().getSelectedSlot())); -@@ -212,8 +255,10 @@ +@@ -212,8 +254,10 @@ } else { 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()); ServerPing serverping = this.server.getStatus(); -@@ -221,19 +266,72 @@ +@@ -221,19 +265,72 @@ entityplayer.sendServerStatus(serverping); } @@ -204,7 +203,7 @@ } } -@@ -260,30 +358,31 @@ +@@ -260,30 +357,31 @@ } public void addWorldborderListener(WorldServer worldserver) { @@ -241,7 +240,7 @@ } @Override -@@ -312,14 +411,15 @@ +@@ -312,14 +410,15 @@ } protected void save(EntityPlayer entityplayer) { @@ -259,7 +258,7 @@ if (advancementdataplayer != null) { advancementdataplayer.save(); -@@ -327,10 +427,24 @@ +@@ -327,10 +426,24 @@ } @@ -285,7 +284,7 @@ this.save(entityplayer); if (entityplayer.isPassenger()) { Entity entity = entityplayer.getRootVehicle(); -@@ -339,7 +453,7 @@ +@@ -339,7 +452,7 @@ PlayerList.LOGGER.debug("Removing player mount"); entityplayer.stopRiding(); entity.getPassengersAndSelf().forEach((entity1) -> { @@ -294,7 +293,7 @@ }); } } -@@ -347,7 +461,7 @@ +@@ -347,7 +460,7 @@ entityplayer.unRide(); for (EntityEnderPearl entityenderpearl : entityplayer.getEnderPearls()) { @@ -303,7 +302,7 @@ } worldserver.removePlayerImmediately(entityplayer, Entity.RemovalReason.UNLOADED_WITH_PLAYER); -@@ -359,15 +473,58 @@ +@@ -359,15 +472,58 @@ if (entityplayer1 == entityplayer) { this.playersByUUID.remove(uuid); @@ -367,7 +366,7 @@ if (this.bans.isBanned(gameprofile)) { GameProfileBanEntry gameprofilebanentry = (GameProfileBanEntry) this.bans.get(gameprofile); 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()))); } @@ -381,7 +380,7 @@ } else if (this.ipBans.isBanned(socketaddress)) { IpBanEntry ipbanentry = this.ipBans.get(socketaddress); 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()))); } @@ -410,7 +409,7 @@ UUID uuid = gameprofile.getId(); Set set = Sets.newIdentityHashSet(); -@@ -414,32 +585,66 @@ +@@ -414,32 +584,66 @@ } return !set.isEmpty(); @@ -479,7 +478,7 @@ } byte b0 = (byte) (flag ? 1 : 0); -@@ -447,17 +652,19 @@ +@@ -447,17 +651,19 @@ WorldData worlddata = worldserver1.getLevelData(); entityplayer1.connection.send(new PacketPlayOutRespawn(entityplayer1.createCommonSpawnInfo(worldserver1), b0)); @@ -504,7 +503,7 @@ entityplayer1.setHealth(entityplayer1.getHealth()); EntityPlayer.RespawnConfig entityplayer_respawnconfig = entityplayer1.getRespawnConfig(); -@@ -473,6 +680,27 @@ +@@ -473,6 +679,27 @@ } } } @@ -532,7 +531,7 @@ return entityplayer1; } -@@ -497,7 +725,18 @@ +@@ -497,7 +724,18 @@ public void tick() { if (++this.sendAllPlayerInfoIn > 600) { @@ -552,7 +551,7 @@ this.sendAllPlayerInfoIn = 0; } -@@ -510,6 +749,25 @@ +@@ -510,6 +748,25 @@ } @@ -578,7 +577,7 @@ public void broadcastAll(Packet packet, ResourceKey resourcekey) { for (EntityPlayer entityplayer : this.players) { if (entityplayer.level().dimension() == resourcekey) { -@@ -604,6 +862,7 @@ +@@ -604,6 +861,7 @@ entityplayer.connection.send(new PacketPlayOutEntityStatus(entityplayer, b0)); } @@ -586,7 +585,7 @@ this.server.getCommands().sendCommands(entityplayer); } -@@ -634,6 +893,12 @@ +@@ -634,6 +892,12 @@ for (int i = 0; i < this.players.size(); ++i) { EntityPlayer entityplayer = (EntityPlayer) this.players.get(i); @@ -599,7 +598,7 @@ if (entityplayer != entityhuman && entityplayer.level().dimension() == resourcekey) { double d4 = d0 - entityplayer.getX(); double d5 = d1 - entityplayer.getY(); -@@ -673,15 +938,19 @@ +@@ -673,15 +937,19 @@ public void reloadWhiteList() {} public void sendLevelInfo(EntityPlayer entityplayer, WorldServer worldserver) { @@ -623,7 +622,7 @@ } 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) { entityplayer.inventoryMenu.sendAllDataToRemote(); @@ -641,7 +640,7 @@ } public int getPlayerCount() { -@@ -744,11 +1021,21 @@ +@@ -744,11 +1020,21 @@ } public void removeAll() { @@ -665,7 +664,7 @@ public void broadcastSystemMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { this.broadcastSystemMessage(ichatbasecomponent, (entityplayer) -> { -@@ -803,16 +1090,23 @@ +@@ -803,16 +1089,23 @@ return playerchatmessage.hasSignature() && !playerchatmessage.hasExpiredServer(Instant.now()); } @@ -693,7 +692,7 @@ Path path = file2.toPath(); 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); @@ -702,7 +701,7 @@ } return serverstatisticmanager; -@@ -829,13 +1123,13 @@ +@@ -829,13 +1122,13 @@ public AdvancementDataPlayer getPlayerAdvancements(EntityPlayer entityplayer) { UUID uuid = entityplayer.getUUID(); @@ -718,7 +717,7 @@ } advancementdataplayer.setPlayer(entityplayer); -@@ -880,11 +1174,24 @@ +@@ -880,11 +1173,24 @@ } public void reloadResources() { diff --git a/pom.xml b/pom.xml index 17254d388..438bffab3 100644 --- a/pom.xml +++ b/pom.xml @@ -40,9 +40,9 @@ compile - jline + org.jline jline - 2.12.1 + 3.30.4 compile diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 70c05fdd4..6c6a97d4f 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -43,7 +43,6 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import javax.imageio.ImageIO; -import jline.console.ConsoleReader; import net.minecraft.advancements.AdvancementHolder; import net.minecraft.commands.CommandDispatcher; import net.minecraft.commands.CommandListenerWrapper; @@ -264,6 +263,7 @@ import org.bukkit.scoreboard.Criteria; import org.bukkit.structure.StructureManager; import org.bukkit.util.StringUtil; import org.bukkit.util.permissions.DefaultPermissions; +import org.jline.terminal.Terminal; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -1334,8 +1334,8 @@ public final class CraftServer implements Server { return logger; } - public ConsoleReader getReader() { - return console.reader; + public Terminal getTerminal() { + return console.terminal; } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java index 298f8b37b..680b17518 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -13,7 +13,8 @@ import java.util.logging.Logger; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.util.PathConverter; -import org.fusesource.jansi.AnsiConsole; +import org.jline.terminal.Terminal; +import org.jline.terminal.TerminalBuilder; public class Main { public static boolean useJline = true; @@ -169,22 +170,15 @@ public class Main { } try { - // This trick bypasses Maven Shade's clever rewriting of our getProperty call when using String literals - 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)); + useJline = !Terminal.TYPE_DUMB.equals(System.getProperty(TerminalBuilder.PROP_PROVIDER)); if (options.has("nojline")) { - System.setProperty("user.language", "en"); useJline = false; } - if (useJline) { - AnsiConsole.systemInstall(); - } else { + if (!useJline) { // 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")) { diff --git a/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java b/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java index 4580642e0..9740ebda1 100644 --- a/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java +++ b/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java @@ -5,27 +5,29 @@ import java.util.EnumMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import jline.Terminal; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.craftbukkit.CraftServer; -import org.fusesource.jansi.Ansi; -import org.fusesource.jansi.Ansi.Attribute; +import org.jline.jansi.Ansi; +import org.jline.jansi.Ansi.Attribute; +import org.jline.terminal.Terminal; public class ColouredConsoleSender extends CraftConsoleCommandSender { private final Terminal terminal; private final Map replacements = new EnumMap(ChatColor.class); private final ChatColor[] colors = ChatColor.values(); private final boolean jansiPassthrough; + private final boolean supportsAnsi; 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 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() { super(); - this.terminal = ((CraftServer) getServer()).getReader().getTerminal(); + this.terminal = ((CraftServer) getServer()).getTerminal(); 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.DARK_BLUE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLUE).boldOff().toString()); @@ -54,7 +56,7 @@ public class ColouredConsoleSender extends CraftConsoleCommandSender { @Override public void sendMessage(String message) { // support jansi passthrough VM option when jansi doesn't detect an ANSI supported terminal - if (jansiPassthrough || terminal.isAnsiSupported()) { + if (jansiPassthrough || supportsAnsi) { if (!conversationTracker.isConversingModaly()) { String result = convertRGBColors(message); for (ChatColor color : colors) { diff --git a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java index befcc19f9..212a01646 100644 --- a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java +++ b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java @@ -4,20 +4,25 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.logging.Level; -import jline.console.completer.Completer; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.util.Waitable; 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 { - private final CraftServer server; - - public ConsoleCommandCompleter(CraftServer server) { - this.server = server; - } @Override - public int complete(final String buffer, final int cursor, final List candidates) { + public void complete(LineReader reader, ParsedLine line, List candidates) { + CraftServer server = (CraftServer) Bukkit.getServer(); + if (server == null) { + return; + } + + String buffer = line.line(); Waitable> waitable = new Waitable>() { @Override protected List evaluate() { @@ -29,25 +34,17 @@ public class ConsoleCommandCompleter implements Completer { return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions(); } }; - this.server.getServer().processQueue.add(waitable); + server.getServer().processQueue.add(waitable); try { List offers = waitable.get(); if (offers == null) { - return cursor; - } - candidates.addAll(offers); - - final int lastSpace = buffer.lastIndexOf(' '); - if (lastSpace == -1) { - return cursor - buffer.length(); - } else { - return cursor - (buffer.length() - lastSpace - 1); + return; } + offers.stream().map(Candidate::new).forEach((candidate) -> candidates.add(candidate)); } 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) { Thread.currentThread().interrupt(); } - return cursor; } } diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java index 2e057fd4c..e96a80117 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java +++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java @@ -15,7 +15,7 @@ public class ServerShutdownThread extends Thread { server.close(); } finally { try { - server.reader.getTerminal().restore(); + server.terminal.close(); } catch (Exception e) { } } diff --git a/src/main/java/org/bukkit/craftbukkit/util/TerminalCompletionHandler.java b/src/main/java/org/bukkit/craftbukkit/util/TerminalCompletionHandler.java deleted file mode 100644 index beaa54b56..000000000 --- a/src/main/java/org/bukkit/craftbukkit/util/TerminalCompletionHandler.java +++ /dev/null @@ -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 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 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; - } -} diff --git a/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java b/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java index 3a4eb5948..e44f048ff 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java +++ b/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java @@ -3,23 +3,16 @@ package org.bukkit.craftbukkit.util; import com.mojang.logging.LogQueues; import java.io.IOException; import java.io.OutputStream; -import java.util.Locale; -import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; -import jline.console.ConsoleReader; -import jline.console.completer.CandidateListCompletionHandler; import org.bukkit.craftbukkit.Main; -import org.fusesource.jansi.Ansi; -import org.fusesource.jansi.Ansi.Erase; +import org.jline.reader.LineReader; public class TerminalConsoleWriterThread extends Thread { - private final ResourceBundle bundle = ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName(), Locale.getDefault()); - private final ConsoleReader reader; + private final LineReader reader; private final OutputStream output; - private volatile int completion = -1; - public TerminalConsoleWriterThread(OutputStream output, ConsoleReader reader) { + public TerminalConsoleWriterThread(OutputStream output, LineReader reader) { super("TerminalConsoleWriter"); this.output = output; this.reader = reader; @@ -40,23 +33,7 @@ public class TerminalConsoleWriterThread extends Thread { try { if (Main.useJline) { - reader.print(Ansi.ansi().eraseLine(Erase.ALL).toString() + ConsoleReader.RESET_LINE); - 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(); + reader.printAbove(message); } else { output.write(message.getBytes()); output.flush(); @@ -66,8 +43,4 @@ public class TerminalConsoleWriterThread extends Thread { } } } - - void setCompletion(int completion) { - this.completion = completion; - } }