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