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