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