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