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