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