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