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