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