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