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                }
417    
418                WorldServer[] tmp = worldServers;
419                for (WorldServer world : tmp)
420                {
421                    DimensionManager.setWorld(world.provider.dimensionId, null);
422                }
423    
424                if (this.usageSnooper != null && this.usageSnooper.isSnooperRunning())
425                {
426                    this.usageSnooper.stopSnooper();
427                }
428            }
429        }
430    
431        /**
432         * "getHostname" is already taken, but both return the hostname.
433         */
434        public String getServerHostname()
435        {
436            return this.hostname;
437        }
438    
439        public void setHostname(String par1Str)
440        {
441            this.hostname = par1Str;
442        }
443    
444        public boolean isServerRunning()
445        {
446            return this.serverRunning;
447        }
448    
449        /**
450         * Sets the serverRunning variable to false, in order to get the server to shut down.
451         */
452        public void initiateShutdown()
453        {
454            this.serverRunning = false;
455        }
456    
457        public void run()
458        {
459            try
460            {
461                if (this.startServer())
462                {
463                    FMLCommonHandler.instance().handleServerStarted();
464                    long var1 = System.currentTimeMillis();
465    
466                    FMLCommonHandler.instance().onWorldLoadTick(worldServers);
467    
468                    for (long var50 = 0L; this.serverRunning; this.serverIsRunning = true)
469                    {
470                        long var5 = System.currentTimeMillis();
471                        long var7 = var5 - var1;
472    
473                        if (var7 > 2000L && var1 - this.timeOfLastWarning >= 15000L)
474                        {
475                            logger.warning("Can\'t keep up! Did the system time change, or is the server overloaded?");
476                            var7 = 2000L;
477                            this.timeOfLastWarning = var1;
478                        }
479    
480                        if (var7 < 0L)
481                        {
482                            logger.warning("Time ran backwards! Did the system time change?");
483                            var7 = 0L;
484                        }
485    
486                        var50 += var7;
487                        var1 = var5;
488    
489                        if (this.worldServers[0].areAllPlayersAsleep())
490                        {
491                            this.tick();
492                            var50 = 0L;
493                        }
494                        else
495                        {
496                            while (var50 > 50L)
497                            {
498                                var50 -= 50L;
499                                this.tick();
500                            }
501                        }
502    
503                        Thread.sleep(1L);
504                    }
505                    FMLCommonHandler.instance().handleServerStopping();
506                }
507                else
508                {
509                    this.finalTick((CrashReport)null);
510                }
511            }
512            catch (Throwable var48)
513            {
514                var48.printStackTrace();
515                logger.log(Level.SEVERE, "Encountered an unexpected exception " + var48.getClass().getSimpleName(), var48);
516                CrashReport var2 = null;
517    
518                if (var48 instanceof ReportedException)
519                {
520                    var2 = this.addServerInfoToCrashReport(((ReportedException)var48).getCrashReport());
521                }
522                else
523                {
524                    var2 = this.addServerInfoToCrashReport(new CrashReport("Exception in server tick loop", var48));
525                }
526    
527                File var3 = new File(new File(this.getDataDirectory(), "crash-reports"), "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-server.txt");
528    
529                if (var2.saveToFile(var3))
530                {
531                    logger.severe("This crash report has been saved to: " + var3.getAbsolutePath());
532                }
533                else
534                {
535                    logger.severe("We were unable to save this crash report to disk.");
536                }
537    
538                this.finalTick(var2);
539            }
540            finally
541            {
542                try
543                {
544                    this.stopServer();
545                    this.serverStopped = true;
546                }
547                catch (Throwable var46)
548                {
549                    var46.printStackTrace();
550                }
551                finally
552                {
553                    this.systemExitNow();
554                }
555            }
556        }
557    
558        protected File getDataDirectory()
559        {
560            return new File(".");
561        }
562    
563        /**
564         * Called on exit from the main run() loop.
565         */
566        protected void finalTick(CrashReport par1CrashReport) {}
567    
568        /**
569         * Directly calls System.exit(0), instantly killing the program.
570         */
571        protected void systemExitNow() {}
572    
573        /**
574         * Main function called by run() every loop.
575         */
576        public void tick()
577        {
578            FMLCommonHandler.instance().rescheduleTicks(Side.SERVER);
579            long var1 = System.nanoTime();
580            AxisAlignedBB.getAABBPool().cleanPool();
581            FMLCommonHandler.instance().onPreServerTick();
582            ++this.tickCounter;
583    
584            if (this.startProfiling)
585            {
586                this.startProfiling = false;
587                this.theProfiler.profilingEnabled = true;
588                this.theProfiler.clearProfiling();
589            }
590    
591            this.theProfiler.startSection("root");
592            this.updateTimeLightAndEntities();
593    
594            if (this.tickCounter % 900 == 0)
595            {
596                this.theProfiler.startSection("save");
597                this.serverConfigManager.saveAllPlayerData();
598                this.saveAllWorlds(true);
599                this.theProfiler.endSection();
600            }
601    
602            this.theProfiler.startSection("tallying");
603            this.tickTimeArray[this.tickCounter % 100] = System.nanoTime() - var1;
604            this.sentPacketCountArray[this.tickCounter % 100] = Packet.sentID - this.lastSentPacketID;
605            this.lastSentPacketID = Packet.sentID;
606            this.sentPacketSizeArray[this.tickCounter % 100] = Packet.sentSize - this.lastSentPacketSize;
607            this.lastSentPacketSize = Packet.sentSize;
608            this.receivedPacketCountArray[this.tickCounter % 100] = Packet.receivedID - this.lastReceivedID;
609            this.lastReceivedID = Packet.receivedID;
610            this.receivedPacketSizeArray[this.tickCounter % 100] = Packet.receivedSize - this.lastReceivedSize;
611            this.lastReceivedSize = Packet.receivedSize;
612            this.theProfiler.endSection();
613            this.theProfiler.startSection("snooper");
614    
615            if (!this.usageSnooper.isSnooperRunning() && this.tickCounter > 100)
616            {
617                this.usageSnooper.startSnooper();
618            }
619    
620            if (this.tickCounter % 6000 == 0)
621            {
622                this.usageSnooper.addMemoryStatsToSnooper();
623            }
624    
625            this.theProfiler.endSection();
626            this.theProfiler.endSection();
627            FMLCommonHandler.instance().onPostServerTick();
628        }
629    
630        public void updateTimeLightAndEntities()
631        {
632            this.theProfiler.startSection("levels");
633            int var1;
634    
635            Integer[] ids = DimensionManager.getIDs();
636            for (int x = 0; x < ids.length; x++)
637            {
638                int id = ids[x];
639                long var2 = System.nanoTime();
640    
641                if (id == 0 || this.getAllowNether())
642                {
643                    WorldServer var4 = DimensionManager.getWorld(id);
644                    this.theProfiler.startSection(var4.getWorldInfo().getWorldName());
645                    this.theProfiler.startSection("pools");
646                    var4.getWorldVec3Pool().clear();
647                    this.theProfiler.endSection();
648    
649                    if (this.tickCounter % 20 == 0)
650                    {
651                        this.theProfiler.startSection("timeSync");
652                        this.serverConfigManager.sendPacketToAllPlayersInDimension(new Packet4UpdateTime(var4.getTotalWorldTime(), var4.getWorldTime()), var4.provider.dimensionId);
653                        this.theProfiler.endSection();
654                    }
655    
656                    this.theProfiler.startSection("tick");
657                    FMLCommonHandler.instance().onPreWorldTick(var4);
658                    CrashReport var6;
659    
660                    try
661                    {
662                        var4.tick();
663                    }
664                    catch (Throwable var8)
665                    {
666                        var6 = CrashReport.func_85055_a(var8, "Exception ticking world");
667                        var4.addWorldInfoToCrashReport(var6);
668                        throw new ReportedException(var6);
669                    }
670    
671                    try
672                    {
673                        var4.updateEntities();
674                    }
675                    catch (Throwable var7)
676                    {
677                        var6 = CrashReport.func_85055_a(var7, "Exception ticking world entities");
678                        var4.addWorldInfoToCrashReport(var6);
679                        throw new ReportedException(var6);
680                    }
681    
682                    FMLCommonHandler.instance().onPostWorldTick(var4);
683                    this.theProfiler.endSection();
684                    this.theProfiler.startSection("tracker");
685                    var4.getEntityTracker().updateTrackedEntities();
686                    this.theProfiler.endSection();
687                    this.theProfiler.endSection();
688                }
689    
690                worldTickTimes.get(id)[this.tickCounter % 100] = System.nanoTime() - var2;
691            }
692    
693            this.theProfiler.endStartSection("dim_unloading");
694            DimensionManager.unloadWorlds(worldTickTimes);
695            this.theProfiler.endStartSection("connection");
696            this.getNetworkThread().networkTick();
697            this.theProfiler.endStartSection("players");
698            this.serverConfigManager.sendPlayerInfoToAllPlayers();
699            this.theProfiler.endStartSection("tickables");
700    
701            for (var1 = 0; var1 < this.tickables.size(); ++var1)
702            {
703                ((IUpdatePlayerListBox)this.tickables.get(var1)).update();
704            }
705    
706            this.theProfiler.endSection();
707        }
708    
709        public boolean getAllowNether()
710        {
711            return true;
712        }
713    
714        public void startServerThread()
715        {
716            (new ThreadMinecraftServer(this, "Server thread")).start();
717        }
718    
719        /**
720         * Returns a File object from the specified string.
721         */
722        public File getFile(String par1Str)
723        {
724            return new File(this.getDataDirectory(), par1Str);
725        }
726    
727        /**
728         * Logs the message with a level of INFO.
729         */
730        public void logInfo(String par1Str)
731        {
732            logger.info(par1Str);
733        }
734    
735        /**
736         * Logs the message with a level of WARN.
737         */
738        public void logWarning(String par1Str)
739        {
740            logger.warning(par1Str);
741        }
742    
743        /**
744         * Gets the worldServer by the given dimension.
745         */
746        public WorldServer worldServerForDimension(int par1)
747        {
748            WorldServer ret = DimensionManager.getWorld(par1);
749            if (ret == null)
750            {
751                DimensionManager.initDimension(par1);
752                ret = DimensionManager.getWorld(par1);
753            }
754            return ret;
755        }
756    
757        @SideOnly(Side.SERVER)
758        public void func_82010_a(IUpdatePlayerListBox par1IUpdatePlayerListBox)
759        {
760            this.tickables.add(par1IUpdatePlayerListBox);
761        }
762    
763        /**
764         * Returns the server's hostname.
765         */
766        public String getHostname()
767        {
768            return this.hostname;
769        }
770    
771        /**
772         * Never used, but "getServerPort" is already taken.
773         */
774        public int getPort()
775        {
776            return this.serverPort;
777        }
778    
779        /**
780         * minecraftServer.getMOTD is used in 2 places instead (it is a non-virtual function which returns the same thing)
781         */
782        public String getServerMOTD()
783        {
784            return this.motd;
785        }
786    
787        /**
788         * Returns the server's Minecraft version as string.
789         */
790        public String getMinecraftVersion()
791        {
792            return "1.4.5";
793        }
794    
795        /**
796         * Returns the number of players currently on the server.
797         */
798        public int getCurrentPlayerCount()
799        {
800            return this.serverConfigManager.getCurrentPlayerCount();
801        }
802    
803        /**
804         * Returns the maximum number of players allowed on the server.
805         */
806        public int getMaxPlayers()
807        {
808            return this.serverConfigManager.getMaxPlayers();
809        }
810    
811        /**
812         * Returns an array of the usernames of all the connected players.
813         */
814        public String[] getAllUsernames()
815        {
816            return this.serverConfigManager.getAllUsernames();
817        }
818    
819        /**
820         * Used by RCon's Query in the form of "MajorServerMod 1.2.3: MyPlugin 1.3; AnotherPlugin 2.1; AndSoForth 1.0".
821         */
822        public String getPlugins()
823        {
824            return "";
825        }
826    
827        public String executeCommand(String par1Str)
828        {
829            RConConsoleSource.consoleBuffer.resetLog();
830            this.commandManager.executeCommand(RConConsoleSource.consoleBuffer, par1Str);
831            return RConConsoleSource.consoleBuffer.getChatBuffer();
832        }
833    
834        /**
835         * Returns true if debugging is enabled, false otherwise.
836         */
837        public boolean isDebuggingEnabled()
838        {
839            return false;
840        }
841    
842        /**
843         * Logs the error message with a level of SEVERE.
844         */
845        public void logSevere(String par1Str)
846        {
847            logger.log(Level.SEVERE, par1Str);
848        }
849    
850        /**
851         * If isDebuggingEnabled(), logs the message with a level of INFO.
852         */
853        public void logDebug(String par1Str)
854        {
855            if (this.isDebuggingEnabled())
856            {
857                logger.log(Level.INFO, par1Str);
858            }
859        }
860    
861        public String getServerModName()
862        {
863            return "forge,fml";
864        }
865    
866        /**
867         * Adds the server info, including from theWorldServer, to the crash report.
868         */
869        public CrashReport addServerInfoToCrashReport(CrashReport par1CrashReport)
870        {
871            par1CrashReport.func_85056_g().addCrashSectionCallable("Profiler Position", new CallableIsServerModded(this));
872    
873            if (this.worldServers != null && this.worldServers.length > 0 && this.worldServers[0] != null)
874            {
875                par1CrashReport.func_85056_g().addCrashSectionCallable("Vec3 Pool Size", new CallableServerProfiler(this));
876            }
877    
878            if (this.serverConfigManager != null)
879            {
880                par1CrashReport.func_85056_g().addCrashSectionCallable("Player Count", new CallableServerMemoryStats(this));
881            }
882    
883            return par1CrashReport;
884        }
885    
886        /**
887         * If par2Str begins with /, then it searches for commands, otherwise it returns players.
888         */
889        public List getPossibleCompletions(ICommandSender par1ICommandSender, String par2Str)
890        {
891            ArrayList var3 = new ArrayList();
892    
893            if (par2Str.startsWith("/"))
894            {
895                par2Str = par2Str.substring(1);
896                boolean var10 = !par2Str.contains(" ");
897                List var11 = this.commandManager.getPossibleCommands(par1ICommandSender, par2Str);
898    
899                if (var11 != null)
900                {
901                    Iterator var12 = var11.iterator();
902    
903                    while (var12.hasNext())
904                    {
905                        String var13 = (String)var12.next();
906    
907                        if (var10)
908                        {
909                            var3.add("/" + var13);
910                        }
911                        else
912                        {
913                            var3.add(var13);
914                        }
915                    }
916                }
917    
918                return var3;
919            }
920            else
921            {
922                String[] var4 = par2Str.split(" ", -1);
923                String var5 = var4[var4.length - 1];
924                String[] var6 = this.serverConfigManager.getAllUsernames();
925                int var7 = var6.length;
926    
927                for (int var8 = 0; var8 < var7; ++var8)
928                {
929                    String var9 = var6[var8];
930    
931                    if (CommandBase.doesStringStartWith(var5, var9))
932                    {
933                        var3.add(var9);
934                    }
935                }
936    
937                return var3;
938            }
939        }
940    
941        /**
942         * Gets mcServer.
943         */
944        public static MinecraftServer getServer()
945        {
946            return mcServer;
947        }
948    
949        /**
950         * Gets the name of this command sender (usually username, but possibly "Rcon")
951         */
952        public String getCommandSenderName()
953        {
954            return "Server";
955        }
956    
957        public void sendChatToPlayer(String par1Str)
958        {
959            logger.info(StringUtils.stripControlCodes(par1Str));
960        }
961    
962        /**
963         * Returns true if the command sender is allowed to use the given command.
964         */
965        public boolean canCommandSenderUseCommand(int par1, String par2Str)
966        {
967            return true;
968        }
969    
970        /**
971         * Translates and formats the given string key with the given arguments.
972         */
973        public String translateString(String par1Str, Object ... par2ArrayOfObj)
974        {
975            return StringTranslate.getInstance().translateKeyFormat(par1Str, par2ArrayOfObj);
976        }
977    
978        public ICommandManager getCommandManager()
979        {
980            return this.commandManager;
981        }
982    
983        /**
984         * Gets KeyPair instanced in MinecraftServer.
985         */
986        public KeyPair getKeyPair()
987        {
988            return this.serverKeyPair;
989        }
990    
991        /**
992         * Gets serverPort.
993         */
994        public int getServerPort()
995        {
996            return this.serverPort;
997        }
998    
999        public void setServerPort(int par1)
1000        {
1001            this.serverPort = par1;
1002        }
1003    
1004        /**
1005         * Returns the username of the server owner (for integrated servers)
1006         */
1007        public String getServerOwner()
1008        {
1009            return this.serverOwner;
1010        }
1011    
1012        /**
1013         * Sets the username of the owner of this server (in the case of an integrated server)
1014         */
1015        public void setServerOwner(String par1Str)
1016        {
1017            this.serverOwner = par1Str;
1018        }
1019    
1020        public boolean isSinglePlayer()
1021        {
1022            return this.serverOwner != null;
1023        }
1024    
1025        public String getFolderName()
1026        {
1027            return this.folderName;
1028        }
1029    
1030        public void setFolderName(String par1Str)
1031        {
1032            this.folderName = par1Str;
1033        }
1034    
1035        @SideOnly(Side.CLIENT)
1036        public void setWorldName(String par1Str)
1037        {
1038            this.worldName = par1Str;
1039        }
1040    
1041        @SideOnly(Side.CLIENT)
1042        public String getWorldName()
1043        {
1044            return this.worldName;
1045        }
1046    
1047        public void setKeyPair(KeyPair par1KeyPair)
1048        {
1049            this.serverKeyPair = par1KeyPair;
1050        }
1051    
1052        public void setDifficultyForAllWorlds(int par1)
1053        {
1054            for (int var2 = 0; var2 < this.worldServers.length; ++var2)
1055            {
1056                WorldServer var3 = this.worldServers[var2];
1057    
1058                if (var3 != null)
1059                {
1060                    if (var3.getWorldInfo().isHardcoreModeEnabled())
1061                    {
1062                        var3.difficultySetting = 3;
1063                        var3.setAllowedSpawnTypes(true, true);
1064                    }
1065                    else if (this.isSinglePlayer())
1066                    {
1067                        var3.difficultySetting = par1;
1068                        var3.setAllowedSpawnTypes(var3.difficultySetting > 0, true);
1069                    }
1070                    else
1071                    {
1072                        var3.difficultySetting = par1;
1073                        var3.setAllowedSpawnTypes(this.allowSpawnMonsters(), this.canSpawnAnimals);
1074                    }
1075                }
1076            }
1077        }
1078    
1079        protected boolean allowSpawnMonsters()
1080        {
1081            return true;
1082        }
1083    
1084        /**
1085         * Gets whether this is a demo or not.
1086         */
1087        public boolean isDemo()
1088        {
1089            return this.isDemo;
1090        }
1091    
1092        /**
1093         * Sets whether this is a demo or not.
1094         */
1095        public void setDemo(boolean par1)
1096        {
1097            this.isDemo = par1;
1098        }
1099    
1100        public void canCreateBonusChest(boolean par1)
1101        {
1102            this.enableBonusChest = par1;
1103        }
1104    
1105        public ISaveFormat getActiveAnvilConverter()
1106        {
1107            return this.anvilConverterForAnvilFile;
1108        }
1109    
1110        /**
1111         * WARNING : directly calls
1112         * getActiveAnvilConverter().deleteWorldDirectory(theWorldServer[0].getSaveHandler().getSaveDirectoryName());
1113         */
1114        public void deleteWorldAndStopServer()
1115        {
1116            this.worldIsBeingDeleted = true;
1117            this.getActiveAnvilConverter().flushCache();
1118    
1119            for (int var1 = 0; var1 < this.worldServers.length; ++var1)
1120            {
1121                WorldServer var2 = this.worldServers[var1];
1122    
1123                if (var2 != null)
1124                {
1125                    MinecraftForge.EVENT_BUS.post(new WorldEvent.Unload(var2));
1126                    var2.flush();
1127                }
1128            }
1129    
1130            this.getActiveAnvilConverter().deleteWorldDirectory(this.worldServers[0].getSaveHandler().getSaveDirectoryName());
1131            this.initiateShutdown();
1132        }
1133    
1134        public String getTexturePack()
1135        {
1136            return this.texturePack;
1137        }
1138    
1139        public void setTexturePack(String par1Str)
1140        {
1141            this.texturePack = par1Str;
1142        }
1143    
1144        public void addServerStatsToSnooper(PlayerUsageSnooper par1PlayerUsageSnooper)
1145        {
1146            par1PlayerUsageSnooper.addData("whitelist_enabled", Boolean.valueOf(false));
1147            par1PlayerUsageSnooper.addData("whitelist_count", Integer.valueOf(0));
1148            par1PlayerUsageSnooper.addData("players_current", Integer.valueOf(this.getCurrentPlayerCount()));
1149            par1PlayerUsageSnooper.addData("players_max", Integer.valueOf(this.getMaxPlayers()));
1150            par1PlayerUsageSnooper.addData("players_seen", Integer.valueOf(this.serverConfigManager.getAvailablePlayerDat().length));
1151            par1PlayerUsageSnooper.addData("uses_auth", Boolean.valueOf(this.onlineMode));
1152            par1PlayerUsageSnooper.addData("gui_state", this.getGuiEnabled() ? "enabled" : "disabled");
1153            par1PlayerUsageSnooper.addData("avg_tick_ms", Integer.valueOf((int)(MathHelper.average(this.tickTimeArray) * 1.0E-6D)));
1154            par1PlayerUsageSnooper.addData("avg_sent_packet_count", Integer.valueOf((int)MathHelper.average(this.sentPacketCountArray)));
1155            par1PlayerUsageSnooper.addData("avg_sent_packet_size", Integer.valueOf((int)MathHelper.average(this.sentPacketSizeArray)));
1156            par1PlayerUsageSnooper.addData("avg_rec_packet_count", Integer.valueOf((int)MathHelper.average(this.receivedPacketCountArray)));
1157            par1PlayerUsageSnooper.addData("avg_rec_packet_size", Integer.valueOf((int)MathHelper.average(this.receivedPacketSizeArray)));
1158            int var2 = 0;
1159    
1160            for (int var3 = 0; var3 < this.worldServers.length; ++var3)
1161            {
1162                if (this.worldServers[var3] != null)
1163                {
1164                    WorldServer var4 = this.worldServers[var3];
1165                    WorldInfo var5 = var4.getWorldInfo();
1166                    par1PlayerUsageSnooper.addData("world[" + var2 + "][dimension]", Integer.valueOf(var4.provider.dimensionId));
1167                    par1PlayerUsageSnooper.addData("world[" + var2 + "][mode]", var5.getGameType());
1168                    par1PlayerUsageSnooper.addData("world[" + var2 + "][difficulty]", Integer.valueOf(var4.difficultySetting));
1169                    par1PlayerUsageSnooper.addData("world[" + var2 + "][hardcore]", Boolean.valueOf(var5.isHardcoreModeEnabled()));
1170                    par1PlayerUsageSnooper.addData("world[" + var2 + "][generator_name]", var5.getTerrainType().getWorldTypeName());
1171                    par1PlayerUsageSnooper.addData("world[" + var2 + "][generator_version]", Integer.valueOf(var5.getTerrainType().getGeneratorVersion()));
1172                    par1PlayerUsageSnooper.addData("world[" + var2 + "][height]", Integer.valueOf(this.buildLimit));
1173                    par1PlayerUsageSnooper.addData("world[" + var2 + "][chunks_loaded]", Integer.valueOf(var4.getChunkProvider().getLoadedChunkCount()));
1174                    ++var2;
1175                }
1176            }
1177    
1178            par1PlayerUsageSnooper.addData("worlds", Integer.valueOf(var2));
1179        }
1180    
1181        public void addServerTypeToSnooper(PlayerUsageSnooper par1PlayerUsageSnooper)
1182        {
1183            par1PlayerUsageSnooper.addData("singleplayer", Boolean.valueOf(this.isSinglePlayer()));
1184            par1PlayerUsageSnooper.addData("server_brand", this.getServerModName());
1185            par1PlayerUsageSnooper.addData("gui_supported", GraphicsEnvironment.isHeadless() ? "headless" : "supported");
1186            par1PlayerUsageSnooper.addData("dedicated", Boolean.valueOf(this.isDedicatedServer()));
1187        }
1188    
1189        /**
1190         * Returns whether snooping is enabled or not.
1191         */
1192        public boolean isSnooperEnabled()
1193        {
1194            return true;
1195        }
1196    
1197        /**
1198         * This is checked to be 16 upon receiving the packet, otherwise the packet is ignored.
1199         */
1200        public int textureSize()
1201        {
1202            return 16;
1203        }
1204    
1205        public abstract boolean isDedicatedServer();
1206    
1207        public boolean isServerInOnlineMode()
1208        {
1209            return this.onlineMode;
1210        }
1211    
1212        public void setOnlineMode(boolean par1)
1213        {
1214            this.onlineMode = par1;
1215        }
1216    
1217        public boolean getCanSpawnAnimals()
1218        {
1219            return this.canSpawnAnimals;
1220        }
1221    
1222        public void setCanSpawnAnimals(boolean par1)
1223        {
1224            this.canSpawnAnimals = par1;
1225        }
1226    
1227        public boolean getCanSpawnNPCs()
1228        {
1229            return this.canSpawnNPCs;
1230        }
1231    
1232        public void setCanSpawnNPCs(boolean par1)
1233        {
1234            this.canSpawnNPCs = par1;
1235        }
1236    
1237        public boolean isPVPEnabled()
1238        {
1239            return this.pvpEnabled;
1240        }
1241    
1242        public void setAllowPvp(boolean par1)
1243        {
1244            this.pvpEnabled = par1;
1245        }
1246    
1247        public boolean isFlightAllowed()
1248        {
1249            return this.allowFlight;
1250        }
1251    
1252        public void setAllowFlight(boolean par1)
1253        {
1254            this.allowFlight = par1;
1255        }
1256    
1257        /**
1258         * Return whether command blocks are enabled.
1259         */
1260        public abstract boolean isCommandBlockEnabled();
1261    
1262        public String getMOTD()
1263        {
1264            return this.motd;
1265        }
1266    
1267        public void setMOTD(String par1Str)
1268        {
1269            this.motd = par1Str;
1270        }
1271    
1272        public int getBuildLimit()
1273        {
1274            return this.buildLimit;
1275        }
1276    
1277        public void setBuildLimit(int par1)
1278        {
1279            this.buildLimit = par1;
1280        }
1281    
1282        public boolean isServerStopped()
1283        {
1284            return this.serverStopped;
1285        }
1286    
1287        public ServerConfigurationManager getConfigurationManager()
1288        {
1289            return this.serverConfigManager;
1290        }
1291    
1292        public void setConfigurationManager(ServerConfigurationManager par1ServerConfigurationManager)
1293        {
1294            this.serverConfigManager = par1ServerConfigurationManager;
1295        }
1296    
1297        /**
1298         * Sets the game type for all worlds.
1299         */
1300        public void setGameType(EnumGameType par1EnumGameType)
1301        {
1302            for (int var2 = 0; var2 < this.worldServers.length; ++var2)
1303            {
1304                getServer().worldServers[var2].getWorldInfo().setGameType(par1EnumGameType);
1305            }
1306        }
1307    
1308        public abstract NetworkListenThread getNetworkThread();
1309    
1310        @SideOnly(Side.CLIENT)
1311        public boolean serverIsInRunLoop()
1312        {
1313            return this.serverIsRunning;
1314        }
1315    
1316        public boolean getGuiEnabled()
1317        {
1318            return false;
1319        }
1320    
1321        /**
1322         * On dedicated does nothing. On integrated, sets commandsAllowedForAll, gameType and allows external connections.
1323         */
1324        public abstract String shareToLAN(EnumGameType var1, boolean var2);
1325    
1326        public int getTickCounter()
1327        {
1328            return this.tickCounter;
1329        }
1330    
1331        public void enableProfiling()
1332        {
1333            this.startProfiling = true;
1334        }
1335    
1336        @SideOnly(Side.CLIENT)
1337        public PlayerUsageSnooper getPlayerUsageSnooper()
1338        {
1339            return this.usageSnooper;
1340        }
1341    
1342        /**
1343         * Return the coordinates for this player as ChunkCoordinates.
1344         */
1345        public ChunkCoordinates getPlayerCoordinates()
1346        {
1347            return new ChunkCoordinates(0, 0, 0);
1348        }
1349    
1350        /**
1351         * Return the spawn protection area's size.
1352         */
1353        public int getSpawnProtectionSize()
1354        {
1355            return 16;
1356        }
1357    
1358        /**
1359         * Gets the current player count, maximum player count, and player entity list.
1360         */
1361        public static ServerConfigurationManager getServerConfigurationManager(MinecraftServer par0MinecraftServer)
1362        {
1363            return par0MinecraftServer.serverConfigManager;
1364        }
1365    
1366        @SideOnly(Side.SERVER)
1367        public static void main(String[] par0ArrayOfStr)
1368        {
1369            FMLRelauncher.handleServerRelaunch(new ArgsWrapper(par0ArrayOfStr));
1370        }
1371        @SideOnly(Side.SERVER)
1372        public static void fmlReentry(ArgsWrapper wrap)
1373        {
1374            String[] par0ArrayOfStr = wrap.args;
1375            StatList.nopInit();
1376    
1377            try
1378            {
1379                boolean var1 = !GraphicsEnvironment.isHeadless();
1380                String var2 = null;
1381                String var3 = ".";
1382                String var4 = null;
1383                boolean var5 = false;
1384                boolean var6 = false;
1385                int var7 = -1;
1386    
1387                for (int var8 = 0; var8 < par0ArrayOfStr.length; ++var8)
1388                {
1389                    String var9 = par0ArrayOfStr[var8];
1390                    String var10 = var8 == par0ArrayOfStr.length - 1 ? null : par0ArrayOfStr[var8 + 1];
1391                    boolean var11 = false;
1392    
1393                    if (!var9.equals("nogui") && !var9.equals("--nogui"))
1394                    {
1395                        if (var9.equals("--port") && var10 != null)
1396                        {
1397                            var11 = true;
1398    
1399                            try
1400                            {
1401                                var7 = Integer.parseInt(var10);
1402                            }
1403                            catch (NumberFormatException var13)
1404                            {
1405                                ;
1406                            }
1407                        }
1408                        else if (var9.equals("--singleplayer") && var10 != null)
1409                        {
1410                            var11 = true;
1411                            var2 = var10;
1412                        }
1413                        else if (var9.equals("--universe") && var10 != null)
1414                        {
1415                            var11 = true;
1416                            var3 = var10;
1417                        }
1418                        else if (var9.equals("--world") && var10 != null)
1419                        {
1420                            var11 = true;
1421                            var4 = var10;
1422                        }
1423                        else if (var9.equals("--demo"))
1424                        {
1425                            var5 = true;
1426                        }
1427                        else if (var9.equals("--bonusChest"))
1428                        {
1429                            var6 = true;
1430                        }
1431                    }
1432                    else
1433                    {
1434                        var1 = false;
1435                    }
1436    
1437                    if (var11)
1438                    {
1439                        ++var8;
1440                    }
1441                }
1442    
1443                DedicatedServer var15 = new DedicatedServer(new File(var3));
1444    
1445                if (var2 != null)
1446                {
1447                    var15.setServerOwner(var2);
1448                }
1449    
1450                if (var4 != null)
1451                {
1452                    var15.setFolderName(var4);
1453                }
1454    
1455                if (var7 >= 0)
1456                {
1457                    var15.setServerPort(var7);
1458                }
1459    
1460                if (var5)
1461                {
1462                    var15.setDemo(true);
1463                }
1464    
1465                if (var6)
1466                {
1467                    var15.canCreateBonusChest(true);
1468                }
1469    
1470                if (var1)
1471                {
1472                    var15.enableGui();
1473                }
1474    
1475                var15.startServerThread();
1476                Runtime.getRuntime().addShutdownHook(new ThreadDedicatedServer(var15));
1477            }
1478            catch (Exception var14)
1479            {
1480                logger.log(Level.SEVERE, "Failed to start the minecraft server", var14);
1481            }
1482        }
1483    }