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