    001package net.minecraft.server.dedicated;
    002
    003import cpw.mods.fml.common.FMLCommonHandler;
    004import cpw.mods.fml.relauncher.Side;
    005import cpw.mods.fml.relauncher.SideOnly;
    006import java.io.File;
    007import java.io.IOException;
    008import java.net.InetAddress;
    009import java.util.ArrayList;
    010import java.util.Collections;
    011import java.util.List;
    012import java.util.Random;
    013import java.util.logging.Level;
    014import net.minecraft.command.ICommandSender;
    015import net.minecraft.command.ServerCommand;
    016import net.minecraft.crash.CrashReport;
    017import net.minecraft.network.NetworkListenThread;
    018import net.minecraft.network.rcon.IServer;
    019import net.minecraft.network.rcon.RConThreadMain;
    020import net.minecraft.network.rcon.RConThreadQuery;
    021import net.minecraft.profiler.PlayerUsageSnooper;
    022import net.minecraft.server.MinecraftServer;
    023import net.minecraft.server.gui.ServerGUI;
    024import net.minecraft.server.management.ServerConfigurationManager;
    025import net.minecraft.util.CryptManager;
    026import net.minecraft.util.MathHelper;
    027import net.minecraft.world.EnumGameType;
    028import net.minecraft.world.WorldSettings;
    029import net.minecraft.world.WorldType;
    030
    031public class DedicatedServer extends MinecraftServer implements IServer
    032{
    033    private final List pendingCommandList = Collections.synchronizedList(new ArrayList());
    034    private RConThreadQuery theRConThreadQuery;
    035    private RConThreadMain theRConThreadMain;
    036    private PropertyManager settings;
    037    private boolean canSpawnStructures;
    038    private EnumGameType gameType;
    039    private NetworkListenThread networkThread;
    040    private boolean guiIsEnabled = false;
    041
    042    public DedicatedServer(File par1File)
    043    {
    044        super(par1File);
    045        new DedicatedServerSleepThread(this);
    046    }
    047
    048    /**
    049     * Initialises the server and starts it.
    050     */
    051    protected boolean startServer() throws IOException
    052    {
    053        DedicatedServerCommandThread var1 = new DedicatedServerCommandThread(this);
    054        var1.setDaemon(true);
    055        var1.start();
    056        ConsoleLogManager.init();
    057        logger.info("Starting minecraft server version 1.4.7");
    058
    059        if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L)
    060        {
    061            logger.warning("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
    062        }
    063
    064        FMLCommonHandler.instance().onServerStart(this);
    065
    066        logger.info("Loading properties");
    067        this.settings = new PropertyManager(new File("server.properties"));
    068
    069        if (this.isSinglePlayer())
    070        {
    071            this.setHostname("127.0.0.1");
    072        }
    073        else
    074        {
    075            this.setOnlineMode(this.settings.getBooleanProperty("online-mode", true));
    076            this.setHostname(this.settings.getProperty("server-ip", ""));
    077        }
    078
    079        this.setCanSpawnAnimals(this.settings.getBooleanProperty("spawn-animals", true));
    080        this.setCanSpawnNPCs(this.settings.getBooleanProperty("spawn-npcs", true));
    081        this.setAllowPvp(this.settings.getBooleanProperty("pvp", true));
    082        this.setAllowFlight(this.settings.getBooleanProperty("allow-flight", false));
    083        this.setTexturePack(this.settings.getProperty("texture-pack", ""));
    084        this.setMOTD(this.settings.getProperty("motd", "A Minecraft Server"));
    085
    086        if (this.settings.getIntProperty("difficulty", 1) < 0)
    087        {
    088            this.settings.setProperty("difficulty", Integer.valueOf(0));
    089        }
    090        else if (this.settings.getIntProperty("difficulty", 1) > 3)
    091        {
    092            this.settings.setProperty("difficulty", Integer.valueOf(3));
    093        }
    094
    095        this.canSpawnStructures = this.settings.getBooleanProperty("generate-structures", true);
    096        int var2 = this.settings.getIntProperty("gamemode", EnumGameType.SURVIVAL.getID());
    097        this.gameType = WorldSettings.getGameTypeById(var2);
    098        logger.info("Default game type: " + this.gameType);
    099        InetAddress var3 = null;
    100
    101        if (this.getServerHostname().length() > 0)
    102        {
    103            var3 = InetAddress.getByName(this.getServerHostname());
    104        }
    105
    106        if (this.getServerPort() < 0)
    107        {
    108            this.setServerPort(this.settings.getIntProperty("server-port", 25565));
    109        }
    110
    111        logger.info("Generating keypair");
    112        this.setKeyPair(CryptManager.createNewKeyPair());
    113        logger.info("Starting Minecraft server on " + (this.getServerHostname().length() == 0 ? "*" : this.getServerHostname()) + ":" + this.getServerPort());
    114
    115        try
    116        {
    117            this.networkThread = new DedicatedServerListenThread(this, var3, this.getServerPort());
    118        }
    119        catch (IOException var16)
    120        {
    121            logger.warning("**** FAILED TO BIND TO PORT!");
    122            logger.log(Level.WARNING, "The exception was: " + var16.toString());
    123            logger.warning("Perhaps a server is already running on that port?");
    124            return false;
    125        }
    126
    127        if (!this.isServerInOnlineMode())
    128        {
    129            logger.warning("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!");
    130            logger.warning("The server will make no attempt to authenticate usernames. Beware.");
    131            logger.warning("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.");
    132            logger.warning("To change this, set \"online-mode\" to \"true\" in the server.properties file.");
    133        }
    134
    135        FMLCommonHandler.instance().onServerStarted();
    136
    137        this.setConfigurationManager(new DedicatedPlayerList(this));
    138        long var4 = System.nanoTime();
    139
    140        if (this.getFolderName() == null)
    141        {
    142            this.setFolderName(this.settings.getProperty("level-name", "world"));
    143        }
    144
    145        String var6 = this.settings.getProperty("level-seed", "");
    146        String var7 = this.settings.getProperty("level-type", "DEFAULT");
    147        String var8 = this.settings.getProperty("generator-settings", "");
    148        long var9 = (new Random()).nextLong();
    149
    150        if (var6.length() > 0)
    151        {
    152            try
    153            {
    154                long var11 = Long.parseLong(var6);
    155
    156                if (var11 != 0L)
    157                {
    158                    var9 = var11;
    159                }
    160            }
    161            catch (NumberFormatException var15)
    162            {
    163                var9 = (long)var6.hashCode();
    164            }
    165        }
    166
    167        WorldType var17 = WorldType.parseWorldType(var7);
    168
    169        if (var17 == null)
    170        {
    171            var17 = WorldType.DEFAULT;
    172        }
    173
    174        this.setBuildLimit(this.settings.getIntProperty("max-build-height", 256));
    175        this.setBuildLimit((this.getBuildLimit() + 8) / 16 * 16);
    176        this.setBuildLimit(MathHelper.clamp_int(this.getBuildLimit(), 64, 256));
    177        this.settings.setProperty("max-build-height", Integer.valueOf(this.getBuildLimit()));
    178        if (!FMLCommonHandler.instance().handleServerAboutToStart(this)) { return false; }
    179        logger.info("Preparing level \"" + this.getFolderName() + "\"");
    180        this.loadAllWorlds(this.getFolderName(), this.getFolderName(), var9, var17, var8);
    181        long var12 = System.nanoTime() - var4;
    182        String var14 = String.format("%.3fs", new Object[] {Double.valueOf((double)var12 / 1.0E9D)});
    183        logger.info("Done (" + var14 + ")! For help, type \"help\" or \"?\"");
    184
    185        if (this.settings.getBooleanProperty("enable-query", false))
    186        {
    187            logger.info("Starting GS4 status listener");
    188            this.theRConThreadQuery = new RConThreadQuery(this);
    189            this.theRConThreadQuery.startThread();
    190        }
    191
    192        if (this.settings.getBooleanProperty("enable-rcon", false))
    193        {
    194            logger.info("Starting remote control listener");
    195            this.theRConThreadMain = new RConThreadMain(this);
    196            this.theRConThreadMain.startThread();
    197        }
    198
    199        return FMLCommonHandler.instance().handleServerStarting(this);
    200    }
    201
    202    public boolean canStructuresSpawn()
    203    {
    204        return this.canSpawnStructures;
    205    }
    206
    207    public EnumGameType getGameType()
    208    {
    209        return this.gameType;
    210    }
    211
    212    /**
    213     * Defaults to "1" (Easy) for the dedicated server, defaults to "2" (Normal) on the client.
    214     */
    215    public int getDifficulty()
    216    {
    217        return this.settings.getIntProperty("difficulty", 1);
    218    }
    219
    220    /**
    221     * Defaults to false.
    222     */
    223    public boolean isHardcore()
    224    {
    225        return this.settings.getBooleanProperty("hardcore", false);
    226    }
    227
    228    /**
    229     * Called on exit from the main run() loop.
    230     */
    231    protected void finalTick(CrashReport par1CrashReport)
    232    {
    233        while (this.isServerRunning())
    234        {
    235            this.executePendingCommands();
    236
    237            try
    238            {
    239                Thread.sleep(10L);
    240            }
    241            catch (InterruptedException var3)
    242            {
    243                var3.printStackTrace();
    244            }
    245        }
    246    }
    247
    248    /**
    249     * Adds the server info, including from theWorldServer, to the crash report.
    250     */
    251    public CrashReport addServerInfoToCrashReport(CrashReport par1CrashReport)
    252    {
    253        par1CrashReport = super.addServerInfoToCrashReport(par1CrashReport);
    254        par1CrashReport.func_85056_g().addCrashSectionCallable("Is Modded", new CallableType(this));
    255        par1CrashReport.func_85056_g().addCrashSectionCallable("Type", new CallableServerType(this));
    256        return par1CrashReport;
    257    }
    258
    259    /**
    260     * Directly calls System.exit(0), instantly killing the program.
    261     */
    262    protected void systemExitNow()
    263    {
    264        System.exit(0);
    265    }
    266
    267    public void updateTimeLightAndEntities()
    268    {
    269        super.updateTimeLightAndEntities();
    270        this.executePendingCommands();
    271    }
    272
    273    public boolean getAllowNether()
    274    {
    275        return this.settings.getBooleanProperty("allow-nether", true);
    276    }
    277
    278    public boolean allowSpawnMonsters()
    279    {
    280        return this.settings.getBooleanProperty("spawn-monsters", true);
    281    }
    282
    283    public void addServerStatsToSnooper(PlayerUsageSnooper par1PlayerUsageSnooper)
    284    {
    285        par1PlayerUsageSnooper.addData("whitelist_enabled", Boolean.valueOf(this.getDedicatedPlayerList().isWhiteListEnabled()));
    286        par1PlayerUsageSnooper.addData("whitelist_count", Integer.valueOf(this.getDedicatedPlayerList().getWhiteListedPlayers().size()));
    287        super.addServerStatsToSnooper(par1PlayerUsageSnooper);
    288    }
    289
    290    /**
    291     * Returns whether snooping is enabled or not.
    292     */
    293    public boolean isSnooperEnabled()
    294    {
    295        return this.settings.getBooleanProperty("snooper-enabled", true);
    296    }
    297
    298    public void addPendingCommand(String par1Str, ICommandSender par2ICommandSender)
    299    {
    300        this.pendingCommandList.add(new ServerCommand(par1Str, par2ICommandSender));
    301    }
    302
    303    public void executePendingCommands()
    304    {
    305        while (!this.pendingCommandList.isEmpty())
    306        {
    307            ServerCommand var1 = (ServerCommand)this.pendingCommandList.remove(0);
    308            this.getCommandManager().executeCommand(var1.sender, var1.command);
    309        }
    310    }
    311
    312    public boolean isDedicatedServer()
    313    {
    314        return true;
    315    }
    316
    317    public DedicatedPlayerList getDedicatedPlayerList()
    318    {
    319        return (DedicatedPlayerList)super.getConfigurationManager();
    320    }
    321
    322    public NetworkListenThread getNetworkThread()
    323    {
    324        return this.networkThread;
    325    }
    326
    327    /**
    328     * Gets an integer property. If it does not exist, set it to the specified value.
    329     */
    330    public int getIntProperty(String par1Str, int par2)
    331    {
    332        return this.settings.getIntProperty(par1Str, par2);
    333    }
    334
    335    /**
    336     * Gets a string property. If it does not exist, set it to the specified value.
    337     */
    338    public String getStringProperty(String par1Str, String par2Str)
    339    {
    340        return this.settings.getProperty(par1Str, par2Str);
    341    }
    342
    343    /**
    344     * Gets a boolean property. If it does not exist, set it to the specified value.
    345     */
    346    public boolean getBooleanProperty(String par1Str, boolean par2)
    347    {
    348        return this.settings.getBooleanProperty(par1Str, par2);
    349    }
    350
    351    /**
    352     * Saves an Object with the given property name.
    353     */
    354    public void setProperty(String par1Str, Object par2Obj)
    355    {
    356        this.settings.setProperty(par1Str, par2Obj);
    357    }
    358
    359    /**
    360     * Saves all of the server properties to the properties file.
    361     */
    362    public void saveProperties()
    363    {
    364        this.settings.saveProperties();
    365    }
    366
    367    /**
    368     * Returns the filename where server properties are stored
    369     */
    370    public String getSettingsFilename()
    371    {
    372        File var1 = this.settings.getPropertiesFile();
    373        return var1 != null ? var1.getAbsolutePath() : "No settings file";
    374    }
    375
    376    public boolean getGuiEnabled()
    377    {
    378        return this.guiIsEnabled;
    379    }
    380
    381    /**
    382     * On dedicated does nothing. On integrated, sets commandsAllowedForAll, gameType and allows external connections.
    383     */
    384    public String shareToLAN(EnumGameType par1EnumGameType, boolean par2)
    385    {
    386        return "";
    387    }
    388
    389    /**
    390     * Return whether command blocks are enabled.
    391     */
    392    public boolean isCommandBlockEnabled()
    393    {
    394        return this.settings.getBooleanProperty("enable-command-block", false);
    395    }
    396
    397    /**
    398     * Return the spawn protection area's size.
    399     */
    400    public int getSpawnProtectionSize()
    401    {
    402        return this.settings.getIntProperty("spawn-protection", super.getSpawnProtectionSize());
    403    }
    404
    405    public ServerConfigurationManager getConfigurationManager()
    406    {
    407        return this.getDedicatedPlayerList();
    408    }
    409
    410    @SideOnly(Side.SERVER)
    411    public void enableGui()
    412    {
    413        ServerGUI.initGUI(this);
    414        this.guiIsEnabled = true;
    415    }
    416}