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