mirror of
https://github.com/barkeser2002/CatServer.git
synced 2026-09-25 08:39:58 +03:00
Fix #355 使用SlackActivityAccountant限制区块加载和卸载 支持ChunkUnloadEvent取消区块保存 [PATCH] Add More Information to session.lock Errors 优化ItemStack.isEmpty(Paper 0181) 优化Tick循环(Paper 0022) 修复获取MOD容器名称导致NoClassDefFoundError 修复SPacketChunkData缓存未及时刷新导致客户端显示错误
1264 lines
51 KiB
Diff
1264 lines
51 KiB
Diff
--- ../src-base/minecraft/net/minecraft/server/MinecraftServer.java
|
|
+++ ../src-work/minecraft/net/minecraft/server/MinecraftServer.java
|
|
@@ -2,6 +2,7 @@
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Queues;
|
|
+import com.google.common.collect.Sets;
|
|
import com.google.common.util.concurrent.Futures;
|
|
import com.google.common.util.concurrent.ListenableFuture;
|
|
import com.google.common.util.concurrent.ListenableFutureTask;
|
|
@@ -9,6 +10,7 @@
|
|
import com.mojang.authlib.GameProfileRepository;
|
|
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
|
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
|
+
|
|
import io.netty.buffer.ByteBuf;
|
|
import io.netty.buffer.ByteBufOutputStream;
|
|
import io.netty.buffer.Unpooled;
|
|
@@ -23,19 +25,23 @@
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.security.KeyPair;
|
|
import java.text.SimpleDateFormat;
|
|
+import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
+import java.util.Comparator;
|
|
import java.util.Date;
|
|
import java.util.List;
|
|
import java.util.Queue;
|
|
import java.util.Random;
|
|
+import java.util.Set;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.Callable;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.FutureTask;
|
|
-import java.util.function.Supplier;
|
|
import javax.annotation.Nullable;
|
|
import javax.imageio.ImageIO;
|
|
+
|
|
+import joptsimple.OptionSet;
|
|
import net.minecraft.advancements.AdvancementManager;
|
|
import net.minecraft.advancements.FunctionManager;
|
|
import net.minecraft.command.CommandBase;
|
|
@@ -55,6 +61,7 @@
|
|
import net.minecraft.profiler.Profiler;
|
|
import net.minecraft.profiler.Snooper;
|
|
import net.minecraft.server.dedicated.DedicatedServer;
|
|
+import net.minecraft.server.dedicated.PropertyManager;
|
|
import net.minecraft.server.management.PlayerList;
|
|
import net.minecraft.server.management.PlayerProfileCache;
|
|
import net.minecraft.util.IProgressUpdate;
|
|
@@ -73,39 +80,51 @@
|
|
import net.minecraft.world.MinecraftException;
|
|
import net.minecraft.world.ServerWorldEventHandler;
|
|
import net.minecraft.world.World;
|
|
+import net.minecraft.world.WorldProvider;
|
|
import net.minecraft.world.WorldServer;
|
|
import net.minecraft.world.WorldServerDemo;
|
|
import net.minecraft.world.WorldServerMulti;
|
|
import net.minecraft.world.WorldSettings;
|
|
import net.minecraft.world.WorldType;
|
|
-import net.minecraft.world.chunk.storage.AnvilSaveConverter;
|
|
+import net.minecraft.world.chunk.storage.AnvilSaveHandler;
|
|
import net.minecraft.world.storage.ISaveFormat;
|
|
import net.minecraft.world.storage.ISaveHandler;
|
|
import net.minecraft.world.storage.WorldInfo;
|
|
+import net.minecraftforge.common.DimensionManager;
|
|
import net.minecraftforge.fml.relauncher.Side;
|
|
import net.minecraftforge.fml.relauncher.SideOnly;
|
|
+
|
|
import org.apache.commons.lang3.Validate;
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.craftbukkit.CraftServer;
|
|
+import org.bukkit.craftbukkit.Main;
|
|
+import org.bukkit.craftbukkit.SpigotTimings;
|
|
+import org.spigotmc.SlackActivityAccountant;
|
|
|
|
+import catserver.server.BukkitInjector;
|
|
+import catserver.server.CatServer;
|
|
+import catserver.server.mcauth.CatProxyAuthenticationService;
|
|
+
|
|
public abstract class MinecraftServer implements ICommandSender, Runnable, IThreadListener, ISnooperInfo
|
|
{
|
|
- private static final Logger LOGGER = LogManager.getLogger();
|
|
+ public static final Logger LOGGER = LogManager.getLogger();
|
|
public static final File USER_CACHE_FILE = new File("usercache.json");
|
|
- private final ISaveFormat anvilConverterForAnvilFile;
|
|
+ public ISaveFormat anvilConverterForAnvilFile;
|
|
private final Snooper usageSnooper = new Snooper("server", this, getCurrentTimeMillis());
|
|
- private final File anvilFile;
|
|
+ public File anvilFile;
|
|
private final List<ITickable> tickables = Lists.<ITickable>newArrayList();
|
|
public final ICommandManager commandManager;
|
|
public final Profiler profiler = new Profiler();
|
|
private final NetworkSystem networkSystem;
|
|
private final ServerStatusResponse statusResponse = new ServerStatusResponse();
|
|
private final Random random = new Random();
|
|
- private final DataFixer dataFixer;
|
|
+ public final DataFixer dataFixer;
|
|
@SideOnly(Side.SERVER)
|
|
private String hostname;
|
|
private int serverPort = -1;
|
|
- public WorldServer[] worlds;
|
|
+ public WorldServer[] worlds = new WorldServer[0];
|
|
private PlayerList playerList;
|
|
private boolean serverRunning = true;
|
|
private boolean serverStopped;
|
|
@@ -123,11 +142,11 @@
|
|
private int buildLimit;
|
|
private int maxPlayerIdleMinutes;
|
|
public final long[] tickTimeArray = new long[100];
|
|
- public long[][] timeOfLastDimensionTick;
|
|
+ //public long[][] timeOfLastDimensionTick;
|
|
+ public java.util.Hashtable<Integer, long[]> worldTickTimes = new java.util.Hashtable<Integer, long[]>();
|
|
private KeyPair serverKeyPair;
|
|
private String serverOwner;
|
|
private String folderName;
|
|
- @SideOnly(Side.CLIENT)
|
|
private String worldName;
|
|
private boolean isDemo;
|
|
private boolean enableBonusChest;
|
|
@@ -143,26 +162,71 @@
|
|
private final GameProfileRepository profileRepo;
|
|
private final PlayerProfileCache profileCache;
|
|
private long nanoTimeSinceStatusRefresh;
|
|
- public final Queue < FutureTask<? >> futureTaskQueue = Queues. < FutureTask<? >> newArrayDeque();
|
|
+ public final Queue < FutureTask<? >> futureTaskQueue = new catserver.server.utils.CachedSizeConcurrentLinkedQueue<>(); // Paper - Make size() constant-time
|
|
private Thread serverThread;
|
|
- protected long currentTime = getCurrentTimeMillis();
|
|
+ private long currentTime = getCurrentTimeMillis();
|
|
@SideOnly(Side.CLIENT)
|
|
private boolean worldIconSet;
|
|
+ // CraftBukkit start
|
|
+ public List<WorldServer> worldServerList = new ArrayList<>();
|
|
+ public org.bukkit.craftbukkit.CraftServer server;
|
|
+ public OptionSet options;
|
|
+ public org.bukkit.command.ConsoleCommandSender console;
|
|
+ public org.bukkit.command.RemoteConsoleCommandSender remoteConsole;
|
|
+ // public ConsoleReader reader; // Paper
|
|
+ public static int currentTick = 0; // Paper - Further improve tick loop
|
|
+ public Thread primaryThread;
|
|
+ public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
|
|
+ public int autosavePeriod;
|
|
+ // CraftBukkit end
|
|
+ // Spigot start
|
|
+ public final SlackActivityAccountant slackActivityAccountant = new SlackActivityAccountant();
|
|
+ // Spigot end
|
|
|
|
- public MinecraftServer(File anvilFileIn, Proxy proxyIn, DataFixer dataFixerIn, YggdrasilAuthenticationService authServiceIn, MinecraftSessionService sessionServiceIn, GameProfileRepository profileRepoIn, PlayerProfileCache profileCacheIn)
|
|
+ public MinecraftServer(OptionSet options, Proxy proxyIn, DataFixer dataFixerIn, YggdrasilAuthenticationService authServiceIn, MinecraftSessionService sessionServiceIn, GameProfileRepository profileRepoIn, PlayerProfileCache profileCacheIn)
|
|
{
|
|
this.serverProxy = proxyIn;
|
|
this.authService = authServiceIn;
|
|
this.sessionService = sessionServiceIn;
|
|
this.profileRepo = profileRepoIn;
|
|
this.profileCache = profileCacheIn;
|
|
- this.anvilFile = anvilFileIn;
|
|
+ // this.anvilFile = anvilFileIn;
|
|
this.networkSystem = new NetworkSystem(this);
|
|
this.commandManager = this.createCommandManager();
|
|
- this.anvilConverterForAnvilFile = new AnvilSaveConverter(anvilFileIn, dataFixerIn);
|
|
+ // this.anvilConverterForAnvilFile = new AnvilSaveConverter(anvilFileIn, dataFixerIn); // CraftBukkit - moved to DedicatedServer.init
|
|
this.dataFixer = dataFixerIn;
|
|
+ this.options = options;
|
|
+ // Paper start - Handled by TerminalConsoleAppender
|
|
+ /*
|
|
+ // 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");
|
|
+ Main.useJline = false;
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ reader = new ConsoleReader(System.in, System.out);
|
|
+ reader.setExpandEvents(false); // Avoid parsing exceptions for uncommonly used event designators
|
|
+ } 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");
|
|
+ Main.useJline = false;
|
|
+ reader = new ConsoleReader(System.in, System.out);
|
|
+ reader.setExpandEvents(false);
|
|
+ } catch (IOException ex) {
|
|
+ LOGGER.warn((String) null, ex);
|
|
+ }
|
|
+ }
|
|
+ */
|
|
+ // Paper end
|
|
+ Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
|
|
+ this.serverThread = primaryThread = new Thread(net.minecraftforge.fml.common.thread.SidedThreadGroups.SERVER, this, "Server thread"); // Moved from main
|
|
}
|
|
|
|
+ public abstract PropertyManager getPropertyManager();
|
|
+
|
|
public ServerCommandManager createCommandManager()
|
|
{
|
|
return new ServerCommandManager(this);
|
|
@@ -220,83 +284,104 @@
|
|
|
|
public void loadAllWorlds(String saveName, String worldNameIn, long seed, WorldType type, String generatorOptions)
|
|
{
|
|
+ ServerCommandManager vanillaCommandManager = (ServerCommandManager) this.getCommandManager();
|
|
+ vanillaCommandManager.registerVanillaCommands();
|
|
this.convertMapIfNeeded(saveName);
|
|
this.setUserMessage("menu.loadingLevel");
|
|
- this.worlds = new WorldServer[3];
|
|
- this.timeOfLastDimensionTick = new long[this.worlds.length][100];
|
|
- ISaveHandler isavehandler = this.anvilConverterForAnvilFile.getSaveLoader(saveName, true);
|
|
- this.setResourcePackFromWorld(this.getFolderName(), isavehandler);
|
|
- WorldInfo worldinfo = isavehandler.loadWorldInfo();
|
|
- WorldSettings worldsettings;
|
|
|
|
- if (worldinfo == null)
|
|
- {
|
|
- if (this.isDemo())
|
|
- {
|
|
- worldsettings = WorldServerDemo.DEMO_WORLD_SETTINGS;
|
|
- }
|
|
- else
|
|
- {
|
|
- worldsettings = new WorldSettings(seed, this.getGameType(), this.canStructuresSpawn(), this.isHardcore(), type);
|
|
- worldsettings.setGeneratorOptions(generatorOptions);
|
|
+ WorldSettings worldsettings = new WorldSettings(seed, this.getGameType(), this.canStructuresSpawn(), this.isHardcore(), type);
|
|
+ worldsettings.setGeneratorOptions(generatorOptions);
|
|
+ WorldServer world;
|
|
|
|
- if (this.enableBonusChest)
|
|
- {
|
|
- worldsettings.enableBonusChest();
|
|
+ // WorldServer overWorld = (WorldServer)(isDemo() ? new WorldServerDemo(this, new AnvilSaveHandler(server.getWorldContainer(), worldNameIn , true, this.dataFixer), worldinfo, 0, profiler).init() : new WorldServer(this, new AnvilSaveHandler(server.getWorldContainer(), worldNameIn , true, this.dataFixer), worldinfo, 0, profiler).init());
|
|
+ // TODO: Reimplement this!
|
|
+ Integer[] dimIds = net.minecraftforge.common.DimensionManager.getStaticDimensionIDs();
|
|
+ Arrays.sort(dimIds, new Comparator<Integer>() {
|
|
+ @Override
|
|
+ public int compare(Integer o1, Integer o2) {
|
|
+ // Zero-dimension must always be the first in array!
|
|
+ if (o1 == 0) {
|
|
+ return -1;
|
|
+ } else {
|
|
+ return Math.max(o1, o2);
|
|
}
|
|
}
|
|
-
|
|
- worldinfo = new WorldInfo(worldsettings, worldNameIn);
|
|
- }
|
|
- else
|
|
+ });
|
|
+ for (int dim : dimIds)
|
|
{
|
|
- worldinfo.setWorldName(worldNameIn);
|
|
- worldsettings = new WorldSettings(worldinfo);
|
|
- }
|
|
-
|
|
- for (int i = 0; i < this.worlds.length; ++i)
|
|
- {
|
|
- int j = 0;
|
|
-
|
|
- if (i == 1)
|
|
- {
|
|
- j = -1;
|
|
+ // World validation
|
|
+ if (dim != 0) {
|
|
+ if ((dim == -1 && !this.getAllowNether()) || (dim == 1 && !server.getAllowEnd())) {
|
|
+ continue;
|
|
+ }
|
|
}
|
|
|
|
- if (i == 2)
|
|
- {
|
|
- j = 1;
|
|
+ org.bukkit.World.Environment worldEnvironment = org.bukkit.World.Environment.getEnvironment(dim);
|
|
+ if (worldEnvironment == null) {
|
|
+ worldEnvironment = org.bukkit.World.Environment.getEnvironment(DimensionManager.getProviderType(dim).getId());
|
|
}
|
|
+ String name = (dim == 0) ? saveName : "DIM" + dim;
|
|
+ org.bukkit.generator.ChunkGenerator gen = null;
|
|
|
|
- if (i == 0)
|
|
- {
|
|
- if (this.isDemo())
|
|
- {
|
|
- this.worlds[i] = (WorldServer)(new WorldServerDemo(this, isavehandler, worldinfo, j, this.profiler)).init();
|
|
+ if (dim == 0) {
|
|
+ ISaveHandler idatamanager = new AnvilSaveHandler(server.getWorldContainer(), worldNameIn, true, this.dataFixer);
|
|
+ WorldInfo worlddata = idatamanager.loadWorldInfo();
|
|
+ // CatServer start
|
|
+ if (!BukkitInjector.initializedBukkit) { // CatServer - inject bukkit materials before plugins load
|
|
+ BukkitInjector.injectBlockBukkitMaterials();
|
|
+ BukkitInjector.injectItemBukkitMaterials();
|
|
+ BukkitInjector.injectBiomes();
|
|
+ BukkitInjector.injectEntityType();
|
|
+ BukkitInjector.registerEnchantments();
|
|
+ BukkitInjector.registerPotions();
|
|
+ BukkitInjector.registerBannerPatterns();
|
|
+ BukkitInjector.initializedBukkit = true;
|
|
}
|
|
- else
|
|
- {
|
|
- this.worlds[i] = (WorldServer)(new WorldServer(this, isavehandler, worldinfo, j, this.profiler)).init();
|
|
+ server.loadPlugins();
|
|
+ server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.STARTUP);
|
|
+ // CatServer end
|
|
+ if (worlddata == null) {
|
|
+ worlddata = new WorldInfo(worldsettings, worldNameIn);
|
|
}
|
|
+ worlddata.checkName(worldNameIn); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end)
|
|
+ if (this.isDemo()) {
|
|
+ world = (WorldServer) (new WorldServerDemo(this, idatamanager, worlddata, dim, this.profiler)).init();
|
|
+ } else {
|
|
+ world = (WorldServer) (new WorldServer(this, idatamanager, worlddata, dim, this.profiler, worldEnvironment, gen)).init();
|
|
+ }
|
|
|
|
- this.worlds[i].initialize(worldsettings);
|
|
+ world.initialize(worldsettings);
|
|
+ this.server.scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(this, world.getScoreboard());
|
|
+ } else {
|
|
+ gen = this.server.getGenerator(name);
|
|
+
|
|
+ ISaveHandler idatamanager = new AnvilSaveHandler(server.getWorldContainer(), name, true, this.dataFixer);
|
|
+ // world =, b0 to dimension, s1 to name, added Environment and gen
|
|
+ WorldInfo worlddata = idatamanager.loadWorldInfo();
|
|
+ if (worlddata == null) {
|
|
+ worlddata = new WorldInfo(worldsettings, name);
|
|
+ }
|
|
+ worlddata.checkName(name); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end)
|
|
+ world = (WorldServer) new WorldServerMulti(this, idatamanager, dim, this.worldServerList.get(0), this.profiler, worlddata, worldEnvironment, gen).init();
|
|
+ worlddata.setServerInitialized(true);
|
|
}
|
|
- else
|
|
- {
|
|
- this.worlds[i] = (WorldServer)(new WorldServerMulti(this, isavehandler, j, this.worlds[0], this.profiler)).init();
|
|
- }
|
|
+ this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldInitEvent(world.getWorld()));
|
|
+ world.addEventListener(new ServerWorldEventHandler(this, world));
|
|
|
|
- this.worlds[i].addEventListener(new ServerWorldEventHandler(this, this.worlds[i]));
|
|
-
|
|
if (!this.isSinglePlayer())
|
|
{
|
|
- this.worlds[i].getWorldInfo().setGameType(this.getGameType());
|
|
+ world.getWorldInfo().setGameType(this.getGameType());
|
|
}
|
|
+ getPlayerList().setPlayerManager(worldServerList.toArray(new WorldServer[worldServerList.size()]));
|
|
+ net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.WorldEvent.Load(world));
|
|
}
|
|
|
|
this.playerList.setPlayerManager(this.worlds);
|
|
this.setDifficultyForAllWorlds(this.getDifficulty());
|
|
this.initialWorldChunkLoad();
|
|
+
|
|
+ this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD);
|
|
+ CatServer.onServerStart();
|
|
}
|
|
|
|
public void initialWorldChunkLoad()
|
|
@@ -309,7 +394,7 @@
|
|
this.setUserMessage("menu.generatingTerrain");
|
|
int j1 = 0;
|
|
LOGGER.info("Preparing start region for level 0");
|
|
- WorldServer worldserver = this.worlds[0];
|
|
+ WorldServer worldserver = net.minecraftforge.common.DimensionManager.getWorld(j1);
|
|
BlockPos blockpos = worldserver.getSpawnPoint();
|
|
long k1 = getCurrentTimeMillis();
|
|
|
|
@@ -330,6 +415,10 @@
|
|
}
|
|
}
|
|
|
|
+ for (WorldServer world : this.worldServerList) {
|
|
+ this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(world.getWorld()));
|
|
+ }
|
|
+
|
|
this.clearCurrentTask();
|
|
}
|
|
|
|
@@ -379,7 +468,7 @@
|
|
|
|
public void saveAllWorlds(boolean isSilent)
|
|
{
|
|
- for (WorldServer worldserver : this.worlds)
|
|
+ for (WorldServer worldserver : this.worldServerList)
|
|
{
|
|
if (worldserver != null)
|
|
{
|
|
@@ -400,10 +489,22 @@
|
|
}
|
|
}
|
|
|
|
- public void stopServer()
|
|
+ private boolean hasStopped = false;
|
|
+ private final Object stopLock = new Object();
|
|
+
|
|
+ public void stopServer() throws MinecraftException
|
|
{
|
|
+ org.spigotmc.AsyncCatcher.enabled = false; // Spigot
|
|
+ // CraftBukkit start - prevent double stopping on multiple threads
|
|
+ synchronized(stopLock) {
|
|
+ if (hasStopped) return;
|
|
+ hasStopped = true;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
LOGGER.info("Stopping server");
|
|
-
|
|
+ if (this.server != null) {
|
|
+ this.server.disablePlugins();
|
|
+ }
|
|
if (this.getNetworkSystem() != null)
|
|
{
|
|
this.getNetworkSystem().terminateEndpoints();
|
|
@@ -414,6 +515,7 @@
|
|
LOGGER.info("Saving players");
|
|
this.playerList.saveAllPlayerData();
|
|
this.playerList.removeAllPlayers();
|
|
+ try { Thread.sleep(100); } catch (InterruptedException ex) {} // CraftBukkit - SPIGOT-625 - give server at least a chance to send packets
|
|
}
|
|
|
|
if (this.worlds != null)
|
|
@@ -430,21 +532,42 @@
|
|
|
|
this.saveAllWorlds(false);
|
|
|
|
- for (WorldServer worldserver1 : this.worlds)
|
|
+ // CraftBukkit start - Handled in saveChunks
|
|
+ for (WorldServer worldserver1 : this.worldServerList)
|
|
{
|
|
if (worldserver1 != null)
|
|
{
|
|
+ net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.WorldEvent.Unload(worldserver1));
|
|
worldserver1.flush();
|
|
}
|
|
}
|
|
+ // CraftBukkit end
|
|
+
|
|
+ WorldServer[] tmp = worlds;
|
|
+ for (WorldServer world : tmp)
|
|
+ {
|
|
+ net.minecraftforge.common.DimensionManager.setWorld(world.provider.getDimension(), null, this);
|
|
+ }
|
|
}
|
|
|
|
if (this.usageSnooper.isSnooperRunning())
|
|
{
|
|
this.usageSnooper.stopSnooper();
|
|
}
|
|
+
|
|
+ CommandBase.setCommandListener(null); // Forge: fix MC-128561
|
|
+
|
|
+ // Spigot start
|
|
+ if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) {
|
|
+ LOGGER.info("Saving usercache.json");
|
|
+ this.profileCache.save(false); // Paper
|
|
+ }
|
|
+ // Spigot end
|
|
+ CatServer.onServerStop();
|
|
}
|
|
|
|
+ public boolean isServerStopping() { return this.hasStopped; } // CatServer
|
|
+
|
|
public boolean isServerRunning()
|
|
{
|
|
return this.serverRunning;
|
|
@@ -455,62 +578,128 @@
|
|
this.serverRunning = false;
|
|
}
|
|
|
|
+ // Paper start - Further improve server tick loop
|
|
+ private static final int TPS = 20;
|
|
+ private static final long SEC_IN_NANO = 1000000000;
|
|
+ public static final long TICK_TIME = SEC_IN_NANO / TPS;
|
|
+ private static final long MAX_CATCHUP_BUFFER = TICK_TIME * TPS * 60L;
|
|
+ private static final int SAMPLE_INTERVAL = 20;
|
|
+ public final RollingAverage tps1 = new RollingAverage(60);
|
|
+ public final RollingAverage tps5 = new RollingAverage(60 * 5);
|
|
+ public final RollingAverage tps15 = new RollingAverage(60 * 15);
|
|
+ public double[] recentTps = new double[3]; // Paper - Fine have your darn compat with bad plugins
|
|
+
|
|
+ public static class RollingAverage {
|
|
+ private final int size;
|
|
+ private long time;
|
|
+ private double total;
|
|
+ private int index = 0;
|
|
+ private final double[] samples;
|
|
+ private final long[] times;
|
|
+
|
|
+ RollingAverage(int size) {
|
|
+ this.size = size;
|
|
+ this.time = size * SEC_IN_NANO;
|
|
+ this.total = TPS * SEC_IN_NANO * size;
|
|
+ this.samples = new double[size];
|
|
+ this.times = new long[size];
|
|
+ for (int i = 0; i < size; i++) {
|
|
+ this.samples[i] = TPS;
|
|
+ this.times[i] = SEC_IN_NANO;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void add(double x, long t) {
|
|
+ time -= times[index];
|
|
+ total -= samples[index] * times[index];
|
|
+ samples[index] = x;
|
|
+ times[index] = t;
|
|
+ time += t;
|
|
+ total += x * t;
|
|
+ if (++index == size) {
|
|
+ index = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public double getAverage() {
|
|
+ return total / time;
|
|
+ }
|
|
+ }
|
|
+ // Paper End
|
|
+
|
|
public void run()
|
|
{
|
|
try
|
|
{
|
|
if (this.init())
|
|
{
|
|
+ net.minecraftforge.fml.common.FMLCommonHandler.instance().handleServerStarted();
|
|
this.currentTime = getCurrentTimeMillis();
|
|
long i = 0L;
|
|
this.statusResponse.setServerDescription(new TextComponentString(this.motd));
|
|
this.statusResponse.setVersion(new ServerStatusResponse.Version("1.12.2", 340));
|
|
this.applyServerIconToResponse(this.statusResponse);
|
|
|
|
+ // Spigot start
|
|
+ Arrays.fill( recentTps, 20 );
|
|
+ long start = System.nanoTime(), lastTick = start - TICK_TIME, catchupTime = 0, curTime, wait, tickSection = start; // Paper - Further improve server tick loop
|
|
while (this.serverRunning)
|
|
{
|
|
- long k = getCurrentTimeMillis();
|
|
- long j = k - this.currentTime;
|
|
-
|
|
- if (j > 2000L && this.currentTime - this.timeOfLastWarning >= 15000L)
|
|
- {
|
|
- LOGGER.warn("Can't keep up! Did the system time change, or is the server overloaded? Running {}ms behind, skipping {} tick(s)", Long.valueOf(j), Long.valueOf(j / 50L));
|
|
- j = 2000L;
|
|
- this.timeOfLastWarning = this.currentTime;
|
|
+ curTime = System.nanoTime();
|
|
+ // Paper start - Further improve server tick loop
|
|
+ wait = TICK_TIME - (curTime - lastTick);
|
|
+ if (wait > 0) {
|
|
+ if (catchupTime < 2E6) {
|
|
+ wait += Math.abs(catchupTime);
|
|
+ } else if (wait < catchupTime) {
|
|
+ catchupTime -= wait;
|
|
+ wait = 0;
|
|
+ } else {
|
|
+ wait -= catchupTime;
|
|
+ catchupTime = 0;
|
|
+ }
|
|
}
|
|
-
|
|
- if (j < 0L)
|
|
- {
|
|
- LOGGER.warn("Time ran backwards! Did the system time change?");
|
|
- j = 0L;
|
|
+ if (wait > 0) {
|
|
+ Thread.sleep(wait / 1000000);
|
|
+ curTime = System.nanoTime();
|
|
+ wait = TICK_TIME - (curTime - lastTick);
|
|
}
|
|
|
|
- i += j;
|
|
- this.currentTime = k;
|
|
-
|
|
- if (this.worlds[0].areAllPlayersAsleep())
|
|
+ catchupTime = Math.min(MAX_CATCHUP_BUFFER, catchupTime - wait);
|
|
+ if ( ++MinecraftServer.currentTick % SAMPLE_INTERVAL == 0 )
|
|
{
|
|
- this.tick();
|
|
- i = 0L;
|
|
+ final long diff = curTime - tickSection;
|
|
+ double currentTps = 1E9 / diff * SAMPLE_INTERVAL;
|
|
+ tps1.add(currentTps, diff);
|
|
+ tps5.add(currentTps, diff);
|
|
+ tps15.add(currentTps, diff);
|
|
+ // Backwards compat with bad plugins
|
|
+ recentTps[0] = tps1.getAverage();
|
|
+ recentTps[1] = tps5.getAverage();
|
|
+ recentTps[2] = tps15.getAverage();
|
|
+ // Paper end
|
|
+ tickSection = curTime;
|
|
}
|
|
- else
|
|
- {
|
|
- while (i > 50L)
|
|
- {
|
|
- i -= 50L;
|
|
- this.tick();
|
|
- }
|
|
- }
|
|
+ lastTick = curTime;
|
|
|
|
- Thread.sleep(Math.max(1L, 50L - i));
|
|
+ this.tick();
|
|
this.serverIsRunning = true;
|
|
}
|
|
+ // Spigot end
|
|
+ net.minecraftforge.fml.common.FMLCommonHandler.instance().handleServerStopping();
|
|
+ net.minecraftforge.fml.common.FMLCommonHandler.instance().expectServerStopped(); // has to come before finalTick to avoid race conditions
|
|
}
|
|
else
|
|
{
|
|
+ net.minecraftforge.fml.common.FMLCommonHandler.instance().expectServerStopped(); // has to come before finalTick to avoid race conditions
|
|
this.finalTick((CrashReport)null);
|
|
}
|
|
}
|
|
+ catch (net.minecraftforge.fml.common.StartupQuery.AbortedException e)
|
|
+ {
|
|
+ // ignore silently
|
|
+ net.minecraftforge.fml.common.FMLCommonHandler.instance().expectServerStopped(); // has to come before finalTick to avoid race conditions
|
|
+ }
|
|
catch (Throwable throwable1)
|
|
{
|
|
LOGGER.error("Encountered an unexpected exception", throwable1);
|
|
@@ -536,13 +725,13 @@
|
|
LOGGER.error("We were unable to save this crash report to disk.");
|
|
}
|
|
|
|
+ net.minecraftforge.fml.common.FMLCommonHandler.instance().expectServerStopped(); // has to come before finalTick to avoid race conditions
|
|
this.finalTick(crashreport);
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
- this.serverStopped = true;
|
|
this.stopServer();
|
|
}
|
|
catch (Throwable throwable)
|
|
@@ -551,6 +740,8 @@
|
|
}
|
|
finally
|
|
{
|
|
+ net.minecraftforge.fml.common.FMLCommonHandler.instance().handleServerStopped();
|
|
+ this.serverStopped = true;
|
|
this.systemExitNow();
|
|
}
|
|
}
|
|
@@ -577,6 +768,7 @@
|
|
ImageIO.write(bufferedimage, "PNG", new ByteBufOutputStream(bytebuf));
|
|
ByteBuf bytebuf1 = Base64.encode(bytebuf);
|
|
response.setFavicon("data:image/png;base64," + bytebuf1.toString(StandardCharsets.UTF_8));
|
|
+ bytebuf1.release(); // Forge: fix MC-122085
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
@@ -617,7 +809,10 @@
|
|
|
|
public void tick()
|
|
{
|
|
+ SpigotTimings.serverTickTimer.startTiming(); // Spigot
|
|
+ this.slackActivityAccountant.tickStarted(); // Spigot
|
|
long i = System.nanoTime();
|
|
+ net.minecraftforge.fml.common.FMLCommonHandler.instance().onPreServerTick();
|
|
++this.tickCounter;
|
|
|
|
if (this.startProfiling)
|
|
@@ -644,14 +839,17 @@
|
|
|
|
Collections.shuffle(Arrays.asList(agameprofile));
|
|
this.statusResponse.getPlayers().setPlayers(agameprofile);
|
|
+ this.statusResponse.invalidateJson();
|
|
}
|
|
|
|
- if (this.tickCounter % 900 == 0)
|
|
+ if (autosavePeriod > 0 && this.tickCounter % autosavePeriod == 0) // CraftBukkit
|
|
{
|
|
+ SpigotTimings.worldSaveTimer.startTiming(); // Spigot
|
|
this.profiler.startSection("save");
|
|
this.playerList.saveAllPlayerData();
|
|
this.saveAllWorlds(true);
|
|
this.profiler.endSection();
|
|
+ SpigotTimings.worldSaveTimer.stopTiming(); // Spigot
|
|
}
|
|
|
|
this.profiler.startSection("tallying");
|
|
@@ -671,46 +869,88 @@
|
|
|
|
this.profiler.endSection();
|
|
this.profiler.endSection();
|
|
+ net.minecraftforge.fml.common.FMLCommonHandler.instance().onPostServerTick();
|
|
+ org.spigotmc.WatchdogThread.tick(); // Spigot
|
|
+ this.slackActivityAccountant.tickEnded(System.nanoTime() - i); // Spigot
|
|
+ SpigotTimings.serverTickTimer.stopTiming(); // Spigot
|
|
+ org.spigotmc.CustomTimingsHandler.tick(); // Spigot
|
|
}
|
|
|
|
public void updateTimeLightAndEntities()
|
|
{
|
|
+ SpigotTimings.schedulerTimer.startTiming(); // Spigot
|
|
+ this.server.getScheduler().mainThreadHeartbeat(this.tickCounter); // CraftBukkit
|
|
+ SpigotTimings.schedulerTimer.stopTiming(); // Spigot
|
|
this.profiler.startSection("jobs");
|
|
|
|
- synchronized (this.futureTaskQueue)
|
|
- {
|
|
- while (!this.futureTaskQueue.isEmpty())
|
|
- {
|
|
- Util.runTask(this.futureTaskQueue.poll(), LOGGER);
|
|
- }
|
|
- }
|
|
+ // Spigot start
|
|
+ FutureTask<?> entry;
|
|
+ int count = this.futureTaskQueue.size();
|
|
+ while (count-- > 0 && (entry = this.futureTaskQueue.poll()) != null) {
|
|
+ Util.runTask(entry, MinecraftServer.LOGGER);
|
|
+ }
|
|
+ // Spigot end
|
|
|
|
this.profiler.endStartSection("levels");
|
|
+ // CraftBukkit start
|
|
+ // Run tasks that are waiting on processing
|
|
+ SpigotTimings.processQueueTimer.startTiming(); // Spigot
|
|
+ while (!processQueue.isEmpty()) {
|
|
+ processQueue.remove().run();
|
|
+ }
|
|
+ SpigotTimings.processQueueTimer.stopTiming(); // Spigot
|
|
|
|
- for (int j = 0; j < this.worlds.length; ++j)
|
|
+ SpigotTimings.chunkIOTickTimer.startTiming(); // Spigot
|
|
+ org.bukkit.craftbukkit.chunkio.ChunkIOExecutor.tick();
|
|
+ SpigotTimings.chunkIOTickTimer.stopTiming(); // Spigot
|
|
+
|
|
+ SpigotTimings.timeUpdateTimer.startTiming(); // Spigot
|
|
+ // Send time updates to everyone, it will get the right time from the world the player is in.
|
|
+ if (this.tickCounter % 20 == 0) {
|
|
+ for (int i = 0; i < this.getPlayerList().getPlayers().size(); ++i) {
|
|
+ EntityPlayerMP entityplayer = (EntityPlayerMP) this.getPlayerList().getPlayers().get(i);
|
|
+ entityplayer.connection.sendPacket(new SPacketTimeUpdate(entityplayer.world.getTotalWorldTime(), entityplayer.getPlayerTime(), entityplayer.world.getGameRules().getBoolean("doDaylightCycle"))); // Add support for per player time
|
|
+ }
|
|
+ }
|
|
+ SpigotTimings.timeUpdateTimer.stopTiming(); // Spigot
|
|
+ net.minecraftforge.common.chunkio.ChunkIOExecutor.tick();
|
|
+ catserver.server.async.AsyncChunkGenerator.tick();
|
|
+ catserver.server.command.internal.CommandChunkStats.onServerTick();
|
|
+
|
|
+ Integer[] ids = net.minecraftforge.common.DimensionManager.getIDs(this.tickCounter % 200 == 0);
|
|
+ for (int x = 0; x < ids.length; x++)
|
|
{
|
|
+ int id = ids[x];
|
|
long i = System.nanoTime();
|
|
|
|
- if (j == 0 || this.getAllowNether())
|
|
- {
|
|
- WorldServer worldserver = this.worlds[j];
|
|
+ // CraftBukkit start
|
|
+ // if (id == 0 || this.getAllowNether())
|
|
+ // {
|
|
+ WorldServer worldserver = net.minecraftforge.common.DimensionManager.getWorld(id);
|
|
+
|
|
+ net.minecraft.tileentity.TileEntityHopper.skipHopperEvents = org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0 || CatServer.getConfig().disableHopperMoveEventWorlds.contains("*") || CatServer.getConfig().disableHopperMoveEventWorlds.contains(worldserver.getWorld().getName()); // CatServer
|
|
+
|
|
this.profiler.func_194340_a(() ->
|
|
{
|
|
return worldserver.getWorldInfo().getWorldName();
|
|
});
|
|
|
|
+ /* Drop global time updates
|
|
if (this.tickCounter % 20 == 0)
|
|
{
|
|
this.profiler.startSection("timeSync");
|
|
- this.playerList.sendPacketToAllPlayersInDimension(new SPacketTimeUpdate(worldserver.getTotalWorldTime(), worldserver.getWorldTime(), worldserver.getGameRules().getBoolean("doDaylightCycle")), worldserver.provider.getDimensionType().getId());
|
|
+ this.playerList.sendPacketToAllPlayersInDimension(new SPacketTimeUpdate(worldserver.getTotalWorldTime(), worldserver.getWorldTime(), worldserver.getGameRules().getBoolean("doDaylightCycle")), worldserver.provider.getDimension());
|
|
this.profiler.endSection();
|
|
}
|
|
-
|
|
+ // CraftBukkit end */
|
|
this.profiler.startSection("tick");
|
|
+ net.minecraftforge.fml.common.FMLCommonHandler.instance().onPreWorldTick(worldserver);
|
|
|
|
try
|
|
{
|
|
+ worldserver.timings.doTick.startTiming(); // Spigot
|
|
worldserver.tick();
|
|
+ worldserver.timings.doTick.stopTiming(); // Spigot
|
|
}
|
|
catch (Throwable throwable1)
|
|
{
|
|
@@ -721,7 +961,9 @@
|
|
|
|
try
|
|
{
|
|
+ worldserver.timings.tickEntities.startTiming(); // Spigot
|
|
worldserver.updateEntities();
|
|
+ worldserver.timings.tickEntities.stopTiming(); // Spigot
|
|
}
|
|
catch (Throwable throwable)
|
|
{
|
|
@@ -730,28 +972,42 @@
|
|
throw new ReportedException(crashreport1);
|
|
}
|
|
|
|
+ net.minecraftforge.fml.common.FMLCommonHandler.instance().onPostWorldTick(worldserver);
|
|
this.profiler.endSection();
|
|
this.profiler.startSection("tracker");
|
|
+ worldserver.timings.tracker.startTiming(); // Spigot
|
|
worldserver.getEntityTracker().tick();
|
|
+ worldserver.timings.tracker.stopTiming(); // Spigot
|
|
this.profiler.endSection();
|
|
this.profiler.endSection();
|
|
- }
|
|
+ worldserver.explosionDensityCache.clear(); // Paper - Optimize explosions
|
|
+ // } // CraftBukkit
|
|
|
|
- this.timeOfLastDimensionTick[j][this.tickCounter % 100] = System.nanoTime() - i;
|
|
+ if (worldTickTimes.containsKey(id)) worldTickTimes.get(id)[this.tickCounter % 100] = System.nanoTime() - i; // CatServer - check world in tickTime list, prevent plugin unload world from causing NPE
|
|
}
|
|
|
|
+ this.profiler.endStartSection("dim_unloading");
|
|
+ net.minecraftforge.common.DimensionManager.unloadWorlds(worldTickTimes);
|
|
this.profiler.endStartSection("connection");
|
|
+ SpigotTimings.connectionTimer.startTiming(); // Spigot
|
|
this.getNetworkSystem().networkTick();
|
|
+ SpigotTimings.connectionTimer.stopTiming(); // Spigot
|
|
this.profiler.endStartSection("players");
|
|
+ SpigotTimings.playerListTimer.startTiming(); // Spigot
|
|
this.playerList.onTick();
|
|
+ SpigotTimings.playerListTimer.stopTiming(); // Spigot
|
|
this.profiler.endStartSection("commandFunctions");
|
|
+ SpigotTimings.commandFunctionsTimer.startTiming(); // Spigot
|
|
this.getFunctionManager().update();
|
|
+ SpigotTimings.commandFunctionsTimer.stopTiming();// Spigot
|
|
this.profiler.endStartSection("tickables");
|
|
|
|
+ SpigotTimings.tickablesTimer.startTiming(); // Spigot
|
|
for (int k = 0; k < this.tickables.size(); ++k)
|
|
{
|
|
((ITickable)this.tickables.get(k)).update();
|
|
}
|
|
+ SpigotTimings.tickablesTimer.stopTiming(); // Spigot
|
|
|
|
this.profiler.endSection();
|
|
}
|
|
@@ -763,8 +1019,11 @@
|
|
|
|
public void startServerThread()
|
|
{
|
|
- this.serverThread = new Thread(this, "Server thread");
|
|
+ /* CraftBukkit start - prevent abuse
|
|
+ net.minecraftforge.fml.common.StartupQuery.reset();
|
|
+ this.serverThread = new Thread(net.minecraftforge.fml.common.thread.SidedThreadGroups.SERVER, this, "Server thread");
|
|
this.serverThread.start();
|
|
+ // CraftBukkit end */
|
|
}
|
|
|
|
public File getFile(String fileName)
|
|
@@ -779,16 +1038,20 @@
|
|
|
|
public WorldServer getWorld(int dimension)
|
|
{
|
|
- if (dimension == -1)
|
|
+ WorldServer ret = net.minecraftforge.common.DimensionManager.getWorld(dimension, true);
|
|
+ if (ret == null)
|
|
{
|
|
- return this.worlds[1];
|
|
+ net.minecraftforge.common.DimensionManager.initDimension(dimension);
|
|
+ ret = net.minecraftforge.common.DimensionManager.getWorld(dimension);
|
|
}
|
|
- else
|
|
- {
|
|
- return dimension == 1 ? this.worlds[2] : this.worlds[0];
|
|
- }
|
|
+ return ret;
|
|
}
|
|
|
|
+ public WorldServer getWorldServer(int i) {
|
|
+ WorldServer world = getWorld(i);
|
|
+ return world != null ? world : worldServerList.get(0);
|
|
+ }
|
|
+
|
|
public String getMinecraftVersion()
|
|
{
|
|
return "1.12.2";
|
|
@@ -816,7 +1079,8 @@
|
|
|
|
public String getServerModName()
|
|
{
|
|
- return "vanilla";
|
|
+ // TODO: Should we change this for CraftBukkit's server name?
|
|
+ return net.minecraftforge.fml.common.FMLCommonHandler.instance().getModName();
|
|
}
|
|
|
|
public CrashReport addServerInfoToCrashReport(CrashReport report)
|
|
@@ -845,7 +1109,7 @@
|
|
|
|
public List<String> getTabCompletions(ICommandSender sender, String input, @Nullable BlockPos pos, boolean hasTargetBlock)
|
|
{
|
|
- List<String> list = Lists.<String>newArrayList();
|
|
+ Set<String> completionsSet = Sets.newHashSet(server.tabComplete(sender, input, pos, hasTargetBlock));
|
|
boolean flag = input.startsWith("/");
|
|
|
|
if (flag)
|
|
@@ -862,11 +1126,9 @@
|
|
{
|
|
if (CommandBase.doesStringStartWith(s2, s1))
|
|
{
|
|
- list.add(s1);
|
|
+ completionsSet.add(s1);
|
|
}
|
|
}
|
|
-
|
|
- return list;
|
|
}
|
|
else
|
|
{
|
|
@@ -879,22 +1141,24 @@
|
|
{
|
|
if (flag1 && !hasTargetBlock)
|
|
{
|
|
- list.add("/" + s);
|
|
+ completionsSet.add("/" + s);
|
|
}
|
|
else
|
|
{
|
|
- list.add(s);
|
|
+ completionsSet.add(s);
|
|
}
|
|
}
|
|
}
|
|
-
|
|
- return list;
|
|
}
|
|
+ List<String> finalCompletionsList = new ArrayList<>(completionsSet);
|
|
+ Collections.sort(finalCompletionsList);
|
|
+ return finalCompletionsList;
|
|
}
|
|
|
|
public boolean isAnvilFileSet()
|
|
{
|
|
- return this.anvilFile != null;
|
|
+ // return this.anvilFile != null;
|
|
+ return true; // CraftBukkit
|
|
}
|
|
|
|
public String getName()
|
|
@@ -904,7 +1168,8 @@
|
|
|
|
public void sendMessage(ITextComponent component)
|
|
{
|
|
- LOGGER.info(component.getUnformattedText());
|
|
+ // Paper - Log message with colors
|
|
+ MinecraftServer.LOGGER.info(org.bukkit.craftbukkit.util.CraftChatMessage.fromComponent(component, net.minecraft.util.text.TextFormatting.WHITE));
|
|
}
|
|
|
|
public boolean canUseCommand(int permLevel, String commandName)
|
|
@@ -947,13 +1212,11 @@
|
|
this.folderName = name;
|
|
}
|
|
|
|
- @SideOnly(Side.CLIENT)
|
|
public void setWorldName(String worldNameIn)
|
|
{
|
|
this.worldName = worldNameIn;
|
|
}
|
|
|
|
- @SideOnly(Side.CLIENT)
|
|
public String getWorldName()
|
|
{
|
|
return this.worldName;
|
|
@@ -966,7 +1229,7 @@
|
|
|
|
public void setDifficultyForAllWorlds(EnumDifficulty difficulty)
|
|
{
|
|
- for (WorldServer worldserver1 : this.worlds)
|
|
+ for (WorldServer worldserver1 : this.worldServerList)
|
|
{
|
|
if (worldserver1 != null)
|
|
{
|
|
@@ -1048,9 +1311,9 @@
|
|
playerSnooper.addClientStat("avg_tick_ms", Integer.valueOf((int)(MathHelper.average(this.tickTimeArray) * 1.0E-6D)));
|
|
int l = 0;
|
|
|
|
- if (this.worlds != null)
|
|
+ if (this.worldServerList != null)
|
|
{
|
|
- for (WorldServer worldserver1 : this.worlds)
|
|
+ for (WorldServer worldserver1 : this.worldServerList)
|
|
{
|
|
if (worldserver1 != null)
|
|
{
|
|
@@ -1088,7 +1351,8 @@
|
|
|
|
public boolean isServerInOnlineMode()
|
|
{
|
|
- return this.onlineMode;
|
|
+ // return this.onlineMode;
|
|
+ return server.getOnlineMode(); // CraftBukkit
|
|
}
|
|
|
|
public void setOnlineMode(boolean online)
|
|
@@ -1177,12 +1441,12 @@
|
|
|
|
public void setPlayerList(PlayerList list)
|
|
{
|
|
- this.playerList = list;
|
|
+ if (this.playerList == null) this.playerList = list.initCraftServer(); else new UnsupportedOperationException("CatServer is not support the use of setPlayerList. If you installed FastWorkbench, Please replace with this fork: https://github.com/Luohuayu/FastWorkbench/").printStackTrace(); // CatServer
|
|
}
|
|
|
|
public void setGameType(GameType gameMode)
|
|
{
|
|
- for (WorldServer worldserver1 : this.worlds)
|
|
+ for (WorldServer worldserver1 : this.worldServerList)
|
|
{
|
|
worldserver1.getWorldInfo().setGameType(gameMode);
|
|
}
|
|
@@ -1224,7 +1488,7 @@
|
|
|
|
public World getEntityWorld()
|
|
{
|
|
- return this.worlds[0];
|
|
+ return this.worldServerList.get(0); // CraftBukkit
|
|
}
|
|
|
|
public boolean isBlockProtected(World worldIn, BlockPos pos, EntityPlayer playerIn)
|
|
@@ -1285,7 +1549,7 @@
|
|
@Nullable
|
|
public Entity getEntityFromUuid(UUID uuid)
|
|
{
|
|
- for (WorldServer worldserver1 : this.worlds)
|
|
+ for (WorldServer worldserver1 : this.worldServerList)
|
|
{
|
|
if (worldserver1 != null)
|
|
{
|
|
@@ -1303,7 +1567,7 @@
|
|
|
|
public boolean sendCommandFeedback()
|
|
{
|
|
- return this.worlds[0].getGameRules().getBoolean("sendCommandFeedback");
|
|
+ return this.worldServerList.get(0).getGameRules().getBoolean("sendCommandFeedback");
|
|
}
|
|
|
|
public MinecraftServer getServer()
|
|
@@ -1311,6 +1575,11 @@
|
|
return this;
|
|
}
|
|
|
|
+ public static MinecraftServer getServerInst()
|
|
+ {
|
|
+ return (Bukkit.getServer() instanceof CraftServer) ? ((CraftServer) Bukkit.getServer()).getServer() : null;
|
|
+ }
|
|
+
|
|
public int getMaxWorldSize()
|
|
{
|
|
return 29999984;
|
|
@@ -1320,15 +1589,14 @@
|
|
{
|
|
Validate.notNull(callable);
|
|
|
|
- if (!this.isCallingFromMinecraftThread() && !this.isServerStopped())
|
|
+ if (!this.isCallingFromMinecraftThread()/* && !this.isServerStopped()*/) // CraftBukkit
|
|
{
|
|
ListenableFutureTask<V> listenablefuturetask = ListenableFutureTask.<V>create(callable);
|
|
|
|
- synchronized (this.futureTaskQueue)
|
|
- {
|
|
- this.futureTaskQueue.add(listenablefuturetask);
|
|
- return listenablefuturetask;
|
|
- }
|
|
+ // Spigot start
|
|
+ this.futureTaskQueue.add(listenablefuturetask);
|
|
+ return listenablefuturetask;
|
|
+ // Spigot end
|
|
}
|
|
else
|
|
{
|
|
@@ -1366,12 +1634,12 @@
|
|
|
|
public AdvancementManager getAdvancementManager()
|
|
{
|
|
- return this.worlds[0].getAdvancementManager();
|
|
+ return this.worldServerList.get(0).getAdvancementManager();
|
|
}
|
|
|
|
public FunctionManager getFunctionManager()
|
|
{
|
|
- return this.worlds[0].getFunctionManager();
|
|
+ return this.worldServerList.get(0).getFunctionManager();
|
|
}
|
|
|
|
public void reload()
|
|
@@ -1379,7 +1647,7 @@
|
|
if (this.isCallingFromMinecraftThread())
|
|
{
|
|
this.getPlayerList().saveAllPlayerData();
|
|
- this.worlds[0].getLootTableManager().reloadLootTables();
|
|
+ this.worldServerList.get(0).getLootTableManager().reloadLootTables();
|
|
this.getAdvancementManager().reload();
|
|
this.getFunctionManager().reload();
|
|
this.getPlayerList().reloadResources();
|
|
@@ -1409,120 +1677,47 @@
|
|
}
|
|
|
|
@SideOnly(Side.SERVER)
|
|
- public static void main(String[] p_main_0_)
|
|
+ public static void main(String[] args)
|
|
{
|
|
+ OptionSet options = org.bukkit.craftbukkit.Main.main(args);
|
|
+ if (options == null)
|
|
+ return;
|
|
+ catserver.server.utils.ServerUtils.acceptEula(); // CatServer
|
|
+ //Forge: Copied from DedicatedServer.init as to run as early as possible, Old code left in place intentionally.
|
|
+ //Done in good faith with permission: https://github.com/MinecraftForge/MinecraftForge/issues/3659#issuecomment-390467028
|
|
+ ServerEula eula = new ServerEula(new File("eula.txt"));
|
|
+ if (!eula.hasAcceptedEULA())
|
|
+ {
|
|
+ LOGGER.info("You need to agree to the EULA in order to run the server. Go to eula.txt for more info.");
|
|
+ eula.createEULAFile();
|
|
+ return;
|
|
+ }
|
|
Bootstrap.register();
|
|
-
|
|
try
|
|
{
|
|
- boolean flag = true;
|
|
- String s = null;
|
|
String s1 = ".";
|
|
- String s2 = null;
|
|
- boolean flag1 = false;
|
|
- boolean flag2 = false;
|
|
- int l = -1;
|
|
-
|
|
- for (int i1 = 0; i1 < p_main_0_.length; ++i1)
|
|
- {
|
|
- String s3 = p_main_0_[i1];
|
|
- String s4 = i1 == p_main_0_.length - 1 ? null : p_main_0_[i1 + 1];
|
|
- boolean flag3 = false;
|
|
-
|
|
- if (!"nogui".equals(s3) && !"--nogui".equals(s3))
|
|
- {
|
|
- if ("--port".equals(s3) && s4 != null)
|
|
- {
|
|
- flag3 = true;
|
|
-
|
|
- try
|
|
- {
|
|
- l = Integer.parseInt(s4);
|
|
- }
|
|
- catch (NumberFormatException var13)
|
|
- {
|
|
- ;
|
|
- }
|
|
- }
|
|
- else if ("--singleplayer".equals(s3) && s4 != null)
|
|
- {
|
|
- flag3 = true;
|
|
- s = s4;
|
|
- }
|
|
- else if ("--universe".equals(s3) && s4 != null)
|
|
- {
|
|
- flag3 = true;
|
|
- s1 = s4;
|
|
- }
|
|
- else if ("--world".equals(s3) && s4 != null)
|
|
- {
|
|
- flag3 = true;
|
|
- s2 = s4;
|
|
- }
|
|
- else if ("--demo".equals(s3))
|
|
- {
|
|
- flag1 = true;
|
|
- }
|
|
- else if ("--bonusChest".equals(s3))
|
|
- {
|
|
- flag2 = true;
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- flag = false;
|
|
- }
|
|
-
|
|
- if (flag3)
|
|
- {
|
|
- ++i1;
|
|
- }
|
|
- }
|
|
-
|
|
- YggdrasilAuthenticationService yggdrasilauthenticationservice = new YggdrasilAuthenticationService(Proxy.NO_PROXY, UUID.randomUUID().toString());
|
|
+ YggdrasilAuthenticationService yggdrasilauthenticationservice = CatServer.getConfig().disableUpdateGameProfile ? new CatProxyAuthenticationService(Proxy.NO_PROXY, UUID.randomUUID().toString()) : new YggdrasilAuthenticationService(Proxy.NO_PROXY, UUID.randomUUID().toString());;
|
|
MinecraftSessionService minecraftsessionservice = yggdrasilauthenticationservice.createMinecraftSessionService();
|
|
GameProfileRepository gameprofilerepository = yggdrasilauthenticationservice.createProfileRepository();
|
|
PlayerProfileCache playerprofilecache = new PlayerProfileCache(gameprofilerepository, new File(s1, USER_CACHE_FILE.getName()));
|
|
- final DedicatedServer dedicatedserver = new DedicatedServer(new File(s1), DataFixesManager.createFixer(), yggdrasilauthenticationservice, minecraftsessionservice, gameprofilerepository, playerprofilecache);
|
|
+ final DedicatedServer dedicatedserver = new DedicatedServer(options, DataFixesManager.createFixer(), yggdrasilauthenticationservice, minecraftsessionservice, gameprofilerepository, playerprofilecache);
|
|
|
|
- if (s != null)
|
|
- {
|
|
- dedicatedserver.setServerOwner(s);
|
|
+ if (options.has("port")) {
|
|
+ int port = (Integer) options.valueOf("port");
|
|
+ if (port > 0) {
|
|
+ dedicatedserver.setServerPort(port);
|
|
+ }
|
|
}
|
|
|
|
- if (s2 != null)
|
|
- {
|
|
- dedicatedserver.setFolderName(s2);
|
|
+ if (options.has("universe")) {
|
|
+ dedicatedserver.anvilFile = (File) options.valueOf("universe");
|
|
}
|
|
|
|
- if (l >= 0)
|
|
- {
|
|
- dedicatedserver.setServerPort(l);
|
|
+ if (options.has("world")) {
|
|
+ dedicatedserver.setWorldName((String) options.valueOf("world"));
|
|
}
|
|
|
|
- if (flag1)
|
|
- {
|
|
- dedicatedserver.setDemo(true);
|
|
- }
|
|
-
|
|
- if (flag2)
|
|
- {
|
|
- dedicatedserver.canCreateBonusChest(true);
|
|
- }
|
|
-
|
|
- if (flag && !GraphicsEnvironment.isHeadless())
|
|
- {
|
|
- dedicatedserver.setGuiEnabled();
|
|
- }
|
|
-
|
|
- dedicatedserver.startServerThread();
|
|
- Runtime.getRuntime().addShutdownHook(new Thread("Server Shutdown Thread")
|
|
- {
|
|
- public void run()
|
|
- {
|
|
- dedicatedserver.stopServer();
|
|
- }
|
|
- });
|
|
+ dedicatedserver.primaryThread.start();
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
@@ -1539,7 +1734,8 @@
|
|
@SideOnly(Side.SERVER)
|
|
public boolean isDebuggingEnabled()
|
|
{
|
|
- return false;
|
|
+ // return false;
|
|
+ return this.getPropertyManager().getBooleanProperty("debug", false); // CraftBukkit - don't hardcode
|
|
}
|
|
|
|
@SideOnly(Side.SERVER)
|
|
@@ -1598,4 +1794,9 @@
|
|
{
|
|
return this.serverThread;
|
|
}
|
|
+
|
|
+ public DataFixer getDataFixer()
|
|
+ {
|
|
+ return this.dataFixer;
|
|
+ }
|
|
}
|