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