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