    001    package net.minecraft.server;
    002
    003    import cpw.mods.fml.common.Side;
    004    import cpw.mods.fml.common.asm.SideOnly;
    005    import java.awt.GraphicsEnvironment;
    006    import java.io.File;
    007    import java.io.IOException;
    008    import java.security.KeyPair;
    009    import java.text.SimpleDateFormat;
    010    import java.util.ArrayList;
    011    import java.util.Date;
    012    import java.util.Hashtable;
    013    import java.util.Iterator;
    014    import java.util.List;
    015    import java.util.logging.Level;
    016    import java.util.logging.Logger;
    017    import cpw.mods.fml.common.FMLCommonHandler;
    018    import cpw.mods.fml.relauncher.ArgsWrapper;
    019    import cpw.mods.fml.relauncher.FMLRelauncher;
    020    import net.minecraft.src.AnvilSaveConverter;
    021    import net.minecraft.src.AxisAlignedBB;
    022    import net.minecraft.src.CallableIsServerModded;
    023    import net.minecraft.src.CallablePlayers;
    024    import net.minecraft.src.CallableServerProfiler;
    025    import net.minecraft.src.ChunkCoordinates;
    026    import net.minecraft.src.CommandBase;
    027    import net.minecraft.src.ConvertProgressUpdater;
    028    import net.minecraft.src.CrashReport;
    029    import net.minecraft.src.DedicatedServer;
    030    import net.minecraft.src.DemoWorldServer;
    031    import net.minecraft.src.EnumGameType;
    032    import net.minecraft.src.ICommandManager;
    033    import net.minecraft.src.ICommandSender;
    034    import net.minecraft.src.IPlayerUsage;
    035    import net.minecraft.src.IProgressUpdate;
    036    import net.minecraft.src.ISaveFormat;
    037    import net.minecraft.src.ISaveHandler;
    038    import net.minecraft.src.IUpdatePlayerListBox;
    039    import net.minecraft.src.MathHelper;
    040    import net.minecraft.src.MinecraftException;
    041    import net.minecraft.src.NetworkListenThread;
    042    import net.minecraft.src.Packet;
    043    import net.minecraft.src.Packet4UpdateTime;
    044    import net.minecraft.src.PlayerUsageSnooper;
    045    import net.minecraft.src.Profiler;
    046    import net.minecraft.src.RConConsoleSource;
    047    import net.minecraft.src.ReportedException;
    048    import net.minecraft.src.ServerCommandManager;
    049    import net.minecraft.src.ServerConfigurationManager;
    050    import net.minecraft.src.StatList;
    051    import net.minecraft.src.StringTranslate;
    052    import net.minecraft.src.StringUtils;
    053    import net.minecraft.src.ThreadDedicatedServer;
    054    import net.minecraft.src.ThreadServerApplication;
    055    import net.minecraft.src.Vec3;
    056    import net.minecraft.src.WorldInfo;
    057    import net.minecraft.src.WorldManager;
    058    import net.minecraft.src.WorldServer;
    059    import net.minecraft.src.WorldServerMulti;
    060    import net.minecraft.src.WorldSettings;
    061    import net.minecraft.src.WorldType;
    062    import net.minecraftforge.common.DimensionManager;
    063    import net.minecraftforge.common.MinecraftForge;
    064    import net.minecraftforge.event.world.WorldEvent;
    065
    066    public abstract class MinecraftServer implements Runnable, IPlayerUsage, ICommandSender
    067    {
    068        /** The logging system. */
    069        public static Logger logger = Logger.getLogger("Minecraft");
    070
    071        /** Instance of Minecraft Server. */
    072        private static MinecraftServer mcServer = null;
    073        private final ISaveFormat anvilConverterForAnvilFile;
    074
    075        /** The PlayerUsageSnooper instance. */
    076        private final PlayerUsageSnooper usageSnooper = new PlayerUsageSnooper("server", this);
    077        private final File anvilFile;
    078
    079        /** List of names of players who are online. */
    080        private final List playersOnline = new ArrayList();
    081        private final ICommandManager commandManager;
    082        public final Profiler theProfiler = new Profiler();
    083
    084        /** The server's hostname. */
    085        private String hostname;
    086
    087        /** The server's port. */
    088        private int serverPort = -1;
    089
    090        /** The server world instances. */
    091        public WorldServer[] worldServers;
    092
    093        /** The ServerConfigurationManager instance. */
    094        private ServerConfigurationManager serverConfigManager;
    095
    096        /**
    097         * Indicates whether the server is running or not. Set to false to initiate a shutdown.
    098         */
    099        private boolean serverRunning = true;
    100
    101        /** Indicates to other classes that the server is safely stopped. */
    102        private boolean serverStopped = false;
    103
    104        /** Incremented every tick. */
    105        private int tickCounter = 0;
    106
    107        /**
    108         * The task the server is currently working on(and will output on outputPercentRemaining).
    109         */
    110        public String currentTask;
    111
    112        /** The percentage of the current task finished so far. */
    113        public int percentDone;
    114
    115        /** True if the server is in online mode. */
    116        private boolean onlineMode;
    117
    118        /** True if the server has animals turned on. */
    119        private boolean canSpawnAnimals;
    120        private boolean canSpawnNPCs;
    121
    122        /** Indicates whether PvP is active on the server or not. */
    123        private boolean pvpEnabled;
    124
    125        /** Determines if flight is allowed or not. */
    126        private boolean allowFlight;
    127
    128        /** The server MOTD string. */
    129        private String motd;
    130
    131        /** Maximum build height. */
    132        private int buildLimit;
    133        private long lastSentPacketID;
    134        private long lastSentPacketSize;
    135        private long lastReceivedID;
    136        private long lastReceivedSize;
    137        public final long[] sentPacketCountArray = new long[100];
    138        public final long[] sentPacketSizeArray = new long[100];
    139        public final long[] receivedPacketCountArray = new long[100];
    140        public final long[] receivedPacketSizeArray = new long[100];
    141        public final long[] tickTimeArray = new long[100];
    142
    143        /** Stats are [dimension][tick%100] system.nanoTime is stored. */
    144        //public long[][] timeOfLastDimensionTick;
    145        public Hashtable<Integer, long[]> worldTickTimes = new Hashtable<Integer, long[]>();
    146        public int spawnProtectionSize = 16;
    147        private KeyPair serverKeyPair;
    148
    149        /** Username of the server owner (for integrated servers) */
    150        private String serverOwner;
    151        private String folderName;
    152        @SideOnly(Side.CLIENT)
    153        private String worldName;
    154        private boolean isDemo;
    155        private boolean enableBonusChest;
    156
    157        /**
    158         * If true, there is no need to save chunks or stop the server, because that is already being done.
    159         */
    160        private boolean worldIsBeingDeleted;
    161        private String texturePack = "";
    162        private boolean serverIsRunning = false;
    163
    164        /**
    165         * Set when warned for "Can't keep up", which triggers again after 15 seconds.
    166         */
    167        private long timeOfLastWarning;
    168        private String userMessage;
    169        private boolean startProfiling;
    170
    171        public MinecraftServer(File par1File)
    172        {
    173            mcServer = this;
    174            this.anvilFile = par1File;
    175            this.commandManager = new ServerCommandManager();
    176            this.anvilConverterForAnvilFile = new AnvilSaveConverter(par1File);
    177        }
    178
    179        /**
    180         * Initialises the server and starts it.
    181         */
    182        protected abstract boolean startServer() throws IOException;
    183
    184        protected void convertMapIfNeeded(String par1Str)
    185        {
    186            if (this.getActiveAnvilConverter().isOldMapFormat(par1Str))
    187            {
    188                logger.info("Converting map!");
    189                this.setUserMessage("menu.convertingLevel");
    190                this.getActiveAnvilConverter().convertMapFormat(par1Str, new ConvertProgressUpdater(this));
    191            }
    192        }
    193
    194        /**
    195         * Typically "menu.convertingLevel", "menu.loadingLevel" or others.
    196         */
    197        protected synchronized void setUserMessage(String par1Str)
    198        {
    199            this.userMessage = par1Str;
    200        }
    201
    202        @SideOnly(Side.CLIENT)
    203
    204        public synchronized String getUserMessage()
    205        {
    206            return this.userMessage;
    207        }
    208
    209        protected void loadAllWorlds(String par1Str, String par2Str, long par3, WorldType par5WorldType)
    210        {
    211            this.convertMapIfNeeded(par1Str);
    212            this.setUserMessage("menu.loadingLevel");
    213            ISaveHandler var6 = this.anvilConverterForAnvilFile.getSaveLoader(par1Str, true);
    214            WorldInfo var8 = var6.loadWorldInfo();
    215            WorldSettings var7;
    216
    217            if (var8 == null)
    218            {
    219                var7 = new WorldSettings(par3, this.getGameType(), this.canStructuresSpawn(), this.isHardcore(), par5WorldType);
    220            }
    221            else
    222            {
    223                var7 = new WorldSettings(var8);
    224            }
    225
    226            if (this.enableBonusChest)
    227            {
    228                var7.enableBonusChest();
    229            }
    230
    231            WorldServer overWorld = (isDemo() ? new DemoWorldServer(this, var6, par2Str, 0, theProfiler) : new WorldServer(this, var6, par2Str, 0, var7, theProfiler));
    232            for (int dim : DimensionManager.getStaticDimensionIDs())
    233            {
    234                WorldServer world = (dim == 0 ? overWorld : new WorldServerMulti(this, var6, par2Str, dim, var7, overWorld, theProfiler));
    235                world.addWorldAccess(new WorldManager(this, world));
    236
    237                if (!this.isSinglePlayer())
    238                {
    239                    world.getWorldInfo().setGameType(this.getGameType());
    240                }
    241
    242                this.serverConfigManager.setPlayerManager(this.worldServers);
    243                MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(world));
    244            }
    245
    246            this.serverConfigManager.setPlayerManager(new WorldServer[]{ overWorld });
    247            this.setDifficultyForAllWorlds(this.getDifficulty());
    248            this.initialWorldChunkLoad();
    249        }
    250
    251        protected void initialWorldChunkLoad()
    252        {
    253            short var1 = 196;
    254            long var2 = System.currentTimeMillis();
    255            this.setUserMessage("menu.generatingTerrain");
    256
    257            for (int var4 = 0; var4 < 1; ++var4)
    258            {
    259                logger.info("Preparing start region for level " + var4);
    260                WorldServer var5 = this.worldServers[var4];
    261                ChunkCoordinates var6 = var5.getSpawnPoint();
    262
    263                for (int var7 = -var1; var7 <= var1 && this.isServerRunning(); var7 += 16)
    264                {
    265                    for (int var8 = -var1; var8 <= var1 && this.isServerRunning(); var8 += 16)
    266                    {
    267                        long var9 = System.currentTimeMillis();
    268
    269                        if (var9 < var2)
    270                        {
    271                            var2 = var9;
    272                        }
    273
    274                        if (var9 > var2 + 1000L)
    275                        {
    276                            int var11 = (var1 * 2 + 1) * (var1 * 2 + 1);
    277                            int var12 = (var7 + var1) * (var1 * 2 + 1) + var8 + 1;
    278                            this.outputPercentRemaining("Preparing spawn area", var12 * 100 / var11);
    279                            var2 = var9;
    280                        }
    281
    282                        var5.theChunkProviderServer.loadChunk(var6.posX + var7 >> 4, var6.posZ + var8 >> 4);
    283
    284                        while (var5.updatingLighting() && this.isServerRunning())
    285                        {
    286                            ;
    287                        }
    288                    }
    289                }
    290            }
    291
    292            this.clearCurrentTask();
    293        }
    294
    295        public abstract boolean canStructuresSpawn();
    296
    297        public abstract EnumGameType getGameType();
    298
    299        /**
    300         * Defaults to "1" (Easy) for the dedicated server, defaults to "2" (Normal) on the client.
    301         */
    302        public abstract int getDifficulty();
    303
    304        /**
    305         * Defaults to false.
    306         */
    307        public abstract boolean isHardcore();
    308
    309        /**
    310         * Used to display a percent remaining given text and the percentage.
    311         */
    312        protected void outputPercentRemaining(String par1Str, int par2)
    313        {
    314            this.currentTask = par1Str;
    315            this.percentDone = par2;
    316            logger.info(par1Str + ": " + par2 + "%");
    317        }
    318
    319        /**
    320         * Set current task to null and set its percentage to 0.
    321         */
    322        protected void clearCurrentTask()
    323        {
    324            this.currentTask = null;
    325            this.percentDone = 0;
    326        }
    327
    328        /**
    329         * par1 indicates if a log message should be output.
    330         */
    331        protected void saveAllWorlds(boolean par1)
    332        {
    333            if (!this.worldIsBeingDeleted)
    334            {
    335                WorldServer[] var2 = this.worldServers;
    336                int var3 = var2.length;
    337
    338                for (int var4 = 0; var4 < var3; ++var4)
    339                {
    340                    WorldServer var5 = var2[var4];
    341
    342                    if (var5 != null)
    343                    {
    344                        if (!par1)
    345                        {
    346                            logger.info("Saving chunks for level \'" + var5.getWorldInfo().getWorldName() + "\'/" + var5.provider.getDimensionName());
    347                        }
    348
    349                        try
    350                        {
    351                            var5.saveAllChunks(true, (IProgressUpdate)null);
    352                        }
    353                        catch (MinecraftException var7)
    354                        {
    355                            logger.warning(var7.getMessage());
    356                        }
    357                    }
    358                }
    359            }
    360        }
    361
    362        /**
    363         * Saves all necessary data as preparation for stopping the server.
    364         */
    365        public void stopServer()
    366        {
    367            if (!this.worldIsBeingDeleted)
    368            {
    369                logger.info("Stopping server");
    370
    371                if (this.getNetworkThread() != null)
    372                {
    373                    this.getNetworkThread().stopListening();
    374                }
    375
    376                if (this.serverConfigManager != null)
    377                {
    378                    logger.info("Saving players");
    379                    this.serverConfigManager.saveAllPlayerData();
    380                    this.serverConfigManager.removeAllPlayers();
    381                }
    382
    383                logger.info("Saving worlds");
    384                this.saveAllWorlds(false);
    385                WorldServer[] var1 = this.worldServers;
    386                int var2 = var1.length;
    387
    388                for (int var3 = 0; var3 < var2; ++var3)
    389                {
    390                    WorldServer var4 = var1[var3];
    391                    MinecraftForge.EVENT_BUS.post(new WorldEvent.Unload(var4));
    392                    var4.flush();
    393                    DimensionManager.setWorld(var4.provider.dimensionId, null);
    394                }
    395
    396                if (this.usageSnooper != null && this.usageSnooper.isSnooperRunning())
    397                {
    398                    this.usageSnooper.stopSnooper();
    399                }
    400            }
    401        }
    402
    403        /**
    404         * "getHostname" is already taken, but both return the hostname.
    405         */
    406        public String getServerHostname()
    407        {
    408            return this.hostname;
    409        }
    410
    411        public void setHostname(String par1Str)
    412        {
    413            this.hostname = par1Str;
    414        }
    415
    416        public boolean isServerRunning()
    417        {
    418            return this.serverRunning;
    419        }
    420
    421        /**
    422         * Sets the serverRunning variable to false, in order to get the server to shut down.
    423         */
    424        public void initiateShutdown()
    425        {
    426            this.serverRunning = false;
    427        }
    428
    429        public void run()
    430        {
    431            try
    432            {
    433                if (this.startServer())
    434                {
    435                    FMLCommonHandler.instance().handleServerStarted();
    436                    long var1 = System.currentTimeMillis();
    437
    438                    FMLCommonHandler.instance().onWorldLoadTick(worldServers);
    439
    440                    for (long var50 = 0L; this.serverRunning; this.serverIsRunning = true)
    441                    {
    442                        long var5 = System.currentTimeMillis();
    443                        long var7 = var5 - var1;
    444
    445                        if (var7 > 2000L && var1 - this.timeOfLastWarning >= 15000L)
    446                        {
    447                            logger.warning("Can\'t keep up! Did the system time change, or is the server overloaded?");
    448                            var7 = 2000L;
    449                            this.timeOfLastWarning = var1;
    450                        }
    451
    452                        if (var7 < 0L)
    453                        {
    454                            logger.warning("Time ran backwards! Did the system time change?");
    455                            var7 = 0L;
    456                        }
    457
    458                        var50 += var7;
    459                        var1 = var5;
    460
    461                        if (this.worldServers[0].areAllPlayersAsleep())
    462                        {
    463                            this.tick();
    464                            var50 = 0L;
    465                        }
    466                        else
    467                        {
    468                            while (var50 > 50L)
    469                            {
    470                                var50 -= 50L;
    471                                this.tick();
    472                            }
    473                        }
    474
    475                        Thread.sleep(1L);
    476                    }
    477                    FMLCommonHandler.instance().handleServerStopping();
    478                }
    479                else
    480                {
    481                    this.finalTick((CrashReport)null);
    482                }
    483            }
    484            catch (Throwable var48)
    485            {
    486                var48.printStackTrace();
    487                logger.log(Level.SEVERE, "Encountered an unexpected exception " + var48.getClass().getSimpleName(), var48);
    488                CrashReport var2 = null;
    489
    490                if (var48 instanceof ReportedException)
    491                {
    492                    var2 = this.addServerInfoToCrashReport(((ReportedException)var48).getTheReportedExceptionCrashReport());
    493                }
    494                else
    495                {
    496                    var2 = this.addServerInfoToCrashReport(new CrashReport("Exception in server tick loop", var48));
    497                }
    498
    499                File var3 = new File(new File(this.getDataDirectory(), "crash-reports"), "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-server.txt");
    500
    501                if (var2.saveToFile(var3))
    502                {
    503                    logger.severe("This crash report has been saved to: " + var3.getAbsolutePath());
    504                }
    505                else
    506                {
    507                    logger.severe("We were unable to save this crash report to disk.");
    508                }
    509
    510                this.finalTick(var2);
    511            }
    512            finally
    513            {
    514                try
    515                {
    516                    this.stopServer();
    517                    this.serverStopped = true;
    518                }
    519                catch (Throwable var46)
    520                {
    521                    var46.printStackTrace();
    522                }
    523                finally
    524                {
    525                    this.systemExitNow();
    526                }
    527            }
    528        }
    529
    530        protected File getDataDirectory()
    531        {
    532            return new File(".");
    533        }
    534
    535        /**
    536         * Called on exit from the main run() loop.
    537         */
    538        protected void finalTick(CrashReport par1CrashReport) {}
    539
    540        /**
    541         * Directly calls System.exit(0), instantly killing the program.
    542         */
    543        protected void systemExitNow() {}
    544
    545        /**
    546         * Main function called by run() every loop.
    547         */
    548        public void tick()
    549        {
    550            FMLCommonHandler.instance().rescheduleTicks(Side.SERVER);
    551            long var1 = System.nanoTime();
    552            AxisAlignedBB.getAABBPool().cleanPool();
    553            Vec3.getVec3Pool().clear();
    554            FMLCommonHandler.instance().onPreServerTick();
    555            ++this.tickCounter;
    556
    557            if (this.startProfiling)
    558            {
    559                this.startProfiling = false;
    560                this.theProfiler.profilingEnabled = true;
    561                this.theProfiler.clearProfiling();
    562            }
    563
    564            this.theProfiler.startSection("root");
    565            this.updateTimeLightAndEntities();
    566
    567            if (this.tickCounter % 900 == 0)
    568            {
    569                this.theProfiler.startSection("save");
    570                this.serverConfigManager.saveAllPlayerData();
    571                this.saveAllWorlds(true);
    572                this.theProfiler.endSection();
    573            }
    574
    575            this.theProfiler.startSection("tallying");
    576            this.tickTimeArray[this.tickCounter % 100] = System.nanoTime() - var1;
    577            this.sentPacketCountArray[this.tickCounter % 100] = Packet.sentID - this.lastSentPacketID;
    578            this.lastSentPacketID = Packet.sentID;
    579            this.sentPacketSizeArray[this.tickCounter % 100] = Packet.sentSize - this.lastSentPacketSize;
    580            this.lastSentPacketSize = Packet.sentSize;
    581            this.receivedPacketCountArray[this.tickCounter % 100] = Packet.receivedID - this.lastReceivedID;
    582            this.lastReceivedID = Packet.receivedID;
    583            this.receivedPacketSizeArray[this.tickCounter % 100] = Packet.receivedSize - this.lastReceivedSize;
    584            this.lastReceivedSize = Packet.receivedSize;
    585            this.theProfiler.endSection();
    586            this.theProfiler.startSection("snooper");
    587
    588            if (!this.usageSnooper.isSnooperRunning() && this.tickCounter > 100)
    589            {
    590                this.usageSnooper.startSnooper();
    591            }
    592
    593            if (this.tickCounter % 6000 == 0)
    594            {
    595                this.usageSnooper.addMemoryStatsToSnooper();
    596            }
    597
    598            this.theProfiler.endSection();
    599            this.theProfiler.endSection();
    600            FMLCommonHandler.instance().onPostServerTick();
    601        }
    602
    603        public void updateTimeLightAndEntities()
    604        {
    605            this.theProfiler.startSection("levels");
    606
    607            for (Integer id : DimensionManager.getIDs())
    608            {
    609                long var2 = System.nanoTime();
    610
    611                if (id == 0 || this.getAllowNether())
    612                {
    613                    WorldServer var4 = DimensionManager.getWorld(id);
    614                    this.theProfiler.startSection(var4.getWorldInfo().getWorldName());
    615
    616                    if (this.tickCounter % 20 == 0)
    617                    {
    618                        this.theProfiler.startSection("timeSync");
    619                        this.serverConfigManager.sendPacketToAllPlayersInDimension(new Packet4UpdateTime(var4.getWorldTime()), var4.provider.dimensionId);
    620                        this.theProfiler.endSection();
    621                    }
    622
    623                    this.theProfiler.startSection("tick");
    624                    FMLCommonHandler.instance().onPreWorldTick(var4);
    625                    var4.tick();
    626                    FMLCommonHandler.instance().onPostWorldTick(var4);
    627                    this.theProfiler.endStartSection("lights");
    628
    629                    while (true)
    630                    {
    631                        if (!var4.updatingLighting())
    632                        {
    633                            this.theProfiler.endSection();
    634                            var4.updateEntities();
    635                            this.theProfiler.startSection("tracker");
    636                            var4.getEntityTracker().updateTrackedEntities();
    637                            this.theProfiler.endSection();
    638                            this.theProfiler.endSection();
    639                            break;
    640                        }
    641                    }
    642                }
    643
    644                worldTickTimes.get(id)[this.tickCounter % 100] = System.nanoTime() - var2;
    645            }
    646
    647            this.theProfiler.endStartSection("dim_unloading");
    648            DimensionManager.unloadWorlds(worldTickTimes);
    649            this.theProfiler.endStartSection("connection");
    650            this.getNetworkThread().networkTick();
    651            this.theProfiler.endStartSection("players");
    652            this.serverConfigManager.sendPlayerInfoToAllPlayers();
    653            this.theProfiler.endStartSection("tickables");
    654            Iterator var5 = this.playersOnline.iterator();
    655
    656            while (var5.hasNext())
    657            {
    658                IUpdatePlayerListBox var6 = (IUpdatePlayerListBox)var5.next();
    659                var6.update();
    660            }
    661
    662            this.theProfiler.endSection();
    663        }
    664
    665        public boolean getAllowNether()
    666        {
    667            return true;
    668        }
    669
    670        public void startServerThread()
    671        {
    672            (new ThreadServerApplication(this, "Server thread")).start();
    673        }
    674
    675        /**
    676         * Returns a File object from the specified string.
    677         */
    678        public File getFile(String par1Str)
    679        {
    680            return new File(this.getDataDirectory(), par1Str);
    681        }
    682
    683        /**
    684         * Logs the message with a level of INFO.
    685         */
    686        public void logInfo(String par1Str)
    687        {
    688            logger.info(par1Str);
    689        }
    690
    691        /**
    692         * Logs the message with a level of WARN.
    693         */
    694        public void logWarning(String par1Str)
    695        {
    696            logger.warning(par1Str);
    697        }
    698
    699        /**
    700         * Gets the worldServer by the given dimension.
    701         */
    702        public WorldServer worldServerForDimension(int par1)
    703        {
    704            WorldServer ret = DimensionManager.getWorld(par1);
    705            if (ret == null) {
    706                DimensionManager.initDimension(par1);
    707                ret = DimensionManager.getWorld(par1);
    708            }
    709            return ret;
    710        }
    711
    712        @SideOnly(Side.SERVER)
    713
    714        /**
    715         * Adds a player's name to the list of online players.
    716         */
    717        public void addToOnlinePlayerList(IUpdatePlayerListBox par1IUpdatePlayerListBox)
    718        {
    719            this.playersOnline.add(par1IUpdatePlayerListBox);
    720        }
    721
    722        /**
    723         * Returns the server's hostname.
    724         */
    725        public String getHostname()
    726        {
    727            return this.hostname;
    728        }
    729
    730        /**
    731         * Never used, but "getServerPort" is already taken.
    732         */
    733        public int getPort()
    734        {
    735            return this.serverPort;
    736        }
    737
    738        /**
    739         * minecraftServer.getMOTD is used in 2 places instead (it is a non-virtual function which returns the same thing)
    740         */
    741        public String getServerMOTD()
    742        {
    743            return this.motd;
    744        }
    745
    746        /**
    747         * Returns the server's Minecraft version as string.
    748         */
    749        public String getMinecraftVersion()
    750        {
    751            return "1.3.2";
    752        }
    753
    754        /**
    755         * Returns the number of players currently on the server.
    756         */
    757        public int getCurrentPlayerCount()
    758        {
    759            return this.serverConfigManager.getCurrentPlayerCount();
    760        }
    761
    762        /**
    763         * Returns the maximum number of players allowed on the server.
    764         */
    765        public int getMaxPlayers()
    766        {
    767            return this.serverConfigManager.getMaxPlayers();
    768        }
    769
    770        /**
    771         * Returns an array of the usernames of all the connected players.
    772         */
    773        public String[] getAllUsernames()
    774        {
    775            return this.serverConfigManager.getAllUsernames();
    776        }
    777
    778        /**
    779         * Used by RCon's Query in the form of "MajorServerMod 1.2.3: MyPlugin 1.3; AnotherPlugin 2.1; AndSoForth 1.0".
    780         */
    781        public String getPlugins()
    782        {
    783            return "";
    784        }
    785
    786        public String executeCommand(String par1Str)
    787        {
    788            RConConsoleSource.consoleBuffer.resetLog();
    789            this.commandManager.executeCommand(RConConsoleSource.consoleBuffer, par1Str);
    790            return RConConsoleSource.consoleBuffer.getChatBuffer();
    791        }
    792
    793        /**
    794         * Returns true if debugging is enabled, false otherwise.
    795         */
    796        public boolean isDebuggingEnabled()
    797        {
    798            return false;
    799        }
    800
    801        /**
    802         * Logs the error message with a level of SEVERE.
    803         */
    804        public void logSevere(String par1Str)
    805        {
    806            logger.log(Level.SEVERE, par1Str);
    807        }
    808
    809        /**
    810         * If isDebuggingEnabled(), logs the message with a level of INFO.
    811         */
    812        public void logDebug(String par1Str)
    813        {
    814            if (this.isDebuggingEnabled())
    815            {
    816                logger.log(Level.INFO, par1Str);
    817            }
    818        }
    819
    820        public String getServerModName()
    821        {
    822            return "forge,fml";
    823        }
    824
    825        /**
    826         * Adds the server info, including from theWorldServer, to the crash report.
    827         */
    828        public CrashReport addServerInfoToCrashReport(CrashReport par1CrashReport)
    829        {
    830            par1CrashReport.addCrashSectionCallable("Is Modded", new CallableIsServerModded(this));
    831            par1CrashReport.addCrashSectionCallable("Profiler Position", new CallableServerProfiler(this));
    832
    833            if (this.serverConfigManager != null)
    834            {
    835                par1CrashReport.addCrashSectionCallable("Player Count", new CallablePlayers(this));
    836            }
    837
    838            if (this.worldServers != null)
    839            {
    840                WorldServer[] var2 = this.worldServers;
    841                int var3 = var2.length;
    842
    843                for (int var4 = 0; var4 < var3; ++var4)
    844                {
    845                    WorldServer var5 = var2[var4];
    846
    847                    if (var5 != null)
    848                    {
    849                        var5.addWorldInfoToCrashReport(par1CrashReport);
    850                    }
    851                }
    852            }
    853
    854            return par1CrashReport;
    855        }
    856
    857        /**
    858         * If par2Str begins with /, then it searches for commands, otherwise it returns players.
    859         */
    860        public List getPossibleCompletions(ICommandSender par1ICommandSender, String par2Str)
    861        {
    862            ArrayList var3 = new ArrayList();
    863
    864            if (par2Str.startsWith("/"))
    865            {
    866                par2Str = par2Str.substring(1);
    867                boolean var10 = !par2Str.contains(" ");
    868                List var11 = this.commandManager.getPossibleCommands(par1ICommandSender, par2Str);
    869
    870                if (var11 != null)
    871                {
    872                    Iterator var12 = var11.iterator();
    873
    874                    while (var12.hasNext())
    875                    {
    876                        String var13 = (String)var12.next();
    877
    878                        if (var10)
    879                        {
    880                            var3.add("/" + var13);
    881                        }
    882                        else
    883                        {
    884                            var3.add(var13);
    885                        }
    886                    }
    887                }
    888
    889                return var3;
    890            }
    891            else
    892            {
    893                String[] var4 = par2Str.split(" ", -1);
    894                String var5 = var4[var4.length - 1];
    895                String[] var6 = this.serverConfigManager.getAllUsernames();
    896                int var7 = var6.length;
    897
    898                for (int var8 = 0; var8 < var7; ++var8)
    899                {
    900                    String var9 = var6[var8];
    901
    902                    if (CommandBase.doesStringStartWith(var5, var9))
    903                    {
    904                        var3.add(var9);
    905                    }
    906                }
    907
    908                return var3;
    909            }
    910        }
    911
    912        /**
    913         * Gets mcServer.
    914         */
    915        public static MinecraftServer getServer()
    916        {
    917            return mcServer;
    918        }
    919
    920        /**
    921         * Gets the name of this command sender (usually username, but possibly "Rcon")
    922         */
    923        public String getCommandSenderName()
    924        {
    925            return "Server";
    926        }
    927
    928        public void sendChatToPlayer(String par1Str)
    929        {
    930            logger.info(StringUtils.stripControlCodes(par1Str));
    931        }
    932
    933        /**
    934         * Returns true if the command sender is allowed to use the given command.
    935         */
    936        public boolean canCommandSenderUseCommand(String par1Str)
    937        {
    938            return true;
    939        }
    940
    941        /**
    942         * Translates and formats the given string key with the given arguments.
    943         */
    944        public String translateString(String par1Str, Object ... par2ArrayOfObj)
    945        {
    946            return StringTranslate.getInstance().translateKeyFormat(par1Str, par2ArrayOfObj);
    947        }
    948
    949        public ICommandManager getCommandManager()
    950        {
    951            return this.commandManager;
    952        }
    953
    954        /**
    955         * Gets KeyPair instanced in MinecraftServer.
    956         */
    957        public KeyPair getKeyPair()
    958        {
    959            return this.serverKeyPair;
    960        }
    961
    962        /**
    963         * Gets serverPort.
    964         */
    965        public int getServerPort()
    966        {
    967            return this.serverPort;
    968        }
    969
    970        public void setServerPort(int par1)
    971        {
    972            this.serverPort = par1;
    973        }
    974
    975        /**
    976         * Returns the username of the server owner (for integrated servers)
    977         */
    978        public String getServerOwner()
    979        {
    980            return this.serverOwner;
    981        }
    982
    983        /**
    984         * Sets the username of the owner of this server (in the case of an integrated server)
    985         */
    986        public void setServerOwner(String par1Str)
    987        {
    988            this.serverOwner = par1Str;
    989        }
    990
    991        public boolean isSinglePlayer()
    992        {
    993            return this.serverOwner != null;
    994        }
    995
    996        public String getFolderName()
    997        {
    998            return this.folderName;
    999        }
    1000
    1001        public void setFolderName(String par1Str)
    1002        {
    1003            this.folderName = par1Str;
    1004        }
    1005
    1006        @SideOnly(Side.CLIENT)
    1007        public void setWorldName(String par1Str)
    1008        {
    1009            this.worldName = par1Str;
    1010        }
    1011
    1012        @SideOnly(Side.CLIENT)
    1013        public String getWorldName()
    1014        {
    1015            return this.worldName;
    1016        }
    1017
    1018        public void setKeyPair(KeyPair par1KeyPair)
    1019        {
    1020            this.serverKeyPair = par1KeyPair;
    1021        }
    1022
    1023        public void setDifficultyForAllWorlds(int par1)
    1024        {
    1025            for (int var2 = 0; var2 < this.worldServers.length; ++var2)
    1026            {
    1027                WorldServer var3 = this.worldServers[var2];
    1028
    1029                if (var3 != null)
    1030                {
    1031                    if (var3.getWorldInfo().isHardcoreModeEnabled())
    1032                    {
    1033                        var3.difficultySetting = 3;
    1034                        var3.setAllowedSpawnTypes(true, true);
    1035                    }
    1036                    else if (this.isSinglePlayer())
    1037                    {
    1038                        var3.difficultySetting = par1;
    1039                        var3.setAllowedSpawnTypes(var3.difficultySetting > 0, true);
    1040                    }
    1041                    else
    1042                    {
    1043                        var3.difficultySetting = par1;
    1044                        var3.setAllowedSpawnTypes(this.allowSpawnMonsters(), this.canSpawnAnimals);
    1045                    }
    1046                }
    1047            }
    1048        }
    1049
    1050        protected boolean allowSpawnMonsters()
    1051        {
    1052            return true;
    1053        }
    1054
    1055        /**
    1056         * Gets whether this is a demo or not.
    1057         */
    1058        public boolean isDemo()
    1059        {
    1060            return this.isDemo;
    1061        }
    1062
    1063        /**
    1064         * Sets whether this is a demo or not.
    1065         */
    1066        public void setDemo(boolean par1)
    1067        {
    1068            this.isDemo = par1;
    1069        }
    1070
    1071        public void canCreateBonusChest(boolean par1)
    1072        {
    1073            this.enableBonusChest = par1;
    1074        }
    1075
    1076        public ISaveFormat getActiveAnvilConverter()
    1077        {
    1078            return this.anvilConverterForAnvilFile;
    1079        }
    1080
    1081        /**
    1082         * WARNING : directly calls
    1083         * getActiveAnvilConverter().deleteWorldDirectory(theWorldServer[0].getSaveHandler().getSaveDirectoryName());
    1084         */
    1085        public void deleteWorldAndStopServer()
    1086        {
    1087            this.worldIsBeingDeleted = true;
    1088            this.getActiveAnvilConverter().flushCache();
    1089
    1090            for (int var1 = 0; var1 < this.worldServers.length; ++var1)
    1091            {
    1092                WorldServer var2 = this.worldServers[var1];
    1093
    1094                if (var2 != null)
    1095                {
    1096                    MinecraftForge.EVENT_BUS.post(new WorldEvent.Unload(var2));
    1097                    var2.flush();
    1098                }
    1099            }
    1100
    1101            this.getActiveAnvilConverter().deleteWorldDirectory(this.worldServers[0].getSaveHandler().getSaveDirectoryName());
    1102            this.initiateShutdown();
    1103        }
    1104
    1105        public String getTexturePack()
    1106        {
    1107            return this.texturePack;
    1108        }
    1109
    1110        public void setTexturePack(String par1Str)
    1111        {
    1112            this.texturePack = par1Str;
    1113        }
    1114
    1115        public void addServerStatsToSnooper(PlayerUsageSnooper par1PlayerUsageSnooper)
    1116        {
    1117            par1PlayerUsageSnooper.addData("whitelist_enabled", Boolean.valueOf(false));
    1118            par1PlayerUsageSnooper.addData("whitelist_count", Integer.valueOf(0));
    1119            par1PlayerUsageSnooper.addData("players_current", Integer.valueOf(this.getCurrentPlayerCount()));
    1120            par1PlayerUsageSnooper.addData("players_max", Integer.valueOf(this.getMaxPlayers()));
    1121            par1PlayerUsageSnooper.addData("players_seen", Integer.valueOf(this.serverConfigManager.getAvailablePlayerDat().length));
    1122            par1PlayerUsageSnooper.addData("uses_auth", Boolean.valueOf(this.onlineMode));
    1123            par1PlayerUsageSnooper.addData("gui_state", this.getGuiEnabled() ? "enabled" : "disabled");
    1124            par1PlayerUsageSnooper.addData("avg_tick_ms", Integer.valueOf((int)(MathHelper.average(this.tickTimeArray) * 1.0E-6D)));
    1125            par1PlayerUsageSnooper.addData("avg_sent_packet_count", Integer.valueOf((int)MathHelper.average(this.sentPacketCountArray)));
    1126            par1PlayerUsageSnooper.addData("avg_sent_packet_size", Integer.valueOf((int)MathHelper.average(this.sentPacketSizeArray)));
    1127            par1PlayerUsageSnooper.addData("avg_rec_packet_count", Integer.valueOf((int)MathHelper.average(this.receivedPacketCountArray)));
    1128            par1PlayerUsageSnooper.addData("avg_rec_packet_size", Integer.valueOf((int)MathHelper.average(this.receivedPacketSizeArray)));
    1129            int var2 = 0;
    1130
    1131            for (int var3 = 0; var3 < this.worldServers.length; ++var3)
    1132            {
    1133                if (this.worldServers[var3] != null)
    1134                {
    1135                    WorldServer var4 = this.worldServers[var3];
    1136                    WorldInfo var5 = var4.getWorldInfo();
    1137                    par1PlayerUsageSnooper.addData("world[" + var2 + "][dimension]", Integer.valueOf(var4.provider.dimensionId));
    1138                    par1PlayerUsageSnooper.addData("world[" + var2 + "][mode]", var5.getGameType());
    1139                    par1PlayerUsageSnooper.addData("world[" + var2 + "][difficulty]", Integer.valueOf(var4.difficultySetting));
    1140                    par1PlayerUsageSnooper.addData("world[" + var2 + "][hardcore]", Boolean.valueOf(var5.isHardcoreModeEnabled()));
    1141                    par1PlayerUsageSnooper.addData("world[" + var2 + "][generator_name]", var5.getTerrainType().getWorldTypeName());
    1142                    par1PlayerUsageSnooper.addData("world[" + var2 + "][generator_version]", Integer.valueOf(var5.getTerrainType().getGeneratorVersion()));
    1143                    par1PlayerUsageSnooper.addData("world[" + var2 + "][height]", Integer.valueOf(this.buildLimit));
    1144                    par1PlayerUsageSnooper.addData("world[" + var2 + "][chunks_loaded]", Integer.valueOf(var4.getChunkProvider().getLoadedChunkCount()));
    1145                    ++var2;
    1146                }
    1147            }
    1148
    1149            par1PlayerUsageSnooper.addData("worlds", Integer.valueOf(var2));
    1150        }
    1151
    1152        public void addServerTypeToSnooper(PlayerUsageSnooper par1PlayerUsageSnooper)
    1153        {
    1154            par1PlayerUsageSnooper.addData("singleplayer", Boolean.valueOf(this.isSinglePlayer()));
    1155            par1PlayerUsageSnooper.addData("server_brand", this.getServerModName());
    1156            par1PlayerUsageSnooper.addData("gui_supported", GraphicsEnvironment.isHeadless() ? "headless" : "supported");
    1157            par1PlayerUsageSnooper.addData("dedicated", Boolean.valueOf(this.isDedicatedServer()));
    1158        }
    1159
    1160        /**
    1161         * Returns whether snooping is enabled or not.
    1162         */
    1163        public boolean isSnooperEnabled()
    1164        {
    1165            return true;
    1166        }
    1167
    1168        /**
    1169         * This is checked to be 16 upon receiving the packet, otherwise the packet is ignored.
    1170         */
    1171        public int textureSize()
    1172        {
    1173            return 16;
    1174        }
    1175
    1176        public abstract boolean isDedicatedServer();
    1177
    1178        public boolean isServerInOnlineMode()
    1179        {
    1180            return this.onlineMode;
    1181        }
    1182
    1183        public void setOnlineMode(boolean par1)
    1184        {
    1185            this.onlineMode = par1;
    1186        }
    1187
    1188        public boolean getCanSpawnAnimals()
    1189        {
    1190            return this.canSpawnAnimals;
    1191        }
    1192
    1193        public void setCanSpawnAnimals(boolean par1)
    1194        {
    1195            this.canSpawnAnimals = par1;
    1196        }
    1197
    1198        public boolean getCanSpawnNPCs()
    1199        {
    1200            return this.canSpawnNPCs;
    1201        }
    1202
    1203        public void setCanSpawnNPCs(boolean par1)
    1204        {
    1205            this.canSpawnNPCs = par1;
    1206        }
    1207
    1208        public boolean isPVPEnabled()
    1209        {
    1210            return this.pvpEnabled;
    1211        }
    1212
    1213        public void setAllowPvp(boolean par1)
    1214        {
    1215            this.pvpEnabled = par1;
    1216        }
    1217
    1218        public boolean isFlightAllowed()
    1219        {
    1220            return this.allowFlight;
    1221        }
    1222
    1223        public void setAllowFlight(boolean par1)
    1224        {
    1225            this.allowFlight = par1;
    1226        }
    1227
    1228        public String getMOTD()
    1229        {
    1230            return this.motd;
    1231        }
    1232
    1233        public void setMOTD(String par1Str)
    1234        {
    1235            this.motd = par1Str;
    1236        }
    1237
    1238        public int getBuildLimit()
    1239        {
    1240            return this.buildLimit;
    1241        }
    1242
    1243        public void setBuildLimit(int par1)
    1244        {
    1245            this.buildLimit = par1;
    1246        }
    1247
    1248        public boolean isServerStopped()
    1249        {
    1250            return this.serverStopped;
    1251        }
    1252
    1253        public ServerConfigurationManager getConfigurationManager()
    1254        {
    1255            return this.serverConfigManager;
    1256        }
    1257
    1258        public void setConfigurationManager(ServerConfigurationManager par1ServerConfigurationManager)
    1259        {
    1260            this.serverConfigManager = par1ServerConfigurationManager;
    1261        }
    1262
    1263        /**
    1264         * Sets the game type for all worlds.
    1265         */
    1266        public void setGameType(EnumGameType par1EnumGameType)
    1267        {
    1268            for (int var2 = 0; var2 < this.worldServers.length; ++var2)
    1269            {
    1270                getServer().worldServers[var2].getWorldInfo().setGameType(par1EnumGameType);
    1271            }
    1272        }
    1273
    1274        public abstract NetworkListenThread getNetworkThread();
    1275
    1276        @SideOnly(Side.CLIENT)
    1277        public boolean serverIsInRunLoop()
    1278        {
    1279            return this.serverIsRunning;
    1280        }
    1281
    1282        public boolean getGuiEnabled()
    1283        {
    1284            return false;
    1285        }
    1286
    1287        /**
    1288         * On dedicated does nothing. On integrated, sets commandsAllowedForAll, gameType and allows external connections.
    1289         */
    1290        public abstract String shareToLAN(EnumGameType var1, boolean var2);
    1291
    1292        public int getTickCounter()
    1293        {
    1294            return this.tickCounter;
    1295        }
    1296
    1297        public void enableProfiling()
    1298        {
    1299            this.startProfiling = true;
    1300        }
    1301
    1302        @SideOnly(Side.CLIENT)
    1303        public PlayerUsageSnooper getPlayerUsageSnooper()
    1304        {
    1305            return this.usageSnooper;
    1306        }
    1307
    1308        /**
    1309         * Gets the current player count, maximum player count, and player entity list.
    1310         */
    1311        public static ServerConfigurationManager getServerConfigurationManager(MinecraftServer par0MinecraftServer)
    1312        {
    1313            return par0MinecraftServer.serverConfigManager;
    1314        }
    1315
    1316        @SideOnly(Side.SERVER)
    1317        public static void main(String[] par0ArrayOfStr)
    1318        {
    1319            FMLRelauncher.handleServerRelaunch(new ArgsWrapper(par0ArrayOfStr));
    1320        }
    1321        @SideOnly(Side.SERVER)
    1322        public static void fmlReentry(ArgsWrapper wrap)
    1323        {
    1324            String[] par0ArrayOfStr = wrap.args;
    1325            StatList.func_75919_a();
    1326
    1327            try
    1328            {
    1329                boolean var1 = !GraphicsEnvironment.isHeadless();
    1330                String var2 = null;
    1331                String var3 = ".";
    1332                String var4 = null;
    1333                boolean var5 = false;
    1334                boolean var6 = false;
    1335                int var7 = -1;
    1336
    1337                for (int var8 = 0; var8 < par0ArrayOfStr.length; ++var8)
    1338                {
    1339                    String var9 = par0ArrayOfStr[var8];
    1340                    String var10 = var8 == par0ArrayOfStr.length - 1 ? null : par0ArrayOfStr[var8 + 1];
    1341                    boolean var11 = false;
    1342
    1343                    if (!var9.equals("nogui") && !var9.equals("--nogui"))
    1344                    {
    1345                        if (var9.equals("--port") && var10 != null)
    1346                        {
    1347                            var11 = true;
    1348
    1349                            try
    1350                            {
    1351                                var7 = Integer.parseInt(var10);
    1352                            }
    1353                            catch (NumberFormatException var13)
    1354                            {
    1355                                ;
    1356                            }
    1357                        }
    1358                        else if (var9.equals("--singleplayer") && var10 != null)
    1359                        {
    1360                            var11 = true;
    1361                            var2 = var10;
    1362                        }
    1363                        else if (var9.equals("--universe") && var10 != null)
    1364                        {
    1365                            var11 = true;
    1366                            var3 = var10;
    1367                        }
    1368                        else if (var9.equals("--world") && var10 != null)
    1369                        {
    1370                            var11 = true;
    1371                            var4 = var10;
    1372                        }
    1373                        else if (var9.equals("--demo"))
    1374                        {
    1375                            var5 = true;
    1376                        }
    1377                        else if (var9.equals("--bonusChest"))
    1378                        {
    1379                            var6 = true;
    1380                        }
    1381                    }
    1382                    else
    1383                    {
    1384                        var1 = false;
    1385                    }
    1386
    1387                    if (var11)
    1388                    {
    1389                        ++var8;
    1390                    }
    1391                }
    1392
    1393                DedicatedServer var15 = new DedicatedServer(new File(var3));
    1394
    1395                if (var2 != null)
    1396                {
    1397                    var15.setServerOwner(var2);
    1398                }
    1399
    1400                if (var4 != null)
    1401                {
    1402                    var15.setFolderName(var4);
    1403                }
    1404
    1405                if (var7 >= 0)
    1406                {
    1407                    var15.setServerPort(var7);
    1408                }
    1409
    1410                if (var5)
    1411                {
    1412                    var15.setDemo(true);
    1413                }
    1414
    1415                if (var6)
    1416                {
    1417                    var15.canCreateBonusChest(true);
    1418                }
    1419
    1420                if (var1)
    1421                {
    1422                    var15.func_79001_aj();
    1423                }
    1424
    1425                var15.startServerThread();
    1426                Runtime.getRuntime().addShutdownHook(new ThreadDedicatedServer(var15));
    1427            }
    1428            catch (Exception var14)
    1429            {
    1430                logger.log(Level.SEVERE, "Failed to start the minecraft server", var14);
    1431            }
    1432        }
    1433    }