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