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